Files
loop-ops/db/cutover-list.sh
2026-10-27 14:29:43 +03:00

184 lines
5.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# Run cutover-one for each line in a list file OR a registry batch.
#
# Usage:
# ./cutover-list.sh --batch wave1-lm
# ./cutover-list.sh batches/foo.txt
# PARALLELISM=2 ./cutover-list.sh --batch wave4-lp-3
# ./cutover-list.sh --batch wave1-lm --force # also re-cutover status=done|cutover
# ./cutover-list.sh --batch wave4-lp-3 --mem-req 256Mi # MM memory request after cutover
#
# Without --force: ONLY registry status=verified (batch export + per-row check).
# With --force: also queue status=done|cutover (re-cutover).
# --mem-req: optional k8s memory request for MM (256Mi, 250M, 1Gi); default from config.env.
# Prefer PARALLELISM=1 for production cutovers unless you know you want overlap.
set -euo pipefail
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "${DIR}/lib.sh"
LIST=""
BATCH=""
FORCE=0
MEM_REQ=""
# locked unless --force (then export may include done/cutover)
STATUS="verified"
while [[ $# -gt 0 ]]; do
case "$1" in
--batch) BATCH="${2:-}"; shift 2 ;;
--status)
# only meaningful with --force; without force we always use verified
STATUS="${2:-}"
shift 2
;;
--mem-req|--memory-request)
MEM_REQ="$(normalize_mem_req "${2:-}")"
shift 2
;;
--force) FORCE=1; shift ;;
-h|--help)
sed -n '2,16p' "$0"
exit 0
;;
*)
LIST="$1"
shift
;;
esac
done
if [[ -n "$MEM_REQ" ]]; then
export CUTOVER_MEMORY_REQUEST="$MEM_REQ"
fi
if [[ "$FORCE" != "1" ]]; then
STATUS="verified"
else
# re-cutover already finished installs too
if [[ "$STATUS" == "verified" ]]; then
STATUS="verified,done,cutover"
fi
fi
registry_status() {
local installation="$1" st
st="$(registry_get "$installation" status 2>/dev/null || true)"
printf '%s' "$st" | tr -d '[:space:]'
}
# Return 0 if this install may be cut over under current FORCE/STATUS rules.
cutover_allowed() {
local installation="$1" st allowed
st="$(registry_status "$installation")"
if [[ "$FORCE" == "1" ]]; then
case "$st" in
verified|done|cutover) return 0 ;;
*)
# honor custom --status list when --force
IFS=',' read -r -a allowed <<<"$STATUS"
for a in "${allowed[@]}"; do
[[ "$st" == "$a" ]] && return 0
done
return 1
;;
esac
fi
[[ "$st" == "verified" ]]
}
WORK="$(mktemp)"
NORM="$(mktemp)"
trap 'rm -f "$WORK" "$NORM"' EXIT
if [[ -n "$BATCH" ]]; then
# --include-done: verified/done/cutover are in DONE_LIKE and would be skipped otherwise
"${DIR}/registry-export-batch.sh" "$BATCH" --status "$STATUS" --include-done -o "$WORK"
log "=== cutover-list batch=${BATCH} status=${STATUS} parallelism=${PARALLELISM} force=${FORCE} mem-req=${CUTOVER_MEMORY_REQUEST:-default} ==="
elif [[ -n "$LIST" ]]; then
[[ -f "$LIST" ]] || die "list file not found: $LIST"
while IFS= read -r line || [[ -n "$line" ]]; do
[[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue
echo "$line" >>"${WORK}.raw"
done <"$LIST"
mv "${WORK}.raw" "$WORK"
log "=== cutover-list ${LIST} parallelism=${PARALLELISM} force=${FORCE} mem-req=${CUTOVER_MEMORY_REQUEST:-default} (filter status=${STATUS}) ==="
else
die "usage: $0 --batch <id> | <list_file> [--force] [--mem-req 256Mi]"
fi
skipped=0
queued_preview=0
while IFS= read -r line || [[ -n "$line" ]]; do
[[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue
# shellcheck disable=SC2086
set -- $line
ctx="${1:-}"; installation="${2:-}"
[[ -n "$ctx" && -n "$installation" ]] || continue
shift 2 2>/dev/null || true
if ! cutover_allowed "$installation"; then
st="$(registry_status "$installation")"
if [[ "$FORCE" == "1" ]]; then
log "SKIP ${installation} (status=${st:-empty}; --force allows verified|done|cutover)"
else
log "SKIP ${installation} (status=${st:-empty}; need verified, or pass --force)"
fi
skipped=$((skipped + 1))
continue
fi
printf '%s\t%s' "$ctx" "$installation" >>"$NORM"
for a in "$@"; do
printf '\t%s' "$a" >>"$NORM"
done
printf '\n' >>"$NORM"
queued_preview=$((queued_preview + 1))
done <"$WORK"
total="$(wc -l <"$NORM" | tr -d ' ')"
log "Queued ${total} installations for cutover (skipped=${skipped})"
[[ "$total" -gt 0 ]] || { log "nothing to cutover"; exit 0; }
run_one() {
local ctx="$1" installation="$2" st
shift 2
# re-check: parallel workers / stale export
if ! cutover_allowed "$installation"; then
st="$(registry_status "$installation")"
log "SKIP ${installation} (status=${st:-empty} at run time)"
return 0
fi
local extra=()
if [[ -n "${MEM_REQ:-}" ]]; then
extra+=(--mem-req "$MEM_REQ")
fi
"${DIR}/cutover-one.sh" "$ctx" "$installation" "${extra[@]+"${extra[@]}"}" "$@" \
&& LOOPCTL_YES=1 "${DIR}/bind.sh" "$ctx" "$installation" \
|| log "FAILED cutover/bind $installation (continuing)"
}
if [[ "${PARALLELISM}" -le 1 ]]; then
while IFS=$'\t' read -r ctx installation a3 a4 || [[ -n "${ctx:-}" ]]; do
[[ -z "${ctx:-}" ]] && continue
args=()
[[ -n "${a3:-}" ]] && args+=("$a3")
[[ -n "${a4:-}" ]] && args+=("$a4")
run_one "$ctx" "$installation" "${args[@]+"${args[@]}"}"
done <"$NORM"
else
while IFS=$'\t' read -r ctx installation a3 a4 || [[ -n "${ctx:-}" ]]; do
[[ -z "${ctx:-}" ]] && continue
while [[ "$(jobs -pr | wc -l | tr -d ' ')" -ge "${PARALLELISM}" ]]; do
sleep 5
done
args=()
[[ -n "${a3:-}" ]] && args+=("$a3")
[[ -n "${a4:-}" ]] && args+=("$a4")
run_one "$ctx" "$installation" "${args[@]+"${args[@]}"}" &
done <"$NORM"
wait || true
fi
log "=== cutover-list finished ==="
python3 "${DIR}/registry_lib.py" --registry "${REGISTRY_PATH}" summary || true