# install-traefik-postboot — install a Type=oneshot systemd unit that
# restarts traefik N seconds after every boot.
#
# Why this exists:
# When dockerd starts (boot, daemon swap, daemon crash-recover), 30+
# containers come up in a burst. Two races bite traefik:
#
#   1. Events-stream debounce. Traefik's docker provider does an initial
#      scan + watches `/events` for live changes. When dozens of "container
#      start" events arrive in a 2-3s burst on daemon startup, the provider
#      can drop / coalesce some of them. Backends end up missing from the
#      routing table.
#
#   2. Network-attach race. A backend's container can be in `Created`
#      state on the docker socket (so traefik scans it) but not yet
#      attached to traefik-net. Traefik builds a router pointing at a
#      gateway IP it can't reach.
#
# Either way, the workaround is the same: `docker restart traefik` once
# the topology has settled. This unit automates that — sleep N seconds
# after docker.service is up, then restart traefik. Runs once per boot.
#
# Usage:
#   scripts/elway esh-docker-vm --playbook playbooks/install-traefik-postboot.yaml
#   scripts/elway ana-docker --playbook playbooks/install-traefik-postboot.yaml
#
# Tunables (override via `--var`):
#   --var delay_seconds=90   (default 60 — empirically enough for ~30 stacks)
#   --var container=traefik  (default 'traefik')
#
# Hosts in scope (have a traefik container, verified 2026-04-24):
#   esh-docker-vm, ana-docker
# Skip on hosts without traefik (the verify phase will catch a typo).
#
# Removal: `sudo systemctl disable --now traefik-postboot && sudo rm
# /etc/systemd/system/traefik-postboot.service && sudo systemctl
# daemon-reload`

vars:
  delay_seconds: "60"
  container: traefik
  unit_path: /etc/systemd/system/traefik-postboot.service

steps:
  - name: Install /etc/systemd/system/traefik-postboot.service
    # Unconditional rewrite — the unit is 12 lines and the cost of a
    # rewrite + daemon-reload is sub-second. cmp-then-skip would be
    # over-engineering. The systemctl enable below is gated separately.
    shell: |
      sudo tee {{ unit_path }} >/dev/null <<'UNIT'
      [Unit]
      Description=Restart {{ container }} {{ delay_seconds }}s after boot for clean topology scan
      After=docker.service
      Wants=docker.service

      [Service]
      Type=oneshot
      ExecStartPre=/bin/sleep {{ delay_seconds }}
      ExecStart=/usr/bin/docker restart {{ container }}

      [Install]
      WantedBy=multi-user.target
      UNIT
      sudo chmod 0644 {{ unit_path }}
    sudo: true

  - name: systemctl daemon-reload
    shell: systemctl daemon-reload
    sudo: true

  - name: Enable traefik-postboot.service (idempotent — skip if already enabled)
    shell: systemctl enable traefik-postboot.service
    sudo: true
    when: "! systemctl is-enabled --quiet traefik-postboot.service"

verify:
  - name: Unit file exists with mode 0644
    shell: test -f {{ unit_path }} && [ "$(stat -c %a {{ unit_path }})" = "644" ]
    changed_when: "false"

  - name: Unit is enabled
    shell: systemctl is-enabled --quiet traefik-postboot.service
    changed_when: "false"

  - name: Unit references the right container in ExecStart
    shell: grep -q 'docker restart {{ container }}' {{ unit_path }}
    changed_when: "false"

  - name: Container '{{ container }}' actually exists on this host (catches typos)
    shell: docker inspect {{ container }} >/dev/null 2>&1
    changed_when: "false"

  - name: systemd-analyze parses the unit cleanly
    # Lints the unit for syntax / dependency / Type errors without
    # actually executing it (executing would needlessly bounce traefik
    # right after install on already-healthy hosts).
    shell: systemd-analyze verify {{ unit_path }} 2>&1
    changed_when: "false"
