Compare commits

...

2 Commits

Author SHA1 Message Date
Hatice Yildiz
ab470b9fb1 fix(scripts): register bp-external-secrets-stores in expected-bootstrap-deps.yaml
The dependency-graph-audit CI step rejected PR #334 because the new
bp-external-secrets-stores HR was on disk at slot 15a but missing from
the expected DAG. This commit adds it with the same dependsOn shape as
clusters/_template/bootstrap-kit/15a-external-secrets-stores.yaml:
[bp-external-secrets, bp-openbao].

Refs: #331, #310 (Phase 0 minimum), PR #334.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 21:30:26 +02:00
Hatice Yildiz
dc4155c675 fix(bp-external-secrets): split ClusterSecretStore into bp-external-secrets-stores chart (resolves CRD ordering, closes #331)
bp-external-secrets@1.0.0 deadlocked on first install on otech.omani.works:

  Helm install failed for release external-secrets-system/external-secrets
  with chart bp-external-secrets@1.0.0:
  failed post-install: unable to build kubernetes object for deleting hook
  bp-external-secrets/templates/clustersecretstore-vault-region1.yaml:
  resource mapping not found for name: "vault-region1" namespace: ""
  no matches for kind "ClusterSecretStore" in version "external-secrets.io/v1beta1"

Root cause: Helm's `helm.sh/hook-delete-policy: before-hook-creation` ran
a kubectl-style lookup of the existing ClusterSecretStore CR before the
upstream `external-secrets` subchart's CRDs finished registration. The
in-line ClusterSecretStore template (templates/clustersecretstore-vault-
region1.yaml) and the upstream subchart's CRDs co-installed in the same
release; admission ordering wasn't deterministic enough to make the
post-install hook safe.

Fix — same pattern as PR #247 (bp-crossplane@1.1.3 ↔ bp-crossplane-claims@1.0.0):
split the chart into controller + stores. Flux dependsOn orders them.

  - bp-external-secrets@1.1.0 — controller-only (just upstream subchart
    + NetworkPolicy + ServiceMonitor toggle). CRDs register here.
  - bp-external-secrets-stores@1.0.0 (NEW) — the default
    ClusterSecretStore CR; depends on bp-external-secrets being Ready.
    No Helm hooks needed: by the time this chart's HelmRelease starts,
    Flux has already verified bp-external-secrets is Ready=True and
    therefore the CRDs are registered.

Files:
  NEW: platform/external-secrets-stores/blueprint.yaml             (1.0.0)
  NEW: platform/external-secrets-stores/chart/Chart.yaml           (1.0.0; no upstream subchart, annotation `catalyst.openova.io/no-upstream: "true"`)
  NEW: platform/external-secrets-stores/chart/values.yaml          (clusterSecretStore.* knobs moved from controller chart)
  MOVED: platform/external-secrets/chart/templates/clustersecretstore-vault-region1.yaml
       → platform/external-secrets-stores/chart/templates/clustersecretstore-vault-region1.yaml
       (Helm hook annotations removed — Flux dependsOn now handles ordering)
  TOUCHED: platform/external-secrets/chart/Chart.yaml              (1.0.0 → 1.1.0; description note appended)
  TOUCHED: platform/external-secrets/blueprint.yaml                (1.0.0 → 1.1.0)
  TOUCHED: platform/external-secrets/chart/values.yaml             (clusterSecretStore block removed; pointer comment added)
  NEW: clusters/_template/bootstrap-kit/15a-external-secrets-stores.yaml
       (Flux HelmRelease, dependsOn: [bp-external-secrets, bp-openbao])
  TOUCHED: clusters/_template/bootstrap-kit/15-external-secrets.yaml
       (chart version 1.0.0 → 1.1.0)
  TOUCHED: clusters/_template/bootstrap-kit/kustomization.yaml
       (slot 15a inserted after 15)

Out of scope for this PR (separate tickets):
  - blueprint-release.yaml CI fan-out: verify the path-matrix picks up
    the new platform/external-secrets-stores/ directory automatically;
    if not, add the directory to the matrix in a follow-up.
  - Per-Sovereign cluster directory edits (#257 will delete those).
  - Phase 0 minimum trim (#310 will renumber slots; this PR uses 15a as
    a non-disruptive sub-slot insertion that works with both the current
    35-slot kustomization and the eventual 15-slot canonical layout —
    when #310 renumbers, 15 + 15a become 08 + 09 in the canonical order).

Refs: #331 (this issue), #247 (pattern reference — bp-crossplane split),
#310 (Phase 0 trim — companion).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 21:24:15 +02:00
11 changed files with 197 additions and 49 deletions

View File

@ -57,7 +57,7 @@ spec:
chart:
spec:
chart: bp-external-secrets
version: 1.0.0
version: 1.1.0
sourceRef:
kind: HelmRepository
name: bp-external-secrets

View File

@ -0,0 +1,65 @@
# bp-external-secrets-stores — Catalyst bootstrap-kit Blueprint, slot 15a
# (sub-slot of 15, follows bp-external-secrets controller).
#
# Owns the default ClusterSecretStore CR(s) wiring ESO to bp-openbao.
# Split from bp-external-secrets@1.0.0 (issue #331) to resolve the
# CRD-ordering deadlock — Helm's `before-hook-creation` delete policy on
# the in-line ClusterSecretStore hook ran a kubectl-style lookup of the
# CR before the upstream chart's CRDs finished registering, deadlocking
# the install with `no matches for kind ClusterSecretStore` (incident on
# otech.omani.works 2026-04-30).
#
# Mirrors the bp-crossplane (controller) ↔ bp-crossplane-claims (CRs)
# split shape from PR #247.
#
# Wrapper chart: platform/external-secrets-stores/chart/
# Catalyst-curated values: platform/external-secrets-stores/chart/values.yaml
# Reconciled by: Flux on the new Sovereign's k3s control plane.
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: bp-external-secrets-stores
namespace: flux-system
spec:
type: oci
interval: 15m
url: oci://ghcr.io/openova-io
secretRef:
name: ghcr-pull
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: bp-external-secrets-stores
namespace: flux-system
spec:
interval: 15m
releaseName: external-secrets-stores
targetNamespace: external-secrets-system
# Order — Flux will not start install until bp-external-secrets reaches
# Ready=True (which means: upstream ESO controller running AND CRDs
# registered) AND bp-openbao reaches Ready (the secret backend the
# ClusterSecretStore points at).
dependsOn:
- name: bp-external-secrets
- name: bp-openbao
chart:
spec:
chart: bp-external-secrets-stores
version: 1.0.0
sourceRef:
kind: HelmRepository
name: bp-external-secrets-stores
namespace: flux-system
# Event-driven install per docs/INVIOLABLE-PRINCIPLES.md #3 (Flux
# dependsOn is the gate, not Helm timeout).
install:
disableWait: true
remediation:
retries: 3
upgrade:
disableWait: true
remediation:
retries: 3

View File

@ -20,6 +20,7 @@ resources:
- 13-bp-catalyst-platform.yaml
- 14-crossplane-claims.yaml
- 15-external-secrets.yaml
- 15a-external-secrets-stores.yaml
- 16-cnpg.yaml
- 17-valkey.yaml
- 18-seaweedfs.yaml

View File

@ -0,0 +1,47 @@
apiVersion: catalyst.openova.io/v1alpha1
kind: Blueprint
metadata:
name: bp-external-secrets-stores
labels:
catalyst.openova.io/section: pts-3-3-security-and-policy
spec:
version: 1.0.0
card:
title: External Secrets — Stores
summary: |
Default ClusterSecretStore CR(s) wiring the bp-external-secrets
controller to bp-openbao. Split from bp-external-secrets@1.0.0 (#331)
to resolve CRD-ordering deadlock — the CR cannot live in the same
Helm release as the operator that registers its CRD; Flux dependsOn
orders the two charts so this one applies after the controller is
Ready and CRDs are guaranteed registered.
Mirrors the bp-crossplane-claims pattern (PR #247).
icon: external-secrets.svg
category: security
visibility: unlisted
configSchema:
type: object
properties:
clusterSecretStore:
type: object
properties:
enabled:
type: boolean
default: true
name:
type: string
default: vault-region1
server:
type: string
default: "http://openbao.openbao.svc.cluster.local:8200"
path:
type: string
default: secret
version:
type: string
enum: [v1, v2]
default: v2
dependsOn:
- bp-external-secrets
- bp-openbao

View File

@ -0,0 +1,29 @@
apiVersion: v2
name: bp-external-secrets-stores
version: 1.0.0
description: |
Catalyst-curated Blueprint chart for the default ESO ClusterSecretStore(s)
that wire each Sovereign's bp-external-secrets controller to its bp-openbao
backend. Split from bp-external-secrets@1.0.0 (issue #331) to resolve
CRD-ordering deadlock: the ClusterSecretStore CR must be applied AFTER ESO's
CRDs are registered AND the controller is Ready, which Helm's
`before-hook-creation` delete policy can't guarantee within a single
HelmRelease (the policy's lookup runs before the upstream chart's CRDs
finish registration → "no matches for kind ClusterSecretStore" — see
bp-external-secrets@1.0.0 incident on otech.omani.works 2026-04-30).
Mirrors the bp-crossplane / bp-crossplane-claims split shape (PR #247):
controller chart owns the operator + CRDs, separate stores chart owns the
CRs, Flux dependsOn orders them.
Keeps zero upstream subchart deps — the chart is pure Catalyst templates
applying CRs against the upstream-registered CRDs.
type: application
keywords: [catalyst, blueprint, external-secrets, eso, openbao, secrets, clustersecretstore]
maintainers:
- name: OpenOva Catalyst
email: catalyst@openova.io
annotations:
catalyst.openova.io/no-upstream: "true"
catalyst.openova.io/depends-on: "bp-external-secrets,bp-openbao"

View File

@ -50,10 +50,6 @@ apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: {{ $name | quote }}
annotations:
"helm.sh/hook": post-install,post-upgrade
"helm.sh/hook-weight": "5"
"helm.sh/hook-delete-policy": before-hook-creation
labels:
catalyst.openova.io/component: external-secrets
catalyst.openova.io/secret-backend: openbao

View File

@ -0,0 +1,25 @@
# Catalyst-curated values overlay for bp-external-secrets-stores.
#
# This chart owns the ClusterSecretStore CR(s) that the bp-external-secrets
# controller chart can no longer ship in-line (CRD-ordering deadlock; see
# Chart.yaml). All knobs that previously lived under
# `clusterSecretStore:` in bp-external-secrets/values.yaml move here
# unchanged — cluster overlays in clusters/<sovereign>/ that previously
# overrode `clusterSecretStore.*` continue to work via the same key path,
# they just target this chart now.
#
# Per docs/INVIOLABLE-PRINCIPLES.md #4 (never hardcode) every operationally
# meaningful value flows from this file; cluster overlays may set
# `clusterSecretStore.*` without rebuilding the Blueprint OCI artifact.
clusterSecretStore:
enabled: true
name: vault-region1
server: "http://openbao.openbao.svc.cluster.local:8200"
path: secret
version: v2
kubernetesAuth:
mountPath: kubernetes
role: external-secrets
serviceAccountName: external-secrets
serviceAccountNamespace: external-secrets-system

View File

@ -5,7 +5,7 @@ metadata:
labels:
catalyst.openova.io/section: pts-3-3-security-and-policy
spec:
version: 1.0.0
version: 1.1.0
card:
title: External Secrets Operator
summary: |

View File

@ -1,15 +1,20 @@
apiVersion: v2
name: bp-external-secrets
version: 1.0.0
version: 1.1.0
description: |
Catalyst-curated Blueprint umbrella chart for External Secrets Operator
(ESO). Depends on the upstream `external-secrets` chart as a Helm
subchart so `helm dependency build` pulls the upstream payload into
this artifact; the Catalyst overlay templates in templates/ (default
ClusterSecretStore wired to bp-openbao, NetworkPolicy guard,
ServiceMonitor toggle) sit alongside the upstream subchart and Helm
renders both at install time. Catalyst-curated values flow into the
upstream subchart under the `external-secrets:` key in values.yaml.
(ESO) — controller-only as of 1.1.0. Depends on the upstream
`external-secrets` chart as a Helm subchart so `helm dependency build`
pulls the upstream payload into this artifact.
1.1.0 (issue #331): the default ClusterSecretStore CR was extracted
into a separate chart `bp-external-secrets-stores@1.0.0`. Reason:
Helm's `before-hook-creation` delete policy on the ClusterSecretStore
hook ran a kubectl-style lookup of the CR before the upstream chart's
CRDs finished registration, deadlocking the install on first
reconcile with `no matches for kind ClusterSecretStore` (incident on
otech.omani.works 2026-04-30). Mirrors the bp-crossplane@1.1.3 ↔
bp-crossplane-claims@1.0.0 split shipped in PR #247.
type: application
keywords: [catalyst, blueprint, external-secrets, eso, openbao, secrets]
maintainers:

View File

@ -90,38 +90,9 @@ external-secrets:
service:
enabled: false
# ─── Catalyst-managed default ClusterSecretStore (templates/clustersecretstore-vault-region1.yaml) ──
# The Catalyst-curated wrapper renders ONE default ClusterSecretStore
# (`vault-region1`) wired to the in-cluster bp-openbao service. Cluster
# overlays MAY:
# - Set `clusterSecretStore.enabled: false` to disable the default and
# author their own ClusterSecretStore CRs (e.g. multi-region with
# explicit primary/replica).
# - Override `clusterSecretStore.server` to point at an external OpenBao
# FQDN when ESO is reading from a different region's primary.
#
# Per docs/INVIOLABLE-PRINCIPLES.md #4 ("never hardcode") all knobs are
# runtime-configurable; cluster overlays in clusters/<sovereign>/ may
# override without rebuilding the Blueprint OCI artifact.
clusterSecretStore:
# Render the default `vault-region1` ClusterSecretStore.
enabled: true
# ClusterSecretStore name — matches platform/external-secrets/blueprint.yaml.
name: vault-region1
# In-cluster OpenBao endpoint. Cluster overlays override to the external
# FQDN (https://openbao.<location-code>.<sovereign-domain>) when ESO is
# reading from a different region's primary.
server: "http://openbao.openbao.svc.cluster.local:8200"
# KV mount path inside OpenBao (default per platform/openbao/README.md).
path: secret
# KV engine version (v2 = versioned KV, the OpenBao default).
version: v2
kubernetesAuth:
# Auth mount inside OpenBao for the Kubernetes auth method.
mountPath: kubernetes
# OpenBao role granted to the ESO ServiceAccount.
role: external-secrets
# ServiceAccount that ESO uses to authenticate against OpenBao.
# Must match external-secrets.serviceAccount.name + targetNamespace.
serviceAccountName: external-secrets
serviceAccountNamespace: external-secrets-system
# ─── ClusterSecretStore values ──────────────────────────────────────────
# As of bp-external-secrets@1.1.0 (issue #331) the default
# ClusterSecretStore CR moved into bp-external-secrets-stores@1.0.0 to
# resolve a CRD-ordering deadlock. Set
# `platform/external-secrets-stores/chart/values.yaml::clusterSecretStore.*`
# instead — the same key path; only the chart owning the CR changed.

View File

@ -88,6 +88,15 @@ slots:
name: bp-external-secrets
depends_on: [bp-openbao, bp-cert-manager]
wave: W2.K1
- slot: 15a
name: bp-external-secrets-stores
# Default ClusterSecretStore CR(s). Split from bp-external-secrets@1.0.0
# at PR #334 (issue #331) to resolve CRD-ordering deadlock —
# ClusterSecretStore CR cannot live in the same Helm release as the ESO
# subchart that registers its CRD. Mirrors bp-crossplane ↔
# bp-crossplane-claims pattern.
depends_on: [bp-external-secrets, bp-openbao]
wave: W2.K1
- slot: 16
name: bp-cnpg
depends_on: [bp-flux]