Code Monkey home page Code Monkey logo

Comments (4)

Fizzadar avatar Fizzadar commented on June 8, 2024 2

Small update on this one: this is on the roadmap for v3. Will initially just be restoring the API docs but I’ll also work on some example code.

from pyinfra.

phlummox avatar phlummox commented on June 8, 2024

In case this is of use to anyone else encountering the same issues, below I've included code adapted from the pyinfra_cli package (mostly main.py), which can be used to execute deploy scripts (or python code contained in a string) on multiple hosts using the PyInfra API.

There is no parallelization - it would be up to the user to implement that however they wished (PyInfra's CLI uses gevent), to construct an Inventory with additional data or groups, and so on.

I couldn't honestly say I understand the details of what's being done with the State and Config objects that are created, but the code below works for me, with PyInfra version 2.8. Perhaps that's something that could be clarified if an example like this were added to the documentation.

import os
import sys
import logging

from typing import Callable, List, Tuple

from pyinfra import logger, state
from pyinfra.api import Config, Inventory, State
from pyinfra.api.connect import connect_all
from pyinfra.api.operations import run_ops
from pyinfra.context import ctx_config, ctx_host, ctx_state

# Don't write out deploy.pyc etc
sys.dont_write_bytecode = True

# Force line buffering
sys.stdout = os.fdopen(sys.stdout.fileno(), "w", 1)
sys.stderr = os.fdopen(sys.stderr.fileno(), "w", 1)

class LogHandler(logging.Handler):
  "handle log records"

  def emit(self, record):
    try:
      message = self.format(record)
      print(message, file=sys.stderr)
    except Exception:
      self.handleError(record)

def exec_file(filename: str):
  """
  Execute a Python file and optionally return its attributes as a dict.
  """
  state.current_exec_filename = filename

  with open(filename, "r", encoding="utf-8") as f:
    code = f.read()
    compiled_code = compile(code, filename, "exec")

  # Execute the code with locals/globals going into the dict
  globals_dict = {}
  exec(compiled_code, globals_dict)
  return globals_dict

def exec_str(code: str, filename: str):
  """
  Execute a Python module string and optionally return its attributes as a dict.
  """
  filename = "(none)"
  state.current_exec_filename = filename
  compiled_code = compile(code, filename, "exec")

  # Execute the code with locals/globals going into the dict
  globals_dict = {}
  exec(compiled_code, globals_dict)
  return globals_dict

def pyinfra_run(hosts: List[str], operations: List[Tuple[str,Callable]]):
  logger.setLevel(logging.INFO)
  handler = LogHandler()
  logger.addHandler(handler)

  # Setup state, config & inventory
  cwd = os.getcwd()
  state = State()
  state.cwd = cwd
  ctx_state.set(state)

  config = Config()
  config.lock_current_state()

  print("--> Loading inventory...", file=sys.stderr)
  inventory = Inventory( (hosts, {}) )

  # Initialise the state
  state.init(inventory, config)

  # Connect to the hosts & start handling the user commands
  print("--> Connecting to hosts...", file=sys.stderr)
  connect_all(state)

  for i, (filename, callback) in enumerate(operations):
    logger.info(f"Loading: {filename}")

    state.current_op_file_number = i
    state.current_deploy_filename = filename
    for host in state.inventory.iter_active_hosts():
      with ctx_config.use(state.config.copy()):
        with ctx_host.use(host):
          callback()
          logger.info(
            "{0}{1} {2}".format(host.print_prefix, "Ready:", filename),
          )

    # Remove any config changes introduced by the deploy file & any includes
    config.reset_locked_state()

  # if desired: the logic from pyinfra_cli.prints.print_meta could be copied,
  # for pretty-printing of proposed changes
  #print("--> Proposed changes:", file=sys.stderr)
  #print_meta(state)

  print("--> Beginning operation run...", file=sys.stderr)

  run_ops(state, serial=True, no_wait=False)

  # if desired: the logic from pyinfra_cli.prints.print_results could be copied,
  # for pretty-printing of final results
  #print("--> Results:", file=sys.stderr)
  #print_results(state)

if __name__ == "__main__":
  hosts = ["host1.example.com", "host2.example.com"]

  operations = [
      ("mydeploy.py", lambda: exec_file("mydeploy.py")),
      ("(nofile)",    lambda: exec_str("print('hi there')", "(nofile)")),
    ]

  pyinfra_run(hosts, operations)

from pyinfra.

phlummox avatar phlummox commented on June 8, 2024

Just to add a bit of further information ...

In my version of the above code, I've amended pyinfra_run() to take an Inventory object rather than a list of hostnames - that seems more flexible than just assuming an SSH connector/host.

The code currently in pyinfra_cli.inventory.make_inventory_from_files – here:

def make_inventory_from_files(

seems generally useful for quickly turning CLI-style inventory-strings (e.g. @docker/some-ctr-id,@ssh/some.hostname.com) into an Inventory, so I've basically just copy-and-pasted it. (I haven't had a chance to go through the logic and see exactly what it's doing, and if it can be tidied up.)

If you'd be interested in a pull request to add a new example in examples, showing how scripts can be created dynamically at runtime using the API, I'd be happy to create one – I'd be keen to hear what you think might be best practices in such an example before I do so.

from pyinfra.

phlummox avatar phlummox commented on June 8, 2024

Awesome! I'm looking forward to it :)

from pyinfra.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.