tools: rewrite ppf-deploy as playbook wrapper

Replace sequential ansible ad-hoc calls with ansible-playbook.
Add ansible_playbook_cmd to shared library. Supports --check
for dry runs.
This commit is contained in:
Username
2026-02-17 23:18:46 +01:00
parent 8208670fc1
commit 782deab95d
2 changed files with 56 additions and 97 deletions

View File

@@ -108,6 +108,17 @@ ansible_cmd() {
)
}
# Runs ansible-playbook with venv activated and ANSIBLE_REMOTE_TMP set.
# Usage: ansible_playbook_cmd <ansible-playbook args...>
ansible_playbook_cmd() {
(
# shellcheck disable=SC1090
. "$ANSIBLE_VENV"
cd "$ANSIBLE_DIR"
ANSIBLE_REMOTE_TMP=/tmp/.ansible ansible-playbook "$@"
)
}
# ---------------------------------------------------------------------------
# Remote podman/compose wrappers
# ---------------------------------------------------------------------------

View File

@@ -2,7 +2,7 @@
# ppf-deploy -- deploy PPF code to nodes
#
# Usage:
# ppf-deploy [--no-restart] [targets...]
# ppf-deploy [options] [targets...]
#
# Targets:
# all odin + all workers (default)
@@ -12,18 +12,22 @@
set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# Resolve to real path (handles symlinks from ~/.local/bin/)
SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd)/$(basename "$0")"
SCRIPT_DIR="$(dirname "$(readlink -f "$SCRIPT_PATH")")"
# shellcheck disable=SC1091
. "$SCRIPT_DIR/lib/ppf-common.sh"
PLAYBOOK_DIR="$SCRIPT_DIR/playbooks"
# ---------------------------------------------------------------------------
# Usage
# ---------------------------------------------------------------------------
usage() {
cat <<EOF
Usage: ppf-deploy [--no-restart] [targets...]
Usage: ppf-deploy [options] [targets...]
Deploy PPF code to nodes.
Deploy PPF code to nodes via Ansible playbook.
Targets:
all odin + all workers (default)
@@ -33,15 +37,17 @@ Targets:
Options:
--no-restart sync files only, skip container restart
--check dry run (ansible --check --diff)
-v verbose ansible output
--help show this help
--version show version
Steps:
Steps performed:
1. Validate Python syntax locally
2. Rsync *.py + servers.txt to targets
2. Rsync *.py + servers.txt (role-aware destinations)
3. Copy compose file per role
4. Fix ownership (podman:podman)
5. Restart containers (unless --no-restart)
5. Restart containers on change (unless --no-restart)
6. Show container status
EOF
exit 0
@@ -51,6 +57,8 @@ EOF
# Parse args
# ---------------------------------------------------------------------------
DO_RESTART=1
CHECK_MODE=0
VERBOSE=""
TARGETS=""
while [ $# -gt 0 ]; do
@@ -58,6 +66,8 @@ while [ $# -gt 0 ]; do
--help|-h) usage ;;
--version|-V) echo "ppf-deploy $PPF_TOOLS_VERSION"; exit 0 ;;
--no-restart) DO_RESTART=0 ;;
--check) CHECK_MODE=1 ;;
-v) VERBOSE="-v" ;;
-*) die "Unknown option: $1" ;;
*) TARGETS="${TARGETS:+$TARGETS }$1" ;;
esac
@@ -65,103 +75,41 @@ while [ $# -gt 0 ]; do
done
TARGETS="${TARGETS:-all}"
HOSTS=$(resolve_targets $TARGETS)
[ -z "$HOSTS" ] && die "No valid targets"
section "Deploy targets"
log_info "$HOSTS"
# ---------------------------------------------------------------------------
# Step 1: Validate syntax
# Pre-flight: local syntax validation
# ---------------------------------------------------------------------------
validate_syntax
# ---------------------------------------------------------------------------
# Step 2: Rsync code to targets
# Build ansible-playbook arguments
# ---------------------------------------------------------------------------
section "Syncing code"
ARGS=(-i "$PLAYBOOK_DIR/inventory.ini")
ARGS+=(-e "ppf_src=$PPF_DIR")
for host in $HOSTS; do
if is_master "$host"; then
dest="/home/podman/ppf/"
else
dest="/home/podman/ppf/src/"
fi
log_info "${host} ${C_DIM}-> ${dest}${C_RST}"
ansible_cmd "$host" -m synchronize \
-a "src=$PPF_DIR/ dest=$dest rsync_opts='--include=*.py,--include=servers.txt,--include=Dockerfile,--exclude=*'" \
> /dev/null 2>&1 \
&& log_ok "$host synced" \
|| { log_err "$host sync failed"; continue; }
done
# ---------------------------------------------------------------------------
# Step 3: Copy compose file per role
# ---------------------------------------------------------------------------
section "Copying compose files"
for host in $HOSTS; do
if is_master "$host"; then
src="$PPF_DIR/compose.master.yml"
else
src="$PPF_DIR/compose.worker.yml"
fi
ansible_cmd "$host" -m copy \
-a "src=$src dest=/home/podman/ppf/compose.yml owner=podman group=podman" \
> /dev/null 2>&1 \
&& log_ok "$host compose file" \
|| log_err "$host compose copy failed"
done
# ---------------------------------------------------------------------------
# Step 4: Fix ownership
# ---------------------------------------------------------------------------
section "Fixing ownership"
csv=$(hosts_csv $HOSTS)
ansible_cmd "$csv" -m raw -a "chown -R podman:podman /home/podman/ppf/" \
> /dev/null 2>&1 \
&& log_ok "ownership fixed on all targets" \
|| log_err "ownership fix failed on some targets"
# ---------------------------------------------------------------------------
# Step 5: Restart (unless --no-restart)
# ---------------------------------------------------------------------------
if [ "$DO_RESTART" -eq 1 ]; then
section "Restarting containers"
for host in $HOSTS; do
compose_cmd "$host" "restart" > /dev/null 2>&1 \
&& log_ok "$host restarted" \
|| log_err "$host restart failed"
done
# Brief pause for containers to settle
sleep 2
# ---------------------------------------------------------------------------
# Step 6: Show status
# ---------------------------------------------------------------------------
section "Container status"
for host in $HOSTS; do
local_output=$(compose_cmd "$host" "ps" 2>/dev/null) || true
if echo "$local_output" | grep -qi "up\|running"; then
log_ok "$host"
else
log_warn "$host"
fi
echo "$local_output" | grep -v '^\s*$' | while IFS= read -r line; do
log_dim "$line"
done
done
else
log_info "Restart skipped (--no-restart)"
if [ "$DO_RESTART" -eq 0 ]; then
ARGS+=(-e "ppf_restart=false")
fi
printf "\n"
log_ok "Deploy complete"
if [ "$CHECK_MODE" -eq 1 ]; then
ARGS+=(--check --diff)
fi
[ -n "$VERBOSE" ] && ARGS+=("$VERBOSE")
# Target resolution: map aliases to ansible --limit
case "$TARGETS" in
all) ;; # no --limit = all hosts in inventory
*)
LIMIT=$(resolve_targets $TARGETS | tr ' ' ',')
ARGS+=(--limit "$LIMIT")
;;
esac
ARGS+=("$PLAYBOOK_DIR/deploy.yml")
# ---------------------------------------------------------------------------
# Run playbook
# ---------------------------------------------------------------------------
section "Deploying to ${TARGETS}"
ansible_playbook_cmd "${ARGS[@]}"