# Catalyst Blueprint umbrella metadata — the upstream chart is resolved as
# a Helm subchart via Chart.yaml `dependencies:`. This values.yaml carries:
#   1. The catalystBlueprint metadata block (provenance + version) so
#      observability/audit pipelines can inspect the artifact and report
#      which upstream chart + version is bundled.
#   2. The upstream subchart values overlay under the `cluster-autoscaler:`
#      key (umbrella-chart convention — the dependency name from Chart.yaml
#      is the values namespace).
#   3. Catalyst overlay values (templates/secret.yaml, NetworkPolicy)
#      consumed by templates/ in this chart.
#
# Per docs/INVIOLABLE-PRINCIPLES.md #4 (never hardcode), every operationally-
# meaningful value is configurable; cluster overlays in clusters/<sovereign>/
# may override any of these without rebuilding the Blueprint OCI artifact.

catalystBlueprint:
  upstream:
    chart: cluster-autoscaler
    version: "9.46.6"
    repo: "https://kubernetes.github.io/autoscaler"
    # Catalyst rationale for this upstream:
    # - kubernetes/autoscaler is the canonical publisher (kubernetes-sigs).
    # - Chart 9.46.x ships appVersion 1.32.0 — upstream-tested against
    #   k8s 1.31 (the k3s 1.31.x apiserver shipping on every Sovereign).
    # - Hetzner cloud-provider is built into the upstream container image.
    # - Apache-2.0 licence.

