--- # 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 2>&1 & args: creates: "{{ lab_sampler_log }}" timeout: 30 when: lab_dns_node