"""Tests for state: absent functionality.""" import os testinfra_hosts = [ "borg-client-single-delete", "borg-client-multi-delete", "borg-client-per-repo-delete", "borg-client-nonroot-delete", "borg-client-never-delete", "borg-server-delete", ] def _scenario_file(filename): scenario_dir = os.environ.get("MOLECULE_SCENARIO_DIRECTORY") if scenario_dir: return os.path.join(scenario_dir, filename) test_dir = os.path.dirname(os.path.abspath(__file__)) return os.path.join(test_dir, "..", filename) def _read_local_file(filename): with open(_scenario_file(filename), "r", encoding="utf-8") as file_handle: return file_handle.read() class TestSingleRepoDelete: def test_systemd_units_removed(self, host): hostname = host.backend.get_hostname() if hostname != "borg-client-single-delete": return timer = host.file("/etc/systemd/system/borg_backup@single-backup.timer") service = host.file("/etc/systemd/system/borg_backup@single-backup.service") assert not timer.exists assert not service.exists def test_backup_scripts_removed(self, host): hostname = host.backend.get_hostname() if hostname != "borg-client-single-delete": return repo_script = host.file("/usr/local/bin/run_borg_backup@single-backup") base_script = host.file("/usr/local/bin/run_borg_backup") assert not repo_script.exists assert not base_script.exists def test_shared_ssh_key_kept(self, host): hostname = host.backend.get_hostname() if hostname != "borg-client-single-delete": return key = host.file("/root/.ssh/id_rsa") assert key.exists def test_repository_data_deleted(self, host): hostname = host.backend.get_hostname() if hostname != "borg-server-delete": return repo = host.file("/opt/borg/single-backup") assert not repo.exists def test_authorized_keys_entry_removed(self, host): hostname = host.backend.get_hostname() if hostname != "borg-server-delete": return auth_keys = host.file("/opt/borg/.ssh/authorized_keys") assert auth_keys.exists assert "root@borg-client-single-delete" not in auth_keys.content_string class TestMultiInstanceDelete: def test_removed_repo_units_removed(self, host): hostname = host.backend.get_hostname() if hostname != "borg-client-multi-delete": return timer = host.file("/etc/systemd/system/borg_backup@multi-repo-a.timer") service = host.file("/etc/systemd/system/borg_backup@multi-repo-a.service") script = host.file("/usr/local/bin/run_borg_backup@multi-repo-a") assert not timer.exists assert not service.exists assert not script.exists def test_remaining_repo_artifacts_exist(self, host): hostname = host.backend.get_hostname() if hostname != "borg-client-multi-delete": return timer = host.file("/etc/systemd/system/borg_backup@multi-repo-b.timer") service = host.file("/etc/systemd/system/borg_backup@multi-repo-b.service") script = host.file("/usr/local/bin/run_borg_backup@multi-repo-b") assert timer.exists assert service.exists assert script.exists def test_base_script_keeps_only_remaining_block(self, host): hostname = host.backend.get_hostname() if hostname != "borg-client-multi-delete": return base_script = host.file("/usr/local/bin/run_borg_backup") assert base_script.exists content = base_script.content_string assert "/opt/borg/multi-repo-b" in content assert "/opt/borg/multi-repo-a" not in content assert content.count("borg create") == 1 def test_shared_ssh_key_kept(self, host): hostname = host.backend.get_hostname() if hostname != "borg-client-multi-delete": return key = host.file("/root/.ssh/id_rsa") assert key.exists def test_repository_data_kept(self, host): hostname = host.backend.get_hostname() if hostname != "borg-server-delete": return removed_repo = host.file("/opt/borg/multi-repo-a") remaining_repo = host.file("/opt/borg/multi-repo-b") assert removed_repo.exists assert removed_repo.is_directory assert remaining_repo.exists assert remaining_repo.is_directory def test_authorized_keys_keeps_only_remaining_repo(self, host): hostname = host.backend.get_hostname() if hostname != "borg-server-delete": return auth_keys = host.file("/opt/borg/.ssh/authorized_keys") content = auth_keys.content_string host_entries = [ line for line in content.splitlines() if "root@borg-client-multi-delete" in line ] assert len(host_entries) == 1 assert "--restrict-to-repository /opt/borg/multi-repo-b" in host_entries[0] assert "--restrict-to-repository /opt/borg/multi-repo-a" not in host_entries[0] class TestPerRepoKeyDelete: def test_removed_repo_artifacts_removed(self, host): hostname = host.backend.get_hostname() if hostname != "borg-client-per-repo-delete": return timer = host.file("/etc/systemd/system/borg_backup@per-repo-a.timer") service = host.file("/etc/systemd/system/borg_backup@per-repo-a.service") script = host.file("/usr/local/bin/run_borg_backup@per-repo-a") assert not timer.exists assert not service.exists assert not script.exists def test_remaining_repo_untouched(self, host): hostname = host.backend.get_hostname() if hostname != "borg-client-per-repo-delete": return timer = host.file("/etc/systemd/system/borg_backup@per-repo-b.timer") service = host.file("/etc/systemd/system/borg_backup@per-repo-b.service") script = host.file("/usr/local/bin/run_borg_backup@per-repo-b") base_script = host.file("/usr/local/bin/run_borg_backup") assert timer.exists assert service.exists assert script.exists assert base_script.exists assert "/opt/borg/per-repo-b" in base_script.content_string assert "/opt/borg/per-repo-a" not in base_script.content_string def test_per_repo_ssh_key_removed(self, host): hostname = host.backend.get_hostname() if hostname != "borg-client-per-repo-delete": return key = host.file( "/root/.ssh/id_ed25519_borgbackup_borg_server_delete_per_repo_a" ) key_pub = host.file( "/root/.ssh/id_ed25519_borgbackup_borg_server_delete_per_repo_a.pub" ) assert not key.exists assert not key_pub.exists def test_other_repo_ssh_key_kept(self, host): hostname = host.backend.get_hostname() if hostname != "borg-client-per-repo-delete": return key = host.file( "/root/.ssh/id_ed25519_borgbackup_borg_server_delete_per_repo_b" ) key_pub = host.file( "/root/.ssh/id_ed25519_borgbackup_borg_server_delete_per_repo_b.pub" ) assert key.exists assert key_pub.exists def test_repository_data_deleted_and_remaining_kept(self, host): hostname = host.backend.get_hostname() if hostname != "borg-server-delete": return removed_repo = host.file("/opt/borg/per-repo-a") remaining_repo = host.file("/opt/borg/per-repo-b") assert not removed_repo.exists assert remaining_repo.exists def test_authorized_keys_per_repo_entry_removed(self, host): hostname = host.backend.get_hostname() if hostname != "borg-server-delete": return auth_keys = host.file("/opt/borg/.ssh/authorized_keys") content = auth_keys.content_string assert "--restrict-to-repository /opt/borg/per-repo-a" not in content assert "--restrict-to-repository /opt/borg/per-repo-b" in content class TestNonRootDelete: def test_removed_repo_artifacts_removed(self, host): hostname = host.backend.get_hostname() if hostname != "borg-client-nonroot-delete": return timer = host.file("/etc/systemd/system/borg_backup@nonroot-a.timer") service = host.file("/etc/systemd/system/borg_backup@nonroot-a.service") script = host.file("/usr/local/bin/run_borg_backup@nonroot-a") assert not timer.exists assert not service.exists assert not script.exists def test_remaining_repo_and_shared_key_kept(self, host): hostname = host.backend.get_hostname() if hostname != "borg-client-nonroot-delete": return timer = host.file("/etc/systemd/system/borg_backup@nonroot-b.timer") service = host.file("/etc/systemd/system/borg_backup@nonroot-b.service") script = host.file("/usr/local/bin/run_borg_backup@nonroot-b") key = host.file("/home/backupuser/.ssh/id_rsa") assert timer.exists assert service.exists assert script.exists assert key.exists def test_authorized_keys_preserves_nonroot_comment(self, host): hostname = host.backend.get_hostname() if hostname != "borg-server-delete": return auth_keys = host.file("/opt/borg/.ssh/authorized_keys") content = auth_keys.content_string host_entries = [ line for line in content.splitlines() if "backupuser@borg-client-nonroot-delete" in line ] assert len(host_entries) == 1 assert "root@borg-client-nonroot-delete" not in host_entries[0] assert "--restrict-to-repository /opt/borg/nonroot-b" in host_entries[0] assert "--restrict-to-repository /opt/borg/nonroot-a" not in host_entries[0] class TestDecryptionKeyCleanup: def test_deleted_entries_removed_and_remaining_kept(self): content = _read_local_file("decryption_keys_delete.yml") assert "borg-client-single-delete_single-backup:" not in content assert "borg-client-multi-delete_multi-repo-a:" not in content assert "borg-client-per-repo-delete_per-repo-a:" not in content assert "borg-client-nonroot-delete_nonroot-a:" not in content assert "borg-client-multi-delete_multi-repo-b:" in content assert "borg-client-per-repo-delete_per-repo-b:" in content assert "borg-client-nonroot-delete_nonroot-b:" in content def test_empty_decryption_keys_file_stays_valid(self): content = _read_local_file("decryption_keys_empty_delete.yml") assert content.strip() == "{}" class TestNeverConfiguredDelete: def test_no_artifacts_created(self, host): hostname = host.backend.get_hostname() if hostname != "borg-client-never-delete": return timer = host.file("/etc/systemd/system/borg_backup@never-configured.timer") service = host.file("/etc/systemd/system/borg_backup@never-configured.service") script = host.file("/usr/local/bin/run_borg_backup@never-configured") base_script = host.file("/usr/local/bin/run_borg_backup") assert not timer.exists assert not service.exists assert not script.exists assert not base_script.exists def test_server_repo_not_created(self, host): hostname = host.backend.get_hostname() if hostname != "borg-server-delete": return repo = host.file("/opt/borg/never-configured") assert not repo.exists def test_no_authorized_keys_entry_created(self, host): hostname = host.backend.get_hostname() if hostname != "borg-server-delete": return auth_keys = host.file("/opt/borg/.ssh/authorized_keys") assert "borg-client-never-delete" not in auth_keys.content_string