Newer
Older
openstack-caracal-ipv4 / scripts / phase-05-amphora-pipeline.sh
#!/usr/bin/env bash
# scripts/phase-05-amphora-pipeline.sh
#
# D-021 Phase 2: seed the amphora base, then retrofit it into the raw octavia-amphora
# image. This encapsulates the phase-05 Step 5.2 canonical block AS A SCRIPT, to remove
# the large multi-line-paste / command-size-overrun risk (the inline block mangled on
# paste 2026-06-27) and make it a reviewed, version-controlled, per-rebuild artifact.
#
# CONSEQUENTIAL + MUTATING, but fully IDEMPOTENT and self-gating, so the human gates by
# CHOOSING to invoke it; re-running is safe:
#   amphora tagged $OTAG already present -> skip seed+build, jump to confirm
#   base present (no amphora)            -> skip download/upload, retrofit only
#   fresh                                -> download + sha256-verify + upload + retrofit
#
# Tunables via env (jammy / Caracal defaults; override for a different base):
#   BASE_IMG_URL BASE_SUM_URL BASE_IMG_FILE BASE_NAME VERSION_NAME PRODUCT_NAME
#   RETRO STAGE MODEL RETROFIT_WAIT
#
# Requires: jumphost; admin-openrc sourced (OS_AUTH_URL); openstack + juju; curl/wget/sha256sum.
# Usage:  source ~/admin-openrc && scripts/phase-05-amphora-pipeline.sh
# Exit:   0 amphora ACTIVE + tagged (D-021 complete)
#         1 gate / seed / build / confirm failure
#         2 precondition (client missing, openrc not sourced)
#
# NOTE on the retrofit wait: if `juju run ... --wait` times out, the hook keeps running
# on the unit -- do NOT blindly re-invoke. Re-running is safe in that it will not fire a
# second build once the image is tagged (it skips), but check `juju operations` /
# `juju show-operation <N>` first. ASCII + LF.

set -euo pipefail
shopt -s inherit_errexit 2>/dev/null || true

# --- tunables (env-overridable) -----------------------------------------------------
BASE_IMG_URL="${BASE_IMG_URL:-https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img}"
BASE_SUM_URL="${BASE_SUM_URL:-https://cloud-images.ubuntu.com/jammy/current/SHA256SUMS}"
BASE_IMG_FILE="${BASE_IMG_FILE:-jammy-server-cloudimg-amd64.img}"
BASE_NAME="${BASE_NAME:-jammy-amphora-base}"          # NOT amphora-tagged; only the retrofit OUTPUT is
VERSION_NAME="${VERSION_NAME:-$(date -u +%Y%m%d)}"    # cosmetic (D-021): feeds the OUTPUT name
PRODUCT_NAME="${PRODUCT_NAME:-com.ubuntu.cloud:server:22.04:amd64}"  # cosmetic metadata
RETRO="${RETRO:-octavia-diskimage-retrofit}"
STAGE="${STAGE:-$HOME/amphora-base}"                  # snap-READABLE (home iface); NOT /tmp (appendix-A L7)
MODEL="${MODEL:-openstack}"
RETROFIT_WAIT="${RETROFIT_WAIT:-30m}"

# --- preconditions ------------------------------------------------------------------
command -v openstack >/dev/null 2>&1 || { echo "FAIL: openstack client not found" >&2; exit 2; }
command -v juju      >/dev/null 2>&1 || { echo "FAIL: juju client not found" >&2; exit 2; }
command -v sha256sum >/dev/null 2>&1 || { echo "FAIL: sha256sum not found" >&2; exit 2; }
[ -n "${OS_AUTH_URL:-}" ] || { echo "FAIL: OS_AUTH_URL unset -- 'source ~/admin-openrc' first" >&2; exit 2; }

