Skip to content

Conversation

@JianLi-RH
Copy link
Contributor

@JianLi-RH JianLi-RH commented Oct 22, 2025

In this PR, I finished 2 things:

  1. MustGetKubeClient() function helps us create an Clientset instance which can be used to connect to cluster.
  2. Update the README.md file, we can follow it to create our e2e test cases.

/cc @wking @DavidHurta @PratikMahajan @hongkailiu @jhou1 @dis016 @jiajliu

@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Oct 22, 2025
@JianLi-RH JianLi-RH force-pushed the implement_client_oc branch 4 times, most recently from a6a8ada to 37caff4 Compare October 23, 2025 07:39
@JianLi-RH JianLi-RH changed the title [WIP] Create packages for connecting to a cluster [WIP] OTA-1605 Create e2e test in CVO repo Oct 23, 2025
@JianLi-RH JianLi-RH force-pushed the implement_client_oc branch 4 times, most recently from a79646a to d944000 Compare October 23, 2025 09:32
@DavidHurta
Copy link
Contributor

Please note that @petr-muller is no longer an active reviewer or approver in the CVO repository. You can refrain from directly pinging him on usual PRs where the expertise of the active folks should be sufficient. He is an "emeritus" approver.

What's an emeritus approver?

GitHub usernames listed under the emeritus_approvers key can no longer approve code (use the /approve command) and will be ignored by prow for assignment. However, it can still be referenced by a person looking at the OWNERS file for a possible second or more informed opinion.

@JianLi-RH JianLi-RH force-pushed the implement_client_oc branch from d944000 to 067cd4b Compare October 24, 2025 07:45
@JianLi-RH JianLi-RH changed the title [WIP] OTA-1605 Create e2e test in CVO repo [WIP] NO-ISSUE: OTA-1605 Create e2e test in CVO repo Oct 24, 2025
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Oct 24, 2025
@openshift-ci-robot
Copy link
Contributor

@JianLi-RH: This pull request explicitly references no jira issue.

In response to this:

In this PR, finished first e2e test case. Related case can be found in: https://polarion.engineering.redhat.com/polarion/#/project/OSE/workitem?id=OCP-42543

Tested it locally:

[jianl@jianl-thinkpadt14gen4 cvo]$ ginkgo --label-filter="42543"
Running Suite: CVO Suite - /home/jianl/1_code/cluster-version-operator/test/cvo
===============================================================================
Random Seed: 1761205561

Will run 1 of 1 specs
•

Ran 1 of 1 Specs in 1.585 seconds
SUCCESS! -- 1 Passed | 0 Failed | 0 Pending | 0 Skipped
PASS

Ginkgo ran 1 suite in 3.310067262s
Test Suite Passed
[jianl@jianl-thinkpadt14gen4 cvo]$ 

Here is the old case in openshift-tests-private: https://github.com/openshift/openshift-tests-private/blob/f1f32ff1e9eeef4cb2cb4ae6549daa9306c6616a/test/extended/ota/cvo/cvo.go#L716-L759

There is a disadvantage here when I use client-go, it seems client-go does not support all Openshift features, for example it could not extract all manifest and it although support methods to get all resource types but it could not use these types to get resources. So in these PR I don't check all manifests but check some special objects.

Another work we need to do is improve the make update command to let it can embed all tests into binary.

/cc @petr-muller @DavidHurta @hongkailiu @wking @jhou1 @dis016 @jiajliu

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 openshift-eng/jira-lifecycle-plugin repository.

@JianLi-RH
Copy link
Contributor Author

Tested on my local machine:

[jianl@jianl-thinkpadt14gen4 cluster-version-operator]$ make update
hack/build-go.sh
Using version from git...
Building binaries into _output/linux/amd64
Building github.com/openshift/cluster-version-operator cluster-version-operator-tests binary (v1.0.0-1482-gd9440005-dirty)
Compressing the cluster-version-operator-tests binary
Building github.com/openshift/cluster-version-operator cluster-version-operator binary (v1.0.0-1482-gd9440005-dirty)
hack/update-test-metadata.sh
Using version from git...
successfully updated metadata
[jianl@jianl-thinkpadt14gen4 cluster-version-operator]$ _output/linux/amd64/cluster-version-operator-tests list
[
  {
    "name": "[Jira:Cluster Version Operator] cluster-version-operator-tests Author:jianl-High-42543-the removed resources are not created in a fresh installed cluster",
    "labels": {
      "42543": {},
      "High": {},
      "cvo": {}
    },
    "resources": {
      "isolation": {}
    },
    "source": "openshift:payload:cluster-version-operator",
    "lifecycle": "blocking",
    "environmentSelector": {}
  }
]
[jianl@jianl-thinkpadt14gen4 cluster-version-operator]$

Run the case:

[jianl@jianl-thinkpadt14gen4 cluster-version-operator]$ _output/linux/amd64/cluster-version-operator-tests list | jq '.[] | select(.name | contains("42543")) | .name' | xargs _output/linux/amd64/cluster-version-operator-tests run-test
  Running Suite:  - /home/jianl/1_code/cluster-version-operator
  =============================================================
  Random Seed: 1761292866 - will randomize all specs

  Will run 1 of 1 specs
  ------------------------------
  [Jira:Cluster Version Operator] cluster-version-operator-tests Author:jianl-High-42543-the removed resources are not created in a fresh installed cluster [cvo, High, 42543]
  /home/jianl/1_code/cluster-version-operator/test/cvo/cvo.go:22
    STEP: Validate resource with 'release.openshift.io/delete: "true"' annotation is not installed @ 10/24/25 16:01:06.862
  • [1.428 seconds]
  ------------------------------

  Ran 1 of 1 Specs in 1.428 seconds
  SUCCESS! -- 1 Passed | 0 Failed | 0 Pending | 0 Skipped
[
  {
    "name": "[Jira:Cluster Version Operator] cluster-version-operator-tests Author:jianl-High-42543-the removed resources are not created in a fresh installed cluster",
    "lifecycle": "blocking",
    "duration": 1428,
    "startTime": "2025-10-24 08:01:06.858981 UTC",
    "endTime": "2025-10-24 08:01:08.287644 UTC",
    "result": "passed",
    "output": "  STEP: Validate resource with 'release.openshift.io/delete: \"true\"' annotation is not installed @ 10/24/25 16:01:06.862\n"
  }
]
[jianl@jianl-thinkpadt14gen4 cluster-version-operator]$ 

@JianLi-RH JianLi-RH changed the title [WIP] NO-ISSUE: OTA-1605 Create e2e test in CVO repo NO-ISSUE: OTA-1605 Create e2e test in CVO repo Oct 24, 2025
@openshift-ci openshift-ci bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Oct 24, 2025
@JianLi-RH JianLi-RH force-pushed the implement_client_oc branch from 067cd4b to bac7793 Compare October 27, 2025 03:16
@DavidHurta
Copy link
Contributor

