#!/usr/bin/env bash
# scripts/dc-cache-proxy.sh <check|install> <site> -- per-DC apt CACHING PROXY
# (apt-cacher-ng). D-135 amendment 2026-07-24 (operator-ruled): the INTERIM DC1
# apt path so the DC1 build starts WITHOUT waiting on the full ~950 GiB
# debmirror. DC0 stays the full-mirror path (D-135 item 1); DC1's proxy is
# REPLACED by a full mirror afterward. Roosevelt: cache-proxy vs full-mirror is
# a per-DC utility-node artifact-delivery choice (D-132/D-133).
#
# RUNS ON THE DC RACK HOST, invoked over ssh like dc-mirror.sh (no clone there):
#   check:   ssh -i <dc-key> -J voffice1 <user>@<rack-transit-ip> \
#               'sudo bash -s' -- check dc1 < scripts/dc-cache-proxy.sh
#   install: same with 'install dc1' (idempotent; safe to re-run).
#
# PREREQS on the rack:
#   - the utility NET LAYER (the .4 alias on metal-admin + the default route via
#     the DC edge) must already be up -- it is provided by dc-mirror.sh's
#     <site>-mirror-net.service, present on any site that ran the mirror install.
#     This script does NOT re-create it (it REUSES it); check fails loud if absent.
#   - apt-cacher-ng must be apt-installed first (fail-loud with the exact line).
#
# CONSUMPTION (Stage 5, PROXY mode -- all http apt cached, incl. UCA, no remap):
#   juju model-config apt-http-proxy=http://<LISTEN>:3142
#   (or per-node /etc/apt/apt.conf.d: Acquire::http::Proxy "http://<LISTEN>:3142";)
#
# EXIT: 0 ok | 1 check failed | 2 bad args/unknown site | 4 install failed.
# ASCII + LF only.
set -uo pipefail

MODE="${1:-}"; SITE="${2:-}"
case "$MODE" in check|install) ;; *)
  echo "usage: dc-cache-proxy.sh <check|install> <site>" >&2; exit 2 ;; esac

# ---------------------------------------------------------------------------
# Site table. Same MEASURED/RULED utility .4 + edge gateway per DC as
# dc-mirror.sh (the proxy listens at the SAME utility address the deploy
# already expects). ADD A SITE ONLY WITH MEASURED/RULED VALUES (hard rule 2);
# the harness rejects rows without a MEASURED tag.
# ---------------------------------------------------------------------------
case "$SITE" in
  dc0)
    # MEASURED/RULED 2026-07-23 (D-134 amendment + D-135). NOTE: dc0's RULED
    # path is the FULL MIRROR (D-135 item 1); this row exists for tooling
    # parity only -- dc0 is not deployed via the proxy.
    UTIL_NET="vr1-dc0-metal-admin"
    LISTEN_CIDR="10.12.8.4/22"
    EDGE_GW="10.12.4.1"
    ;;
  dc1)
    # MEASURED/RULED 2026-07-24 (D-135 amendment -- interim DC1 apt path):
    #   LISTEN 10.12.68.4 -- the D-134 utility .4, reused from the mirror-net
    #     layer; EDGE_GW 10.12.64.1 -- dc1 edge LAN gw (upstream egress).
    UTIL_NET="vr1-dc1-metal-admin"
    LISTEN_CIDR="10.12.68.4/22"
    EDGE_GW="10.12.64.1"
    ;;
  *) echo "FAIL: unknown site '$SITE' -- add a MEASURED row block first" >&2; exit 2 ;;
esac
LISTEN="${LISTEN_CIDR%%/*}"
PROXY_PORT="3142"
ACNG_DROPIN="/etc/apt-cacher-ng/acng.conf.d/${SITE}-cache-proxy.conf"

# ---------------------------------------------------------------------------
# Generated config (single source of truth for check AND install). Minimal:
# apt-cacher-ng in proxy mode caches all http apt (archive/security/UCA) with
# no per-repo remap; we only pin the port so a package-default change cannot
# move it. Default CacheDir (/var/cache/apt-cacher-ng) is kept (owner-correct).
# ---------------------------------------------------------------------------
gen_acng_dropin() {
  cat <<EOF
# ${SITE} apt caching proxy -- generated by scripts/dc-cache-proxy.sh
# (D-135 amendment 2026-07-24); do not hand-edit. apt-cacher-ng proxy mode:
# caches all http apt requested by the DC nodes (archive/security/UCA), no
# per-repo remap. Consume via: juju model-config apt-http-proxy=http://${LISTEN}:${PROXY_PORT}
Port: ${PROXY_PORT}
EOF
}

# Bridge for the utility net, resolved from the libvirt net NAME at runtime
# (virbrN is auto-assigned, not stable identity -- hard rule 3).
util_bridge() {
  virsh -c qemu:///system net-dumpxml "$UTIL_NET" 2>/dev/null \
    | sed -n "s/.*bridge name='\([^']*\)'.*/\1/p"
}

