add unit tests for the clusterinfo package - #2869
Conversation
📝 WalkthroughWalkthroughAdded comprehensive cluster information tests. The changes add HTTP API stubs, discovery fixtures, structured log capture, and typed resource builders. Tests cover runtime detection, Priority: ⬇️ Low Merge Risk: 🔵 Low · up to 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 |
There was a problem hiding this comment.
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 winThe cache assertion can pass with a zero baseline.
groupListRequestCountDuringNewis used only as the expected length. IfNewstops running discovery, the baseline is 0 andrequire.Lencompares 0 against 0. The test then reports that the oneshot verdict is cached while no discovery ran at all, anddraSupportedisfalsein 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 winAssert the remaining Driver Toolkit diagnostics. The empty-name case only checks the returned map, so removing the
RHBZ#2015024logger.Infocall leaves it green. The client-construction case only checksnil, so removingfailed to build openshift image stream clientalso leaves it green. Capture logs withtestContextWithCapturedLogsand assert both messages.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: QUIET
Plan: Enterprise
Run ID: dc2f67e4-feb0-4bcb-926e-1c50034a99c8
📒 Files selected for processing (9)
controllers/clusterinfo/clusterinfo_characterization_test.gocontrollers/clusterinfo/clusterinfo_test.gocontrollers/clusterinfo/dra_test.gocontrollers/clusterinfo/logging_test.gocontrollers/clusterinfo/openshift_dtk_test.gocontrollers/clusterinfo/openshift_proxy_test.gocontrollers/clusterinfo/openshift_version_test.gocontrollers/clusterinfo/runtime_test.gocontrollers/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>
d35f2d9 to
a64dc0d
Compare
clusterinfoanswers questions about the cluster the operator runs in: which containerruntime 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
clusterinfobuilds its typed clients inside each function from a*rest.Config, so thereis no seam to inject a fake through. The tests point
rest.Configat anhttptestserverthat 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
getOpenshiftVersionandgetOpenshiftProxySpectreat a 404 in opposite ways, andthe
apierrors.IsNotFoundmapping 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,
getOpenshiftVersionreads 404 as "not OpenShift", and the testpasses 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-Afteris notretried, 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.getRuntimeStringkeeps all five cases from #2831 and gains three: a bare prefix with noversion suffix, a runtime name appearing as a substring rather than a prefix, and case
sensitivity. They move from
clusterinfo_test.gointoruntime_test.go, which now holdsevery 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 callsos.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 assertingintended 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
getContainerRuntimediscards the error fromgetOpenshiftVersionand continues with theempty 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.
getOpenshiftDTKImageslogs the NotFound case and then falls through tologger.Errorinstead of returning, so every driver reconcile on plain Kubernetes logs an error for an
ImageStream that only exists on OpenShift.
getDRAResourceGVRscans every API group for KindDeviceClassbefore narrowing toresource.k8s.io. An unrelated CRD using that Kind makes a cluster with no DRA fail itsreconcile with "could not determine the GVR" instead of the message telling the operator
to enable Dynamic Resource Allocation.
getDRAResourceGVRtreats anyErrGroupDiscoveryFailedas recoverable without checkingwhich group failed. If
resource.k8s.ioitself is unreachable the result is "DRA notsupported" with a nil error, and the reconcile renders as if the cluster has no DRA.
getOpenshiftVersionreturns("", nil)for a Completed history entry with an emptyversion — the same value that means "not OpenShift". The upstream field is documented as
optionally empty, so this is reachable on a real cluster.
getContainerRuntimestops its node scan only for containerd. On a cluster mixing dockerand 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.gobuildsclusterinfowithWithOneShot(false), so these cannot bereached today.
getOpenshiftProxySpeclogs the error fromocpconfigv1.NewForConfigwithout returningit, so a nil client reaches
Proxies().Getand panics. It needs arest.Configtheconstructor rejects, and
mgr.GetConfig()never is.New's oneshot path never fetches the proxy, soGetOpenshiftProxySpecreports none forthe instance's lifetime and a configured cluster Proxy would drop out of the rendered
driver DaemonSet.
GetOpenshiftDriverToolkitImageshands back the cached map itself rather than a copy. Nocaller mutates it today.
Verification
make checkand the package tests pass under the toolchain inversions.mk(Go 1.27.1,golangci-lint v2.13.1) on Linux, with the repo's own
.golangci.yml: 0 issues. The suite isclean under
-raceand-shuffle=on, and every test and subtest also passes when run in itsown process.