/cc

@openshift-ci openshift-ci bot requested a review from DavidHurta October 27, 2025 11:41
@hongkailiu
Copy link
Member

/cc

@openshift-ci openshift-ci bot requested a review from hongkailiu October 27, 2025 13:04
@JianLi-RH JianLi-RH force-pushed the implement_client_oc branch from bac7793 to 51facb3 Compare October 29, 2025 04:06
@JianLi-RH
Copy link
Contributor Author

After modification, the case is still valid:

[jianl@jianl-thinkpadt14gen4 cluster-version-operator]$ _output/linux/amd64/cluster-version-operator-tests run-test "[Jira:Cluster Version Operator] The cluster version operator the removed resources are not created in a fresh installed cluster"
  Running Suite:  - /home/jianl/1_code/cluster-version-operator
  =============================================================
  Random Seed: 1761710868 - will randomize all specs

  Will run 1 of 1 specs
  ------------------------------
  [Jira:Cluster Version Operator] The cluster version operator the removed resources are not created in a fresh installed cluster [cvo, High, 42543]
  /home/jianl/1_code/cluster-version-operator/test/cvo/cvo.go:24
    STEP: Service controller-manager-service should not be installed @ 10/29/25 12:07:48.862
    STEP: ClusterRoleBindings default-account-openshift-machine-config-operator should not be installed @ 10/29/25 12:07:49.759
    STEP: CronJobs machine-config-nodes-crd-cleanup should not be installed @ 10/29/25 12:07:50.024
  • [1.428 seconds]
  ------------------------------

  Ran 1 of 1 Specs in 1.429 seconds
  SUCCESS! -- 1 Passed | 0 Failed | 0 Pending | 0 Skipped
[
  {
    "name": "[Jira:Cluster Version Operator] The cluster version operator the removed resources are not created in a fresh installed cluster",
    "lifecycle": "blocking",
    "duration": 1429,
    "startTime": "2025-10-29 04:07:48.858522 UTC",
    "endTime": "2025-10-29 04:07:50.287978 UTC",
    "result": "passed",
    "output": "  STEP: Service controller-manager-service should not be installed @ 10/29/25 12:07:48.862\n  STEP: ClusterRoleBindings default-account-openshift-machine-config-operator should not be installed @ 10/29/25 12:07:49.759\n  STEP: CronJobs machine-config-nodes-crd-cleanup should not be installed @ 10/29/25 12:07:50.024\n"
  }
]
[jianl@jianl-thinkpadt14gen4 cluster-version-operator]$ 

@JianLi-RH JianLi-RH force-pushed the implement_client_oc branch from 51facb3 to 8e9da36 Compare October 30, 2025 01:44
@JianLi-RH JianLi-RH force-pushed the implement_client_oc branch 2 times, most recently from 72354d1 to 3a8631b Compare November 4, 2025 01:46
@DavidHurta
Copy link
Contributor

/hold
I want to investigate more about client-go.

@JianLi-RH, in case the investigation is not done by the end of the day, you want simply more time to explore or are simply busy, I would like to ask you whether it would be fine for me to rename the sanity test in a separate PR. That specific change is blocking my openshift/origin#30316 PR. I am happy to rename the test personally in a separate PR to unblock my PR while we are working on this one.

@JianLi-RH
Copy link
Contributor Author

@DavidHurta of course, go ahead, please. I am not sure when I can finish the investigation.

@JianLi-RH JianLi-RH force-pushed the implement_client_oc branch 6 times, most recently from d2300d9 to f98e03c Compare November 13, 2025 09:10
@JianLi-RH
Copy link
Contributor Author

/unhold

@openshift-ci openshift-ci bot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Nov 13, 2025
Clientset instance can be used to connect to cluster, manage resources.
With clientset instance we can evaluate the posibility of moving other tests to OTE.

The reference to OTE framework:
https://docs.google.com/document/d/1cFZj9QdzW8hbHc3H0Nce-2xrJMtpDJrwAse9H7hLiWk/edit?tab=t.0#heading=h.8cf3f4eii1q8
@JianLi-RH JianLi-RH force-pushed the implement_client_oc branch 4 times, most recently from 878cbc8 to 1102f9c Compare November 17, 2025 04:26
@JianLi-RH
Copy link
Contributor Author

@hongkailiu @DavidHurta Just added back the case, please help review it. Here is the output:

$ ginkgo ./test/...
Running Suite: CVO Suite - /home/jianl/1_code/cluster-version-operator/test/cvo
===============================================================================
Random Seed: 1763353592

Will run 2 of 2 specs
•Test Authentication...
namespace: default
        - Test services...
        - Test RoleBinding...
        - Test CronJob...
success
namespace: kube-node-lease
        - Test services...
        - Test RoleBinding...
        - Test CronJob...
success
namespace: kube-public
        - Test services...
        - Test RoleBinding...
        - Test CronJob...
.....................
namespace: openshift-vsphere-infra
        - Test services...
        - Test RoleBinding...
        - Test CronJob...
success


Ran 2 of 2 Specs in 82.840 seconds
SUCCESS! -- 2 Passed | 0 Failed | 0 Pending | 0 Skipped
PASS

Ginkgo ran 1 suite in 1m24.112444203s
Test Suite Passed

If there is resource installed unexpected, it will fail as below:

$ ginkgo ./test/...
Running Suite: CVO Suite - /home/jianl/1_code/cluster-version-operator/test/cvo
===============================================================================
Random Seed: 1763353871

Will run 2 of 2 specs
•Test Authentication...

------------------------------
• [FAILED] [1.522 seconds]
[Jira:"Cluster Version Operator"] cluster-version-operator-tests [It] should not install resources annotated with release.openshift.io/delete=true [cvo, Conformance, High, 42543]
/home/jianl/1_code/cluster-version-operator/test/cvo/cvo.go:20

  [FAILED] Unexpectedly installed authentication cluster which has release.openshift.io/delete annotation
  Expected
      <bool>: true
  not to be true
  In [It] at: /home/jianl/1_code/cluster-version-operator/test/cvo/cvo.go:30 @ 11/17/25 12:31:14.175
------------------------------

Summarizing 1 Failure:
  [FAIL] [Jira:"Cluster Version Operator"] cluster-version-operator-tests [It] should not install resources annotated with release.openshift.io/delete=true [cvo, Conformance, High, 42543]
  /home/jianl/1_code/cluster-version-operator/test/cvo/cvo.go:30

Ran 2 of 2 Specs in 1.522 seconds
FAIL! -- 1 Passed | 1 Failed | 0 Pending | 0 Skipped
--- FAIL: TestGinkgo (1.52s)
FAIL

Ginkgo ran 1 suite in 2.725034617s

Test Suite Failed

