diff --git a/pkg/operator/apiserver/controllerset/apiservercontrollerset.go b/pkg/operator/apiserver/controllerset/apiservercontrollerset.go index 29e741febe..249dfc2162 100644 --- a/pkg/operator/apiserver/controllerset/apiservercontrollerset.go +++ b/pkg/operator/apiserver/controllerset/apiservercontrollerset.go @@ -170,6 +170,22 @@ func WithStatusControllerPdbCompatibleHighInertia(workloadConditionsPrefix strin } } +// WithStatusControllerAPIServicesAvailableInertia sets inertia for APIServicesAvailable +// conditions to prevent brief transient errors from causing Available=False. +// This is useful for handling temporary network issues or brief missing HTTP headers +// that self-resolve within seconds. +func WithStatusControllerAPIServicesAvailableInertia() func(s *status.StatusSyncer) *status.StatusSyncer { + return func(s *status.StatusSyncer) *status.StatusSyncer { + return s.WithAvailableInertia(status.MustNewInertia( + 0, // default: no inertia for other conditions + status.InertiaCondition{ + ConditionTypeMatcher: regexp.MustCompile("^APIServicesAvailable$"), + Duration: 5 * time.Second, // tolerate brief transient errors + }).Inertia, + ) + } +} + func (cs *APIServerControllerSet) WithoutClusterOperatorStatusController() *APIServerControllerSet { cs.clusterOperatorStatusController.controller = nil cs.clusterOperatorStatusController.emptyAllowed = true diff --git a/pkg/operator/status/status_controller.go b/pkg/operator/status/status_controller.go index de348e797d..d53da28dac 100644 --- a/pkg/operator/status/status_controller.go +++ b/pkg/operator/status/status_controller.go @@ -52,6 +52,7 @@ type StatusSyncer struct { controllerFactory *factory.Factory recorder events.Recorder degradedInertia Inertia + availableInertia Inertia removeUnusedVersions bool } @@ -123,6 +124,14 @@ func (c *StatusSyncer) WithDegradedInertia(inertia Inertia) *StatusSyncer { return &output } +// WithAvailableInertia returns a copy of the StatusSyncer with the +// requested inertia function for available conditions. +func (c *StatusSyncer) WithAvailableInertia(inertia Inertia) *StatusSyncer { + output := *c + output.availableInertia = inertia + return &output +} + // WithVersionRemoval returns a copy of the StatusSyncer that will // remove versions that are missing in VersionGetter from the status. func (c *StatusSyncer) WithVersionRemoval() *StatusSyncer { @@ -217,7 +226,7 @@ func (c StatusSyncer) Sync(ctx context.Context, syncCtx factory.SyncContext) err configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, UnionClusterCondition(configv1.OperatorDegraded, operatorv1.ConditionFalse, c.degradedInertia, currentDetailedStatus.Conditions...), c.clock) configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, UnionClusterCondition(configv1.OperatorProgressing, operatorv1.ConditionFalse, nil, currentDetailedStatus.Conditions...), c.clock) - configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, UnionClusterCondition(configv1.OperatorAvailable, operatorv1.ConditionTrue, nil, currentDetailedStatus.Conditions...), c.clock) + configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, UnionClusterCondition(configv1.OperatorAvailable, operatorv1.ConditionTrue, c.availableInertia, currentDetailedStatus.Conditions...), c.clock) configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, UnionClusterCondition(configv1.OperatorUpgradeable, operatorv1.ConditionTrue, nil, currentDetailedStatus.Conditions...), c.clock) configv1helpers.SetStatusCondition(&clusterOperatorObj.Status.Conditions, UnionClusterCondition(configv1.EvaluationConditionsDetected, operatorv1.ConditionFalse, nil, currentDetailedStatus.Conditions...), c.clock)