Luigit
repositories / bugabinga.net

bugabinga.net

personal infrastructure for bugabinga!

owned by admin

scripts/local/deploy-valheim-klops.sh

Raw
#!/usr/bin/env bash
set -euo pipefail

[[ "$#" -eq 4 ]] || {
  echo "usage: deploy-valheim-klops.sh HOST PORT WG_IPV4 WG_IPV6" >&2
  exit 2
}
host="$1"
port="$2"
wg_ipv4="$3"
wg_ipv6="$4"
[[ "$port" =~ ^[0-9]+$ && "$wg_ipv4" = 10.100.0.2 && "$wg_ipv6" = fd00:100::2 ]] || {
  echo "invalid Valheim ingress input" >&2
  exit 2
}
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"

ssh -p "$port" "root@${host}" 'bash -s -- 10.100.0.2 fd00:100::2' \
  <"$repo_root/scripts/remote/apply-valheim-klops-firewall.sh"

owner_env="${PUFFER_OWNER_ENV:-$repo_root/keys/pufferpanel-owner.env}"
[[ -f "$owner_env" && ! -L "$owner_env" && "$(stat -c %a "$owner_env")" = 600 ]] || {
  echo "missing owner credential file with mode 0600" >&2
  exit 1
}

umask 077
scratch_root=/var/tmp/pufferpanel-fork/valheim-ingress-klops
install -d -m 0700 "$scratch_root"
scratch="$(mktemp -d "$scratch_root/run.XXXXXX")"
definition="$scratch/definition.json"
body="$scratch/body.json"
login_body="$scratch/login.json"
cookie_jar="$scratch/cookies.txt"
status_body="$scratch/status.json"
: >"$cookie_jar"
chmod 0600 "$cookie_jar"
cleanup() {
  if [[ -n "${base_url:-}" ]]; then
    curl --silent --show-error --connect-timeout 10 --max-time 30 --cookie "$cookie_jar" --cookie-jar "$cookie_jar" \
      -X POST "$base_url/auth/logout" >/dev/null 2>&1 || true
  fi
  rm -rf -- "$scratch"
}
trap cleanup EXIT

# shellcheck disable=SC1090
source "$owner_env"
: "${PUFFER_OWNER_EMAIL:?missing PUFFER_OWNER_EMAIL}"
: "${PUFFER_OWNER_PASSWORD:?missing PUFFER_OWNER_PASSWORD}"
: "${PUFFER_OWNER_URL:?missing PUFFER_OWNER_URL}"
base_url="${PUFFER_OWNER_URL%/}"
[[ "$base_url" = https://games.bugabinga.net ]] || {
  echo "unexpected PufferPanel URL" >&2
  exit 1
}
export PUFFER_OWNER_EMAIL PUFFER_OWNER_PASSWORD
jq -n '{email: env.PUFFER_OWNER_EMAIL, password: env.PUFFER_OWNER_PASSWORD}' >"$login_body"
curl --fail --silent --show-error --connect-timeout 10 --max-time 30 --cookie "$cookie_jar" --cookie-jar "$cookie_jar" \
  -H 'Content-Type: application/json' --data-binary "@$login_body" "$base_url/auth/login" >/dev/null
api() {
  curl --fail --silent --show-error --connect-timeout 10 --max-time 30 --cookie "$cookie_jar" --cookie-jar "$cookie_jar" \
    -H 'Content-Type: application/json' "$@"
}
read_status() {
  api "$base_url/api/servers/valheim1/status" >"$status_body"
  jq -e 'type == "object" and (.running | type == "boolean") and (.installing | type == "boolean")' "$status_body" >/dev/null
}
is_running() {
  jq -e '.running == true' "$status_body" >/dev/null
}
assert_not_installing() {
  jq -e '.installing == false' "$status_body" >/dev/null || {
    echo 'Valheim is installing; definition unchanged' >&2
    return 1
  }
}
poll_stopped() {
  for _ in $(seq 1 24); do
    read_status
    assert_not_installing
    is_running || return 0
    sleep 5
  done
  echo 'Valheim did not stop within 120 seconds; definition unchanged' >&2
  return 1
}
poll_running() {
  for _ in $(seq 1 60); do
    read_status
    assert_not_installing
    is_running && return 0
    sleep 5
  done
  echo 'Valheim did not report running within 300 seconds after restart' >&2
  return 1
}

api "$base_url/api/servers/valheim1/definition" >"$definition"
read_status
assert_not_installing
was_running=false
is_running && was_running=true
bindings="$(jq -cn --arg v4 "$wg_ipv4" --arg v6 "$wg_ipv6" '[ $v4 + ":2456:2456/udp", "[" + $v6 + "]:2456:2456/udp", $v4 + ":2457:2457/udp", "[" + $v6 + "]:2457:2457/udp" ]')"
if jq -e --argjson bindings "$bindings" '.environment.portBindings == $bindings' "$definition" >/dev/null; then
  printf '%s\n' 'Valheim port bindings already reconciled'
  exit 0
fi
if "$was_running"; then
  api -X POST "$base_url/api/servers/valheim1/stop" >/dev/null
  poll_stopped
fi
timestamp="$(date -u +%Y%m%dT%H%M%SZ)"
backup="/data/pufferpanel/data/backups/valheim-ingress-${timestamp}"
ssh -p "$port" "root@${host}" "install -d -m 0700 -o pufferpanel -g pufferpanel '$backup' && runuser -u pufferpanel -- sh -c 'umask 077; cat > \"$backup/definition.json\"; cp -a /data/pufferpanel/data/servers/valheim1/saves \"$backup/saves\"'" <"$definition"
jq --argjson bindings "$bindings" '.environment.portBindings = $bindings' "$definition" >"$body"
api -X PUT --data-binary "@$body" "$base_url/api/servers/valheim1/definition" >/dev/null
if "$was_running"; then
  api -X POST "$base_url/api/servers/valheim1/start" >/dev/null
  poll_running
fi
printf 'changed: Valheim definition and backup %s\n' "$backup"