@JianLi-RH JianLi-RH changed the title NO-ISSUE: OTA-1605 Create MustGetKubeClient function for e2e test NO-ISSUE: OTA-1605 Create first e2e test OCP-42543 Nov 17, 2025
…ient-go

openshift/client-go gives us more efficient functions to access cluster
After adding new OTE test cases, we need to update test metadata
@coderabbitai
Copy link

coderabbitai bot commented Nov 24, 2025

Walkthrough

Adds a new CVO test entry and implements a Ginkgo v2 CVO test suite that validates resources annotated release.openshift.io/delete=true are not installed, plus new Kubernetes/OpenShift client helper functions used by tests.

Changes

Cohort / File(s) Summary
Test configuration
.openshift-tests-extension/openshift_payload_cluster-version-operator.json
Adds a new test case entry named "[Jira:Cluster Version Operator] The cluster version operator should not install resources annotated with release.openshift.io/delete=true" with labels (42543, Conformance, High, cvo) and isolation set.
CVO test suite
test/cvo/cvo.go
Replaces dot-imports with explicit aliased imports and migrates to Ginkgo v2 Describe/It/BeforeAll scaffolding. Adds substantial test logic that iterates namespaces and verifies annotated resources (Services, RoleBindings, CronJobs, Deployments, PodDisruptionBudgets, etc.) are not installed when annotated release.openshift.io/delete=true. Introduces exported helpers CreateServiceAccount(...) and DeleteServiceAccount(...), token handling, polling/wait logic, and expanded error handling.
Test utilities
test/utilities/connection.go
New utilities to construct Kubernetes and OpenShift ConfigV1 clients from KUBECONFIG: private helpers that build rest.Config and clients, plus public panic-on-failure wrappers MustGetKubeClient() and MustGetV1Client(). Errors wrapped with contextual messages and explicit handling when KUBECONFIG is absent.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Inspect namespace/resource iteration and annotation checks in test/cvo/cvo.go for correctness and edge cases (NotFound handling, resource type mapping).
  • Review CreateServiceAccount / DeleteServiceAccount for RBAC binding correctness, TokenRequest usage, and cleanup semantics.
  • Verify polling/wait logic for pod readiness and PDB/Deployment interactions for flakiness risks.
  • Validate test/utilities/connection.go behavior when KUBECONFIG is missing and client construction error contexts.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between df1ec55 and 946876f.

📒 Files selected for processing (3)
  • .openshift-tests-extension/openshift_payload_cluster-version-operator.json (1 hunks)
  • test/cvo/cvo.go (1 hunks)
  • test/utilities/connection.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

-Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.

Files:

  • test/utilities/connection.go
  • test/cvo/cvo.go
🧬 Code graph analysis (1)
test/cvo/cvo.go (1)
test/utilities/connection.go (2)
  • MustGetV1Client (65-71)
  • MustGetKubeClient (56-62)
🔇 Additional comments (3)
.openshift-tests-extension/openshift_payload_cluster-version-operator.json (1)

13-25: New test metadata entry looks consistent and well-formed

The added metadata object matches the existing structure (name, labels, resources, source, lifecycle, environmentSelector) and aligns with the new CVO test and label conventions (42543, Conformance, High, cvo). No issues from a structure/maintainability perspective.

test/utilities/connection.go (1)

1-71: Client helper utilities are correct and suitable for test usage

KUBECONFIG handling, config construction, clientset creation, and error wrapping all look correct. The MustGet* helpers are appropriate for tests where a missing/invalid config should hard-fail immediately. No blocking issues here.

test/cvo/cvo.go (1)

17-21: Sanity test Describe block is fine

The lightweight [Jira:"Cluster Version Operator"] cluster-version-operator-tests sanity spec is simple and effective as an integration signal. No issues.

