Files
PSProxmoxVE/tests/infrastructure/scripts/create-api-token.sh
T
Clint Branham ddecafee91 feat(ci): automated nested PVE provisioning for integration tests
Workflow now provisions a throwaway nested PVE VM via Terraform, runs
integration tests against it, then destroys it. Uses proxmox-auto-install-assistant
to bake the answer file and a first-boot script (installs qemu-guest-agent)
directly into the ISO. IP is discovered via the QEMU guest agent on the
parent PVE, eliminating the need for static IP configuration.

Supports both PVE 8.x and 9.x via workflow_dispatch version selector.
Skip provisioning with skip_provision=true to test against a pre-existing PVE.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 14:13:41 -05:00

105 lines
3.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# Wait for a fresh nested PVE instance to boot, discover its IP via the QEMU guest agent,
# then wait for the PVE API and create an API token.
#
# Usage: create-api-token.sh <parent-pve-host> <parent-api-token> <vm-id> <root-password> [max-wait-seconds]
# Outputs two lines:
# IP=<discovered-ip>
# TOKEN=root@pam!integration=<secret>
set -euo pipefail
PARENT_HOST="$1"
PARENT_TOKEN="$2"
VM_ID="$3"
ROOT_PASSWORD="$4"
MAX_WAIT="${5:-600}"
INTERVAL=10
PARENT_API="https://${PARENT_HOST}/api2/json"
PARENT_NODE=$(curl -sk -H "Authorization: PVEAPIToken=${PARENT_TOKEN}" \
"${PARENT_API}/nodes" | python3 -c "import json,sys; print(json.load(sys.stdin)['data'][0]['node'])")
# --- Phase 1: Discover IP via QEMU guest agent ---
echo "Waiting for guest agent on VM ${VM_ID} (node: ${PARENT_NODE})..."
VM_IP=""
elapsed=0
while [ $elapsed -lt $MAX_WAIT ]; do
AGENT_RESPONSE=$(curl -sk \
-H "Authorization: PVEAPIToken=${PARENT_TOKEN}" \
"${PARENT_API}/nodes/${PARENT_NODE}/qemu/${VM_ID}/agent/network-get-interfaces" 2>/dev/null || true)
VM_IP=$(echo "$AGENT_RESPONSE" | python3 -c "
import json, sys
try:
data = json.load(sys.stdin).get('data', {}).get('result', [])
for iface in data:
if iface.get('name') == 'lo':
continue
for addr in iface.get('ip-addresses', []):
if addr.get('ip-address-type') == 'ipv4' and not addr['ip-address'].startswith('127.'):
print(addr['ip-address'])
sys.exit(0)
except:
pass
" 2>/dev/null || true)
if [ -n "$VM_IP" ]; then
echo "Discovered VM IP: $VM_IP (after ${elapsed}s)"
break
fi
echo " Guest agent not ready yet (${elapsed}s elapsed)..."
sleep $INTERVAL
elapsed=$((elapsed + INTERVAL))
done
if [ -z "$VM_IP" ]; then
echo "ERROR: Could not discover VM IP via guest agent after ${MAX_WAIT}s" >&2
exit 1
fi
# --- Phase 2: Wait for PVE API on the nested instance ---
NESTED_API="https://${VM_IP}:8006/api2/json"
echo "Waiting for nested PVE API at ${NESTED_API}..."
while [ $elapsed -lt $MAX_WAIT ]; do
if curl -sk --connect-timeout 5 "${NESTED_API}/version" 2>/dev/null | grep -q '"version"'; then
echo "Nested PVE API is responsive after ${elapsed}s"
break
fi
echo " API not ready yet (${elapsed}s elapsed)..."
sleep $INTERVAL
elapsed=$((elapsed + INTERVAL))
done
if [ $elapsed -ge $MAX_WAIT ]; then
echo "ERROR: Nested PVE API not responsive after ${MAX_WAIT}s" >&2
exit 1
fi
# --- Phase 3: Authenticate and create API token ---
echo "Authenticating as root@pam on nested PVE..."
AUTH_RESPONSE=$(curl -sk -d "username=root@pam&password=${ROOT_PASSWORD}" \
"${NESTED_API}/access/ticket")
TICKET=$(echo "$AUTH_RESPONSE" | python3 -c "import json,sys; print(json.load(sys.stdin)['data']['ticket'])" 2>/dev/null || true)
CSRF=$(echo "$AUTH_RESPONSE" | python3 -c "import json,sys; print(json.load(sys.stdin)['data']['CSRFPreventionToken'])" 2>/dev/null || true)
if [ -z "$TICKET" ] || [ -z "$CSRF" ]; then
echo "ERROR: Authentication failed. Response: $AUTH_RESPONSE" >&2
exit 1
fi
echo "Creating API token root@pam!integration..."
TOKEN_RESPONSE=$(curl -sk \
-b "PVEAuthCookie=${TICKET}" \
-H "CSRFPreventionToken: ${CSRF}" \
-d "privsep=0" \
"${NESTED_API}/access/users/root@pam/token/integration")
TOKEN_VALUE=$(echo "$TOKEN_RESPONSE" | python3 -c "import json,sys; print(json.load(sys.stdin)['data']['value'])" 2>/dev/null || true)
if [ -z "$TOKEN_VALUE" ]; then
echo "ERROR: Token creation failed. Response: $TOKEN_RESPONSE" >&2
exit 1
fi
echo "IP=${VM_IP}"
echo "TOKEN=root@pam!integration=${TOKEN_VALUE}"