Fix tests
All checks were successful
CI / test (push) Successful in 5m7s
Lint / test (push) Successful in 29s
Trivy / test (push) Successful in 18s

This commit is contained in:
Miguel Jacq 2025-12-29 16:35:21 +11:00
parent 043802e800
commit 081739fd19
Signed by: mig5
GPG key ID: 59B3F0C24135C6A9
6 changed files with 457 additions and 278 deletions

View file

@ -390,9 +390,7 @@ def _render_generic_files_tasks(
# Using first_found makes roles work in both modes: # Using first_found makes roles work in both modes:
# - site-mode: inventory/host_vars/<host>/<role>/.files/... # - site-mode: inventory/host_vars/<host>/<role>/.files/...
# - non-site: roles/<role>/files/... # - non-site: roles/<role>/files/...
return f""" return f"""- name: Deploy any systemd unit files (templates)
- name: Deploy any systemd unit files (templates)
ansible.builtin.template: ansible.builtin.template:
src: "{{{{ item.src_rel }}}}.j2" src: "{{{{ item.src_rel }}}}.j2"
dest: "{{{{ item.dest }}}}" dest: "{{{{ item.dest }}}}"
@ -475,9 +473,7 @@ def _render_install_packages_tasks(role: str, var_prefix: str) -> str:
generic `package` module. This keeps generated roles usable on both generic `package` module. This keeps generated roles usable on both
Debian-like and RPM-like systems. Debian-like and RPM-like systems.
""" """
return f""" return f"""- name: Install packages for {role} (APT)
- name: Install packages for {role} (APT)
ansible.builtin.apt: ansible.builtin.apt:
name: "{{{{ {var_prefix}_packages | default([]) }}}}" name: "{{{{ {var_prefix}_packages | default([]) }}}}"
state: present state: present
@ -995,7 +991,7 @@ Generated non-system user accounts and SSH public material.
else: else:
_write_role_defaults(role_dir, vars_map) _write_role_defaults(role_dir, vars_map)
tasks = """---\n""" + _render_generic_files_tasks( tasks = "---\n" + _render_generic_files_tasks(
var_prefix, include_restart_notify=False var_prefix, include_restart_notify=False
) )
with open( with open(
@ -1297,7 +1293,7 @@ DNF/YUM configuration harvested from the system (repos, config files, and RPM GP
else: else:
_write_role_defaults(role_dir, vars_map) _write_role_defaults(role_dir, vars_map)
tasks = """---\n""" + _render_generic_files_tasks( tasks = "---\n" + _render_generic_files_tasks(
var_prefix, include_restart_notify=False var_prefix, include_restart_notify=False
) )
with open( with open(
@ -1663,8 +1659,7 @@ User-requested extra file harvesting.
) )
task_parts.append( task_parts.append(
f""" f"""- name: Probe whether systemd unit exists and is manageable
- name: Probe whether systemd unit exists and is manageable
ansible.builtin.systemd: ansible.builtin.systemd:
name: "{{{{ {var_prefix}_unit_name }}}}" name: "{{{{ {var_prefix}_unit_name }}}}"
check_mode: true check_mode: true

View file

@ -104,7 +104,7 @@ def list_manual_packages() -> List[str]:
if pkgs: if pkgs:
return _dedupe(pkgs) return _dedupe(pkgs)
# Fallback: human-oriented output. # Fallback
rc, out = _run( rc, out = _run(
["dnf", "-q", "history", "userinstalled"], allow_fail=True, merge_err=True ["dnf", "-q", "history", "userinstalled"], allow_fail=True, merge_err=True
) )

View file

@ -18,65 +18,106 @@ def test_diff_includes_usr_local_custom_files(tmp_path: Path):
new = tmp_path / "new" new = tmp_path / "new"
old_state = { old_state = {
"host": {"hostname": "h1", "os": "debian"}, "schema_version": 3,
"users": { "host": {"hostname": "h1", "os": "debian", "pkg_backend": "dpkg"},
"role_name": "users", "inventory": {
"users": [], "packages": {
"managed_files": [], "curl": {
"excluded": [], "version": "1.0",
"notes": [], "arches": [],
}, "installations": [{"version": "1.0", "arch": "amd64"}],
"services": [], "observed_via": [{"kind": "user_installed"}],
"package_roles": [], "roles": [],
"manual_packages": ["curl"],
"manual_packages_skipped": [],
"etc_custom": {
"role_name": "etc_custom",
"managed_files": [],
"excluded": [],
"notes": [],
},
"usr_local_custom": {
"role_name": "usr_local_custom",
"managed_files": [
{
"path": "/usr/local/etc/myapp.conf",
"src_rel": "usr/local/etc/myapp.conf",
"owner": "root",
"group": "root",
"mode": "0644",
"reason": "usr_local_etc_custom",
} }
], }
"excluded": [], },
"notes": [], "roles": {
"users": {
"role_name": "users",
"users": [],
"managed_files": [],
"excluded": [],
"notes": [],
},
"services": [],
"packages": [],
"apt_config": {
"role_name": "apt_config",
"managed_files": [],
"excluded": [],
"notes": [],
},
"etc_custom": {
"role_name": "etc_custom",
"managed_files": [],
"excluded": [],
"notes": [],
},
"usr_local_custom": {
"role_name": "usr_local_custom",
"managed_files": [
{
"path": "/usr/local/etc/myapp.conf",
"src_rel": "usr/local/etc/myapp.conf",
"owner": "root",
"group": "root",
"mode": "0644",
"reason": "usr_local_etc_custom",
}
],
"excluded": [],
"notes": [],
},
"extra_paths": {
"role_name": "extra_paths",
"include_patterns": [],
"exclude_patterns": [],
"managed_files": [],
"excluded": [],
"notes": [],
},
}, },
} }
new_state = { new_state = {
**old_state, **old_state,
"manual_packages": ["curl", "htop"], "inventory": {
"usr_local_custom": { "packages": {
"role_name": "usr_local_custom", **old_state["inventory"]["packages"],
"managed_files": [ "htop": {
{ "version": "3.0",
"path": "/usr/local/etc/myapp.conf", "arches": [],
"src_rel": "usr/local/etc/myapp.conf", "installations": [{"version": "3.0", "arch": "amd64"}],
"owner": "root", "observed_via": [{"kind": "user_installed"}],
"group": "root", "roles": [],
"mode": "0644",
"reason": "usr_local_etc_custom",
}, },
{ }
"path": "/usr/local/bin/myscript", },
"src_rel": "usr/local/bin/myscript", "roles": {
"owner": "root", **old_state["roles"],
"group": "root", "usr_local_custom": {
"mode": "0755", "role_name": "usr_local_custom",
"reason": "usr_local_bin_script", "managed_files": [
}, {
], "path": "/usr/local/etc/myapp.conf",
"excluded": [], "src_rel": "usr/local/etc/myapp.conf",
"notes": [], "owner": "root",
"group": "root",
"mode": "0644",
"reason": "usr_local_etc_custom",
},
{
"path": "/usr/local/bin/myscript",
"src_rel": "usr/local/bin/myscript",
"owner": "root",
"group": "root",
"mode": "0755",
"reason": "usr_local_bin_script",
},
],
"excluded": [],
"notes": [],
},
}, },
} }

View file

@ -30,6 +30,7 @@ class FakeBackend:
owner_fn, owner_fn,
modified_by_pkg: dict[str, dict[str, str]] | None = None, modified_by_pkg: dict[str, dict[str, str]] | None = None,
pkg_config_prefixes: tuple[str, ...] = ("/etc/apt/",), pkg_config_prefixes: tuple[str, ...] = ("/etc/apt/",),
installed: dict[str, list[dict[str, str]]] | None = None,
): ):
self.name = name self.name = name
self.pkg_config_prefixes = pkg_config_prefixes self.pkg_config_prefixes = pkg_config_prefixes
@ -40,6 +41,7 @@ class FakeBackend:
self._manual = manual_pkgs self._manual = manual_pkgs
self._owner_fn = owner_fn self._owner_fn = owner_fn
self._modified_by_pkg = modified_by_pkg or {} self._modified_by_pkg = modified_by_pkg or {}
self._installed = installed or {}
def build_etc_index(self): def build_etc_index(self):
return ( return (
@ -55,6 +57,14 @@ class FakeBackend:
def list_manual_packages(self): def list_manual_packages(self):
return list(self._manual) return list(self._manual)
def installed_packages(self):
"""Return mapping package -> installations.
The real backends return:
{"pkg": [{"version": "...", "arch": "..."}, ...]}
"""
return dict(self._installed)
def specific_paths_for_hints(self, hints: set[str]): def specific_paths_for_hints(self, hints: set[str]):
return [] return []
@ -214,26 +224,36 @@ def test_harvest_dedup_manual_packages_and_builds_etc_custom(
state_path = h.harvest(str(bundle), policy=AllowAllPolicy()) state_path = h.harvest(str(bundle), policy=AllowAllPolicy())
st = json.loads(Path(state_path).read_text(encoding="utf-8")) st = json.loads(Path(state_path).read_text(encoding="utf-8"))
assert "openvpn" in st["manual_packages"] inv = st["inventory"]["packages"]
assert "curl" in st["manual_packages"] assert "openvpn" in inv
assert "openvpn" in st["manual_packages_skipped"] assert "curl" in inv
assert all(pr["package"] != "openvpn" for pr in st["package_roles"])
assert any(pr["package"] == "curl" for pr in st["package_roles"]) # openvpn is managed by the service role, so it should NOT appear as a package role.
pkg_roles = st["roles"]["packages"]
assert all(pr["package"] != "openvpn" for pr in pkg_roles)
assert any(pr["package"] == "curl" for pr in pkg_roles)
# Inventory provenance: openvpn should be observed via systemd unit.
openvpn_obs = inv["openvpn"]["observed_via"]
assert any(
o.get("kind") == "systemd_unit" and o.get("ref") == "openvpn.service"
for o in openvpn_obs
)
# Service role captured modified conffile # Service role captured modified conffile
svc = st["services"][0] svc = st["roles"]["services"][0]
assert svc["unit"] == "openvpn.service" assert svc["unit"] == "openvpn.service"
assert "openvpn" in svc["packages"] assert "openvpn" in svc["packages"]
assert any(mf["path"] == "/etc/openvpn/server.conf" for mf in svc["managed_files"]) assert any(mf["path"] == "/etc/openvpn/server.conf" for mf in svc["managed_files"])
# Unowned /etc/default/keyboard is attributed to etc_custom only # Unowned /etc/default/keyboard is attributed to etc_custom only
etc_custom = st["etc_custom"] etc_custom = st["roles"]["etc_custom"]
assert any( assert any(
mf["path"] == "/etc/default/keyboard" for mf in etc_custom["managed_files"] mf["path"] == "/etc/default/keyboard" for mf in etc_custom["managed_files"]
) )
# /usr/local content is attributed to usr_local_custom # /usr/local content is attributed to usr_local_custom
ul = st["usr_local_custom"] ul = st["roles"]["usr_local_custom"]
assert any(mf["path"] == "/usr/local/etc/myapp.conf" for mf in ul["managed_files"]) assert any(mf["path"] == "/usr/local/etc/myapp.conf" for mf in ul["managed_files"])
assert any(mf["path"] == "/usr/local/bin/myscript" for mf in ul["managed_files"]) assert any(mf["path"] == "/usr/local/bin/myscript" for mf in ul["managed_files"])
assert all(mf["path"] != "/usr/local/bin/readme.txt" for mf in ul["managed_files"]) assert all(mf["path"] != "/usr/local/bin/readme.txt" for mf in ul["managed_files"])
@ -338,10 +358,12 @@ def test_shared_cron_snippet_prefers_matching_role_over_lexicographic(
st = json.loads(Path(state_path).read_text(encoding="utf-8")) st = json.loads(Path(state_path).read_text(encoding="utf-8"))
# Cron snippet should end up attached to the ntpsec role, not apparmor. # Cron snippet should end up attached to the ntpsec role, not apparmor.
svc_ntpsec = next(s for s in st["services"] if s["role_name"] == "ntpsec") svc_ntpsec = next(s for s in st["roles"]["services"] if s["role_name"] == "ntpsec")
assert any(mf["path"] == "/etc/cron.d/ntpsec" for mf in svc_ntpsec["managed_files"]) assert any(mf["path"] == "/etc/cron.d/ntpsec" for mf in svc_ntpsec["managed_files"])
svc_apparmor = next(s for s in st["services"] if s["role_name"] == "apparmor") svc_apparmor = next(
s for s in st["roles"]["services"] if s["role_name"] == "apparmor"
)
assert all( assert all(
mf["path"] != "/etc/cron.d/ntpsec" for mf in svc_apparmor["managed_files"] mf["path"] != "/etc/cron.d/ntpsec" for mf in svc_apparmor["managed_files"]
) )

View file

@ -24,44 +24,78 @@ def test_manifest_uses_jinjaturtle_templates_and_does_not_copy_raw(
) )
state = { state = {
"host": {"hostname": "test", "os": "debian"}, "schema_version": 3,
"users": { "host": {"hostname": "test", "os": "debian", "pkg_backend": "dpkg"},
"role_name": "users", "inventory": {
"users": [], "packages": {
"managed_files": [], "foo": {
"excluded": [], "version": "1.0",
"notes": [], "arches": [],
"installations": [{"version": "1.0", "arch": "amd64"}],
"observed_via": [{"kind": "systemd_unit", "ref": "foo.service"}],
"roles": ["foo"],
}
}
}, },
"etc_custom": { "roles": {
"role_name": "etc_custom", "users": {
"managed_files": [], "role_name": "users",
"excluded": [], "users": [],
"notes": [], "managed_files": [],
},
"services": [
{
"unit": "foo.service",
"role_name": "foo",
"packages": ["foo"],
"active_state": "inactive",
"sub_state": "dead",
"unit_file_state": "disabled",
"condition_result": "no",
"managed_files": [
{
"path": "/etc/foo.ini",
"src_rel": "etc/foo.ini",
"owner": "root",
"group": "root",
"mode": "0644",
"reason": "modified_conffile",
}
],
"excluded": [], "excluded": [],
"notes": [], "notes": [],
} },
], "services": [
"package_roles": [], {
"unit": "foo.service",
"role_name": "foo",
"packages": ["foo"],
"active_state": "inactive",
"sub_state": "dead",
"unit_file_state": "disabled",
"condition_result": "no",
"managed_files": [
{
"path": "/etc/foo.ini",
"src_rel": "etc/foo.ini",
"owner": "root",
"group": "root",
"mode": "0644",
"reason": "modified_conffile",
}
],
"excluded": [],
"notes": [],
}
],
"packages": [],
"apt_config": {
"role_name": "apt_config",
"managed_files": [],
"excluded": [],
"notes": [],
},
"etc_custom": {
"role_name": "etc_custom",
"managed_files": [],
"excluded": [],
"notes": [],
},
"usr_local_custom": {
"role_name": "usr_local_custom",
"managed_files": [],
"excluded": [],
"notes": [],
},
"extra_paths": {
"role_name": "extra_paths",
"include_patterns": [],
"exclude_patterns": [],
"managed_files": [],
"excluded": [],
"notes": [],
},
},
} }
bundle.mkdir(parents=True, exist_ok=True) bundle.mkdir(parents=True, exist_ok=True)

View file

@ -13,95 +13,136 @@ def test_manifest_writes_roles_and_playbook_with_clean_when(tmp_path: Path):
) )
state = { state = {
"host": {"hostname": "test", "os": "debian"}, "schema_version": 3,
"users": { "host": {"hostname": "test", "os": "debian", "pkg_backend": "dpkg"},
"role_name": "users", "inventory": {
"users": [ "packages": {
"foo": {
"version": "1.0",
"arches": [],
"installations": [{"version": "1.0", "arch": "amd64"}],
"observed_via": [{"kind": "systemd_unit", "ref": "foo.service"}],
"roles": ["foo"],
},
"curl": {
"version": "8.0",
"arches": [],
"installations": [{"version": "8.0", "arch": "amd64"}],
"observed_via": [{"kind": "package_role", "ref": "curl"}],
"roles": ["curl"],
},
}
},
"roles": {
"users": {
"role_name": "users",
"users": [
{
"name": "alice",
"uid": 1000,
"gid": 1000,
"gecos": "Alice",
"home": "/home/alice",
"shell": "/bin/bash",
"primary_group": "alice",
"supplementary_groups": ["docker", "qubes"],
}
],
"managed_files": [],
"excluded": [],
"notes": [],
},
"services": [
{ {
"name": "alice", "unit": "foo.service",
"uid": 1000, "role_name": "foo",
"gid": 1000, "packages": ["foo"],
"gecos": "Alice", "active_state": "inactive",
"home": "/home/alice", "sub_state": "dead",
"shell": "/bin/bash", "unit_file_state": "enabled",
"primary_group": "alice", "condition_result": "no",
"supplementary_groups": ["docker", "qubes"], "managed_files": [
{
"path": "/etc/foo.conf",
"src_rel": "etc/foo.conf",
"owner": "root",
"group": "root",
"mode": "0644",
"reason": "modified_conffile",
}
],
"excluded": [],
"notes": [],
} }
], ],
"managed_files": [], "packages": [
"excluded": [],
"notes": [],
},
"etc_custom": {
"role_name": "etc_custom",
"managed_files": [
{ {
"path": "/etc/default/keyboard", "package": "curl",
"src_rel": "etc/default/keyboard", "role_name": "curl",
"owner": "root", "managed_files": [],
"group": "root", "excluded": [],
"mode": "0644", "notes": [],
"reason": "custom_unowned",
} }
], ],
"excluded": [], "apt_config": {
"notes": [], "role_name": "apt_config",
}, "managed_files": [],
"usr_local_custom": { "excluded": [],
"role_name": "usr_local_custom", "notes": [],
"managed_files": [ },
{ "dnf_config": {
"path": "/usr/local/etc/myapp.conf", "role_name": "dnf_config",
"src_rel": "usr/local/etc/myapp.conf", "managed_files": [],
"owner": "root", "excluded": [],
"group": "root", "notes": [],
"mode": "0644", },
"reason": "usr_local_etc_custom", "etc_custom": {
}, "role_name": "etc_custom",
{
"path": "/usr/local/bin/myscript",
"src_rel": "usr/local/bin/myscript",
"owner": "root",
"group": "root",
"mode": "0755",
"reason": "usr_local_bin_script",
},
],
"excluded": [],
"notes": [],
},
"services": [
{
"unit": "foo.service",
"role_name": "foo",
"packages": ["foo"],
"active_state": "inactive",
"sub_state": "dead",
"unit_file_state": "enabled",
"condition_result": "no",
"managed_files": [ "managed_files": [
{ {
"path": "/etc/foo.conf", "path": "/etc/default/keyboard",
"src_rel": "etc/foo.conf", "src_rel": "etc/default/keyboard",
"owner": "root", "owner": "root",
"group": "root", "group": "root",
"mode": "0644", "mode": "0644",
"reason": "modified_conffile", "reason": "custom_unowned",
} }
], ],
"excluded": [], "excluded": [],
"notes": [], "notes": [],
} },
], "usr_local_custom": {
"package_roles": [ "role_name": "usr_local_custom",
{ "managed_files": [
"package": "curl", {
"role_name": "curl", "path": "/usr/local/etc/myapp.conf",
"src_rel": "usr/local/etc/myapp.conf",
"owner": "root",
"group": "root",
"mode": "0644",
"reason": "usr_local_etc_custom",
},
{
"path": "/usr/local/bin/myscript",
"src_rel": "usr/local/bin/myscript",
"owner": "root",
"group": "root",
"mode": "0755",
"reason": "usr_local_bin_script",
},
],
"excluded": [],
"notes": [],
},
"extra_paths": {
"role_name": "extra_paths",
"include_patterns": [],
"exclude_patterns": [],
"managed_files": [], "managed_files": [],
"excluded": [], "excluded": [],
"notes": [], "notes": [],
} },
], },
} }
bundle.mkdir(parents=True, exist_ok=True) bundle.mkdir(parents=True, exist_ok=True)
@ -189,68 +230,102 @@ def test_manifest_site_mode_creates_host_inventory_and_raw_files(tmp_path: Path)
) )
state = { state = {
"host": {"hostname": "test", "os": "debian"}, "schema_version": 3,
"users": { "host": {"hostname": "test", "os": "debian", "pkg_backend": "dpkg"},
"role_name": "users", "inventory": {
"users": [], "packages": {
"managed_files": [], "foo": {
"excluded": [], "version": "1.0",
"notes": [], "arches": [],
"installations": [{"version": "1.0", "arch": "amd64"}],
"observed_via": [{"kind": "systemd_unit", "ref": "foo.service"}],
"roles": ["foo"],
}
}
}, },
"etc_custom": { "roles": {
"role_name": "etc_custom", "users": {
"managed_files": [ "role_name": "users",
"users": [],
"managed_files": [],
"excluded": [],
"notes": [],
},
"services": [
{ {
"path": "/etc/default/keyboard", "unit": "foo.service",
"src_rel": "etc/default/keyboard", "role_name": "foo",
"owner": "root", "packages": ["foo"],
"group": "root", "active_state": "active",
"mode": "0644", "sub_state": "running",
"reason": "custom_unowned", "unit_file_state": "enabled",
"condition_result": "yes",
"managed_files": [
{
"path": "/etc/foo.conf",
"src_rel": "etc/foo.conf",
"owner": "root",
"group": "root",
"mode": "0644",
"reason": "modified_conffile",
}
],
"excluded": [],
"notes": [],
} }
], ],
"excluded": [], "packages": [],
"notes": [], "apt_config": {
}, "role_name": "apt_config",
"usr_local_custom": { "managed_files": [],
"role_name": "usr_local_custom", "excluded": [],
"managed_files": [ "notes": [],
{ },
"path": "/usr/local/etc/myapp.conf", "dnf_config": {
"src_rel": "usr/local/etc/myapp.conf", "role_name": "dnf_config",
"owner": "root", "managed_files": [],
"group": "root", "excluded": [],
"mode": "0644", "notes": [],
"reason": "usr_local_etc_custom", },
} "etc_custom": {
], "role_name": "etc_custom",
"excluded": [],
"notes": [],
},
"services": [
{
"unit": "foo.service",
"role_name": "foo",
"packages": ["foo"],
"active_state": "active",
"sub_state": "running",
"unit_file_state": "enabled",
"condition_result": "yes",
"managed_files": [ "managed_files": [
{ {
"path": "/etc/foo.conf", "path": "/etc/default/keyboard",
"src_rel": "etc/foo.conf", "src_rel": "etc/default/keyboard",
"owner": "root", "owner": "root",
"group": "root", "group": "root",
"mode": "0644", "mode": "0644",
"reason": "modified_conffile", "reason": "custom_unowned",
} }
], ],
"excluded": [], "excluded": [],
"notes": [], "notes": [],
} },
], "usr_local_custom": {
"package_roles": [], "role_name": "usr_local_custom",
"managed_files": [
{
"path": "/usr/local/etc/myapp.conf",
"src_rel": "usr/local/etc/myapp.conf",
"owner": "root",
"group": "root",
"mode": "0644",
"reason": "usr_local_etc_custom",
}
],
"excluded": [],
"notes": [],
},
"extra_paths": {
"role_name": "extra_paths",
"include_patterns": [],
"exclude_patterns": [],
"managed_files": [],
"excluded": [],
"notes": [],
},
},
} }
bundle.mkdir(parents=True, exist_ok=True) bundle.mkdir(parents=True, exist_ok=True)
@ -337,58 +412,70 @@ def test_manifest_includes_dnf_config_role_when_present(tmp_path: Path):
) )
state = { state = {
"schema_version": 3,
"host": {"hostname": "test", "os": "redhat", "pkg_backend": "rpm"}, "host": {"hostname": "test", "os": "redhat", "pkg_backend": "rpm"},
"users": { "inventory": {
"role_name": "users", "packages": {
"users": [], "dnf": {
"managed_files": [], "version": "4.0",
"excluded": [], "arches": [],
"notes": [], "installations": [{"version": "4.0", "arch": "x86_64"}],
}, "observed_via": [{"kind": "dnf_config"}],
"services": [], "roles": [],
"package_roles": [],
"manual_packages": [],
"manual_packages_skipped": [],
"apt_config": {
"role_name": "apt_config",
"managed_files": [],
"excluded": [],
"notes": [],
},
"dnf_config": {
"role_name": "dnf_config",
"managed_files": [
{
"path": "/etc/dnf/dnf.conf",
"src_rel": "etc/dnf/dnf.conf",
"owner": "root",
"group": "root",
"mode": "0644",
"reason": "dnf_config",
} }
], }
"excluded": [],
"notes": [],
}, },
"etc_custom": { "roles": {
"role_name": "etc_custom", "users": {
"managed_files": [], "role_name": "users",
"excluded": [], "users": [],
"notes": [], "managed_files": [],
}, "excluded": [],
"usr_local_custom": { "notes": [],
"role_name": "usr_local_custom", },
"managed_files": [], "services": [],
"excluded": [], "packages": [],
"notes": [], "apt_config": {
}, "role_name": "apt_config",
"extra_paths": { "managed_files": [],
"role_name": "extra_paths", "excluded": [],
"include_patterns": [], "notes": [],
"exclude_patterns": [], },
"managed_files": [], "dnf_config": {
"excluded": [], "role_name": "dnf_config",
"notes": [], "managed_files": [
{
"path": "/etc/dnf/dnf.conf",
"src_rel": "etc/dnf/dnf.conf",
"owner": "root",
"group": "root",
"mode": "0644",
"reason": "dnf_config",
}
],
"excluded": [],
"notes": [],
},
"etc_custom": {
"role_name": "etc_custom",
"managed_files": [],
"excluded": [],
"notes": [],
},
"usr_local_custom": {
"role_name": "usr_local_custom",
"managed_files": [],
"excluded": [],
"notes": [],
},
"extra_paths": {
"role_name": "extra_paths",
"include_patterns": [],
"exclude_patterns": [],
"managed_files": [],
"excluded": [],
"notes": [],
},
}, },
} }