feat(operator): SPIFFE ClusterSPIFFEID provisioning (proposal 011 PR-2) - #84
Merged
Conversation
Second PR of proposal 011 — when an AgentCollective carries spec.spiffe.enabled=true, the operator issues a matching ClusterSPIFFEID custom resource so spire-controller-manager attests the collective's agent pods. Inert by design: no AgentCollective carries spec.spiffe until an operator opts in. Default behaviour for every existing deployment is unchanged. CRD changes (agentcollective_types.go + hand-updated CRD bases + zz_generated.deepcopy.go): * New SpiffeSpec struct on AgentCollectiveSpec — Enabled + TrustDomain. Edge fields land in proposal 012 PR-2. * New status fields SpiffeID / SpiffeIssued / SpiffeError on AgentCollectiveStatus. Prerequisite detection: * New PrerequisiteStatus.SpireInstalled, set by PrerequisiteReconciler from APIGroupChecker.SpireInstalled() (detects the spire.spiffe.io API group). New reconciler (operator/internal/reconcilers/collective/spiffe.go): * SpiffeReconciler.ReconcileCollective issues / updates one ClusterSPIFFEID per SPIFFE-enabled collective. * SPIFFE ID: spiffe://<trust-domain>/role/<collective-name>. Trust domain defaults to <corpus>.acc.local when blank. * podSelector targets the collective's pods by acc.io/collective. * Strict no-op when spiffe disabled OR SPIRE absent — the latter reports an operator-readable reason via SpiffeResult.Err → status.spiffeError. Never fails reconciliation. * ClusterSPIFFEID is cluster-scoped so no namespaced owner ref; created with ACC labels, explicit cleanup via DeleteClusterSPIFFEID (wired in a follow-up). Wired into CollectiveReconciler — runs after KServe; the SpiffeResult is patched onto the AgentCollective status sub-resource via patchSpiffeStatus (no-op when unchanged). RBAC: operator ClusterRole gains spire.spiffe.io/clusterspiffeids (get;list;watch;create;update;patch;delete) — kubebuilder marker on the reconciler + hand-updated config/rbac/role.yaml. Test coverage: 7 unit tests in spiffe_reconciler_test.go covering disabled / enabled-false / SPIRE-absent no-op paths, ClusterSPIFFEID issuance + idempotency, trust-domain derivation, deletion. Fake client registers the ClusterSPIFFEID GVK as unstructured. Generated artefacts (deepcopy + CRD bases) hand-edited since controller-gen could not be run in this environment; `make manifests generate` should produce an identical diff. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 15, 2026
flg77
added a commit
that referenced
this pull request
May 15, 2026
Third PR of proposal 011 — when an AgentCollective has spec.spiffe.enabled=true, every agent pod gains a spiffe-helper sidecar that materialises the pod's X.509-SVID + JWT-SVID into a shared emptyDir. The agent container reads SVID files from there. Inert by design: pods are unchanged until an operator opts in. New operator/internal/reconcilers/collective/spiffe_sidecar.go: * ApplySpiffeSidecar(deploy, collective, helperCMName) mutates a built agent Deployment in place — adds the spiffe-helper container, three volumes (spiffe-svids emptyDir shared with the agent; spiffe-workload-api CSI volume exposing the SPIRE Agent Workload API socket; spiffe-helper-config ConfigMap), the spiffe.io/spire-managed-identity pod annotation, and the agent container's read mount + ACC_SVID_* env vars. No-op when SPIFFE is disabled. * RenderSpiffeHelperConfig() produces the helper.conf HCL that drives the sidecar (agent_address, cert_dir, jwt_svids with audience acc-role-update). * SpiffeEnabled() + SpiffeHelperConfigMapName() helpers, shared with the wiring in agent_deployment.go. agent_deployment.go changes: * ReconcileCollective upserts a per-collective spiffe-helper ConfigMap (<collective>-spiffe-helper) when SPIFFE is enabled. * reconcileRoleDeployment calls ApplySpiffeSidecar after building the base Deployment, before the Upsert. Test coverage: 6 unit tests in spiffe_sidecar_test.go covering SpiffeEnabled, helper.conf rendering, disabled-is-no-op, sidecar injection (containers/volumes/annotation/agent-env), helper container shape, and ConfigMap naming. contains() reused from role_catalogue_test.go (same package). Validated on lighthouse: go build ./... clean, full operator suite green. Stacked on spiffe/operator-controller (011 PR-2, PR #84). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
flg77
added a commit
that referenced
this pull request
May 15, 2026
Third PR of proposal 011 — when an AgentCollective has spec.spiffe.enabled=true, every agent pod gains a spiffe-helper sidecar that materialises the pod's X.509-SVID + JWT-SVID into a shared emptyDir. The agent container reads SVID files from there. Inert by design: pods are unchanged until an operator opts in. New operator/internal/reconcilers/collective/spiffe_sidecar.go: * ApplySpiffeSidecar(deploy, collective, helperCMName) mutates a built agent Deployment in place — adds the spiffe-helper container, three volumes (spiffe-svids emptyDir shared with the agent; spiffe-workload-api CSI volume exposing the SPIRE Agent Workload API socket; spiffe-helper-config ConfigMap), the spiffe.io/spire-managed-identity pod annotation, and the agent container's read mount + ACC_SVID_* env vars. No-op when SPIFFE is disabled. * RenderSpiffeHelperConfig() produces the helper.conf HCL that drives the sidecar (agent_address, cert_dir, jwt_svids with audience acc-role-update). * SpiffeEnabled() + SpiffeHelperConfigMapName() helpers, shared with the wiring in agent_deployment.go. agent_deployment.go changes: * ReconcileCollective upserts a per-collective spiffe-helper ConfigMap (<collective>-spiffe-helper) when SPIFFE is enabled. * reconcileRoleDeployment calls ApplySpiffeSidecar after building the base Deployment, before the Upsert. Test coverage: 6 unit tests in spiffe_sidecar_test.go covering SpiffeEnabled, helper.conf rendering, disabled-is-no-op, sidecar injection (containers/volumes/annotation/agent-env), helper container shape, and ConfigMap naming. contains() reused from role_catalogue_test.go (same package). Validated on lighthouse: go build ./... clean, full operator suite green. Stacked on spiffe/operator-controller (011 PR-2, PR #84). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
flg77
added a commit
that referenced
this pull request
May 15, 2026
Second PR of proposal 012 — extends the operator-side SPIFFE
provisioning (011 PR-2) to edge deployments.
Inert by design: no behaviour change until an operator sets
deployMode=edge + spec.spiffe.
CRD changes (agentcollective_types.go + hand-updated CRD bases):
* SpiffeSpec gains EdgeTopology (nested | federated | ed25519,
default nested) + EdgeSiteID.
* AgentCollectiveStatus gains EdgeSiteID — echoes the site
qualifier baked into SpiffeID.
Reconciler (collective/spiffe.go):
* New computeSpiffeID() — when the owning AgentCorpus has
deployMode=edge AND spiffe.edgeTopology=nested, the SPIFFE ID
is site-qualified:
spiffe://<trust-domain>/edge/<site-id>/role/<collective>
Federated / ed25519 topologies + every non-edge deploy mode
keep the flat spiffe://<trust-domain>/role/<collective> form.
* nested topology without an edgeSiteID reports a config error
via SpiffeResult.Err -> status.spiffeError; never hard-fails
reconciliation.
* CollectiveReconciler.patchSpiffeStatus also patches EdgeSiteID.
Deploy manifests (deploy/edge-spire/):
* nested-spire-server.values.yaml — Helm values overlay for the
upstream spiffe/spire chart configured for nested topology
(edge SPIRE downstream of an rhoai parent). ACC does not
vendor SPIRE — operators helm-install it.
* edge-bundle-fetcher.yaml — PVC + CronJob caching the parent
SPIRE trust bundle every 6h for offline survival.
* README.md — install runbook + the edgeSiteID-uniqueness
requirement + the no-RWX fallback.
Test coverage: 5 unit tests in spiffe_edge_test.go covering the
site-qualified ID, the missing-edgeSiteID error path, federated
plain ID, non-edge topology-ignored, and ed25519 topology.
Validated on lighthouse: go build ./... clean, full operator
suite green.
Stacked on spiffe/agent-sidecar (011 PR-3, PR #85), which is
itself stacked on 011 PR-2 (#84).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
flg77
added a commit
that referenced
this pull request
May 15, 2026
Second PR of proposal 012 — extends the operator-side SPIFFE
provisioning (011 PR-2) to edge deployments.
Inert by design: no behaviour change until an operator sets
deployMode=edge + spec.spiffe.
CRD changes (agentcollective_types.go + hand-updated CRD bases):
* SpiffeSpec gains EdgeTopology (nested | federated | ed25519,
default nested) + EdgeSiteID.
* AgentCollectiveStatus gains EdgeSiteID — echoes the site
qualifier baked into SpiffeID.
Reconciler (collective/spiffe.go):
* New computeSpiffeID() — when the owning AgentCorpus has
deployMode=edge AND spiffe.edgeTopology=nested, the SPIFFE ID
is site-qualified:
spiffe://<trust-domain>/edge/<site-id>/role/<collective>
Federated / ed25519 topologies + every non-edge deploy mode
keep the flat spiffe://<trust-domain>/role/<collective> form.
* nested topology without an edgeSiteID reports a config error
via SpiffeResult.Err -> status.spiffeError; never hard-fails
reconciliation.
* CollectiveReconciler.patchSpiffeStatus also patches EdgeSiteID.
Deploy manifests (deploy/edge-spire/):
* nested-spire-server.values.yaml — Helm values overlay for the
upstream spiffe/spire chart configured for nested topology
(edge SPIRE downstream of an rhoai parent). ACC does not
vendor SPIRE — operators helm-install it.
* edge-bundle-fetcher.yaml — PVC + CronJob caching the parent
SPIRE trust bundle every 6h for offline survival.
* README.md — install runbook + the edgeSiteID-uniqueness
requirement + the no-RWX fallback.
Test coverage: 5 unit tests in spiffe_edge_test.go covering the
site-qualified ID, the missing-edgeSiteID error path, federated
plain ID, non-edge topology-ignored, and ed25519 topology.
Validated on lighthouse: go build ./... clean, full operator
suite green.
Stacked on spiffe/agent-sidecar (011 PR-3, PR #85), which is
itself stacked on 011 PR-2 (#84).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 15, 2026
flg77
added a commit
that referenced
this pull request
Jun 15, 2026
feat(agent): pack-role boot-and-wait (AgentRole=str + boot-dormant/self-promote)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Second PR of proposal 011. When an
AgentCollectivecarriesspec.spiffe.enabled: true, the operator issues a matchingClusterSPIFFEIDcustom resource so spire-controller-manager attests the collective's agent pods.Inert by design — no
AgentCollectivecarriesspec.spiffeuntil an operator opts in. Existing deployments see zero behaviour change.What's new
CRD (
agentcollective_types.go+ hand-updated CRD bases +zz_generated.deepcopy.go):SpiffeSpeconAgentCollectiveSpec—enabled,trustDomain. (Edge fields land in 012 PR-2.)spiffeID,spiffeIssued,spiffeErroronAgentCollectiveStatus.Prerequisite detection:
PrerequisiteStatus.SpireInstalled, set byPrerequisiteReconcilervia the newAPIGroupChecker.SpireInstalled()(detects thespire.spiffe.ioAPI group).Reconciler (
operator/internal/reconcilers/collective/spiffe.go):SpiffeReconciler.ReconcileCollectiveissues / updates oneClusterSPIFFEIDper SPIFFE-enabled collective.spiffe://<trust-domain>/role/<collective-name>; trust domain defaults to<corpus>.acc.localwhen blank.podSelectortargets the collective's pods by theacc.io/collectivelabel.spec.spiffeabsent/disabled, or when spire-controller-manager is not installed — the latter reports an operator-readable reason viastatus.spiffeError, never failing reconciliation.ClusterSPIFFEIDis cluster-scoped → no namespaced owner ref; created with ACC labels, explicit cleanup viaDeleteClusterSPIFFEID(wired in a follow-up).Wired into
CollectiveReconcilerafter KServe; the result is patched onto theAgentCollectivestatus sub-resource (no-op when unchanged).RBAC: operator ClusterRole gains
spire.spiffe.io/clusterspiffeids(full verbs) — kubebuilder marker + hand-updatedconfig/rbac/role.yaml.Test plan
go build ./...clean — validated on lighthouse (Linux, Go 1.23.4)operator/test/unit/spiffe_reconciler_test.go— disabled / enabled-false / SPIRE-absent no-op paths,ClusterSPIFFEIDissuance + idempotency, trust-domain derivation, deletion. All pass.go test ./...)zz_generated.deepcopy.go, CRD bases) were hand-edited since controller-gen could not run in this environment.make manifests generateshould produce an identical diff — worth a confirming run.Proposal 011 reference
Vault:
011 - SPIFFE workload identity for ACC agents.md. PR-1 merged (#81). PR-3 (spiffe-helper sidecar) is next.🤖 Generated with Claude Code