fix(operations): fail upgrade ops fast on unresolvable componentDef or serviceVersion#10547
fix(operations): fail upgrade ops fast on unresolvable componentDef or serviceVersion#10547weicao wants to merge 4 commits into
Conversation
…r serviceVersion An Upgrade OpsRequest referencing a nonexistent ComponentDefinition or a serviceVersion with no matched image was retried forever: Action wrote the invalid values into the cluster spec first, and ReconcileAction then surfaced bare NotFound / "no matched image found" errors that the ops framework treats as retryable. - add an upgrade pre-check in the Action phase that verifies the desired componentDefinition exists (name prefix/regex patterns are tolerated, as the cluster controller resolves them) and that the desired serviceVersion resolves to images, BEFORE the cluster spec is patched. - classify the same deterministic failures as fatal in the ReconcileAction stage to cover the window where the ComponentDefinition is deleted between validation and reconcile. - the shared resolvers (component.GetCompDefByName, resolveImagesWithCompVersions) are intentionally untouched; classification happens only at the ops consumption layer. Fixes #10546 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Auto Cherry-pick Instructions CLA Recheck Instructions |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #10547 +/- ##
==========================================
- Coverage 63.68% 63.57% -0.11%
==========================================
Files 519 519
Lines 62852 62895 +43
==========================================
- Hits 40027 39987 -40
- Misses 19202 19299 +97
+ Partials 3623 3609 -14
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| } | ||
| // compDefName may be a name prefix or a regex pattern that will be resolved | ||
| // to a specific componentDefinition by the cluster controller. | ||
| matched, matchedErr := u.compDefPatternMatched(reqCtx, cli, compDefName) |
There was a problem hiding this comment.
This still misses the pattern/regex ComponentDefinition path. If the current or requested ComponentDefinition is a prefix/regex and the requested serviceVersion has no compatible ComponentDefinition, compDefPatternMatched returns true and upgradePreCheck lets Action update the Cluster spec anyway. Later getComponentDefMapWithUpdatedImages keeps returning the non-fatal “waiting for the componentDefinition ... to be resolved” error, so the OpsRequest is back to retrying forever with the Cluster spec already polluted. Please resolve the pattern together with the requested serviceVersion, using the same compatibility semantics as cluster normalization, before treating this as retryable.
There was a problem hiding this comment.
Fixed in d0ce3db. The pattern path is no longer treated as retryable-by-default:
compDefPatternMatchedis gone. When the name does notGetas a concrete ComponentDefinition, the newresolveCompDefFromPatternresolves the pattern together with the requested serviceVersion, mirroring the compatibility semantics of cluster normalization'sresolveCompDefinitionNServiceVersion(listCompDefinitionsWithPattern+serviceVersionToCompDefinitions): list pattern-matched cmpds viacomponent.PrefixOrRegexMatched, build the provided-serviceVersion -> cmpd map from each cmpd's ownspec.serviceVersionplus the releases ofcomponent.CompatibleCompVersions4Definitionwhose compatibility rules match, filter withcomponent.CompareServiceVersion, look up the requested version (or the latest when empty) and pick the latest matching cmpd. The shared helpers are untouched; classification stays consumer-side inupgrade.go.- A deterministic no-match (
no matched component definition found with componentDef ... and serviceVersion ...) isintctrlutil.NewFatalErrorraised byupgradePreCheckin the Action, before the Cluster spec is written. Transient conditions (list errors, ComponentVersion unavailable/not observed) remain plain retryable errors. - ReconcileAction:
getComponentDefMapWithUpdatedImagesgoes through the same resolution, so the non-fatalwaiting for the componentDefinition ... to be resolvedretry-forever branch is removed; a pattern that stops matching mid-flight (e.g. cmpds deleted) is fatal there too, via the same helper.
Tests (pkg/operations/upgrade_test.go):
- negative: ops requests prefix pattern + serviceVersion
99.99.99-> opsFailedat Action, Cluster specConsistentlyunchanged. RED-verified against the previousupgrade.go(b3770b1): the spec fails withExpected <OpsPhase>: Creating to equal <OpsPhase>: Failed(timed out after 10s), i.e. the old code passed the precheck and kept retrying. - positive: same prefix + a supported serviceVersion passes the precheck, updates the Cluster spec, reconcile stays non-fatal while the pattern is not yet normalized, and the ops succeeds after the controller write-back is mimicked.
Full go test ./pkg/operations/... -count=1 green, go vet/gofmt clean.
… upgrade precheck When the current or requested componentDefinition of an upgrade opsRequest is a name prefix or regex pattern, the precheck only verified that the pattern matched some existing componentDefinition. A requested serviceVersion with no compatible componentDefinition then passed the precheck, polluted the cluster spec, and left the opsRequest retrying the non-fatal 'waiting for the componentDefinition ... to be resolved' error forever. - resolve the pattern together with the requested serviceVersion in getCompDefWithUpdatedImages, mirroring the compatibility semantics of the cluster normalization (resolveCompDefinitionNServiceVersion) and reusing the shared helpers component.PrefixOrRegexMatched, component.CompatibleCompVersions4Definition and component.CompareServiceVersion; a deterministic no-match is a fatal error raised before the Action writes the cluster spec. - getComponentDefMapWithUpdatedImages now resolves the pattern the same way in ReconcileAction, removing the retry-forever waiting branch: a pattern with no compatible componentDefinition mid-flight is fatal as well, while transient conditions (list errors, unavailable componentVersions) stay retryable. - the shared resolution helpers are untouched; classification stays in the ops consumption layer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
/nopick |
…ver in upgrade precheck Extract resolveCompDefinitionNServiceVersion and its helpers from controllers/apps/cluster into pkg/controller/component as the exported ResolveCompDefinitionNServiceVersion, and replace the duplicated pattern/serviceVersion resolution in the upgrade ops precheck with it. The deterministic no-match failure is now signaled by the errors.Is-able ErrCompDefinitionNServiceVersionUnmatched sentinel, which the ops layer classifies as a fatal error. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Pushed 07c0158: extracted the cluster normalization's Note: |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Problem
An Upgrade OpsRequest with deterministically invalid inputs — a nonexistent
componentDefinitionNameor aserviceVersionthat no ComponentVersion release provides — never fails, and pollutes the cluster spec before the error even surfaces:upgradeOpsHandler.Actionwrites the invalid values intocluster.spec.componentSpecs[*].componentDef/serviceVersionand updates the Cluster.ReconcileAction->getComponentDefMapWithUpdatedImagesthen returns a bareNotFound(fromcomponent.GetCompDefByName) or a bare"no matched image found for container ..."(fromcomponent.UpdateCompDefinitionImages4ServiceVersion).Root cause
The ops framework only fails an OpsRequest on
intctrlutil.NewFatalError; plain errors fromAction/ReconcileActionare requeued indefinitely (pkg/operations/ops_manager.go:95-104, 184-192). The webhook-levelvalidateUpgradeonly checks semver syntax, so these deterministic failures are classified as retryable and the ops staysRunningforever — with the cluster spec already polluted.Changes (
pkg/operations/upgrade.go)upgradePreCheck(new, called at the top ofAction): for each upgrade component, resolves the post-upgradecomponentDef/serviceVersionand validates them before the cluster spec is patched, mirroring theswitchoverPreCheck/ rebuild-instance Action-phase Fatal-check idiom. Fatal on: component not found in cluster, componentDefinition not found, no image matching the serviceVersion.getCompDefWithUpdatedImages(new, shared by both stages): classifies the deterministic resolution failures asintctrlutil.NewFatalError—IsNotFoundon the componentDefinition and"no matched image found"from image resolution. Name prefix / regex patterns are tolerated (compDefPatternMatched): if the name is a pattern matching existing componentDefinitions, the pre-check defers to the cluster controller's resolution, and the reconcile stage keeps the previous retry behavior (plain error) instead of failing.getComponentDefMapWithUpdatedImages(ReconcileAction stage): now goes through the classifying helper, so a ComponentDefinition deleted after validation but before/during reconcile fails the ops instead of requeueing forever (belt for the validation-to-reconcile window).Deliberately NOT changed
component.GetCompDefByNameandresolveImagesWithCompVersions/UpdateCompDefinitionImages4ServiceVersionare untouched: they are shared by declarative reconcile paths where waiting on a not-yet-created object is correct. Error classification is done only at the ops consumption layer (consumer-side classification).validateUpgradeuntouched: existence checks belong in the controller pre-check, which also covers ops created while the webhook is bypassed.Regression tests (RED-first,
pkg/operations/upgrade_test.go)Three new specs, verified to fail on
origin/maincode (fix stashed) and pass with the fix:componentDefinitionName-> opsFailedfast, and the cluster spec is asserted unchanged (Consistentlystill the original componentDef).serviceVersion(99.99.99, valid semver, no matching release) -> opsFailedfast, cluster specserviceVersionunchanged.Running->Reconcilereturns no error and the ops transitions toFailed(previously returned bare NotFound and requeued forever).RED evidence (fix stashed, tests kept):
GREEN evidence (fix applied):
go vet ./pkg/operations/andgofmt -l pkg/operations/clean.Fixes #10546
🤖 Generated with Claude Code