init
This commit is contained in:
Executable
+127
@@ -0,0 +1,127 @@
|
||||
#!/usr/bin/env bash
|
||||
# Compare Users/Channels/Posts counts: source MDB vs target (CNPG or Selectel).
|
||||
# CNPG cluster: registry.target_cluster / logs/<id>.cnpg-cluster / CLI profile.
|
||||
# Usage: verify-one.sh <kube_context> <installation_id|uuid> [cnpg|selectel|loop-cnpg-lm|…]
|
||||
set -euo pipefail
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck disable=SC1091
|
||||
source "${DIR}/lib.sh"
|
||||
|
||||
[[ $# -ge 2 ]] || die "usage: $0 <kube_context> <installation_id|uuid> [cnpg|selectel|loop-cnpg-lm|loop-cnpg-wilix-1|loop-cnpg-ff]"
|
||||
CTX="$1"
|
||||
INSTALLATION_RAW="$2"
|
||||
shift 2
|
||||
parse_target_dsn_args "$@"
|
||||
if [[ -n "${PARSE_TARGET}" ]]; then
|
||||
TARGET="${PARSE_TARGET}"
|
||||
fi
|
||||
if [[ -n "${PARSE_CNPG_PROFILE:-}" ]]; then
|
||||
CNPG_CLUSTER_PROFILE="${PARSE_CNPG_PROFILE}"
|
||||
fi
|
||||
|
||||
INSTALLATION="$(resolve_installation "$CTX" "$INSTALLATION_RAW")"
|
||||
if [[ -z "${PARSE_TARGET:-}" ]]; then
|
||||
TARGET="$(load_installation_target "$INSTALLATION")"
|
||||
fi
|
||||
|
||||
resolve_cnpg_cluster_profile "$INSTALLATION"
|
||||
resolve_target "${TARGET:-cnpg}"
|
||||
|
||||
SOURCE_FILE="${DIR}/${LOG_DIR}/${INSTALLATION}.source.dsn"
|
||||
if [[ -n "${SOURCE_DSN:-}" ]]; then
|
||||
parse_dsn "$SOURCE_DSN"
|
||||
elif [[ -f "$SOURCE_FILE" ]]; then
|
||||
parse_dsn "$(cat "$SOURCE_FILE")"
|
||||
else
|
||||
die "No source DSN; run migrate-one first or set SOURCE_DSN="
|
||||
fi
|
||||
|
||||
DB_NAME="$SRC_DB"
|
||||
log "=== verify-one ${INSTALLATION} target=${TARGET_KIND} cnpg=${CNPG_CLUSTER_PROFILE:-${CNPG_CLUSTER:-?}} endpoint=${TARGET_HOST}:${TARGET_PORT} admin=${CNPG_KUBE_CONTEXT}/${CNPG_NAMESPACE}/${CNPG_CLUSTER} ==="
|
||||
log "${TARGET_KIND} counts for db=${DB_NAME}"
|
||||
tgt="$(target_counts "$CTX" "$INSTALLATION" "$DB_NAME" "$SRC_USER" "$SRC_PASS")"
|
||||
log "${TARGET_KIND} users/channels/posts: ${tgt}"
|
||||
|
||||
JOB_NAME="pgverify-${INSTALLATION:0:18}-$(date +%s | tail -c 4)"
|
||||
SECRET_NAME="${JOB_NAME}-env"
|
||||
JOB_FILE="$(mktemp)"
|
||||
cleanup() {
|
||||
kubectl --context "$CTX" -n "$JOB_NS" delete job "$JOB_NAME" --ignore-not-found --wait=false >/dev/null 2>&1 || true
|
||||
kubectl --context "$CTX" -n "$JOB_NS" delete secret "$SECRET_NAME" --ignore-not-found --wait=false >/dev/null 2>&1 || true
|
||||
rm -f "$JOB_FILE"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
kubectl --context "$CTX" -n "$JOB_NS" create secret generic "$SECRET_NAME" \
|
||||
--from-literal=SRC_HOST="$SRC_HOST" \
|
||||
--from-literal=SRC_PORT="$SRC_PORT" \
|
||||
--from-literal=SRC_USER="$SRC_USER" \
|
||||
--from-literal=SRC_PASS="$SRC_PASS" \
|
||||
--from-literal=SRC_DB="$SRC_DB" \
|
||||
--from-literal=SRC_SSLMODE="${SRC_SSLMODE}" \
|
||||
--dry-run=client -o yaml | kubectl --context "$CTX" -n "$JOB_NS" apply -f -
|
||||
|
||||
JOB_NAME="$JOB_NAME" JOB_NS="$JOB_NS" PG_IMAGE="$PG_IMAGE" SECRET_NAME="$SECRET_NAME" \
|
||||
python3 - "$JOB_FILE" <<'PY'
|
||||
import json, os, sys
|
||||
script = r"""set -euo pipefail
|
||||
export PGSSLMODE="${SRC_SSLMODE:-disable}"
|
||||
PGPASSWORD="${SRC_PASS}" psql -h "${SRC_HOST}" -p "${SRC_PORT}" -U "${SRC_USER}" -d "${SRC_DB}" -Atc \
|
||||
"SELECT (SELECT count(*) FROM users)||E'\t'||(SELECT count(*) FROM channels)||E'\t'||(SELECT count(*) FROM posts);"
|
||||
"""
|
||||
job = {
|
||||
"apiVersion": "batch/v1",
|
||||
"kind": "Job",
|
||||
"metadata": {"name": os.environ["JOB_NAME"], "namespace": os.environ["JOB_NS"]},
|
||||
"spec": {
|
||||
"ttlSecondsAfterFinished": 600,
|
||||
"backoffLimit": 1,
|
||||
"template": {"spec": {
|
||||
"restartPolicy": "Never",
|
||||
"containers": [{
|
||||
"name": "verify",
|
||||
"image": os.environ["PG_IMAGE"],
|
||||
"envFrom": [{"secretRef": {"name": os.environ["SECRET_NAME"]}}],
|
||||
"command": ["bash", "-c"],
|
||||
"args": [script],
|
||||
}],
|
||||
}},
|
||||
},
|
||||
}
|
||||
json.dump(job, open(sys.argv[1], "w"))
|
||||
PY
|
||||
|
||||
kubectl --context "$CTX" -n "$JOB_NS" apply -f "$JOB_FILE"
|
||||
|
||||
MAX_POST_DELTA="${VERIFY_MAX_POST_DELTA:-100}"
|
||||
mark_verified() {
|
||||
registry_set "$INSTALLATION" "status=verified" || true
|
||||
if [[ "${TARGET_KIND}" == "cnpg" && -n "${CNPG_CLUSTER_PROFILE:-}" ]]; then
|
||||
registry_set "$INSTALLATION" "target_cluster=${CNPG_CLUSTER_PROFILE}" || true
|
||||
fi
|
||||
}
|
||||
|
||||
if wait_job "$CTX" "$JOB_NS" "$JOB_NAME" 600; then
|
||||
src="$(kubectl --context "$CTX" -n "$JOB_NS" logs "job/${JOB_NAME}" | tail -1)"
|
||||
log "MDB users/channels/posts: ${src}"
|
||||
if [[ "$src" == "$tgt" ]]; then
|
||||
log "VERIFY OK counts match exactly"
|
||||
mark_verified
|
||||
exit 0
|
||||
fi
|
||||
python3 - "$src" "$tgt" "$MAX_POST_DELTA" <<'PY'
|
||||
import sys
|
||||
src, tgt, maxd = sys.argv[1], sys.argv[2], int(sys.argv[3])
|
||||
su,sc,sp = map(int, src.split("\t"))
|
||||
tu,tc,tp = map(int, tgt.split("\t"))
|
||||
if su!=tu or sc!=tc:
|
||||
print(f"users/channels mismatch src={src} tgt={tgt}", file=sys.stderr); sys.exit(2)
|
||||
if abs(sp-tp) > maxd:
|
||||
print(f"posts delta {abs(sp-tp)} > {maxd}", file=sys.stderr); sys.exit(2)
|
||||
print(f"VERIFY OK users/channels match; posts delta={abs(sp-tp)} (live source)")
|
||||
PY
|
||||
mark_verified
|
||||
exit 0
|
||||
fi
|
||||
die "verify source job failed"
|
||||
Reference in New Issue
Block a user