-
Notifications
You must be signed in to change notification settings - Fork 181
Adding test cases for "Add support for event-ttl in Kube API Server Operator" #1941
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?
Conversation
|
Skipping CI for Draft Pull Request. |
|
Warning Rate limit exceeded@gangwgr has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 5 minutes and 54 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (7)
WalkthroughMoves the test extension into test/extended/tests-extension as a separate Go module, updates build/packaging paths and scripts (.gitignore, Makefile, Dockerfile.rhel7, root go.mod), adds module files (go.mod, cmd/main.go, test/OWNERS, README), and introduces a new EventTTL extended test implementation. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: gangwgr The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
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.
📝 Code review complete. Found 13 suggestion(s).
52b8f7e to
a5e1528
Compare
a5e1528 to
0abb026
Compare
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.
Actionable comments posted: 3
🧹 Nitpick comments (6)
test/extended/event_ttl.go (6)
46-53: Cap test duration; make TTLs configurableThree serial subtests (5/10/15) + rollouts can exceed CI time. Prefer a single default TTL (e.g., 5) with env override.
- // Loop to create separate test cases for each TTL value (5m, 10m, 15m) - testValues := []int32{5, 10, 15} + // Default TTLs; allow override via EVENT_TTL_MINUTES="5,10,15" + testValues := []int32{5} + if v := strings.TrimSpace(getenvDefault("EVENT_TTL_MINUTES", "")); v != "" { + var vals []int32 + for _, s := range strings.Split(v, ",") { + if m, err := strconv.Atoi(strings.TrimSpace(s)); err == nil && m > 0 { + vals = append(vals, int32(m)) + } + } + if len(vals) > 0 { + testValues = vals + } + }Add helpers near imports:
+func getenvDefault(k, d string) string { if v := os.Getenv(k); v != "" { return v }; return d }
262-296: Poll for deletion to finish sooner; avoid fixed sleepSleeping for TTL+buffer guarantees maximum wait time even if the event expires earlier. Polling shortens test runtime and reduces flake due to clock skew.
- bufferMinutes := int32(3) // Increased buffer from 2 to 3 minutes - waitDuration := time.Duration(ttl+bufferMinutes) * time.Minute - expectedExpirationTime := creationTime.Add(waitDuration) + bufferMinutes := int32(3) // buffer for GC intervals/clock skew + maxWait := time.Duration(ttl+bufferMinutes) * time.Minute + expectedExpirationTime := creationTime.Add(maxWait) @@ - // Log progress every minute - ticker := time.NewTicker(1 * time.Minute) - done := make(chan bool, 1) // Buffered channel to prevent goroutine leak - elapsed := 0 + // Log progress every minute while polling + ticker := time.NewTicker(1 * time.Minute) + done := make(chan struct{}) + elapsed := 0 @@ - case done <- true: + case <-done: @@ -go func() { +go func() { for { select { - case <-done: + case <-done: return case <-ticker.C: elapsed++ g.By(fmt.Sprintf(" ... %d/%d minutes elapsed", elapsed, int(ttl+bufferMinutes))) } } }() -time.Sleep(waitDuration) +// Poll until deleted or timeout +_ = wait.PollUntilContextTimeout(ctx, 10*time.Second, maxWait, false, func(_ context.Context) (bool, error) { + _, err := kubeClient.CoreV1().Events(testNamespace).Get(ctx, eventName, metav1.GetOptions{}) + if err != nil && apierrors.IsNotFound(err) { + return true, nil + } + return false, nil +}) +close(done) +ticker.Stop()
445-517: Parse config.yaml instead of string contains; accept “5m0s” variantsString search is fragile. Parse YAML and validate apiServerArguments.event-ttl, tolerating duration formats like 5m0s.
@@ - configData, found := configMap.Data["config.yaml"] + configData, found := configMap.Data["config.yaml"] o.Expect(found).To(o.BeTrue(), "config.yaml not found in ConfigMap") @@ - // Parse the config and look for event-ttl in apiServerArguments - expectedTTL := fmt.Sprintf("%dm", expectedMinutes) - - // Check if event-ttl is in the config - hasEventTTL := strings.Contains(configData, "event-ttl") - if !hasEventTTL { - // event-ttl must be present in the config - o.Expect(hasEventTTL).To(o.BeTrue(), - fmt.Sprintf("event-ttl not found in ConfigMap %s (path in pod: %s)", configMapName, configPath)) - } - - // Further verify it has the correct value - hasCorrectValue := strings.Contains(configData, fmt.Sprintf("- %s", expectedTTL)) || - strings.Contains(configData, fmt.Sprintf("\"%s\"", expectedTTL)) || - strings.Contains(configData, fmt.Sprintf("'%s'", expectedTTL)) + // Parse the config and look for event-ttl in apiServerArguments + type kasConfig struct { + APIServerArguments map[string][]string `json:"apiServerArguments" yaml:"apiServerArguments"` + } + var cfg kasConfig + o.Expect(yaml.Unmarshal([]byte(configData), &cfg)).NotTo(o.HaveOccurred(), "failed to unmarshal config.yaml") + vals, ok := cfg.APIServerArguments["event-ttl"] + o.Expect(ok && len(vals) > 0).To(o.BeTrue(), "event-ttl not present in apiServerArguments") + // Accept "5m" or "5m0s" + expectedTTL := fmt.Sprintf("%dm", expectedMinutes) + hasCorrectValue := false + for _, v := range vals { + if v == expectedTTL { + hasCorrectValue = true + break + } + if d, err := time.ParseDuration(v); err == nil { + if d == time.Duration(expectedMinutes)*time.Minute { + hasCorrectValue = true + break + } + } + } @@ - if hasCorrectValue { + if hasCorrectValue { g.By(fmt.Sprintf(" [OK] Found event-ttl=%s in config.yaml", expectedTTL)) } else { @@ - o.Expect(hasCorrectValue).To(o.BeTrue(), - fmt.Sprintf("event-ttl found in config but value doesn't match expected %s", expectedTTL)) + o.Expect(hasCorrectValue).To(o.BeTrue(), + fmt.Sprintf("event-ttl value doesn't match expected %s", expectedTTL)) }Add import:
+ "sigs.k8s.io/yaml"
226-244: Event object: prefer events.k8s.io/v1 or drop deprecated fieldscorev1.Event with FirstTimestamp/LastTimestamp is deprecated. Either use events.k8s.io/v1.Event or omit deprecated fields; only CreationTimestamp is needed for TTL.
- FirstTimestamp: metav1.Now(), - LastTimestamp: metav1.Now(), - Count: 1, + // Omit deprecated FirstTimestamp/LastTimestamp; TTL uses object lease + Count: 1,Optional switch to events.k8s.io/v1 if the repo already uses it elsewhere.
183-187: Make verify helper return an error instead of hard-failingReturning error allows richer context at call site and keeps helpers reusable.
-func verifyEventTTLInConfigFile(ctx context.Context, kubeClient *kubernetes.Clientset, expectedMinutes int32) { +func verifyEventTTLInConfigFile(ctx context.Context, kubeClient *kubernetes.Clientset, expectedMinutes int32) error { @@ - o.Expect(err).NotTo(o.HaveOccurred()) + if err != nil { return err } @@ - o.Expect(targetPod).NotTo(o.BeNil(), "no running kube-apiserver pod found") + if targetPod == nil { return fmt.Errorf("no running kube-apiserver pod found") } @@ - o.Expect(err).NotTo(o.HaveOccurred(), "failed to get config ConfigMap") + if err != nil { return fmt.Errorf("failed to get config ConfigMap: %w", err) } @@ - o.Expect(found).To(o.BeTrue(), "config.yaml not found in ConfigMap") + if !found { return fmt.Errorf("config.yaml not found in ConfigMap") } @@ - o.Expect(hasCorrectValue).To(o.BeTrue(), - fmt.Sprintf("event-ttl value doesn't match expected %s", expectedTTL)) + if !hasCorrectValue { + return fmt.Errorf("event-ttl value doesn't match expected %s", expectedTTL) + } + return nilAnd at call site:
- verifyEventTTLInConfigFile(ctx, kubeClient, ttl) + o.Expect(verifyEventTTLInConfigFile(ctx, kubeClient, ttl)).NotTo(o.HaveOccurred())
31-44: Prefer context.WithTimeout for one-shot API callsUsing context.TODO() can hang on transient apiserver issues. Wrap one-shot calls in short timeouts; keep long waits in your explicit waiters.
- ctx = context.TODO() + var cancel context.CancelFunc + ctx, cancel = context.WithCancel(context.Background()) + g.DeferCleanup(cancel)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting
📒 Files selected for processing (1)
test/extended/event_ttl.go(1 hunks)
🔇 Additional comments (1)
test/extended/event_ttl.go (1)
23-44: Solid test harness setupClient wiring and BeforeEach look good for extended tests.
| // Verify event is deleted | ||
| actualExpirationTime := time.Now() | ||
| _, err = kubeClient.CoreV1().Events(testNamespace).Get(ctx, eventName, metav1.GetOptions{}) | ||
| o.Expect(err).To(o.HaveOccurred(), "event should be deleted after TTL") | ||
| o.Expect(err.Error()).To(o.ContainSubstring("not found"), "event should return 'not found' error") | ||
|
|
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.
Use apierrors.IsNotFound instead of string matching
String matching “not found” is brittle and locale-dependent. Use apierrors.IsNotFound.
@@
-import (
+import (
@@
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
@@
- o.Expect(err).To(o.HaveOccurred(), "event should be deleted after TTL")
- o.Expect(err.Error()).To(o.ContainSubstring("not found"), "event should return 'not found' error")
+ o.Expect(err).To(o.HaveOccurred(), "event should be deleted after TTL")
+ o.Expect(apierrors.IsNotFound(err)).To(o.BeTrue(), "expected NotFound error after TTL expiration")Also applies to: 3-21
| func waitForAPIServerRollout(ctx context.Context, kubeClient *kubernetes.Clientset, timeout time.Duration) error { | ||
| // First, get the current revision and pod creation times BEFORE we start waiting | ||
| initialPods, err := kubeClient.CoreV1().Pods(operatorclient.TargetNamespace).List(ctx, metav1.ListOptions{ | ||
| LabelSelector: "app=openshift-kube-apiserver,apiserver=true", | ||
| }) | ||
| if err != nil { | ||
| g.By(fmt.Sprintf(" Warning: Could not get initial pods: %v", err)) | ||
| } | ||
|
|
||
| // Track the oldest pod creation time - we need to see pods newer than this | ||
| var oldestPodTime time.Time | ||
| initialRevision := "" | ||
| if initialPods != nil && len(initialPods.Items) > 0 { | ||
| oldestPodTime = initialPods.Items[0].CreationTimestamp.Time | ||
| for _, pod := range initialPods.Items { | ||
| if pod.CreationTimestamp.Time.Before(oldestPodTime) { | ||
| oldestPodTime = pod.CreationTimestamp.Time | ||
| } | ||
| // Get the revision from labels | ||
| if rev, ok := pod.Labels["revision"]; ok && initialRevision == "" { | ||
| initialRevision = rev | ||
| } | ||
| } | ||
| g.By(fmt.Sprintf(" Initial state: %d pods, oldest created at %s, initial revision: %s", | ||
| len(initialPods.Items), oldestPodTime.Format(time.RFC3339), initialRevision)) | ||
| } | ||
|
|
||
| attempt := 0 | ||
| lastPodCount := 0 | ||
| lastNotRunningCount := 0 | ||
| rolloutStartTime := time.Now() | ||
|
|
||
| return wait.PollUntilContextTimeout(ctx, 15*time.Second, timeout, false, func(cxt context.Context) (bool, error) { | ||
| attempt++ | ||
| pods, err := kubeClient.CoreV1().Pods(operatorclient.TargetNamespace).List(cxt, metav1.ListOptions{ | ||
| LabelSelector: "app=openshift-kube-apiserver,apiserver=true", | ||
| }) | ||
| if err != nil { | ||
| g.By(fmt.Sprintf(" [Attempt %d] Error listing pods: %v", attempt, err)) | ||
| return false, nil | ||
| } | ||
|
|
||
| if len(pods.Items) == 0 { | ||
| g.By(fmt.Sprintf(" [Attempt %d] No kube-apiserver pods found yet", attempt)) | ||
| return false, nil | ||
| } | ||
|
|
||
| // Count pods and check if we have new pods (created after rollout started) | ||
| notRunningCount := 0 | ||
| newPodsCount := 0 | ||
| runningNewPodsCount := 0 | ||
| var notRunningPods []string | ||
| var currentRevision string | ||
|
|
||
| for _, pod := range pods.Items { | ||
| // Check if this is a new pod (created after we started waiting for rollout) | ||
| isNewPod := pod.CreationTimestamp.Time.After(rolloutStartTime) | ||
|
|
||
| if pod.Status.Phase != corev1.PodRunning { | ||
| notRunningCount++ | ||
| notRunningPods = append(notRunningPods, fmt.Sprintf("%s (%s)", pod.Name, pod.Status.Phase)) | ||
| } | ||
|
|
||
| if isNewPod { | ||
| newPodsCount++ | ||
| if pod.Status.Phase == corev1.PodRunning { | ||
| runningNewPodsCount++ | ||
| } | ||
| } | ||
|
|
||
| // Track current revision | ||
| if rev, ok := pod.Labels["revision"]; ok && currentRevision == "" { | ||
| currentRevision = rev | ||
| } | ||
| } | ||
|
|
||
| // We need ALL pods to be: | ||
| // 1. Running | ||
| // 2. Created after rollout started (new pods with new configuration) | ||
| // We expect 3 pods for a typical control plane | ||
| expectedPodCount := 3 | ||
| allPodsNewAndRunning := (newPodsCount == expectedPodCount && runningNewPodsCount == expectedPodCount) | ||
|
|
||
| // Log only when state changes or every 4th attempt (1 minute) | ||
| if notRunningCount != lastNotRunningCount || len(pods.Items) != lastPodCount || attempt%4 == 0 { | ||
| if notRunningCount > 0 { | ||
| g.By(fmt.Sprintf(" [Attempt %d] %d/%d pods running. Not running: %v. New pods: %d/%d running", | ||
| attempt, len(pods.Items)-notRunningCount, len(pods.Items), notRunningPods, runningNewPodsCount, newPodsCount)) | ||
| } else { | ||
| g.By(fmt.Sprintf(" [Attempt %d] All %d pods are running. New pods: %d/%d. Revision: %s", | ||
| attempt, len(pods.Items), runningNewPodsCount, newPodsCount, currentRevision)) | ||
| } | ||
| lastPodCount = len(pods.Items) | ||
| lastNotRunningCount = notRunningCount | ||
| } | ||
|
|
||
| // Success: all expected pods are new and running | ||
| return allPodsNewAndRunning, nil | ||
| }) | ||
| } |
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.
Rollout waiter: don’t assume 3 pods; detect revision change
Hardcoding expectedPodCount=3 breaks on SNO and other topologies. Also, using “created after rolloutStartTime” is flaky. Compute expected count from current pods and gate success on all pods Running and sharing a single revision different from the initial one.
@@
- // We expect 3 pods for a typical control plane
- expectedPodCount := 3
- allPodsNewAndRunning := (newPodsCount == expectedPodCount && runningNewPodsCount == expectedPodCount)
+ // Determine expected pods dynamically
+ expectedPodCount := len(pods.Items)
+ // All pods running and on the same (new) revision
+ sameRevision := true
+ rev0 := ""
+ for _, p := range pods.Items {
+ if r, ok := p.Labels["revision"]; ok {
+ if rev0 == "" { rev0 = r } else if rev0 != r { sameRevision = false }
+ } else {
+ sameRevision = false
+ }
+ }
+ // If we knew the initial revision, consider success only if it changed
+ revChanged := (initialRevision == "") || (rev0 != "" && rev0 != initialRevision)
+ allRunning := (notRunningCount == 0)
+ allPodsNewAndRunning := allRunning && sameRevision && revChanged && (expectedPodCount > 0)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| func waitForAPIServerRollout(ctx context.Context, kubeClient *kubernetes.Clientset, timeout time.Duration) error { | |
| // First, get the current revision and pod creation times BEFORE we start waiting | |
| initialPods, err := kubeClient.CoreV1().Pods(operatorclient.TargetNamespace).List(ctx, metav1.ListOptions{ | |
| LabelSelector: "app=openshift-kube-apiserver,apiserver=true", | |
| }) | |
| if err != nil { | |
| g.By(fmt.Sprintf(" Warning: Could not get initial pods: %v", err)) | |
| } | |
| // Track the oldest pod creation time - we need to see pods newer than this | |
| var oldestPodTime time.Time | |
| initialRevision := "" | |
| if initialPods != nil && len(initialPods.Items) > 0 { | |
| oldestPodTime = initialPods.Items[0].CreationTimestamp.Time | |
| for _, pod := range initialPods.Items { | |
| if pod.CreationTimestamp.Time.Before(oldestPodTime) { | |
| oldestPodTime = pod.CreationTimestamp.Time | |
| } | |
| // Get the revision from labels | |
| if rev, ok := pod.Labels["revision"]; ok && initialRevision == "" { | |
| initialRevision = rev | |
| } | |
| } | |
| g.By(fmt.Sprintf(" Initial state: %d pods, oldest created at %s, initial revision: %s", | |
| len(initialPods.Items), oldestPodTime.Format(time.RFC3339), initialRevision)) | |
| } | |
| attempt := 0 | |
| lastPodCount := 0 | |
| lastNotRunningCount := 0 | |
| rolloutStartTime := time.Now() | |
| return wait.PollUntilContextTimeout(ctx, 15*time.Second, timeout, false, func(cxt context.Context) (bool, error) { | |
| attempt++ | |
| pods, err := kubeClient.CoreV1().Pods(operatorclient.TargetNamespace).List(cxt, metav1.ListOptions{ | |
| LabelSelector: "app=openshift-kube-apiserver,apiserver=true", | |
| }) | |
| if err != nil { | |
| g.By(fmt.Sprintf(" [Attempt %d] Error listing pods: %v", attempt, err)) | |
| return false, nil | |
| } | |
| if len(pods.Items) == 0 { | |
| g.By(fmt.Sprintf(" [Attempt %d] No kube-apiserver pods found yet", attempt)) | |
| return false, nil | |
| } | |
| // Count pods and check if we have new pods (created after rollout started) | |
| notRunningCount := 0 | |
| newPodsCount := 0 | |
| runningNewPodsCount := 0 | |
| var notRunningPods []string | |
| var currentRevision string | |
| for _, pod := range pods.Items { | |
| // Check if this is a new pod (created after we started waiting for rollout) | |
| isNewPod := pod.CreationTimestamp.Time.After(rolloutStartTime) | |
| if pod.Status.Phase != corev1.PodRunning { | |
| notRunningCount++ | |
| notRunningPods = append(notRunningPods, fmt.Sprintf("%s (%s)", pod.Name, pod.Status.Phase)) | |
| } | |
| if isNewPod { | |
| newPodsCount++ | |
| if pod.Status.Phase == corev1.PodRunning { | |
| runningNewPodsCount++ | |
| } | |
| } | |
| // Track current revision | |
| if rev, ok := pod.Labels["revision"]; ok && currentRevision == "" { | |
| currentRevision = rev | |
| } | |
| } | |
| // We need ALL pods to be: | |
| // 1. Running | |
| // 2. Created after rollout started (new pods with new configuration) | |
| // We expect 3 pods for a typical control plane | |
| expectedPodCount := 3 | |
| allPodsNewAndRunning := (newPodsCount == expectedPodCount && runningNewPodsCount == expectedPodCount) | |
| // Log only when state changes or every 4th attempt (1 minute) | |
| if notRunningCount != lastNotRunningCount || len(pods.Items) != lastPodCount || attempt%4 == 0 { | |
| if notRunningCount > 0 { | |
| g.By(fmt.Sprintf(" [Attempt %d] %d/%d pods running. Not running: %v. New pods: %d/%d running", | |
| attempt, len(pods.Items)-notRunningCount, len(pods.Items), notRunningPods, runningNewPodsCount, newPodsCount)) | |
| } else { | |
| g.By(fmt.Sprintf(" [Attempt %d] All %d pods are running. New pods: %d/%d. Revision: %s", | |
| attempt, len(pods.Items), runningNewPodsCount, newPodsCount, currentRevision)) | |
| } | |
| lastPodCount = len(pods.Items) | |
| lastNotRunningCount = notRunningCount | |
| } | |
| // Success: all expected pods are new and running | |
| return allPodsNewAndRunning, nil | |
| }) | |
| } | |
| func waitForAPIServerRollout(ctx context.Context, kubeClient *kubernetes.Clientset, timeout time.Duration) error { | |
| // First, get the current revision and pod creation times BEFORE we start waiting | |
| initialPods, err := kubeClient.CoreV1().Pods(operatorclient.TargetNamespace).List(ctx, metav1.ListOptions{ | |
| LabelSelector: "app=openshift-kube-apiserver,apiserver=true", | |
| }) | |
| if err != nil { | |
| g.By(fmt.Sprintf(" Warning: Could not get initial pods: %v", err)) | |
| } | |
| // Track the oldest pod creation time - we need to see pods newer than this | |
| var oldestPodTime time.Time | |
| initialRevision := "" | |
| if initialPods != nil && len(initialPods.Items) > 0 { | |
| oldestPodTime = initialPods.Items[0].CreationTimestamp.Time | |
| for _, pod := range initialPods.Items { | |
| if pod.CreationTimestamp.Time.Before(oldestPodTime) { | |
| oldestPodTime = pod.CreationTimestamp.Time | |
| } | |
| // Get the revision from labels | |
| if rev, ok := pod.Labels["revision"]; ok && initialRevision == "" { | |
| initialRevision = rev | |
| } | |
| } | |
| g.By(fmt.Sprintf(" Initial state: %d pods, oldest created at %s, initial revision: %s", | |
| len(initialPods.Items), oldestPodTime.Format(time.RFC3339), initialRevision)) | |
| } | |
| attempt := 0 | |
| lastPodCount := 0 | |
| lastNotRunningCount := 0 | |
| rolloutStartTime := time.Now() | |
| return wait.PollUntilContextTimeout(ctx, 15*time.Second, timeout, false, func(cxt context.Context) (bool, error) { | |
| attempt++ | |
| pods, err := kubeClient.CoreV1().Pods(operatorclient.TargetNamespace).List(cxt, metav1.ListOptions{ | |
| LabelSelector: "app=openshift-kube-apiserver,apiserver=true", | |
| }) | |
| if err != nil { | |
| g.By(fmt.Sprintf(" [Attempt %d] Error listing pods: %v", attempt, err)) | |
| return false, nil | |
| } | |
| if len(pods.Items) == 0 { | |
| g.By(fmt.Sprintf(" [Attempt %d] No kube-apiserver pods found yet", attempt)) | |
| return false, nil | |
| } | |
| // Count pods and check if we have new pods (created after rollout started) | |
| notRunningCount := 0 | |
| newPodsCount := 0 | |
| runningNewPodsCount := 0 | |
| var notRunningPods []string | |
| var currentRevision string | |
| for _, pod := range pods.Items { | |
| // Check if this is a new pod (created after we started waiting for rollout) | |
| isNewPod := pod.CreationTimestamp.Time.After(rolloutStartTime) | |
| if pod.Status.Phase != corev1.PodRunning { | |
| notRunningCount++ | |
| notRunningPods = append(notRunningPods, fmt.Sprintf("%s (%s)", pod.Name, pod.Status.Phase)) | |
| } | |
| if isNewPod { | |
| newPodsCount++ | |
| if pod.Status.Phase == corev1.PodRunning { | |
| runningNewPodsCount++ | |
| } | |
| } | |
| // Track current revision | |
| if rev, ok := pod.Labels["revision"]; ok && currentRevision == "" { | |
| currentRevision = rev | |
| } | |
| } | |
| // Determine expected pods dynamically | |
| expectedPodCount := len(pods.Items) | |
| // All pods running and on the same (new) revision | |
| sameRevision := true | |
| rev0 := "" | |
| for _, p := range pods.Items { | |
| if r, ok := p.Labels["revision"]; ok { | |
| if rev0 == "" { rev0 = r } else if rev0 != r { sameRevision = false } | |
| } else { | |
| sameRevision = false | |
| } | |
| } | |
| // If we knew the initial revision, consider success only if it changed | |
| revChanged := (initialRevision == "") || (rev0 != "" && rev0 != initialRevision) | |
| allRunning := (notRunningCount == 0) | |
| allPodsNewAndRunning := allRunning && sameRevision && revChanged && (expectedPodCount > 0) | |
| // Log only when state changes or every 4th attempt (1 minute) | |
| if notRunningCount != lastNotRunningCount || len(pods.Items) != lastPodCount || attempt%4 == 0 { | |
| if notRunningCount > 0 { | |
| g.By(fmt.Sprintf(" [Attempt %d] %d/%d pods running. Not running: %v. New pods: %d/%d running", | |
| attempt, len(pods.Items)-notRunningCount, len(pods.Items), notRunningPods, runningNewPodsCount, newPodsCount)) | |
| } else { | |
| g.By(fmt.Sprintf(" [Attempt %d] All %d pods are running. New pods: %d/%d. Revision: %s", | |
| attempt, len(pods.Items), runningNewPodsCount, newPodsCount, currentRevision)) | |
| } | |
| lastPodCount = len(pods.Items) | |
| lastNotRunningCount = notRunningCount | |
| } | |
| // Success: all expected pods are new and running | |
| return allPodsNewAndRunning, nil | |
| }) | |
| } |
🤖 Prompt for AI Agents
In test/extended/event_ttl.go around lines 344-443, the waiter currently
hardcodes expectedPodCount=3 and treats "new" pods by creation time
(rolloutStartTime), which fails on single-node or other topologies and is flaky;
change the logic to derive expectedPodCount from the current pod list
(expectedPodCount := len(pods.Items)), stop using creation
timestamps/newPodsCount/runningNewPodsCount, and instead compute a single
currentRevision from pod labels and verify all pods share that same revision and
are in Phase Running and that currentRevision != initialRevision (or
initialRevision=="" treat as changed when any consistent revision appears). Gate
success on: all pods Running AND all pods have the same revision AND that
revision differs from the initialRevision observed before waiting; update
logging accordingly and remove the time-based "new pod" checks.
This commit introduces dependency isolation by creating a separate Go module for the Cluster Kube API Server Operator test extension at test/extended/tests-extension/. Key benefits: - Isolates test dependencies (ginkgo, gomega, openshift-tests-extension) from production operator code - Prevents dependency version conflicts between operator and test framework - Keeps production go.mod clean and focused on operator requirements - Follows the pattern used by other OpenShift operators The separate module includes: - go.mod with test-specific dependencies - go.sum with locked dependency versions - Same k8s.io version pinning as root module via replace directives - Local replace for main operator module to access test/library Reference implementation: https://github.com/openshift/operator-framework-operator-controller/tree/main/openshift/tests-extension
Relocates all test-related code into the isolated test module: Test code moved: - test/extended/event_ttl.go → test/extended/tests-extension/event_ttl.go - test/extended/main.go → test/extended/tests-extension/main.go - cmd/cluster-kube-apiserver-operator-tests-ext → test/extended/tests-extension/cmd/ Metadata moved: - .openshift-tests-extension/ → test/extended/tests-extension/.openshift-tests-extension/ Import path updates: - Updated cmd/main.go to import from tests-extension module This consolidates all test extension components (code, binary entry point, and test metadata) into a single self-contained directory that uses its own go.mod for dependency management. The test binary and metadata now live alongside the test code in test/extended/tests-extension/, making the test module completely independent from the operator codebase.
Updates Makefile, Dockerfile, and .gitignore to work with the new test extension module location. Makefile changes: - Added TESTS_EXT_DIR variable pointing to test/extended/tests-extension - tests-ext-build now builds from test/extended/tests-extension/ - tests-ext-update runs from and updates metadata in tests-extension dir - Binary output: test/extended/tests-extension/cluster-kube-apiserver-operator-tests-ext Dockerfile changes: - Updated gzip path: test/extended/tests-extension/cluster-kube-apiserver-operator-tests-ext - Updated COPY path for .gz file to match new location .gitignore changes: - Updated to ignore binary at new path: /test/extended/tests-extension/cluster-kube-apiserver-operator-tests-ext The build process now: 1. cd into test/extended/tests-extension/ (where the separate go.mod is) 2. Build the binary using the isolated dependencies 3. Output binary stays within the test module directory 4. Docker build references the new paths for gzip and copy operations
Removes test-only dependencies from the production operator module now that they are isolated in test/extended/tests-extension/go.mod. Removed dependencies: - github.com/onsi/ginkgo/v2 v2.22.0 - github.com/onsi/gomega v1.36.1 - github.com/openshift-eng/openshift-tests-extension v0.0.0-20250804142706-7b3ab438a292 Removed replace directive: - github.com/onsi/ginkgo/v2 => github.com/openshift/onsi-ginkgo/v2 Vendor directory updated: - Removed test framework packages from vendor/ - Updated vendor/modules.txt to reflect clean dependencies This achieves complete dependency isolation: - Production go.mod is now free of test framework dependencies - Test dependencies are managed separately in test/extended/tests-extension/go.mod - No risk of version conflicts between operator and test framework - Cleaner dependency tree for production builds The operator binary no longer carries any testing framework overhead.
1a7a1f4 to
7f653c9
Compare
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.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
test/extended/tests-extension/cmd/main.go (1)
28-28: Fix identifier typo: use “cluster-kube-apiserver-operator” everywhereThe extension ID and suite names use “apiserve” (missing “r”). This breaks metadata file naming, suite discovery, and docs alignment.
Apply this diff:
- ext := e.NewExtension("openshift", "payload", "cluster-kube-apiserve-operator") + ext := e.NewExtension("openshift", "payload", "cluster-kube-apiserver-operator") @@ - Name: "openshift/cluster-kube-apiserve-operator/conformance/parallel", + Name: "openshift/cluster-kube-apiserver-operator/conformance/parallel", @@ - Name: "openshift/cluster-kube-apiserve-operator/conformance/serial", + Name: "openshift/cluster-kube-apiserver-operator/conformance/serial", @@ - Name: "openshift/cluster-kube-apiserve-operator/optional/slow", + Name: "openshift/cluster-kube-apiserver-operator/optional/slow", @@ - Name: "openshift/cluster-kube-apiserve-operator/all", + Name: "openshift/cluster-kube-apiserver-operator/all",Also applies to: 32-33, 41-42, 50-51, 59-60
♻️ Duplicate comments (3)
test/extended/tests-extension/event_ttl.go (3)
23-29: Don’t overwrite cluster FeatureGate; merge-add EventTTL and restore original on cleanup
Overwrites spec to CustomNoUpgrade with only EventTTL and never restores. Preserve existing enabled/disabled entries, append EventTTL if missing, and restore the original spec in defer. This avoids collateral feature flips across the cluster.Apply diffs:
@@ - kubeClient *kubernetes.Clientset - configClient *configclient.Clientset - operatorClient *operatorclientset.Clientset - ctx context.Context + kubeClient *kubernetes.Clientset + configClient *configclient.Clientset + operatorClient *operatorclientset.Clientset + ctx context.Context + originalFeatureGateSpecJSON []byte + featureGatePatched bool @@ - defer func() { + defer func() { g.By(fmt.Sprintf("\nCleaning up eventTTLMinutes=%d configuration", ttl)) @@ - } + } + + // Restore FeatureGate if we changed it + if featureGatePatched && len(originalFeatureGateSpecJSON) > 0 { + g.By(" Restoring original FeatureGate spec") + if _, err := configClient.ConfigV1().FeatureGates().Patch(ctx, "cluster", types.MergePatchType, originalFeatureGateSpecJSON, metav1.PatchOptions{}); err != nil { + g.By(fmt.Sprintf(" Warning: failed to restore FeatureGate: %v", err)) + } else { + _ = waitForAPIServerRollout(ctx, kubeClient, 20*time.Minute) + } + } }() @@ - featureGate, err := configClient.ConfigV1().FeatureGates().Get(ctx, "cluster", metav1.GetOptions{}) + featureGate, err := configClient.ConfigV1().FeatureGates().Get(ctx, "cluster", metav1.GetOptions{}) o.Expect(err).NotTo(o.HaveOccurred(), "failed to get feature gate") + // capture original spec for restore + originalFeatureGateSpecJSON, _ = json.Marshal(map[string]interface{}{"spec": featureGate.Spec}) @@ - if !isEnabled { + if !isEnabled { g.By("Step 1b: Enabling EventTTL feature gate...") @@ - patchData := map[string]interface{}{ - "spec": map[string]interface{}{ - "featureSet": "CustomNoUpgrade", - "customNoUpgrade": map[string]interface{}{ - "enabled": []string{"EventTTL"}, - }, - }, - } + // merge-add EventTTL into existing lists + enabledSet := map[string]struct{}{} + for _, n := range featureGate.Spec.CustomNoUpgrade.Enabled { enabledSet[n] = struct{}{} } + enabledSet["EventTTL"] = struct{}{} + var enabledList []string + for k := range enabledSet { enabledList = append(enabledList, k) } + patchData := map[string]interface{}{ + "spec": map[string]interface{}{ + "featureSet": "CustomNoUpgrade", + "customNoUpgrade": map[string]interface{}{ + "enabled": enabledList, + "disabled": featureGate.Spec.CustomNoUpgrade.Disabled, + }, + }, + } patchBytes, err := json.Marshal(patchData) o.Expect(err).NotTo(o.HaveOccurred()) @@ - _, err = configClient.ConfigV1().FeatureGates().Patch(ctx, "cluster", types.MergePatchType, patchBytes, metav1.PatchOptions{}) + _, err = configClient.ConfigV1().FeatureGates().Patch(ctx, "cluster", types.MergePatchType, patchBytes, metav1.PatchOptions{}) o.Expect(err).NotTo(o.HaveOccurred()) + featureGatePatched = trueAlso applies to: 57-76, 81-88, 120-145
3-21: Use apierrors.IsNotFound instead of string matching
String matching “not found” is brittle; use the typed helper.@@ -import ( +import ( @@ "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" + apierrors "k8s.io/apimachinery/pkg/api/errors" ) @@ - _, err = kubeClient.CoreV1().Events(testNamespace).Get(ctx, eventName, metav1.GetOptions{}) - o.Expect(err).To(o.HaveOccurred(), "event should be deleted after TTL") - o.Expect(err.Error()).To(o.ContainSubstring("not found"), "event should return 'not found' error") + _, err = kubeClient.CoreV1().Events(testNamespace).Get(ctx, eventName, metav1.GetOptions{}) + o.Expect(err).To(o.HaveOccurred(), "event should be deleted after TTL") + o.Expect(apierrors.IsNotFound(err)).To(o.BeTrue(), "expected NotFound error after TTL expiration")Also applies to: 297-302
344-443: Rollout waiter: don’t assume 3 pods; gate on uniform new revision and Running
Hardcoding expectedPodCount=3 and “created after start” is topology‑ and timing‑sensitive. Derive count, ensure all pods Running, share one revision, and that revision differs from the initial one.@@ - // Count pods and check if we have new pods (created after rollout started) - notRunningCount := 0 - newPodsCount := 0 - runningNewPodsCount := 0 + // Evaluate pod health and revisions + notRunningCount := 0 var notRunningPods []string - var currentRevision string + sameRevision := true + currentRevision := "" @@ - // Check if this is a new pod (created after we started waiting for rollout) - isNewPod := pod.CreationTimestamp.Time.After(rolloutStartTime) - if pod.Status.Phase != corev1.PodRunning { notRunningCount++ notRunningPods = append(notRunningPods, fmt.Sprintf("%s (%s)", pod.Name, pod.Status.Phase)) } - - if isNewPod { - newPodsCount++ - if pod.Status.Phase == corev1.PodRunning { - runningNewPodsCount++ - } - } - - // Track current revision - if rev, ok := pod.Labels["revision"]; ok && currentRevision == "" { - currentRevision = rev - } + if rev, ok := pod.Labels["revision"]; ok { + if currentRevision == "" { currentRevision = rev } else if currentRevision != rev { sameRevision = false } + } else { + sameRevision = false + } @@ - // Success: all expected pods are new and running - return allPodsNewAndRunning, nil + expectedPodCount := len(pods.Items) + allRunning := (notRunningCount == 0) + revChanged := (initialRevision == "") || (currentRevision != "" && currentRevision != initialRevision) + success := allRunning && sameRevision && revChanged && (expectedPodCount > 0) + return success, nil
🧹 Nitpick comments (4)
test/extended/tests-extension/README.md (1)
13-15: Unify invocation path and remove duplicate/contradictory examplesTwo styles are shown for running tests; keep one to avoid confusion and ensure the path matches the new location.
Suggested edit:
- Prefer the full path under test/extended/tests-extension for local runs, or explain both clearly (when/why).
- Remove the second duplicate “How to Run the Tests Locally” header or rename it (“Running with KUBECONFIG”).
Example diff:
@@ -| `test/extended/tests-extension/cluster-kube-apiserver-operator-tests-ext run <test-name>` | Runs one specific test. | +| `test/extended/tests-extension/cluster-kube-apiserver-operator-tests-ext run <test-name>` | Runs one specific test. | @@ -export KUBECONFIG=path/to/kubeconfig -test/extended/tests-extension/cluster-kube-apiserver-operator-tests-ext run <test-name> -./cluster-kube-apiserver-operator-tests-ext run <test-name> +export KUBECONFIG=path/to/kubeconfig +test/extended/tests-extension/cluster-kube-apiserver-operator-tests-ext run <test-name>Optional: Update the Cluster Bot example to a currently supported OCP version to avoid future staleness.
Also applies to: 25-27
Dockerfile.rhel7 (1)
7-7: Make artifact compression reproducible and idempotentUse gzip flags to avoid non-deterministic timestamps and re-run failures.
- && gzip test/extended/tests-extension/cluster-kube-apiserver-operator-tests-ext + && gzip -n -f test/extended/tests-extension/cluster-kube-apiserver-operator-tests-extAlso confirm the Makefile’s tests-ext-build target emits the binary at the same path used here and in COPY.
Also applies to: 16-16
test/extended/tests-extension/event_ttl.go (2)
46-55: Shorten test runtime: poll for deletion instead of sleeping TTL+buffer; consider a single canonical TTL
Sleeping the full TTL+buffer per case makes the suite very long (especially 5/10/15m). Poll for NotFound with an overall timeout and run one canonical TTL by default; make others opt‑in (e.g., via DescribeTable or env gating).Example change for polling:
- time.Sleep(waitDuration) - - // Verify event is deleted - actualExpirationTime := time.Now() - _, err = kubeClient.CoreV1().Events(testNamespace).Get(ctx, eventName, metav1.GetOptions{}) - o.Expect(err).To(o.HaveOccurred(), "event should be deleted after TTL") - o.Expect(apierrors.IsNotFound(err)).To(o.BeTrue(), "expected NotFound error after TTL expiration") + pollStart := time.Now() + err = wait.PollUntilContextTimeout(ctx, 10*time.Second, waitDuration, false, func(_ context.Context) (bool, error) { + _, e := kubeClient.CoreV1().Events(testNamespace).Get(ctx, eventName, metav1.GetOptions{}) + if apierrors.IsNotFound(e) { return true, nil } + if e != nil { g.By(fmt.Sprintf(" get event error: %v", e)) } + return false, nil + }) + o.Expect(err).NotTo(o.HaveOccurred(), "timed out waiting for event deletion") + actualExpirationTime := time.Now()And to cut matrix size:
- testValues := []int32{5, 10, 15} + // Keep a single canonical TTL by default to control runtime; gate others via env/label if needed. + testValues := []int32{5}Also applies to: 262-296
469-516: Parse config.yaml instead of substring search for event-ttl
String matching can false‑positive. Parse YAML and assert apiServerArguments["event-ttl"][0] equals “m”.If acceptable, I can follow up with a small helper that unmarshals the config and validates the exact field.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting
⛔ Files ignored due to path filters (290)
go.sumis excluded by!**/*.sumtest/extended/tests-extension/go.sumis excluded by!**/*.sumvendor/github.com/go-task/slim-sprig/v3/.editorconfigis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/.gitattributesis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/.gitignoreis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/CHANGELOG.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/LICENSE.txtis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/README.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/Taskfile.ymlis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/crypto.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/date.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/defaults.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/dict.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/doc.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/functions.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/list.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/network.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/numeric.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/reflect.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/regex.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/strings.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/url.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/.gitignoreis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/CHANGELOG.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/CONTRIBUTING.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/LICENSEis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/Makefileis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/OWNERSis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/README.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/RELEASING.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/config/deprecated.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/core_dsl.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/core_dsl_patch.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/decorator_dsl.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/deprecated_dsl.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/formatter/colorable_others.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/formatter/colorable_windows.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/formatter/formatter.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/build/build_command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/command/abort.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/command/command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/command/program.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/boostrap_templates.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_templates.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/compile.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/gocovmerge.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/profiles_and_reports.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/test_suite.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/utils.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/verify_version.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/labels/labels_command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/main.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/import.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline_command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/unfocus/unfocus_command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta_tracker.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/dependencies.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hashes.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/suite.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo_cli_dependencies.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo_t_dsl.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/counter.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/failer.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/focus.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/global/init.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/group.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/interrupt_handler.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_unix.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_windows.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/node.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/ordering.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/output_interceptor.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/output_interceptor_unix.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/output_interceptor_wasm.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/output_interceptor_win.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/client_server.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_client.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_server.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_client.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_server.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/server_handler.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/progress_report.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/progress_report_bsd.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/progress_report_unix.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/progress_report_wasm.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/progress_report_win.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/progress_reporter_manager.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/report_entry.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/spec.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/spec_context.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/spec_patch.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/suite.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/suite_patch.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/testingtproxy/testing_t_proxy.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/tree.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/writer.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/reporters/deprecated_reporter.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/reporters/json_report.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/reporters/reporter.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/reporters/teamcity_report.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/reporting_dsl.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/table_dsl.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/code_location.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/config.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/deprecated_types.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/deprecation_support.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/enum_support.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/errors.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/file_filter.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/flags.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/label_filter.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/report_entry.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/types.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/types_patch.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/version.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/.gitignoreis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/CHANGELOG.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/CONTRIBUTING.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/LICENSEis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/README.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/RELEASING.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/format/format.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/gomega_dsl.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/internal/assertion.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/internal/async_assertion.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/internal/duration_bundle.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/internal/gomega.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/internal/gutil/post_ioutil.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/internal/gutil/using_ioutil.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/internal/polling_signal_error.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/internal/vetoptdesc.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/and.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/assignable_to_type_of_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/attributes_slice.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_a_directory.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_a_regular_file.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_an_existing_file.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_closed_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_comparable_to_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_element_of_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_empty_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_equivalent_to_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_false_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_identical_to.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_key_of_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_nil_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_numerically_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_sent_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_temporally_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_true_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/be_zero_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/consist_of.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/contain_element_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/contain_elements_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/contain_substring_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/equal_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/have_cap_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/have_each_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/have_exact_elements.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/have_existing_field_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/have_field.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/have_http_body_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/have_http_header_with_value_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/have_http_status_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/have_key_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/have_key_with_value_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/have_len_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/have_occurred_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/have_prefix_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/have_suffix_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/have_value.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/internal/miter/type_support_iter.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/internal/miter/type_support_noiter.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/match_error_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/match_json_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/match_regexp_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/match_xml_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/match_yaml_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/not.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/or.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/panic_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/receive_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/satisfy_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/semi_structured_data_support.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/succeed_matcher.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraph.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/support/goraph/bipartitegraph/bipartitegraphmatching.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/support/goraph/edge/edge.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/support/goraph/node/node.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/support/goraph/util/util.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/type_support.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/matchers/with_transform.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/types/types.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/LICENSEis excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/cmd/cmd.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/cmd/cmdimages/cmdimages.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/cmd/cmdinfo/info.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/cmd/cmdlist/list.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/cmd/cmdrun/runsuite.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/cmd/cmdrun/runtest.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/cmd/cmdupdate/update.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/dbtime/time.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/extension/extension.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/extension/extensiontests/environment.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/extension/extensiontests/result.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/extension/extensiontests/result_writer.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/extension/extensiontests/spec.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/extension/extensiontests/task.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/extension/extensiontests/types.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/extension/registry.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/extension/types.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/flags/component.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/flags/concurrency.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/flags/environment.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/flags/names.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/flags/output.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/flags/suite.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/ginkgo/logging.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/ginkgo/parallel.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/ginkgo/util.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/junit/types.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/util/sets/LICENSEis excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/util/sets/README.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/util/sets/byte.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/util/sets/doc.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/util/sets/empty.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/util/sets/int.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/util/sets/int32.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/util/sets/int64.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/util/sets/set.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/util/sets/string.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/openshift-eng/openshift-tests-extension/pkg/version/version.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/net/html/atom/atom.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/net/html/atom/table.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/net/html/charset/charset.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/net/html/const.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/net/html/doc.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/net/html/doctype.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/net/html/entity.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/net/html/escape.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/net/html/foreign.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/net/html/iter.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/net/html/node.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/net/html/parse.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/net/html/render.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/net/html/token.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/charmap/charmap.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/charmap/tables.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/encoding.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/htmlindex/htmlindex.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/htmlindex/map.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/htmlindex/tables.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/internal/identifier/identifier.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/internal/identifier/mib.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/internal/internal.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/japanese/all.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/japanese/eucjp.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/japanese/iso2022jp.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/japanese/shiftjis.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/japanese/tables.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/korean/euckr.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/korean/tables.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/simplifiedchinese/all.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/simplifiedchinese/gbk.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/simplifiedchinese/hzgb2312.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/simplifiedchinese/tables.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/traditionalchinese/big5.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/traditionalchinese/tables.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/unicode/override.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/encoding/unicode/unicode.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/internal/utf8internal/utf8internal.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/runes/cond.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/text/runes/runes.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/tools/cover/profile.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/tools/go/ast/edge/edge.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/tools/go/ast/inspector/cursor.gois excluded by!vendor/**,!**/vendor/**vendor/golang.org/x/tools/go/ast/inspector/inspector.gois excluded by!vendor/**,!**/vendor/**
📒 Files selected for processing (8)
.gitignore(1 hunks)Dockerfile.rhel7(2 hunks)Makefile(3 hunks)go.mod(0 hunks)test/extended/tests-extension/README.md(2 hunks)test/extended/tests-extension/cmd/main.go(1 hunks)test/extended/tests-extension/event_ttl.go(1 hunks)test/extended/tests-extension/go.mod(1 hunks)
💤 Files with no reviewable changes (1)
- go.mod
✅ Files skipped from review due to trivial changes (2)
- .gitignore
- Makefile
🔇 Additional comments (1)
test/extended/tests-extension/go.mod (1)
1-15: No issues found—toolchain and dependency graph are alignedVerification confirms Go version 1.24.0 is consistent across root module, tests-extension, and builder image (golang-1.24). The replace directive correctly references the local repo. Module resolution (tidy/vendor) succeeded without errors. No drift detected.
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.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
test/extended/tests-extension/cmd/main.go (1)
28-28: Fix typo "apiserve" → "apiserver" in extension and suite namesSuite names must match for test discovery and labeling. The correct operator name is "cluster-kube-apiserver-operator" (confirmed in go.mod, imports, existing test references, and OpenShift documentation).
Apply to test/extended/tests-extension/cmd/main.go:
- ext := e.NewExtension("openshift", "payload", "cluster-kube-apiserve-operator") + ext := e.NewExtension("openshift", "payload", "cluster-kube-apiserver-operator") @@ - Name: "openshift/cluster-kube-apiserve-operator/conformance/parallel", + Name: "openshift/cluster-kube-apiserver-operator/conformance/parallel", @@ - Name: "openshift/cluster-kube-apiserve-operator/conformance/serial", + Name: "openshift/cluster-kube-apiserver-operator/conformance/serial", @@ - Name: "openshift/cluster-kube-apiserve-operator/optional/slow", + Name: "openshift/cluster-kube-apiserver-operator/optional/slow", @@ - Name: "openshift/cluster-kube-apiserve-operator/all", + Name: "openshift/cluster-kube-apiserver-operator/all",All five instances at lines 28, 32, 41, 50, 59 need correction.
♻️ Duplicate comments (3)
test/extended/tests-extension/event_ttl.go (3)
297-302: Use apierrors.IsNotFound instead of string matchingString checks are brittle; use typed error.
+ apierrors "k8s.io/apimachinery/pkg/api/errors" @@ - o.Expect(err).To(o.HaveOccurred(), "event should be deleted after TTL") - o.Expect(err.Error()).To(o.ContainSubstring("not found"), "event should return 'not found' error") + o.Expect(err).To(o.HaveOccurred(), "event should be deleted after TTL") + o.Expect(apierrors.IsNotFound(err)).To(o.BeTrue(), "expected NotFound error after TTL expiration")
344-443: Rollout waiter: detect revision change; don’t assume 3 pods or use creation-time heuristicHandle SNO/HA topologies and reduce flakiness by verifying all pods Running, same revision, and revision != initial.
@@ - // Count pods and check if we have new pods (created after rollout started) - notRunningCount := 0 - newPodsCount := 0 - runningNewPodsCount := 0 + // Count pod readiness and revisions + notRunningCount := 0 var notRunningPods []string - var currentRevision string + var currentRevision string + sameRevision := true @@ - // Check if this is a new pod (created after we started waiting for rollout) - isNewPod := pod.CreationTimestamp.Time.After(rolloutStartTime) - if pod.Status.Phase != corev1.PodRunning { notRunningCount++ notRunningPods = append(notRunningPods, fmt.Sprintf("%s (%s)", pod.Name, pod.Status.Phase)) } - - if isNewPod { - newPodsCount++ - if pod.Status.Phase == corev1.PodRunning { - runningNewPodsCount++ - } - } - - // Track current revision - if rev, ok := pod.Labels["revision"]; ok && currentRevision == "" { - currentRevision = rev - } + // Track current revision and ensure all pods share it + if rev, ok := pod.Labels["revision"]; ok { + if currentRevision == "" { currentRevision = rev } else if currentRevision != rev { sameRevision = false } + } else { + sameRevision = false + } @@ - // We need ALL pods to be: - // 1. Running - // 2. Created after rollout started (new pods with new configuration) - // We expect 3 pods for a typical control plane - expectedPodCount := 3 - allPodsNewAndRunning := (newPodsCount == expectedPodCount && runningNewPodsCount == expectedPodCount) + expectedPodCount := len(pods.Items) + allRunning := (notRunningCount == 0) + revChanged := (initialRevision == "") || (currentRevision != "" && currentRevision != initialRevision) + allPodsNewAndRunning := allRunning && sameRevision && revChanged && (expectedPodCount > 0) @@ - if notRunningCount > 0 { - g.By(fmt.Sprintf(" [Attempt %d] %d/%d pods running. Not running: %v. New pods: %d/%d running", - attempt, len(pods.Items)-notRunningCount, len(pods.Items), notRunningPods, runningNewPodsCount, newPodsCount)) - } else { - g.By(fmt.Sprintf(" [Attempt %d] All %d pods are running. New pods: %d/%d. Revision: %s", - attempt, len(pods.Items), runningNewPodsCount, newPodsCount, currentRevision)) - } + if notRunningCount > 0 { + g.By(fmt.Sprintf(" [Attempt %d] %d/%d pods running. Not running: %v. Current revision: %s", + attempt, len(pods.Items)-notRunningCount, len(pods.Items), notRunningPods, currentRevision)) + } else { + g.By(fmt.Sprintf(" [Attempt %d] All %d pods running. Revision: %s (initial: %s), sameRevision=%t", + attempt, len(pods.Items), currentRevision, initialRevision, sameRevision)) + }
57-76: Don’t overwrite FeatureGate; merge-add EventTTL and restore after testPreserve existing FeatureGate entries and restore at the end. This was previously flagged.
Apply:
@@ var ( kubeClient *kubernetes.Clientset configClient *configclient.Clientset operatorClient *operatorclientset.Clientset ctx context.Context + originalFeatureGateSpecJSON []byte + featureGatePatched bool ) @@ featureGate, err := configClient.ConfigV1().FeatureGates().Get(ctx, "cluster", metav1.GetOptions{}) o.Expect(err).NotTo(o.HaveOccurred(), "failed to get feature gate") + // capture original spec for restore + originalFeatureGateSpecJSON, _ = json.Marshal(map[string]interface{}{"spec": featureGate.Spec}) @@ - if !isEnabled { + if !isEnabled { g.By("Step 1b: Enabling EventTTL feature gate...") @@ - patchData := map[string]interface{}{ - "spec": map[string]interface{}{ - "featureSet": "CustomNoUpgrade", - "customNoUpgrade": map[string]interface{}{ - "enabled": []string{"EventTTL"}, - }, - }, - } + // merge-add EventTTL to existing enabled list; preserve disabled list + enabledSet := map[string]struct{}{} + for _, n := range featureGate.Spec.CustomNoUpgrade.Enabled { enabledSet[n] = struct{}{} } + if _, ok := enabledSet["EventTTL"]; !ok { enabledSet["EventTTL"] = struct{}{} } + var enabledList []string + for k := range enabledSet { enabledList = append(enabledList, k) } + patchData := map[string]interface{}{ + "spec": map[string]interface{}{ + "featureSet": "CustomNoUpgrade", + "customNoUpgrade": map[string]interface{}{ + "enabled": enabledList, + "disabled": featureGate.Spec.CustomNoUpgrade.Disabled, + }, + }, + } @@ - _, err = configClient.ConfigV1().FeatureGates().Patch(ctx, "cluster", types.MergePatchType, patchBytes, metav1.PatchOptions{}) + _, err = configClient.ConfigV1().FeatureGates().Patch(ctx, "cluster", types.MergePatchType, patchBytes, metav1.PatchOptions{}) o.Expect(err).NotTo(o.HaveOccurred()) + featureGatePatched = true @@ - defer func() { + defer func() { g.By(fmt.Sprintf("\nCleaning up eventTTLMinutes=%d configuration", ttl)) @@ } + + // Restore FeatureGate if changed + if featureGatePatched && len(originalFeatureGateSpecJSON) > 0 { + g.By(" Restoring original FeatureGate spec") + if _, err := configClient.ConfigV1().FeatureGates().Patch(ctx, "cluster", types.MergePatchType, originalFeatureGateSpecJSON, metav1.PatchOptions{}); err != nil { + g.By(fmt.Sprintf(" Warning: failed to restore FeatureGate: %v", err)) + } else { + _ = waitForAPIServerRollout(ctx, kubeClient, 20*time.Minute) + } + } }()Also applies to: 81-114, 120-152
🧹 Nitpick comments (3)
Dockerfile.rhel7 (1)
7-7: Harden gzip for reproducibility and idempotencyUse -n (omit timestamp), -9 (max compression), -f (overwrite).
- && gzip test/extended/tests-extension/cluster-kube-apiserver-operator-tests-ext + && gzip -n -9 -f test/extended/tests-extension/cluster-kube-apiserver-operator-tests-exttest/extended/tests-extension/event_ttl.go (2)
465-517: Parse YAML instead of substring search for config.yamlAvoid false positives; inspect apiServerArguments.event-ttl value.
- // Check if event-ttl is in the config - hasEventTTL := strings.Contains(configData, "event-ttl") - ... - hasCorrectValue := strings.Contains(configData, fmt.Sprintf("- %s", expectedTTL)) || - strings.Contains(configData, fmt.Sprintf("\"%s\"", expectedTTL)) || - strings.Contains(configData, fmt.Sprintf("'%s'", expectedTTL)) + type apiserverConfig struct { + APIServerArguments map[string][]string `json:"apiServerArguments" yaml:"apiServerArguments"` + } + var cfg apiserverConfig + o.Expect(yaml.Unmarshal([]byte(configData), &cfg)).NotTo(o.HaveOccurred(), "failed to parse config.yaml") + vals := cfg.APIServerArguments["event-ttl"] + o.Expect(vals).NotTo(o.BeEmpty(), "event-ttl not found in apiServerArguments") + hasCorrectValue := false + for _, v := range vals { if v == expectedTTL { hasCorrectValue = true; break } }Note: add import "sigs.k8s.io/yaml".
262-296: Optional: poll for deletion instead of fixed sleep to reduce test time and flakesPoll until NotFound with a max timeout (TTL + buffer), rather than sleeping the full duration.
- time.Sleep(waitDuration) - - // Verify event is deleted - actualExpirationTime := time.Now() - _, err = kubeClient.CoreV1().Events(testNamespace).Get(ctx, eventName, metav1.GetOptions{}) + var actualExpirationTime time.Time + _ = wait.PollUntilContextTimeout(ctx, 10*time.Second, waitDuration, false, func(_ context.Context) (bool, error) { + _, err := kubeClient.CoreV1().Events(testNamespace).Get(ctx, eventName, metav1.GetOptions{}) + if apierrors.IsNotFound(err) { actualExpirationTime = time.Now(); return true, nil } + return false, nil + }) + _, err = kubeClient.CoreV1().Events(testNamespace).Get(ctx, eventName, metav1.GetOptions{})Requires apierrors import (already suggested).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting
⛔ Files ignored due to path filters (132)
go.sumis excluded by!**/*.sumtest/extended/tests-extension/go.sumis excluded by!**/*.sumvendor/github.com/go-task/slim-sprig/v3/.editorconfigis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/.gitattributesis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/.gitignoreis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/CHANGELOG.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/LICENSE.txtis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/README.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/Taskfile.ymlis excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/crypto.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/date.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/defaults.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/dict.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/doc.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/functions.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/list.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/network.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/numeric.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/reflect.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/regex.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/strings.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/go-task/slim-sprig/v3/url.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/.gitignoreis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/CHANGELOG.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/CONTRIBUTING.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/LICENSEis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/Makefileis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/OWNERSis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/README.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/RELEASING.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/config/deprecated.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/core_dsl.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/core_dsl_patch.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/decorator_dsl.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/deprecated_dsl.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/formatter/colorable_others.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/formatter/colorable_windows.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/formatter/formatter.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/build/build_command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/command/abort.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/command/command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/command/program.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/boostrap_templates.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_templates.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/compile.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/gocovmerge.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/profiles_and_reports.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/test_suite.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/utils.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/verify_version.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/labels/labels_command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/main.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/import.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline_command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/run/run_command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/unfocus/unfocus_command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/delta_tracker.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/dependencies.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hash.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/package_hashes.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/suite.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo/watch/watch_command.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo_cli_dependencies.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/ginkgo_t_dsl.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/counter.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/failer.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/focus.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/global/init.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/group.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/interrupt_handler.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_unix.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/interrupt_handler/sigquit_swallower_windows.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/node.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/ordering.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/output_interceptor.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/output_interceptor_unix.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/output_interceptor_wasm.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/output_interceptor_win.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/client_server.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_client.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/http_server.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_client.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/rpc_server.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/parallel_support/server_handler.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/progress_report.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/progress_report_bsd.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/progress_report_unix.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/progress_report_wasm.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/progress_report_win.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/progress_reporter_manager.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/report_entry.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/spec.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/spec_context.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/spec_patch.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/suite.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/suite_patch.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/testingtproxy/testing_t_proxy.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/tree.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/internal/writer.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/reporters/default_reporter.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/reporters/deprecated_reporter.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/reporters/json_report.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/reporters/junit_report.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/reporters/reporter.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/reporters/teamcity_report.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/reporting_dsl.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/table_dsl.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/code_location.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/config.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/deprecated_types.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/deprecation_support.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/enum_support.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/errors.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/file_filter.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/flags.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/label_filter.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/report_entry.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/types.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/types_patch.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/ginkgo/v2/types/version.gois excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/.gitignoreis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/CHANGELOG.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/CONTRIBUTING.mdis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/LICENSEis excluded by!vendor/**,!**/vendor/**vendor/github.com/onsi/gomega/README.mdis excluded by!vendor/**,!**/vendor/**
📒 Files selected for processing (8)
.gitignore(1 hunks)Dockerfile.rhel7(2 hunks)Makefile(3 hunks)go.mod(0 hunks)test/extended/tests-extension/README.md(2 hunks)test/extended/tests-extension/cmd/main.go(1 hunks)test/extended/tests-extension/event_ttl.go(1 hunks)test/extended/tests-extension/go.mod(1 hunks)
💤 Files with no reviewable changes (1)
- go.mod
🚧 Files skipped from review as they are similar to previous changes (3)
- Makefile
- test/extended/tests-extension/README.md
- .gitignore
🔇 Additional comments (3)
test/extended/tests-extension/cmd/main.go (1)
23-23: LGTM: side-effect import path update is correctThe new registration path matches the relocated tests-extension module.
Dockerfile.rhel7 (1)
16-16: LGTM: updated COPY matches new artifact locationPath aligns with the build-stage gzip output.
test/extended/tests-extension/go.mod (1)
72-74: Ginkgo OpenShift fork replace is intentional and compatibleThe replace directive is explicit in go.mod and Ginkgo v2.6.1 is confirmed compatible with Gomega v1.36.1. This follows the standard OpenShift fork pattern used in test-extension modules.
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.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting
⛔ Files ignored due to path filters (1)
test/extended/tests-extension/go.sumis excluded by!**/*.sum
📒 Files selected for processing (1)
test/extended/tests-extension/go.mod(1 hunks)
🔇 Additional comments (3)
test/extended/tests-extension/go.mod (3)
8-8: No issues found—the code is correctly configured.Verification confirms:
- The openshift/client-go pseudo-version (v0.0.0-20251015124057-db0dee36e235) is valid and recent (Oct 15, 2025)
- The fake version (v0.0.0-00010101000000-000000000000) follows Go module conventions for local replacements
- The replace directive path (../../..) correctly resolves from test/extended/tests-extension/ to the root module
3-3: Consider upgrading from Go 1.24.0 to a current patch version for security improvements.Kubernetes v1.34 was built with Go 1.24.5, confirming ecosystem compatibility with Go 1.24. However, Go 1.24.8 and 1.24.9 include important security fixes released since the initial 1.24.0 in February 2025. Maintaining Go 1.24.0 forgoes months of accumulated security patches. Consider updating the go.mod to use the latest stable patch version (currently 1.24.9 as of October 2025) to ensure the test extension benefits from security improvements while maintaining compatibility with the broader Kubernetes ecosystem.
12-14: No changes required. The Kubernetes and OpenShift dependencies are correctly pinned and compatible.The k8s.io/client-go v0.34.1 corresponds to Kubernetes v1.34.1, released September 9, 2025—it is not a "future version" as claimed, but rather a current stable release. Additionally, the root module (
go.mod) already pins identical k8s.io packages to v0.34.1:
- k8s.io/api v0.34.1
- k8s.io/apimachinery v0.34.1
- k8s.io/client-go v0.34.1
- (and others at v0.34.1)
The test module's dependencies are synchronized with the root module, and both use the same OpenShift client-go pseudo-version (v0.0.0-20251015124057-db0dee36e235). The test extension is self-contained and explicitly configured to run against OpenShift clusters via its isolated test runner; no version conflict exists with the root module.
Likely an incorrect or invalid review comment.
| replace ( | ||
| github.com/onsi/ginkgo/v2 => github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20250416174521-4eb003743b54 | ||
| github.com/openshift/cluster-kube-apiserver-operator => ../../.. | ||
| ) |
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.
🧩 Analysis chain
Verify OpenShift ginkgo fork is actively maintained.
The replace directive uses an OpenShift fork of ginkgo/v2 (v2.6.1-0.20250416174521-4eb003743b54) instead of the upstream version (v2.22.0). Verify that:
- This fork is actively maintained by OpenShift
- The version is recent and includes security patches
- The fork is compatible with the upstream version v2.22.0 in terms of API surface
🌐 Web query:
What is the status and maintenance of the OpenShift onsi-ginkgo fork?
💡 Result:
Short answer: OpenShift does not maintain a separate, actively maintained "onsi-ginkgo" fork — OpenShift projects use the upstream onsi/ginkgo (v2) releases and keep their Go module dependencies updated in their repos. See the upstream onsi/ginkgo repository/releases and OpenShift docs showing they depend on github.com/onsi/ginkgo/v2. [1][2]
Sources:
- onsi/ginkgo (upstream) — repo & releases. [1]
- OpenShift documentation (operators/dev guide) showing dependency on github.com/onsi/ginkgo/v2. [2]
The web search result suggests OpenShift uses upstream ginkgo, but this conflicts with the code showing an explicit OpenShift fork URL. Let me search more specifically for this fork's existence and purpose.
[/web_search]
🌐 Web query:
github.com/openshift/onsi-ginkgo repository fork maintenance
💡 Result:
Short answer: There is no public GitHub repository named openshift/onsi-ginkgo that is kept up-to-date. OpenShift projects generally consume and sometimes vendor or patch onsi/ginkgo inside their own repos (e.g. a forked copy appears in OpenShift Origin/Godeps or as a versioned module like github.com/onsi/ginkgo@...-origin), but the canonical upstream maintained project is onsi/ginkgo. Use the upstream onsi/ginkgo for the official, actively maintained source and treat any OpenShift-internal copies as vendored/patched for OpenShift's own needs. [1][2]
Sources
- onsi/ginkgo (official upstream repo). [1]
- Examples showing OpenShift/origin using a vendored/modified ginkgo (versioned with “-origin” / present in Godeps). [2]
The web search results indicate that the openshift/onsi-ginkgo fork referenced in the go.mod file does not appear to exist as a public, actively maintained repository. Let me search for context about why this specific fork reference was chosen and whether there are known issues with upstream ginkgo that necessitate it.
[/web_search]
🌐 Web query:
OpenShift ginkgo fork why use instead upstream v2
💡 Result:
Short answer — because switching to upstream Ginkgo v2 changes test behaviour and tooling in ways that would break or require large work to migrate OpenShift’s long-standing test suites, CI/reporting, and In‑tree test tooling. Typical reasons OpenShift (and related projects) keep a fork or pinned version instead of moving to upstream v2 are:
- Compatibility with existing tests and helpers: OpenShift has many extended/e2e/extended-style tests and custom test helpers that rely on Ginkgo v1 semantics and reporters; moving to v2 can change lifecycle hooks, reporting and output behaviour. [1][2]
- Test-reporting and JUnit/AfterSuite/parallel semantics: Ginkgo v2 changed how reports/AfterSuite and parallel test output are handled (and introduced new decorators like Serial). That can break CI pipelines that merge JUnit files or rely on previous ordering/aggregation. Projects often delay migration until tooling and CI are adapted. [1]
- Custom patches / vendoring and stability: Large downstream projects often vendor a fork to apply fixes, add integration with their test infrastructure, or pin behavior for stable releases rather than constantly chasing upstream API/behavior changes. [2]
- Migration cost/risk: The effort to update thousands of tests, update CI, and revalidate flakes/regressions is non‑trivial; teams prefer a staged migration or keep a maintained fork until upstream fits their needs.
If you want, I can:
- look up specific OpenShift issues/PRs that discuss a Ginkgo fork or migration plan, or
- find concrete examples of behavior changes between Ginkgo v1 and v2 (Serial, reporting, AfterSuite) and how they affect e2e pipelines.
Sources:
- discussion about Ginkgo V2 Serial / reporting changes (kubevirt dev thread). [1]
- OpenShift repositories vendoring/depending on specific Ginkgo versions (Godeps / origin references). [2]
The OpenShift onsi-ginkgo fork referenced in the go.mod cannot be verified—the repository does not appear to exist publicly.
Web searches confirm there is no public GitHub repository named openshift/onsi-ginkgo that is kept up-to-date or actively maintained. The go.mod file references github.com/openshift/onsi-ginkgo/v2 with a commit hash, but this URL is unverifiable.
While OpenShift projects typically vendor or patch ginkgo internally and may keep a fork to maintain compatibility with existing tests, custom patches, and CI pipelines rather than upgrading to upstream v2, the specific fork URL in this go.mod file should either:
- Be verified to exist (including whether it's private and accessible)
- Be updated to reference the actual upstream
github.com/onsi/ginkgo/v2or the correct OpenShift-maintained version
This is a dependency resolution risk and requires clarification before merging.
🤖 Prompt for AI Agents
In test/extended/tests-extension/go.mod around lines 74-77 the replace directive
points to github.com/openshift/onsi-ginkgo/v2 which is not publicly verifiable;
update this dependency to a verifiable source by either changing the replace to
the upstream module (github.com/onsi/ginkgo/v2) at the intended tag/commit
(e.g., v2.6.1) or, if an OpenShift-private fork is intended, replace it with a
correct accessible URL or a local path vendored copy and document access, then
run go mod tidy to refresh go.sum and ensure builds resolve.
Remove k8s.io replace directives from test module to resolve version conflicts. The test module should not override k8s.io versions with replace directives as this creates conflicts between: - k8s.io/apimachinery v0.34.1 (required by dependencies) - Forced downgrade to v0.33.2 (via replace) - sigs.k8s.io/structured-merge-diff v4 vs v6 incompatibility By removing k8s.io replaces and keeping only the necessary ones (ginkgo and cluster-kube-apiserver-operator local replace), the test module can naturally resolve to compatible versions. This allows the CI build to succeed without vendoring in the test module.
649261f to
49786fc
Compare
| @@ -0,0 +1,29 @@ | |||
| reviewers: | |||
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.
you may want to check this list again, many of those folks are not even at the company anymore. Some of them are now working for different components or other orgs within redhat.
70bf2be to
99417b7
Compare
99417b7 to
53dee2d
Compare
- Updated all binary paths to use test/extended/tests-extension/bin/ - Added Test Module Structure section explaining dependency isolation - Added Makefile and bin/ directory to structure diagram - Clarified build output location in Makefile commands table - Updated all command examples to reference bin/ subdirectory - Fixed metadata file path in note
|
@gangwgr: The following tests failed, say
Full PR test history. Your PR dashboard. 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. I understand the commands that are listed here. |
|
Could you open another PR focused on splitting out the extension test module, please? If any issues come up in the CI signal later, it would be easier to distinguish the effects of the boilerplate changes from the reliability of the event-ttl test itself. |
Adding test cases for "Add support for event-ttl in Kube API Server Operator"
Create separate Go module for test extension
Move test code and metadata to tests-extension directory
Update build infrastructure for isolated test module
Remove test dependencies from root go.mod
Assisted-by: Claude
test run