#!/usr/bin/env bash
# Idempotent bootstrap for a pom node under Sando. Run on the node as root.
#
# pom used to be installed by Bento straight into /usr/local/bin/pom. Under the
# Sando/Bento boundary Bento builds and hands off, and Sando deploys the way it
# deploys everything else: a content-addressed bundle under <release_root>,
# an atomic `current` symlink, and `systemctl reload-or-restart` on the unit.
#
#   /opt/pom/
#     releases/<digest16>/{pom,MANIFEST}
#     current -> releases/<digest16>
#
# Three things have to be true on the node before the first promote, and none of
# them were:
#
#   1. <release_root> exists and the user Sando SSHes in as can write it.
#      sandod runs `mkdir -p <release_root>/releases/<id>` as that user, and /opt
#      is root-owned.
#   2. That user may restart the unit: sandod runs
#      `sudo /bin/systemctl reload-or-restart pom.service`.
#   3. The unit's ExecStart points at <release_root>/current/<bin>. This is the
#      one that fails silently rather than loudly: a promote to a node whose unit
#      still runs /usr/local/bin/pom rsyncs the bundle, swaps the symlink,
#      restarts the service onto the OLD binary, and passes node_health. Sando
#      reports the version shipped and nothing shipped.
#
# The ExecStart move is a drop-in, not an edit of pom.service. A deploy never
# rewrites the unit (deploy/README.md, "What a deploy does not touch"), and the
# same restraint applies here: the override is one file, it is legible next to
# the unit it changes, and removing it puts the node back.
#
# Ordering is why this is a script rather than a runbook. Repointing ExecStart
# before a bundle exists leaves the unit pointing at a dangling symlink until the
# first promote, and any restart in between (a reboot, Restart=on-failure) takes
# pom down for however long that is. So the currently-installed binary is seeded
# as the first release and `current` points at it BEFORE the unit moves. The node
# comes out of this on the same version it went in on, deployable.
#
# Usage:
#   sudo DEPLOY_USER=max SANDO_PUBKEY="$(ssh fw13 'sudo cat /srv/sando/.ssh/id_ed25519.pub')" \
#       ./bootstrap-pom-node.sh
#
# Required env:
#   DEPLOY_USER   — the user in the node's `ssh_target` in pom-topology.toml
#                   (`max` on astra, `root` on the Hetzner box).
#
# Optional env:
#   SANDO_PUBKEY  — sando's public key on the Sando host, appended to
#                   DEPLOY_USER's authorized_keys. Omit on a host reached by
#                   Tailscale SSH, which does not consult authorized_keys.
#   RELEASE_ROOT  — defaults to /opt/pom. Must match the node's `release_root`.
#   BIN_NAME      — defaults to pom. Must match sando-pom.toml's `bin_names[0]`.
#   SERVICE_NAME  — defaults to pom.service.
#   CONFIG_PATH   — defaults to /etc/pom/pom.toml. The unit's --config argument.
#   HEALTH_URL    — defaults to http://127.0.0.1:9100/api/health.
#
# What this does NOT do, and will not:
#
#   Config. pom-astra.toml and pom-hetzner.toml differ per instance and the live
#   copies have carried blocks this repo did not. Config is a deliberate act.
#
#   Remove the old Bento install path. /usr/local/bin/pom, its `.prev`, the
#   /usr/local/lib/bento installer and its sudoers grant are all left in place:
#   until a promote has actually landed on this node, that binary is what the
#   rollback is. Clearing them is a separate pass after the cutover, and the
#   script prints the paths so they are not forgotten.

set -euo pipefail

if [[ $EUID -ne 0 ]]; then
    echo "must run as root" >&2
    exit 1
fi
if [[ -z "${DEPLOY_USER:-}" ]]; then
    echo "DEPLOY_USER env var is required (the user in this node's ssh_target)" >&2
    exit 1
fi
if ! id "$DEPLOY_USER" &>/dev/null; then
    echo "no such user: $DEPLOY_USER" >&2
    exit 1
fi

RELEASE_ROOT="${RELEASE_ROOT:-/opt/pom}"
BIN_NAME="${BIN_NAME:-pom}"
SERVICE_NAME="${SERVICE_NAME:-pom.service}"
CONFIG_PATH="${CONFIG_PATH:-/etc/pom/pom.toml}"
HEALTH_URL="${HEALTH_URL:-http://127.0.0.1:9100/api/health}"
DROPIN_DIR="/etc/systemd/system/${SERVICE_NAME}.d"
DROPIN="${DROPIN_DIR}/20-release-root.conf"

log() { echo "[bootstrap-pom] $*"; }

log "1/6 release root at $RELEASE_ROOT, writable by $DEPLOY_USER"
install -d -o "$DEPLOY_USER" -g "$(id -gn "$DEPLOY_USER")" -m 0755 \
    "$RELEASE_ROOT" "$RELEASE_ROOT/releases"

log "2/6 seeding the running binary as a release, so the unit never points at nothing"
if [[ -L "$RELEASE_ROOT/current" ]]; then
    log "    current -> $(readlink "$RELEASE_ROOT/current") (already seeded, left alone)"
