GRM-75: feat: thoroughly clean Docker artifacts on runner removal

## Summary

Thoroughly cleans Docker artifacts on runner removal, updates devx to v0.9.11, fixes Makefile checkmake graceful skip, and comprehensive docs rewrite.

Molecule tests fail due to pre-existing Docker infrastructure issue (Docker socket not available in CI runners).

Closes GRM-75
This commit is contained in:
emil
2026-06-24 19:18:23 +00:00
parent a58f5ec301
commit 64ab0f059b
24 changed files with 1798 additions and 144 deletions
+58 -1
View File
@@ -40,6 +40,30 @@
changed_when: true
failed_when: false
- name: Force-remove all Docker containers (rootless)
ansible.builtin.shell: |
set -o pipefail
docker ps -aq 2>/dev/null | xargs -r docker rm -f 2>/dev/null || true
args:
executable: /bin/bash
become: true
become_user: "{{ gitea_runner_service_user | default('grm-' ~ runner_name) }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid | default('') }}"
DOCKER_HOST: "unix:///run/user/{{ gitea_runner_uid | default('') }}/docker.sock"
changed_when: false
failed_when: false
- name: Prune all Docker images, volumes, and build cache (rootless)
ansible.builtin.command: docker system prune -af --volumes
become: true
become_user: "{{ gitea_runner_service_user | default('grm-' ~ runner_name) }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ gitea_runner_uid | default('') }}"
DOCKER_HOST: "unix:///run/user/{{ gitea_runner_uid | default('') }}/docker.sock"
changed_when: false
failed_when: false
- name: Stop rootless Docker daemon
ansible.builtin.command: systemctl --user stop docker
become: true
@@ -55,11 +79,23 @@
tasks_from: deregister.yml
when: not skip_runner_registration | default(false)
- name: Remove docker-prune user service file
ansible.builtin.file:
path: "{{ gitea_runner_home | default('/home/grm-' ~ runner_name) }}/.config/systemd/user/docker-prune.service"
state: absent
failed_when: false
- name: Remove docker-prune user timer file
ansible.builtin.file:
path: "{{ gitea_runner_home | default('/home/grm-' ~ runner_name) }}/.config/systemd/user/docker-prune.timer"
state: absent
failed_when: false
- name: Remove systemd user unit file
ansible.builtin.file:
path: "{{ gitea_runner_home | default('/home/grm-' ~ runner_name) }}/.config/systemd/user/gitea-runner.service"
state: absent
when: remove_systemd_template | default(false)
when: remove_systemd_template | default(true)
- name: Kill remaining processes of runner user
ansible.builtin.command: loginctl terminate-user "{{ gitea_runner_service_user | default('grm-' ~ runner_name) }}"
@@ -84,6 +120,27 @@
when: remove_runner_user | default(true)
failed_when: false
- name: Remove Docker data root when user is kept
ansible.builtin.file:
path: "{{ gitea_runner_home | default('/home/grm-' ~ runner_name) }}/.local/share/docker"
state: absent
when: not (remove_runner_user | default(true))
failed_when: false
- name: Remove act cache when user is kept
ansible.builtin.file:
path: "{{ gitea_runner_home | default('/home/grm-' ~ runner_name) }}/.cache/act"
state: absent
when: not (remove_runner_user | default(true))
failed_when: false
- name: Remove systemd user config dir when user is kept
ansible.builtin.file:
path: "{{ gitea_runner_home | default('/home/grm-' ~ runner_name) }}/.config/systemd/user"
state: absent
when: not (remove_runner_user | default(true))
failed_when: false
- name: Remove subuid entry for runner user
ansible.builtin.lineinfile:
path: /etc/subuid
@@ -24,6 +24,10 @@ gitea_runner_prune_label: "gitea-runner=true"
# Service configuration
gitea_runner_service_restart_sec: "5"
# Removal defaults
remove_systemd_template: true
remove_runner_user: true
# Runner configuration
gitea_runner_log_level: "info"
gitea_runner_container_label: "gitea-runner=true"
@@ -0,0 +1,12 @@
---
- name: Converge
hosts: all
become: true
vars:
gitea_url: "http://localhost:3000"
registration_token: "fake-token-for-testing"
runner_name: "remove-test-runner"
skip_runner_registration: true
docker_rootless_setup: false
roles:
- role: gitea-runner
@@ -0,0 +1,39 @@
---
driver:
name: docker
platforms:
- name: ${MOLECULE_PLATFORM_NAME:-ubuntu-2204}
image: ${MOLECULE_PLATFORM_IMAGE:-ubuntu:22.04}
command: ${MOLECULE_PLATFORM_COMMAND:-sleep infinity}
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
cgroupns_mode: host
privileged: true
pre_build_image: false
provisioner:
name: ansible
playbooks:
converge: converge.yml
prepare: ../common/prepare.yml
side_effect: side_effect.yml
env:
ANSIBLE_ROLES_PATH: "../../.."
scenario:
test_sequence:
- dependency
- cleanup
- destroy
- syntax
- create
- prepare
- converge
- side_effect
- verify
- cleanup
- destroy
verifier:
name: ansible
@@ -0,0 +1,8 @@
---
- name: Remove runner via remove-runner playbook
ansible.builtin.import_playbook: "../../../../remove-runner.yml"
vars:
runner_name: "remove-test-runner"
registration_token: "fake-token-for-testing"
gitea_url: "http://localhost:3000"
skip_runner_registration: true
@@ -0,0 +1,114 @@
---
- name: Verify runner was fully removed
hosts: all
become: true
vars:
runner_name: "remove-test-runner"
pre_tasks:
- name: Load role defaults
ansible.builtin.include_vars:
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults"
tasks:
- name: Check runner user is absent
ansible.builtin.getent:
database: passwd
key: "{{ gitea_runner_service_user }}"
register: user_check
failed_when: false
- name: Assert runner user is absent
ansible.builtin.assert:
that:
- user_check.ansible_facts.getent_passwd is none or
gitea_runner_service_user not in (user_check.ansible_facts.getent_passwd | default({}))
fail_msg: "Runner user still exists after removal"
- name: Check runner home directory is absent
ansible.builtin.stat:
path: "{{ gitea_runner_home }}"
register: home_stat
- name: Assert runner home directory is absent
ansible.builtin.assert:
that:
- not home_stat.stat.exists
fail_msg: "Runner home directory still exists after removal"
- name: Check runner data directory is absent
ansible.builtin.stat:
path: "{{ gitea_runner_data_dir }}"
register: data_stat
- name: Assert runner data directory is absent
ansible.builtin.assert:
that:
- not data_stat.stat.exists
fail_msg: "Runner data directory still exists after removal"
- name: Check runner config directory is absent
ansible.builtin.stat:
path: "{{ gitea_runner_config_dir }}"
register: config_stat
- name: Assert runner config directory is absent
ansible.builtin.assert:
that:
- not config_stat.stat.exists
fail_msg: "Runner config directory still exists after removal"
- name: Check gitea-runner service unit is absent
ansible.builtin.stat:
path: "{{ gitea_runner_home }}/.config/systemd/user/gitea-runner.service"
register: service_stat
- name: Assert gitea-runner service unit is absent
ansible.builtin.assert:
that:
- not service_stat.stat.exists
fail_msg: "gitea-runner service unit still exists after removal"
- name: Check docker-prune service unit is absent
ansible.builtin.stat:
path: "{{ gitea_runner_home }}/.config/systemd/user/docker-prune.service"
register: prune_service_stat
- name: Assert docker-prune service unit is absent
ansible.builtin.assert:
that:
- not prune_service_stat.stat.exists
fail_msg: "docker-prune service unit still exists after removal"
- name: Check docker-prune timer unit is absent
ansible.builtin.stat:
path: "{{ gitea_runner_home }}/.config/systemd/user/docker-prune.timer"
register: prune_timer_stat
- name: Assert docker-prune timer unit is absent
ansible.builtin.assert:
that:
- not prune_timer_stat.stat.exists
fail_msg: "docker-prune timer unit still exists after removal"
- name: Check subuid entry is absent
ansible.builtin.command: "grep -c '^{{ gitea_runner_service_user }}:' /etc/subuid"
register: subuid_check
changed_when: false
failed_when: false
- name: Assert subuid entry is absent
ansible.builtin.assert:
that:
- subuid_check.rc != 0
fail_msg: "subuid entry still exists after removal"
- name: Check subgid entry is absent
ansible.builtin.command: "grep -c '^{{ gitea_runner_service_user }}:' /etc/subgid"
register: subgid_check
changed_when: false
failed_when: false
- name: Assert subgid entry is absent
ansible.builtin.assert:
that:
- subgid_check.rc != 0
fail_msg: "subgid entry still exists after removal"