diff --git a/tools/ppf-deploy b/tools/ppf-deploy new file mode 100755 index 0000000..2952d04 --- /dev/null +++ b/tools/ppf-deploy @@ -0,0 +1,167 @@ +#!/bin/bash +# ppf-deploy -- deploy PPF code to nodes +# +# Usage: +# ppf-deploy [--no-restart] [targets...] +# +# Targets: +# all odin + all workers (default) +# workers cassius, edge, sentinel +# master odin +# specific host(s) + +set -eu + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck disable=SC1091 +. "$SCRIPT_DIR/lib/ppf-common.sh" + +# --------------------------------------------------------------------------- +# Usage +# --------------------------------------------------------------------------- +usage() { + cat < specific host(s) + +Options: + --no-restart sync files only, skip container restart + --help show this help + --version show version + +Steps: + 1. Validate Python syntax locally + 2. Rsync *.py + servers.txt to targets + 3. Copy compose file per role + 4. Fix ownership (podman:podman) + 5. Restart containers (unless --no-restart) + 6. Show container status +EOF + exit 0 +} + +# --------------------------------------------------------------------------- +# Parse args +# --------------------------------------------------------------------------- +DO_RESTART=1 +TARGETS="" + +while [ $# -gt 0 ]; do + case "$1" in + --help|-h) usage ;; + --version|-V) echo "ppf-deploy $PPF_TOOLS_VERSION"; exit 0 ;; + --no-restart) DO_RESTART=0 ;; + -*) die "Unknown option: $1" ;; + *) TARGETS="${TARGETS:+$TARGETS }$1" ;; + esac + shift +done + +TARGETS="${TARGETS:-all}" +HOSTS=$(resolve_targets $TARGETS) + +[ -z "$HOSTS" ] && die "No valid targets" + +section "Deploy targets" +log_info "$HOSTS" + +# --------------------------------------------------------------------------- +# Step 1: Validate syntax +# --------------------------------------------------------------------------- +validate_syntax + +# --------------------------------------------------------------------------- +# Step 2: Rsync code to targets +# --------------------------------------------------------------------------- +section "Syncing code" + +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)" +fi + +printf "\n" +log_ok "Deploy complete"