Commit Graph

53 Commits (e1df98168c0e056f27f8de70a4080773955ea8a7)

Author SHA1 Message Date
David Wilson 599da0689a issue #477 / ansible: avoid a race in async job startup.
Ansible 2.3/Python 2.4 work revealed there is no guarantee a slow target
will have written the initial job status file out before a fast
controller makes an initial check for it. Therefore, provide AsyncRunner
with a sender it should send a message to when the initial job file has
been written.

As a bonus, also catch and report exceptions happening early in
AsyncRunner, rather than leaving them to end up in -vvv output.
6 years ago
David Wilson 8f5b65f7ec issue #477: introduce subprocess isolation.
Since Python 2.4 fork is so defective, we must use subprocesses for
mitogen_task_isolation=fork. This has plenty of upside, since the long
term goal is to dump forking altogether. This allows a gentle
introduction of its replacement.
6 years ago
David Wilson b8ca015b83 issue #61: unused variable (reported by LGTM) 6 years ago
David Wilson 04755c3321 issue #426: tighten up PushFileService types.
Bytes/Unicode mixing caused a hang, so prevent bytes entirely.
6 years ago
David Wilson 5521945bd2 ansible: temporary files take 5. 6 years ago
David Wilson e241081cae ansible: stop sharing target temp_dir in runner.
This cannot work with delegate_to, since delegate_to permits multiple
concurrent tasks to be executing on the same target.
6 years ago
David Wilson 43d9815f6d ansible: use CallChain everywhere.
This replaces the 'dump to logger' behaviour of pipelined calls from
before with a call chain that returns any exception on next synchronized
call.
6 years ago
David Wilson 6c4b01642c ansible: don't crash when adhoc tries to run a missing module.
ansible-playbook prints a separate error during parsing stage, adhoc
performs no such check.
6 years ago
David Wilson 2c74eac19a issue #291: more Ansible-compatible script invocation
When running any kind of script, rewrite the hashbang like Ansible does,
but subsequently ignore it and explicitly use a fragment of shell from
the ansible_*_interpreter variable to call the interpreter, just like
Ansible does.

This fixes hashbangs containing '/usr/bin/env A=1 bash' on Linux, where
putting that into a hashbang line results in an infinite loop.
6 years ago
David Wilson e39c602fd3 issue #291: support UNIX hashbang syntax for ansible_*_interpreter.
Closes #291.
6 years ago
David Wilson d8e0c9e12c issue #297: local commands must execute with WorkerProcess environment. 6 years ago
David Wilson 012745efea issue #297: local actions must execute with fixed directory.
Local actions must execute in the the parent directory of the playbook
that defines the action.
6 years ago
David Wilson 5b03e06457 issue #294: ansible: fix mixed vanilla/Mitogen runs.
Don't bother trying to understand what damage PluginLoader has done to
ansible.plugins.* namespace, just ask it for the base class instead.
6 years ago
David Wilson 410016ff47 Initial Python 3.x port work.
* ansible: use unicode_literals everywhere since it only needs to be
  compatible back to 2.6.
* compat/collections.py: delete this entirely and rip out the parts of
  functools that require it.
* Introduce serializable Kwargs dict subclass that translates keys to
  Unicode on instantiation.
* enable_debug_logging() must set _v/_vv globals.
* cStringIO does not exist in 3.x.
* Treat IOLogger and LogForwarder input as latin-1.
* Avoid ResourceWarnings in first stage by explicitly closing fps.
* Fix preamble_size.py syntax errors.
6 years ago
David Wilson 7853b74e7f issue #280: put 'dnf' on the always fork list 7 years ago
David Wilson 08538d327b ansible: don't write failed job result after async timeout.
The failed job result is likely to be "interrupted system call", and we
don't want that to overwrite the SIGALRM handler's "the task timed out",
so just discard it.
7 years ago
David Wilson 3994f1b30a ansible: implment async job time limit. 7 years ago
David Wilson e35694acd5 ansible: flake8 fixes. 7 years ago
David Wilson caffaa79f7 issue #186: rework async/forked tasks again.
The controller must know the ID of the forked child in order to
propagate dependencies to it, so forking+starting the module run cannot
happen entirely on the target, without some additional mechanism to
wait-and-repropagate the deps as they arrive on the target.

