203 lines
7.4 KiB
YAML
203 lines
7.4 KiB
YAML
---
|
|
# 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://10.10.0.111: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"
|