Comment on lines +23 to +82
var _ = g.Describe("[Jira:Cluster Version Operator] The cluster version operator", g.Ordered, g.Label("cvo"), func() {
defer g.GinkgoRecover()
var client *v1.ConfigV1Client
var kubeclient *kubernetes.Clientset

g.BeforeAll(func() {
client = utilities.MustGetV1Client()
kubeclient = utilities.MustGetKubeClient()
})

g.It(`should not install resources annotated with release.openshift.io/delete=true`, g.Label("Conformance", "High", "42543"), func() {
annotation := "release.openshift.io/delete"

auths, err := client.Authentications().List(context.TODO(), metav1.ListOptions{})
o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing authentications")

g.By(fmt.Sprintf("checking if authentication with %s annotation exists", annotation))
for _, auth := range auths.Items {
if _, ok := auth.Annotations[annotation]; ok {
o.Expect(ok).NotTo(o.BeTrue(), fmt.Sprintf("Unexpectedly installed authentication %s which has '%s' annotation", auth.Name, annotation))
}
}

namespaces, err := kubeclient.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing namespaces")

g.By(fmt.Sprintf("checking if special resources with %s annotation exist in all namespaces", annotation))
for _, ns := range namespaces.Items {
namespace := ns.Name
fmt.Printf("namespace: %s\n", namespace)

fmt.Println(" - Test services...")
services, err := kubeclient.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{})
o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing services")
for _, service := range services.Items {
if _, ok := service.Annotations[annotation]; ok {
o.Expect(ok).NotTo(o.BeTrue(), fmt.Sprintf("Unexpectedly installed service %s which has '%s' annotation", service.Name, annotation))
}
}

fmt.Println(" - Test RoleBinding...")
rolebindings, err := kubeclient.RbacV1().RoleBindings(namespace).List(context.TODO(), metav1.ListOptions{})
o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing rolebindings")
for _, rb := range rolebindings.Items {
if _, ok := rb.Annotations[annotation]; ok {
o.Expect(ok).NotTo(o.BeTrue(), fmt.Sprintf("Unexpectedly installed RoleBinding %s which has '%s' annotation", rb.Name, annotation))
}
}

fmt.Println(" - Test CronJob...")
cronjobs, err := kubeclient.BatchV1().CronJobs(namespace).List(context.TODO(), metav1.ListOptions{})
o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing cronjobs")
for _, cj := range cronjobs.Items {
if _, ok := cj.Annotations[annotation]; ok {
o.Expect(ok).NotTo(o.BeTrue(), fmt.Sprintf("Unexpectedly installed CronJob %s which has %s annotation", cj.Name, annotation))
}
}

fmt.Println("success")
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Fix List error handling to fail on real API errors, not just NotFound

All the List calls currently do:

auths, err := client.Authentications().List(...)
o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing ...")

This has two problems:

  1. False positives on failures

    • Any non-NotFound error (e.g., auth, RBAC, networking, API server issues) makes kerrors.IsNotFound(err) return false, so the expectation passes.
    • The test then proceeds, typically iterating over an empty/zero-valued list, and can still pass even though the underlying List failed.
    • That means the test can report success while not actually verifying the cluster state.
  2. Misleading messages

    • The description says “The NotFound error should occur when listing …” while the matcher asserts BeFalse(), which is contradictory.

You want the test to fail on any error from those List calls. A more robust pattern is to assert on err directly, for each List:

-		auths, err := client.Authentications().List(context.TODO(), metav1.ListOptions{})
-		o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing authentications")
+		auths, err := client.Authentications().List(context.TODO(), metav1.ListOptions{})
+		o.Expect(err).NotTo(o.HaveOccurred(), "failed to list authentications")

-		namespaces, err := kubeclient.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
-		o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing namespaces")
+		namespaces, err := kubeclient.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
+		o.Expect(err).NotTo(o.HaveOccurred(), "failed to list namespaces")

-			services, err := kubeclient.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{})
-			o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing services")
+			services, err := kubeclient.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{})
+			o.Expect(err).NotTo(o.HaveOccurred(), "failed to list services")

-			rolebindings, err := kubeclient.RbacV1().RoleBindings(namespace).List(context.TODO(), metav1.ListOptions{})
-			o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing rolebindings")
+			rolebindings, err := kubeclient.RbacV1().RoleBindings(namespace).List(context.TODO(), metav1.ListOptions{})
+			o.Expect(err).NotTo(o.HaveOccurred(), "failed to list rolebindings")

-			cronjobs, err := kubeclient.BatchV1().CronJobs(namespace).List(context.TODO(), metav1.ListOptions{})
-			o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing cronjobs")
+			cronjobs, err := kubeclient.BatchV1().CronJobs(namespace).List(context.TODO(), metav1.ListOptions{})
+			o.Expect(err).NotTo(o.HaveOccurred(), "failed to list cronjobs")

This way, any failure to talk to the API (including NotFound on the resource type) will correctly fail the spec instead of silently passing.

If you want to double‑check semantics, please confirm in the Kubernetes/client-go docs (or local experiments) that List should normally return an empty list with err == nil when no items exist, and that kerrors.IsNotFound is intended for handling Get/Delete style operations rather than normal successful Lists.


🌐 Web query:

How does the Kubernetes client-go List API signal errors vs. an empty result, and when is k8s.io/apimachinery/pkg/api/errors.IsNotFound typically used?

💡 Result:

Short answer: List returns an empty list with err == nil (the returned List object's Items slice is just empty). IsNotFound is not used for a successful List — it's used when an operation asking for a specific named resource (e.g., Get or Delete by name) returns HTTP 404; k8s.io/apimachinery/pkg/api/errors.IsNotFound(err) detects that 404 StatusError. Use IsNotFound when you expect a single resource might not exist and you want to handle "resource missing" specially.

References: k8s error helpers (IsNotFound). [1] Client List/Get semantics (List returns list object; Reader/List returns error only on failure). [2] REST practice: collection endpoints return 200 + empty array when no items. [3]

Sources:
[1] k8s.io/apimachinery/pkg/api/errors (IsNotFound description).
[2] sigs.k8s.io/controller-runtime/pkg/client (Client.List / Reader semantics).
[3] RESTful API design discussion (return 200 with empty array for empty collections).


Replace error assertions with proper nil checks for all List calls

The web search confirms your analysis: Kubernetes List operations return an empty list with err == nil on success. The IsNotFound check is not intended for List operations—it detects HTTP 404 on named-resource operations (Get/Delete), not on collection endpoints.

This means the current code has a critical flaw:

o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing ...")

—will pass whenever IsNotFound(err) == false, which includes:

  • Normal success with no items ✓
  • Network/auth/RBAC failures ✗ (silent pass when should fail)

Your proposed fix is correct. Replace all five List error checks in test/cvo/cvo.go (lines 26–27, 41–42, 51–52, 60–61, 68–69) with:

-o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing ...")
+o.Expect(err).NotTo(o.HaveOccurred(), "failed to list ...")

This ensures any API failure is caught, not silently suppressed.

🤖 Prompt for AI Agents
In test/cvo/cvo.go around lines 23 to 82, the List() error checks use
kerrors.IsNotFound(err) which is inappropriate for collection endpoints and can
silently ignore real API errors; replace each usage at lines ~26–27, ~41–42,
~51–52, ~60–61, and ~68–69 with a direct nil-check assertion (i.e., assert err
is nil) so any List failure causes the test to fail; keep the existing
descriptive messages but change the assertion to ensure err == nil.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
test/cvo/cvo.go (1)

88-138: Fix incorrect error handling for all List operations.

As flagged in a previous review, lines 92, 102, 111, 120, and 129 all use kerrors.IsNotFound(err) incorrectly for List operations. This causes the test to silently pass when List calls fail due to network, authentication, or RBAC issues, creating false positives.

Kubernetes List operations return an empty list with err == nil on success. IsNotFound is only appropriate for Get/Delete operations on specific named resources.

Replace all five error checks with direct nil assertions:

 	auths, err := client.Authentications().List(context.TODO(), metav1.ListOptions{})
-	o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing authentications")
+	o.Expect(err).NotTo(o.HaveOccurred(), "failed to list authentications")

 	namespaces, err := kubeclient.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
-	o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing namespaces")
+	o.Expect(err).NotTo(o.HaveOccurred(), "failed to list namespaces")

 		services, err := kubeclient.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{})
-		o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing services")
+		o.Expect(err).NotTo(o.HaveOccurred(), "failed to list services")

 		rolebindings, err := kubeclient.RbacV1().RoleBindings(namespace).List(context.TODO(), metav1.ListOptions{})
-		o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing rolebindings")
+		o.Expect(err).NotTo(o.HaveOccurred(), "failed to list rolebindings")

 		cronjobs, err := kubeclient.BatchV1().CronJobs(namespace).List(context.TODO(), metav1.ListOptions{})
-		o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing cronjobs")
+		o.Expect(err).NotTo(o.HaveOccurred(), "failed to list cronjobs")
📜 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

📥 Commits

Reviewing files that changed from the base of the PR and between 946876f and f9a032b.

📒 Files selected for processing (1)
  • test/cvo/cvo.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

-Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.

Files:

  • test/cvo/cvo.go
🧬 Code graph analysis (1)
test/cvo/cvo.go (1)
test/utilities/connection.go (2)
  • MustGetV1Client (65-71)
  • MustGetKubeClient (56-62)
🔇 Additional comments (4)
test/cvo/cvo.go (4)

3-27: LGTM: Import organization is clean.

The imports are well-organized with named aliases for Ginkgo and Gomega, and local packages appropriately placed at the end.


72-76: LGTM: Sanity test provides integration signal.

This simple test validates the test framework integration and provides a useful signal when troubleshooting OTE issues, as discussed in past reviews.


78-86: LGTM: Test suite setup is correct.

The suite properly uses g.Ordered with BeforeAll to initialize clients once for all tests, and the labels follow OpenShift testing conventions.


234-234: Update CreateServiceAccount call after fixing the helper function.

After applying the fixes to CreateServiceAccount (removing the unused clusterRole parameter), this call needs to be updated. Additionally, the error return value should be checked.

-	token, _ := CreateServiceAccount(kubeclient, "monitorer", "cluster-admin", ns)
+	token, err := CreateServiceAccount(kubeclient, "monitorer", ns)
+	o.Expect(err).NotTo(o.HaveOccurred(), "failed to create service account")

Comment on lines 29 to 70
func CreateServiceAccount(client *kubernetes.Clientset, accountName string, clusterRole string, namespace string) (token string, err error) {

_, err = client.CoreV1().ServiceAccounts(namespace).Get(context.TODO(), accountName, metav1.GetOptions{})

if err == nil {
token, err := client.CoreV1().ServiceAccounts(namespace).CreateToken(context.TODO(), accountName, &authenticationv1.TokenRequest{}, metav1.CreateOptions{})
return token.String(), err
}

account := &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: accountName,
Namespace: namespace,
},
}
_, err = client.CoreV1().ServiceAccounts(namespace).Create(context.TODO(), account, metav1.CreateOptions{})
o.Expect(err).NotTo(o.HaveOccurred())

rb := &rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "admin",
Namespace: namespace,
},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "ClusterRole",
Name: "cluster-admin",
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: accountName,
Namespace: namespace,
},
},
}
_, err = client.RbacV1().ClusterRoleBindings().Create(context.TODO(), rb, metav1.CreateOptions{})
o.Expect(err).NotTo(o.HaveOccurred())

