From 9c7586c7c3a235672ec6490d1a8bc44a222ce5d1 Mon Sep 17 00:00:00 2001 From: Colin Wilk Date: Tue, 1 Sep 2026 21:01:05 +0200 Subject: Add borg prune and compact jobs Run repository retention either after a successful backup or from a dedicated systemd timer. Clean up script generation with templates and expand molecule test coverage. BREAKING CHANGE: Aggregate backup scripts are no longer managed, and state=preset now requires at least one readable included directory. --- molecule/delete/converge.yml | 8 ++++++++ molecule/delete/prepare.yml | 4 ++++ molecule/delete/tests/test_delete.py | 34 +++++++++++++++++++++++----------- 3 files changed, 35 insertions(+), 11 deletions(-) (limited to 'molecule/delete') diff --git a/molecule/delete/converge.yml b/molecule/delete/converge.yml index 00de93d..037e56a 100644 --- a/molecule/delete/converge.yml +++ b/molecule/delete/converge.yml @@ -35,6 +35,10 @@ borg_backup_argument: single-backup borg_dangerously_delete_backups: true borg_decryption_keys_yaml_path: "{{ playbook_dir }}/decryption_keys_delete.yml" + borg_prune_enabled: true + borg_prune_trigger: timer + borg_prune_keep_last: "2" + borg_prune_glob_archives: "{hostname}-*" - name: Delete one repo from multi-instance (keep data) hosts: borg-client-multi-delete @@ -135,6 +139,10 @@ borg_ssh_key_type: "{{ 'ed25519' if inventory_hostname == 'borg-client-per-repo-delete' else 'rsa' }}" borg_dangerously_delete_backups: >- {{ inventory_hostname in ['borg-client-single-delete', 'borg-client-per-repo-delete'] }} + borg_prune_enabled: "{{ inventory_hostname == 'borg-client-single-delete' }}" + borg_prune_trigger: "{{ 'timer' if inventory_hostname == 'borg-client-single-delete' else 'after_backup' }}" + borg_prune_keep_last: "{{ '2' if inventory_hostname == 'borg-client-single-delete' else '' }}" + borg_prune_glob_archives: "{{ '{hostname}-*' if inventory_hostname == 'borg-client-single-delete' else '{hostname}-*' }}" borg_decryption_keys_yaml_path: >- {{ (playbook_dir ~ '/decryption_keys_empty_delete.yml') diff --git a/molecule/delete/prepare.yml b/molecule/delete/prepare.yml index 98a94a1..430387d 100644 --- a/molecule/delete/prepare.yml +++ b/molecule/delete/prepare.yml @@ -44,6 +44,10 @@ borg_included_dirs: - /etc borg_excluded_dirs: [] + borg_prune_enabled: true + borg_prune_trigger: timer + borg_prune_keep_last: "2" + borg_prune_glob_archives: "{hostname}-*" - name: Create multi-instance backup (shared key) hosts: borg-client-multi-delete diff --git a/molecule/delete/tests/test_delete.py b/molecule/delete/tests/test_delete.py index 4c0d859..3fc3189 100644 --- a/molecule/delete/tests/test_delete.py +++ b/molecule/delete/tests/test_delete.py @@ -37,6 +37,16 @@ class TestSingleRepoDelete: assert not timer.exists assert not service.exists + def test_prune_systemd_units_removed(self, host): + hostname = host.backend.get_hostname() + if hostname != "borg-client-single-delete": + return + + timer = host.file("/etc/systemd/system/borg_prune@single-backup.timer") + service = host.file("/etc/systemd/system/borg_prune@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": @@ -47,6 +57,16 @@ class TestSingleRepoDelete: assert not repo_script.exists assert not base_script.exists + def test_prune_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_prune@single-backup") + base_script = host.file("/usr/local/bin/run_borg_prune") + 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": @@ -98,17 +118,12 @@ class TestMultiInstanceDelete: assert service.exists assert script.exists - def test_base_script_keeps_only_remaining_block(self, host): + def test_aggregate_script_is_absent(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 + assert not host.file("/usr/local/bin/run_borg_backup").exists def test_shared_ssh_key_kept(self, host): hostname = host.backend.get_hostname() @@ -168,13 +183,10 @@ class TestPerRepoKeyDelete: 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 + assert not host.file("/usr/local/bin/run_borg_backup").exists def test_per_repo_ssh_key_removed(self, host): hostname = host.backend.get_hostname() -- cgit v1.2.3