Add emergency Hetzner shutdown script

This commit is contained in:
Tero Halla-aho 2025-12-08 23:52:11 +02:00
parent 5462b0adab
commit e26c3ecc1e
2 changed files with 119 additions and 0 deletions

View file

@ -118,6 +118,16 @@ flowchart TB
<li>Session auth: signed JWT cookie <code>session_token</code>; roles: USER, ADMIN, USER_MODERATOR, LISTING_MODERATOR.</li> <li>Session auth: signed JWT cookie <code>session_token</code>; roles: USER, ADMIN, USER_MODERATOR, LISTING_MODERATOR.</li>
</ul> </ul>
</section> </section>
<section class="card">
<h2>Emergency shutdown</h2>
<ul>
<li>Script: <code>scripts/emergency-shutdown.sh</code> issues Hetzner poweroff/shutdown commands for tracked nodes (currently <code>node1.lomavuokraus.fi</code> and <code>db1.lomavuokraus.fi</code>).</li>
<li>List tracked nodes: <code>./scripts/emergency-shutdown.sh --list</code>.</li>
<li>Hard stop everything: <code>./scripts/emergency-shutdown.sh --yes --confirm "SHUTDOWN ALL LOMAVUOKRAUS NODES NOW"</code> (default action is <code>poweroff</code>; use <code>--action shutdown</code> for ACPI).</li>
<li>Requires <code>hcloud</code> CLI with <code>HCLOUD_TOKEN</code> or configured at <code>~/.config/hcloud/cli.toml</code>; keep the node list updated when adding servers.</li>
</ul>
</section>
</main> </main>
<script type="module"> <script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';

109
scripts/emergency-shutdown.sh Executable file
View file

@ -0,0 +1,109 @@
#!/usr/bin/env bash
set -euo pipefail
# Emergency hard power-off for all lomavuokraus Hetzner nodes.
# Keep the NODES list up to date when adding/removing servers so this script
# always covers every host.
NODES=(
"node1.lomavuokraus.fi|k3s + ingress + app workloads"
"db1.lomavuokraus.fi|PostgreSQL"
)
CONFIRM_PHRASE="SHUTDOWN ALL LOMAVUOKRAUS NODES NOW"
HCLOUD_BIN="${HCLOUD_BIN:-hcloud}"
ACTION="poweroff" # poweroff = immediate; shutdown = ACPI (softer)
usage() {
cat <<EOF
Usage: $0 [--yes] [--confirm "${CONFIRM_PHRASE}"] [--action poweroff|shutdown] [--list]
Options:
--list List tracked nodes and exit.
--action <mode> Either "poweroff" (default, immediate) or "shutdown" (soft ACPI).
--yes Actually issue the command (otherwise dry-run).
--confirm <phrase> Required confirmation phrase: "${CONFIRM_PHRASE}"
Env:
HCLOUD_BIN Override hcloud binary path (default: hcloud)
HCLOUD_TOKEN Hetzner API token (or ~/.config/hcloud/cli.toml)
Examples:
$0 --list
$0 --yes --confirm "${CONFIRM_PHRASE}"
EOF
}
log() {
echo "[$(date +"%H:%M:%S")] $*"
}
list_nodes() {
log "Tracked nodes:"
for entry in "${NODES[@]}"; do
IFS="|" read -r name desc <<<"$entry"
echo "- ${name}: ${desc}"
done
}
require_hcloud() {
if ! command -v "$HCLOUD_BIN" >/dev/null 2>&1; then
echo "hcloud CLI not found (HCLOUD_BIN=${HCLOUD_BIN}). Install it before running." >&2
exit 1
fi
if [[ -z "${HCLOUD_TOKEN:-}" && ! -f "${HOME}/.config/hcloud/cli.toml" ]]; then
echo "HCLOUD_TOKEN not set and no CLI config found at ~/.config/hcloud/cli.toml." >&2
exit 1
fi
}
CONFIRM_INPUT=""
DO_IT=0
LIST_ONLY=0
while [[ $# -gt 0 ]]; do
case "$1" in
--yes) DO_IT=1 ;;
--confirm) shift; CONFIRM_INPUT="${1:-}";;
--action) shift; ACTION="${1:-poweroff}";;
--list) LIST_ONLY=1 ;;
-h|--help) usage; exit 0 ;;
*) echo "Unknown argument: $1" >&2; usage; exit 1 ;;
esac
shift
done
if [[ "$ACTION" != "poweroff" && "$ACTION" != "shutdown" ]]; then
echo "Invalid action: ${ACTION}. Use poweroff or shutdown." >&2
exit 1
fi
if [[ $LIST_ONLY -eq 1 ]]; then
list_nodes
exit 0
fi
log "Requested action: ${ACTION}"
list_nodes
if [[ $DO_IT -ne 1 ]]; then
log "Dry run only. Add --yes --confirm \"${CONFIRM_PHRASE}\" to execute."
exit 0
fi
if [[ "${CONFIRM_INPUT}" != "${CONFIRM_PHRASE}" ]]; then
echo "Confirmation phrase mismatch. Expected: \"${CONFIRM_PHRASE}\"." >&2
exit 1
fi
require_hcloud
for entry in "${NODES[@]}"; do
IFS="|" read -r name desc <<<"$entry"
log "Issuing ${ACTION} to ${name} (${desc})..."
# poweroff is an immediate hard stop; shutdown attempts a graceful halt.
"$HCLOUD_BIN" server "${ACTION}" "$name"
done
log "All shutdown requests sent."