Repository navigation
controlplane: resolve workload issuers in the background instead of failing to start - #736
Conversation
…ailing to start
Start-up ran OpenID discovery for every issuer and returned an error
after five attempts, which agentmesh-one turns into a fatal exit. For
the login issuer (--issuer) that is the right call: it is operator-fixed
and a typo must stop the process where it is seen. For workload issuers
(--workload-issuer) it is not: they belong to the platforms whose
identities the mesh accepts (a SPIRE OIDC provider in another cluster,
a Substrate credential provider), appear and disappear with those
platforms, and one of them being unreachable took down enrollment,
policy sync and token minting for everyone else. On Cloud Run it also
meant the hub could not be deployed before the first platform's
issuer existed, and that adding a platform was a redeploy.
A workload issuer that does not answer at start-up now gets a
background resolver with exponential backoff capped at five minutes.
Until it resolves, tokens from that issuer are refused with 401 and a
message that names it as configured-but-unresolved (new
identity.UnknownIssuerError lets the handlers tell that apart from an
issuer that was never configured), and the gauge
agentmesh_control_plane_workload_issuer_ready{issuer} is 0. Every
retry is logged. --issuer keeps its fail-fast behavior; the two
existing retry constants become variables so tests can shorten them.
Tests: a workload issuer that is down at start-up then comes up
(Start succeeds, gauge 0, exchange 401 naming the issuer, provider
resolved without a restart, gauge 1, exchange 200), and an unreachable
--issuer still failing Start. Clean under -race and 338 stress runs.
There was a problem hiding this comment.
Code Review
This pull request implements background resolution for workload OIDC issuers, ensuring that an unreachable workload issuer at startup does not prevent the control plane from starting. Instead, the control plane retries resolution in the background with exponential backoff, reports the status via a new Prometheus gauge, and returns a clear error message for tokens from unresolved issuers. In contrast, failures to resolve required login issuers remain fatal. The feedback highlights critical resource management issues in the new background resolution logic, specifically a potential TCP connection leak from cloning http.DefaultTransport without closing idle connections, a timer leak from using time.After in a loop, and a misleading log message regarding the next retry delay.
…log the real next delay Review: discoverProvider clones a transport per attempt and the resolver calls it for as long as the issuer is down, so close its idle connections each time; the retry wait uses a timer that is stopped on shutdown; the warning reports the delay the resolver will actually wait next. The resolver keeps retrying until the server stops, and the comment says why.
|
Verified on the Cloud Run hub with this branch's build, in the order that failed on
|
…overy passes Init is exported for embedders, so discoverProviders can run more than once in a process. Track the issuers a resolver is working on under the providers mutex: a later pass skips issuers already resolved or in flight, a resolver removes itself on success or shutdown. The test runs discovery twice and checks one resolver, then none after resolution.
|
On "do we keep rediscovering issuers / leak goroutines", from the code paths:
|
Problem
Server.Startruns OpenID discovery for every issuer and fails after five attempts (#228's bounded retry), whichagentmesh-oneturns intoFATAL Failed to start. Observed deployingagentmesh-one v0.1.0to Cloud Run as a hub for an OpenShell cluster whose SPIRE OIDC provider did not exist yet:Consequences: the hub cannot be deployed before the first platform's issuer exists; adding a platform is a redeploy; a workload issuer that goes away takes the hub down on its next restart, and with it enrollment, policy sync and
/sts/tokenfor every other member.What this keeps and what it changes
The history matters here. Fail-fast dates from the hub decoupling (
06fccab9); #228 added the bounded retry for rollouts; #266 fixed the console for the opposite failure, a one-shot discovery that failed silently and hid SSO for the pod's lifetime. The constraint from #266 is "an unresolved issuer must be loud and fail closed", not "must be fatal".--issuer(login, operator-fixed): unchanged, still fatal after the bounded retry. A typo should stop the process where it is seen.--workload-issuerentries: one bounded attempt at start-up; on failure a background resolver with exponential backoff (5s → 5m) keeps trying until the server stops. Until it resolves:401andJWT validation failed: workload issuer <iss> is configured but its discovery document has not been resolved yet(on/register,/refresh,/token/exchange); a newidentity.UnknownIssuerErrorlets the handlers tell that apart from an issuer that was never configured;agentmesh_control_plane_workload_issuer_ready{issuer}is0(1once resolved);WARNwith the issuer, attempt and next delay; resolution atINFO.The two discovery retry constants become package variables so tests can shorten them. Reference docs updated for both binaries.
Tests
TestWorkloadIssuerResolvedInBackground: workload issuer answering 503 at start-up →Startsucceeds, gauge 0,/token/exchangewith a token from it → 401 naming it; issuer comes up → provider resolved without a restart, gauge 1, exchange 200.TestLoginIssuerDiscoveryFailureIsFatal: unreachable--issuer→Startreturns an error naming it.go test ./internal/controlplane/ ./internal/identity/ -raceclean; the two new tests clean over 338 runs understress -p 8with the race detector;./hack/lint.sh0 issues; nogo.modchange (the gauge is read in tests throughclient_model, already a dependency).Follow-up, not in this PR: deploying this build to the Cloud Run hub to confirm the start-before-issuer order end to end.