Rework things so that init_child() also handles starting the fork parent,
and returns it along with the context's home directory in a single round
trip.

Now master knows the identity of the fork parent, it can directly create
fork children and call run_module_async() in them. This necessitates 2
roundtrips to start an asynchronous task.

This whole thing sucks and entirely needs simplified, but for now things
almost work, so keeping it.

connection.py:
  * Expect ContextService to return the entire dict return value of
    init_child(). Store the fork_contxt from the return value.

planner.py:
  * Rework Planner to store the invocation as an instance attribute, to
    simplify method calls.
  * Add Planner.get_push_files() and Planner.get_module_deps().
  * Add _propagate_deps() which takes a Planner and ensures the deps it
    describes are sent to a (non forked or forked) context.
  * Move async task logic out of target.py and into invoke() /
    _invoke_*().

process.py:
  * Services no longer need references to each other. planner.py handles
    sending module deps with one extra RPC.

services.py:
  * Return "init_child_result" key instead of simple "home_dir" key.
  * Get rid of dep propagation from ModuleDepService, it lives in
    planner.py now.

target.py:
  * Get rid of async task start logic, lives in planner.py now.
7 years ago
David Wilson 569c12a2d6 ansible: use PushFileService for module deps.
planner.py:
  * Rather than grant FileService access to a file for children, use
    PushFileService to trigger deduplicating send of the file through
    the hierarchy immediately.
  * Send the complete list of Ansible module imports to the target so
    runner.py knows which files and scripts must be loaded via
    PushFileService prior to detaching.

runner.py:
  * Teach NewStyleRunner to use the full module map to block until
    everything is loaded prior to detach().

target.py:
  * Delete old _get_file(), replace get_file() with get_small_file()
    which uses PushFileService instead.

Closes #186
7 years ago
David Wilson d9087c510b ansible: move FileService into mitogen.service. 7 years ago
David Wilson fdbd954113 ansible: preload built-in modules in ModuleDepScanner.
For "ansible -m setup" over a 25ms link, avoids 65 roundtrips and
reduces runtime from 5.7s to 4.1s (-28%).

For "ansible -m setup" over a simulated 250 ms link, reduces runtime
from m27.015s to 0m8.254s (-69%).
7 years ago
David Wilson 3b0addcfb0 service: v2. Closes #213 7 years ago
David Wilson bb61745a1a issue #217: pass through non-custom module utils to regular importer.
This may come back to bite later, but in the meantime it avoids shipping
up to 12KiB of junk metadata for every single task invocation.

For detachment (aka. async), we must ensure the target has two types of
preloads completed (modules and module_utils files) before detaching.
7 years ago
David Wilson 30034877a5 issue #217: ansible: working, if extremely inefficient implementation 7 years ago
David Wilson 81b62d9a1a issue #217: ansible: beginnings of ModuleDepService. 7 years ago
David Wilson 6a17042020 issue #217: ansible: merge duplicate ReplacerPlanner. 7 years ago
David Wilson f737ff5276 ansible: stop passing through remote_tmp variable
Ansiballz does not use remote_tmp so neither should we, per #239.
7 years ago
David Wilson 8c3b1fcf15 ansible: disable script interpreter processing for new-style
Re: #199
7 years ago
David Wilson c5fe817db2 ansible: tidy up log repr slightly 7 years ago
David Wilson 85e1f5f515 ansible: remove JobResultService, more compatible async jobs; closes #191.
And by "compatible" I mean "terrible". This does not implement async job
timeouts, but I'm not going to bother, upstream async implementation is
so buggy and inconsistent it resists even having its behaviour captured
in tests.
7 years ago
David Wilson f4d791662e ansible: better planner logging, try again 7 years ago
David Wilson a9a2793a49 tests: yep more debug for Travis 7 years ago
David Wilson 1fa377152d ansible: extra planner log messages. 7 years ago
David Wilson c8ce3059d1 ansible: make FileService register method synchronous
On Travis it seems it is possible for the fetch message to reach
FileService before the register message is fully processed.
7 years ago
David Wilson 4a61527774 ansible: more docstrings and call_async() 7 years ago
David Wilson 135b3738ba ansible: don't wait on FileService response
Any (unlikely) execption will show up in the debug logs.
7 years ago
David Wilson 3613162bc0 ansible: enable forking when requested and for async jobs.
Closes #105.
References #155.