# ---- Phase 0: config GATE (abort if the cloud is not in the expected state) --------
UIE=$(juju config -m "$MODEL" "$RETRO" use-internal-endpoints)
IMGFMT=$(juju config -m "$MODEL" "$RETRO" image-format)
RTAG=$(juju config -m "$MODEL" "$RETRO" amp-image-tag)
OTAG=$(juju config -m "$MODEL" octavia amp-image-tag)
[ "$UIE" = true ]   || { echo "GATE FAIL: $RETRO use-internal-endpoints=$UIE (need true; retrofit is metal-only)"; exit 1; }
[ "$IMGFMT" = raw ] || { echo "GATE FAIL: $RETRO image-format=$IMGFMT (need raw; Ceph RBD fast-clone)"; exit 1; }
{ [ -n "$RTAG" ] && [ "$RTAG" = "$OTAG" ]; } || { echo "GATE FAIL: amp-image-tag mismatch retrofit='$RTAG' octavia='$OTAG' (LP#1937003)"; exit 1; }
echo "[OK] config gate: use-internal-endpoints=true image-format=raw amp-image-tag=$OTAG"

# ---- Phase 1: idempotency + seed the base (only if no amphora AND no base) ----------
AMPH=$(openstack image list --tag "$OTAG" -f value -c ID | head -1)
if [ -n "$AMPH" ]; then
  echo "[SKIP] image already tagged $OTAG ($AMPH) -- pipeline complete; jumping to confirm"
else
  BASE_ID=$(openstack image list --name "$BASE_NAME" -f value -c ID | head -1)
  if [ -z "$BASE_ID" ]; then
    mkdir -p "$STAGE"; LOCAL="$STAGE/$BASE_IMG_FILE"
    EXP=$(curl -fsSL "$BASE_SUM_URL" | awk -v f="$BASE_IMG_FILE" '$2=="*"f || $2==f {print $1}')
    [ -n "$EXP" ] || { echo "GATE FAIL: no published checksum for $BASE_IMG_FILE"; exit 1; }
    if [ -f "$LOCAL" ] && [ "$(sha256sum "$LOCAL" | awk '{print $1}')" = "$EXP" ]; then
      echo "[OK] staged base present + checksum-valid; skipping download"
    else
      echo "[..] downloading base to $LOCAL (snap-readable; NOT /tmp)"
      wget -q -O "$LOCAL" "$BASE_IMG_URL"
      GOT=$(sha256sum "$LOCAL" | awk '{print $1}')
      [ "$EXP" = "$GOT" ] || { echo "GATE FAIL: checksum mismatch exp='$EXP' got='$GOT'"; exit 1; }
      echo "[OK] checksum verified ($GOT)"
    fi
    echo "[..] uploading base to glance (qcow2; retrofit props; NO amphora tag on the base)"
    BASE_ID=$(openstack image create "$BASE_NAME" \
      --file "$LOCAL" --disk-format qcow2 --container-format bare \
      --property architecture=x86_64 --property os_distro=ubuntu --property os_version=22.04 \
      --property version_name="$VERSION_NAME" --property product_name="$PRODUCT_NAME" \
      -f value -c id)
  fi
  [ -n "$BASE_ID" ] || { echo "GATE FAIL: base image id empty after seed"; exit 1; }
  echo "[OK] base image: $BASE_ID"

  # ---- Phase 2: retrofit (long-running build; bounded wait; tee the result) --------
  echo "[..] running retrofit-image (multi-minute build; --wait=$RETROFIT_WAIT)"
  juju run "$RETRO/leader" retrofit-image source-image="$BASE_ID" -m "$MODEL" \
    --wait="$RETROFIT_WAIT" 2>&1 | tee "$HOME/retrofit-image.out"
fi

# ---- Phase 3: confirm (amphora present + active + tagged == octavia's tag) ----------
echo "=== CONFIRM: images tagged $OTAG ==="
openstack image list --tag "$OTAG" -f value -c ID -c Name -c Status
ACT=$(openstack image list --tag "$OTAG" -f value -c Status | grep -xc active || true)
[ "$ACT" -ge 1 ] || { echo "CONFIRM FAIL: no ACTIVE image tagged $OTAG"; exit 1; }
echo "[OK] amphora present + active + tagged $OTAG (matches octavia amp-image-tag) -- D-021 complete"