Skip to content

add unit tests for the clusterinfo package - #2869

Open
abrarshivani wants to merge 1 commit into
NVIDIA:mainfrom
abrarshivani:unit-test-clusterinfo
Open

add unit tests for the clusterinfo package#2869
abrarshivani wants to merge 1 commit into
NVIDIA:mainfrom
abrarshivani:unit-test-clusterinfo

Conversation

@abrarshivani

Copy link
Copy Markdown
Contributor

clusterinfo answers questions about the cluster the operator runs in: which container
runtime the GPU nodes use, whether this is OpenShift and at what version, whether there is
a cluster-wide proxy, and whether the cluster supports DRA. Other parts of the operator use
those answers to decide what to install and how to configure it, so a wrong answer here is
quiet and hard to trace.

#2831 added tests for getRuntimeString. The rest of the package had none. This adds them.
No production code is changed.

How the tests work

clusterinfo builds its typed clients inside each function from a *rest.Config, so there
is no seam to inject a fake through. The tests point rest.Config at an httptest server
that serves the endpoints the package reads: nodes, ClusterVersion, Proxy, the
driver-toolkit ImageStream, and API discovery. Real client-go runs against it, so URL
building, decoding and 404 handling are exercised rather than simulated. That matters here
because getOpenshiftVersion and getOpenshiftProxySpec treat a 404 in opposite ways, and
the apierrors.IsNotFound mapping is part of the behaviour under test.

The stub fails a test if a client requests a path it has no handler for. Without that a
wrong URL returns 404, getOpenshiftVersion reads 404 as "not OpenShift", and the test
passes for the wrong reason. That check is what caught the DRA discovery calls when this
branch was rebased forward.