newToken, err := client.CoreV1().ServiceAccounts(namespace).CreateToken(context.TODO(), accountName, &authenticationv1.TokenRequest{}, metav1.CreateOptions{})
return newToken.String(), err
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix multiple issues in CreateServiceAccount helper.

This function has several problems:

  1. Parameter clusterRole is unused – the function always binds to "cluster-admin" (line 55), making the parameter misleading.
  2. Name collision risk – ClusterRoleBinding name is hardcoded as "admin" (line 49), which will cause conflicts if this function is called multiple times with different service accounts.
  3. Invalid namespace field – ClusterRoleBindings are cluster-scoped; setting Namespace (line 50) has no effect and is misleading.
  4. Type errortoken.String() (lines 35, 69) is invalid; TokenRequest doesn't have a String() method. You likely want token.Status.Token.
  5. Inconsistent error handling – Using o.Expect (lines 45, 66) inside a function that returns error mixes panic-on-failure with explicit error returns.

Apply this diff to fix the issues:

-func CreateServiceAccount(client *kubernetes.Clientset, accountName string, clusterRole string, namespace string) (token string, err error) {
+func CreateServiceAccount(client *kubernetes.Clientset, accountName string, namespace string) (token string, err error) {
 
 	_, err = client.CoreV1().ServiceAccounts(namespace).Get(context.TODO(), accountName, metav1.GetOptions{})
 
 	if err == nil {
-		token, err := client.CoreV1().ServiceAccounts(namespace).CreateToken(context.TODO(), accountName, &authenticationv1.TokenRequest{}, metav1.CreateOptions{})
-		return token.String(), err
+		tokenReq, err := client.CoreV1().ServiceAccounts(namespace).CreateToken(context.TODO(), accountName, &authenticationv1.TokenRequest{}, metav1.CreateOptions{})
+		if err != nil {
+			return "", err
+		}
+		return tokenReq.Status.Token, nil
 	}
 
 	account := &corev1.ServiceAccount{
@@ -42,11 +44,13 @@
 		},
 	}
 	_, err = client.CoreV1().ServiceAccounts(namespace).Create(context.TODO(), account, metav1.CreateOptions{})
-	o.Expect(err).NotTo(o.HaveOccurred())
+	if err != nil {
+		return "", err
+	}
 
 	rb := &rbacv1.ClusterRoleBinding{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      "admin",
-			Namespace: namespace,
+			Name: fmt.Sprintf("%s-cluster-admin-binding", accountName),
 		},
 		RoleRef: rbacv1.RoleRef{
 			APIGroup: "rbac.authorization.k8s.io",
@@ -63,8 +67,12 @@
 		},
 	}
 	_, err = client.RbacV1().ClusterRoleBindings().Create(context.TODO(), rb, metav1.CreateOptions{})
-	o.Expect(err).NotTo(o.HaveOccurred())
+	if err != nil {
+		return "", err
+	}
 
-	newToken, err := client.CoreV1().ServiceAccounts(namespace).CreateToken(context.TODO(), accountName, &authenticationv1.TokenRequest{}, metav1.CreateOptions{})
-	return newToken.String(), err
+	tokenReq, err := client.CoreV1().ServiceAccounts(namespace).CreateToken(context.TODO(), accountName, &authenticationv1.TokenRequest{}, metav1.CreateOptions{})
+	if err != nil {
+		return "", err
+	}
+	return tokenReq.Status.Token, nil
 }
🤖 Prompt for AI Agents
In test/cvo/cvo.go around lines 29 to 70, fix CreateServiceAccount so it uses
the clusterRole parameter, returns errors instead of calling o.Expect, avoids
hardcoded/duplicated ClusterRoleBinding names, removes the Namespace field from
the cluster-scoped ClusterRoleBinding metadata, and accesses the token string
via token.Status.Token (not token.String()). Specifically: change the
ClusterRoleBinding RoleRef.Name to use the clusterRole param, generate a unique
binding name (e.g. based on accountName and clusterRole) instead of "admin",
omit the Namespace field from the binding metadata, update both token retrievals
to return token.Status.Token, and replace o.Expect checks with proper error
checks and early returns so the function consistently returns (token, err) on
failure.

Comment on lines 140 to 253
g.It(`Precheck with oc adm upgrade recommend`, g.Label("Conformance", "Low", "70980"), func() {

g.By("create a namespace")
ns := "ns-70980"
tmpNs := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}}
kubeclient.CoreV1().Namespaces().Create(context.TODO(), tmpNs, metav1.CreateOptions{})

defer func() {
kubeclient.CoreV1().Namespaces().Delete(context.TODO(), ns, metav1.DeleteOptions{})
}()

g.By("create a deployment")
deploymentName := "hello-openshift"
containerName := "hello-openshift"
containerImage := "openshift/hello-openshift:invaid"
deployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: deploymentName,
},
Spec: appsv1.DeploymentSpec{
Replicas: ptr.To(int32(2)), // Number of desired replicas
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": containerName,
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": containerName,
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: containerName,
Image: containerImage,
Ports: []corev1.ContainerPort{
{
ContainerPort: 80,
},
},
},
},
},
},
},
}
kubeclient.AppsV1().Deployments(ns).Create(context.TODO(), deployment, metav1.CreateOptions{})

