module

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

apply_file_attrs(path : String, check_mode : Bool) : Tuple(Bool, PluginResult | Nil)

Returns {changed, failure}: failure a failed PluginResult when an owner:/group: could not be applied, nil otherwise.

Source
attrs_failure(path : String, msg : String, detail : String) : PluginResult

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.

Source
before_gid(path : String) : Int32 | Nil
Source
before_mode(path : String) : Int32 | Nil
Source
before_uid(path : String) : Int32 | Nil
Source
chgrp(path : String, gid : Int32) : PluginResult | Nil
Source
chmod(path : String, mode : String) : Nil
Source
chown(path : String, uid : Int32) : PluginResult | Nil

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.

Source
os_error_text(ex : File::Error, path : String) : String

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.

Source
parse_mode(mode : String) : Int32 | Nil

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.

Source
raw_stat(path : String) : LibC::Stat | Nil
Source