Files

672 lines
27 KiB
Bash
Executable File

#!/usr/bin/env bash
set -Eeuo pipefail
readonly EXPECTED_KUSTOMIZE_VERSION="v5.8.1"
readonly EXPECTED_HELM_VERSION="v3.19.4"
readonly TARGET_KUBERNETES_VERSION="1.36.2"
readonly CNPG_CHART_NAME="cloudnative-pg"
readonly CNPG_CHART_VERSION="0.29.0"
readonly CNPG_CHART_REPOSITORY="https://cloudnative-pg.github.io/charts"
readonly EXPECTED_CNPG_CHART_SHA256="668e065ff53508d58238788fd35b355a925060843629a951df0e6a9362e6d32f"
readonly GITEA_CHART_NAME="gitea"
readonly GITEA_CHART_VERSION="12.7.0"
readonly GITEA_CHART_REPOSITORY="https://dl.gitea.com/charts/"
readonly EXPECTED_GITEA_CHART_SHA256="5881ef9c59400bee2d5547e77c4cd0efb925143c2f5d93fb4f38446db76b0167"
readonly REPOSITORY_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd -P)"
readonly -a RENDERED_MANIFEST_NAMES=(
namespaces
ssd-local-pv
cnpg-operator
platform-postgres
gitea
gitea-oidc
)
fail() {
printf 'ERROR: %s\n' "$*" >&2
exit 1
}
assert_exact_source_text() {
local file="$1"
local expected_text="$2"
local description="$3"
local count
count="$(rg --count-matches --fixed-strings -- "$expected_text" "$file" || true)"
[[ "$count" == "1" ]] || \
fail "${description} must appear exactly once in ${file#${REPOSITORY_ROOT}/}"
}
usage() {
cat <<'USAGE'
Usage:
bash scripts/validate/render-phase1.sh
bash scripts/validate/render-phase1.sh \
--verified-output-dir /tmp/platform-phase1-apply.XXXXXX
The output option is an internal handoff used only by the Phase 1 apply
script. The destination must be an existing, empty, non-symlink directory
created directly below /tmp with the platform-phase1-apply.* prefix.
USAGE
}
verified_output_dir=""
case "$#" in
0)
;;
2)
[[ "$1" == "--verified-output-dir" ]] || {
usage >&2
exit 2
}
verified_output_dir="$2"
;;
*)
usage >&2
exit 2
;;
esac
if [[ -n "$verified_output_dir" ]]; then
[[ "$verified_output_dir" == /tmp/platform-phase1-apply.* ]] || \
fail "verified output directory must match /tmp/platform-phase1-apply.*"
[[ -d "$verified_output_dir" && ! -L "$verified_output_dir" ]] || \
fail "verified output directory must be an existing non-symlink directory"
[[ "$(cd -- "$verified_output_dir" && pwd -P)" == "$verified_output_dir" ]] || \
fail "verified output directory must be an absolute canonical path"
[[ -O "$verified_output_dir" ]] || \
fail "verified output directory must be owned by the current user"
[[ "$(stat --format='%a' -- "$verified_output_dir")" == "700" ]] || \
fail "verified output directory must have mode 0700"
[[ -z "$(find "$verified_output_dir" -mindepth 1 -maxdepth 1 -print -quit)" ]] || \
fail "verified output directory must be empty"
fi
command -v kubectl >/dev/null 2>&1 || fail "kubectl is required"
command -v jq >/dev/null 2>&1 || fail "jq is required"
command -v rg >/dev/null 2>&1 || fail "ripgrep (rg) is required"
command -v sha256sum >/dev/null 2>&1 || fail "sha256sum is required"
command -v tar >/dev/null 2>&1 || fail "tar is required"
command -v cmp >/dev/null 2>&1 || fail "cmp is required"
command -v find >/dev/null 2>&1 || fail "find is required"
command -v install >/dev/null 2>&1 || fail "install is required"
command -v stat >/dev/null 2>&1 || fail "stat is required"
if [[ -n "${PLATFORM_HELM_BIN:-}" ]]; then
[[ "$PLATFORM_HELM_BIN" == /* ]] || \
fail "PLATFORM_HELM_BIN must be an absolute path"
[[ -f "$PLATFORM_HELM_BIN" && -x "$PLATFORM_HELM_BIN" ]] || \
fail "PLATFORM_HELM_BIN is not an executable file: ${PLATFORM_HELM_BIN}"
readonly HELM_BIN="$PLATFORM_HELM_BIN"
else
HELM_BIN="$(command -v helm 2>/dev/null)" || \
fail "Helm ${EXPECTED_HELM_VERSION} is required"
readonly HELM_BIN
fi
kustomize_version="$(kubectl version --client --output=yaml | sed -n 's/^kustomizeVersion: //p')"
helm_version="$("$HELM_BIN" version --template '{{.Version}}')"
[[ "$kustomize_version" == "$EXPECTED_KUSTOMIZE_VERSION" ]] || \
fail "expected Kustomize ${EXPECTED_KUSTOMIZE_VERSION}, found ${kustomize_version:-unknown}"
[[ "$helm_version" == "$EXPECTED_HELM_VERSION" ]] || \
fail "expected Helm ${EXPECTED_HELM_VERSION}, found ${helm_version:-unknown}"
render_dir="$(mktemp -d "${TMPDIR:-/tmp}/platform-phase1-render.XXXXXX")"
declare -a generated_chart_cache_dirs=()
declare -a generated_chart_cache_parent_dirs=()
cleanup() {
local cache_dir
local parent_dir
for cache_dir in "${generated_chart_cache_dirs[@]}"; do
case "$cache_dir" in
"${REPOSITORY_ROOT}/infrastructure/controllers/cloudnative-pg/.helm/charts/cloudnative-pg-0.29.0"|\
"${REPOSITORY_ROOT}/services/gitea/profiles/oidc/.helm/charts/gitea-12.7.0")
rm -rf -- "$cache_dir"
;;
*)
printf 'WARNING: refusing to remove unexpected chart cache: %s\n' \
"$cache_dir" >&2
;;
esac
done
for parent_dir in "${generated_chart_cache_parent_dirs[@]}"; do
case "$parent_dir" in
"${REPOSITORY_ROOT}/infrastructure/controllers/cloudnative-pg/.helm/charts"|\
"${REPOSITORY_ROOT}/infrastructure/controllers/cloudnative-pg/.helm"|\
"${REPOSITORY_ROOT}/services/gitea/profiles/oidc/.helm/charts"|\
"${REPOSITORY_ROOT}/services/gitea/profiles/oidc/.helm")
rmdir -- "$parent_dir" 2>/dev/null || true
;;
*)
printf 'WARNING: refusing to remove unexpected chart cache parent: %s\n' \
"$parent_dir" >&2
;;
esac
done
case "$render_dir" in
/tmp/platform-phase1-render.*|"${TMPDIR:-/tmp}"/platform-phase1-render.*)
rm -rf -- "$render_dir"
;;
*)
printf 'WARNING: refusing to remove unexpected render directory: %s\n' "$render_dir" >&2
;;
esac
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
render_plain() {
local label="$1"
local relative_path="$2"
local output="${render_dir}/${label}.yaml"
kubectl kustomize "${REPOSITORY_ROOT}/${relative_path}" >"$output"
[[ -s "$output" ]] || fail "${label} rendered an empty manifest"
printf 'Rendered %-20s %8s bytes\n' "$label" "$(wc -c <"$output" | tr -d '[:space:]')"
}
render_helm() {
local label="$1"
local relative_path="$2"
local output="${render_dir}/${label}.yaml"
kubectl kustomize \
--enable-helm \
--helm-command "$HELM_BIN" \
--helm-kube-version "$TARGET_KUBERNETES_VERSION" \
"${REPOSITORY_ROOT}/${relative_path}" >"$output"
[[ -s "$output" ]] || fail "${label} rendered an empty manifest"
printf 'Rendered %-20s %8s bytes\n' "$label" "$(wc -c <"$output" | tr -d '[:space:]')"
}
extract_rendered_document() {
local manifest="$1"
local wanted_kind="$2"
local wanted_name="$3"
local output="$4"
awk \
-v wanted_kind="$wanted_kind" \
-v wanted_name="$wanted_name" \
'
function reset_document() {
document = ""
document_kind = ""
document_name = ""
in_metadata = 0
}
function flush_document() {
if (document_kind == wanted_kind && document_name == wanted_name) {
matches++
printf "%s", document
}
}
BEGIN {
reset_document()
}
/^---[[:space:]]*$/ {
flush_document()
reset_document()
next
}
{
document = document $0 ORS
if ($0 ~ /^kind:[[:space:]]*/) {
document_kind = $0
sub(/^kind:[[:space:]]*/, "", document_kind)
sub(/[[:space:]]*$/, "", document_kind)
}
if ($0 == "metadata:") {
in_metadata = 1
next
}
if (in_metadata && $0 ~ /^ name:[[:space:]]*/) {
document_name = $0
sub(/^ name:[[:space:]]*/, "", document_name)
sub(/[[:space:]]*$/, "", document_name)
in_metadata = 0
} else if (in_metadata && $0 ~ /^[^[:space:]]/) {
in_metadata = 0
}
}
END {
flush_document()
if (matches != 1) {
exit 42
}
}
' \
"$manifest" >"$output"
}
prepare_verified_chart_cache() {
local label="$1"
local chart_name="$2"
local chart_repository="$3"
local chart_version="$4"
local build_root_relative_path="$5"
local expected_digest="$6"
local package_path="${render_dir}/${chart_name}-${chart_version}.tgz"
local cache_version_dir="${REPOSITORY_ROOT}/${build_root_relative_path}/.helm/charts/${chart_name}-${chart_version}"
local checksum_output
local actual_digest
local chart_cache_dir="${cache_version_dir%/*}"
local helm_cache_dir="${chart_cache_dir%/*}"
local cache_parent
if [[ ! -f "$package_path" ]]; then
"$HELM_BIN" pull "$chart_name" \
--repo "$chart_repository" \
--version "$chart_version" \
--destination "$render_dir"
fi
[[ -f "$package_path" ]] || fail "${label} chart package was not downloaded: ${package_path}"
checksum_output="$(sha256sum -- "$package_path")"
actual_digest="${checksum_output%% *}"
[[ "$actual_digest" == "$expected_digest" ]] || \
fail "${label} chart package SHA-256 mismatch: expected ${expected_digest}, found ${actual_digest}"
case "$cache_version_dir" in
"${REPOSITORY_ROOT}/infrastructure/controllers/cloudnative-pg/.helm/charts/cloudnative-pg-0.29.0"|\
"${REPOSITORY_ROOT}/services/gitea/profiles/oidc/.helm/charts/gitea-12.7.0")
;;
*)
fail "refusing to create unexpected chart cache directory: ${cache_version_dir}"
;;
esac
for cache_parent in "$helm_cache_dir" "$chart_cache_dir"; do
[[ ! -L "$cache_parent" ]] || \
fail "refusing symlinked chart cache parent: ${cache_parent}"
[[ ! -e "$cache_parent" || -d "$cache_parent" ]] || \
fail "chart cache parent is not a directory: ${cache_parent}"
done
[[ ! -e "$cache_version_dir" && ! -L "$cache_version_dir" ]] || \
fail "generated chart cache already exists; remove it only after confirming it is disposable: ${cache_version_dir}"
[[ -e "$chart_cache_dir" ]] || generated_chart_cache_parent_dirs+=("$chart_cache_dir")
[[ -e "$helm_cache_dir" ]] || generated_chart_cache_parent_dirs+=("$helm_cache_dir")
mkdir -p -- "$cache_version_dir"
generated_chart_cache_dirs+=("$cache_version_dir")
tar -xzf "$package_path" -C "$cache_version_dir"
[[ -f "${cache_version_dir}/${chart_name}/Chart.yaml" ]] || \
fail "${label} extracted chart is missing Chart.yaml"
printf 'Verified %-20s SHA-256 %s\n' "$label" "$actual_digest"
}
cd -- "$REPOSITORY_ROOT"
if rg --line-number --glob '*.yaml' --glob '*.yml' \
--glob '!**/.helm/**' --glob '!**/charts/**' \
'^[[:space:]]*kind:[[:space:]]*Secret[[:space:]]*$' \
infrastructure services bootstrap clusters components; then
fail "a source-controlled Kubernetes Secret manifest was found"
fi
if rg --line-number --glob 'kustomization.yaml' 'LoadRestrictionsNone|load-restrictor' .; then
fail "the repository must keep Kustomize LoadRestrictionsRootOnly"
fi
readonly GITEA_BASELINE_KUSTOMIZATION="${REPOSITORY_ROOT}/services/gitea/kustomization.yaml"
readonly GITEA_OIDC_KUSTOMIZATION="${REPOSITORY_ROOT}/services/gitea/profiles/oidc/kustomization.yaml"
for gitea_kustomization in \
"$GITEA_BASELINE_KUSTOMIZATION" \
"$GITEA_OIDC_KUSTOMIZATION"; do
assert_exact_source_text "$gitea_kustomization" \
'repo: https://dl.gitea.com/charts/' \
"the pinned Gitea Chart repository"
assert_exact_source_text "$gitea_kustomization" \
'version: 12.7.0' \
"the pinned Gitea Chart version"
assert_exact_source_text "$gitea_kustomization" \
'includeCRDs: false' \
"the Gitea includeCRDs policy"
assert_exact_source_text "$gitea_kustomization" \
'skipTests: true' \
"the Gitea Helm test policy"
done
assert_exact_source_text "$GITEA_BASELINE_KUSTOMIZATION" \
'chartHome: profiles/oidc/.helm/charts' \
"the baseline Gitea verified Chart cache"
assert_exact_source_text "$GITEA_BASELINE_KUSTOMIZATION" \
'valuesFile: profiles/oidc/values/baseline.yaml' \
"the baseline Gitea values path"
assert_exact_source_text "$GITEA_OIDC_KUSTOMIZATION" \
'chartHome: .helm/charts' \
"the OIDC Gitea verified Chart cache"
assert_exact_source_text "$GITEA_OIDC_KUSTOMIZATION" \
'valuesFile: values/baseline.yaml' \
"the OIDC Gitea baseline values path"
assert_exact_source_text "$GITEA_OIDC_KUSTOMIZATION" \
'additionalValuesFiles:' \
"the OIDC Gitea values merge"
assert_exact_source_text "$GITEA_OIDC_KUSTOMIZATION" \
'values/oidc.yaml' \
"the OIDC Gitea override path"
render_plain namespaces infrastructure/namespaces/overlays/home
render_plain ssd-local-pv infrastructure/storage/ssd-local-pv
prepare_verified_chart_cache \
cloudnative-pg-chart \
"$CNPG_CHART_NAME" \
"$CNPG_CHART_REPOSITORY" \
"$CNPG_CHART_VERSION" \
infrastructure/controllers/cloudnative-pg \
"$EXPECTED_CNPG_CHART_SHA256"
render_helm cnpg-operator infrastructure/controllers/cloudnative-pg
render_plain platform-postgres services/platform-postgres
prepare_verified_chart_cache \
gitea-chart \
"$GITEA_CHART_NAME" \
"$GITEA_CHART_REPOSITORY" \
"$GITEA_CHART_VERSION" \
services/gitea/profiles/oidc \
"$EXPECTED_GITEA_CHART_SHA256"
render_helm gitea services/gitea
render_helm gitea-oidc services/gitea/profiles/oidc
[[ "$(awk '$0 == "kind: Cluster" { count++ } END { print count + 0 }' "${render_dir}/platform-postgres.yaml")" == "1" ]] || \
fail "the Phase 1 PostgreSQL root must contain exactly one Cluster"
[[ "$(awk '$0 == "kind: DatabaseRole" { count++ } END { print count + 0 }' "${render_dir}/platform-postgres.yaml")" == "1" ]] || \
fail "the Phase 1 PostgreSQL root must contain exactly one Gitea DatabaseRole"
[[ "$(awk '$0 == "kind: Database" { count++ } END { print count + 0 }' "${render_dir}/platform-postgres.yaml")" == "1" ]] || \
fail "the Phase 1 PostgreSQL root must contain exactly one Gitea Database"
[[ "$(awk '$0 == "kind: NetworkPolicy" { count++ } END { print count + 0 }' "${render_dir}/platform-postgres.yaml")" == "1" ]] || \
fail "the Phase 1 PostgreSQL root must contain exactly one base NetworkPolicy"
[[ "$(rg --count -- '^[[:space:]]*name:[[:space:]]platform-postgres-gitea[[:space:]]*$' "${render_dir}/platform-postgres.yaml" || true)" == "2" ]] || \
fail "the Phase 1 PostgreSQL root must contain the Gitea DatabaseRole and Database"
if rg --quiet '^[[:space:]]*name:[[:space:]]platform-postgres-keycloak[[:space:]]*$' "${render_dir}/platform-postgres.yaml"; then
fail "the Phase 1 PostgreSQL root must not contain a Keycloak DatabaseRole or Database"
fi
[[ "$(rg --count -- '^[[:space:]]*-[[:space:]]host gitea gitea all scram-sha-256[[:space:]]*$' "${render_dir}/platform-postgres.yaml" || true)" == "1" ]] || \
fail "the Gitea role must authenticate only to the Gitea database"
[[ "$(rg --count -- '^[[:space:]]*-[[:space:]]host all gitea all reject[[:space:]]*$' "${render_dir}/platform-postgres.yaml" || true)" == "1" ]] || \
fail "the Gitea role must be rejected from every other database"
[[ "$(rg --count -- '^[[:space:]]*-[[:space:]]host keycloak keycloak all scram-sha-256[[:space:]]*$' "${render_dir}/platform-postgres.yaml" || true)" == "1" ]] || \
fail "the Keycloak role must authenticate only to the Keycloak database"
[[ "$(rg --count -- '^[[:space:]]*-[[:space:]]host all keycloak all reject[[:space:]]*$' "${render_dir}/platform-postgres.yaml" || true)" == "1" ]] || \
fail "the Keycloak role must be rejected from every other database"
rg --quiet '^kind: CustomResourceDefinition$' "${render_dir}/cnpg-operator.yaml" || \
fail "CloudNativePG CRDs are missing from the operator render"
rg --quiet '^kind: Cluster$' "${render_dir}/platform-postgres.yaml" || \
fail "the platform PostgreSQL Cluster is missing"
rg --quiet '^kind: DatabaseRole$' "${render_dir}/platform-postgres.yaml" || \
fail "the Gitea DatabaseRole is missing"
rg --quiet '^kind: Database$' "${render_dir}/platform-postgres.yaml" || \
fail "the Gitea Database is missing"
for gitea_profile in gitea gitea-oidc; do
gitea_profile_manifest="${render_dir}/${gitea_profile}.yaml"
gitea_profile_deployment_document="${render_dir}/${gitea_profile}-deployment.yaml"
gitea_profile_ingress_document="${render_dir}/${gitea_profile}-ingress.yaml"
gitea_profile_servicemonitor_document="${render_dir}/${gitea_profile}-servicemonitor.yaml"
rg --quiet '^kind: PersistentVolumeClaim$' "$gitea_profile_manifest" || \
fail "${gitea_profile} is missing the Gitea PVC"
rg --quiet '^kind: Ingress$' "$gitea_profile_manifest" || \
fail "${gitea_profile} is missing the Gitea Ingress"
rg --quiet \
'^[[:space:]]*-[[:space:]]*host:[[:space:]]*git\.learn\.hyeonworks\.com[[:space:]]*$' \
"$gitea_profile_manifest" || \
fail "${gitea_profile} Ingress host is not git.learn.hyeonworks.com"
[[ "$(rg --count-matches \
'^[[:space:]]*ROOT_URL=https://git\.learn\.hyeonworks\.com/[[:space:]]*$' \
"$gitea_profile_manifest" || true)" == "1" ]] || \
fail "${gitea_profile} must render the external HTTPS ROOT_URL exactly once"
extract_rendered_document \
"$gitea_profile_manifest" \
Deployment \
gitea \
"$gitea_profile_deployment_document" || \
fail "${gitea_profile} must contain exactly one gitea Deployment"
extract_rendered_document \
"$gitea_profile_manifest" \
Ingress \
gitea-http \
"$gitea_profile_ingress_document" || \
fail "${gitea_profile} must contain exactly one gitea-http Ingress"
extract_rendered_document \
"$gitea_profile_manifest" \
ServiceMonitor \
gitea \
"$gitea_profile_servicemonitor_document" || \
fail "${gitea_profile} must contain exactly one gitea ServiceMonitor"
if rg --quiet '^[[:space:]]{2}tls:[[:space:]]*' "$gitea_profile_ingress_document"; then
fail "${gitea_profile} must not render an in-cluster TLS section"
fi
if ! kubectl create --dry-run=client \
-f "$gitea_profile_servicemonitor_document" \
-o json | jq -e '
.apiVersion == "monitoring.coreos.com/v1" and
.kind == "ServiceMonitor" and
.metadata.name == "gitea" and
.metadata.namespace == "gitea" and
.metadata.labels["observability.hyeonworks.com/instance"] == "home" and
.spec.jobLabel == "app.kubernetes.io/name" and
.spec.selector.matchLabels == {
"app.kubernetes.io/instance": "gitea",
"app.kubernetes.io/name": "gitea"
} and
.spec.endpoints == [{
"interval": "30s",
"port": "http",
"scrapeTimeout": "10s"
}]
' >/dev/null; then
fail "${gitea_profile} ServiceMonitor contract is not exact"
fi
for restricted_setting in \
'allowPrivilegeEscalation: false' \
'runAsNonRoot: true' \
'type: RuntimeDefault' \
'- ALL'; do
[[ "$(rg --count-matches --fixed-strings -- "$restricted_setting" \
"$gitea_profile_deployment_document" || true)" == "4" ]] || \
fail "${gitea_profile} must apply ${restricted_setting} to all four containers"
done
done
for forbidden_baseline_marker in \
'gitea-keycloak-oidc' \
'id.learn.hyeonworks.com' \
'gitea-allow-host-nginx-keycloak' \
'gitea-branding-assets' \
'gitea-branding-templates' \
'ALLOW_ONLY_EXTERNAL_REGISTRATION=true'; do
if rg --quiet --fixed-strings -- "$forbidden_baseline_marker" "${render_dir}/gitea.yaml"; then
fail "the baseline Gitea render contains OIDC-only marker ${forbidden_baseline_marker}"
fi
done
for baseline_setting in \
'DISABLE_REGISTRATION=true' \
'ALLOW_ONLY_EXTERNAL_REGISTRATION=false' \
'SHOW_REGISTRATION_BUTTON=false' \
'ENABLE_PASSWORD_SIGNIN_FORM=true'; do
[[ "$(rg --count-matches --fixed-strings -- "$baseline_setting" \
"${render_dir}/gitea.yaml" || true)" == "1" ]] || \
fail "the baseline Gitea render must contain exactly one ${baseline_setting} setting"
done
gitea_deployment_document="${render_dir}/gitea-oidc-deployment.yaml"
gitea_keycloak_policy_document="${render_dir}/gitea-keycloak-egress-policy.yaml"
gitea_branding_assets_document="${render_dir}/gitea-branding-assets-configmap.yaml"
gitea_branding_templates_document="${render_dir}/gitea-branding-templates-configmap.yaml"
extract_rendered_document \
"${render_dir}/gitea-oidc.yaml" \
Deployment \
gitea \
"$gitea_deployment_document" || \
fail "the Gitea render must contain exactly one gitea Deployment"
extract_rendered_document \
"${render_dir}/gitea-oidc.yaml" \
NetworkPolicy \
gitea-allow-host-nginx-keycloak \
"$gitea_keycloak_policy_document" || \
fail "the Gitea render must contain exactly one dedicated Keycloak egress NetworkPolicy"
# Assert only references to the externally-created OIDC credential Secret.
# No credential payload is rendered, decoded, read, or printed by these checks.
rg --quiet --multiline \
'(?s)- name: GITEA_OAUTH_KEY_0\n[[:space:]]+valueFrom:\n[[:space:]]+secretKeyRef:\n[[:space:]]+key: key\n[[:space:]]+name: gitea-keycloak-oidc' \
"$gitea_deployment_document" || \
fail "the Gitea Deployment does not reference gitea-keycloak-oidc key=key"
rg --quiet --multiline \
'(?s)- name: GITEA_OAUTH_SECRET_0\n[[:space:]]+valueFrom:\n[[:space:]]+secretKeyRef:\n[[:space:]]+key: secret\n[[:space:]]+name: gitea-keycloak-oidc' \
"$gitea_deployment_document" || \
fail "the Gitea Deployment does not reference gitea-keycloak-oidc key=secret"
[[ "$(rg --count-matches --fixed-strings \
'https://id.learn.hyeonworks.com/realms/hyeonworks/.well-known/openid-configuration' \
"${render_dir}/gitea-oidc.yaml" || true)" == "2" ]] || \
fail "the Gitea OAuth add/update script must use the exact Keycloak discovery URL"
# These are non-sensitive app.ini policy values rendered by the pinned Chart.
for expected_setting in \
'ALLOW_ONLY_EXTERNAL_REGISTRATION=true' \
'DISABLE_REGISTRATION=false' \
'SHOW_REGISTRATION_BUTTON=false' \
'ENABLE_PASSWORD_SIGNIN_FORM=true' \
'ENABLE_AUTO_REGISTRATION=true' \
'USERNAME=preferred_username' \
'ACCOUNT_LINKING=login' \
'OPENID_CONNECT_SCOPES=profile email'; do
[[ "$(rg --count-matches --fixed-strings "$expected_setting" \
"${render_dir}/gitea-oidc.yaml" || true)" == "1" ]] || \
fail "the Gitea render must contain exactly one ${expected_setting} setting"
done
if rg --quiet '^[[:space:]]*hostAliases:' "$gitea_deployment_document"; then
fail "the Gitea Deployment must rely on CoreDNS and must not contain hostAliases"
fi
[[ "$(rg --count-matches \
'^[[:space:]]*cidr:[[:space:]]*192\.168\.0\.107/32[[:space:]]*$' \
"$gitea_keycloak_policy_document" || true)" == "1" ]] || \
fail "the dedicated Keycloak egress policy must allow exactly 192.168.0.107/32"
[[ "$(rg --count-matches \
'^[[:space:]]*-[[:space:]]*port:[[:space:]]*443[[:space:]]*$' \
"$gitea_keycloak_policy_document" || true)" == "1" ]] || \
fail "the dedicated Keycloak egress policy must allow exactly TCP port 443"
[[ "$(rg --count-matches \
'^[[:space:]]*protocol:[[:space:]]*TCP[[:space:]]*$' \
"$gitea_keycloak_policy_document" || true)" == "1" ]] || \
fail "the dedicated Keycloak egress policy must use TCP"
rg --quiet \
'^[[:space:]]*-[[:space:]]*Egress[[:space:]]*$' \
"$gitea_keycloak_policy_document" || \
fail "the dedicated Keycloak NetworkPolicy must select egress traffic"
gitea_branding_assets_configmap_name="$(
(rg --only-matching --no-filename \
'gitea-branding-assets-[a-z0-9]+' \
"${render_dir}/gitea-oidc.yaml" || true) |
LC_ALL=C sort --unique
)"
gitea_branding_templates_configmap_name="$(
(rg --only-matching --no-filename \
'gitea-branding-templates-[a-z0-9]+' \
"${render_dir}/gitea-oidc.yaml" || true) |
LC_ALL=C sort --unique
)"
[[ "$gitea_branding_assets_configmap_name" =~ ^gitea-branding-assets-[a-z0-9]{10}$ ]] || \
fail "the Gitea branding assets ConfigMap must have one Kustomize content hash"
[[ "$gitea_branding_templates_configmap_name" =~ ^gitea-branding-templates-[a-z0-9]{10}$ ]] || \
fail "the Gitea branding templates ConfigMap must have one Kustomize content hash"
extract_rendered_document \
"${render_dir}/gitea-oidc.yaml" \
ConfigMap \
"$gitea_branding_assets_configmap_name" \
"$gitea_branding_assets_document" || \
fail "the Gitea render must contain exactly one branding assets ConfigMap"
extract_rendered_document \
"${render_dir}/gitea-oidc.yaml" \
ConfigMap \
"$gitea_branding_templates_configmap_name" \
"$gitea_branding_templates_document" || \
fail "the Gitea render must contain exactly one branding templates ConfigMap"
for asset_key in hyeonworks.css logo.svg favicon.svg; do
[[ "$(rg --count-matches \
"^[[:space:]]{2}${asset_key//./\\.}:[[:space:]]*\\|[+-]?[[:space:]]*$" \
"$gitea_branding_assets_document" || true)" == "1" ]] || \
fail "the branding assets ConfigMap must contain exactly one ${asset_key}"
done
for template_key in header.tmpl extra_links.tmpl; do
[[ "$(rg --count-matches \
"^[[:space:]]{2}${template_key//./\\.}:[[:space:]]*\\|[+-]?[[:space:]]*$" \
"$gitea_branding_templates_document" || true)" == "1" ]] || \
fail "the branding templates ConfigMap must contain exactly one ${template_key}"
done
[[ "$(rg --count-matches --fixed-strings \
"$gitea_branding_assets_configmap_name" \
"$gitea_deployment_document" || true)" == "1" ]] || \
fail "the Gitea Deployment must reference the hashed branding assets ConfigMap once"
[[ "$(rg --count-matches --fixed-strings \
"$gitea_branding_templates_configmap_name" \
"$gitea_deployment_document" || true)" == "1" ]] || \
fail "the Gitea Deployment must reference the hashed branding templates ConfigMap once"
rg --quiet --multiline \
'(?s)- mountPath: /data/gitea/public/assets\n[[:space:]]+name: branding-assets\n[[:space:]]+readOnly: true' \
"$gitea_deployment_document" || \
fail "the Gitea branding assets must be mounted read-only at the official custom path"
rg --quiet --multiline \
'(?s)- mountPath: /data/gitea/templates/custom\n[[:space:]]+name: branding-templates\n[[:space:]]+readOnly: true' \
"$gitea_deployment_document" || \
fail "the Gitea branding templates must be mounted read-only at the official custom path"
if rg --quiet '^[[:space:]]*type:[[:space:]]*(NodePort|LoadBalancer)[[:space:]]*$' \
"${render_dir}/platform-postgres.yaml" \
"${render_dir}/gitea.yaml" \
"${render_dir}/gitea-oidc.yaml"; then
fail "an application service is exposed as NodePort or LoadBalancer"
fi
if rg --quiet '^[[:space:]]*name:[[:space:]]*gitea-ssh[[:space:]]*$' \
"${render_dir}/gitea.yaml" "${render_dir}/gitea-oidc.yaml"; then
fail "the disabled Gitea SSH Service is still rendered"
fi
if [[ -n "$verified_output_dir" ]]; then
for manifest_name in "${RENDERED_MANIFEST_NAMES[@]}"; do
source_manifest="${render_dir}/${manifest_name}.yaml"
output_manifest="${verified_output_dir}/${manifest_name}.yaml"
[[ -f "$source_manifest" && ! -L "$source_manifest" && -s "$source_manifest" ]] || \
fail "validated manifest is missing or unsafe: ${source_manifest}"
install -m 0600 -- "$source_manifest" "$output_manifest"
[[ -f "$output_manifest" && ! -L "$output_manifest" && -s "$output_manifest" ]] || \
fail "verified manifest handoff failed: ${output_manifest}"
cmp --silent -- "$source_manifest" "$output_manifest" || \
fail "verified manifest changed during handoff: ${manifest_name}.yaml"
done
verified_entry_count="$(find "$verified_output_dir" -mindepth 1 -maxdepth 1 | wc -l | tr -d '[:space:]')"
[[ "$verified_entry_count" == "${#RENDERED_MANIFEST_NAMES[@]}" ]] || \
fail "verified output directory does not contain exactly six manifest files"
printf 'Preserved six verified manifests for the apply handoff.\n'
fi
printf 'Phase 1 baseline and Gitea OIDC desired rendering invariants passed.\n'
printf 'Temporary rendered manifests and generated chart caches will be removed on exit.\n'