115 lines
6 KiB
Bash
Executable file
115 lines
6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Install/update backup-manager (cluster mode + on-host runners).
|
|
#
|
|
# Operating model:
|
|
# 1. Receives bootstrap secrets via env (decrypted by the wrapper from
|
|
# SOPS+Age — only at deploy time; runtime keys live in vault per
|
|
# ADR-017).
|
|
# 2. Creates the bootstrap K8s Secret in $NAMESPACE.
|
|
# 3. Renders and applies Deployment + Service + ServiceMonitor +
|
|
# PrometheusRule for the daemon (cluster mode).
|
|
# 4. For every entry in system-backups.ncl: SSH to the selected hosts and
|
|
# install the prvng-backup binary + systemd timer or cron.d entry.
|
|
#
|
|
# Required env (typically populated by `provisioning component install …`):
|
|
# VAULT_BOOTSTRAP_TOKEN
|
|
# NATS_BOOTSTRAP_NKEY_SEED
|
|
# BACKUP_AGE_KEY bootstrap age key (NOT in vault — ADR-011)
|
|
# BACKUP_S3_PRIMARY_ACCESS_KEY
|
|
# BACKUP_S3_PRIMARY_SECRET_KEY
|
|
# BACKUP_B2_REPLICA_KEY_ID
|
|
# BACKUP_B2_REPLICA_APPLICATION_KEY
|
|
# NAMESPACE (default: backup-system)
|
|
# COMPONENT_PATH (path to instance .ncl rendered to JSON for Jinja2)
|
|
|
|
set -euo pipefail
|
|
|
|
NAMESPACE="${NAMESPACE:-backup-system}"
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
TEMPLATES="$SCRIPT_DIR/templates"
|
|
|
|
# ── Pre-flight ──────────────────────────────────────────────────────────────
|
|
require_var() {
|
|
local name="$1"
|
|
if [ -z "${!name:-}" ]; then
|
|
echo "ERROR: required env var $name is not set" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
require_var VAULT_BOOTSTRAP_TOKEN
|
|
require_var NATS_BOOTSTRAP_NKEY_SEED
|
|
require_var BACKUP_AGE_KEY
|
|
require_var BACKUP_S3_PRIMARY_ACCESS_KEY
|
|
require_var BACKUP_S3_PRIMARY_SECRET_KEY
|
|
require_var BACKUP_B2_REPLICA_KEY_ID
|
|
require_var BACKUP_B2_REPLICA_APPLICATION_KEY
|
|
|
|
command -v kubectl >/dev/null || { echo "kubectl is required" >&2; exit 1; }
|
|
command -v jq >/dev/null || { echo "jq is required" >&2; exit 1; }
|
|
|
|
# ── Namespace ────────────────────────────────────────────────────────────────
|
|
kubectl create namespace "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -
|
|
|
|
# ── Bootstrap Secret ─────────────────────────────────────────────────────────
|
|
# Pattern mirrors zot S3 install (provisioning/extensions/components/zot/cluster/install-zot.sh):
|
|
# wrapper passes secrets as env vars; we materialise them as a K8s Secret.
|
|
# Daemon reads on first run and promotes runtime credentials into vault.
|
|
kubectl create secret generic backup-manager-bootstrap \
|
|
--namespace="$NAMESPACE" \
|
|
--from-literal=vault_token="$VAULT_BOOTSTRAP_TOKEN" \
|
|
--from-literal=nats_nkey_seed="$NATS_BOOTSTRAP_NKEY_SEED" \
|
|
--from-literal=age_key="$BACKUP_AGE_KEY" \
|
|
--from-literal=s3_primary_access_key="$BACKUP_S3_PRIMARY_ACCESS_KEY" \
|
|
--from-literal=s3_primary_secret_key="$BACKUP_S3_PRIMARY_SECRET_KEY" \
|
|
--from-literal=b2_replica_key_id="$BACKUP_B2_REPLICA_KEY_ID" \
|
|
--from-literal=b2_replica_application_key="$BACKUP_B2_REPLICA_APPLICATION_KEY" \
|
|
--dry-run=client -o yaml | kubectl apply -f -
|
|
|
|
# ── Deployment + Service ─────────────────────────────────────────────────────
|
|
# Templates are Jinja2; rendered by `nu render-template.nu` (provisioning helper)
|
|
# with the JSON dump of the component config bound as $component.
|
|
# Here we leave a render hook the wrapper invokes; if invoked directly without
|
|
# the wrapper, fall back to substituting NAMESPACE and DAEMON_IMAGE only.
|
|
RENDER_BIN="${RENDER_BIN:-render-template.nu}"
|
|
RENDER_INPUT="${COMPONENT_PATH:-}"
|
|
|
|
render_template() {
|
|
local template="$1"
|
|
if command -v "$RENDER_BIN" >/dev/null && [ -n "$RENDER_INPUT" ]; then
|
|
"$RENDER_BIN" "$template" --json "$RENDER_INPUT"
|
|
else
|
|
# Minimal substitution fallback — sufficient for development.
|
|
sed -e "s|{{ namespace }}|$NAMESPACE|g" \
|
|
-e "s|{{ daemon_image }}|${DAEMON_IMAGE:-ghcr.io/jesusperezlorenzo/backup-manager-runner:0.1.0}|g" \
|
|
-e "s|{{ daemon_http_port }}|${DAEMON_HTTP_PORT:-9099}|g" \
|
|
-e "s|{{ daemon_metrics_port }}|${DAEMON_METRICS_PORT:-9100}|g" \
|
|
"$template"
|
|
fi
|
|
}
|
|
|
|
for tpl in deployment.yaml.j2 service.yaml.j2 servicemonitor.yaml.j2; do
|
|
if [ -f "$TEMPLATES/$tpl" ]; then
|
|
render_template "$TEMPLATES/$tpl" | kubectl apply -f -
|
|
fi
|
|
done
|
|
|
|
# ── PrometheusRule (alerts from prometheus.ncl) ─────────────────────────────
|
|
# The alert rules are declared in the prometheus component; install-prometheus.sh
|
|
# is responsible for rendering them. backup-manager only emits the metrics; the
|
|
# rules live with the observability stack.
|
|
|
|
# ── On-host runners ──────────────────────────────────────────────────────────
|
|
# For each SystemBackupDef in the workspace, install /usr/local/bin/prvng-backup
|
|
# and a systemd timer or cron.d entry on the selected hosts. This step is
|
|
# delegated to the provisioning host runner (Nushell) when available; here we
|
|
# emit the list to stdout so the caller can act.
|
|
SYSTEM_BACKUPS_PATH="${SYSTEM_BACKUPS_PATH:-infra/libre-wuji/system-backups.ncl}"
|
|
if command -v nickel >/dev/null && [ -f "$SYSTEM_BACKUPS_PATH" ]; then
|
|
echo "==> SystemBackupDef entries requiring on-host runner installation:"
|
|
nickel export "$SYSTEM_BACKUPS_PATH" 2>/dev/null \
|
|
| jq -r 'to_entries[] | " \(.value.name) -> host_selector=\(.value.host_selector.kind), members=\(.value.host_selector.members | join(","))"' || true
|
|
echo "==> Run 'provisioning component install backup_manager --target-hosts' to deploy binaries."
|
|
fi
|
|
|
|
echo "==> backup-manager cluster install complete in namespace $NAMESPACE"
|