Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,37 @@ func checkAuthenticationAvailableExceptions(condition *configv1.ClusterOperatorS
return false
}

// noExecuteTaintManagerAvailableGrace covers brief Available blips that land just after
// NoExecuteTaintManager removes its taint while Deployments are still rescheduling
// (observed packageserver recovery ~3s after test end).
const noExecuteTaintManagerAvailableGrace = time.Minute

func noExecuteTaintManagerTestIntervals(events monitorapi.Intervals) monitorapi.Intervals {
return events.Filter(func(eventInterval monitorapi.Interval) bool {
return eventInterval.Source == monitorapi.SourceE2ETest &&
strings.Contains(eventInterval.Locator.Keys[monitorapi.LocatorE2ETestKey], "NoExecuteTaintManager")
})
}

// overlapsNoExecuteTaintManagerTest reports whether conditionInterval overlaps a
// NoExecuteTaintManager e2e window, including grace after the test ends.
func overlapsNoExecuteTaintManagerTest(conditionInterval monitorapi.Interval, taintTests monitorapi.Intervals, grace time.Duration) bool {
for _, test := range taintTests {
window := test
if !window.To.IsZero() {
window.To = window.To.Add(grace)
}
if utility.IntervalsOverlap(conditionInterval, window) {
return true
}
}
return false
}

func testStableSystemOperatorStateTransitions(events monitorapi.Intervals, topology configv1.TopologyMode) []*junitapi.JUnitTestCase {
except := func(operator string, condition *configv1.ClusterOperatorStatusCondition, _ monitorapi.Interval) string {
isTwoNodeDualReplica := topology == configv1.DualReplicaTopologyMode
taintTests := noExecuteTaintManagerTestIntervals(events)
except := func(operator string, condition *configv1.ClusterOperatorStatusCondition, eventInterval monitorapi.Interval) string {
if condition.Status == configv1.ConditionTrue {
if condition.Type == configv1.OperatorAvailable {
return fmt.Sprintf("%s=%s is the happy case", condition.Type, condition.Status)
Expand Down Expand Up @@ -86,6 +115,22 @@ func testStableSystemOperatorStateTransitions(events monitorapi.Intervals, topol
if operator == "ingress" && condition.Reason == "IngressUnavailable" {
return "https://issues.redhat.com/browse/OCPBUGS-92835"
}
// DualReplica / TNF: serial NoExecuteTaintManager tests NoExecute-taint a control-plane
// node (masters are also workers), so Deployments briefly cannot schedule. Require the
// blip to overlap a NoExecuteTaintManager window (+grace) so unrelated Available=False
// regressions are not allowlisted for the whole DualReplica job.
if isTwoNodeDualReplica && overlapsNoExecuteTaintManagerTest(eventInterval, taintTests, noExecuteTaintManagerAvailableGrace) {
switch operator {
case "csi-snapshot-controller":
if strings.Contains(condition.Message, `Waiting for Deployment`) {
return "csi-snapshot-controller may report Available=False while Waiting for Deployment during DualReplica NoExecuteTaintManager tests"
}
Comment thread
eggfoobar marked this conversation as resolved.
case "operator-lifecycle-manager-packageserver":
if condition.Reason == "ClusterServiceVersionNotSucceeded" {
return "https://issues.redhat.com/browse/OCPBUGS-23744"
}
}
}
return ""
}
if condition.Type == configv1.OperatorDegraded && condition.Status == configv1.ConditionTrue {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -490,3 +490,77 @@ func TestIsTNFJobClusterOperatorReason(t *testing.T) {
})
}
}

func TestOverlapsNoExecuteTaintManagerTest(t *testing.T) {
base := time.Date(2026, 8, 10, 13, 55, 43, 0, time.UTC)
taintTest := monitorapi.Interval{
Condition: monitorapi.Condition{
Locator: monitorapi.Locator{
Keys: map[monitorapi.LocatorKey]string{
monitorapi.LocatorE2ETestKey: "[sig-node] NoExecuteTaintManager Single Pod [Serial] eventually evict pod with finite tolerations from tainted nodes",
},
},
},
Source: monitorapi.SourceE2ETest,
From: base,
To: base.Add(2 * time.Minute), // ends 13:57:55
}
otherTest := monitorapi.Interval{
Condition: monitorapi.Condition{
Locator: monitorapi.Locator{
Keys: map[monitorapi.LocatorKey]string{
monitorapi.LocatorE2ETestKey: "[sig-api-machinery] Namespaces [Serial] should ensure that all pods are removed when a namespace is deleted",
},
},
},
Source: monitorapi.SourceE2ETest,
From: base.Add(2 * time.Minute),
To: base.Add(3 * time.Minute),
}
taintTests := noExecuteTaintManagerTestIntervals(monitorapi.Intervals{taintTest, otherTest})
assert.Len(t, taintTests, 1)

tests := []struct {
name string
from time.Time
to time.Time
want bool
}{
{
name: "during test",
from: base.Add(30 * time.Second),
to: base.Add(45 * time.Second),
want: true,
},
{
name: "few seconds after test within grace",
from: base.Add(2*time.Minute + 3*time.Second),
to: base.Add(2*time.Minute + 3*time.Second + 81*time.Millisecond),
want: true,
},
{
name: "just inside grace window",
from: base.Add(2*time.Minute + 59*time.Second),
to: base.Add(3 * time.Minute),
want: true,
},
{
name: "after grace window",
from: base.Add(3*time.Minute + time.Second),
to: base.Add(3*time.Minute + 2*time.Second),
want: false,
},
{
name: "before test",
from: base.Add(-2 * time.Minute),
to: base.Add(-time.Minute),
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
condition := monitorapi.Interval{From: tt.from, To: tt.to}
assert.Equal(t, tt.want, overlapsNoExecuteTaintManagerTest(condition, taintTests, noExecuteTaintManagerAvailableGrace))
})
}
}