{{- /*
cert-manager CRD-establishment gate (issue #149 / prov #24).

Background — the race we close
==============================
This chart ships TWO ClusterIssuer CRs (letsencrypt-dns01-prod,
letsencrypt-http01-prod) as Helm post-install hooks (weight 5). Those
CRs are instances of the cert-manager.io/v1 ClusterIssuer CRD which
the upstream Jetstack subchart installs as REGULAR templates (no
helm.sh/hook annotation).

Helm install ordering is:
  1. Render every template in the chart
  2. Apply main manifests (CRDs land here, in install-order)
  3. Wait briefly for Pod readiness IF disableWait=false (we set
     disableWait=true in the HR — see clusters/_template/bootstrap-kit/
     02-cert-manager.yaml)
  4. Run post-install hooks in weight order

Step 2 calls `kubectl apply` on the CRD object — which CREATES the CRD
resource but does NOT wait for the apiserver's CRD controller to set
`status.conditions[?(@.type=="Established")].status == "True"`. The
Established transition is asynchronous (typically <1s on a warm
apiserver, observed up to 30s on a fresh Hetzner cold-start k3s where
the apiextensions-apiserver is itself still warming up).

Without this gate, the post-install ClusterIssuer hook (weight 5) can
fire BEFORE the ClusterIssuer CRD is Established, and Helm reports:
  "no matches for kind \"ClusterIssuer\" in version \"cert-manager.io/v1\""
  → terminal HelmRelease failure on prov #24 (`c776423270f4ae30`).

This Job actively polls every CRD in the gate's allowlist for
condition Established=True and only exits 0 when all are present.
Fails fast (300s budget) so a genuinely broken upstream surfaces in
Flux conditions rather than in a 15-minute spin.

Why post-install AND post-upgrade
=================================
Helm upgrade re-applies the ClusterIssuer post-install hook every
upgrade pass. The CRDs are normally still present (kept via
`crds.keep: true`), so the gate exits in <1s on the happy path — but
during a chart-version bump that adds a new CRD (e.g. v1alpha2
something), the gate ensures the new CRD is Established before any
dependent CR applies.

Why hook-weight -10
===================
Lower weight than the RBAC hook (-20) in templates/crd-gate-rbac.yaml
so the ServiceAccount + ClusterRole + Binding land BEFORE this Job
needs them; lower than the ClusterIssuer hooks (5) so the gate fires
BEFORE those issuers attempt to apply.

Pairs with:
  - templates/crd-gate-rbac.yaml — SA + ClusterRole + Binding
  - values.yaml `crdGate:` — knobs (enabled, crds, timeout, image)
*/}}
{{- $gate := .Values.crdGate | default dict -}}
{{- $enabled := true -}}
{{- if hasKey $gate "enabled" -}}
{{-   $enabled = $gate.enabled -}}
{{- end -}}
{{- if $enabled }}
{{- $crds := $gate.crds | default (list "clusterissuers.cert-manager.io" "issuers.cert-manager.io" "certificates.cert-manager.io") -}}
{{- $timeout := $gate.timeoutSeconds | default 300 -}}
{{- $interval := $gate.intervalSeconds | default 2 -}}
{{- /* Fix #158: bitnamilegacy/kubectl (Bitnami deleted versioned tags
       from docker.io/bitnami/kubectl 2025-08; bitnamilegacy is the
       deprecation-fallback path with bash/sh intact). */ -}}
{{- $image := $gate.image | default "bitnamilegacy/kubectl:1.30.7" -}}
{{- $pullPolicy := $gate.imagePullPolicy | default "IfNotPresent" -}}
apiVersion: batch/v1
kind: Job
metadata:
  name: {{ .Release.Name }}-crd-gate
  namespace: {{ .Release.Namespace }}
  labels:
    app.kubernetes.io/name: bp-cert-manager
    app.kubernetes.io/instance: {{ .Release.Name }}
    catalyst.openova.io/component: crd-gate
  annotations:
    "helm.sh/hook": post-install,post-upgrade
    "helm.sh/hook-weight": "-10"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
  backoffLimit: 0
  ttlSecondsAfterFinished: 300
  template:
    metadata:
      labels:
        app.kubernetes.io/name: bp-cert-manager
        app.kubernetes.io/instance: {{ .Release.Name }}
        catalyst.openova.io/component: crd-gate
    spec:
      serviceAccountName: {{ .Release.Name }}-crd-gate
      restartPolicy: Never
      containers:
        - name: gate
          image: {{ $image | quote }}
          imagePullPolicy: {{ $pullPolicy | quote }}
          securityContext:
            runAsNonRoot: true
            runAsUser: 1001
            runAsGroup: 1001
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: [ALL]
            seccompProfile:
              type: RuntimeDefault
          env:
            - name: CRDS
              value: {{ join " " $crds | quote }}
            - name: TIMEOUT_SECONDS
              value: {{ $timeout | quote }}
            - name: INTERVAL_SECONDS
              value: {{ $interval | quote }}
          command: ["/bin/bash", "-c"]
          args:
            - |
              set -euo pipefail
              echo "cert-manager CRD gate: wait for Established=True on: ${CRDS} (budget=${TIMEOUT_SECONDS}s, interval=${INTERVAL_SECONDS}s)"
              deadline=$(( $(date +%s) + TIMEOUT_SECONDS ))
              attempt=0
              while [ "$(date +%s)" -lt "${deadline}" ]; do
                attempt=$(( attempt + 1 ))
                all_ok=true
                missing=""
                for crd in ${CRDS}; do
                  # `kubectl wait` returns immediately if the resource exists
                  # AND meets the condition; we use a per-CRD jsonpath check
                  # instead so a single missing CRD doesn't crash the loop.
                  status="$(kubectl get crd "${crd}" \
                    --ignore-not-found \
                    -o jsonpath='{.status.conditions[?(@.type=="Established")].status}' 2>/dev/null || true)"
                  if [ "${status}" != "True" ]; then
                    all_ok=false
                    missing="${missing} ${crd}(=${status:-absent})"
                  fi
                done
                if [ "${all_ok}" = "true" ]; then
                  echo "All CRDs Established after ${attempt} attempt(s)."
                  exit 0
                fi
                echo "Attempt ${attempt}: CRDs not yet Established:${missing}; sleeping ${INTERVAL_SECONDS}s."
                sleep "${INTERVAL_SECONDS}"
              done
              echo "FATAL: cert-manager CRDs did not reach Established=True within ${TIMEOUT_SECONDS}s." >&2
              echo "Still missing/not-Established:${missing}" >&2
              echo "Diagnose with:" >&2
              echo "  kubectl get crd | grep cert-manager.io" >&2
              echo "  kubectl get pods -n {{ .Release.Namespace }}" >&2
              echo "  kubectl describe crd clusterissuers.cert-manager.io" >&2
              exit 1
{{- end }}
