Files
sicabe-core/deploy/scripts/verify-nessie.sh
T
2026-10-13 07:07:05 +07:00

249 lines
12 KiB
Bash
Executable File

#!/usr/bin/env bash
# =============================================================================
# eBdesk SiCABE — Verifikasi Nessie: versi + dukungan API (native v2 & Iceberg REST)
# + contoh schema pada catalog (memastikan konfigurasi benar).
#
# PENTING saat Nessie EXISTING: Trino/Dremio memakai API NATIVE v2 (/api/v2),
# sedangkan StarRocks & Sling memakai Iceberg REST (/iceberg). Bila Nessie existing
# HANYA punya API native (tanpa Iceberg REST), StarRocks/Sling GAGAL — skrip ini
# membuktikannya lebih awal (bukan menunggu verify-phase4/5).
#
# Yang diperiksa:
# A. Konektivitas & versi : /api/v2/config (specVersion, defaultBranch, …) + versi
# server: /q/info (build.version) · NESSIE_VERSION(.env) · image container.
# B. Dukungan API : native v2 + Iceberg REST (<prefix>/v1/config) — wajib ada.
# C. Catalog (contoh) : ≤5 namespace (schema) via Iceberg REST + branch/refs.
# D. Ringkasan
#
# Pemakaian:
# ./scripts/verify-nessie.sh
# ./scripts/verify-nessie.sh --env-file generated/host-compute/.env
# NESSIE_URL=http://nessie.corp:19120 ./scripts/verify-nessie.sh
# Keluar: 0 = OK (WARN boleh) · 1 = ada FAIL · 2 = argumen salah
# =============================================================================
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
# shellcheck source=../lib/common.sh
source "${SCRIPT_DIR}/lib/common.sh" 2>/dev/null || true
# shellcheck source=../config/environment.defaults.sh
source "${SCRIPT_DIR}/config/environment.defaults.sh" 2>/dev/null || true
sicabe_load_env "${SCRIPT_DIR}" "$@" || exit 2
usage() { awk 'NR>1 && /^#/ {sub(/^# ?/,""); print; next} NR>1 {exit}' "$0"; }
while [[ $# -gt 0 ]]; do
case "$1" in
--env-file) shift 2 ;;
-h|--help) usage; exit 0 ;;
*) echo "Argumen tidak dikenal: $1" >&2; exit 2 ;;
esac
done
BOLD=$'\e[1m'; GREEN=$'\e[32m'; RED=$'\e[31m'; YELLOW=$'\e[33m'; CYAN=$'\e[36m'; NC=$'\e[0m'
PASSC="${GREEN}[PASS]${NC}"; FAILC="${RED}[FAIL]${NC}"; WARNC="${YELLOW}[WARN]${NC}"; INFOC="${CYAN}[INFO]${NC}"
PASS_N=0; FAIL_N=0; WARN_N=0
pass() { echo -e " ${PASSC} $1"; PASS_N=$((PASS_N + 1)); }
fail() { echo -e " ${FAILC} $1"; FAIL_N=$((FAIL_N + 1)); }
warn() { echo -e " ${WARNC} $1"; WARN_N=$((WARN_N + 1)); }
info() { echo -e " ${INFOC} $1"; }
# Dasar URL host-facing (sadar NESSIE_URL eksternal/lintas-host) + prefix REST + konteks.
BASE="$(sicabe_host_url NESSIE_URL NESSIE_HOST "${NESSIE_PORT:-12120}")"
PFX="${NESSIE_ICEBERG_PREFIX:-/iceberg}"
WAREHOUSE="${ICEBERG_WAREHOUSE:-warehouse}"
BUCKET="${MINIO_BUCKET_ICEBERG_DATA:-iceberg-data}"
# Kode HTTP + penyebab (agar FAIL informatif, bukan "tidak merespon" saja).
http_code() { local c; c="$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 "$1" 2>/dev/null)"; printf '%s' "${c:-000}"; }
code_hint() {
case "$1" in
000) echo "tidak terjangkau (koneksi/DNS/timeout) — cek port/alamat/jaringan";;
401|403) echo "butuh autentikasi (Nessie existing memakai bearer?)";;
404) echo "path tidak ada — versi Nessie lama / Iceberg REST belum aktif (butuh warehouse+object store) / proxy tak merutekan path ini";;
429) echo "rate-limited oleh proxy/edge";;
500|502|503|504) echo "error di server/proxy — cek konfigurasi Nessie (warehouse/object store)";;
*) echo "HTTP $1";;
esac
}
echo -e "${BOLD}Verifikasi Nessie — ${BASE} (Iceberg REST prefix '${PFX}')${NC}"
echo ""
# ─────────────────────────────────────────────────────────────────────────────
# A. Konektivitas & versi
# ─────────────────────────────────────────────────────────────────────────────
echo -e "${BOLD}A. Konektivitas & versi${NC}"
cfg="$(curl -sf --max-time 10 "${BASE}/api/v2/config" 2>/dev/null || echo "")"
if [[ -z "${cfg}" ]]; then
code="$(http_code "${BASE}/api/v2/config")"
fail "Nessie API native v2 tidak merespon di ${BASE}/api/v2/config [HTTP ${code} — $(code_hint "${code}")]"
echo ""
echo -e "${BOLD}Ringkasan: ${PASS_N} PASS · ${FAIL_N} FAIL · ${WARN_N} WARN${NC}"
echo -e "${RED}✗ Nessie tak terjangkau / API v2 tak ada.${NC}"
exit 1
fi
pass "Nessie API native v2 aktif"
printf '%s' "${cfg}" | python3 -c '
import sys, json
try:
d = json.load(sys.stdin)
except Exception:
sys.exit(0)
for k in ("specVersion", "defaultBranch", "maxSupportedApiVersion", "minSupportedApiVersion", "upstreamCommitId"):
if d.get(k) is not None:
print(f" {k} = {d.get(k)}")
' 2>/dev/null || true
ver_env="${NESSIE_VERSION:-}"
img="$(docker inspect --format '{{.Config.Image}}' sicabe-nessie 2>/dev/null || true)"
# Versi APLIKASI via Quarkus info (/q/info) — bila ekstensi quarkus-info aktif.
qi="$(curl -sf --max-time 10 "${BASE}/q/info" 2>/dev/null || echo "")"
qi_ver=""
if [[ -n "${qi}" ]]; then
qi_ver="$(printf '%s' "${qi}" | python3 -c '
import sys, json
try:
d = json.load(sys.stdin)
except Exception:
sys.exit()
b = d.get("build") or {}
print(b.get("version") or d.get("version") or "")
' 2>/dev/null)"
fi
[[ -n "${ver_env}" ]] && info "NESSIE_VERSION(.env) = ${ver_env}"
[[ -n "${img}" ]] && info "image container (lokal) = ${img}"
if [[ -n "${qi_ver}" ]]; then
info "versi server (/q/info build.version) = ${qi_ver}"
elif [[ -n "${qi}" ]]; then
info "/q/info tersedia tetapi build.version tak terlihat"
fi
hdr="$(curl -sD - -o /dev/null --max-time 10 "${BASE}/api/v2/config" 2>/dev/null | tr -d '\r' | grep -iE '^(server|via|x-nessie)' | paste -sd' ' - || true)"
[[ -n "${hdr}" ]] && info "header: ${hdr}"
if [[ -z "${ver_env}" && -z "${img}" && -z "${qi_ver}" ]]; then
warn "Versi SERVER Nessie tak terdeteksi (bukan container lokal; /q/info & header kosong) — set NESSIE_VERSION bila perlu"
fi
echo ""
# ─────────────────────────────────────────────────────────────────────────────
# B. Dukungan API (native v2 + Iceberg REST)
# ─────────────────────────────────────────────────────────────────────────────
echo -e "${BOLD}B. Dukungan API${NC}"
pass "native v2 (/api/v2) — dipakai Trino & Dremio"
ic_cfg="$(curl -sf --max-time 10 "${BASE}${PFX}/v1/config" 2>/dev/null || echo "")"
if [[ -n "${ic_cfg}" ]]; then
pass "Iceberg REST (${PFX}/v1/config) aktif — dipakai Sling & StarRocks"
printf '%s' "${ic_cfg}" | python3 -c '
import sys, json
try:
d = json.load(sys.stdin)
except Exception:
sys.exit(0)
ov = d.get("overrides") or {}
df = d.get("defaults") or {}
for k in ("warehouse", "prefix"):
v = ov.get(k) or df.get(k)
if v:
print(f" {k} = {v}")
' 2>/dev/null || true
else
code="$(http_code "${BASE}${PFX}/v1/config")"
fail "Iceberg REST ${BASE}${PFX} TIDAK tersedia [HTTP ${code} — $(code_hint "${code}")]"
warn "→ StarRocks (katalog 'iceberg_catalog') & Sling (catalog_type=rest) akan GAGAL."
warn " Trino/Dremio tetap OK (mereka memakai API native v2)."
fi
v1="$(http_code "${BASE}/api/v1/config")"
[[ "${v1}" == "200" ]] && info "API native v1 MASIH tersedia (HTTP 200) — versi lama, tak dipakai"
echo ""
# ─────────────────────────────────────────────────────────────────────────────
# C. Catalog — contoh schema (namespace) & refs
# ─────────────────────────────────────────────────────────────────────────────
echo -e "${BOLD}C. Catalog — contoh schema (≤5) & refs${NC}"
# Endpoint 'namespaces' Iceberg REST memakai PREFIX (warehouse/branch) yang bergantung
# pada server — `/v1/namespaces` (tanpa prefix) bisa 404 walau REST aktif. Coba beberapa
# kandidat; kandidat pertama yang mengembalikan JSON namespace dipakai.
ns_parse() {
printf '%s' "$1" | python3 -c '
import sys, json
try:
d = json.load(sys.stdin)
except Exception:
sys.exit(1)
ns = d.get("namespaces")
if ns is None:
sys.exit(1)
def flat(n): return ".".join(n) if isinstance(n, list) else str(n)
print(f"{len(ns)}|" + ", ".join(flat(n) for n in ns[:5]))
' 2>/dev/null
}
ic_prefix="$(printf '%s' "${ic_cfg}" | python3 -c '
import sys, json
try:
d = json.load(sys.stdin)
except Exception:
sys.exit()
ov = d.get("overrides") or {}; df = d.get("defaults") or {}
print(ov.get("prefix") or df.get("prefix") or "")
' 2>/dev/null)"
ns_cands=()
[[ -n "${ic_prefix}" ]] && ns_cands+=("${BASE}${PFX}/v1/${ic_prefix}/namespaces")
ns_cands+=("${BASE}${PFX}/v1/namespaces")
[[ -n "${WAREHOUSE}" ]] && ns_cands+=("${BASE}${PFX}/v1/${WAREHOUSE}/namespaces")
ns_cands+=("${BASE}${PFX}/v1/main/namespaces")
[[ -n "${WAREHOUSE}" ]] && ns_cands+=("${BASE}${PFX}/v1/main%7C${WAREHOUSE}/namespaces")
[[ -n "${WAREHOUSE}" ]] && ns_cands+=("${BASE}${PFX}/v1/%7C${WAREHOUSE}/namespaces")
ns_out=""; ns_used=""
for _u in "${ns_cands[@]}"; do
_j="$(curl -sf --max-time 10 "${_u}" 2>/dev/null || echo "")"
if [[ -n "${_j}" ]] && ns_out="$(ns_parse "${_j}")" && [[ -n "${ns_out}" ]]; then ns_used="${_u}"; break; fi
ns_out=""
done
if [[ -n "${ns_out}" ]]; then
ns_count="${ns_out%%|*}"; ns_list="${ns_out#*|}"
if [[ "${ns_count:-0}" -gt 0 ]]; then
pass "Catalog berisi ${ns_count} namespace — contoh (≤5): ${ns_list}"
else
warn "Iceberg REST aktif tetapi catalog MASIH KOSONG (0 namespace) — normal sebelum ingest"
fi
info "namespace via ${ns_used#${BASE}} (bucket='${BUCKET}' warehouse='${WAREHOUSE}' prefix='${PFX}')"
elif [[ -z "${ic_cfg}" ]]; then
warn "Tak bisa membaca namespace karena Iceberg REST tak tersedia [HTTP $(http_code "${BASE}${PFX}/v1/namespaces")]"
else
codes=""
for _u in "${ns_cands[@]}"; do codes+="${_u#${BASE}}→$(http_code "${_u}") "; done
fail "Iceberg REST aktif tetapi namespace TIDAK terbaca — kandidat path: ${codes% }"
info "Nessie umumnya menaruh namespace di path ber-PREFIX (warehouse/branch), mis. ${PFX}/v1/${WAREHOUSE}/namespaces"
fi
main_code="$(http_code "${BASE}/api/v2/trees/main")"
[[ "${main_code}" == "200" ]] && pass "Branch 'main' tersedia" \
|| warn "Branch 'main' tak merespon 200 [HTTP ${main_code} — $(code_hint "${main_code}")]"
refs_json="$(curl -sf --max-time 10 "${BASE}/api/v2/trees" 2>/dev/null || echo "")"
if [[ -n "${refs_json}" ]]; then
refs_list="$(printf '%s' "${refs_json}" | python3 -c '
import sys, json
try:
d = json.load(sys.stdin)
except Exception:
sys.exit(0)
rs = d.get("references") or d.get("trees") or []
out = []
for r in rs[:5]:
if isinstance(r, dict):
n, t = r.get("name"), r.get("type", "")
if n: out.append(f"{n}({t})" if t else n)
print(", ".join(out))
' 2>/dev/null)"
[[ -n "${refs_list}" ]] && info "refs (≤5): ${refs_list}"
fi
echo ""
# ─────────────────────────────────────────────────────────────────────────────
# D. Ringkasan
# ─────────────────────────────────────────────────────────────────────────────
echo -e "${BOLD}Ringkasan: ${PASS_N} PASS · ${FAIL_N} FAIL · ${WARN_N} WARN${NC}"
if [[ ${FAIL_N} -eq 0 ]]; then
echo -e "${GREEN}✓ Nessie siap (API native v2 + Iceberg REST).${NC}"
exit 0
fi
echo -e "${RED}✗ Ada ${FAIL_N} kegagalan — Nessie belum memenuhi kebutuhan SiCABE.${NC}"
exit 1