defer func() {
kubeclient.AppsV1().Deployments(ns).Delete(context.TODO(), deploymentName, metav1.DeleteOptions{})
}()

err := wait.Poll(1*time.Minute, 3*time.Minute, func() (bool, error) {
allPods, err := kubeclient.CoreV1().Pods(ns).List(context.TODO(), metav1.ListOptions{})
if err != nil {
log.Fatalf("Error listing pods: %v", err)
}
for _, pod := range allPods.Items {
if pod.Status.Phase == corev1.PodRunning {
return true, errors.New("there are pods running: " + pod.Name)
}
}
return true, nil
})
allPods, _ := kubeclient.CoreV1().Pods(ns).List(context.TODO(), metav1.ListOptions{})
fmt.Printf("there are %v pods\n", len(allPods.Items))
for _, pod := range allPods.Items {
fmt.Printf(" - Pod: %s - %s\n", pod.Name, pod.Status.Phase)
}
o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should not occur")

g.By("create a PodDisruptionBudget")
pdbName := "my-pdb"
pdb := &applyconfigurationspolicyv1.PodDisruptionBudgetApplyConfiguration{
ObjectMetaApplyConfiguration: &clientmetav1.ObjectMetaApplyConfiguration{
Name: &pdbName,
Namespace: &ns,
},
Spec: &applyconfigurationspolicyv1.PodDisruptionBudgetSpecApplyConfiguration{
MaxUnavailable: &intstr.IntOrString{
Type: intstr.Int,
IntVal: 1,
},
},
}
kubeclient.PolicyV1().PodDisruptionBudgets(ns).Apply(context.TODO(), pdb, metav1.ApplyOptions{})

defer func() {
kubeclient.PolicyV1().PodDisruptionBudgets(ns).Delete(context.TODO(), pdbName, metav1.DeleteOptions{})
}()

g.By("wait some minutes, there is a critical issue for PDB")
token, _ := CreateServiceAccount(kubeclient, "monitorer", "cluster-admin", ns)
// TODO: get alert
fmt.Println(token)
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Precheck test has multiple issues and appears incomplete.

This test has several problems:

  1. Typo in image name – Line 154: "openshift/hello-openshift:invaid" should be "invalid".
  2. Confusing poll logic – Lines 194-211: The wait.Poll returns an error when a pod is running (line 201), then expects IsNotFound(err) to be false (line 211). This logic is unclear and appears backwards.
  3. Same List error handling issue – Line 211 uses kerrors.IsNotFound(err) incorrectly (should check err directly).
  4. Fatal error handling – Line 197: log.Fatalf terminates the entire test run; use Gomega assertions instead: o.Expect(err).NotTo(o.HaveOccurred()).
  5. Missing error checks – Lines 145, 188, 227: Resource creation operations don't check for errors.
  6. Incomplete test – Line 235: TODO comment indicates the test is unfinished (no alert fetching logic).
  7. Misleading title – The test is labeled "Precheck with oc adm upgrade recommend" but doesn't perform upgrade recommendations; it tests deployment and PDB behavior.

The test logic needs clarification. What is the intended behavior being tested here? If this is incomplete work for a future test case, consider removing it or marking it as g.Skip() until the implementation is complete.

🤖 Prompt for AI Agents
In test/cvo/cvo.go around lines 140-237: fix the image typo ("invaid" ->
"invalid"); replace all unchecked API calls (Namespaces().Create,
Deployments(ns).Create, Pods/List, PodDisruptionBudgets(ns).Apply and Delete
calls) to capture and assert errors with o.Expect(err).NotTo(o.HaveOccurred())
instead of ignoring them; remove log.Fatalf and use Gomega assertions
(o.Expect(err).NotTo(o.HaveOccurred())) for list errors; correct the wait.Poll
logic so it returns success when the expected condition holds (e.g., wait until
no pods are Running if that’s intended, or wait until pods are Running and
return (true, nil) accordingly) and assert the final err directly (do not use
kerrors.IsNotFound(err) incorrectly); implement or remove the TODO for alert
fetching (or mark the test with g.Skip() and update the test title to reflect
actual behavior) so the test is complete and its intent is clear.

This is the demo to show how we use client-go to automate a test case.
Copy link

@coderabbitai coderabbitai bot left a 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

♻️ Duplicate comments (3)
test/cvo/cvo.go (3)

29-70: Fix multiple critical issues in CreateServiceAccount.

This function has several problems that will cause runtime failures and logical errors:

  1. Lines 35, 69: token.String() is invalid—TokenRequest doesn't have a String() method. Use token.Status.Token to access the token string.
  2. Line 55: The clusterRole parameter is unused; the function always binds to "cluster-admin". Either use the parameter or remove it.
  3. Line 50: Namespace field on ClusterRoleBinding is invalid—ClusterRoleBindings are cluster-scoped resources and don't have a namespace.
  4. Lines 45, 66: Using o.Expect (which panics on failure) inside a function that returns error creates inconsistent error handling.

Apply this diff to fix these issues:

 func CreateServiceAccount(client *kubernetes.Clientset, accountName string, clusterRole string, namespace string) (token string, err error) {
 
 	_, err = client.CoreV1().ServiceAccounts(namespace).Get(context.TODO(), accountName, metav1.GetOptions{})
 
 	if err == nil {
-		token, err := client.CoreV1().ServiceAccounts(namespace).CreateToken(context.TODO(), accountName, &authenticationv1.TokenRequest{}, metav1.CreateOptions{})
-		return token.String(), err
+		tokenReq, err := client.CoreV1().ServiceAccounts(namespace).CreateToken(context.TODO(), accountName, &authenticationv1.TokenRequest{}, metav1.CreateOptions{})
+		if err != nil {
+			return "", err
+		}
+		return tokenReq.Status.Token, nil
 	}
 
 	account := &corev1.ServiceAccount{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      accountName,
 			Namespace: namespace,
 		},
 	}
 	_, err = client.CoreV1().ServiceAccounts(namespace).Create(context.TODO(), account, metav1.CreateOptions{})
-	o.Expect(err).NotTo(o.HaveOccurred())
+	if err != nil {
+		return "", err
+	}
 
 	rb := &rbacv1.ClusterRoleBinding{
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      fmt.Sprintf("%s:%s:%s", namespace, clusterRole, accountName),
-			Namespace: namespace,
 		},
 		RoleRef: rbacv1.RoleRef{
 			APIGroup: "rbac.authorization.k8s.io",
 			Kind:     "ClusterRole",
-			Name:     "cluster-admin",
+			Name:     clusterRole,
 		},
 		Subjects: []rbacv1.Subject{
 			{
 				Kind:      "ServiceAccount",
 				Name:      accountName,
 				Namespace: namespace,
 			},
 		},
 	}
 	_, err = client.RbacV1().ClusterRoleBindings().Create(context.TODO(), rb, metav1.CreateOptions{})
