#!/usr/bin/env bash
# scripts/dc-dc-radosgw-multisite.sh -- radosgw MULTISITE setup for D-108's
# cinder-backup cross-DC replication mechanism (tooling gap register item #5).
# Extracts runbooks/dc-dc-phase5-dr-failover-drill.md Step 4 (stage ONE-WAY:
# DC1 master realm/zonegroup/zone, DC2 read-only join) and Step 8's radosgw
# half (flip DC2 to read-write -- TWO-WAY) into a callable, parameterized
# script instead of a hand-typed command sequence.
#
# STANDARD, well-documented upstream Ceph administration (trust these): every
# `radosgw-admin` subcommand/flag below (realm create, zonegroup create
# --master, zone create --master / --read-only, zone modify --read-only=false,
# period update --commit) is copied verbatim from the runbook, which is itself
# copied from upstream radosgw multisite docs -- nothing here is invented.
#
# GENUINELY REPO-SPECIFIC (needs review before trusting; none of it exists
# yet): realm/zonegroup/zone NAMES, radosgw endpoint URLs, the juju model/unit
# this targets, and the charm's current restart-action name (runbook Step 4:
# "confirm the current action name against `juju actions ceph-radosgw` rather
# than assuming"). Every one of these is a REQUIRED argument below -- no
# invented defaults, matching this runbook's own `<UPPER_SNAKE_TBD>`
# placeholder discipline. --restart-action is the one OPTIONAL flag: if the
# operator does not yet know the confirmed action name, omit it and this
# script reminds you to restart manually rather than guessing a name.
#
# NO LIVE RADOSGW CLUSTER EXISTS THIS SESSION (2026-07-10, prep-only) -- this
# script has never been run for real and cannot be, tonight. Default mode is
# a DRY-RUN plan print (the exact `juju ssh ...` / `juju run ...` command
# sequence it would issue, secrets redacted); --apply executes it, ONE
# command at a time, ABORTING immediately on the first non-zero exit -- per
# the runbook's own caution ("run each command, verify its output, THEN
# proceed -- do not chain the whole sequence unattended"). Its matching
# harness (tests/dc-dc-radosgw-multisite/run-tests.sh) validates ONLY this
# script's own logic (argument parsing, guard clauses, dry-run output) -- it
# does NOT and CANNOT validate real radosgw behavior.
#
# $DC gate: sources scripts/lib-net.sh and calls `lib_net_select_dc "$DC"`
# once (DOCFIX-151 convention), immediately after argument parsing. This
# deliberately makes `--dc dc2` FAIL LOUD today -- D-101/gap #3: DC2's
# network literals are not assigned yet, so there is no real DC2 Ceph
# cluster to target regardless of what endpoint URL an operator might type
# in. Also prints the replication-plane CIDR key (PLANE_NAME in lib-net.sh,
# "10.12.36.0/22" per D-101) as a REMINDER that the endpoint above must ride
# that plane -- it is NEVER substituted into any command; the real
# replication-plane ULA address is always a separately measured value, not
# inferred from this constant.
#
# Usage:
#   dc-dc-radosgw-multisite.sh master-init --dc dc1 --model <juju-model> \
#     --unit <ceph-radosgw/N> --realm <NAME> --zonegroup <NAME> --zone <NAME> \
#     --endpoint <http://host:port> [--restart-action <ACTION>] [--apply]
#   dc-dc-radosgw-multisite.sh join-readonly --dc dc2 --model <juju-model> \
#     --unit <ceph-radosgw/N> --zonegroup <NAME> --zone <NAME> \
#     --endpoint <http://host:port> --access-key <KEY> --secret <SECRET> \
#     [--restart-action <ACTION>] [--apply]
#   dc-dc-radosgw-multisite.sh enable-two-way --dc dc2 --model <juju-model> \
#     --unit <ceph-radosgw/N> --zone <NAME> [--apply]
#   dc-dc-radosgw-multisite.sh --help
#
# Exit: 0 ok | 1 bad/missing args | 2 juju not on PATH (apply only) |
#       3 $DC gate refused | 5 apply-mode command failed.  ASCII + LF.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

usage() {
  cat <<'EOF'
usage:
  dc-dc-radosgw-multisite.sh master-init --dc vr1-dc0|vr1-dc1 --model M --unit U \
    --realm NAME --zonegroup NAME --zone NAME --endpoint URL \
    [--restart-action ACTION] [--apply]
  dc-dc-radosgw-multisite.sh join-readonly --dc vr1-dc0|vr1-dc1 --model M --unit U \
    --zonegroup NAME --zone NAME --endpoint URL \
    --access-key KEY --secret SECRET [--restart-action ACTION] [--apply]
  dc-dc-radosgw-multisite.sh enable-two-way --dc vr1-dc0|vr1-dc1 --model M --unit U \
    --zone NAME [--apply]
  dc-dc-radosgw-multisite.sh --help

Default mode is dry-run (prints the plan, executes nothing). --apply executes,
one command at a time, aborting on the first failure. No values are defaulted
or inferred -- realm/zonegroup/zone/endpoint/unit/model/keys are all required.
EOF
}

