diff --git a/pkg/monitortests/clusterversionoperator/legacycvomonitortests/operators.go b/pkg/monitortests/clusterversionoperator/legacycvomonitortests/operators.go index 44a48375f26e..7ad2c9b56296 100644 --- a/pkg/monitortests/clusterversionoperator/legacycvomonitortests/operators.go +++ b/pkg/monitortests/clusterversionoperator/legacycvomonitortests/operators.go @@ -383,9 +383,9 @@ func testUpgradeOperatorStateTransitions(events monitorapi.Intervals, clientConf if checkAuthenticationAvailableExceptions(condition) { return "https://issues.redhat.com/browse/OCPBUGS-20056" } - if isTwoNode && condition.Type == configv1.OperatorDegraded && condition.Status == configv1.ConditionTrue && + if condition.Type == configv1.OperatorDegraded && condition.Status == configv1.ConditionTrue && strings.Contains(condition.Reason, "OAuthServerDeployment_UnavailablePod") { - return "authentication may report Degraded while oauth-openshift pods roll out during DualReplica disruptive upgrades" + return "authentication transiently reports Degraded while oauth-apiserver/oauth-server pods roll out during upgrade (OCPBUGS-111997)" } case "console": if condition.Type == configv1.OperatorDegraded && condition.Status == configv1.ConditionTrue { diff --git a/pkg/monitortests/clusterversionoperator/legacycvomonitortests/operators_test.go b/pkg/monitortests/clusterversionoperator/legacycvomonitortests/operators_test.go index 07ea31944413..5937f9b68bfa 100644 --- a/pkg/monitortests/clusterversionoperator/legacycvomonitortests/operators_test.go +++ b/pkg/monitortests/clusterversionoperator/legacycvomonitortests/operators_test.go @@ -674,3 +674,103 @@ func TestOverlapsNoExecuteTaintManagerTest(t *testing.T) { }) } } + +func buildOperatorConditionInterval(operator, conditionType, status, reason string, from, to time.Time) monitorapi.Interval { + return monitorapi.Interval{ + Source: monitorapi.SourceClusterOperatorMonitor, + Condition: monitorapi.Condition{ + Locator: monitorapi.Locator{ + Keys: map[monitorapi.LocatorKey]string{ + monitorapi.LocatorClusterOperatorKey: operator, + }, + }, + Message: monitorapi.Message{ + Reason: monitorapi.IntervalReason(reason), + HumanMessage: "test condition", + Annotations: map[monitorapi.AnnotationKey]string{ + monitorapi.AnnotationCondition: conditionType, + monitorapi.AnnotationStatus: status, + monitorapi.AnnotationReason: reason, + }, + }, + }, + From: from, + To: to, + } +} + +func Test_authenticationDegradedExceptionDuringUpgrade(t *testing.T) { + upgradeStart := time.Date(2026, 8, 17, 16, 0, 0, 0, time.UTC) + upgradeEnd := time.Date(2026, 8, 17, 17, 30, 0, 0, time.UTC) + + upgradeEvents := makeUpgradeEventList([]upgradeEvent{ + {eventTime: upgradeStart, reason: monitorapi.UpgradeStartedReason}, + {eventTime: upgradeEnd, reason: monitorapi.UpgradeCompleteReason}, + }) + + tests := []struct { + name string + reason string + wantFatal bool + }{ + { + name: "compound reason containing OAuthServerDeployment_UnavailablePod should be excepted", + reason: "APIServerDeployment_UnavailablePod::OAuthServerDeployment_UnavailablePod", + wantFatal: false, + }, + { + name: "OAuthServerDeployment_UnavailablePod alone should be excepted", + reason: "OAuthServerDeployment_UnavailablePod", + wantFatal: false, + }, + { + name: "APIServerDeployment_UnavailablePod alone should NOT be excepted", + reason: "APIServerDeployment_UnavailablePod", + wantFatal: true, + }, + { + name: "unrelated Degraded reason should NOT be excepted", + reason: "SomeOtherDegradedReason", + wantFatal: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + condFrom := upgradeStart.Add(50 * time.Minute) + condTo := condFrom.Add(10 * time.Second) + + conditionEvent := buildOperatorConditionInterval( + "authentication", "Degraded", "True", tt.reason, + condFrom, condTo, + ) + + var events monitorapi.Intervals + events = append(events, upgradeEvents...) + events = append(events, conditionEvent) + + results := testUpgradeOperatorStateTransitions(events, nil, configv1.HighlyAvailableTopologyMode) + + testName := "[bz-apiserver-auth] clusteroperator/authentication should not change condition/Degraded" + var hasFailure, hasSuccess bool + for _, tc := range results { + if tc.Name == testName { + if tc.FailureOutput != nil { + hasFailure = true + } else { + hasSuccess = true + } + } + } + + if tt.wantFatal { + assert.True(t, hasFailure, "expected a failure JUnit for reason %s", tt.reason) + assert.False(t, hasSuccess, "expected no success JUnit for reason %s (should be hard failure)", tt.reason) + } else { + if hasFailure { + assert.True(t, hasSuccess, "expected both failure and success JUnit (flake) for reason %s", tt.reason) + } + } + }) + } +}