# ─── Upstream chart values (subchart key: cluster-autoscaler) ────────────
# `helm dependency build` resolves the upstream as a subchart; values here
# under the `cluster-autoscaler:` key flow into that subchart unchanged.
cluster-autoscaler:
  # Hetzner Cloud cloud-provider — built into the upstream image. Selecting
  # `hetzner` here switches the deployment template's --nodes argument to
  # the Hetzner-specific shape (minSize:maxSize:instanceType:region:name)
  # per the upstream chart's deployment.yaml.
  cloudProvider: hetzner

  # autoDiscovery is irrelevant for the Hetzner provider — Hetzner has no
  # native ASG/MIG concept; the autoscaler manages servers directly via
  # the Cloud API. Disable to silence the lint warning the upstream chart
  # emits when autoDiscovery is partially set.
  autoDiscovery:
    clusterName: ""

  # autoscalingGroups — the Hetzner provider's "node groups". Each entry
  # becomes a `--nodes=min:max:instanceType:region:name` flag rendered by
  # the upstream deployment template (see chart templates/deployment.yaml
  # eq cloudProvider "hetzner" branch). The default below MUST be
  # overridden per-Sovereign via clusters/<sovereign>/.../bootstrap-kit/
  # so the autoscaler creates servers of the SAME SKU + region the Tofu
  # module provisioned at Phase 0. Per-Sovereign overlays write the
  # actual instanceType / region / counts at HelmRelease apply time.
  #
  # Defaults below match the canonical otechN topology (cpx32 worker SKU
  # in fsn1, min 2 / max 10) so a vanilla Sovereign that forgets to
  # patch this still gets a sensible autoscaler.
  autoscalingGroups:
    - name: workers
      instanceType: cpx32
      region: fsn1
      minSize: 2
      maxSize: 10

  # extraArgs — operational tuning. These flags are independent of the
  # cloud-provider and match the upstream defaults except where called
  # out below.
  extraArgs:
    # Scale-down idle interval — workers must remain underutilised for
    # N minutes before being removed. 10m is the cost-saving Catalyst
    # default; per-Sovereign overlays MAY raise this for spiky workloads.
    scale-down-unneeded-time: 10m
    scale-down-delay-after-add: 10m
    scale-down-delay-after-failure: 3m
    # Run on the control plane only — the autoscaler must NEVER schedule
    # onto a node it could itself terminate (race during scale-down).
    # The k3s control-plane node carries the
    # `node-role.kubernetes.io/control-plane=` taint; the toleration +
    # nodeSelector below pin the pod there.
    expander: least-waste
    skip-nodes-with-local-storage: false
    skip-nodes-with-system-pods: false
    balance-similar-node-groups: true

  # ─── Pin to the k3s control plane ────────────────────────────────────
  # The autoscaler must never run on a worker (it could terminate itself).
  # k3s control-plane nodes carry both labels — the selector + toleration
  # below match either. Per-Sovereign overlays MAY override this when the
  # Sovereign is multi-CP and the operator wants the pod elsewhere.
  nodeSelector:
    node-role.kubernetes.io/control-plane: "true"
  tolerations:
    - key: node-role.kubernetes.io/control-plane
      operator: Exists
      effect: NoSchedule
    - key: node-role.kubernetes.io/master
      operator: Exists
      effect: NoSchedule

  # Single replica — leader election is built into the autoscaler binary
  # (–leader-elect=true is the default for v1.32). Two replicas is
  # support-friendly but not required for correctness.
  replicaCount: 1

  # ─── Hetzner credentials + node-bootstrap config (issue #921) ───────
  # Wired via `extraEnvSecrets`: the upstream deployment template lifts
  # each `name → {name, key}` entry into a regular env var with a
  # secretKeyRef. The Secrets themselves are rendered by templates/
  # hetzner-token-secret.yaml + hetzner-node-config-secret.yaml in this
  # umbrella chart, sourced from canonical flux-system Secrets via Flux
  # `valuesFrom` at HelmRelease apply time (matches the velero / harbor
  # object-storage pattern — see clusters/_template/bootstrap-kit/
  # 34-velero.yaml).
  #
  # HCLOUD_TOKEN — Hetzner Cloud API token (mandatory).
  #
  # HCLOUD_CLUSTER_CONFIG / HCLOUD_CLOUD_INIT — at least one of these
  # MUST be set or cluster-autoscaler 1.32.x exits with the FATAL
  # "HCLOUD_CLUSTER_CONFIG or HCLOUD_CLOUD_INIT is not specified"
  # message at startup (issue #921). The upstream Hetzner provider uses
  # these to know how to bootstrap a fresh server it provisions during
  # scale-up: cloud-init script (legacy single-pool) or the newer
  # per-pool ClusterConfig JSON.
  #
  # Both keys are populated by per-Sovereign overlay
  # (clusters/<sovereign>/bootstrap-kit/50-cluster-autoscaler.yaml)
  # writing into clusterAutoscalerHcloud.cloudInit / .clusterConfig
  # below — see `templates/hetzner-node-config-secret.yaml`. The chart
  # renders the env var entries here unconditionally so the upstream
  # deployment always declares them; the Hetzner provider explicitly
  # accepts "either is specified", so an empty CLUSTER_CONFIG with a
  # populated CLOUD_INIT (or vice-versa) is the recommended Phase-1
  # shape.
  #
  # Per-Sovereign overlays MAY override `name:` to point at a different
  # Secret (e.g. an ExternalSecret materialised from OpenBao once the
  # Sovereign reaches Phase 2+).
  extraEnvSecrets:
    HCLOUD_TOKEN:
      name: hcloud-token
      key: token
    HCLOUD_CLUSTER_CONFIG:
      name: hetzner-node-config
      key: cluster-config
    HCLOUD_CLOUD_INIT:
      name: hetzner-node-config
      key: cloud-init

  # ─── HCLOUD_NETWORK / FIREWALL / SSH_KEY (issue #1778) ───────────────
  # cluster-autoscaler's Hetzner provider creates servers via the Hetzner
  # Cloud API. By DEFAULT those servers come up with PUBLIC ipv4 only —
  # they are NOT attached to any existing private network. That is fatal
  # for Catalyst Sovereigns because the worker cloud-init (identical to
  # the Phase-0 user_data) issues
  #   curl -sfL https://get.k3s.io | K3S_URL=https://10.0.1.2:6443 ... sh -
  # against the control plane's PRIVATE network IP (10.0.1.2). Without
  # the 10.0.0.0/16 attachment that URL is unreachable, the k3s agent
  # install fails, the node never registers with the apiserver, and the
  # autoscaler times out the scale-up after 15m → enters backoff →
  # bp-catalyst-platform install (which depends on Pending Pods being
  # placeable) wedges → chroot canvas tests blocked.
  #
  # Live evidence (prov #38/#39/#41/#43 omantel.biz, 2026-05-12):
  #   hcloud_server name=workers-77439321e2047e3e
  #     public_net.ipv4=178.105.102.237  private_net=[]
  #     ← never attached, never joined
  # Autoscaler log line at scale-up timeout:
  #   W orchestrator.go:626 Node group workers is not ready for scaleup -
  #     backoff with status: Scale-up timed out for node group workers
  #     after 15m2s
  #
  # The Hetzner provider docs (autoscaler/cluster-autoscaler/
  # cloudprovider/hetzner/README.md §"Environment Variables") name the
  # env vars that fix this:
  #   HCLOUD_NETWORK  — existing network name OR id to attach to
  #   HCLOUD_FIREWALL — existing firewall name(s) to attach to
  #   HCLOUD_SSH_KEY  — existing ssh-key name (debug + recovery parity
  #                     with Phase-0 nodes)
  #
  # Wired through the upstream chart's `extraEnv` (raw env vars; the
  # values are not credentials — they're Hetzner resource names the
  # autoscaler resolves via API at scale-up time). Per-Sovereign
  # overlays populate these strings via Flux `valuesFrom` against
  # `flux-system/cloud-credentials` keys `hcloud-network-name`,
  # `hcloud-firewall-name`, `hcloud-ssh-key-name` (written by cloud-init
  # — see infra/hetzner/cloudinit-control-plane.tftpl) onto the
  # `targetPath: cluster-autoscaler.extraEnv.HCLOUD_*` slots below.
  #
  # Empty string defaults: when an overlay leaves a name blank the
  # upstream chart still renders an `HCLOUD_*=""` env var on the
  # deployment. The Hetzner provider treats empty as unset (no
  # attachment / no firewall / no ssh-key), preserving the legacy
  # pre-#1778 shape for Sovereigns whose cloud-init never stamped the
  # name keys.
  extraEnv:
    HCLOUD_NETWORK: ""
    HCLOUD_FIREWALL: ""
    HCLOUD_SSH_KEY: ""

  # Resources — modest defaults for a controller-pattern workload that
  # mainly polls the apiserver + Hetzner API.
  resources:
    requests:
      cpu: 50m
      memory: 128Mi
    limits:
      cpu: 200m
      memory: 512Mi

  # ServiceMonitor / PodDisruptionBudget — DEFAULT FALSE per the
  # observability-toggle rule for the bp-* family
  # (docs/BLUEPRINT-AUTHORING.md §11.2). Per-Sovereign overlays MAY flip
  # them on once Prometheus Operator + PDB-aware drainers are present.
  serviceMonitor:
    enabled: false
  prometheusRule:
    enabled: false
  podDisruptionBudget: {}

  # RBAC + service account — chart manages its own. Cluster-scoped
  # reads on Pods/Nodes/Replicasets/StatefulSets/DaemonSets are required
  # for scheduling-decision visibility.
  rbac:
    create: true
    serviceAccount:
      create: true