SUBCMD="${1:-}"
case "$SUBCMD" in
  -h|--help) usage; exit 0 ;;
  master-init|join-readonly|enable-two-way) shift ;;
  "") echo "FAIL: a subcommand is required" >&2; usage >&2; exit 1 ;;
  *) echo "FAIL: unknown subcommand '$SUBCMD'" >&2; usage >&2; exit 1 ;;
esac

DC=""; MODEL="${MODEL:-openstack}"; UNIT=""; APPLY=0
REALM=""; ZONEGROUP=""; ZONE=""; ENDPOINT=""; RESTART_ACTION=""
ACCESS_KEY=""; SECRET=""

while [ $# -gt 0 ]; do
  case "$1" in
    -h|--help) usage; exit 0 ;;
    --dc) DC="${2:-}"; shift 2 ;;
    --model) MODEL="${2:-}"; shift 2 ;;
    --unit) UNIT="${2:-}"; shift 2 ;;
    --realm) REALM="${2:-}"; shift 2 ;;
    --zonegroup) ZONEGROUP="${2:-}"; shift 2 ;;
    --zone) ZONE="${2:-}"; shift 2 ;;
    --endpoint) ENDPOINT="${2:-}"; shift 2 ;;
    --restart-action) RESTART_ACTION="${2:-}"; shift 2 ;;
    --access-key) ACCESS_KEY="${2:-}"; shift 2 ;;
    --secret) SECRET="${2:-}"; shift 2 ;;
    --apply) APPLY=1; shift ;;
    *) echo "FAIL: unknown argument '$1'" >&2; usage >&2; exit 1 ;;
  esac
done

# --- guard clauses: required args, no invented defaults ---
# --zonegroup/--endpoint are only meaningful for the two zone-CREATE
# subcommands (Step 4) -- enable-two-way (Step 8) only MODIFIES an existing
# zone's read-only flag, so it does not need them; forcing them here would
# make an operator supply irrelevant values just to satisfy the parser.
[ -n "$DC" ] || { echo "FAIL: --dc is required (vr1-dc0|vr1-dc1)" >&2; exit 1; }
case "$DC" in vr1-dc0|vr1-dc1) ;; *) echo "FAIL: --dc must be vr1-dc0 or vr1-dc1 (got '$DC'). D-119: bare dcN is RETIRED -- it was ambiguous across regions." >&2; exit 1 ;; esac
[ -n "$UNIT" ] || { echo "FAIL: --unit is required (e.g. ceph-radosgw/0 -- measured this session, never assumed)" >&2; exit 1; }
[ -n "$ZONE" ] || { echo "FAIL: --zone is required" >&2; exit 1; }

case "$SUBCMD" in
  master-init)
    [ -n "$REALM" ] || { echo "FAIL: --realm is required for master-init" >&2; exit 1; }
    [ -n "$ZONEGROUP" ] || { echo "FAIL: --zonegroup is required for master-init" >&2; exit 1; }
    [ -n "$ENDPOINT" ] || { echo "FAIL: --endpoint is required for master-init" >&2; exit 1; }
    ;;
  join-readonly)
    [ -n "$ZONEGROUP" ] || { echo "FAIL: --zonegroup is required for join-readonly" >&2; exit 1; }
    [ -n "$ENDPOINT" ] || { echo "FAIL: --endpoint is required for join-readonly" >&2; exit 1; }
    [ -n "$ACCESS_KEY" ] || { echo "FAIL: --access-key is required for join-readonly (capture live from DC1's multisite system user -- never invent)" >&2; exit 1; }
    [ -n "$SECRET" ] || { echo "FAIL: --secret is required for join-readonly" >&2; exit 1; }
    ;;
  enable-two-way) : ;;
esac

# --- $DC gate (DOCFIX-151 convention) ---
# Informational in dry-run (a plan preview is not a mutation -- the whole
# point of --dry-run is to be usable/reviewable even for a DC whose literals
# aren't assigned yet); HARD BLOCKING before any --apply execution below.
# shellcheck source=scripts/lib-net.sh
. "$HERE/lib-net.sh"
DC_GATE_MSG="$(lib_net_select_dc "$DC" 2>&1 1>/dev/null)"; DC_GATE_RC=$?
if [ "$DC_GATE_RC" -eq 0 ]; then
  DC_GATE_LINE="\$DC gate: OK ($DC)"
  # The gate check ran in a subshell; select again HERE so the plane vars the
  # reminder reads below belong to $DC, not the file's flat DC0 defaults
  # (cross-DC value surfacing). rc 0 is already proven, safe under set -e.
  lib_net_select_dc "$DC"