The cost worth knowing about: these tests are coupled to client-go behaviour that is not an
API contract — the discovery fan-out, the fact that a 5xx without Retry-After is not
retried, and protobuf-vs-JSON content negotiation. Each of those is commented where it is
relied on, so a k8s.io/* bump that changes them should fail loudly rather than silently.

getRuntimeString keeps all five cases from #2831 and gains three: a bare prefix with no
version suffix, a runtime name appearing as a substring rather than a prefix, and case
sensitivity. They move from clusterinfo_test.go into runtime_test.go, which now holds
every test for the container runtime path. That move is why the diff shows deletions in a
file this branch did not create.

Coverage is 99.3% of statements. The uncovered line is the config.GetConfigOrDie()
fallback in New, which calls os.Exit.

Behaviour that looks wrong

Nine behaviours look like bugs. None are fixed here. The tests record what the code does
today and live in clusterinfo_characterization_test.go, apart from the tests asserting
intended behaviour, so fixing any one of them fails only tests in that file — a maintainer
sees "I improved this" rather than "I broke this". That property is checked by applying each
fix and confirming where the failures land, not assumed.

Each is a numbered comment in that file. Happy to split any of these into their own issue
and fix them separately; I kept them out of this PR so it stays test-only.

Can affect a running cluster

  1. getContainerRuntime discards the error from getOpenshiftVersion and continues with the
    empty version it got back, so the crio short-circuit is skipped. An apiserver failure
    shows up only as a "failed to retrieve" log line and the function returns a runtime as
    though the read succeeded.
  2. getOpenshiftDTKImages logs the NotFound case and then falls through to logger.Error
    instead of returning, so every driver reconcile on plain Kubernetes logs an error for an
    ImageStream that only exists on OpenShift.
  3. getDRAResourceGVR scans every API group for Kind DeviceClass before narrowing to
    resource.k8s.io. An unrelated CRD using that Kind makes a cluster with no DRA fail its
    reconcile with "could not determine the GVR" instead of the message telling the operator
    to enable Dynamic Resource Allocation.
  4. getDRAResourceGVR treats any ErrGroupDiscoveryFailed as recoverable without checking
    which group failed. If resource.k8s.io itself is unreachable the result is "DRA not
    supported" with a nil error, and the reconcile renders as if the cluster has no DRA.
  5. getOpenshiftVersion returns ("", nil) for a Completed history entry with an empty
    version — the same value that means "not OpenShift". The upstream field is documented as
    optionally empty, so this is reachable on a real cluster.
  6. getContainerRuntime stops its node scan only for containerd. On a cluster mixing docker
    and cri-o nodes the answer is whichever the apiserver listed last, which flips whether the
    RHSM subscription paths get mounted into the driver container.

Latent

cmd/gpu-operator/main.go builds clusterinfo with WithOneShot(false), so these cannot be
reached today.

  1. getOpenshiftProxySpec logs the error from ocpconfigv1.NewForConfig without returning
    it, so a nil client reaches Proxies().Get and panics. It needs a rest.Config the
    constructor rejects, and mgr.GetConfig() never is.
  2. New's oneshot path never fetches the proxy, so GetOpenshiftProxySpec reports none for
    the instance's lifetime and a configured cluster Proxy would drop out of the rendered
    driver DaemonSet.
  3. GetOpenshiftDriverToolkitImages hands back the cached map itself rather than a copy. No
    caller mutates it today.

Verification

make check and the package tests pass under the toolchain in versions.mk (Go 1.27.1,
golangci-lint v2.13.1) on Linux, with the repo's own .golangci.yml: 0 issues. The suite is
clean under -race and -shuffle=on, and every test and subtest also passes when run in its
own process.

@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

📝 Walkthrough

Walkthrough

Added comprehensive cluster information tests. The changes add HTTP API stubs, discovery fixtures, structured log capture, and typed resource builders. Tests cover runtime detection, New initialization, context and option handling, OpenShift version detection, DRA discovery, proxy retrieval, Driver Toolkit image lookup, request ordering, error propagation, caching, and returned-value mutation behavior.

Priority: ⬇️ Low

Merge Risk: 🔵 Low · up to d35f2

The PR is mergeable with low risk, but two test assertions should be strengthened to ensure future regressions in diagnostics and DRA initialization are detected.


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Note

Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.

🟡 Other comments (2)
controllers/clusterinfo/clusterinfo_characterization_test.go-237-244 (1)

237-244: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

The cache assertion can pass with a zero baseline.

groupListRequestCountDuringNew is used only as the expected length. If New stops running discovery, the baseline is 0 and require.Len compares 0 against 0. The test then reports that the oneshot verdict is cached while no discovery ran at all, and draSupported is false in both worlds.

Pin the baseline so the caching claim needs a real request to have happened during New.

Assert the baseline
 	groupListRequestCountDuringNew := len(apiServer.requestsTo(pathAPIGroups))
+	require.NotZero(t, groupListRequestCountDuringNew,
+		"New must have run discovery for the cached verdict to mean anything")

As per path instructions: "Flag assertions that would still pass if the behavior under test were broken."

Source: Path instructions

controllers/clusterinfo/openshift_dtk_test.go-95-104 (1)

95-104: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Assert the remaining Driver Toolkit diagnostics. The empty-name case only checks the returned map, so removing the RHBZ#2015024 logger.Info call leaves it green. The client-construction case only checks nil, so removing failed to build openshift image stream client also leaves it green. Capture logs with testContextWithCapturedLogs and assert both messages.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Enterprise

Run ID: dc2f67e4-feb0-4bcb-926e-1c50034a99c8

📥 Commits

Reviewing files that changed from the base of the PR and between 6e330ac and d35f2d9.

📒 Files selected for processing (9)
  • controllers/clusterinfo/clusterinfo_characterization_test.go
  • controllers/clusterinfo/clusterinfo_test.go
  • controllers/clusterinfo/dra_test.go
  • controllers/clusterinfo/logging_test.go
  • controllers/clusterinfo/openshift_dtk_test.go
  • controllers/clusterinfo/openshift_proxy_test.go
  • controllers/clusterinfo/openshift_version_test.go
  • controllers/clusterinfo/runtime_test.go
  • controllers/clusterinfo/stubs_test.go

Included review availability: Your plan provides up to 12 included reviews per hour; 11 remain after this review.

clusterinfo answers questions about the cluster the operator runs in: which
container runtime the GPU nodes use, whether this is OpenShift, whether there
is a cluster proxy, whether the cluster supports DRA. Other parts of the
operator use those answers to decide what to install. NVIDIA#2831 added tests for
getRuntimeString; the rest of the package had none.

The package builds its Kubernetes clients inside each function from a
*rest.Config, so there is nothing to inject a mock into. The tests point
rest.Config at an httptest server that serves the endpoints the package reads.
Real client-go runs against it, so URL building, decoding and 404 handling are
exercised rather than simulated. The stub fails a test if a client requests a
path it has no handler for, which matters because getOpenshiftVersion reads a
404 as "not OpenShift" — without that check a wrong URL would pass for the
wrong reason.

getRuntimeString keeps the five cases from NVIDIA#2831 and gains three: a bare
prefix with no version suffix, a runtime name appearing as a substring rather
than a prefix, and case sensitivity. They move from clusterinfo_test.go to
runtime_test.go, which now holds every test for the container runtime path.

Nine behaviours look wrong. None are fixed here. The tests record what the
code does today and live in clusterinfo_characterization_test.go, apart from
the tests asserting intended behaviour, so fixing one of them fails only tests
in that file. That is checked by applying each fix and confirming where the
failures land.

Coverage is 99.3% of statements. The uncovered line calls os.Exit. No
production code is changed.

Signed-off-by: Abrar Shivani <ashivani@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant