88 lines
3.3 KiB
Python
88 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Regression coverage for timmy-home #694 fleet secret rotation assets."""
|
|
|
|
from pathlib import Path
|
|
import unittest
|
|
|
|
import yaml
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
ANSIBLE_DIR = ROOT / "ansible"
|
|
HOSTS_FILE = ANSIBLE_DIR / "inventory" / "hosts.ini"
|
|
TARGETS_FILE = ANSIBLE_DIR / "inventory" / "group_vars" / "fleet.yml"
|
|
SECRETS_FILE = ANSIBLE_DIR / "inventory" / "group_vars" / "fleet_secrets.vault.yml"
|
|
PLAYBOOK_FILE = ANSIBLE_DIR / "playbooks" / "rotate_fleet_secrets.yml"
|
|
DOC_FILE = ROOT / "docs" / "FLEET_SECRET_ROTATION.md"
|
|
|
|
|
|
class TestFleetSecretRotation(unittest.TestCase):
|
|
def test_inventory_declares_each_host_target(self):
|
|
self.assertTrue(HOSTS_FILE.exists(), "missing ansible inventory hosts file")
|
|
self.assertTrue(TARGETS_FILE.exists(), "missing fleet target metadata")
|
|
|
|
hosts_text = HOSTS_FILE.read_text(encoding="utf-8")
|
|
self.assertIn("[fleet]", hosts_text)
|
|
self.assertIn("ezra", hosts_text)
|
|
self.assertIn("bezalel", hosts_text)
|
|
|
|
targets = yaml.safe_load(TARGETS_FILE.read_text(encoding="utf-8"))
|
|
self.assertIn("fleet_secret_targets", targets)
|
|
|
|
expected_env_files = {
|
|
"ezra": "/root/wizards/ezra/home/.env",
|
|
"bezalel": "/root/wizards/bezalel/home/.env",
|
|
}
|
|
for host, env_file in expected_env_files.items():
|
|
self.assertIn(host, targets["fleet_secret_targets"])
|
|
target = targets["fleet_secret_targets"][host]
|
|
self.assertEqual(target["env_file"], env_file)
|
|
self.assertEqual(target["ssh_authorized_keys_file"], "/root/.ssh/authorized_keys")
|
|
self.assertGreaterEqual(len(target["services"]), 1)
|
|
self.assertGreaterEqual(len(target["required_env_keys"]), 3)
|
|
|
|
def test_vault_file_contains_encrypted_secret_bundle_for_each_host(self):
|
|
self.assertTrue(SECRETS_FILE.exists(), "missing vaulted secrets inventory")
|
|
text = SECRETS_FILE.read_text(encoding="utf-8")
|
|
self.assertIn("fleet_secret_bundle:", text)
|
|
self.assertIn("$ANSIBLE_VAULT;1.1;AES256", text)
|
|
for host in ("ezra", "bezalel"):
|
|
self.assertIn(f" {host}:", text)
|
|
self.assertGreaterEqual(text.count("!vault |"), 4)
|
|
|
|
def test_playbook_has_staging_verification_and_rollback(self):
|
|
self.assertTrue(PLAYBOOK_FILE.exists(), "missing rotation playbook")
|
|
text = PLAYBOOK_FILE.read_text(encoding="utf-8")
|
|
for snippet in (
|
|
"any_errors_fatal: true",
|
|
"vars_files:",
|
|
"fleet_secrets.vault.yml",
|
|
"backup_root",
|
|
"env_backup_path",
|
|
"ssh_backup_path",
|
|
"lineinfile:",
|
|
"copy:",
|
|
"systemd:",
|
|
"state: restarted",
|
|
"systemctl is-active",
|
|
"block:",
|
|
"rescue:",
|
|
):
|
|
self.assertIn(snippet, text)
|
|
|
|
def test_docs_explain_rotation_command_and_rollback(self):
|
|
self.assertTrue(DOC_FILE.exists(), "missing fleet secret rotation docs")
|
|
text = DOC_FILE.read_text(encoding="utf-8")
|
|
for snippet in (
|
|
"ansible-playbook",
|
|
"--ask-vault-pass",
|
|
"rollback",
|
|
"authorized_keys",
|
|
"fleet_secret_bundle",
|
|
):
|
|
self.assertIn(snippet, text)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main(verbosity=2)
|