incus-contrib/bin/helpers/proxmox-api

199 lines
5.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# proxmox-api -- Make authenticated Proxmox API calls
#
# Usage: proxmox-api METHOD PATH [CURL_ARGS...]
#
# Handles the ! in automation@pve!deploy safely (no bash history expansion).
# Config: proxmox.yaml (host, api_token_id), env file (PROXMOX_TOKEN_SECRET)
#
# Outputs JSON response on stdout.
set -euo pipefail
# ---------------------------------------------------------------------------
# Usage
# ---------------------------------------------------------------------------
usage() {
cat <<'EOF'
Usage: proxmox-api METHOD PATH [CURL_ARGS...]
Make authenticated Proxmox API calls with proper token handling.
Arguments:
METHOD HTTP method (GET, POST, PUT, DELETE)
PATH API path (e.g., /nodes/pve/qemu)
CURL_ARGS Additional curl arguments (e.g., -d 'key=val')
Options:
--json Pretty-print JSON output
--help, -h Show this help
Environment:
PROXMOX_TOKEN_SECRET API token secret (from env file)
Config:
proxmox.yaml Proxmox host + api_token_id (auto-discovered)
Examples:
proxmox-api GET /nodes/pve/qemu
proxmox-api GET /nodes/pve/qemu/900/status/current --json
proxmox-api POST /nodes/pve/qemu/900/status/stop
proxmox-api GET /cluster/resources --json
EOF
exit 0
}
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
usage
fi
# ---------------------------------------------------------------------------
# Parse arguments
# ---------------------------------------------------------------------------
PRETTY=false
ARGS=()
for arg in "$@"; do
if [[ "$arg" == "--json" ]]; then
PRETTY=true
else
ARGS+=("$arg")
fi
done
if [[ ${#ARGS[@]} -lt 2 ]]; then
echo "Error: METHOD and PATH required. Usage: proxmox-api METHOD PATH [CURL_ARGS...]" >&2
exit 1
fi
METHOD="${ARGS[0]}"
API_PATH="${ARGS[1]}"
EXTRA_ARGS=("${ARGS[@]:2}")
# ---------------------------------------------------------------------------
# Load environment
# ---------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
load_env() {
# Already exported (user sourced envs/*/env)
[[ -n "${PROXMOX_TOKEN_SECRET:-}" ]] && return
# Check envs/*/env in repo root
local repo_root
repo_root=$(cd "$SCRIPT_DIR/../.." && pwd)
for env_file in "${repo_root}"/envs/*/env; do
if [[ -f "$env_file" ]]; then
# shellcheck disable=SC1091
source "$env_file"
[[ -n "${PROXMOX_TOKEN_SECRET:-}" ]] && return
fi
done
# Walk up from script dir (backward compat)
local dir="$SCRIPT_DIR"
while [[ "$dir" != "/" ]]; do
if [[ -f "${dir}/env" ]]; then
# shellcheck disable=SC1091
source "${dir}/env"
return
fi
dir="$(dirname "$dir")"
done
}
load_env
if [[ -z "${PROXMOX_TOKEN_SECRET:-}" ]]; then
echo "Error: PROXMOX_TOKEN_SECRET not set. Create an env file or export it." >&2
exit 1
fi
# ---------------------------------------------------------------------------
# Load proxmox.yaml
# ---------------------------------------------------------------------------
yaml_get() {
local file="$1" key="$2" default="${3:-}"
local val
val=$(awk -F': ' -v k="$key" '$1 == k {print $2; exit}' "$file" 2>/dev/null) || val=""
val="${val#\"}" ; val="${val%\"}"
val="${val#\'}" ; val="${val%\'}"
echo "${val:-$default}"
}
find_proxmox_yaml() {
local repo_root
repo_root=$(cd "$SCRIPT_DIR/../.." && pwd)
# Check envs/*/proxmox.yaml in repo root
for f in "${repo_root}"/envs/*/proxmox.yaml; do
if [[ -f "$f" ]]; then
echo "$f"
return
fi
done
# Walk up from script dir (backward compat)
local search_dirs=(
"${SCRIPT_DIR}/.."
"${repo_root}"
"$(pwd)"
)
for dir in "${search_dirs[@]}"; do
if [[ -f "${dir}/proxmox.yaml" ]]; then
echo "${dir}/proxmox.yaml"
return
fi
done
echo ""
}
PROXMOX_YAML=$(find_proxmox_yaml)
if [[ -z "$PROXMOX_YAML" ]]; then
echo "Error: proxmox.yaml not found" >&2
exit 1
fi
PVE_HOST=$(yaml_get "$PROXMOX_YAML" "host")
PVE_TOKEN_ID=$(yaml_get "$PROXMOX_YAML" "api_token_id")
if [[ -z "$PVE_HOST" ]]; then
echo "Error: proxmox.yaml: 'host' is required" >&2
exit 1
fi
if [[ -z "$PVE_TOKEN_ID" ]]; then
echo "Error: proxmox.yaml: 'api_token_id' is required" >&2
exit 1
fi
# ---------------------------------------------------------------------------
# Make API call
# ---------------------------------------------------------------------------
# Build the full auth header in a variable (avoids ! expansion issues)
AUTH_HEADER="Authorization: PVEAPIToken=${PVE_TOKEN_ID}=${PROXMOX_TOKEN_SECRET}"
URL="https://${PVE_HOST}:8006/api2/json${API_PATH}"
CURL_CMD=(curl -fsSk -X "$METHOD" -H "$AUTH_HEADER")
if [[ ${#EXTRA_ARGS[@]} -gt 0 ]]; then
CURL_CMD+=("${EXTRA_ARGS[@]}")
fi
CURL_CMD+=("$URL")
RESPONSE=$("${CURL_CMD[@]}" 2>&1) || {
echo "Error: API call failed: $METHOD $API_PATH" >&2
echo "$RESPONSE" >&2
exit 1
}
if [[ "$PRETTY" == true ]]; then
echo "$RESPONSE" | python3 -m json.tool 2>/dev/null || echo "$RESPONSE"
else
echo "$RESPONSE"
fi