---
# post-deploy.yml — Runs after Aether creates an instance
#
# Aether passes these extra vars (ffsdn_ prefix):
#   ffsdn_instance_name  — instance name
#   ffsdn_instance_ip    — IP address assigned by Aether
#   ffsdn_cluster_id     — numeric cluster ID
#   ffsdn_cluster_name   — cluster display name
#   ffsdn_deployed_by    — Aether username who triggered the deploy
#   ffsdn_image_os       — image OS (e.g., "Debian")
#   ffsdn_image_release  — image release (e.g., "bookworm")
#   ffsdn_image_alias    — image alias (may be empty)
#
# This playbook configures new instances via the Incus REST API
# (file push + exec), which works regardless of network topology.
# AWX does NOT need SSH access to the container — it talks to the
# Incus cluster API at the node level.
#
# Requirements:
#   - Incus client cert/key pushed to AWX task pod at
#     /var/lib/awx/projects/incus-contrib/ (mounted as /runner/project/
#     inside the EE container during job execution)
#   - Cluster API reachable from AWX (e.g., https://192.168.102.140:8443)

- name: Post-deploy — configure instance via Incus API
  hosts: localhost
  gather_facts: false
  connection: local

  vars:
    # Incus cluster API (any cluster member works)
    incus_api: "https://192.168.102.140:8443"
    # AWX runner mounts project at /runner/project/ during job execution
    incus_cert: "/runner/project/incus-client.crt"
    incus_key: "/runner/project/incus-client.key"
    base_packages: "curl vim htop jq tmux rsync unattended-upgrades"

  tasks:
    - name: Validate required variables
      ansible.builtin.assert:
        that:
          - ffsdn_instance_name is defined
          - ffsdn_instance_name | length > 0
        fail_msg: "ffsdn_instance_name must be provided by Aether"

    - name: Wait for instance to be running (up to 60s)
      ansible.builtin.uri:
        url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/state"
        method: GET
        client_cert: "{{ incus_cert }}"
        client_key: "{{ incus_key }}"
        validate_certs: false
        return_content: true
      register: instance_state
      until: instance_state.json.metadata.status == "Running"
      retries: 12
      delay: 5

    - name: Generate setup script
      ansible.builtin.set_fact:
        setup_script: |
          #!/bin/bash
          set -e

          echo "=== Post-deploy configuration starting ==="

          # Set hostname
          hostname {{ ffsdn_instance_name }}
          echo "{{ ffsdn_instance_name }}" > /etc/hostname
          sed -i '/^127\.0\.1\.1/d' /etc/hosts
          echo "127.0.1.1 {{ ffsdn_instance_name }}" >> /etc/hosts
          echo "[OK] Hostname set to {{ ffsdn_instance_name }}"

          # Update package cache and install base packages
          export DEBIAN_FRONTEND=noninteractive
          apt-get update -qq
          apt-get install -y -qq {{ base_packages }} 2>&1 | tail -1
          echo "[OK] Base packages installed"

          # Set timezone
          if command -v timedatectl >/dev/null 2>&1; then
            timedatectl set-timezone UTC
          else
            ln -sf /usr/share/zoneinfo/UTC /etc/localtime
            echo "UTC" > /etc/timezone
          fi
          echo "[OK] Timezone set to UTC"

          # Configure unattended upgrades
          cat > /etc/apt/apt.conf.d/20auto-upgrades << 'AUTOUPGRADE'
          APT::Periodic::Update-Package-Lists "1";
          APT::Periodic::Unattended-Upgrade "1";
          AUTOUPGRADE
          echo "[OK] Unattended upgrades configured"

          # Write deployment metadata
          cat > /etc/deploy-info << DEPLOYINFO
          hostname={{ ffsdn_instance_name }}
          ip={{ ffsdn_instance_ip | default('unknown') }}
          cluster={{ ffsdn_cluster_name | default('unknown') }}
          deployed_by={{ ffsdn_deployed_by | default('unknown') }}
          image={{ ffsdn_image_os | default('') }}/{{ ffsdn_image_release | default('') }}
          deployed=$(date -Iseconds)
          DEPLOYINFO
          echo "[OK] Deployment metadata written to /etc/deploy-info"

          echo "=== Post-deploy configuration complete ==="

    - name: Push setup script to instance
      ansible.builtin.uri:
        url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/files?path=/tmp/post-deploy-setup.sh"
        method: POST
        client_cert: "{{ incus_cert }}"
        client_key: "{{ incus_key }}"
        validate_certs: false
        body: "{{ setup_script }}"
        headers:
          Content-Type: "application/octet-stream"
          X-Incus-type: "file"
          X-Incus-mode: "0755"
        status_code: [200]

    - name: Execute setup script via Incus API
      ansible.builtin.uri:
        url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/exec"
        method: POST
        client_cert: "{{ incus_cert }}"
        client_key: "{{ incus_key }}"
        validate_certs: false
        body_format: json
        body:
          command: ["/bin/bash", "/tmp/post-deploy-setup.sh"]
          record-output: true
          interactive: false
          wait-for-websocket: false
          environment:
            DEBIAN_FRONTEND: "noninteractive"
        status_code: [202]
        return_content: true
      register: exec_result

    - name: Wait for setup script to complete (up to 5 minutes)
      ansible.builtin.uri:
        url: "{{ incus_api }}{{ exec_result.json.operation }}/wait?timeout=300"
        method: GET
        client_cert: "{{ incus_cert }}"
        client_key: "{{ incus_key }}"
        validate_certs: false
        return_content: true
      register: exec_wait
      failed_when: exec_wait.json.metadata.status != "Success"

    - name: Verify exit code
      ansible.builtin.assert:
        that:
          - exec_wait.json.metadata.metadata.return | int == 0
        fail_msg: "Setup script exited with code {{ exec_wait.json.metadata.metadata.return }}"
        success_msg: "Setup completed successfully (exit code 0)"

    - name: Verify deployment metadata was written
      ansible.builtin.uri:
        url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/files?path=/etc/deploy-info"
        method: GET
        client_cert: "{{ incus_cert }}"
        client_key: "{{ incus_key }}"
        validate_certs: false
        return_content: true
      register: deploy_info

    - name: Display deployment metadata
      ansible.builtin.debug:
        msg: "{{ deploy_info.content }}"

    - name: Clean up setup script
      ansible.builtin.uri:
        url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/exec"
        method: POST
        client_cert: "{{ incus_cert }}"
        client_key: "{{ incus_key }}"
        validate_certs: false
        body_format: json
        body:
          command: ["/bin/rm", "-f", "/tmp/post-deploy-setup.sh"]
          record-output: false
          interactive: false
          wait-for-websocket: false
        status_code: [202]
      ignore_errors: true

    - name: Record deployment in ledger
      ansible.builtin.lineinfile:
        path: /tmp/awx-deploy-ledger.log
        line: >-
          {{ lookup('pipe', 'date -Iseconds') }}
          DEPLOY instance={{ ffsdn_instance_name | default('unknown') }}
          ip={{ ffsdn_instance_ip | default('') }}
          cluster={{ ffsdn_cluster_name | default('unknown') }}
          deployed_by={{ ffsdn_deployed_by | default('unknown') }}
          image={{ ffsdn_image_os | default('') }}/{{ ffsdn_image_release | default('') }}
          status=success
        create: true
        mode: "0644"
