Krikri::PluginHelpers::FileAttrs
AnsibleModule.set_fs_attributes_if_different (basic.py) and
the add_path_info its result passes through - the shared protocol
layer behind add_file_common_args, for the plugins that hand-roll
their owner:/group:/mode: application instead of going through the
copy/lineinfile file-* helpers.
Own one implementation of all three details, which are easy to get subtly wrong per-plugin:
- ORDER. basic.py applies secontext, then owner, then group, then mode, then attributes. Owner before group matters: a task with both a rejected chown and a rejected chgrp reports the CHOWN one.
- FAILURE SHAPE. An unresolvable owner:/group: name fails with
"chown failed: failed to look up user X" / "chgrp failed: failed
to look up group X" (base_plugin's OwnerLookupFailure already
raises exactly those, and its run_and_capture rescue surfaces
them verbatim). A rejected chown syscall fails with the bare
"chown failed" in the fatal msg and the OSError's own text
appended in the [ERROR] block - Ansible's
fail_json(path=..., msg='chown failed', exception=ex)pair. chgrp carries no detail at all (fail_json(msg='chgrp failed')). A rejected chmod is only a warning: set_mode_if_different logs it and carries on. - WHEN. The walk runs at module-exit on an UNCHANGED result too, so an owner:/group:/mode: that drifted from the file is itself a change, and an existing path's stat fields (uid/gid/owner/group/ mode/state/size) ride along on the result either way.
Live-verified against ansible-core 2.19.11 for ini_file (kpg32 seed 32) and htpasswd.
Instance methods
Returns {changed, failure}: failure a failed PluginResult when an owner:/group: could not be applied, nil otherwise.
Ansible's fail_json(path=..., msg=..., exception=...) routes through add_path_info, so the file's stat fields ride along on the failure too - exactly as on a successful result.
Each returns nil on success and the failure Ansible reports otherwise, with the fatal msg and the [ERROR] block text split the way Ansible's own fail_json(msg=..., exception=...) pair renders them.
Formats the Errno the way Python's str(OSError) does over a BYTES path - the b'...' repr included, since that is literally what Ansible's os.lchown(b_path, ...) raises with.
Ansible parses ANY all-digit mode string as octal, leading zero or not ("640" and "0640" are identical); a symbolic mode ("u+x") has no libc equivalent and goes to the real chmod binary, exactly like copy.cr's own apply_file_attributes.