Files
ppf/tools/lib/ppf-common.sh
Username 9b8be9d302 tools: use toolkit inventory for all ansible commands
Route ansible_cmd through ppf inventory instead of /opt/ansible
default. Eliminates dynamic inventory warnings and connects
via WireGuard IPs.
2026-02-17 23:22:29 +01:00

171 lines
5.4 KiB
Bash

#!/bin/bash
# ppf-common.sh -- shared library for PPF operations toolkit
# Source this file; do not execute directly.
set -eu
# ---------------------------------------------------------------------------
# Paths
# ---------------------------------------------------------------------------
PPF_DIR="${PPF_DIR:-$HOME/git/ppf}"
ANSIBLE_DIR="/opt/ansible"
ANSIBLE_VENV="${ANSIBLE_DIR}/venv/bin/activate"
PPF_INVENTORY="${PPF_DIR}/tools/playbooks/inventory.ini"
# ---------------------------------------------------------------------------
# Host topology
# ---------------------------------------------------------------------------
MASTER="odin"
WORKERS="cassius edge sentinel"
ALL_HOSTS="odin cassius edge sentinel"
# Container names per role
MASTER_CONTAINER="ppf"
WORKER_CONTAINER="ppf-worker"
# ---------------------------------------------------------------------------
# Colors (respects NO_COLOR -- https://no-color.org)
# ---------------------------------------------------------------------------
if [ -z "${NO_COLOR:-}" ] && [ -t 1 ]; then
C_RST='\033[0m'
C_DIM='\033[2m'
C_BOLD='\033[1m'
C_RED='\033[38;5;167m'
C_GREEN='\033[38;5;114m'
C_YELLOW='\033[38;5;180m'
C_BLUE='\033[38;5;110m'
C_CYAN='\033[38;5;116m'
else
C_RST='' C_DIM='' C_BOLD='' C_RED='' C_GREEN=''
C_YELLOW='' C_BLUE='' C_CYAN=''
fi
# ---------------------------------------------------------------------------
# Output helpers
# ---------------------------------------------------------------------------
log_ok() { printf "${C_GREEN}${C_RST} %s\n" "$*"; }
log_err() { printf "${C_RED}${C_RST} %s\n" "$*" >&2; }
log_warn() { printf "${C_YELLOW}${C_RST} %s\n" "$*"; }
log_info() { printf "${C_BLUE}${C_RST} %s\n" "$*"; }
log_dim() { printf "${C_DIM} %s${C_RST}\n" "$*"; }
die() { log_err "$@"; exit 1; }
# Section header
section() {
printf "\n${C_BOLD}${C_CYAN} %s${C_RST}\n" "$*"
}
# ---------------------------------------------------------------------------
# Host resolution helpers
# ---------------------------------------------------------------------------
is_master() { [ "$1" = "$MASTER" ]; }
is_worker() {
local h
for h in $WORKERS; do [ "$h" = "$1" ] && return 0; done
return 1
}
container_name() {
if is_master "$1"; then echo "$MASTER_CONTAINER"; else echo "$WORKER_CONTAINER"; fi
}
# Expand target aliases into host list
# "all" -> all hosts
# "workers" -> worker hosts
# "odin" -> just odin
# Multiple args are concatenated with comma
resolve_targets() {
local targets=""
local arg
for arg in "$@"; do
case "$arg" in
all) targets="${targets:+$targets }$ALL_HOSTS" ;;
workers) targets="${targets:+$targets }$WORKERS" ;;
master) targets="${targets:+$targets }$MASTER" ;;
*) targets="${targets:+$targets }$arg" ;;
esac
done
# Deduplicate while preserving order
echo "$targets" | tr ' ' '\n' | awk '!seen[$0]++' | tr '\n' ' ' | sed 's/ $//'
}
# Convert space-separated host list to comma-separated for ansible
hosts_csv() {
echo "$*" | tr ' ' ','
}
# ---------------------------------------------------------------------------
# Ansible wrapper
# ---------------------------------------------------------------------------
# Runs ansible with toolkit inventory via venv.
# Usage: ansible_cmd <ansible args...>
ansible_cmd() {
(
# shellcheck disable=SC1090
. "$ANSIBLE_VENV"
cd "$ANSIBLE_DIR"
ansible -i "$PPF_INVENTORY" "$@"
)
}
# Runs ansible-playbook with toolkit inventory via venv.
# Usage: ansible_playbook_cmd <ansible-playbook args...>
ansible_playbook_cmd() {
(
# shellcheck disable=SC1090
. "$ANSIBLE_VENV"
cd "$ANSIBLE_DIR"
ansible-playbook "$@"
)
}
# ---------------------------------------------------------------------------
# Remote podman/compose wrappers
# ---------------------------------------------------------------------------
# Run a podman command on a remote host as the podman user.
# Uses dynamic UID discovery.
# Usage: podman_cmd HOST "podman subcommand..."
podman_cmd() {
local host="$1"; shift
local cmd="$*"
ansible_cmd "$host" -m raw -a \
"uid=\$(id -u podman) && sudo -u podman XDG_RUNTIME_DIR=/run/user/\$uid $cmd"
}
# Run a podman-compose subcommand on a remote host.
# Usage: compose_cmd HOST "subcommand [args]"
compose_cmd() {
local host="$1"; shift
local cmd="$*"
ansible_cmd "$host" -m raw -a \
"uid=\$(id -u podman) && cd /home/podman/ppf && sudo -u podman XDG_RUNTIME_DIR=/run/user/\$uid podman-compose $cmd"
}
# ---------------------------------------------------------------------------
# Validation
# ---------------------------------------------------------------------------
validate_syntax() {
local errors=0
local f
section "Validating Python syntax"
for f in "$PPF_DIR"/*.py; do
[ -f "$f" ] || continue
if python3 -m py_compile "$f" 2>/dev/null; then
log_dim "$(basename "$f")"
else
log_err "$(basename "$f")"
errors=$((errors + 1))
fi
done
if [ "$errors" -gt 0 ]; then
die "$errors file(s) failed syntax check"
fi
log_ok "All files valid"
}
# ---------------------------------------------------------------------------
# Version
# ---------------------------------------------------------------------------
PPF_TOOLS_VERSION="1.0.0"