--- # ------------------------------------------------------------------------------ # FILE: playbooks/add_service_route.yml # DESCRIPTION: Ensures all services in the Traefik dynamic config directory # are routed and have DNS CNAME records on monorail. # # 1. Syncs boilerplates/traefik/dynamic/ to lightning-lane # 2. Scans the directory for service configs # 3. Extracts all hostnames from Host() rules (supports multi-host) # 4. Creates CNAME records for each hostname -> lightning-lane # # PREREQUISITES: # - Service dynamic config YAML committed to boilerplates/traefik/dynamic/ # - vault_technitium_api_key defined in group_vars/all/vault # # USAGE: # ansible-playbook -i inventory.yml playbooks/add_service_route.yml # ------------------------------------------------------------------------------ - name: Sync Traefik routes and ensure DNS records hosts: localhost connection: local gather_facts: false vars: base_domain: "local.mk-labs.cloud" dns_server: "monorail" traefik_host: "lightning-lane.local.mk-labs.cloud" traefik_user: "wed" traefik_dynamic_path: "/opt/docker/traefik/dynamic/" dynamic_config_dir: "{{ playbook_dir }}/../../boilerplates/traefik/dynamic" # Files in the dynamic directory that are NOT service routes exclude_configs: - default.yml tasks: # ── Step 1: Sync dynamic config to lightning-lane ── - name: Sync Traefik dynamic configuration to lightning-lane ansible.builtin.shell: > rsync -av --delete {{ dynamic_config_dir }}/ {{ traefik_user }}@{{ traefik_host }}:{{ traefik_dynamic_path }} register: sync_result changed_when: "'sending incremental file list' in sync_result.stdout" # ── Step 2: Discover hostnames from Traefik router rules ── - name: Find all dynamic config files ansible.builtin.find: paths: "{{ dynamic_config_dir }}" patterns: "*.yml" register: config_files - name: Read config files ansible.builtin.slurp: src: "{{ item.path }}" register: slurped_configs loop: "{{ config_files.files }}" when: item.path | basename not in exclude_configs - name: Extract all hostnames from Host() rules ansible.builtin.set_fact: hostnames: >- {% set hosts = [] -%} {% for result in slurped_configs.results if result.content is defined -%} {% set content = result.content | b64decode -%} {% for match in content | regex_findall('Host\(`([^`]+)`\)') -%} {% for h in match.split(' || ') -%} {% set h = h | regex_replace('`', '') | trim -%} {% if h.endswith('.local.mk-labs.cloud') and h not in hosts -%} {% set _ = hosts.append(h) -%} {% endif -%} {% endfor -%} {% endfor -%} {% endfor -%} {{ hosts | unique | list }} - name: Display hostnames to create ansible.builtin.debug: msg: "Hostnames found: {{ hostnames }}" # ── Step 3: Create DNS CNAME records ── - name: Create DNS CNAME record for each hostname ansible.builtin.uri: url: "http://{{ dns_server }}.{{ base_domain }}:5380/api/zones/records/add\ ?token={{ vault_technitium_api_key }}\ &domain={{ item }}\ &type=CNAME\ &cname=lightning-lane.{{ base_domain }}\ &ttl=360" method: GET return_content: true status_code: 200 register: dns_result failed_when: "(dns_result.json.status | default('error')) != 'ok'" loop: "{{ hostnames }}" loop_control: label: "{{ item }}"