#!/usr/bin/env bash
# Массовое развёртывание hard-monitor по SSH — в одну команду на список хостов.
# Для каждой машины: создать сервер на панели (если нет) → залить агента →
# установить (install.sh) → дождаться первого пакета.
#
# ./deploy.sh --panel https://panel.example.com user@host1 user@host2
#
# Опции:
# --panel URL адрес панели (обязательно)
# --token TOKEN GHARD_ADMIN_TOKEN панели (нужен, если токен на панели задан)
# --interval N интервал агента в секундах (по умолчанию 30)
# --recreate если сервер с таким именем уже есть на панели — удалить
# его и создать заново (новый ключ); иначе ключ берётся
# из /opt/hard-monitor/.env на хосте, а запись переиспользуется
# --git-install ставить curl-ом прямо из репозитория (по умолчанию scp
# локальных файлов — работает даже без доступа хоста к гиту)
#
# Требования к хостам: ssh-доступ по ключу, python3, sudo без пароля
# (или запусти с agent'ом, который умеет отвечать на пароль sudo).
set -euo pipefail
PANEL=""
TOKEN=""
INTERVAL=30
RECREATE=0
GIT_INSTALL=0
TARGETS=()
RAW_BASE="https://git.gnexus.space/git/root/hard-panel/raw/branch/master"
say() { printf '\e[1;32m==>\e[0m %s\n' "$*"; }
warn() { printf '\e[1;33m!!\e[0m %s\n' "$*"; }
die() { printf '\e[1;31mошибка:\e[0m %s\n' "$*" >&2; exit 1; }
usage() { sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'; exit "${1:-0}"; }
while [[ $# -gt 0 ]]; do
case "$1" in
--panel) PANEL="$2"; shift 2 ;;
--token) TOKEN="$2"; shift 2 ;;
--interval) INTERVAL="$2"; shift 2 ;;
--recreate) RECREATE=1; shift ;;
--git-install) GIT_INSTALL=1; shift ;;
-h|--help) usage 0 ;;
-*) die "неизвестная опция: $1" ;;
*) TARGETS+=("$1"); shift ;;
esac
done
[[ "$PANEL" =~ ^https?:// ]] || { usage >&2; die "укажи --panel https://panel.example.com"; }
[[ ${#TARGETS[@]} -gt 0 ]] || { usage >&2; die "укажи хотя бы один хост: user@host"; }
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
api() { # api METHOD PATH [JSON]
local method="$1" path="$2" body="${3:-}"
curl -sS -X "$method" -H "Content-Type: application/json" \
${TOKEN:+-H "Authorization: Bearer $TOKEN"} \
${body:+-d "$body"} "$PANEL/api/v1$path"
}
# server_id по имени (пусто, если нет); python3 есть всюду, где есть ssh+curl
find_server() {
api GET /servers | python3 -c "
import json, sys
name = sys.argv[1]
for s in json.load(sys.stdin):
if s['name'] == name:
print(s['id']); break
" "$1"
}
# первый пакет от сервера id (0/1)
is_online() {
api GET /servers | python3 -c "
import json, sys
sid = int(sys.argv[1])
for s in json.load(sys.stdin):
if s['id'] == sid:
print(1 if s.get('status') == 'online' else 0); break
else:
print(0)
" "$1"
}
for target in "${TARGETS[@]}"; do
say "$target"
ssh -o BatchMode=yes -o ConnectTimeout=10 "$target" true 2>/dev/null \
|| die "$target: нет ssh-доступа (проверь ключ/алиас)"
name="$(ssh -o BatchMode=yes -o ConnectTimeout=10 "$target" 'hostname -s')"
server_id="$(find_server "$name" || true)"
key=""
if [[ -n "$server_id" ]]; then
if [[ $RECREATE -eq 1 ]]; then
warn "сервер #$server_id «$name» уже есть — удаляю (--recreate)"
api DELETE "/servers/$server_id" >/dev/null
server_id=""
else
say "сервер #$server_id «$name» уже есть — переиспользую запись"
key="$(ssh -o BatchMode=yes -o ConnectTimeout=10 "$target" \
"grep -oP '^SERVER_KEY=\K.*' /opt/hard-monitor/.env 2>/dev/null || true")"
if [[ -z "$key" ]]; then
die "на $target нет /opt/hard-monitor/.env с ключом. Удали сервер «$name» на панели или запусти с --recreate"
fi
say "ключ взят из /opt/hard-monitor/.env на хосте"
fi
fi
if [[ -z "$server_id" ]]; then
say "создаю сервер «$name» на панели"
resp="$(api POST /servers "{\"name\": \"$name\"}")"
server_id="$(printf '%s' "$resp" | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])')"
key="$(printf '%s' "$resp" | python3 -c 'import json,sys; print(json.load(sys.stdin)["key"])')"
fi
if [[ $GIT_INSTALL -eq 1 ]]; then
say "устанавливаю агента (curl из $RAW_BASE)"
ssh -o BatchMode=yes -o ConnectTimeout=10 "$target" \
"curl -fsSL $RAW_BASE/monitor/install.sh | sudo bash -s -- '$PANEL' '$key' '$INTERVAL'"
else
say "заливаю файлы агента и устанавливаю"
tmpd="/tmp/hard-monitor-install.$$"
ssh -o BatchMode=yes -o ConnectTimeout=10 "$target" "mkdir -p '$tmpd'"
scp -q "$SCRIPT_DIR/install.sh" "$SCRIPT_DIR/hard_monitor.py" \
"$SCRIPT_DIR/hard-monitor.service" "$target:$tmpd/"
ssh -o BatchMode=yes -o ConnectTimeout=10 -t "$target" \
"sudo bash '$tmpd/install.sh' '$PANEL' '$key' '$INTERVAL'; rc=\$?; rm -rf '$tmpd'; exit \$rc" \
|| die "$target: установка не удалась"
fi
say "жду первый пакет (до $((INTERVAL * 4)) с)"
ok=0
for _ in $(seq 1 $((INTERVAL * 4 / 5))); do
sleep 5
if [[ "$(is_online "$server_id")" == "1" ]]; then ok=1; break; fi
done
if [[ $ok -eq 1 ]]; then
say "✓ $target: агент онлайн (сервер #$server_id «$name»)"
else
die "$target: пакет не пришёл — смотри journalctl -u hard-monitor на хосте"
fi
done
say "все хосты развёрнуты. Дашборд: $PANEL"