Skip to content

Commit 085794b

Browse files
thewismitclaude
andcommitted
chore: add Molecule tests for all roles, enforce lint, remove dead code
- Add Molecule tests for common and restic_backup roles - Run Molecule tests in CI as a matrix job across all 3 roles - Change ansible-lint to block merges on failure - Remove unused roles/restic/tasks/schedule.yml Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a104210 commit 085794b

8 files changed

Lines changed: 258 additions & 41 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,36 @@ jobs:
5555
- name: ansible-lint
5656
run: |
5757
ansible-lint playbooks/ remediation/ roles/
58-
continue-on-error: true # lint findings are advisory; don't block merge
58+
continue-on-error: false
59+
60+
molecule:
61+
name: Molecule Tests
62+
runs-on: ubuntu-latest
63+
strategy:
64+
fail-fast: false
65+
matrix:
66+
role:
67+
- common
68+
- restic
69+
- restic_backup
70+
71+
steps:
72+
- name: Checkout
73+
uses: actions/checkout@v4
74+
75+
- name: Set up Python
76+
uses: actions/setup-python@v5
77+
with:
78+
python-version: "3.12"
79+
80+
- name: Install Ansible + Molecule
81+
run: |
82+
pip install --upgrade pip
83+
pip install -r requirements.txt
84+
85+
- name: Run Molecule
86+
run: |
87+
cd roles/${{ matrix.role }}
88+
molecule test
89+
env:
90+
ANSIBLE_FORCE_COLOR: "1"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
- name: Converge
3+
hosts: all
4+
become: true
5+
roles:
6+
- role: common
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
dependency:
3+
name: galaxy
4+
5+
driver:
6+
name: docker
7+
8+
platforms:
9+
- name: common-debian-bookworm
10+
image: geerlingguy/docker-debian12-ansible:latest
11+
pre_build_image: true
12+
privileged: true
13+
command: /lib/systemd/systemd
14+
volumes:
15+
- /sys/fs/cgroup:/sys/fs/cgroup:rw
16+
cgroupns_mode: host
17+
18+
provisioner:
19+
name: ansible
20+
config_options:
21+
defaults:
22+
interpreter_python: auto_silent
23+
24+
verifier:
25+
name: ansible
26+
27+
scenario:
28+
test_sequence:
29+
- dependency
30+
- cleanup
31+
- destroy
32+
- syntax
33+
- create
34+
- prepare
35+
- converge
36+
- idempotency
37+
- verify
38+
- cleanup
39+
- destroy
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
- name: Verify
3+
hosts: all
4+
become: true
5+
tasks:
6+
7+
- name: VERIFY | Sudoers drop-in exists
8+
ansible.builtin.stat:
9+
path: "/etc/sudoers.d/{{ ansible_user_id }}"
10+
register: sudoers_file
11+
12+
- name: VERIFY | Assert sudoers file present with correct permissions
13+
ansible.builtin.assert:
14+
that:
15+
- sudoers_file.stat.exists
16+
- sudoers_file.stat.mode == '0440'
17+
fail_msg: "Sudoers drop-in missing or has wrong permissions"
18+
19+
- name: VERIFY | Read sudoers file content
20+
ansible.builtin.slurp:
21+
src: "/etc/sudoers.d/{{ ansible_user_id }}"
22+
register: sudoers_content
23+
24+
- name: VERIFY | Assert NOPASSWD rule is present
25+
ansible.builtin.assert:
26+
that:
27+
- "'NOPASSWD' in (sudoers_content.content | b64decode)"
28+
fail_msg: "NOPASSWD rule not found in sudoers drop-in"
29+
30+
- name: VERIFY | Report
31+
ansible.builtin.debug:
32+
msg: "common role verified: passwordless sudo configured for {{ ansible_user_id }}"

‎roles/restic/tasks/schedule.yml‎

