Add configurable ServiceAccount for the DCGM Exporter - #2870
Conversation
The DCGM Exporter ServiceAccount name is hardcoded to nvidia-dcgm-exporter
across the assets, the RBAC bindings, the OpenShift SCC and the DaemonSet.
Platforms that bind an identity (IRSA, Workload Identity, PKI) to a specific
ServiceAccount name therefore cannot use the operator-managed exporter and have
to run a standalone one, vendor-patch the operator, or fight reconciliation with
an admission mutator.
Add DCGMExporterSpec.serviceAccount with a {name, create} shape:
- unset keeps the current behaviour;
- name selects the ServiceAccount every exporter operand references;
- create: false binds to a ServiceAccount that already exists in the operator
namespace.
A user-provided ServiceAccount is never created, adopted, mutated or deleted: it
is left without an owner reference, it survives disabling the exporter, and a
missing one is surfaced as NotReady rather than leaving the DaemonSet pending.
A ServiceAccount is only deleted when it carries a ClusterPolicy owner
reference, so one provisioned by the user under the same name is left alone.
The SCC name and the openshift.io/scc annotation stay tied to the asset; only
its users entry follows the resolved ServiceAccount. On the RBAC bindings only
the exporter subject is rewritten, so the Prometheus subject is preserved.
GPUCluster embeds the same spec, so the DRA manifests honour it as well,
defaulting to nvidia-dcgm-exporter-dra.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UiFVTVAyEMmn68Xvo9LSTf
Signed-off-by: 백지명 <wlaud9805@gmail.com>
📝 WalkthroughWalkthroughThe change adds optional DCGM Exporter ServiceAccount configuration with a custom name and creation flag. Validation requires a name for externally managed accounts. The controller resolves the configured or default name, manages only operator-owned accounts, validates missing external accounts, and updates RBAC, SCC, and DaemonSet references. Helm values and ClusterPolicy templates expose the configuration. Rendered manifests conditionally create the ServiceAccount and use the configured name. Priority: ➖ Normal Severity of issue fixed: Medium Merge Risk: 🔵 Low · up to ServiceAccount names such as 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)
api/nvidia/v1/clusterpolicy_types_test.go-113-117 (1)
113-117: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winAdd a CRD validation regression test for
dcgmExporter.serviceAccount: {create: false}withoutname.The helper tests do not exercise the generated
ClusterPolicyCRD. The XValidation rule is the only safeguard before reconciliation; without it, the helper defaults the missing name tonvidia-dcgm-exporterand selects unmanaged reconciliation. Add a test that asserts the generated CRD rejects this configuration.manifests/state-dcgm-exporter/0700_daemonset.yaml-43-43 (1)
43-43: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winQuote
ServiceAccountNamein this manifest.When the configured string is
trueor123, the unquoted template output is decoded into the unstructured object as a YAML boolean or number. Kubernetes can then rejectspec.template.spec.serviceAccountName, which requires a string.- serviceAccountName: {{ .ServiceAccountName }} + serviceAccountName: {{ .ServiceAccountName | quote }}
🧹 Nitpick comments (1)
controllers/object_controls_test.go (1)
2670-2684: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd one non-DCGM state case.
Every subtest sets
stateNamestostate-dcgm-exporter, so the newisDCGMExporterguard is never exercised in the false direction. If that guard is later widened, the ownership check would start skipping deletion for driver, toolkit and device-plugin ServiceAccounts on cleanup, and no test here would fail. A subtest withstateNames: []string{"state-driver"}and a disabled driver that asserts the unowned ServiceAccount is deleted pins the current behavior.As per path instructions: "Flag ... new operand or CRD behaviour that ships with no regression test."
Source: Path instructions
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: QUIET
Plan: Enterprise
Run ID: d440e324-e2a5-4b14-a69e-3c8eb81a44f5
⛔ Files ignored due to path filters (7)
api/nvidia/v1/zz_generated.deepcopy.gois excluded by!**/zz_generated.*.gobundle/manifests/nvidia.com_clusterpolicies.yamlis excluded by!bundle/manifests/nvidia.com_*.yamlbundle/manifests/nvidia.com_gpuclusters.yamlis excluded by!bundle/manifests/nvidia.com_*.yamlconfig/crd/bases/nvidia.com_clusterpolicies.yamlis excluded by!config/crd/bases/**config/crd/bases/nvidia.com_gpuclusters.yamlis excluded by!config/crd/bases/**deployments/gpu-operator/crds/nvidia.com_clusterpolicies.yamlis excluded by!deployments/gpu-operator/crds/**deployments/gpu-operator/crds/nvidia.com_gpuclusters.yamlis excluded by!deployments/gpu-operator/crds/**
📒 Files selected for processing (14)
api/nvidia/v1/clusterpolicy_types.goapi/nvidia/v1/clusterpolicy_types_test.gocontrollers/object_controls.gocontrollers/object_controls_test.gocontrollers/transforms_test.godeployments/gpu-operator/templates/clusterpolicy.yamldeployments/gpu-operator/values.yamlinternal/state/dcgm_exporter.gointernal/state/types.gomanifests/state-dcgm-exporter/0100_serviceaccount.yamlmanifests/state-dcgm-exporter/0300_rolebinding.yamlmanifests/state-dcgm-exporter/0310_clusterrolebinding.yamlmanifests/state-dcgm-exporter/0450_scc.openshift.yamlmanifests/state-dcgm-exporter/0700_daemonset.yaml
Included review availability: Your plan provides up to 12 included reviews per hour; 11 remain after this review.
- quote the templated ServiceAccount name in the DRA manifests so a name that YAML would otherwise decode as a boolean or a number stays a string; - pin the CEL rule that rejects create: false without a name with a test over the generated ClusterPolicy and GPUCluster CRDs, since the helpers cannot catch that combination on their own; - cover a non-DCGM state in the ServiceAccount cleanup test so the ownership check staying scoped to the DCGM Exporter is exercised in both directions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UiFVTVAyEMmn68Xvo9LSTf Signed-off-by: 백지명 <wlaud9805@gmail.com>
|
Thanks — all three points addressed in dd3685a. Quote the templated ServiceAccount name. Applied to CRD validation regression test. Added Non-DCGM state case. Added a subtest that runs
|
|
1. Helm GPUCluster does not pass through the field dcgmExporter.serviceAccount is wired in deployments/gpu-operator/templates/clusterpolicy.yaml but not here (after the service / serviceMonitor block, ~L121). A Helm GPUCluster install cannot set the field. Add the same {{- if .Values.dcgmExporter.serviceAccount }} passthrough. Values are already documented in deployments/gpu-operator/values.yaml. 2. create: false does not surface NotReady when the SA is missing manifests/state-dcgm-exporter/0100_serviceaccount.yaml skips the SA when CreateServiceAccount is false, but configurable_state.go Sync() never checks that the user-provided SA exists. ClusterPolicy does this in controllers/object_controls.go (ServiceAccount()). Before syncObjects, if create: false, Get the SA and return SyncStateNotReady when it is missing. 3. GPUCluster create: true can adopt a pre-existing SA createOrUpdateObjs() always SetControllerReference then updates on AlreadyExists. That adopts a pre-existing SA and GC will delete it with the GPUCluster. ClusterPolicy skips update on AlreadyExists in controllers/object_controls.go. For the DCGM exporter SA, do not set ownerRef / update if the object already exists and is not owned by this CR. 4. Renaming does not delete the previous operator-owned SA ServiceAccount() only reconciles the new name. Switching nvidia-dcgm-exporter → metrics-identity leaves the old owned SA. On disable it also only deletes the current name. After resolving the new name, delete the previous default/configured SA only if it has a ClusterPolicy/GPUCluster ownerRef. Same cleanup needed on the DRA path (stale objects are not removed in state_skel.go syncObjects). 5. No GPUCluster tests for the new field ClusterPolicy coverage in controllers/object_controls_test.go / controllers/transforms_test.go is good. Add GPUCluster cases for a custom name and create: false (SA omitted, DaemonSet/RBAC/SCC still reference it). Update goldens under internal/state/testdata/golden/ if the default render changes. |
Review feedback: the first commit wired the field on the ClusterPolicy path only, leaving the DRA path able to render the reference without honouring the contract behind it. - pass the field through deployments/gpu-operator/templates/gpucluster.yaml, so a Helm GPUCluster install can set it; - add a preSync hook to configurableState and use it for the exporter, so create: false reports NotReady when the ServiceAccount is missing rather than leaving the DaemonSet pending on an object the manifests deliberately omit; - refuse to take over a ServiceAccount that already exists under a configured name and is not owned by the CR. createOrUpdateObjs() would otherwise adopt it on the DRA path and hand it to garbage collection with the GPUCluster. The default name stays tolerant so an upgrade that lost the owner reference keeps converging; - reclaim the operator-owned default ServiceAccount once a different name takes over, on both paths. Renaming between two custom names is not tracked, so only the default is reclaimed; - cover the GPUCluster path: the rendered ServiceAccount, DaemonSet and RBAC subjects for a custom name, the omitted ServiceAccount for create: false, and each preSync branch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UiFVTVAyEMmn68Xvo9LSTf Signed-off-by: 백지명 <wlaud9805@gmail.com>
b864a60 to
ba6cc85
Compare
|
Thanks — this was a real gap: the first commit wired the field on the ClusterPolicy path and left the DRA path able to render the reference without honouring the contract behind it. All five points are addressed in ba6cc85. 1. Helm GPUCluster passthrough. Added after the 2. 3. No adoption on the DRA path. Rather than special-casing a kind inside 4. Renaming reclaims the previous ServiceAccount. Both paths now delete the operator-owned default once a different name takes over, and only when it carries the CR's owner reference — a ServiceAccount the user had provisioned under that name is left alone. Renaming between two custom names is not tracked, since the previous name is not recorded anywhere; that limitation is called out in the helper's comment. If you would rather see it handled, recording the applied name in status would be the way, and I am happy to do it in a follow-up. 5. GPUCluster tests.
|
|
Thanks — @100-JM covers the previous notes (
|
The create=false branch returned as soon as the user-provided ServiceAccount was found, so the default install path -- the operator creates nvidia-dcgm-exporter, the user then switches to their own ServiceAccount for IRSA or Workload Identity -- left the superseded default behind on both the ClusterPolicy and the DRA path. Reclaim it there as well, under the same ownership rule as the rename case, and skip the reclaim when the user brings the default name itself: that object is the one now being referenced. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UiFVTVAyEMmn68Xvo9LSTf Signed-off-by: 백지명 <wlaud9805@gmail.com>
|
Good catch — that is the transition the feature exists for, and both One guard added on top of your note: the reclaim is skipped when the user brings the default name itself ( Tests on both paths:
|
Description
Closes #2729.
The DCGM Exporter ServiceAccount name is hardcoded to
nvidia-dcgm-exporteracross the assets, the RBAC bindings, the OpenShift SCC and the DaemonSet, so platforms that bind an identity (IRSA, Workload Identity, PKI) to a specific ServiceAccount name cannot use the operator-managed exporter — they have to run a standalone exporter, vendor-patch the operator, or fight reconciliation with an admission mutator.This adds
dcgmExporter.serviceAccountwith the{name, create}shape agreed in the issue:{name: X}X; every exporter operand references it{name: X, create: false}Xmust already exist in the operator namespace; the operator only references it{create: false}without a nameA user-provided ServiceAccount is treated as unmanaged: it is never created, adopted, mutated or deleted, it is left without an owner reference, it survives disabling the exporter, and a missing one is reported as
NotReadyinstead of silently leaving the DaemonSet pending. A ServiceAccount is only deleted when it carries a ClusterPolicy owner reference, so one a user had already provisioned under the same name is left alone.Scope follows the issue discussion:
openshift.io/sccannotation on the DaemonSet stay tied to the asset name; only theusersentry follows the resolved ServiceAccount.0500_prom_rolebinding_openshift.yamlis preserved.DCGMExporterSpecis shared, so the DRA manifests honour the same field, defaulting tonvidia-dcgm-exporter-dra.create: false.DCGM Exporter is the only component touched.
Checklist
make lint)make validate-generated-assets)make validate-modules)Testing
make unit-testpasses (no failures) andmake lint,make fmt,make license-check,make validate-generated-assets,make validate-modulesare clean.New unit tests:
api/nvidia/v1:TestDCGMExporterServiceAccount— name/create resolution including the unset default.controllers:TestDCGMExporterServiceAccountReconcile— the default is created and owned; a configured name is created instead of the default;create: falsereportsNotReadywhen the ServiceAccount is missing, references an existing one without adopting it, and keeps it when the exporter is disabled; a ServiceAccount without a ClusterPolicy owner reference is not deleted; one with it is.controllers:TestDCGMExporterRBACSubjects— RoleBinding / ClusterRoleBinding subjects and the SCCusersentry follow the configured ServiceAccount while the object names stay unchanged, and the Prometheus subject is untouched.controllers:TestTransformDCGMExporterServiceAccount— the DaemonSet reference, and that an unset or default configuration does not change the asset value.Not tested on a live cluster: I do not have an OpenShift cluster for the SCC path, so that path is covered by unit tests only.
🤖 Generated with Claude Code
https://claude.ai/code/session_01UiFVTVAyEMmn68Xvo9LSTf