Krikri::SynchronizeActionPlugin
Inherits Krikri::PluginHelpers::StrictBoolValidation < Krikri::ActionPlugin < Reference < Object
ansible.posix.synchronize as a controller-side action plugin that computes the ENTIRE task result itself (ActionResult.final - no module binary is ever dispatched, local or remote).
This mirrors Ansible's own architecture rather than working
around this engine's: synchronize is not a normal target-side module
there either. Its action plugin munges src/dest into rsync's remote
form and hands off to a module that ultimately shells out to the
rsync CLI - and it does so from the CONTROLLER (or the delegate_to
host), with rsync making its OWN connection out to the remote end.
Dispatched here the normal way (upload a plugin binary, run it ON the
target), the rsync invocation would run on the wrong machine entirely:
push mode's src: lives on the controller, which the target host has no
path to. The rsync-arg-building/execution core itself lives in
SynchronizeRsync (plugin_helpers/synchronize_rsync.cr), shared with
plugins/synchronize.cr - the standalone binary kept for --async/
manual invocation, same split as debug/pause.
Supported and faithful: push (default; controller src -> remote dest)
and pull (remote src -> controller dest) over the play's own SSH
connection details (host/user/port, ansible_ssh_private_key_file,
dest_port: override); both-ends-local sync (ansible_connection=local
or delegate_to: localhost on a localhost task); delegate_to: localhost
(any local-transport delegate) on a NON-localhost task host -
Ansible then runs rsync on the controller with the TASK host as
rsync's own ssh remote (target:/path, no connection plugin in
between, verified live: a local-connection task host still gets
qualified by its inventory name and rsync dials it over its own
rsh); the full flag set (archive and its
toggles, checksum, compress, delete, dirs, existing_only, recursive,
links, copy_links, perms, times, owner, group, rsync_opts, rsync_path,
rsync_timeout, partial, verify_host, private_key, link_dest,
delay_updates, set_remote_user); role-relative src: dwim (handled
executor-side in resolve_role_relative_src, before this runs).
Idempotency comes from rsync itself - see SynchronizeRsync's
changed-detection comment.
Known divergence: delegate_to: to a host that is neither localhost nor the sync endpoint (Ansible runs rsync ON the delegate, connecting out to the inventory host) is not modeled - here the delegate-resolved host IS the endpoint, which covers the shapes real roles actually write apart from that one: no delegate_to:, delegate_to: localhost, and delegate_to: the task's own host.
Constants
Real failure shape: fail_json(msg=err, rc=rc, cmd=cmdstr) - fail_json's kwargs dict leads (rc, cmd), then failed/msg, and the controller appends changed then exception: registered as [rc, cmd, failed, msg, changed, exception] (exception backfilled at register via the _ansible_key_order marker - see TaskExecutor#apply_failed_key_order).
Ansible module fail_json(msg="...") with no other kwargs: failed/msg lead (fail_json's dict(failed=True, msg=msg) update), then the controller's changed/exception backfill.
Ansible module success shape: exit_json(changed=, msg=, rc=, cmd=, stdout_lines=) then the controller backfills failed: false last. The empty msg is kept (the Ansible module passes msg=out_clean explicitly, and exit_json emits the key even as "").
Instance methods
Execute action on controller Returns: modified params to send to remote plugin, or nil if action failed