# ---------------------------------------------------------------------------
# check -- mutate no CONFIG/service state; report and exit 0 ok / 1 incomplete.
# (The behavioral smoke test may populate the proxy cache with two Release
# files -- benign and self-managing; it is the proof the proxy actually serves.)
# ---------------------------------------------------------------------------
FAILED=0
say(){ echo "  $1"; }
miss(){ echo "  MISS $1"; FAILED=1; }

do_check() {
  command -v apt-cacher-ng >/dev/null 2>&1 || dpkg -s apt-cacher-ng >/dev/null 2>&1 \
    && say "OK   apt-cacher-ng installed" || miss "apt-cacher-ng not installed"
  if [ ! -f "$ACNG_DROPIN" ]; then miss "$ACNG_DROPIN absent"; else
    if diff -q <(gen_acng_dropin) "$ACNG_DROPIN" >/dev/null 2>&1; then
      say "OK   $ACNG_DROPIN matches"; else miss "$ACNG_DROPIN DIFFERS from generated"; fi
  fi
  systemctl is-active --quiet apt-cacher-ng \
    && say "OK   apt-cacher-ng active" || miss "apt-cacher-ng not active"
  ss -ltn 2>/dev/null | grep -q ":${PROXY_PORT} " \
    && say "OK   listening on :${PROXY_PORT}" || miss "not listening on :${PROXY_PORT}"
  # Utility net layer (REUSED from <site>-mirror-net.service, not owned here).
  br="$(util_bridge)"
  if [ -n "$br" ] && ip -4 -o addr show dev "$br" 2>/dev/null | grep -q " ${LISTEN}/"; then
    say "OK   ${LISTEN_CIDR} on $br ($UTIL_NET)"
  else miss "${LISTEN_CIDR} absent on metal-admin bridge (run dc-mirror.sh install $SITE -- it owns the net layer)"; fi
  ip route show default 2>/dev/null | grep -q "via ${EDGE_GW}" \
    && say "OK   default route via edge ${EDGE_GW}" \
    || miss "default route via ${EDGE_GW} absent"
  # Behavioral smoke test: fetch through the proxy (proxy mode) -- archive AND
  # UCA, the two the deploy needs; proves cache-miss passthrough + upstream reach.
  if command -v curl >/dev/null 2>&1; then
    for u in "http://archive.ubuntu.com/ubuntu/dists/jammy/Release" \
             "http://ubuntu-cloud.archive.canonical.com/ubuntu/dists/jammy-updates/caracal/Release"; do
      HTTP="$(curl -s -o /dev/null -w '%{http_code}' -x "http://${LISTEN}:${PROXY_PORT}" -m 20 "$u" 2>/dev/null)"
      [ "$HTTP" = "200" ] && say "OK   proxy serves $u (200)" \
        || miss "proxy did NOT serve $u (got '$HTTP')"
    done
  fi
  [ "$FAILED" -eq 0 ] && { echo "dc-cache-proxy check ($SITE): PASS"; exit 0; }
  echo "dc-cache-proxy check ($SITE): FAIL"; exit 1
}

# ---------------------------------------------------------------------------
# install -- idempotent; requires root. Net layer is a PREREQ (owned by
# dc-mirror.sh), verified fail-loud; apt-cacher-ng package is fail-loud too.
# ---------------------------------------------------------------------------
do_install() {
  [ "$(id -u)" = "0" ] || { echo "FAIL: install requires root" >&2; exit 4; }
  umask 022
  # Prereq 1: the utility net layer must be up (we reuse it, never re-create it).
  br="$(util_bridge)"
  if [ -z "$br" ] || ! ip -4 -o addr show dev "$br" 2>/dev/null | grep -q " ${LISTEN}/" \
     || ! ip route show default 2>/dev/null | grep -q "via ${EDGE_GW}"; then
    echo "FAIL: utility net layer absent (${LISTEN} on ${UTIL_NET} + default via ${EDGE_GW})" >&2
    echo "  it is owned by dc-mirror.sh -- run: dc-mirror.sh install $SITE  first" >&2
    exit 4
  fi
  # Prereq 2: the package (fail-loud, like dc-mirror.sh does for debmirror/nginx).
  if ! dpkg -s apt-cacher-ng >/dev/null 2>&1; then
    echo "FAIL: apt-cacher-ng not installed" >&2
    echo "  net layer is up -- run: apt-get update && apt-get install -y apt-cacher-ng" >&2
    echo "  then re-run: dc-cache-proxy.sh install $SITE" >&2
    exit 4
  fi
  mkdir -p "$(dirname "$ACNG_DROPIN")" || exit 4
  gen_acng_dropin > "$ACNG_DROPIN" || exit 4
  systemctl enable apt-cacher-ng >/dev/null 2>&1 || exit 4
  systemctl restart apt-cacher-ng || { echo "FAIL: apt-cacher-ng restart" >&2; exit 4; }
  echo "dc-cache-proxy install ($SITE): done -- running check:"
  do_check
}

case "$MODE" in
  check)   do_check ;;
  install) do_install ;;
esac
