Wire Beszel fleet filesystems, GPU telemetry, dashboard and alerts
This commit is contained in:
+24
-10
@@ -25,6 +25,9 @@
|
||||
# scripts/deploy-stack.sh <host> <stack> --yes # skip prompt (use sparingly)
|
||||
# scripts/deploy-stack.sh <host> <stack> --compose # push only compose side
|
||||
# scripts/deploy-stack.sh <host> <stack> --conf # push only conf side
|
||||
# Optional environment:
|
||||
# DEPLOY_DEST_STACK=<name> retain a legacy remote stack directory/project
|
||||
# DEPLOY_SUDO=1 use passwordless sudo for remote files and rsync
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -67,6 +70,7 @@ STACK=
|
||||
ASSUME_YES=0
|
||||
DO_COMPOSE=1
|
||||
DO_CONF=1
|
||||
DEST_STACK=${DEPLOY_DEST_STACK:-}
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
--yes|-y) ASSUME_YES=1 ;;
|
||||
@@ -85,6 +89,8 @@ done
|
||||
|
||||
[ -n "$HOST" ] || { echo "usage: $(basename "$0") <host> <stack>" >&2; exit 2; }
|
||||
[ -n "$STACK" ] || { echo "usage: $(basename "$0") <host> <stack>" >&2; exit 2; }
|
||||
DEST_STACK=${DEST_STACK:-$STACK}
|
||||
[[ "$DEST_STACK" =~ ^[a-zA-Z0-9][a-zA-Z0-9_-]*$ ]] || { echo "invalid DEPLOY_DEST_STACK" >&2; exit 2; }
|
||||
|
||||
resolve_target() {
|
||||
# ssh-target file wins when present (may carry user@ or non-default port);
|
||||
@@ -105,6 +111,12 @@ resolve_target() {
|
||||
}
|
||||
|
||||
TARGET=$(resolve_target "$HOST")
|
||||
REMOTE_PREFIX=
|
||||
RSYNC_REMOTE=()
|
||||
if [ "${DEPLOY_SUDO:-0}" = 1 ]; then
|
||||
REMOTE_PREFIX='sudo -n '
|
||||
RSYNC_REMOTE=(--rsync-path='sudo -n rsync')
|
||||
fi
|
||||
STACK_DIR="$STACKS_DIR/$STACK"
|
||||
|
||||
[ -d "$STACK_DIR" ] || { echo "error: $STACK_DIR not found — author the canonical stack first (see stacks/<other>/ for examples)" >&2; exit 2; }
|
||||
@@ -112,10 +124,10 @@ STACK_DIR="$STACKS_DIR/$STACK"
|
||||
# Collect the two src/dest pairs we need to consider.
|
||||
PAIRS=() # each entry: "<kind>|<src>|<dest>"
|
||||
if [ "$DO_COMPOSE" -eq 1 ]; then
|
||||
PAIRS+=("compose|$STACK_DIR/|$TARGET:/opt/docker/compose/$STACK/")
|
||||
PAIRS+=("compose|$STACK_DIR/|$TARGET:/opt/docker/compose/$DEST_STACK/")
|
||||
fi
|
||||
if [ "$DO_CONF" -eq 1 ] && [ -d "$STACK_DIR/conf" ]; then
|
||||
PAIRS+=("conf|$STACK_DIR/conf/|$TARGET:/opt/docker/conf/$STACK/")
|
||||
PAIRS+=("conf|$STACK_DIR/conf/|$TARGET:/opt/docker/conf/$DEST_STACK/")
|
||||
fi
|
||||
|
||||
[ "${#PAIRS[@]}" -gt 0 ] || { echo "nothing to deploy"; exit 0; }
|
||||
@@ -133,9 +145,9 @@ for entry in "${PAIRS[@]}"; do
|
||||
|
||||
# Pre-create the remote dir. Without this, rsync against a nonexistent
|
||||
# destination can fail in ways the dry-run doesn't surface cleanly.
|
||||
remote_path="/opt/docker/$kind/$STACK/"
|
||||
remote_path="/opt/docker/$kind/$DEST_STACK/"
|
||||
if ! ssh -n -o BatchMode=yes -o ConnectTimeout=10 "$TARGET" \
|
||||
"mkdir -p '$remote_path'" 2>/dev/null; then
|
||||
"${REMOTE_PREFIX}mkdir -p '$remote_path'" 2>/dev/null; then
|
||||
echo "error: could not create $remote_path on $TARGET (check perms / ssh)" >&2
|
||||
exit 2
|
||||
fi
|
||||
@@ -143,6 +155,7 @@ for entry in "${PAIRS[@]}"; do
|
||||
tmp_out=$(mktemp) tmp_err=$(mktemp)
|
||||
rc=0
|
||||
rsync -az --delete --dry-run \
|
||||
"${RSYNC_REMOTE[@]}" \
|
||||
--out-format='%i %n' \
|
||||
"${EXCLUDES[@]}" "${extra[@]}" \
|
||||
"$src" "$dest" >"$tmp_out" 2>"$tmp_err" || rc=$?
|
||||
@@ -190,9 +203,9 @@ if [ "$any_change" -eq 0 ]; then
|
||||
for entry in "${PAIRS[@]}"; do
|
||||
IFS='|' read -r kind _ _ <<<"$entry"
|
||||
raw=${RAW_RSYNC_OUT_BY_KIND[$kind]:-}
|
||||
remote_path="/opt/docker/$kind/$STACK/"
|
||||
remote_path="/opt/docker/$kind/$DEST_STACK/"
|
||||
remote_count=$(ssh -n -o BatchMode=yes "$TARGET" \
|
||||
"find '$remote_path' -mindepth 1 -maxdepth 1 2>/dev/null | wc -l" \
|
||||
"${REMOTE_PREFIX}find '$remote_path' -mindepth 1 -maxdepth 1 2>/dev/null | wc -l" \
|
||||
2>/dev/null || echo "?")
|
||||
printf ' %s: remote has %s entries, rsync itemize output:\n' "$kind" "$remote_count"
|
||||
if [ -z "$raw" ]; then
|
||||
@@ -209,7 +222,7 @@ divider() { printf '\n%s\n' "---------------------------------------------------
|
||||
|
||||
for entry in "${PAIRS[@]}"; do
|
||||
IFS='|' read -r kind src dest <<<"$entry"
|
||||
remote_base="/opt/docker/$kind/$STACK"
|
||||
remote_base="/opt/docker/$kind/$DEST_STACK"
|
||||
changed=${CHANGED_FILES_BY_KIND[$kind]:-}
|
||||
deleted=${DELETED_FILES_BY_KIND[$kind]:-}
|
||||
[ -z "$changed$deleted" ] && continue
|
||||
@@ -224,10 +237,10 @@ for entry in "${PAIRS[@]}"; do
|
||||
local_file="$src$rel"
|
||||
remote_file="$remote_base/$rel"
|
||||
divider
|
||||
if ssh -n -o BatchMode=yes "$TARGET" "[ -f '$remote_file' ]" 2>/dev/null; then
|
||||
if ssh -n -o BatchMode=yes "$TARGET" "${REMOTE_PREFIX}test -f '$remote_file'" 2>/dev/null; then
|
||||
printf 'MODIFY %s\n' "$rel"
|
||||
diff -u --label "a/$rel (remote)" --label "b/$rel (local)" \
|
||||
<(ssh -n -o BatchMode=yes "$TARGET" "cat '$remote_file'" 2>/dev/null) \
|
||||
<(ssh -n -o BatchMode=yes "$TARGET" "${REMOTE_PREFIX}cat '$remote_file'" 2>/dev/null) \
|
||||
"$local_file" || true
|
||||
else
|
||||
printf 'ADD %s\n' "$rel"
|
||||
@@ -242,7 +255,7 @@ for entry in "${PAIRS[@]}"; do
|
||||
divider
|
||||
printf 'DELETE %s\n' "$rel"
|
||||
diff -u --label "a/$rel (remote)" --label /dev/null \
|
||||
<(ssh -n -o BatchMode=yes "$TARGET" "cat '$remote_file'" 2>/dev/null) \
|
||||
<(ssh -n -o BatchMode=yes "$TARGET" "${REMOTE_PREFIX}cat '$remote_file'" 2>/dev/null) \
|
||||
/dev/null || true
|
||||
done <<<"$deleted"
|
||||
done
|
||||
@@ -264,6 +277,7 @@ for entry in "${PAIRS[@]}"; do
|
||||
[ "$kind" = compose ] && extra+=(--exclude='conf/')
|
||||
printf 'pushing %s → %s\n' "$src" "$dest"
|
||||
rsync -az --delete \
|
||||
"${RSYNC_REMOTE[@]}" \
|
||||
"${EXCLUDES[@]}" "${extra[@]}" \
|
||||
"$src" "$dest"
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user