Add sysctl detection

This commit is contained in:
Miguel Jacq 2026-06-16 14:23:44 +10:00
parent 3c19ae54b2
commit 9546e1b8ed
Signed by: mig5
GPG key ID: 03906B4110AAD3B8
11 changed files with 544 additions and 2 deletions

View file

@ -303,3 +303,48 @@ def test_service_role_names_do_not_collide_with_singleton_roles():
assert _role_name_from_unit("flatpak.service") == "service_flatpak"
assert _role_name_from_unit("users.service") == "service_users"
assert _role_name_from_unit("nginx.service") == "nginx"
def test_parse_sysctl_a_output_keeps_persistable_values(monkeypatch):
monkeypatch.setattr(
h,
"_sysctl_key_is_persistable",
lambda key: (key != "kernel.hostname", "test"),
)
params, skipped = h._parse_sysctl_a_output(
"net.ipv4.ip_forward = 1\n"
"kernel.hostname = example\n"
"malformed line\n"
"dev.cdrom.info = \n"
"net.ipv4.ip_forward = 0\n"
)
assert params == {"net.ipv4.ip_forward": "1"}
assert skipped["non_persistable"] == 1
assert skipped["malformed"] == 1
assert skipped["empty_value"] == 1
assert skipped["duplicate"] == 1
def test_collect_sysctl_snapshot_writes_generated_artifact(monkeypatch, tmp_path: Path):
monkeypatch.setattr(
h,
"_run_capture_command",
lambda command_key, *, timeout=10: (
"net.ipv4.ip_forward = 1\nvm.swappiness = 10\n",
None,
),
)
monkeypatch.setattr(h, "_sysctl_key_is_persistable", lambda key: (True, ""))
snap = h._collect_sysctl_snapshot(str(tmp_path))
assert snap.role_name == "sysctl"
assert snap.parameters == {"net.ipv4.ip_forward": "1", "vm.swappiness": "10"}
assert len(snap.managed_files) == 1
assert snap.managed_files[0].path == "/etc/sysctl.d/99-enroll.conf"
conf = tmp_path / "artifacts" / "sysctl" / "sysctl" / "99-enroll.conf"
text = conf.read_text(encoding="utf-8")
assert "net.ipv4.ip_forward = 1" in text
assert "vm.swappiness = 10" in text

View file

@ -260,6 +260,7 @@ def test_manifest_writes_roles_and_playbook_with_clean_when(tmp_path: Path):
# Service role: systemd management should be gated on foo_manage_unit and a probe.
tasks = (out / "roles" / "foo" / "tasks" / "main.yml").read_text(encoding="utf-8")
assert "- name: Probe whether systemd unit exists and is manageable" in tasks
assert 'no_log: "{{ enroll_hide_systemd_status | default(true) | bool }}"' in tasks
assert "when: foo_manage_unit | default(false)" in tasks
assert (
"when:\n - foo_manage_unit | default(false)\n - _unit_probe is succeeded\n"
@ -618,6 +619,7 @@ def test_manifest_groups_systemd_units_into_common_role(tmp_path: Path):
assert "dest: /etc/NetworkManager/NetworkManager.conf" in defaults
tasks = (out / "roles" / "net" / "tasks" / "main.yml").read_text(encoding="utf-8")
assert "Ensure grouped unit enablement matches harvest" in tasks
assert 'no_log: "{{ enroll_hide_systemd_status | default(true) | bool }}"' in tasks
def test_manifest_fqdn_implies_no_common_roles(tmp_path: Path):
@ -1811,3 +1813,111 @@ def test_manifest_avoids_package_role_collision_with_flatpak_singleton(tmp_path)
assert (out / "roles" / "package_flatpak" / "tasks" / "main.yml").exists()
assert "role: flatpak" in playbook
assert "role: package_flatpak" in playbook
def test_manifest_writes_sysctl_role(tmp_path: Path):
bundle = tmp_path / "bundle"
out = tmp_path / "ansible"
(bundle / "artifacts" / "sysctl" / "sysctl").mkdir(parents=True, exist_ok=True)
(bundle / "artifacts" / "sysctl" / "sysctl" / "99-enroll.conf").write_text(
"net.ipv4.ip_forward = 1\n",
encoding="utf-8",
)
state = {
"schema_version": 3,
"host": {"hostname": "test", "os": "debian", "pkg_backend": "dpkg"},
"inventory": {"packages": {}},
"roles": {
"users": {
"role_name": "users",
"users": [],
"managed_files": [],
"excluded": [],
"notes": [],
},
"services": [],
"packages": [],
"apt_config": {
"role_name": "apt_config",
"managed_files": [],
"excluded": [],
"notes": [],
},
"dnf_config": {
"role_name": "dnf_config",
"managed_files": [],
"excluded": [],
"notes": [],
},
"sysctl": {
"role_name": "sysctl",
"managed_files": [
{
"path": "/etc/sysctl.d/99-enroll.conf",
"src_rel": "sysctl/99-enroll.conf",
"owner": "root",
"group": "root",
"mode": "0644",
"reason": "system_sysctl",
}
],
"parameters": {"net.ipv4.ip_forward": "1"},
"notes": ["Captured 1 live writable sysctl parameter(s)."],
},
"firewall_runtime": {
"role_name": "firewall_runtime",
"packages": [],
"ipset_save": None,
"ipset_sets": [],
"iptables_v4_save": None,
"iptables_v6_save": None,
"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 / "state.json").write_text(json.dumps(state, indent=2), encoding="utf-8")
manifest.manifest(str(bundle), str(out))
tasks = (out / "roles" / "sysctl" / "tasks" / "main.yml").read_text(
encoding="utf-8"
)
assert "dest: /etc/sysctl.d/99-enroll.conf" in tasks
assert "notify: Apply captured sysctl configuration" in tasks
handlers = (out / "roles" / "sysctl" / "handlers" / "main.yml").read_text(
encoding="utf-8"
)
assert "- -p" in handlers
assert "- /etc/sysctl.d/99-enroll.conf" in handlers
defaults = (out / "roles" / "sysctl" / "defaults" / "main.yml").read_text(
encoding="utf-8"
)
assert "sysctl_conf_src_rel: sysctl/99-enroll.conf" in defaults
assert "sysctl_ignore_apply_errors: true" in defaults
pb = (out / "playbook.yml").read_text(encoding="utf-8")
assert "role: sysctl" in pb
assert (out / "roles" / "sysctl" / "files" / "sysctl" / "99-enroll.conf").exists()