Files
computer-playbook/compose/swarm/playbook.yml

200 lines
8.5 KiB
YAML

---
# Lab-only node provisioning for the simulated cluster; never a production
# role. Runs over ansible_connection: docker right after `compose up` (python +
# ansible + the CLI are baked into the node image via the distro's infinito
# image, so no .deb bootstrap is needed): waits for systemd, wires lab DNS
# (sys-hostname skips /etc/hosts on DOCKER_IN_CONTAINER hosts; the dnsmasq
# wildcard points the app domain at the manager's ingress), then unpacks the
# current repo onto every node. Host-side concerns (image build, compose up)
# live in scripts/tests/deploy/swarm/routine/01_bootstrap.sh.
- name: provision the simulated cluster nodes
hosts: all
gather_facts: false
vars_files:
- "{{ playbook_dir }}/../../group_vars/all/08_networks.yml" # nocheck: path-absolute - lab-only playbook, playbook_dir is stable and path_absolute plugin loading is fragile in a standalone -c docker run
vars:
lab_repo_root: "{{ playbook_dir }}/../.." # nocheck: path-absolute - see above
lab_mgr: "{{ lookup('env', 'MGR') }}"
lab_wrk1: "{{ lookup('env', 'WRK1') }}"
lab_wrk2: "{{ lookup('env', 'WRK2') }}"
lab_nfs: "{{ lookup('env', 'NFS_SERVER') }}"
lab_backup: "{{ lookup('env', 'BACKUP_NODE') }}"
lab_domain: "{{ lookup('env', 'INFINITO_DOMAIN') }}"
lab_export_base: "{{ lookup('env', 'NFS_EXPORT_BASE') }}"
lab_node_src: "{{ lookup('env', 'INFINITO_NODE_SRC_DIR') }}"
lab_runner_temp: "{{ lookup('env', 'RUNNER_TEMP') }}"
lab_sampler_log: "{{ lookup('env', 'INFINITO_DNS53_SAMPLER_LOG') }}"
lab_systemd_timeout: "{{ lookup('env', 'SWARM_PILOT_SYSTEMD_TIMEOUT') | default('180', true) }}"
lab_is_mgr: "{{ inventory_hostname == lab_mgr }}"
lab_is_worker: "{{ inventory_hostname in [lab_wrk1, lab_wrk2] }}"
lab_dns_node: "{{ lab_is_mgr or lab_is_worker or inventory_hostname == lab_nfs }}"
lab_domain_target: "{{ lookup('env', 'INFINITO_SWARM_MGR_IP') }}"
lab_filesystem: "{{ lookup('env', 'INFINITO_DOCKER_FILESYSTEM') }}"
lab_filesystem_required: "{{ lookup('env', 'INFINITO_DOCKER_FILESYSTEM_REQUIRED') | default('false', true) }}"
lab_dataroot_size: "{{ '60G' if lab_is_mgr else '30G' }}"
lab_no_proxy: "localhost,127.0.0.1,::1,infinito-registry-cache,{{ lab_mgr }},{{ lab_wrk1 }},{{ lab_wrk2 }},{{ lab_nfs }},{{ lab_backup }},gcr.io,storage.googleapis.com,googleusercontent.com"
tasks:
- name: require the lab manager IP (lookup('env') yields '' for a missing key, and an empty dnsmasq address= is valid syntax)
assert:
that: lab_domain_target | length > 0
fail_msg: INFINITO_SWARM_MGR_IP is empty; it comes from scripts/tests/deploy/swarm/utils/topology/base.sh
run_once: true
- block:
- name: wait for systemd ready (status from stdout; rc is nonzero when degraded)
command: systemctl is-system-running
timeout: 15
register: lab_systemd
until: lab_systemd.stdout is search('running|degraded|starting|maintenance')
failed_when: lab_systemd.stdout is not search('running|degraded|starting|maintenance')
retries: "{{ lab_systemd_timeout }}"
delay: 1
changed_when: false
rescue:
- name: dump docker logs
command:
argv: [docker, logs, "{{ inventory_hostname }}"]
timeout: 30
delegate_to: localhost
ignore_errors: true
changed_when: false
- name: dump journal + pending jobs
command: "{{ item }}"
loop:
- journalctl --no-pager -n 50
- systemctl list-jobs --no-pager
timeout: 30
ignore_errors: true
changed_when: false
- name: dump container state
command:
argv: [docker, inspect, --format, "{{ '{{json .State}}' }}", "{{ inventory_hostname }}"]
timeout: 30
delegate_to: localhost
ignore_errors: true
changed_when: false
- name: fail the bring-up
fail:
msg: "{{ inventory_hostname }} systemd did not become responsive within {{ lab_systemd_timeout }}s"
- name: permit user logins (degraded lab boot never clears /run/nologin)
systemd:
name: systemd-user-sessions
state: started
timeout: 30
- name: put the node docker data root on the stated filesystem (before anything is pulled)
script: >-
{{ lab_repo_root }}/scripts/tests/deploy/utils/filesystem/docker_dataroot.sh
{{ lab_filesystem }} {{ lab_filesystem_required }} {{ lab_dataroot_size }} {{ inventory_hostname }}
timeout: 600
when: lab_filesystem | length > 0
changed_when: true
register: lab_dataroot
- name: surface the node's effective data root filesystem
debug:
msg: "{{ lab_dataroot.stdout_lines | select('search', '^docker-dataroot-filesystem: status=') | list }}"
when: not (lab_dataroot is skipped)
- name: probe the registry-cache CA (mounted by cache.override.yml, absent without a local cache stack)
stat:
path: /opt/registry-cache-ca/ca.crt
timeout: 30
register: lab_registry_ca
- name: wire the node docker daemon to the host registry cache
include_tasks: registry_cache.yml
when: lab_registry_ca.stat.exists
- name: wipe stale NFS export state (as root in-node; the act runner cannot)
command: find {{ lab_export_base }} -mindepth 1 -delete
timeout: 120
when: inventory_hostname == lab_nfs
changed_when: true
- name: render the app-domain hosts entries
command: python3 -m utils.tests.swarm.write.hosts_entries
args:
chdir: "{{ lab_repo_root }}"
environment:
HOSTS_ENTRY_IP: "{{ lab_domain_target }}"
timeout: 60
register: lab_hosts_entries
when: lab_is_mgr or inventory_hostname in [lab_nfs, lab_backup]
delegate_to: localhost
changed_when: false
- name: seed /etc/hosts with the app domains
blockinfile:
path: /etc/hosts
marker: "# {mark} INFINITO LAB DOMAINS"
block: "{{ lab_hosts_entries.stdout }}"
unsafe_writes: true
timeout: 30
when: lab_is_mgr or inventory_hostname in [lab_nfs, lab_backup]
- name: own the whole dnsmasq config (the packaging appends --local-service unconditionally and only an interface, except-interface, listen-address or auth-server directive resets it; without that reset a failed interface re-enumeration empties dnsmasq's local-subnet list and it rejects every query, loopback included, until an address event rebuilds it)
template: # nocheck: stack-host-template - lab node system config, every DNS node writes its own /etc/dnsmasq.conf and IS_STACK_HOST does not exist in this play
src: dnsmasq.conf.j2
dest: /etc/dnsmasq.conf
timeout: 30
when: lab_dns_node
- name: enable and restart dnsmasq (arch is Type=dbus, so systemd waits its full 90s DefaultTimeoutStartSec before reporting)
systemd:
name: dnsmasq
enabled: true
state: restarted
timeout: 120
when: lab_dns_node
- name: point resolv.conf at dnsmasq
copy:
dest: /etc/resolv.conf
content: "nameserver 127.0.0.1\n"
unsafe_writes: true
timeout: 30
when: lab_dns_node
- name: verify the app domain resolves to its lab target
command: getent ahosts {{ ('test.' ~ lab_domain) if lab_is_worker else lab_domain }}
timeout: 10
register: lab_resolve
until: lab_resolve.stdout is search('^' ~ (lab_domain_target | regex_escape) ~ ' ', multiline=True)
retries: 30
delay: 1
changed_when: false
when: lab_dns_node
- name: pack the repo once on the controller
command: tar -C {{ lab_repo_root }} -cf {{ lab_runner_temp }}/infinito-src.tar .
timeout: 300
delegate_to: localhost
run_once: true
changed_when: true
- name: create the node source dir
file:
path: "{{ lab_node_src }}"
state: directory
mode: "0755"
timeout: 30
- name: unpack the repo onto the node
unarchive:
src: "{{ lab_runner_temp }}/infinito-src.tar"
dest: "{{ lab_node_src }}"
timeout: 300
- name: sample what the resolver answers, not just whether it listens
shell: |
set -eu
sampler={{ lab_node_src }}/scripts/tests/deploy/ci/dns53-sampler.sh
INFINITO_DNS53_SAMPLER_LOG={{ lab_sampler_log }} \
setsid sh "${sampler}" </dev/null >/dev/null 2>&1 &
args:
creates: "{{ lab_sampler_log }}"
timeout: 30
when: lab_dns_node