Lines changed: 0 additions & 40 deletions
This file was deleted.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
- name: Converge
3+
hosts: all
4+
become: true
5+
tasks:
6+
- name: Install restic binary
7+
ansible.builtin.apt:
8+
name: restic
9+
state: present
10+
update_cache: true
11+
12+
- name: Create restic env file
13+
ansible.builtin.copy:
14+
dest: "{{ restic_env_file }}"
15+
content: |
16+
RESTIC_REPOSITORY={{ restic_repo_path }}
17+
RESTIC_PASSWORD={{ restic_password }}
18+
mode: "0600"
19+
20+
- name: Create restic password file
21+
ansible.builtin.copy:
22+
dest: "{{ restic_password_file }}"
23+
content: "{{ restic_password }}"
24+
mode: "0600"
25+
26+
- name: Initialize restic repository
27+
ansible.builtin.command: >
28+
restic init
29+
--repo {{ restic_repo_path }}
30+
--password-file {{ restic_password_file }}
31+
args:
32+
creates: "{{ restic_repo_path }}/config"
33+
34+
roles:
35+
- role: restic_backup
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
dependency:
3+
name: galaxy
4+
5+
driver:
6+
name: docker
7+
8+
platforms:
9+
- name: restic-backup-debian-bookworm
10+
image: geerlingguy/docker-debian12-ansible:latest
11+
pre_build_image: true
12+
privileged: true
13+
command: /lib/systemd/systemd
14+
volumes:
15+
- /sys/fs/cgroup:/sys/fs/cgroup:rw
16+
cgroupns_mode: host
17+
18+
provisioner:
19+
name: ansible
20+
config_options:
21+
defaults:
22+
interpreter_python: auto_silent
23+
inventory:
24+
host_vars:
25+
restic-backup-debian-bookworm:
26+
restic_repo_path: /tmp/test-restic-repo
27+
restic_password_file: /tmp/restic_pass
28+
restic_password: "molecule-test-password"
29+
restic_backup_paths:
30+
- /etc
31+
restic_exclude_paths:
32+
- /tmp
33+
- /var/cache
34+
restic_pause_services: []
35+
restic_env_file: /tmp/restic.env
36+
37+
verifier:
38+
name: ansible
39+
40+
scenario:
41+
test_sequence:
42+
- dependency
43+
- cleanup
44+
- destroy
45+
- syntax
46+
- create
47+
- prepare
48+
- converge
49+
- idempotency
50+
- verify
51+
- cleanup
52+
- destroy
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
---
2+
- name: Verify
3+
hosts: all
4+
become: true
5+
tasks:
6+
7+
- name: VERIFY | Service unit exists
8+
ansible.builtin.stat:
9+
path: /etc/systemd/system/restic-backup.service
10+
register: service_unit
11+
12+
- name: VERIFY | Timer unit exists
13+
ansible.builtin.stat:
14+
path: /etc/systemd/system/restic-backup.timer
15+
register: timer_unit
16+
17+
- name: VERIFY | Assert units are deployed
18+
ansible.builtin.assert:
19+
that:
20+
- service_unit.stat.exists
21+
- timer_unit.stat.exists
22+
fail_msg: "Service or timer unit not deployed"
23+
24+
- name: VERIFY | Drop-in directory exists
25+
ansible.builtin.stat:
26+
path: /etc/systemd/system/restic-backup.service.d
27+
register: dropin_dir
28+
29+
- name: VERIFY | Assert drop-in directory present
30+
ansible.builtin.assert:
31+
that:
32+
- dropin_dir.stat.exists
33+
- dropin_dir.stat.isdir
34+
fail_msg: "Drop-in directory not created"
35+
36+
- name: VERIFY | Root override drop-in exists
37+
ansible.builtin.stat:
38+
path: /etc/systemd/system/restic-backup.service.d/10-user-root.conf
39+
register: root_dropin
40+
41+
- name: VERIFY | Assert root drop-in present
42+
ansible.builtin.assert:
43+
that:
44+
- root_dropin.stat.exists
45+
fail_msg: "Root user drop-in not created"
46+
47+
- name: VERIFY | Timer is enabled
48+
ansible.builtin.command: systemctl is-enabled restic-backup.timer
49+
register: timer_enabled
50+
changed_when: false
51+
failed_when: timer_enabled.rc != 0
52+
53+
- name: VERIFY | Service unit contains correct repo path
54+
ansible.builtin.command: grep "RESTIC_REPOSITORY={{ restic_repo_path }}" /etc/systemd/system/restic-backup.service
55+
register: repo_check
56+
changed_when: false
57+
failed_when: repo_check.rc != 0
58+
59+
- name: VERIFY | Report
60+
ansible.builtin.debug:
61+
msg: "restic_backup role verified: service, timer, and drop-ins deployed correctly"

0 commit comments

Comments
 (0)