else
  DC_GATE_LINE="\$DC gate: FAILED for '$DC' -- $DC_GATE_MSG"
fi

REPL_CIDR=""
for c in "${PLANE_CIDRS[@]}"; do
  [ "${PLANE_NAME[$c]:-}" = "replication" ] && REPL_CIDR="$c"
done

hdr()  { echo; echo "=== $* ==="; }
note() { echo "  $*"; }

hdr "dc-dc-radosgw-multisite  subcmd=$SUBCMD  dc=$DC  mode=$([ "$APPLY" -eq 1 ] && echo apply || echo dry-run)"
note "juju model=$MODEL unit=$UNIT"
note "$DC_GATE_LINE"
note "reminder: the endpoint above must ride the REPLICATION plane (${REPL_CIDR:-unassigned}, per D-101/lib-net.sh) -- not substituted into any command, confirm the real ULA address separately"

# --- build the remote radosgw-admin command sequence (verbatim upstream ops) ---
SSH_CMDS=()
case "$SUBCMD" in
  master-init)
    SSH_CMDS+=("radosgw-admin realm create --rgw-realm=$REALM --default")
    SSH_CMDS+=("radosgw-admin zonegroup create --rgw-zonegroup=$ZONEGROUP --endpoints=$ENDPOINT --master --default")
    SSH_CMDS+=("radosgw-admin zone create --rgw-zonegroup=$ZONEGROUP --rgw-zone=$ZONE --endpoints=$ENDPOINT --master --default")
    SSH_CMDS+=("radosgw-admin period update --commit")
    ;;
  join-readonly)
    SSH_CMDS+=("radosgw-admin zone create --rgw-zonegroup=$ZONEGROUP --rgw-zone=$ZONE --endpoints=$ENDPOINT --read-only --access-key=$ACCESS_KEY --secret=$SECRET")
    SSH_CMDS+=("radosgw-admin period update --commit")
    ;;
  enable-two-way)
    SSH_CMDS+=("radosgw-admin zone modify --rgw-zone=$ZONE --read-only=false")
    SSH_CMDS+=("radosgw-admin period update --commit")
    ;;
esac

redact() { # redact <string> -- hides --secret's value in printed output only
  local s="$1"
  if [ -n "$SECRET" ]; then s="${s//$SECRET/<REDACTED>}"; fi
  printf '%s' "$s"
}

hdr "PLAN"
n=0
for cmd in "${SSH_CMDS[@]}"; do
  n=$((n+1))
  note "STEP $n: juju ssh -m $MODEL $UNIT -- \"$(redact "$cmd")\""
done
if [ -n "$RESTART_ACTION" ]; then
  n=$((n+1))
  note "STEP $n: juju run $UNIT $RESTART_ACTION -m $MODEL   (restart to pick up realm/zone membership)"
else
  note "NOTE: no --restart-action given -- restart the unit manually after this sequence (confirm the action name via 'juju actions ceph-radosgw', per the runbook; not guessed here)"
fi

if [ "$APPLY" -ne 1 ]; then
  echo
  echo "re-run with --apply to execute (one command at a time, aborts on first failure)."
  echo "OK (dry-run)"
  exit 0
fi

[ "$DC_GATE_RC" -eq 0 ] || { echo "FAIL: \$DC gate refused for '$DC' -- STOP, do not mutate an unassigned DC ($DC_GATE_MSG)" >&2; exit 3; }
command -v juju >/dev/null 2>&1 || { echo "FAIL: juju required on PATH for --apply" >&2; exit 2; }

hdr "EXECUTE (apply mode -- one command at a time, abort on first failure)"
n=0
for cmd in "${SSH_CMDS[@]}"; do
  n=$((n+1))
  echo "  DO STEP $n: juju ssh -m $MODEL $UNIT -- \"$(redact "$cmd")\""
  juju ssh -m "$MODEL" "$UNIT" -- "$cmd" </dev/null || { echo "FAIL: step $n failed -- STOP (no further commands run)" >&2; exit 5; }
done
if [ -n "$RESTART_ACTION" ]; then
  n=$((n+1))
  echo "  DO STEP $n: juju run $UNIT $RESTART_ACTION -m $MODEL"
  juju run "$UNIT" "$RESTART_ACTION" -m "$MODEL" </dev/null || { echo "FAIL: restart action failed -- STOP" >&2; exit 5; }
fi

echo "OK (apply) -- verify with: radosgw-admin realm list / zonegroup get / sync status (per runbook Step 4/8 VERIFY)"