else
    # Whatever the unit runs today, resolved from its own ExecStart rather than
    # assumed to be /usr/local/bin/pom.
    running="$(systemctl show -p ExecStart --value "$SERVICE_NAME" \
        | sed -n 's/.*path=\([^ ;]*\).*/\1/p' | head -1)"
    if [[ -z "$running" || ! -x "$running" ]]; then
        echo "cannot find the binary $SERVICE_NAME currently runs (ExecStart=$running)." >&2
        echo "Seed $RELEASE_ROOT/current by hand, or promote before repointing the unit." >&2
        exit 4
    fi
    ver="$("$running" --version 2>/dev/null | awk '{print $NF}')"
    seed="$RELEASE_ROOT/releases/preexisting-${ver:-unknown}"
    install -d -o "$DEPLOY_USER" -g "$(id -gn "$DEPLOY_USER")" -m 0755 "$seed"
    install -o "$DEPLOY_USER" -g "$(id -gn "$DEPLOY_USER")" -m 0755 "$running" "$seed/$BIN_NAME"
    # No MANIFEST: this bundle came from the node, not from an intake, and a
    # hand-written one would claim an artifact record that does not exist.
    # Sando skips verification for a bundle without one and says so in the log.
    ln -sfn "releases/preexisting-${ver:-unknown}" "$RELEASE_ROOT/current.new"
    mv -Tf "$RELEASE_ROOT/current.new" "$RELEASE_ROOT/current"
    chown -h "$DEPLOY_USER:$(id -gn "$DEPLOY_USER")" "$RELEASE_ROOT/current"
    log "    seeded $running (${ver:-unknown}) -> $RELEASE_ROOT/current"
fi

log "3/6 sudo grant: $DEPLOY_USER may restart $SERVICE_NAME"
if [[ "$DEPLOY_USER" == "root" ]]; then
    log "    root needs no grant; skipping (sudo must still be installed)"
    command -v sudo >/dev/null || { echo "sudo is not installed; sandod shells out to it" >&2; exit 5; }
else
    cat > "/etc/sudoers.d/${DEPLOY_USER}-pom" <<EOF
$DEPLOY_USER ALL=(ALL) NOPASSWD: /bin/systemctl reload-or-restart $SERVICE_NAME, /bin/systemctl restart $SERVICE_NAME, /bin/systemctl status $SERVICE_NAME
EOF
    chmod 0440 "/etc/sudoers.d/${DEPLOY_USER}-pom"
    visudo -c -f "/etc/sudoers.d/${DEPLOY_USER}-pom" >/dev/null
fi

log "4/6 sando's key for $DEPLOY_USER"
if [[ -n "${SANDO_PUBKEY:-}" ]]; then
    home="$(getent passwd "$DEPLOY_USER" | cut -d: -f6)"
    install -d -o "$DEPLOY_USER" -g "$(id -gn "$DEPLOY_USER")" -m 0700 "$home/.ssh"
    if ! grep -qF "$SANDO_PUBKEY" "$home/.ssh/authorized_keys" 2>/dev/null; then
        echo "$SANDO_PUBKEY" >> "$home/.ssh/authorized_keys"
    fi
    chown "$DEPLOY_USER:$(id -gn "$DEPLOY_USER")" "$home/.ssh/authorized_keys"
    chmod 0600 "$home/.ssh/authorized_keys"
else
    log "    SANDO_PUBKEY unset; assuming Tailscale SSH reaches this node"
fi

log "5/6 ExecStart -> $RELEASE_ROOT/current/$BIN_NAME (drop-in, unit untouched)"
install -d -m 0755 "$DROPIN_DIR"
cat > "$DROPIN" <<EOF
# Written by pom/deploy/bootstrap-pom-node.sh. Sando deploys pom as a
# content-addressed bundle and swaps <release_root>/current; the unit has to
# follow the symlink or a promote restarts the service onto the old binary and
# still reports success.
#
# A drop-in rather than an edit of pom.service, so the release path is one
# reversible file and the hardened unit stays the unit this repo ships.
[Service]
ExecStart=
ExecStart=$RELEASE_ROOT/current/$BIN_NAME serve --config $CONFIG_PATH
EOF
systemctl daemon-reload

log "6/6 restart and prove the node is serving from the new path"
systemctl reload-or-restart "$SERVICE_NAME"
for _ in $(seq 1 30); do
    if curl -fsS --max-time 3 "$HEALTH_URL" >/dev/null 2>&1; then
        served=1
        break
    fi
    sleep 1
done
if [[ "${served:-0}" != 1 ]]; then
    echo "$SERVICE_NAME did not answer $HEALTH_URL within 30s." >&2
    echo "Roll back by removing $DROPIN and running: systemctl daemon-reload && systemctl restart $SERVICE_NAME" >&2
    exit 6
fi

echo
log "Done. $SERVICE_NAME is running $("$RELEASE_ROOT/current/$BIN_NAME" --version 2>/dev/null)"
log "Sando can now promote to this node: POST /apps/pom/promote/<tier>"
echo
log "Left in place on purpose, until a promote has landed here:"
log "  /usr/local/bin/$BIN_NAME (+ .prev)   the old Bento install path, and today's rollback"
log "  /usr/local/lib/bento/install-service.sh + /etc/sudoers.d/bento-deploy"
log "Clear both in a separate pass once this node has taken a Sando release."
