Make deploy scripts environment-agnostic, update paths for new structure

- deploy-haproxy: clear hardcoded beelink IPs from defaults, add
  require_config() validation, update load_password() for envs/*/env
- deploy-awx: clear hardcoded IPs, add require_config(), update
  MANIFESTS_DIR to data/awx-manifests
- deploy-observability: clear hardcoded IPs, add --config/parse_config()
  support, add require_config(), update DASHBOARDS_DIR to
  data/observability-dashboards
- manage-dashboards: update DASHBOARDS_DIR path
- incusos-proxmox: update load_env_file() to search envs/*/env
- helpers/proxmox-api, proxmox-screenshot: update load_env() and
  find_proxmox_yaml() for new envs/ layout
- diagnostic/*: update load_env() and proxmox.yaml discovery
- .claude/skills: update helper script paths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Maarten 2026-03-03 18:45:49 +01:00
parent 6ed083fd51
commit 855cbfa073
13 changed files with 321 additions and 75 deletions

View File

@ -16,7 +16,7 @@ Make an authenticated Proxmox API call with proper token handling.
1. Run the helper script: 1. Run the helper script:
```bash ```bash
/home/maarten/dev/incus-contrib/incusos/helpers/proxmox-api {{args}} /home/maarten/dev/incus-contrib/bin/helpers/proxmox-api {{args}}
``` ```
2. Parse the JSON response 2. Parse the JSON response
3. Present the results in a readable format 3. Present the results in a readable format

View File

@ -16,7 +16,7 @@ Take a console screenshot of a Proxmox VM and display it.
1. Run the helper script to capture the screenshot: 1. Run the helper script to capture the screenshot:
```bash ```bash
/home/maarten/dev/incus-contrib/incusos/helpers/proxmox-screenshot {{vmid}} /home/maarten/dev/incus-contrib/bin/helpers/proxmox-screenshot {{vmid}}
``` ```
2. Read the output path from stdout 2. Read the output path from stdout
3. Use the Read tool to view the PNG image 3. Use the Read tool to view the PNG image

View File

@ -13,7 +13,7 @@ set -euo pipefail
readonly VERSION="0.1.0" readonly VERSION="0.1.0"
readonly SCRIPT_NAME="deploy-awx" readonly SCRIPT_NAME="deploy-awx"
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly MANIFESTS_DIR="${SCRIPT_DIR}/awx-manifests" readonly MANIFESTS_DIR="${SCRIPT_DIR}/data/awx-manifests"
readonly AWX_NAMESPACE="awx" readonly AWX_NAMESPACE="awx"
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@ -21,20 +21,20 @@ readonly AWX_NAMESPACE="awx"
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
VM_NAME="awx" VM_NAME="awx"
TARGET_REMOTE="oc-node-02" TARGET_REMOTE=""
TARGET_NODE="" # Node within cluster for --target; defaults to TARGET_REMOTE if empty TARGET_NODE="" # Node within cluster for --target; defaults to TARGET_REMOTE if empty
AWX_IP="192.168.102.161" AWX_IP=""
AWX_PREFIX="22" AWX_PREFIX="22"
AWX_GATEWAY="192.168.100.1" AWX_GATEWAY=""
AWX_DNS="192.168.100.1" AWX_DNS=""
AWX_CPU="4" AWX_CPU="4"
AWX_MEMORY="8GiB" AWX_MEMORY="8GiB"
AWX_DISK="40GiB" AWX_DISK="40GiB"
GIT_REPO="ssh://git@192.168.1.200:2222/maarten/incus-contrib.git" GIT_REPO=""
GIT_BRANCH="master" GIT_BRANCH="master"
PLAYBOOK_DIR="playbooks" PLAYBOOK_DIR="playbooks"
AETHER_URL="https://192.168.102.160:8443" AETHER_URL=""
AETHER_CLUSTER_ID="52" AETHER_CLUSTER_ID=""
# Runtime flags # Runtime flags
DRY_RUN=false DRY_RUN=false
@ -1196,6 +1196,21 @@ parse_args() {
# Main # Main
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
require_config() {
local missing=()
[[ -z "$TARGET_REMOTE" ]] && missing+=("target_remote")
[[ -z "$AWX_IP" ]] && missing+=("ip")
[[ -z "$AWX_GATEWAY" ]] && missing+=("gateway")
[[ -z "$AWX_DNS" ]] && missing+=("dns")
[[ -z "$AETHER_URL" ]] && missing+=("aether_url")
[[ -z "$AETHER_CLUSTER_ID" ]] && missing+=("aether_cluster_id")
if [[ ${#missing[@]} -gt 0 ]]; then
error "Required config missing: ${missing[*]}"
error "Use --config FILE to load environment settings (see envs/)"
exit 1
fi
}
main() { main() {
setup_colors setup_colors
parse_args "$@" parse_args "$@"
@ -1205,6 +1220,11 @@ main() {
parse_config "$CONFIG_FILE" parse_config "$CONFIG_FILE"
fi fi
# Validate required fields (must come from --config or CLI flags)
if [[ "$ACTION" != "doctor" ]]; then
require_config
fi
echo echo
case "$ACTION" in case "$ACTION" in
deploy) action_deploy;; deploy) action_deploy;;

View File

@ -16,27 +16,26 @@ set -euo pipefail
readonly VERSION="0.1.0" readonly VERSION="0.1.0"
readonly SCRIPT_NAME="deploy-haproxy" readonly SCRIPT_NAME="deploy-haproxy"
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly ENV_FILE="${SCRIPT_DIR}/../env"
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Defaults (overridden by config file or CLI flags) # Defaults (overridden by config file or CLI flags)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
CLUSTER_REMOTE="oc-node-01" CLUSTER_REMOTE=""
CLUSTER_ID="52" CLUSTER_ID=""
OVN_NETWORK="net-prod" OVN_NETWORK="net-prod"
VIP="192.168.103.200" VIP=""
HAPROXY_01_IP="10.10.10.50" HAPROXY_01_IP=""
HAPROXY_02_IP="10.10.10.51" HAPROXY_02_IP=""
HAPROXY_CPU="2" HAPROXY_CPU="2"
HAPROXY_MEMORY="1GB" HAPROXY_MEMORY="1GB"
BACKEND_IPS="10.10.10.60,10.10.10.61,10.10.10.62" BACKEND_IPS=""
BACKEND_PORT="80" BACKEND_PORT="80"
SERVICE_NAME="web-test" SERVICE_NAME="web-test"
SERVICE_MODE="http" SERVICE_MODE="http"
SERVICE_BALANCE="roundrobin" SERVICE_BALANCE="roundrobin"
IMAGE_VERSION="1.0.0" IMAGE_VERSION="1.0.0"
AETHER_URL="https://192.168.102.160:8443" AETHER_URL=""
AETHER_USER="admin" AETHER_USER="admin"
AETHER_PASSWORD="" AETHER_PASSWORD=""
@ -197,18 +196,29 @@ load_password() {
return 0 return 0
fi fi
if [[ -f "$ENV_FILE" ]]; then # Check if already exported (user sourced envs/*/env)
local val if [[ -n "${AETHER_ADMIN_PASSWORD:-}" ]]; then
val=$(grep 'AETHER_ADMIN_PASSWORD=' "$ENV_FILE" | head -1 | sed 's/^.*AETHER_ADMIN_PASSWORD=//' | tr -d '"' | tr -d "'" || true) AETHER_PASSWORD="$AETHER_ADMIN_PASSWORD"
if [[ -n "$val" ]]; then return 0
AETHER_PASSWORD="$val"
detail "Password loaded from env file"
return 0
fi
fi fi
# Search for env file: walk up from script dir
local dir="$SCRIPT_DIR"
while [[ "$dir" != "/" ]]; do
if [[ -f "${dir}/env" ]]; then
local val
val=$(grep 'AETHER_ADMIN_PASSWORD=' "${dir}/env" | head -1 | sed 's/^.*AETHER_ADMIN_PASSWORD=//' | tr -d '"' | tr -d "'" || true)
if [[ -n "$val" ]]; then
AETHER_PASSWORD="$val"
detail "Password loaded from ${dir}/env"
return 0
fi
fi
dir="$(dirname "$dir")"
done
error "Aether password not found" error "Aether password not found"
error "Set AETHER_ADMIN_PASSWORD in ${ENV_FILE} or use --password" error "Source envs/<env>/env or use --password"
return 1 return 1
} }
@ -1190,6 +1200,22 @@ parse_args() {
# Main # Main
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
require_config() {
local missing=()
[[ -z "$CLUSTER_REMOTE" ]] && missing+=("cluster_remote")
[[ -z "$CLUSTER_ID" ]] && missing+=("cluster_id")
[[ -z "$VIP" ]] && missing+=("vip")
[[ -z "$HAPROXY_01_IP" ]] && missing+=("haproxy_01_ip")
[[ -z "$HAPROXY_02_IP" ]] && missing+=("haproxy_02_ip")
[[ -z "$BACKEND_IPS" ]] && missing+=("backend_ips")
[[ -z "$AETHER_URL" ]] && missing+=("aether_url")
if [[ ${#missing[@]} -gt 0 ]]; then
error "Required config missing: ${missing[*]}"
error "Use --config FILE to load environment settings (see envs/)"
exit 1
fi
}
main() { main() {
setup_colors setup_colors
parse_args "$@" parse_args "$@"
@ -1199,6 +1225,11 @@ main() {
parse_config "$CONFIG_FILE" parse_config "$CONFIG_FILE"
fi fi
# Validate required fields (must come from --config or CLI flags)
if [[ "$ACTION" != "doctor" ]]; then
require_config
fi
echo echo
case "$ACTION" in case "$ACTION" in
deploy) action_deploy;; deploy) action_deploy;;

View File

@ -14,29 +14,29 @@ set -euo pipefail
readonly VERSION="0.1.0" readonly VERSION="0.1.0"
readonly SCRIPT_NAME="deploy-observability" readonly SCRIPT_NAME="deploy-observability"
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly DASHBOARDS_DIR="${SCRIPT_DIR}/observability-dashboards" readonly DASHBOARDS_DIR="${SCRIPT_DIR}/data/observability-dashboards"
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Defaults (overridden by CLI flags) # Defaults (overridden by config file or CLI flags)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
CLUSTER_REMOTE="oc-node-01" CLUSTER_REMOTE=""
OVN_NETWORK="net-prod" OVN_NETWORK="net-prod"
MONITORING_IP="10.10.10.70" MONITORING_IP="10.10.10.70"
MONITORING_TARGET="oc-node-02" MONITORING_TARGET=""
FORWARD_VIP="192.168.103.201" FORWARD_VIP=""
# Fallback defaults — used when cluster is unreachable (e.g., --dry-run) # Fallback defaults — used when cluster is unreachable (e.g., --dry-run)
DEFAULT_INCUS_NODES=("192.168.102.140" "192.168.102.141" "192.168.102.142") DEFAULT_INCUS_NODES=()
DEFAULT_NODE_EXP_TARGETS=("oc-node-01" "oc-node-02" "oc-node-03") DEFAULT_NODE_EXP_TARGETS=()
DEFAULT_NODE_EXP_IPS=("10.10.10.71" "10.10.10.72" "10.10.10.73") DEFAULT_NODE_EXP_IPS=()
INCUS_NODES=() INCUS_NODES=()
NODE_EXP_TARGETS=() NODE_EXP_TARGETS=()
NODE_EXP_IPS=() NODE_EXP_IPS=()
HAPROXY_IPS=("10.10.10.50" "10.10.10.51") HAPROXY_IPS=()
HAPROXY_CLUSTER_ID="52" HAPROXY_CLUSTER_ID=""
OC_SERVER_IP="" OC_SERVER_IP=""
# Runtime flags # Runtime flags
@ -44,6 +44,7 @@ DRY_RUN=false
VERBOSE=false VERBOSE=false
QUIET=false QUIET=false
RESUME=false RESUME=false
CONFIG_FILE=""
ACTION="" ACTION=""
MANUAL_NODES="" MANUAL_NODES=""
@ -1704,6 +1705,48 @@ except Exception as e:
fi fi
} }
# ---------------------------------------------------------------------------
# Config file parsing
# ---------------------------------------------------------------------------
parse_config() {
local file="$1"
if [[ ! -f "$file" ]]; then
error "Config file not found: $file"
exit 1
fi
local val
val=$(grep -A50 '^observability:' "$file" | grep '^ *cluster_remote:' | head -1 | sed 's/.*: *//' | tr -d '"' || true)
[[ -n "$val" ]] && CLUSTER_REMOTE="$val"
val=$(grep -A50 '^observability:' "$file" | grep '^ *ovn_network:' | head -1 | sed 's/.*: *//' | tr -d '"' || true)
[[ -n "$val" ]] && OVN_NETWORK="$val"
val=$(grep -A50 '^observability:' "$file" | grep '^ *monitoring_ip:' | head -1 | sed 's/.*: *//' | tr -d '"' || true)
[[ -n "$val" ]] && MONITORING_IP="$val"
val=$(grep -A50 '^observability:' "$file" | grep '^ *monitoring_target:' | head -1 | sed 's/.*: *//' | tr -d '"' || true)
[[ -n "$val" ]] && MONITORING_TARGET="$val"
val=$(grep -A50 '^observability:' "$file" | grep '^ *forward_vip:' | head -1 | sed 's/.*: *//' | tr -d '"' || true)
[[ -n "$val" ]] && FORWARD_VIP="$val"
val=$(grep -A50 '^observability:' "$file" | grep '^ *incus_nodes:' | head -1 | sed 's/.*: *//' | tr -d '"' || true)
if [[ -n "$val" ]]; then
IFS=',' read -ra DEFAULT_INCUS_NODES <<< "$val"
fi
val=$(grep -A50 '^observability:' "$file" | grep '^ *node_exp_targets:' | head -1 | sed 's/.*: *//' | tr -d '"' || true)
if [[ -n "$val" ]]; then
IFS=',' read -ra DEFAULT_NODE_EXP_TARGETS <<< "$val"
fi
val=$(grep -A50 '^observability:' "$file" | grep '^ *node_exp_ips:' | head -1 | sed 's/.*: *//' | tr -d '"' || true)
if [[ -n "$val" ]]; then
IFS=',' read -ra DEFAULT_NODE_EXP_IPS <<< "$val"
fi
val=$(grep -A50 '^observability:' "$file" | grep '^ *haproxy_ips:' | head -1 | sed 's/.*: *//' | tr -d '"' || true)
if [[ -n "$val" ]]; then
IFS=',' read -ra HAPROXY_IPS <<< "$val"
fi
val=$(grep -A50 '^observability:' "$file" | grep '^ *haproxy_cluster_id:' | head -1 | sed 's/.*: *//' | tr -d '"' || true)
[[ -n "$val" ]] && HAPROXY_CLUSTER_ID="$val"
}
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Argument parsing # Argument parsing
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@ -1716,6 +1759,7 @@ parse_args() {
--status) ACTION="status";; --status) ACTION="status";;
--cleanup) ACTION="cleanup";; --cleanup) ACTION="cleanup";;
--doctor) ACTION="doctor";; --doctor) ACTION="doctor";;
-c|--config) CONFIG_FILE="$2"; shift;;
-r|--remote) CLUSTER_REMOTE="$2"; shift;; -r|--remote) CLUSTER_REMOTE="$2"; shift;;
--nodes) MANUAL_NODES="$2"; shift;; --nodes) MANUAL_NODES="$2"; shift;;
--monitoring-target) MONITORING_TARGET="$2"; shift;; --monitoring-target) MONITORING_TARGET="$2"; shift;;
@ -1743,6 +1787,18 @@ parse_args() {
fi fi
} }
require_config() {
local missing=()
[[ -z "$CLUSTER_REMOTE" ]] && missing+=("cluster_remote")
[[ -z "$FORWARD_VIP" ]] && missing+=("forward_vip")
[[ -z "$MONITORING_TARGET" ]] && missing+=("monitoring_target")
if [[ ${#missing[@]} -gt 0 ]]; then
error "Required config missing: ${missing[*]}"
error "Use --config FILE to load environment settings (see envs/)"
exit 1
fi
}
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Main # Main
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@ -1751,6 +1807,16 @@ main() {
setup_colors setup_colors
parse_args "$@" parse_args "$@"
# Load config file if provided
if [[ -n "$CONFIG_FILE" ]]; then
parse_config "$CONFIG_FILE"
fi
# Validate required fields (must come from --config or CLI flags)
if [[ "$ACTION" != "doctor" ]]; then
require_config
fi
# Discover cluster nodes (populates INCUS_NODES, NODE_EXP_TARGETS, NODE_EXP_IPS) # Discover cluster nodes (populates INCUS_NODES, NODE_EXP_TARGETS, NODE_EXP_IPS)
discover_cluster_nodes discover_cluster_nodes

View File

@ -163,6 +163,20 @@ done
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
load_env() { load_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" local dir="$SCRIPT_DIR"
while [[ "$dir" != "/" ]]; do while [[ "$dir" != "/" ]]; do
if [[ -f "${dir}/env" ]]; then if [[ -f "${dir}/env" ]]; then

View File

@ -86,6 +86,16 @@ done
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
load_env() { load_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
[[ -f "$env_file" ]] && { source "$env_file"; [[ -n "${PROXMOX_TOKEN_SECRET:-}" ]] && return; }
done
# Walk up from script dir (backward compat)
local dir="$SCRIPT_DIR" local dir="$SCRIPT_DIR"
while [[ "$dir" != "/" ]]; do while [[ "$dir" != "/" ]]; do
[[ -f "${dir}/env" ]] && { source "${dir}/env"; return; } [[ -f "${dir}/env" ]] && { source "${dir}/env"; return; }
@ -97,9 +107,21 @@ load_env
[[ -z "${PROXMOX_TOKEN_SECRET:-}" ]] && { error "PROXMOX_TOKEN_SECRET not set"; exit 1; } [[ -z "${PROXMOX_TOKEN_SECRET:-}" ]] && { error "PROXMOX_TOKEN_SECRET not set"; exit 1; }
[[ -z "${PROXMOX_ROOT_PASSWORD:-}" ]] && { error "PROXMOX_ROOT_PASSWORD not set"; exit 1; } [[ -z "${PROXMOX_ROOT_PASSWORD:-}" ]] && { error "PROXMOX_ROOT_PASSWORD not set"; exit 1; }
find_proxmox_yaml() {
local repo_root
repo_root=$(cd "$SCRIPT_DIR/../.." && pwd)
for f in "${repo_root}"/envs/*/proxmox.yaml; do
[[ -f "$f" ]] && { echo "$f"; return; }
done
[[ -f "${repo_root}/proxmox.yaml" ]] && { echo "${repo_root}/proxmox.yaml"; return; }
echo ""
}
PROXMOX_YAML=$(find_proxmox_yaml)
[[ -z "$PROXMOX_YAML" ]] && { error "proxmox.yaml not found"; exit 1; }
yaml_get() { yaml_get() {
local val local val
val=$(awk -F': ' -v k="$1" '$1 == k {print $2; exit}' "${SCRIPT_DIR}/proxmox.yaml" 2>/dev/null) || val="" val=$(awk -F': ' -v k="$1" '$1 == k {print $2; exit}' "$PROXMOX_YAML" 2>/dev/null) || val=""
val="${val#\"}" ; val="${val%\"}" ; val="${val#\'}" ; val="${val%\'}" val="${val#\"}" ; val="${val%\"}" ; val="${val#\'}" ; val="${val%\'}"
echo "${val:-${2:-}}" echo "${val:-${2:-}}"
} }
@ -448,7 +470,7 @@ for ((i=0; i<COUNT; i++)); do
import subprocess, os, sys import subprocess, os, sys
# Read token_id from proxmox.yaml # Read token_id from proxmox.yaml
token_id = '' token_id = ''
yaml_file = '${SCRIPT_DIR}/proxmox.yaml' yaml_file = '${PROXMOX_YAML}'
for line in open(yaml_file): for line in open(yaml_file):
line = line.strip() line = line.strip()
if line.startswith('api_token_id:'): if line.startswith('api_token_id:'):

View File

@ -30,7 +30,8 @@ set -euo pipefail
readonly VERSION="0.1.0" readonly VERSION="0.1.0"
readonly SCRIPT_NAME="observe-deploy" readonly SCRIPT_NAME="observe-deploy"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SEED_TOOL="${SCRIPT_DIR}/incusos-seed" BIN_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
SEED_TOOL="${BIN_DIR}/incusos-seed"
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Defaults # Defaults
@ -184,6 +185,20 @@ done
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
load_env() { load_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" local dir="$SCRIPT_DIR"
while [[ "$dir" != "/" ]]; do while [[ "$dir" != "/" ]]; do
if [[ -f "${dir}/env" ]]; then if [[ -f "${dir}/env" ]]; then
@ -222,13 +237,27 @@ yaml_get() {
load_proxmox_yaml() { load_proxmox_yaml() {
local yaml_file="" local yaml_file=""
if [[ -f "${SCRIPT_DIR}/proxmox.yaml" ]]; then local repo_root
yaml_file="${SCRIPT_DIR}/proxmox.yaml" repo_root=$(cd "$SCRIPT_DIR/../.." && pwd)
elif [[ -f "proxmox.yaml" ]]; then
yaml_file="proxmox.yaml" # Check envs/*/proxmox.yaml
else for f in "${repo_root}"/envs/*/proxmox.yaml; do
error "proxmox.yaml not found" if [[ -f "$f" ]]; then
exit 1 yaml_file="$f"
break
fi
done
# Backward compat: repo root or cwd
if [[ -z "$yaml_file" ]]; then
if [[ -f "${repo_root}/proxmox.yaml" ]]; then
yaml_file="${repo_root}/proxmox.yaml"
elif [[ -f "proxmox.yaml" ]]; then
yaml_file="proxmox.yaml"
else
error "proxmox.yaml not found"
exit 1
fi
fi fi
PVE_HOST=$(yaml_get "$yaml_file" "host") PVE_HOST=$(yaml_get "$yaml_file" "host")
@ -1026,7 +1055,7 @@ setup_output() {
fi fi
fi fi
RUN_DIR="${SCRIPT_DIR}/observe-runs/${timestamp}_${LABEL}" RUN_DIR="${BIN_DIR}/diagnostic/observe-runs/${timestamp}_${LABEL}"
mkdir -p "$RUN_DIR" mkdir -p "$RUN_DIR"
LOG_FILE="${RUN_DIR}/deploy.log" LOG_FILE="${RUN_DIR}/deploy.log"
@ -1315,7 +1344,7 @@ else
echo "" echo ""
# Output directories # Output directories
echo " Output: ${SCRIPT_DIR}/observe-runs/*${BASE_LABEL}*" echo " Output: ${BIN_DIR}/diagnostic/observe-runs/*${BASE_LABEL}*"
echo "" echo ""
if [[ $total_fail -gt 0 ]]; then if [[ $total_fail -gt 0 ]]; then

View File

@ -97,8 +97,21 @@ done
# Load environment # Load environment
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Auto-load env file for PROXMOX_TOKEN_SECRET
load_env() { load_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" local dir="$SCRIPT_DIR"
while [[ "$dir" != "/" ]]; do while [[ "$dir" != "/" ]]; do
if [[ -f "${dir}/env" ]]; then if [[ -f "${dir}/env" ]]; then

View File

@ -78,6 +78,21 @@ EXTRA_ARGS=("${ARGS[@]:2}")
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
load_env() { 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" local dir="$SCRIPT_DIR"
while [[ "$dir" != "/" ]]; do while [[ "$dir" != "/" ]]; do
if [[ -f "${dir}/env" ]]; then if [[ -f "${dir}/env" ]]; then
@ -109,9 +124,21 @@ yaml_get() {
} }
find_proxmox_yaml() { 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=( local search_dirs=(
"${SCRIPT_DIR}/.." "${SCRIPT_DIR}/.."
"${SCRIPT_DIR}/../.." "${repo_root}"
"$(pwd)" "$(pwd)"
) )
for dir in "${search_dirs[@]}"; do for dir in "${search_dirs[@]}"; do
@ -119,10 +146,6 @@ find_proxmox_yaml() {
echo "${dir}/proxmox.yaml" echo "${dir}/proxmox.yaml"
return return
fi fi
if [[ -f "${dir}/incusos/proxmox.yaml" ]]; then
echo "${dir}/incusos/proxmox.yaml"
return
fi
done done
echo "" echo ""
} }

View File

@ -71,6 +71,21 @@ fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
load_env() { load_env() {
# Already exported (user sourced envs/*/env)
[[ -n "${PROXMOX_ROOT_PASSWORD:-}" ]] && 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_ROOT_PASSWORD:-}" ]] && return
fi
done
# Walk up from script dir (backward compat)
local dir="$SCRIPT_DIR" local dir="$SCRIPT_DIR"
while [[ "$dir" != "/" ]]; do while [[ "$dir" != "/" ]]; do
if [[ -f "${dir}/env" ]]; then if [[ -f "${dir}/env" ]]; then
@ -102,10 +117,21 @@ yaml_get() {
} }
find_proxmox_yaml() { find_proxmox_yaml() {
# Search: script's grandparent (repo/incusos -> repo), script parent, cwd 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=( local search_dirs=(
"${SCRIPT_DIR}/.." "${SCRIPT_DIR}/.."
"${SCRIPT_DIR}/../.." "${repo_root}"
"$(pwd)" "$(pwd)"
) )
for dir in "${search_dirs[@]}"; do for dir in "${search_dirs[@]}"; do
@ -113,10 +139,6 @@ find_proxmox_yaml() {
echo "${dir}/proxmox.yaml" echo "${dir}/proxmox.yaml"
return return
fi fi
if [[ -f "${dir}/incusos/proxmox.yaml" ]]; then
echo "${dir}/incusos/proxmox.yaml"
return
fi
done done
echo "" echo ""
} }

View File

@ -22,10 +22,10 @@ readonly MANAGED_MARKER="[incusos-lab:managed]"
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Auto-load env file (secrets like PROXMOX_TOKEN_SECRET) # Auto-load env file (secrets like PROXMOX_TOKEN_SECRET)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Searches for an 'env' file: first next to the script, then walking up from # Searches for an 'env' file. Sources it if found and not already loaded.
# the current directory. Sources it if found and not already loaded. This is # Priority: already exported > envs/*/env in repo > walk up from cwd.
# a convenience -- users can still export variables manually or source the # This is a convenience -- users can still export variables manually or
# file themselves. # source the file themselves (e.g., source envs/hetzner/env).
load_env_file() { load_env_file() {
# Skip if the secret is already set (user exported it, or env already sourced) # Skip if the secret is already set (user exported it, or env already sourced)
@ -35,18 +35,24 @@ load_env_file() {
local script_real local script_real
script_real=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) script_real=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
local repo_root
repo_root=$(cd "$script_real/.." && pwd)
# Check: script directory's parent (repo root when script is in incusos/) # Check: envs/*/env in repo root (per-environment secrets)
if [[ -f "${script_real}/../env" ]]; then for env_file in "${repo_root}"/envs/*/env; do
# shellcheck source=/dev/null if [[ -f "$env_file" ]]; then
source "${script_real}/../env" # shellcheck source=/dev/null
return 0 source "$env_file"
fi if [[ -n "${PROXMOX_TOKEN_SECRET:-}" ]]; then
return 0
fi
fi
done
# Check: script directory itself # Check: repo root env file (backward compat)
if [[ -f "${script_real}/env" ]]; then if [[ -f "${repo_root}/env" ]]; then
# shellcheck source=/dev/null # shellcheck source=/dev/null
source "${script_real}/env" source "${repo_root}/env"
return 0 return 0
fi fi

View File

@ -13,7 +13,7 @@ set -euo pipefail
readonly VERSION="0.1.0" readonly VERSION="0.1.0"
readonly SCRIPT_NAME="manage-dashboards" readonly SCRIPT_NAME="manage-dashboards"
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly DASHBOARDS_DIR="${SCRIPT_DIR}/observability-dashboards" readonly DASHBOARDS_DIR="${SCRIPT_DIR}/data/observability-dashboards"
readonly MANIFEST_FILE="${DASHBOARDS_DIR}/dashboard.yaml" readonly MANIFEST_FILE="${DASHBOARDS_DIR}/dashboard.yaml"
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------