-	o.Expect(err).NotTo(o.HaveOccurred())
+	if err != nil {
+		return "", err
+	}
 
-	newToken, err := client.CoreV1().ServiceAccounts(namespace).CreateToken(context.TODO(), accountName, &authenticationv1.TokenRequest{}, metav1.CreateOptions{})
-	return newToken.String(), err
+	tokenReq, err := client.CoreV1().ServiceAccounts(namespace).CreateToken(context.TODO(), accountName, &authenticationv1.TokenRequest{}, metav1.CreateOptions{})
+	if err != nil {
+		return "", err
+	}
+	return tokenReq.Status.Token, nil
 }

104-105: Fix incorrect error handling on all List operations.

All five List operations (lines 105, 115, 124, 133, 142) incorrectly use kerrors.IsNotFound(err) to check for errors. This has two critical problems:

  1. Silently masks real API failures: Any non-NotFound error (authentication, RBAC, network issues, etc.) makes IsNotFound(err) return false, causing the expectation to pass even though the API call failed. The test then iterates over an empty/zero-valued list and can incorrectly report success.

  2. Wrong error type: Kubernetes List operations return an empty list with err == nil when no items exist, not a NotFound error. The IsNotFound check is designed for Get/Delete operations on named resources (single-resource operations), not collection endpoints.

Apply this diff to fix all List error checks:

 		auths, err := client.Authentications().List(context.TODO(), metav1.ListOptions{})
-		o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing authentications")
+		o.Expect(err).NotTo(o.HaveOccurred(), "failed to list authentications")
 
 		g.By(fmt.Sprintf("checking if authentication with %s annotation exists", annotation))
 		for _, auth := range auths.Items {
 			if _, ok := auth.Annotations[annotation]; ok {
 				o.Expect(ok).NotTo(o.BeTrue(), fmt.Sprintf("Unexpectedly installed authentication %s which has '%s' annotation", auth.Name, annotation))
 			}
 		}
 
 		namespaces, err := kubeclient.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{})
-		o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing namespaces")
+		o.Expect(err).NotTo(o.HaveOccurred(), "failed to list namespaces")
 
 		g.By(fmt.Sprintf("checking if special resources with %s annotation exist in all namespaces", annotation))
 		for _, ns := range namespaces.Items {
 			namespace := ns.Name
 			fmt.Printf("namespace: %s\n", namespace)
 
 			fmt.Println("	- Test services...")
 			services, err := kubeclient.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{})
-			o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing services")
+			o.Expect(err).NotTo(o.HaveOccurred(), "failed to list services")
 			for _, service := range services.Items {
 				if _, ok := service.Annotations[annotation]; ok {
 					o.Expect(ok).NotTo(o.BeTrue(), fmt.Sprintf("Unexpectedly installed service %s which has '%s' annotation", service.Name, annotation))
 				}
 			}
 
 			fmt.Println("	- Test RoleBinding...")
 			rolebindings, err := kubeclient.RbacV1().RoleBindings(namespace).List(context.TODO(), metav1.ListOptions{})
-			o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing rolebindings")
+			o.Expect(err).NotTo(o.HaveOccurred(), "failed to list rolebindings")
 			for _, rb := range rolebindings.Items {
 				if _, ok := rb.Annotations[annotation]; ok {
 					o.Expect(ok).NotTo(o.BeTrue(), fmt.Sprintf("Unexpectedly installed RoleBinding %s which has '%s' annotation", rb.Name, annotation))
 				}
 			}
 
 			fmt.Println("	- Test CronJob...")
 			cronjobs, err := kubeclient.BatchV1().CronJobs(namespace).List(context.TODO(), metav1.ListOptions{})
-			o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should occur when listing cronjobs")
+			o.Expect(err).NotTo(o.HaveOccurred(), "failed to list cronjobs")
 			for _, cj := range cronjobs.Items {
 				if _, ok := cj.Annotations[annotation]; ok {
 					o.Expect(ok).NotTo(o.BeTrue(), fmt.Sprintf("Unexpectedly installed CronJob %s which has %s annotation", cj.Name, annotation))
 				}
 			}

Also applies to: 114-115, 123-124, 132-133, 141-142


153-253: Fix multiple critical issues in precheck test.

This test has several problems that affect correctness and completeness:

  1. Line 167: Typo "invaid" should be "invalid" (unless the invalid image is intentional for testing failure scenarios).

  2. Lines 158, 201, 240: Missing error checks on Create/Apply operations. These calls can fail silently, causing the test to proceed with invalid state.

  3. Line 210: log.Fatalf terminates the entire test process. Use Gomega assertions instead: o.Expect(err).NotTo(o.HaveOccurred()).

  4. Lines 207-218: The wait.Poll logic is confusing—it returns an error when a pod is running (line 214), then expects the final error to not be NotFound (line 224). This appears backwards and uses the same incorrect IsNotFound pattern on List operations.

  5. Line 224: Same List error handling issue—should check err directly, not use IsNotFound.

  6. Line 251: TODO comment indicates incomplete implementation (no alert fetching logic).

  7. Test title mismatch: The title mentions "oc adm upgrade recommend" but the test doesn't perform upgrade recommendations; it tests deployment and PDB behavior.

Apply this diff to fix the immediate correctness issues:

-		containerImage := "openshift/hello-openshift:invaid"
+		containerImage := "openshift/hello-openshift:invalid"
 		deployment := &appsv1.Deployment{
 			// ... deployment spec ...
 		}
-		kubeclient.AppsV1().Deployments(ns).Create(context.TODO(), deployment, metav1.CreateOptions{})
+		_, err := kubeclient.AppsV1().Deployments(ns).Create(context.TODO(), deployment, metav1.CreateOptions{})
+		o.Expect(err).NotTo(o.HaveOccurred(), "failed to create deployment")
 
 		defer func() {
 			kubeclient.AppsV1().Deployments(ns).Delete(context.TODO(), deploymentName, metav1.DeleteOptions{})
 		}()
 
