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
|