431 lines
15 KiB
Python
431 lines
15 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from enroll import manifest
|
|
|
|
|
|
def _write_state(bundle: Path, state: dict) -> None:
|
|
bundle.mkdir(parents=True, exist_ok=True)
|
|
(bundle / "state.json").write_text(json.dumps(state, indent=2), encoding="utf-8")
|
|
|
|
|
|
def test_manifest_puppet_writes_control_repo_style_output(tmp_path: Path):
|
|
bundle = tmp_path / "bundle"
|
|
out = tmp_path / "puppet"
|
|
artifact = bundle / "artifacts" / "foo" / "etc" / "foo.conf"
|
|
artifact.parent.mkdir(parents=True, exist_ok=True)
|
|
artifact.write_text("setting = true\n", encoding="utf-8")
|
|
sysctl_artifact = bundle / "artifacts" / "sysctl" / "sysctl" / "99-enroll.conf"
|
|
sysctl_artifact.parent.mkdir(parents=True, exist_ok=True)
|
|
sysctl_artifact.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": [
|
|
{
|
|
"name": "alice",
|
|
"uid": 1000,
|
|
"gid": 1000,
|
|
"gecos": "Alice Example",
|
|
"home": "/home/alice",
|
|
"shell": "/bin/bash",
|
|
"primary_group": "alice",
|
|
"supplementary_groups": ["docker"],
|
|
}
|
|
],
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
"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_dirs": [
|
|
{
|
|
"path": "/etc/foo",
|
|
"owner": "root",
|
|
"group": "root",
|
|
"mode": "0755",
|
|
"reason": "parent_dir",
|
|
}
|
|
],
|
|
"managed_files": [
|
|
{
|
|
"path": "/etc/foo/foo.conf",
|
|
"src_rel": "etc/foo.conf",
|
|
"owner": "root",
|
|
"group": "root",
|
|
"mode": "0644",
|
|
"reason": "modified_conffile",
|
|
}
|
|
],
|
|
"managed_links": [],
|
|
"excluded": [],
|
|
"notes": [],
|
|
}
|
|
],
|
|
"packages": [
|
|
{
|
|
"package": "curl",
|
|
"role_name": "curl",
|
|
"section": "net",
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
"managed_links": [],
|
|
"excluded": [],
|
|
"notes": [],
|
|
}
|
|
],
|
|
"apt_config": {
|
|
"role_name": "apt_config",
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
"excluded": [],
|
|
"notes": [],
|
|
},
|
|
"dnf_config": {
|
|
"role_name": "dnf_config",
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
"excluded": [],
|
|
"notes": [],
|
|
},
|
|
"sysctl": {
|
|
"role_name": "sysctl",
|
|
"managed_dirs": [],
|
|
"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": [],
|
|
},
|
|
"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_dirs": [],
|
|
"managed_files": [],
|
|
"excluded": [],
|
|
"notes": [],
|
|
},
|
|
"usr_local_custom": {
|
|
"role_name": "usr_local_custom",
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
"excluded": [],
|
|
"notes": [],
|
|
},
|
|
"extra_paths": {
|
|
"role_name": "extra_paths",
|
|
"include_patterns": [],
|
|
"exclude_patterns": [],
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
"managed_links": [],
|
|
"excluded": [],
|
|
"notes": [],
|
|
},
|
|
},
|
|
}
|
|
_write_state(bundle, state)
|
|
|
|
manifest.manifest(str(bundle), str(out), target="puppet", fqdn="test.example")
|
|
|
|
site_pp = (out / "manifests" / "site.pp").read_text(encoding="utf-8")
|
|
assert site_pp == (
|
|
"node 'test.example' {\n"
|
|
" include curl\n"
|
|
" include foo\n"
|
|
" include users\n"
|
|
" include sysctl\n"
|
|
"}\n"
|
|
)
|
|
|
|
curl_pp = (out / "modules" / "curl" / "manifests" / "init.pp").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
assert "class curl" in curl_pp
|
|
assert "package { 'curl':" in curl_pp
|
|
|
|
foo_pp = (out / "modules" / "foo" / "manifests" / "init.pp").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
assert "class foo" in foo_pp
|
|
assert "package { 'foo':" in foo_pp
|
|
assert "file { '/etc/foo/foo.conf':" in foo_pp
|
|
assert "source => 'puppet:///modules/foo/etc/foo.conf'" in foo_pp
|
|
assert "service { 'foo.service':" in foo_pp
|
|
|
|
users_pp = (out / "modules" / "users" / "manifests" / "init.pp").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
assert "class users" in users_pp
|
|
assert "group { 'docker':" in users_pp
|
|
assert "user { 'alice':" in users_pp
|
|
|
|
sysctl_pp = (out / "modules" / "sysctl" / "manifests" / "init.pp").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
assert "class sysctl" in sysctl_pp
|
|
assert "Boolean $sysctl_apply = true" in sysctl_pp
|
|
assert "Boolean $sysctl_ignore_apply_errors = true" in sysctl_pp
|
|
assert "exec { 'enroll-apply-sysctl':" in sysctl_pp
|
|
assert "command => $sysctl_ignore_apply_errors ? {" in sysctl_pp
|
|
assert "sysctl -e -p /etc/sysctl.d/99-enroll.conf || true" in sysctl_pp
|
|
|
|
assert (out / "modules" / "foo" / "files" / "etc" / "foo.conf").exists()
|
|
assert (out / "modules" / "sysctl" / "files" / "sysctl" / "99-enroll.conf").exists()
|
|
|
|
|
|
def test_manifest_puppet_uses_default_node_and_common_package_modules(tmp_path: Path):
|
|
bundle = tmp_path / "bundle"
|
|
out = tmp_path / "puppet"
|
|
artifact = bundle / "artifacts" / "foo" / "etc" / "foo.conf"
|
|
artifact.parent.mkdir(parents=True, exist_ok=True)
|
|
artifact.write_text("setting = true\n", encoding="utf-8")
|
|
|
|
state = {
|
|
"schema_version": 3,
|
|
"host": {"hostname": "test", "os": "debian", "pkg_backend": "dpkg"},
|
|
"inventory": {
|
|
"packages": {
|
|
"curl": {"section": "net"},
|
|
"foo": {"installations": [{"section": "net"}]},
|
|
}
|
|
},
|
|
"roles": {
|
|
"services": [
|
|
{
|
|
"unit": "foo.service",
|
|
"role_name": "foo",
|
|
"packages": ["foo"],
|
|
"active_state": "active",
|
|
"unit_file_state": "enabled",
|
|
"managed_dirs": [],
|
|
"managed_files": [
|
|
{
|
|
"path": "/etc/foo/foo.conf",
|
|
"src_rel": "etc/foo.conf",
|
|
"owner": "root",
|
|
"group": "root",
|
|
"mode": "0644",
|
|
}
|
|
],
|
|
"managed_links": [],
|
|
}
|
|
],
|
|
"packages": [
|
|
{
|
|
"package": "curl",
|
|
"role_name": "curl",
|
|
"section": "net",
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
"managed_links": [],
|
|
}
|
|
],
|
|
"users": {
|
|
"role_name": "users",
|
|
"users": [],
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
},
|
|
"apt_config": {
|
|
"role_name": "apt_config",
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
},
|
|
"dnf_config": {
|
|
"role_name": "dnf_config",
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
},
|
|
"sysctl": {"role_name": "sysctl", "managed_dirs": [], "managed_files": []},
|
|
"firewall_runtime": {"role_name": "firewall_runtime", "packages": []},
|
|
"etc_custom": {
|
|
"role_name": "etc_custom",
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
},
|
|
"usr_local_custom": {
|
|
"role_name": "usr_local_custom",
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
},
|
|
"extra_paths": {
|
|
"role_name": "extra_paths",
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
"managed_links": [],
|
|
},
|
|
},
|
|
}
|
|
_write_state(bundle, state)
|
|
|
|
manifest.manifest(str(bundle), str(out), target="puppet")
|
|
|
|
site_pp = (out / "manifests" / "site.pp").read_text(encoding="utf-8")
|
|
assert site_pp == "node default {\n include net\n}\n"
|
|
|
|
net_pp = (out / "modules" / "net" / "manifests" / "init.pp").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
assert "class net" in net_pp
|
|
assert "package { 'curl':" in net_pp
|
|
assert "package { 'foo':" in net_pp
|
|
assert "file { '/etc/foo/foo.conf':" in net_pp
|
|
assert "source => 'puppet:///modules/net/etc/foo.conf'" in net_pp
|
|
assert "service { 'foo.service':" in net_pp
|
|
assert (out / "modules" / "net" / "files" / "etc" / "foo.conf").exists()
|
|
assert not (out / "modules" / "curl").exists()
|
|
assert not (out / "modules" / "foo").exists()
|
|
|
|
|
|
def test_manifest_puppet_avoids_reserved_module_names_and_duplicate_resources(
|
|
tmp_path: Path,
|
|
):
|
|
bundle = tmp_path / "bundle"
|
|
out = tmp_path / "puppet"
|
|
state = {
|
|
"schema_version": 3,
|
|
"host": {"hostname": "test", "os": "debian", "pkg_backend": "dpkg"},
|
|
"inventory": {
|
|
"packages": {
|
|
"alpha": {"section": "admin"},
|
|
"beta": {"section": "misc"},
|
|
"gamma": {"section": "default"},
|
|
}
|
|
},
|
|
"roles": {
|
|
"packages": [
|
|
{
|
|
"package": "alpha",
|
|
"role_name": "alpha",
|
|
"section": "admin",
|
|
"managed_dirs": [
|
|
{
|
|
"path": "/etc/default",
|
|
"owner": "root",
|
|
"group": "root",
|
|
"mode": "0755",
|
|
}
|
|
],
|
|
"managed_files": [],
|
|
"managed_links": [],
|
|
},
|
|
{
|
|
"package": "beta",
|
|
"role_name": "beta",
|
|
"section": "misc",
|
|
"managed_dirs": [
|
|
{
|
|
"path": "/etc/default",
|
|
"owner": "root",
|
|
"group": "root",
|
|
"mode": "0755",
|
|
}
|
|
],
|
|
"managed_files": [],
|
|
"managed_links": [],
|
|
},
|
|
{
|
|
"package": "gamma",
|
|
"role_name": "gamma",
|
|
"section": "default",
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
"managed_links": [],
|
|
},
|
|
],
|
|
"users": {
|
|
"role_name": "users",
|
|
"users": [],
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
},
|
|
"apt_config": {
|
|
"role_name": "apt_config",
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
},
|
|
"dnf_config": {
|
|
"role_name": "dnf_config",
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
},
|
|
"sysctl": {"role_name": "sysctl", "managed_dirs": [], "managed_files": []},
|
|
"firewall_runtime": {"role_name": "firewall_runtime", "packages": []},
|
|
"etc_custom": {
|
|
"role_name": "etc_custom",
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
},
|
|
"usr_local_custom": {
|
|
"role_name": "usr_local_custom",
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
},
|
|
"extra_paths": {
|
|
"role_name": "extra_paths",
|
|
"managed_dirs": [],
|
|
"managed_files": [],
|
|
"managed_links": [],
|
|
},
|
|
},
|
|
}
|
|
_write_state(bundle, state)
|
|
|
|
manifest.manifest(str(bundle), str(out), target="puppet")
|
|
|
|
site_pp = (out / "manifests" / "site.pp").read_text(encoding="utf-8")
|
|
assert "include default\n" not in site_pp
|
|
assert "include package_group_default" in site_pp
|
|
assert (
|
|
out / "modules" / "package_group_default" / "manifests" / "init.pp"
|
|
).exists()
|
|
|
|
init_pps = "\n".join(
|
|
p.read_text(encoding="utf-8")
|
|
for p in sorted((out / "modules").glob("*/manifests/init.pp"))
|
|
)
|
|
assert init_pps.count("file { '/etc/default':") == 1
|
|
|
|
|
|
def test_manifest_rejects_unknown_target(tmp_path: Path):
|
|
bundle = tmp_path / "bundle"
|
|
_write_state(bundle, {"roles": {}})
|
|
|
|
try:
|
|
manifest.manifest(str(bundle), str(tmp_path / "out"), target="chef")
|
|
except ValueError as e:
|
|
assert "unsupported manifest target" in str(e)
|
|
else:
|
|
raise AssertionError("expected ValueError")
|