Skip to content

refactor: replace error-based validation with structured ValidationResult - #6057

Open
David Eads (deads2k) wants to merge 2 commits into
Azure:mainfrom
deads2k:cs-195-validation-simplify
Open

David Eads (deads2k) wants to merge 2 commits into
Azure:mainfrom
deads2k:cs-195-validation-simplify

Conversation

@deads2k

Copy link
Copy Markdown
Collaborator

Validation controllers now return *ValidationResult (Passed/Failed/Unknown)
instead of error, giving each outcome explicit retry timing, user-facing
messages, and service-provider diagnostics.

Key changes:

  • Validate() returns *ValidationResult with Outcome, Failed/Unknown details,
    and EarliestRetryAfter
  • SettableCooldownChecker enforces per-key retry timing inside SyncOnce
  • AfterEnqueuer exposes workqueue AddAfter for explicit requeue scheduling
  • Unknown results preserve the previous stored condition for up to 10
    consecutive attempts before overwriting
  • DeepEqual guard skips Cosmos Replace when the condition hasn't changed
  • DefaultResult and BuildCondition extracted as shared helpers

David Eads (deads2k) and others added 2 commits July 13, 2026 14:26
…sult

Validation controllers now return *ValidationResult (Passed/Failed/Unknown)
instead of error, giving each outcome explicit retry timing, user-facing
messages, and service-provider diagnostics.

Key changes:
- Validate() returns *ValidationResult with Outcome, Failed/Unknown details,
  and EarliestRetryAfter
- SettableCooldownChecker enforces per-key retry timing inside SyncOnce
- AfterEnqueuer exposes workqueue AddAfter for explicit requeue scheduling
- Unknown results preserve the previous stored condition for up to 10
  consecutive attempts before overwriting
- DeepEqual guard skips Cosmos Replace when the condition hasn't changed
- DefaultResult and BuildCondition extracted as shared helpers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 13, 2026 19:50
@openshift-ci

openshift-ci Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: deads2k

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

_, err := uaisClient.Get(ctx, resourceID.ResourceGroupName, resourceID.Name, nil)
if azureclient.IsResourceNotFoundErr(err) {
notFoundMIsStrs = append(notFoundMIsStrs, resourceID.String())
continue

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

existing bug?

Reason: "ClientError",
ServiceProviderMessage: fmt.Sprintf("failed to get user assigned identities client: %s", err),
UserMessage: "Failed to check managed identity existence.",
ReportingPolicy: ReportingPolicyTypeError,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no preference, you?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors validation controllers to return a structured *ValidationResult (Passed/Failed/Unknown) instead of error, enabling explicit retry scheduling (EarliestRetryAfter), more precise condition reporting, and reduced write churn via DeepEqual guards.

Changes:

  • Introduces ValidationResult/OutcomeType plus shared helpers (DefaultResult, BuildCondition) and updates validation interfaces to return structured outcomes.
  • Adds explicit requeue scheduling via AfterEnqueuer (workqueue.AddAfter) and per-key retry cooldown enforcement with SettableCooldownChecker.
  • Updates cluster/nodepool validation controllers and tests to use the new result model, including “preserve previous condition on Unknown for N attempts” behavior.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
internal/controllerutils/cooldown.go Adds a settable per-key cooldown gate and “time until ready” helper used for retry enforcement.
backend/pkg/controllers/validationcontrollers/validations/nodepool_validation.go Updates nodepool validation interface to return *ValidationResult.
backend/pkg/controllers/validationcontrollers/validations/cluster_validation.go Defines ValidationResult model and shared helpers to default/convert results into metav1.Condition.
backend/pkg/controllers/validationcontrollers/validations/azure_rp_registration_validation.go Migrates RP registration validation to structured validation outcomes and retry timing.
backend/pkg/controllers/validationcontrollers/validations/azure_cluster_resource_group_existence_validation.go Migrates RG existence validation to structured outcomes and retry timing.
backend/pkg/controllers/validationcontrollers/validations/azure_cluster_mis_existence_validation.go Migrates managed identity existence validation to structured outcomes and retry timing.
backend/pkg/controllers/validationcontrollers/validations/always_success_validation.go Updates always-success validation to return a Passed result.
backend/pkg/controllers/validationcontrollers/nodepool_validation_controller.go Implements result-based condition writing, explicit retry scheduling, and unknown-preservation logic for nodepools.
backend/pkg/controllers/validationcontrollers/nodepool_validation_controller_test.go Updates/extends tests for structured outcomes and retry scheduling timing.
backend/pkg/controllers/validationcontrollers/cluster_validation_controller.go Implements result-based condition writing, explicit retry scheduling, and unknown-preservation logic for clusters.
backend/pkg/controllers/controllerutils/generic_watching_controller.go Adds AfterEnqueuer API backed by workqueue.AddAfter.
.gitignore Ignores /.claude/ directory.

Comment on lines +80 to +84
if enqueuer, ok := controller.(controllerutils.AfterEnqueuer); ok {
syncer.enqueueAfter = enqueuer
} else {
panic("ClusterValidationController must implement AfterEnqueuer")
}
Comment on lines 27 to +29
Name() string
// Validate validates the NodePool. It returns nil if the validation succeeds and an error otherwise.
Validate(ctx context.Context, cluster *api.HCPOpenShiftCluster, nodePoolSubscription *arm.Subscription, nodePool *api.HCPOpenShiftClusterNodePool) error
Validate(ctx context.Context, cluster *api.HCPOpenShiftCluster, nodePoolSubscription *arm.Subscription, nodePool *api.HCPOpenShiftClusterNodePool) *ValidationResult
Comment on lines +96 to +113
func DefaultResult(result *ValidationResult) *ValidationResult {
if result == nil {
return &ValidationResult{
Outcome: OutcomeTypeUnknown,
Unknown: &UnknownResult{
Reason: "NilResult",
ServiceProviderMessage: "Validation returned nil result.",
UserMessage: "Validation status is unknown.",
ReportingPolicy: ReportingPolicyTypeError,
},
EarliestRetryAfter: ptr.To(60 * time.Second),
}
}
if result.EarliestRetryAfter == nil {
result.EarliestRetryAfter = ptr.To(60 * time.Second)
}
return result
}
Comment on lines +89 to +90
// ReportingPolicyTypeError will return an error for rapid retry, but the EarliestRetryAfter will prevent rapid retry and queue (without error) for a time after that.
ReportingPolicyTypeError ReportingPolicyType = "ReportError"
Comment on lines +87 to +93
func (c *genericWatchingController[T]) EnqueueAfter(keyObj any, duration time.Duration) {
key, ok := keyObj.(T)
if !ok {
return
}
c.queue.AddAfter(key, duration)
}
Comment on lines +118 to +139
func (c *SettableCooldownChecker) CanSync(_ context.Context, key any) bool {
now := c.clock.Now()
nextExecTime, ok := c.nextExecTime.Get(key)
if !ok {
return true
}
return now.After(nextExecTime.(time.Time))
}

// TimeUntilReady returns the duration until the key's cooldown expires.
// Returns 0 if the key has no cooldown set or the cooldown has already expired.
func (c *SettableCooldownChecker) TimeUntilReady(key any) time.Duration {
nextExecTime, ok := c.nextExecTime.Get(key)
if !ok {
return 0
}
d := nextExecTime.(time.Time).Sub(c.clock.Now())
if d < 0 {
return 0
}
return d
}
@openshift-ci

openshift-ci Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants