Add stop.sh (compose down) and disable print_forge (#34)
Sync runner checkout / sync (push) Successful in 6s
Sync runner checkout / sync (push) Successful in 6s
## Summary - Closes [#33](#33). - Add `scripts/stop.sh` → `playbooks/stop-apps.yml` → `docker compose down` for one `--app` + `--env` (no `-v`; checkouts/secrets stay). - `host_apps.enabled: false` skips deploy (including CI `--app`) so a stopped app cannot come back. Rows stay for ports and `stop.sh`. - Disable `print_forge` beta + prod on all app hosts. After merge (or from this branch): `./scripts/stop.sh --app print_forge --env prod` and `--env beta`. ## Test plan - [ ] `./scripts/stop.sh --help` shows required `--app` / `--env` - [ ] `ansible-playbook playbooks/stop-apps.yml --syntax-check -e app=print_forge -e app_env=prod` - [ ] `./scripts/stop.sh --app print_forge --env prod` compose-downs `print_forge_prod` on all webservers - [ ] `./scripts/stop.sh --app print_forge --env beta` compose-downs `print_forge_beta` (including adama worker) - [ ] `./scripts/deploy.sh --app print_forge --env prod --check` skips disabled rows (does not start containers) - [ ] NPM: `print-forge-preview.aimloperations.com` will 502 until the proxy host is disabled (out of scope) Reviewed-on: #34
This commit was merged in pull request #34.
This commit is contained in:
@@ -6,24 +6,21 @@
|
||||
# at branch master.
|
||||
#
|
||||
# host_apps (host_vars) lists what runs on THIS host; app_catalog (group_vars)
|
||||
# describes how each app is built.
|
||||
# describes how each app is built. host_apps.enabled: false (omit = true)
|
||||
# keeps the row for ports/stop.sh but skips deploy so CI cannot resurrect it.
|
||||
|
||||
- name: Classify catalog by type
|
||||
ansible.builtin.set_fact:
|
||||
django_names: "{{ app_catalog | dict2items | selectattr('value.type', 'equalto', 'django') | map(attribute='key') | list }}"
|
||||
node_names: "{{ app_catalog | dict2items | selectattr('value.type', 'equalto', 'node-static') | map(attribute='key') | list }}"
|
||||
|
||||
- name: Resolve deploy targets for {{ inventory_hostname }}
|
||||
- name: Resolve matching host_apps for {{ inventory_hostname }}
|
||||
ansible.builtin.set_fact:
|
||||
deploy_targets: >-
|
||||
matching_host_apps: >-
|
||||
{{ (host_apps | default([]) | selectattr('name', 'equalto', app | default('')) | selectattr('env', 'equalto', app_env | default('')) | list)
|
||||
if (app is defined and app_env is defined)
|
||||
else (host_apps | default([])) }}
|
||||
|
||||
- name: Show deploy targets
|
||||
ansible.builtin.debug:
|
||||
msg: "ref={{ app_ref | default('(per-app default branch)') }} targets={{ deploy_targets | map(attribute='name') | zip(deploy_targets | map(attribute='env')) | list }}"
|
||||
|
||||
# Explicit --app/--env with no host_apps match used to silently no-op Django
|
||||
# and still refresh web-static (looked like a "static-only" deploy). Fail loud.
|
||||
- name: Fail when requested app+env is not on this host
|
||||
@@ -36,7 +33,22 @@
|
||||
when:
|
||||
- app is defined
|
||||
- app_env is defined
|
||||
- deploy_targets | length == 0
|
||||
- matching_host_apps | length == 0
|
||||
|
||||
- name: "Drop disabled host_apps (enabled: false)"
|
||||
ansible.builtin.set_fact:
|
||||
deploy_targets: "{{ matching_host_apps | rejectattr('enabled', 'equalto', false) | list }}"
|
||||
|
||||
- name: Show skipped disabled apps
|
||||
ansible.builtin.debug:
|
||||
msg: "skip disabled {{ item.name }}/{{ item.env }} on {{ inventory_hostname }}"
|
||||
loop: "{{ matching_host_apps | selectattr('enabled', 'equalto', false) | list }}"
|
||||
loop_control:
|
||||
label: "{{ item.name }}/{{ item.env }}"
|
||||
|
||||
- name: Show deploy targets
|
||||
ansible.builtin.debug:
|
||||
msg: "ref={{ app_ref | default('(per-app default branch)') }} targets={{ deploy_targets | map(attribute='name') | zip(deploy_targets | map(attribute='env')) | list }}"
|
||||
|
||||
- name: Fail when requested app missing from app_catalog
|
||||
ansible.builtin.fail:
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
# Stop one app+env. CI/manual must pass: app=<name> app_env=<beta|prod>
|
||||
#
|
||||
# Matches host_apps including enabled: false. Hosts without that row skip.
|
||||
# Django/compose → docker compose down (no -v). Node-static → message only.
|
||||
|
||||
- name: Fail unless app and app_env extra-vars set
|
||||
ansible.builtin.fail:
|
||||
msg: "stop-apps.yml requires -e app=<name> -e app_env=<beta|prod>"
|
||||
when: app is not defined or app_env is not defined
|
||||
|
||||
- name: Classify catalog by type
|
||||
ansible.builtin.set_fact:
|
||||
django_names: "{{ app_catalog | dict2items | selectattr('value.type', 'equalto', 'django') | map(attribute='key') | list }}"
|
||||
node_names: "{{ app_catalog | dict2items | selectattr('value.type', 'equalto', 'node-static') | map(attribute='key') | list }}"
|
||||
|
||||
- name: Fail when requested app missing from app_catalog
|
||||
ansible.builtin.fail:
|
||||
msg: >-
|
||||
app={{ app }} is not in app_catalog. Known: {{ app_catalog.keys() | list }}.
|
||||
when: app not in app_catalog
|
||||
|
||||
- name: Resolve stop targets for {{ inventory_hostname }}
|
||||
ansible.builtin.set_fact:
|
||||
stop_targets: "{{ host_apps | default([]) | selectattr('name', 'equalto', app) | selectattr('env', 'equalto', app_env) | list }}"
|
||||
|
||||
- name: Skip {{ inventory_hostname }} — no host_apps row for {{ app }}/{{ app_env }}
|
||||
ansible.builtin.debug:
|
||||
msg: "skip {{ inventory_hostname }}: no host_apps entry for app={{ app }} env={{ app_env }}"
|
||||
when: stop_targets | length == 0
|
||||
|
||||
- name: Skip node-static {{ app }}/{{ app_env }} (no compose project)
|
||||
ansible.builtin.debug:
|
||||
msg: >-
|
||||
{{ app }} is node-static. stop.sh only compose-downs Django apps.
|
||||
Drop the host_apps row (or set enabled: false) and re-run deploy.sh
|
||||
so web-static regenerates nginx without that vhost.
|
||||
when:
|
||||
- stop_targets | length > 0
|
||||
- app in node_names
|
||||
|
||||
- name: Stop Django (docker compose) apps
|
||||
ansible.builtin.include_tasks: stop_django.yml
|
||||
loop: "{{ stop_targets | selectattr('name', 'in', django_names) | list }}"
|
||||
loop_control:
|
||||
loop_var: app_item
|
||||
label: "{{ app_item.name }}/{{ app_item.env }}"
|
||||
@@ -0,0 +1,72 @@
|
||||
---
|
||||
# Compose-down one Django app+env. Called per item with loop_var app_item.
|
||||
# No `down -v` — volumes stay. Checkout and secrets stay.
|
||||
|
||||
- name: "stop[{{ app_item.name }}/{{ app_item.env }}] locals"
|
||||
ansible.builtin.set_fact:
|
||||
_spec: "{{ app_catalog[app_item.name] }}"
|
||||
_src: "{{ apps_src_dir }}/{{ app_item.name }}_{{ app_item.env }}"
|
||||
_project: "{{ app_item.name }}_{{ app_item.env }}"
|
||||
|
||||
- name: "stop[{{ _project }}] checkout dir"
|
||||
ansible.builtin.stat:
|
||||
path: "{{ _src }}"
|
||||
register: _src_stat
|
||||
|
||||
- name: "stop[{{ _project }}] compose file"
|
||||
ansible.builtin.stat:
|
||||
path: "{{ _src }}/{{ _spec.compose_file }}"
|
||||
register: _compose_stat
|
||||
when: _src_stat.stat.exists
|
||||
|
||||
- name: "stop[{{ _project }}] env file"
|
||||
ansible.builtin.stat:
|
||||
path: "{{ _src }}/.env"
|
||||
register: _env_stat
|
||||
when: _src_stat.stat.exists
|
||||
|
||||
- name: "stop[{{ _project }}] use compose down"
|
||||
ansible.builtin.set_fact:
|
||||
_use_compose: "{{ _src_stat.stat.exists and _compose_stat is defined and _compose_stat.stat.exists }}"
|
||||
|
||||
- name: "stop[{{ _project }}] compose down"
|
||||
ansible.builtin.command:
|
||||
cmd: >-
|
||||
docker compose -f {{ _spec.compose_file }}
|
||||
{{ '--env-file .env' if (_env_stat is defined and _env_stat.stat.exists) else '' }}
|
||||
down
|
||||
chdir: "{{ _src }}"
|
||||
environment:
|
||||
COMPOSE_PROJECT_NAME: "{{ _project }}"
|
||||
WEB_PORT: "{{ app_item.port }}"
|
||||
COMPOSE_PROFILES: "{{ (app_item.compose_profiles | default([])) | join(',') }}"
|
||||
become: true
|
||||
become_user: "{{ admin_user }}"
|
||||
when: _use_compose | bool
|
||||
changed_when: true
|
||||
|
||||
- name: "stop[{{ _project }}] find leftover project containers"
|
||||
ansible.builtin.command:
|
||||
cmd: docker ps -aq --filter label=com.docker.compose.project={{ _project }}
|
||||
register: _leftovers
|
||||
changed_when: false
|
||||
become: true
|
||||
when: not (_use_compose | bool)
|
||||
|
||||
- name: "stop[{{ _project }}] remove leftover project containers"
|
||||
ansible.builtin.command:
|
||||
cmd: "docker rm -f {{ _leftovers.stdout_lines | join(' ') }}"
|
||||
become: true
|
||||
when:
|
||||
- not (_use_compose | bool)
|
||||
- _leftovers is defined
|
||||
- _leftovers.stdout_lines | length > 0
|
||||
changed_when: true
|
||||
|
||||
- name: "stop[{{ _project }}] nothing to stop (no checkout)"
|
||||
ansible.builtin.debug:
|
||||
msg: "no checkout at {{ _src }} and no containers for project {{ _project }}"
|
||||
when:
|
||||
- not (_use_compose | bool)
|
||||
- _leftovers is defined
|
||||
- _leftovers.stdout_lines | length == 0
|
||||
Reference in New Issue
Block a user