-		err := wait.Poll(1*time.Minute, 3*time.Minute, func() (bool, error) {
+		err = wait.Poll(1*time.Minute, 3*time.Minute, func() (bool, error) {
 			allPods, err := kubeclient.CoreV1().Pods(ns).List(context.TODO(), metav1.ListOptions{})
 			if err != nil {
-				log.Fatalf("Error listing pods: %v", err)
+				return false, err
 			}
 			for _, pod := range allPods.Items {
 				if pod.Status.Phase == corev1.PodRunning {
-					return true, errors.New("there are pods running: " + pod.Name)
+					return false, errors.New("there are pods running: " + pod.Name)
 				}
 			}
 			return true, nil
 		})
-		allPods, _ := kubeclient.CoreV1().Pods(ns).List(context.TODO(), metav1.ListOptions{})
+		allPods, err := kubeclient.CoreV1().Pods(ns).List(context.TODO(), metav1.ListOptions{})
+		o.Expect(err).NotTo(o.HaveOccurred(), "failed to list pods after poll")
 		fmt.Printf("there are %v pods\n", len(allPods.Items))
 		for _, pod := range allPods.Items {
 			fmt.Printf("	- Pod: %s - %s\n", pod.Name, pod.Status.Phase)
 		}
-		o.Expect(kerrors.IsNotFound(err)).To(o.BeFalse(), "The NotFound error should not occur")
 
 		g.By("create a PodDisruptionBudget")
 		pdbName := "my-pdb"
 		pdb := &applyconfigurationspolicyv1.PodDisruptionBudgetApplyConfiguration{
 			// ... pdb spec ...
 		}
-		kubeclient.PolicyV1().PodDisruptionBudgets(ns).Apply(context.TODO(), pdb, metav1.ApplyOptions{})
+		_, err = kubeclient.PolicyV1().PodDisruptionBudgets(ns).Apply(context.TODO(), pdb, metav1.ApplyOptions{FieldManager: "test"})
+		o.Expect(err).NotTo(o.HaveOccurred(), "failed to apply PDB")

Regarding the incomplete test: The TODO at line 251 and missing alert logic suggest this test is still under development. Consider either completing the implementation or marking it with g.Skip() until the alert fetching logic is ready.

🧹 Nitpick comments (1)
test/cvo/cvo.go (1)

3-27: Consider organizing imports by grouping local packages separately.

Following Go conventions and the repository's coding guidelines, imports from this repository should be in their own section, separated from external dependencies.

Apply this diff to improve import organization:

 import (
 	"context"
 	"errors"
 	"fmt"
 	"log"
 	"time"
 
 	g "github.com/onsi/ginkgo/v2"
 	o "github.com/onsi/gomega"
 	appsv1 "k8s.io/api/apps/v1"
 	authenticationv1 "k8s.io/api/authentication/v1"
 	corev1 "k8s.io/api/core/v1"
 	rbacv1 "k8s.io/api/rbac/v1"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/intstr"
 	"k8s.io/apimachinery/pkg/util/wait"
 	clientmetav1 "k8s.io/client-go/applyconfigurations/meta/v1"
 	applyconfigurationspolicyv1 "k8s.io/client-go/applyconfigurations/policy/v1"
 	"k8s.io/client-go/kubernetes"
 	"k8s.io/utils/ptr"
-
 	v1 "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
+
 	"github.com/openshift/cluster-version-operator/test/utilities"
 )

Based on past review comments.

📜 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

📥 Commits

Reviewing files that changed from the base of the PR and between f9a032b and 338557b.

📒 Files selected for processing (1)
  • test/cvo/cvo.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

-Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.

Files:

  • test/cvo/cvo.go
🧬 Code graph analysis (1)
test/cvo/cvo.go (1)
test/utilities/connection.go (2)
  • MustGetV1Client (65-71)
  • MustGetKubeClient (56-62)
🔇 Additional comments (1)
test/cvo/cvo.go (1)

85-89: LGTM - sanity test serves its purpose.

This simple test provides a useful signal for validating the test framework integration, as discussed in previous reviews.

Comment on lines +72 to +83
func DeleteServiceAccount(client *kubernetes.Clientset, accountName string, clusterRole string, namespace string) {
name := fmt.Sprintf("%s:%s:%s", namespace, clusterRole, accountName)
err := client.RbacV1().ClusterRoleBindings().Delete(context.TODO(), name, metav1.DeleteOptions{})
if err != nil {
panic("failed to delete ClusterRoleBindings")
}

err = client.CoreV1().ServiceAccounts(namespace).Delete(context.TODO(), accountName, metav1.DeleteOptions{})
if err != nil {
panic("failed to delete ServiceAccount")
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace panic calls with proper error handling or Gomega assertions.

Using panic for error handling in test helpers makes debugging more difficult and doesn't integrate well with Ginkgo's failure reporting. Consider either returning errors for the caller to handle or using Gomega assertions (o.Expect(err).NotTo(o.HaveOccurred())) for clearer test failure messages.

Apply this diff:

 func DeleteServiceAccount(client *kubernetes.Clientset, accountName string, clusterRole string, namespace string) {
 	name := fmt.Sprintf("%s:%s:%s", namespace, clusterRole, accountName)
 	err := client.RbacV1().ClusterRoleBindings().Delete(context.TODO(), name, metav1.DeleteOptions{})
-	if err != nil {
-		panic("failed to delete ClusterRoleBindings")
-	}
+	o.Expect(err).NotTo(o.HaveOccurred(), "failed to delete ClusterRoleBinding %s", name)
 
 	err = client.CoreV1().ServiceAccounts(namespace).Delete(context.TODO(), accountName, metav1.DeleteOptions{})
-	if err != nil {
-		panic("failed to delete ServiceAccount")
-	}
+	o.Expect(err).NotTo(o.HaveOccurred(), "failed to delete ServiceAccount %s/%s", namespace, accountName)
 }
📝 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.

Suggested change
func DeleteServiceAccount(client *kubernetes.Clientset, accountName string, clusterRole string, namespace string) {
name := fmt.Sprintf("%s:%s:%s", namespace, clusterRole, accountName)
err := client.RbacV1().ClusterRoleBindings().Delete(context.TODO(), name, metav1.DeleteOptions{})
if err != nil {
panic("failed to delete ClusterRoleBindings")
}
err = client.CoreV1().ServiceAccounts(namespace).Delete(context.TODO(), accountName, metav1.DeleteOptions{})
if err != nil {
panic("failed to delete ServiceAccount")
}
}
func DeleteServiceAccount(client *kubernetes.Clientset, accountName string, clusterRole string, namespace string) {
name := fmt.Sprintf("%s:%s:%s", namespace, clusterRole, accountName)
err := client.RbacV1().ClusterRoleBindings().Delete(context.TODO(), name, metav1.DeleteOptions{})
o.Expect(err).NotTo(o.HaveOccurred(), "failed to delete ClusterRoleBinding %s", name)
err = client.CoreV1().ServiceAccounts(namespace).Delete(context.TODO(), accountName, metav1.DeleteOptions{})
o.Expect(err).NotTo(o.HaveOccurred(), "failed to delete ServiceAccount %s/%s", namespace, accountName)
}

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Nov 24, 2025

@JianLi-RH: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-aws-ovn-techpreview 338557b link true /test e2e-aws-ovn-techpreview
ci/prow/verify-update 338557b link true /test verify-update
ci/prow/lint 338557b link true /test lint

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants