196 lines
8.1 KiB
Bash
Executable File
196 lines
8.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# render-config.sh — render config/** untuk deploy multi-host (Fase 0/1, R10 §4.9)
|
|
#
|
|
# MASALAH: file config/** yang dibaca CONTAINER (.properties/.yaml/.cfg/.json/rego)
|
|
# TIDAK dapat melakukan ekspansi env saat runtime, berbeda dari docker-compose.yml.
|
|
# Pada deploy multi-host, hostname nama container (mis. `nessie:19120`) harus
|
|
# menjadi alamat host sebenarnya (mis. `10.20.0.10:19120`).
|
|
#
|
|
# SOLUSI (Opsi (i) R10 §4.9): `config/` dibiarkan LITERAL (single-host tidak
|
|
# tersentuh); script ini menyalinnya ke direktori output sambil mengganti
|
|
# hostname TERDAFTAR, menghasilkan `generated/host-<x>/config/` (gitignored).
|
|
#
|
|
# PRINSIP KEAMANAN
|
|
# 1. Hanya hostname yang di-`--map` yang diganti → tidak ada kejutan.
|
|
# 2. Penggantian ANCHORED: `host` diganti hanya bila diikuti `:` atau `/`
|
|
# (posisi target jaringan). Karena itu `KC_DB: postgres` (dialek DB),
|
|
# `DB_SCHEME: postgresql`, dan `postgres:15` (tag image di compose) TIDAK
|
|
# ikut berubah.
|
|
# 3. `config/` sumber tidak pernah ditulis — hanya dibaca.
|
|
# 4. Baris KOMENTAR (`#`) tidak ditulis ulang: komentar sering menyebut nilai
|
|
# default lokal, dan menimpanya akan membuat komentar itu justru salah.
|
|
#
|
|
# PEMAKAIAN
|
|
# # Uji tanpa menulis (perlihatkan rencana):
|
|
# ./scripts/render-config.sh --out /tmp/render --dry-run \
|
|
# --map postgres=10.20.0.12 --map nessie=10.20.0.10
|
|
#
|
|
# # Render sungguhan:
|
|
# ./scripts/render-config.sh --out generated/host-compute/config \
|
|
# --map postgres=10.20.0.12 --map minio=10.20.0.12
|
|
#
|
|
# # Fase 1 akan menambah: --inventory cluster.yml --host <nama>
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
DEPLOY_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
|
|
SRC_CONFIG="${DEPLOY_DIR}/config"
|
|
OUT_DIR=""
|
|
DRY_RUN=0
|
|
declare -A MAP=()
|
|
|
|
# Hostname yang SAH dipetakan ke alamat host. Daftar sengaja tetap (bukan bebas)
|
|
# supaya salah ketik --map tidak diam-diam menulis ulang file config.
|
|
KNOWN_HOSTS=(
|
|
postgres minio nessie keycloak openldap opa
|
|
trino trino-coordinator trino-worker
|
|
starrocks-fe starrocks-cn airflow-webserver
|
|
openmetadata-server openmetadata-opensearch dremio grafana prometheus
|
|
# Sidecar observabilitas (M2/M3): target di config/prometheus dipetakan ke alamat
|
|
# HOST-nya saat multi-host. WAJIB ada di sini — generate.py meng-`--map` setiap
|
|
# RENDER_HOSTS, dan hostname yang tak terdaftar membuat render-config menolak,
|
|
# sehingga `install.sh --generate` GAGAL TOTAL (bug M2/M3 yang diperbaiki di sini).
|
|
statsd-exporter trino-jmx-exporter
|
|
)
|
|
|
|
# Tidak di-render: skrip yang sanggup ekspansi env sendiri + dokumentasi.
|
|
# environment.defaults.sh — di-source shell, memakai ${VAR:-...}
|
|
# buckets.sh — skrip mc, memakai ${MINIO_ENDPOINT:-...}
|
|
SKIP_FILES=(environment.defaults.sh buckets.sh)
|
|
SKIP_PATTERNS=("*.md")
|
|
|
|
RED=$'\e[31m'; GRN=$'\e[32m'; YLW=$'\e[33m'; BLD=$'\e[1m'; NC=$'\e[0m'
|
|
ok() { echo -e " ${GRN}[OK]${NC} $*"; }
|
|
inf() { echo -e " ${YLW}[..]${NC} $*"; }
|
|
err() { echo -e " ${RED}[!!]${NC} $*" >&2; }
|
|
|
|
usage() {
|
|
sed -n '2,30p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
|
echo
|
|
echo "Hostname yang dikenal: ${KNOWN_HOSTS[*]}"
|
|
}
|
|
|
|
is_known_host() {
|
|
local h="$1" k
|
|
for k in "${KNOWN_HOSTS[@]}"; do [[ "${k}" == "${h}" ]] && return 0; done
|
|
return 1
|
|
}
|
|
|
|
add_map() {
|
|
local pair="$1" host addr
|
|
if [[ "${pair}" != *"="* ]]; then
|
|
err "--map harus berformat host=alamat (diberikan: '${pair}')"; exit 2
|
|
fi
|
|
host="${pair%%=*}"; addr="${pair#*=}"
|
|
[[ -n "${host}" && -n "${addr}" ]] || { err "--map kosong: '${pair}'"; exit 2; }
|
|
is_known_host "${host}" || { err "hostname '${host}' tidak dikenal (lihat --help)"; exit 2; }
|
|
MAP["${host}"]="${addr}"
|
|
}
|
|
|
|
parse_args() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--out) OUT_DIR="${2:-}"; shift 2 ;;
|
|
--map) add_map "${2:-}"; shift 2 ;;
|
|
--dry-run) DRY_RUN=1; shift ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) err "Argumen tidak dikenal: $1"; usage; exit 2 ;;
|
|
esac
|
|
done
|
|
[[ -n "${OUT_DIR}" ]] || { err "--out wajib diisi"; exit 2; }
|
|
[[ ${#MAP[@]} -gt 0 ]] || { err "minimal satu --map host=alamat wajib diisi"; exit 2; }
|
|
}
|
|
|
|
# Satu pass perl untuk semua host. Anchor diperketat ke TIGA konteks jaringan —
|
|
# tanpa ini, path filesystem seperti `/etc/trino/` atau `/data/trino/` ikut
|
|
# terkorupsi (hostname `trino` diikuti `/`):
|
|
# 1. setelah skema URL `//` → http://nessie:19120, jdbc:postgresql://postgres:5432
|
|
# 2. setelah `@` (JDBC) → user:pw@postgres:5432
|
|
# 3. bare `host:port` → nessie:19120, starrocks-fe:8030 (bukan bagian token lain)
|
|
# Baris komentar (`#`) tidak ditulis ulang.
|
|
render_file() {
|
|
local src="$1" dst="$2"
|
|
local -a pairs=()
|
|
local k
|
|
for k in "${!MAP[@]}"; do pairs+=("${k}=${MAP[$k]}"); done
|
|
perl -e '
|
|
my ($src, $dst, @pairs) = @ARGV;
|
|
my %map = map { split(/=/, $_, 2) } @pairs;
|
|
my $alt = join "|", sort { length($b) <=> length($a) } map { quotemeta } keys %map;
|
|
open(my $in, "<", $src) or die "buka gagal: $src\n";
|
|
open(my $out, ">", $dst) or die "tulis gagal: $dst\n";
|
|
while (my $l = <$in>) {
|
|
unless ($l =~ /^\s*#/) {
|
|
$l =~ s{(?<=//)(${alt})(?=[:/])}{$map{$1}}g; # 1. setelah skema //
|
|
$l =~ s{(?<=@)(${alt})(?=[:/])}{$map{$1}}g; # 2. setelah @ (JDBC)
|
|
$l =~ s{(?<![\w/.:-])(${alt})(?=:\d)}{$map{$1}}g; # 3. bare host:port
|
|
# 4. nilai BARE untuk key yang jelas host (host, hostname, *_HOST).
|
|
# `host: postgres` (Sling) tidak punya ':' atau '/', sehingga aturan 1-3
|
|
# tidak mengenainya -> di host lain Sling mencari nama container yang tidak
|
|
# ada (kelas bug yang sama dengan DB_HOST OpenMetadata). Hanya key ber-'host'
|
|
# yang disentuh, supaya `type: postgres` / `KC_DB: postgres` (dialek) aman.
|
|
$l =~ s{^(\s*(?:-\s*)?[A-Za-z_.\-]*(?:host|hostname)[A-Za-z_.\-]*\s*[:=]\s*)(${alt})(\s*)$}{$1 . $map{$2} . $3}gie;
|
|
}
|
|
print $out $l;
|
|
}
|
|
close $in; close $out;
|
|
' "${src}" "${dst}" "${pairs[@]}"
|
|
}
|
|
|
|
# Jumlah baris sumber yang berubah di file tujuan.
|
|
count_subs() {
|
|
local src="$1" dst="$2"
|
|
diff "${src}" "${dst}" 2>/dev/null | grep -c '^<' || true
|
|
}
|
|
|
|
should_skip() {
|
|
local name="$1" pat skip
|
|
for skip in "${SKIP_FILES[@]}"; do [[ "${name}" == "${skip}" ]] && return 0; done
|
|
for pat in "${SKIP_PATTERNS[@]}"; do
|
|
# shellcheck disable=SC2053
|
|
[[ "${name}" == ${pat} ]] && return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
# Render satu file; cetak jumlah baris berubah bila ada.
|
|
process_file() {
|
|
local src="$1" rel="${1#${SRC_CONFIG}/}" dst="$2" n tmp
|
|
if [[ ${DRY_RUN} -eq 1 ]]; then
|
|
tmp="$(mktemp)"; render_file "${src}" "${tmp}"; n="$(count_subs "${src}" "${tmp}")"; rm -f "${tmp}"
|
|
else
|
|
dst="${OUT_DIR}/${rel}"
|
|
mkdir -p "$(dirname "${dst}")"
|
|
render_file "${src}" "${dst}"
|
|
n="$(count_subs "${src}" "${dst}")"
|
|
fi
|
|
CHANGED_TOTAL=$((CHANGED_TOTAL + n))
|
|
if [[ "${n}" -gt 0 ]]; then ok "${rel} — ${n} baris"; FILES_CHANGED=$((FILES_CHANGED + 1)); fi
|
|
}
|
|
|
|
main() {
|
|
parse_args "$@"
|
|
echo -e "${BLD}Render config/** untuk multi-host${NC}"
|
|
echo " sumber : ${SRC_CONFIG}"
|
|
echo " tujuan : ${OUT_DIR}$([[ ${DRY_RUN} -eq 1 ]] && echo ' (DRY-RUN)')"
|
|
echo -n " peta : "; for k in "${!MAP[@]}"; do printf '%s→%s ' "${k}" "${MAP[$k]}"; done; echo; echo
|
|
[[ ${DRY_RUN} -eq 0 ]] && mkdir -p "${OUT_DIR}"
|
|
|
|
FILES_CHANGED=0; CHANGED_TOTAL=0
|
|
local src scanned=0 skipped=0
|
|
while IFS= read -r -d '' src; do
|
|
scanned=$((scanned + 1))
|
|
if should_skip "$(basename "${src}")"; then skipped=$((skipped + 1)); continue; fi
|
|
process_file "${src}" ""
|
|
done < <(find "${SRC_CONFIG}" -type f -print0 | sort -z)
|
|
|
|
echo
|
|
inf "dipindai ${scanned} file · ${FILES_CHANGED} berubah (${CHANGED_TOTAL} baris) · ${skipped} dilewati"
|
|
echo -e " ${GRN}${BLD}Selesai.${NC} config/ sumber tidak tersentuh."
|
|
}
|
|
|
|
main "$@"
|