-
Notifications
You must be signed in to change notification settings - Fork 532
OCPBUGS-99883: fix(azure): cache VNet location validation and requeue on ARM 429 throttling #9123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -614,12 +614,14 @@ func (r *HostedControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.R | |
| return ctrl.Result{}, nil | ||
| } | ||
|
|
||
| var configValidationErr error | ||
| { | ||
| condition := metav1.Condition{ | ||
| Type: string(hyperv1.ValidHostedControlPlaneConfiguration), | ||
| ObservedGeneration: hostedControlPlane.Generation, | ||
| } | ||
| if err := r.validateConfigAndClusterCapabilities(ctx, hostedControlPlane); err != nil { | ||
| configValidationErr = err | ||
| condition.Status = metav1.ConditionFalse | ||
| condition.Message = err.Error() | ||
| condition.Reason = hyperv1.InsufficientClusterCapabilitiesReason | ||
|
|
@@ -709,6 +711,12 @@ func (r *HostedControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.R | |
| { | ||
| validConfig := meta.FindStatusCondition(hostedControlPlane.Status.Conditions, string(hyperv1.ValidHostedControlPlaneConfiguration)) | ||
| if validConfig != nil && validConfig.Status == metav1.ConditionFalse { | ||
| // Transient Azure API failures (e.g. 429 rate limiting) should requeue | ||
| // respecting Retry-After rather than hard-blocking until an unrelated watch event. | ||
| if result, shouldRequeue := requeueOnAzureAPIError(configValidationErr); shouldRequeue { | ||
| r.Log.Info("Configuration validation hit a transient Azure API error, requeueing", "requeueAfter", result.RequeueAfter) | ||
| return result, nil | ||
| } | ||
| r.Log.Info("Configuration is invalid, reconciliation is blocked") | ||
| return reconcile.Result{}, nil | ||
| } | ||
|
|
@@ -3226,8 +3234,36 @@ func (r *HostedControlPlaneReconciler) reconcileSREMetricsConfig(ctx context.Con | |
| return nil | ||
| } | ||
|
|
||
| // shouldSkipAzureResourceGroupLocationValidation reports whether Azure ARM calls to | ||
| // verify VNet/NSG/RG locations can be skipped because validation already succeeded for | ||
| // the current generation. Locations are immutable for a given set of Azure resource IDs, | ||
| // so re-checking on every reconcile is unnecessary and contributes to ARM throttling. | ||
| func shouldSkipAzureResourceGroupLocationValidation(hcp *hyperv1.HostedControlPlane) bool { | ||
| validConfig := meta.FindStatusCondition(hcp.Status.Conditions, string(hyperv1.ValidHostedControlPlaneConfiguration)) | ||
| return validConfig != nil && | ||
| validConfig.Status == metav1.ConditionTrue && | ||
| validConfig.ObservedGeneration == hcp.Generation | ||
| } | ||
|
Comment on lines
+3241
to
+3246
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor: the spec suggests caching based on specific field changes ( |
||
|
|
||
| // requeueOnAzureAPIError returns a RequeueAfter result when err wraps an Azure API | ||
| // ResponseError (e.g. 429), using ClassifyAzureError for differentiated backoff that | ||
| // respects the Retry-After header. Permanent validation failures that are not Azure | ||
| // API errors return false so reconciliation remains hard-gated. | ||
| func requeueOnAzureAPIError(err error) (ctrl.Result, bool) { | ||
| var respErr *azcore.ResponseError | ||
| if err == nil || !errors.As(err, &respErr) { | ||
| return ctrl.Result{}, false | ||
| } | ||
| requeueAfter, _ := hyperazureutil.ClassifyAzureError(err) | ||
| return ctrl.Result{RequeueAfter: requeueAfter}, true | ||
|
Comment on lines
+3252
to
+3258
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
rg -n -A35 -B5 'func\s+ClassifyAzureError\b' .
rg -n -A20 -B5 'TestRequeueOnAzureAPIError|StatusBadRequest' control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.goRepository: openshift/hypershift Length of output: 3161 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
echo "== Locate relevant symbols and file sizes =="
wc -l control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go \
control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go \
support/azureutil/errors.go
echo
echo "== Requeue helper and callers =="
rg -n -A25 -B8 'func requeueOnAzureAPIError|requeueOnAzureAPIError\(' control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go
echo
echo "== Azure test cases around requeue helpers =="
sed -n '4708,4815p' control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.go
echo
echo "== Azure error classifier =="
sed -n '1,140p' support/azureutil/errors.go
echo
echo "== StatusBadRequest references in controller tests =="
rg -n -C4 '400|Bad Request|BadRequest|StatusBadRequest|Validation|invalid' control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller_test.goRepository: openshift/hypershift Length of output: 10081 Requeue only retryable Azure API responses.
🤖 Prompt for AI Agents |
||
| } | ||
|
Comment on lines
+3252
to
+3259
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The spec asks for 429 handling specifically, but this requeues on any Consider filtering to only transient codes: if respErr.StatusCode != http.StatusTooManyRequests && (respErr.StatusCode < 500 || respErr.StatusCode >= 600) {
return ctrl.Result{}, false
}
Comment on lines
+3237
to
+3259
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: per DEVELOPMENT.md ("Platform-specific logic should be isolated"), these Azure-specific helpers would fit better in |
||
|
|
||
| // verifyResourceGroupLocationsMatch verifies the locations match for the VNET, network security group, and managed resource groups | ||
| func (r *HostedControlPlaneReconciler) verifyResourceGroupLocationsMatch(ctx context.Context, hcp *hyperv1.HostedControlPlane) error { | ||
| if shouldSkipAzureResourceGroupLocationValidation(hcp) { | ||
| return nil | ||
| } | ||
|
|
||
| var ( | ||
| creds azcore.TokenCredential | ||
| found, ok bool | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This variable is declared here but consumed ~100 lines later at the gate check (line 714). The reconciler uses bare
{}blocks to isolate scope, and this threads state across those boundaries. Could this be scoped tighter — e.g., by checkingrequeueOnAzureAPIErrorinline where the condition is set to False, or by attaching the error to the condition's Message and re-parsing it at the gate?