aboutsummaryrefslogtreecommitdiffstats
path: root/molecule/default/converge.yml
blob: 0e9fbf0b3d6dd05e8650951eec16caf35ec79396 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
---
- 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