2026-10-09 02:35:14 +07:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# =============================================================================
|
|
|
|
|
# test-bootstrap-host.sh — penjagaan untuk `bootstrap.sh --host` (Fase 2b/2c)
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
# Latar: `generate_runtime_credentials()` menulis TIGA berkas ke `config/` BERSAMA.
|
|
|
|
|
# Dalam mode `--host`, endpoint-nya milik host itu, sehingga berkas bersama tertimpa
|
|
|
|
|
# alamat host lain — dan itu TIDAK terlihat di `git status` karena
|
|
|
|
|
# `config/airflow/sling/env.yaml` memang gitignored. Dampaknya: deploy single-host
|
|
|
|
|
# yang sedang berjalan menunjuk alamat yang tidak ada.
|
|
|
|
|
#
|
|
|
|
|
# Uji ini punya dua lapis:
|
|
|
|
|
# A. STATIS — membaca bootstrap.sh dan memastikan ketiga penulisan memakai
|
|
|
|
|
# `${cfg_base}` (bukan `${SCRIPT_DIR}/config` langsung). Tidak butuh
|
|
|
|
|
# apa pun, selalu jalan.
|
|
|
|
|
# B. DINAMIS — bila stack SiCABE sedang berjalan: hash ketiga berkas bersama,
|
|
|
|
|
# jalankan `bootstrap.sh --host state` (hanya step 2 = pastikan bucket,
|
|
|
|
|
# idempoten), lalu pastikan hash TIDAK berubah.
|
|
|
|
|
#
|
|
|
|
|
# Jalankan: ./scripts/test-bootstrap-host.sh
|
|
|
|
|
# =============================================================================
|
|
|
|
|
set -uo pipefail
|
|
|
|
|
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
|
DEPLOY_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
|
|
|
GRN=$'\e[32m'; RED=$'\e[31m'; YLW=$'\e[33m'; BLD=$'\e[1m'; NC=$'\e[0m'
|
|
|
|
|
PASS_N=0; FAIL_N=0
|
|
|
|
|
ok() { printf " ${GRN}✅${NC} %s\n" "$1"; PASS_N=$((PASS_N + 1)); }
|
|
|
|
|
bad() { printf " ${RED}❌${NC} %s\n" "$1"; FAIL_N=$((FAIL_N + 1)); }
|
|
|
|
|
inf() { printf " ${YLW}[..]${NC} %s\n" "$1"; }
|
|
|
|
|
|
|
|
|
|
SHARED_FILES=(
|
|
|
|
|
"config/airflow/sling/env.yaml"
|
|
|
|
|
"config/airflow/simple_auth_manager_passwords.json"
|
|
|
|
|
"config/trino/password.db"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
echo -e "${BLD}Uji bootstrap.sh --host${NC}"
|
|
|
|
|
echo
|
|
|
|
|
echo "── A. penjagaan statis (tanpa dependensi) ──"
|
|
|
|
|
|
|
|
|
|
# Ekstrak badan fungsi generate_runtime_credentials.
|
|
|
|
|
body="$(awk '/^generate_runtime_credentials\(\) \{/{f=1} f{print} f&&/^\}/{exit}' "${DEPLOY_DIR}/bootstrap.sh")"
|
|
|
|
|
if [[ -z "${body}" ]]; then
|
|
|
|
|
bad "fungsi generate_runtime_credentials() tidak ditemukan"
|
|
|
|
|
else
|
|
|
|
|
ok "fungsi generate_runtime_credentials() ditemukan"
|
|
|
|
|
|
|
|
|
|
for target in 'airflow/sling' 'airflow/simple_auth_manager_passwords.json' 'trino/password.db'; do
|
|
|
|
|
if grep -q 'local .*="\${cfg_base}/'"${target}"'"' <<<"${body}"; then
|
|
|
|
|
ok "penulisan ${target} memakai \${cfg_base}"
|
|
|
|
|
else
|
|
|
|
|
bad "penulisan ${target} TIDAK memakai \${cfg_base} (rawan menimpa config bersama)"
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
if grep -q 'HOST_NAME' <<<"${body}" && grep -q 'generated/host-' <<<"${body}"; then
|
|
|
|
|
ok "cfg_base dialihkan ke bundle host saat HOST_NAME diisi"
|
|
|
|
|
else
|
|
|
|
|
bad "cfg_base tidak dialihkan berdasarkan HOST_NAME"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Tidak boleh ada penulisan langsung ke ${SCRIPT_DIR}/config (selain default cfg_base).
|
|
|
|
|
if grep -nE '(cat >|: >|>>) *"\$\{SCRIPT_DIR\}/config' <<<"${body}"; then
|
|
|
|
|
bad "masih ada penulisan langsung ke \${SCRIPT_DIR}/config"
|
|
|
|
|
else
|
|
|
|
|
ok "tidak ada penulisan langsung ke \${SCRIPT_DIR}/config"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# step_tiers harus memetakan seluruh step 1..9. Bentuk pattern bisa berkelompok
|
|
|
|
|
# (mis. `1|3)`, `4|5)`, `7|8)`), jadi diambil hanya bagian sebelum ')' lalu dipecah
|
|
|
|
|
# pada '|' — regex "baris diawali angka" akan salah untuk bentuk berkelompok.
|
|
|
|
|
patterns="$(awk '/^step_tiers\(\) \{/{f=1} f{print} f&&/^\}/{exit}' "${DEPLOY_DIR}/bootstrap.sh" \
|
|
|
|
|
| grep -E '\)' | sed 's/).*//' | tr '|' '\n' | tr -d ' ')"
|
|
|
|
|
missing=""
|
|
|
|
|
for s in 1 2 3 4 5 6 7 8 9; do
|
|
|
|
|
grep -qx "${s}" <<<"${patterns}" || missing+=" ${s}"
|
|
|
|
|
done
|
|
|
|
|
if [[ -z "${missing}" ]]; then
|
|
|
|
|
ok "step_tiers() memetakan step 1..9"
|
|
|
|
|
else
|
|
|
|
|
bad "step_tiers() belum memetakan step:${missing}"
|
|
|
|
|
fi
|
|
|
|
|
|
2026-10-11 13:59:47 +07:00
|
|
|
# step_container() juga harus memetakan step 1..9 (dipakai mode --host untuk melewati
|
|
|
|
|
# step yang komponennya dikelola SISTEM LAIN).
|
|
|
|
|
sc_patterns="$(awk '/^step_container\(\) \{/{f=1} f{print} f&&/^\}/{exit}' "${DEPLOY_DIR}/bootstrap.sh" \
|
|
|
|
|
| grep -E '^[[:space:]]*[0-9]+\)' | sed 's/).*//' | tr -d ' ')"
|
|
|
|
|
sc_missing=""
|
|
|
|
|
for s in 1 2 3 4 5 6 7 8 9; do
|
|
|
|
|
grep -qx "${s}" <<<"${sc_patterns}" || sc_missing+=" ${s}"
|
|
|
|
|
done
|
|
|
|
|
if [[ -z "${sc_missing}" ]]; then
|
|
|
|
|
ok "step_container() memetakan step 1..9"
|
|
|
|
|
else
|
|
|
|
|
bad "step_container() belum memetakan step:${sc_missing}"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Mode --host HARUS melewati step milik sistem lain (bukan mencoba bootstrap-nya),
|
|
|
|
|
# kecuali step 2 (MinIO → mode verifikasi).
|
|
|
|
|
if grep -q 'sicabe_host_expects_container "${_step_ctr}"' "${DEPLOY_DIR}/bootstrap.sh" \
|
|
|
|
|
&& grep -q '"${step}" != "2"' "${DEPLOY_DIR}/bootstrap.sh"; then
|
|
|
|
|
ok "bootstrap --host melewati step komponen eksternal (step 2 dikecualikan)"
|
|
|
|
|
else
|
|
|
|
|
bad "bootstrap --host tidak melewati step komponen eksternal"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Bundle host harus di-set agar sicabe_host_expects_container membaca bundle yang benar.
|
|
|
|
|
if grep -q 'export SICABE_BUNDLE_DIR=' "${DEPLOY_DIR}/bootstrap.sh"; then
|
|
|
|
|
ok "SICABE_BUNDLE_DIR di-set pada mode --host"
|
|
|
|
|
else
|
|
|
|
|
bad "SICABE_BUNDLE_DIR tidak di-set (deteksi komponen per-host salah)"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Step 2 (MinIO): buat bucket hanya bila MinIO milik host ini; storage sistem lain → verifikasi.
|
|
|
|
|
if grep -q 'mc_mode="create"' "${DEPLOY_DIR}/bootstrap.sh" \
|
|
|
|
|
&& grep -q 'mc_mode="verify"' "${DEPLOY_DIR}/bootstrap.sh"; then
|
|
|
|
|
ok "bootstrap_minio: create (lokal) vs verify (sistem lain)"
|
|
|
|
|
else
|
|
|
|
|
bad "bootstrap_minio tidak membedakan create vs verify"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Sinkron redirectUris Keycloak harus memuat ORIGIN HOST client (bukan hanya localhost),
|
|
|
|
|
# kalau tidak login lewat alamat (mis. https://<ip>:12443) ditolak "invalid redirect_uri".
|
|
|
|
|
if grep -q 'CLIENT_ORIGIN' "${DEPLOY_DIR}/bootstrap.sh" \
|
|
|
|
|
&& grep -q '_client_origin' "${DEPLOY_DIR}/bootstrap.sh" \
|
|
|
|
|
&& grep -q 'host + "/\*"' "${DEPLOY_DIR}/bootstrap.sh"; then
|
|
|
|
|
ok "bootstrap: sinkron redirectUris memuat ORIGIN HOST client"
|
|
|
|
|
else
|
|
|
|
|
bad "bootstrap: sinkron redirectUris TIDAK memuat origin host (login via alamat ditolak)"
|
|
|
|
|
fi
|
|
|
|
|
|
2026-10-09 02:35:14 +07:00
|
|
|
echo
|
|
|
|
|
echo "── B. penjagaan dinamis (butuh stack SiCABE berjalan) ──"
|
|
|
|
|
if ! docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^sicabe-'; then
|
|
|
|
|
inf "tidak ada container sicabe- yang berjalan — lapis dinamis DILEWATI"
|
|
|
|
|
echo
|
|
|
|
|
printf " ${YLW}Ringkasan:${NC} %s PASS · %s FAIL (lapis dinamis dilewati)\n" "${PASS_N}" "${FAIL_N}"
|
|
|
|
|
[[ ${FAIL_N} -eq 0 ]] && exit 0 || exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
GEN_DIR="${DEPLOY_DIR}/generated"
|
|
|
|
|
created_gen=0
|
|
|
|
|
if [[ ! -f "${GEN_DIR}/host-state/.env" ]]; then
|
|
|
|
|
inf "bundle host belum ada — generate dulu (sementara, akan dihapus)"
|
|
|
|
|
if python3 "${DEPLOY_DIR}/lib/cluster/generate.py" render \
|
|
|
|
|
"${DEPLOY_DIR}/cluster.example.yml" "${DEPLOY_DIR}/docker-compose.yml" \
|
|
|
|
|
"${DEPLOY_DIR}/.env" "${GEN_DIR}" "${DEPLOY_DIR}/scripts/render-config.sh" \
|
|
|
|
|
>/dev/null 2>&1; then
|
|
|
|
|
created_gen=1
|
|
|
|
|
else
|
|
|
|
|
bad "gagal menyiapkan bundle host untuk uji dinamis"
|
|
|
|
|
GEN_DIR=""
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if [[ -n "${GEN_DIR}" ]]; then
|
|
|
|
|
declare -A before=()
|
|
|
|
|
for f in "${SHARED_FILES[@]}"; do
|
|
|
|
|
[[ -f "${DEPLOY_DIR}/${f}" ]] && before["${f}"]="$(sha256sum "${DEPLOY_DIR}/${f}" | cut -c1-16)"
|
|
|
|
|
done
|
|
|
|
|
inf "menjalankan: bootstrap.sh --host state (hanya step 2 — pastikan bucket)"
|
|
|
|
|
"${DEPLOY_DIR}/bootstrap.sh" --host state >/tmp/test-bootstrap-host.log 2>&1 || true
|
|
|
|
|
|
|
|
|
|
touched=0
|
|
|
|
|
for f in "${SHARED_FILES[@]}"; do
|
|
|
|
|
[[ -z "${before[${f}]:-}" ]] && continue
|
|
|
|
|
now="$(sha256sum "${DEPLOY_DIR}/${f}" | cut -c1-16)"
|
|
|
|
|
if [[ "${before[${f}]}" != "${now}" ]]; then
|
|
|
|
|
bad "${f} BERUBAH setelah --host state (bug config bersama kembali)"
|
|
|
|
|
touched=1
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
[[ ${touched} -eq 0 ]] && ok "ketiga berkas config bersama tidak berubah (sha256 identik)"
|
|
|
|
|
|
|
|
|
|
# Kredensial host harus mendarat di bundle host.
|
|
|
|
|
if [[ -f "${GEN_DIR}/host-state/config/airflow/sling/env.yaml" ]]; then
|
|
|
|
|
ok "kredensial runtime ditulis ke bundle host (generated/host-state/config/...)"
|
|
|
|
|
else
|
|
|
|
|
bad "kredensial runtime TIDAK ditemukan di bundle host"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
[[ ${created_gen} -eq 1 ]] && rm -rf "${GEN_DIR}" && inf "artefak uji generated/ dihapus"
|
|
|
|
|
rm -f /tmp/test-bootstrap-host.log
|
|
|
|
|
|
|
|
|
|
# ── install.sh --host: profil `edge` ikut saat bundle memuat traefik (T-20) ─────
|
|
|
|
|
# Traefik berada di profile `edge`. Bila run_host hanya memakai `--profile all`,
|
|
|
|
|
# Traefik tidak pernah start sehingga SELURUH rute publik tidak ada — padahal label
|
|
|
|
|
# sudah terpasang di setiap layanan dan sertifikat sudah bisa diterbitkan.
|
|
|
|
|
run_host_body="$(sed -n '/^run_host()/,/^}/p' "${DEPLOY_DIR}/install.sh")"
|
|
|
|
|
if grep -q 'traefik:' <<<"${run_host_body}" && grep -q 'profile edge' <<<"${run_host_body}"; then
|
|
|
|
|
ok "run_host menyertakan profile edge bila bundle memuat traefik"
|
|
|
|
|
else
|
|
|
|
|
bad "run_host tidak mendeteksi profile edge (Traefik tidak akan pernah start)"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ── install.sh: nama LOGIS harus diuraikan di EXTERNAL_SERVICES/LOCAL_SERVICES ──
|
|
|
|
|
# Tanpa ini, `starrocks` (dan dulu hanya `trino` yang istimewa) diam-diam tidak
|
|
|
|
|
# berefek: filter mencocokkan nama SERVICE (`starrocks-fe`, `starrocks-cn`).
|
|
|
|
|
if grep -q 'expand_service_names()' "${DEPLOY_DIR}/install.sh" \
|
|
|
|
|
&& grep -q 'starrocks-fe starrocks-cn' "${DEPLOY_DIR}/install.sh" \
|
|
|
|
|
&& grep -q 'expand_service_names ${LOCAL_SERVICES' "${DEPLOY_DIR}/install.sh" \
|
|
|
|
|
&& grep -q 'expand_service_names ${EXTERNAL_SERVICES' "${DEPLOY_DIR}/install.sh"; then
|
|
|
|
|
ok "nama logis diuraikan di EXTERNAL_SERVICES & LOCAL_SERVICES (starrocks/trino/…)"
|
|
|
|
|
else
|
|
|
|
|
bad "pemetaan nama logis hilang — setelan seperti LOCAL_SERVICES=starrocks akan diabaikan"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
echo
|
|
|
|
|
if [[ ${FAIL_N} -eq 0 ]]; then
|
|
|
|
|
echo -e " ${GRN}${BLD}SEMUA LULUS${NC} (${PASS_N} cek)"
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
echo -e " ${RED}${BLD}GAGAL${NC}: ${FAIL_N} masalah · ${PASS_N} lulus"
|
|
|
|
|
exit 1
|