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:
@@ -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
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
142
tools/ppf-deploy
142
tools/ppf-deploy
@@ -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[@]}"
|
||||
|
||||
Reference in New Issue
Block a user