# ─── Catalyst overlay values (consumed by templates/ in this chart) ──────
# Knobs for the Catalyst-curated Secret + NetworkPolicy rendered by
# templates/. Per INVIOLABLE-PRINCIPLES #4 everything here is runtime-
# configurable; cluster overlays MAY override without rebuilding the
# Blueprint OCI artifact.
clusterAutoscalerHcloud:
  # Hetzner API token — rendered into a namespace-local Secret from the
  # canonical flux-system/cloud-credentials.hcloud-token via Flux
  # `valuesFrom`. The HelmRelease in clusters/_template/bootstrap-kit/
  # 26-cluster-autoscaler.yaml writes the value into
  # `clusterAutoscalerHcloud.hcloudToken` at apply time so it never
  # appears in the committed manifest.
  hcloudToken: ""

  # ─── Node-bootstrap config (issue #921) ────────────────────────────
  # cluster-autoscaler 1.32.x's Hetzner provider FATALs at startup
  # ("`HCLOUD_CLUSTER_CONFIG` or `HCLOUD_CLOUD_INIT` is not specified")
  # unless at least one of these is wired. The two values are
  # complementary — `clusterConfig` is the modern per-pool JSON shape
  # (recommended for multi-pool Sovereigns), `cloudInit` is the legacy
  # single-script shape that matches what the OpenTofu Phase 0 module
  # already writes for the initial worker fleet. At least one MUST be
  # non-empty when the autoscaler is enabled.
  #
  # Per-Sovereign overlays populate one of these via Flux `valuesFrom`
  # against the canonical `flux-system/cloud-credentials` Secret cloud-
  # init writes at Phase 0 (the worker cloud-init is base64-encoded
  # under the `hcloud-cloud-init` key of the same Secret that already
  # carries `hcloud-token`).
  #
  # When BOTH are empty the chart still renders the Secret with both
  # keys empty so the upstream Deployment's secretKeyRef references
  # resolve cleanly during `helm template` lint and in the live
  # cluster regardless of overlay state. The autoscaler will FATAL on
  # first start in that mode, which surfaces the misconfiguration in
  # chart-test smoke rather than masking it as a missing-Secret
  # CreateContainerConfigError on Pod admission.
  #
  # Format reference (autoscaler/cluster-autoscaler/cloudprovider/
  # hetzner/README.md):
  #   HCLOUD_CLUSTER_CONFIG — base64(JSON) per-pool node config
  #   HCLOUD_CLOUD_INIT     — base64(cloud-init.yaml) shared bootstrap
  # The values supplied here MUST already be the BASE64-ENCODED form
  # per the upstream contract; the chart writes them verbatim into the
  # Secret. Phase 0 cloud-init is responsible for the base64 encoding
  # before stamping the `flux-system/cloud-credentials` key.
  clusterConfig: ""
  cloudInit: ""

  # ─── Chart-derived HCLOUD_CLUSTER_CONFIG fallback (qa-loop Wave 5 #79) ──
  # When `clusterConfig:` above is empty AND the per-Sovereign cloud-init
  # has not yet stamped the `hcloud-cloud-init` key into
  # `flux-system/cloud-credentials`, the chart's
  # `templates/hetzner-node-config-secret.yaml` synthesises a minimal
  # HCLOUD_CLUSTER_CONFIG JSON from the existing
  # `cluster-autoscaler.autoscalingGroups[]` array (already mandatory for
  # the `--nodes` flag) and the values below. This guarantees the
  # autoscaler binary always finds a non-empty HCLOUD_CLUSTER_CONFIG and
  # never FATALs with the generic "not specified" error.
  #
  # Live evidence (omantel.biz prov #7, 2026-05-10):
  #   F0510 17:02:56 cluster_autoscaler.go:309 Failed to create Hetzner
  #   manager: HCLOUD_CLUSTER_CONFIG or HCLOUD_CLOUD_INIT is not specified
  #
  # Per docs/INVIOLABLE-PRINCIPLES.md #4 every field below is operator-
  # tunable through `clusterAutoscalerHcloud.derivedClusterConfig.*`.
  derivedClusterConfig:
    # Default OS images per architecture — passed to the Hetzner provider
    # so the autoscaler-spawned servers come up on the same image family
    # the OpenTofu Phase-0 module used for the initial worker fleet.
    # `ubuntu-24.04` matches the Tofu module default; per-Sovereign
    # overlays MAY override (e.g. RHEL UBI for compliance Sovereigns).
    imagesForArch:
      amd64: ubuntu-24.04
      arm64: ubuntu-24.04
    # Per-pool overrides keyed by pool name (matches `name:` in
    # `cluster-autoscaler.autoscalingGroups[]`). Each entry MAY supply:
    #   cloudInit — pool-specific cloud-init override (otherwise inherits
    #               .clusterAutoscalerHcloud.cloudInit)
    #   labels    — node labels stamped at server creation
    #   taints    — node taints stamped at server creation
    # Empty by default — every pool inherits the shared cloudInit and
    # ships with no extra labels/taints.
    nodeConfigs: {}

  # NetworkPolicy — locks the autoscaler pod down to: egress to the
  # apiserver + DNS + Hetzner API (api.hetzner.cloud). DEFAULT FALSE
  # because the apiserver/Hetzner-API allowlist needs cluster-specific
  # CIDRs the umbrella chart does not know in advance; per-Sovereign
  # overlays flip this on with the Sovereign's apiserver Service CIDR.
  networkPolicy:
    enabled: false
