Wire Beszel fleet filesystems, GPU telemetry, dashboard and alerts

This commit is contained in:
vh
2026-09-10 08:35:28 -07:00
parent 20bbb95113
commit eb75713c1b
18 changed files with 438 additions and 84 deletions
+24 -10
View File
@@ -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