--- - name: Setup - Configure SSH keys for all hosts hosts: - borg-client - borg-client-2 - borg-client-multi - borg-client-nonroot - borg-client-multi-keys - borg-client-transition - borg-client-validation vars: borg_server_host: borg-server pre_tasks: # This would usually be set by the user globally on their ansible # repository and can be a security risk to do automatically. We will # however set the variable here in the pre_tasks since it is for testing. - name: Start ssh on borg-server ansible.builtin.systemd: name: sshd state: started become: true delegate_to: borg-server - name: Start ssh on borg-server-2 ansible.builtin.systemd: name: sshd state: started become: true delegate_to: borg-server-2 - name: Fetch ssh_key for borg-server ansible.builtin.command: >- ssh-keyscan -t rsa borg-server | sed "s/^[^ ]* //" register: borg_server_ssh_keyscan changed_when: false - name: Fetch ssh_key for borg-server-2 ansible.builtin.command: >- ssh-keyscan -t rsa borg-server-2 | sed "s/^[^ ]* //" register: borg_server_2_ssh_keyscan changed_when: false - name: Set ssh_key for borg-server ansible.builtin.set_fact: borg_server_host_ssh_key: >- {{ borg_server_ssh_keyscan.stdout | split(" ") | reject("search", borg_server_host) | join(" ") }} - name: Converge - Default borg-client hosts: borg-client roles: - role: kliwniloc.borgbackup vars: borg_server_host: borg-server borg_server_user_home: /opt/borg borg_decryption_keys_yaml_path: "{{ playbook_dir }}/decryption_keys.yml" borg_included_dirs: - /etc - /home borg_excluded_dirs: - /opt - /var - /reee reeee borg_prune_enabled: true borg_prune_keep_last: "2" borg_prune_glob_archives: "{hostname}-*" borg_prune_compact_enabled: true borg_compact_threshold: 1 - name: Converge - borg-client-2 (custom server user) hosts: borg-client-2 vars: borg_server_host_ssh_key: "{{ hostvars['localhost']['borg_server_2_ssh_keyscan']['stdout'] | split(' ') | reject('search', 'borg-server-2') | join(' ') }}" roles: - role: kliwniloc.borgbackup vars: borg_server_host: borg-server-2 borg_server_user: backupserver borg_server_user_home: /var/backups borg_decryption_keys_yaml_path: "" borg_ssh_key_type: ed25519 borg_backup_service_successful_exit_status: - 1 - TEMPFAIL borg_compression: lz4 borg_create_additional_arguments: >- -C zlib,6 borg_systemd_oncalendar: "*-*-* 03:00:00" borg_included_dirs: - /etc - /home borg_excluded_dirs: - /opt - /var - /reee reeee borg_prune_enabled: true borg_prune_trigger: timer borg_prune_keep_daily: "7" borg_prune_keep_weekly: "4" borg_prune_glob_archives: "{hostname}-*" borg_prune_compact_enabled: true borg_compact_threshold: 10 borg_prune_service_successful_exit_status: - 1 - TEMPFAIL borg_prune_systemd_oncalendar: "*-*-* 05:00:00" - name: Converge - Multi-instance backup (same host, different repos) hosts: borg-client-multi serial: 1 vars: borg_server_host: borg-server borg_server_user_home: /opt/borg borg_decryption_keys_yaml_path: "{{ playbook_dir }}/decryption_keys.yml" roles: - role: kliwniloc.borgbackup vars: borg_repo_name: configs borg_backup_argument: configs borg_compression: zstd borg_included_dirs: - /etc borg_excluded_dirs: [] borg_systemd_oncalendar: "*-*-* 02:00:00" - role: kliwniloc.borgbackup vars: borg_repo_name: home-data borg_backup_argument: home-data borg_compression: lz4 borg_included_dirs: - /home borg_excluded_dirs: - /home/*/.cache borg_systemd_oncalendar: "*-*-* 04:00:00" - name: Converge - Non-root backup user hosts: borg-client-nonroot pre_tasks: - name: Create backup user for non-root test ansible.builtin.user: name: backupuser home: /home/backupuser shell: /bin/bash state: present become: true roles: - role: kliwniloc.borgbackup vars: borg_server_host: borg-server borg_server_user_home: /opt/borg borg_client_user: backupuser borg_decryption_keys_yaml_path: "{{ playbook_dir }}/decryption_keys.yml" borg_included_dirs: - /etc borg_excluded_dirs: [] - name: Converge - Per-repo SSH keys with different append-only settings hosts: borg-client-multi-keys serial: 1 vars: borg_server_host: borg-server borg_server_user_home: /opt/borg borg_decryption_keys_yaml_path: "{{ playbook_dir }}/decryption_keys.yml" roles: - role: kliwniloc.borgbackup vars: borg_repo_name: configs-keys borg_backup_argument: configs-keys borg_ssh_key_per_repo: true borg_ssh_key_type: ed25519 borg_mode_append_only: true borg_storage_quota: 10G borg_compression: zstd borg_create_additional_arguments: >- --stats --list --filter=AME borg_included_dirs: - /etc borg_excluded_dirs: [] borg_systemd_oncalendar: "*-*-* 02:00:00" - role: kliwniloc.borgbackup vars: borg_repo_name: home-data-keys borg_backup_argument: home-data-keys borg_ssh_key_per_repo: true borg_ssh_key_type: ed25519 borg_mode_append_only: false borg_storage_quota: 50G borg_compression: lz4 borg_create_additional_arguments: >- --one-file-system --exclude-caches borg_included_dirs: - /home borg_excluded_dirs: - /home/*/.cache borg_systemd_oncalendar: "*-*-* 04:00:00" - name: Converge - Transition host in stable after_backup state hosts: borg-client-transition pre_tasks: - name: Seed legacy aggregate backup script ansible.builtin.copy: dest: /usr/local/bin/run_borg_backup content: | #!/bin/bash ## BEGIN ANSIBLE MANAGED BLOCK for borg-server/transition-repo echo legacy-backup-block ## END ANSIBLE MANAGED BLOCK for borg-server/transition-repo owner: root group: root mode: "0711" force: false become: true roles: - role: kliwniloc.borgbackup vars: borg_server_host: borg-server borg_server_user_home: /opt/borg borg_decryption_keys_yaml_path: "{{ playbook_dir }}/decryption_keys.yml" borg_repo_name: transition-repo borg_backup_argument: transition-repo borg_included_dirs: - /etc borg_excluded_dirs: [] borg_prune_enabled: true borg_prune_trigger: after_backup borg_prune_keep_last: "2" borg_prune_glob_archives: "{hostname}-*" - name: Converge - Validation guard coverage hosts: borg-client-validation tasks: - name: Verify prune with append-only fails validation block: - name: Run role with incompatible append-only pruning ansible.builtin.include_role: name: kliwniloc.borgbackup vars: borg_server_host: borg-server borg_server_user_home: /opt/borg borg_decryption_keys_yaml_path: "{{ playbook_dir }}/decryption_keys.yml" borg_repo_name: validation-append-only borg_backup_argument: validation-append-only borg_included_dirs: - /etc borg_prune_enabled: true borg_mode_append_only: true borg_prune_keep_last: "2" borg_prune_glob_archives: "{hostname}-*" - name: Fail when append-only prune validation unexpectedly passes ansible.builtin.fail: msg: Append-only prune validation unexpectedly passed rescue: - name: Assert append-only prune validation failed as expected ansible.builtin.assert: that: - >- 'incompatible with borg_mode_append_only' in (ansible_failed_result.msg | default('')) - name: Verify prune without a retention policy fails validation block: - name: Run role without a prune retention policy ansible.builtin.include_role: name: kliwniloc.borgbackup vars: borg_server_host: borg-server borg_server_user_home: /opt/borg borg_decryption_keys_yaml_path: "{{ playbook_dir }}/decryption_keys.yml" borg_repo_name: validation-no-retention borg_backup_argument: validation-no-retention borg_included_dirs: - /etc borg_prune_enabled: true borg_prune_glob_archives: "{hostname}-*" - name: Fail when missing retention validation unexpectedly passes ansible.builtin.fail: msg: Missing retention validation unexpectedly passed rescue: - name: Assert missing retention validation failed as expected ansible.builtin.assert: that: - >- 'Prune requires at least one retention rule' in (ansible_failed_result.msg | default('')) - name: Prune with retention only in additional arguments should succeed ansible.builtin.include_role: name: kliwniloc.borgbackup vars: borg_server_host: borg-server borg_server_user_home: /opt/borg borg_decryption_keys_yaml_path: "{{ playbook_dir }}/decryption_keys.yml" borg_repo_name: validation-additional-args borg_backup_argument: validation-additional-args borg_included_dirs: - /etc borg_prune_enabled: true borg_prune_glob_archives: "{hostname}-*" borg_prune_additional_arguments: --keep-last 2 register: prune_additional_args_result - name: Assert additional-arguments prune validation passes ansible.builtin.assert: that: - prune_additional_args_result is succeeded - name: Converge disabled pruning baseline ansible.builtin.include_role: name: kliwniloc.borgbackup vars: borg_server_host: borg-server borg_server_user_home: /opt/borg borg_decryption_keys_yaml_path: "{{ playbook_dir }}/decryption_keys.yml" borg_repo_name: validation-disable-prune borg_backup_argument: validation-disable-prune borg_included_dirs: - /etc borg_prune_enabled: false - name: Absent state with empty prune script path should succeed ansible.builtin.include_role: name: kliwniloc.borgbackup vars: state: absent borg_server_host: borg-server borg_server_user_home: /opt/borg borg_decryption_keys_yaml_path: "{{ playbook_dir }}/decryption_keys.yml" borg_repo_name: validation-absent-empty-prune-path borg_backup_argument: validation-absent-empty-prune-path borg_prune_script_location: "" register: absent_empty_prune_path_result - name: Assert absent with empty prune script path passes ansible.builtin.assert: that: - absent_empty_prune_path_result is succeeded