mitogen/service.py:
    Refactor services to support individually exposed methods with
    different security policies for each method.

    - @mitogen.service.expose() to expose a method and set its policy
    - @mitogen.service.arg_spec() to validate input.
    - Require basic service message format to be a tuple of
      `(method, kwargs)`, where kwargs is always a dict.
    - Update DeduplicatingService to match the new scheme.

ansible_mitogen/connection.py:
    - Rename 'method' to 'method_name' to disambiguate it from the
      service.call()'s method= argument.

ansible_mitogen/planner.py:
    - Generate an ID for every job, sync or not, and fetch job results
      from JobResultService rather than via the initiating function
      call's return value.
    - Planner subclasses now get to select whether their Runner should
      run in a forked process. The base implementation requests this if
      the 'mitogen_isolation_mode=fork' task variable is present.

ansible_mitogen/runner.py:
    Teach runners to deliver their result via JobResultService executing
    in their indirect parent mux process.

ansible_mitogen/plugins/actions/mitogen_async_status.py:
    Split the implementation up into methods, and more compatibly
    emulate Ansible's existing output.

ansible_mitogen/process.py:
    Mux processes now host JobResultService.

ansible_mitogen/services.py:
    Update existing services to the new mitogen.service scheme, and
    implement JobResultService:

    * listen() method for synchronous jobs. planner.invoke() registers a
      Sender with the service prior to invoking the job, then sleeps
      waiting for the service to write the job result to the
      corresponding Receiver.

    * Non-blocking get() method for implementing mitogen_async_status
      action.

    * Child-accessible push() method for delivering task results.

ansible_mitogen/target.py:
    New helpers for spawning a virginal subprocess on startup, from
    which asynchronous and mitogen_task_isolation=fork jobs are forked.
    Necessary to avoid a task inheriting potentially
    polluted/monkey-patched parent environment, since remaining jobs
    continue to run in the original child process.

docs/ansible.rst:
    Add/merge/remove some behaviours/risks.

tests/ansible/integration:
    New tests for forking/async.
7 years ago
David Wilson 71057c78f9 ansible: rename helpers.py to target.py, to reflect its purpose 7 years ago
David Wilson e0381606af Ensure remote_tmp is respected everywhere.
Logic is still somewhat different from Ansible: we don't have to care
about sudo/non-sudo cases, etc.
7 years ago
David Wilson 0e648dbd53 ansible: tidy up planner.py. 7 years ago
David Wilson fe9bf1d81d ansible: match Ansible behaviour when script lacks interpreter line. 7 years ago
David Wilson 047458a8b3 "examples": start adding structure to regression tests. 7 years ago
David Wilson 470d8399a3 ansible: document planner.Planner. 7 years ago
David Wilson 822502125d issue #106: 2.3.x compatible get_shebang-alike. 7 years ago
David Wilson a98a51a328 issue #106: handle JSONARGS modules too. 7 years ago
David Wilson 8cc20856a8 issue #106: support custom new-style modules + 'reexec by default'
Rather than assume any structure about the Python code:

* Delete the exit_json/fail_json monkeypatches.
* Patch SystemExit rather than a magic monkeypatch-thrown exception
* Setup fake cStringIO stdin, stdout, stderr and return those along with
  SystemExit exit status
* Setup _ANSIBLE_ARGS as we used to, since we still want to override
  that with '{}' to prevent accidental import hangs, but also provide
  the same string via sys.stdin.
* Compile the module bytecode once and re-execute it for every
  invocation. May change this back again later, once some benchmarks are
  done.
* Remove the fixups stuff for now, it's handled by ^ above.

Should support any "somewhat new style" Python module, including those
that just give up and dump stuff to stdout directly.
7 years ago
David Wilson a954d54644 issue #106: support old-style too. 7 years ago
David Wilson 16b64392e2 issue #106: support WANT_JSON modules. 7 years ago
David Wilson 0dd5e04eae issue #106: partially working BinaryRunner/Planner.
Refactor planner.py to look a lot more like runner.py. This 'structural
cutpaste' looks messy -- probably we can simplify this code, even though
it's pretty simple already.
7 years ago