homelab/build/config/ansible/roles/compose_stack/tasks/remove.yml
Russell Seymour 1250c9cef6
Some checks are pending
deploy / deploy (push) Waiting to run
Initial checkin of code for managing homelab
2026-08-24 20:52:36 +01:00

106 lines
4.6 KiB
YAML

---
# Tears one stack down off the current Unraid host: `docker compose down`,
# then delete the project folder, then optionally drop its database.
#
# Included from main.yml when the stack's `state:` is `absent`.
#
# Ordering matters and is not obvious: `docker compose down` reads the compose
# file to know what it is removing, so the project folder has to survive until
# after that step. Delete the folder (or the stack's `src/` tree) first and
# there is nothing left to tell Docker what belonged to the project — the
# containers and networks have to be cleaned up by hand instead.
#
# What `down` takes with it, and what it deliberately doesn't:
# - removed: the containers, and any network the project itself created
# - kept: bind mounts. Appdata under /mnt/user/appdata/<app> survives, so
# a removal is reversible by flipping `state:` back to `present`.
# - kept: external networks (caddy-net, unraid_shared) — they belong to
# another stack or to Unraid, not to this project.
# - opt-in: named volumes (`remove_volumes: true`) and images
# (`remove_images: local` or `all`).
#
# Not touched at all: the stack's `homelab/<app>` path in Vault. That has its
# own lifecycle and no reason to be destroyed by a redeployable teardown.
- name: Check whether the stack's project directory is still present
ansible.builtin.stat:
path: "{{ stack_remote_dir }}/docker-compose.yml"
register: stack_compose_file
- name: Tear down stack with Docker Compose
community.docker.docker_compose_v2:
project_src: "{{ stack_remote_dir }}"
state: absent
# Named volumes and images are destructive beyond "stop running this
# here", so they stay opt-in per stack rather than being implied by
# `state: absent`.
remove_volumes: "{{ stack.remove_volumes | default(false) }}"
remove_images: "{{ stack.remove_images | default(omit) }}"
remove_orphans: true
# Absent already — a re-run after a successful removal, or a stack that was
# never deployed to this host. Both are the desired end state, not an error.
when: stack_compose_file.stat.exists
- name: Remove stack project directory
ansible.builtin.file:
path: "{{ stack_remote_dir }}"
state: absent
# Unlike appdata, these are not state — they're copies of PNGs committed in
# the repo, put there purely so the Docker page had something to render.
# Nothing is lost by deleting them and a redeploy puts them back, so they
# aren't opt-in the way volumes and images are.
#
# Listed from the repo checkout (main.yml) rather than globbed on the host:
# `{{ stack.name }}-*.png` would also match the icons of any stack whose name
# starts with this one's.
- name: Remove stack icons
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop: "{{ [unraid_icons_root ~ '/' ~ stack.name ~ '.png']
+ stack_service_icons.values() | list }}"
- name: Drop application database
when:
- stack.db is defined
- stack.remove_database | default(false)
no_log: true
block:
# Only the Postgres superuser credentials are needed here. The stack's own
# Vault secrets are not read, so a teardown still works after its
# `homelab/<app>` path has been deleted.
- name: Look up Postgres superuser credentials from Vault
ansible.builtin.set_fact:
pg_admin_secrets: >-
{{ lookup('community.hashi_vault.vault_kv2_get',
stack.db.admin_vault_path,
engine_mount_point=vault_kv_mount,
url=vault_addr,
auth_method=vault_auth_method,
role_id=vault_role_id | default(omit),
secret_id=vault_secret_id | default(omit)).secret }}
# Database before role: Postgres refuses to drop a role that still owns
# objects, and the role owns this database.
- name: Drop application database
community.postgresql.postgresql_db:
name: "{{ stack.db.name }}"
login_host: "{{ stack.db.provision_host }}"
login_port: "{{ stack.db.provision_port }}"
login_user: "{{ pg_admin_secrets.POSTGRES_SUPERUSER }}"
login_password: "{{ pg_admin_secrets.POSTGRES_SUPERUSER_PASSWORD }}"
state: absent
delegate_to: localhost
become: false
- name: Drop application database role
community.postgresql.postgresql_user:
name: "{{ stack.db.user }}"
login_host: "{{ stack.db.provision_host }}"
login_port: "{{ stack.db.provision_port }}"
login_user: "{{ pg_admin_secrets.POSTGRES_SUPERUSER }}"
login_password: "{{ pg_admin_secrets.POSTGRES_SUPERUSER_PASSWORD }}"
state: absent
delegate_to: localhost
become: false