Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions api/nvidia/v1/clusterpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,11 @@ type DCGMExporterSpec struct {
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.displayName="Service configuration for NVIDIA DCGM Exporter"
ServiceSpec *DCGMExporterServiceConfig `json:"service,omitempty"`

// Optional: ServiceAccount configuration for NVIDIA DCGM Exporter
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.displayName="ServiceAccount configuration for NVIDIA DCGM Exporter"
ServiceAccount *DCGMExporterServiceAccountConfig `json:"serviceAccount,omitempty"`

// HostPID allows the DCGM-Exporter daemon set to access the host's PID namespace
// +kubebuilder:validation:Optional
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true
Expand Down Expand Up @@ -1148,6 +1153,30 @@ type DCGMExporterServiceConfig struct {
InternalTrafficPolicy *corev1.ServiceInternalTrafficPolicy `json:"internalTrafficPolicy,omitempty"`
}

// DCGMExporterServiceAccountConfig defines the ServiceAccount used by the NVIDIA
// DCGM Exporter DaemonSet.
// +kubebuilder:validation:XValidation:rule="!has(self.create) || self.create || (has(self.name) && size(self.name) > 0)",message="name is required when create is false"
type DCGMExporterServiceAccountConfig struct {
// Name of the ServiceAccount used by the NVIDIA DCGM Exporter DaemonSet.
// Defaults to the operator-managed ServiceAccount when left empty.
// +kubebuilder:validation:Optional
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.displayName="ServiceAccount name for NVIDIA DCGM Exporter"
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.x-descriptors="urn:alm:descriptor:com.tectonic.ui:text"
Name string `json:"name,omitempty"`

// Create indicates whether the operator manages the lifecycle of the DCGM
// Exporter ServiceAccount. Defaults to true. When set to false, a
// ServiceAccount with the configured name has to already exist in the
// operator namespace; the operator then only references it and never
// creates, adopts, mutates or deletes it.
// +kubebuilder:validation:Optional
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.displayName="Create the ServiceAccount for NVIDIA DCGM Exporter"
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.x-descriptors="urn:alm:descriptor:com.tectonic.ui:booleanSwitch"
Create *bool `json:"create,omitempty"`
}

// DCGMSpec defines the properties for NVIDIA DCGM deployment
type DCGMSpec struct {
// Enabled indicates if deployment of NVIDIA DCGM Hostengine as a separate pod is enabled.
Expand Down Expand Up @@ -2301,6 +2330,26 @@ func (e *DCGMExporterSpec) IsKubernetesPodMetadataEnabled() bool {
return e.IsPodLabelsEnabled() || e.IsPodUIDEnabled()
}

// GetServiceAccountName returns the name of the ServiceAccount referenced by the
// DCGM Exporter operands, falling back to defaultName when it is not configured.
func (e *DCGMExporterSpec) GetServiceAccountName(defaultName string) string {
if e.ServiceAccount == nil || e.ServiceAccount.Name == "" {
return defaultName
}
return e.ServiceAccount.Name
}

// IsServiceAccountCreateEnabled returns true if the operator owns the lifecycle of
// the DCGM Exporter ServiceAccount. When false the ServiceAccount is supplied by
// the user and is never created, adopted, mutated or deleted by the operator.
func (e *DCGMExporterSpec) IsServiceAccountCreateEnabled() bool {
if e.ServiceAccount == nil || e.ServiceAccount.Create == nil {
// default is true if not specified by user
return true
}
return *e.ServiceAccount.Create
}

// IsEnabled returns true if gpu-feature-discovery is enabled(default) through gpu-operator
func (g *GPUFeatureDiscoverySpec) IsEnabled() bool {
if g.Enabled == nil {
Expand Down
82 changes: 82 additions & 0 deletions api/nvidia/v1/clusterpolicy_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
package v1

import (
"os"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/yaml"
)

func TestImagePath(t *testing.T) {
Expand Down Expand Up @@ -86,3 +89,82 @@ func TestImagePath(t *testing.T) {
assert.ErrorContains(t, err, "invalid nil spec")
})
}

func TestDCGMExporterServiceAccount(t *testing.T) {
const defaultName = "nvidia-dcgm-exporter"

testCases := map[string]struct {
serviceAccount *DCGMExporterServiceAccountConfig
expectedName string
expectedCreate bool
}{
"unset falls back to the default and is operator-managed": {
serviceAccount: nil,
expectedName: defaultName,
expectedCreate: true,
},
"empty name falls back to the default": {
serviceAccount: &DCGMExporterServiceAccountConfig{},
expectedName: defaultName,
expectedCreate: true,
},
"name only stays operator-managed": {
serviceAccount: &DCGMExporterServiceAccountConfig{Name: "metrics-identity"},
expectedName: "metrics-identity",
expectedCreate: true,
},
"create=false marks the ServiceAccount as user-provided": {
serviceAccount: &DCGMExporterServiceAccountConfig{Name: "byo-sa", Create: new(false)},
expectedName: "byo-sa",
expectedCreate: false,
},
"create=true is explicit operator management": {
serviceAccount: &DCGMExporterServiceAccountConfig{Name: "managed-sa", Create: new(true)},
expectedName: "managed-sa",
expectedCreate: true,
},
}

for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
spec := &DCGMExporterSpec{ServiceAccount: tc.serviceAccount}
require.Equal(t, tc.expectedName, spec.GetServiceAccountName(defaultName))
require.Equal(t, tc.expectedCreate, spec.IsServiceAccountCreateEnabled())
})
}
}

// TestDCGMExporterServiceAccountCRDValidation pins the CEL rule that guards
// `serviceAccount: {create: false}` without a name. The helpers cannot catch that
// combination -- GetServiceAccountName falls back to the default and the operator
// would then treat the default ServiceAccount as user-provided -- so the generated
// CRD is the only safeguard before reconciliation.
func TestDCGMExporterServiceAccountCRDValidation(t *testing.T) {
crds := map[string]string{
"ClusterPolicy": "../../../config/crd/bases/nvidia.com_clusterpolicies.yaml",
"GPUCluster": "../../../config/crd/bases/nvidia.com_gpuclusters.yaml",
}

for kind, path := range crds {
t.Run(kind, func(t *testing.T) {
data, err := os.ReadFile(path)
require.NoError(t, err)

crd := &apiextensionsv1.CustomResourceDefinition{}
require.NoError(t, yaml.Unmarshal(data, crd))
require.NotEmpty(t, crd.Spec.Versions)

props := crd.Spec.Versions[0].Schema.OpenAPIV3Schema.Properties["spec"].
Properties["dcgmExporter"].Properties["serviceAccount"]
require.Contains(t, props.Properties, "name")
require.Contains(t, props.Properties, "create")

require.Len(t, props.XValidations, 1,
"the create/name consistency rule must survive CRD regeneration")
rule := props.XValidations[0]
require.Equal(t, "name is required when create is false", rule.Message)
require.Contains(t, rule.Rule, "self.create")
require.Contains(t, rule.Rule, "self.name")
})
}
}
25 changes: 25 additions & 0 deletions api/nvidia/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions bundle/manifests/nvidia.com_clusterpolicies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,28 @@ spec:
ingress methods for a service
type: string
type: object
serviceAccount:
description: 'Optional: ServiceAccount configuration for NVIDIA
DCGM Exporter'
properties:
create:
description: |-
Create indicates whether the operator manages the lifecycle of the DCGM
Exporter ServiceAccount. Defaults to true. When set to false, a
ServiceAccount with the configured name has to already exist in the
operator namespace; the operator then only references it and never
creates, adopts, mutates or deletes it.
type: boolean
name:
description: |-
Name of the ServiceAccount used by the NVIDIA DCGM Exporter DaemonSet.
Defaults to the operator-managed ServiceAccount when left empty.
type: string
type: object
x-kubernetes-validations:
- message: name is required when create is false
rule: '!has(self.create) || self.create || (has(self.name) &&
size(self.name) > 0)'
serviceMonitor:
description: 'Optional: ServiceMonitor configuration for NVIDIA
DCGM Exporter'
Expand Down
22 changes: 22 additions & 0 deletions bundle/manifests/nvidia.com_gpuclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,28 @@ spec:
ingress methods for a service
type: string
type: object
serviceAccount:
description: 'Optional: ServiceAccount configuration for NVIDIA
DCGM Exporter'
properties:
create:
description: |-
Create indicates whether the operator manages the lifecycle of the DCGM
Exporter ServiceAccount. Defaults to true. When set to false, a
ServiceAccount with the configured name has to already exist in the
operator namespace; the operator then only references it and never
creates, adopts, mutates or deletes it.
type: boolean
name:
description: |-
Name of the ServiceAccount used by the NVIDIA DCGM Exporter DaemonSet.
Defaults to the operator-managed ServiceAccount when left empty.
type: string
type: object
x-kubernetes-validations:
- message: name is required when create is false
rule: '!has(self.create) || self.create || (has(self.name) &&
size(self.name) > 0)'
serviceMonitor:
description: 'Optional: ServiceMonitor configuration for NVIDIA
DCGM Exporter'
Expand Down
22 changes: 22 additions & 0 deletions config/crd/bases/nvidia.com_clusterpolicies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,28 @@ spec:
ingress methods for a service
type: string
type: object
serviceAccount:
description: 'Optional: ServiceAccount configuration for NVIDIA
DCGM Exporter'
properties:
create:
description: |-
Create indicates whether the operator manages the lifecycle of the DCGM
Exporter ServiceAccount. Defaults to true. When set to false, a
ServiceAccount with the configured name has to already exist in the
operator namespace; the operator then only references it and never
creates, adopts, mutates or deletes it.
type: boolean
name:
description: |-
Name of the ServiceAccount used by the NVIDIA DCGM Exporter DaemonSet.
Defaults to the operator-managed ServiceAccount when left empty.
type: string
type: object
x-kubernetes-validations:
- message: name is required when create is false
rule: '!has(self.create) || self.create || (has(self.name) &&
size(self.name) > 0)'
serviceMonitor:
description: 'Optional: ServiceMonitor configuration for NVIDIA
DCGM Exporter'
Expand Down
22 changes: 22 additions & 0 deletions config/crd/bases/nvidia.com_gpuclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,28 @@ spec:
ingress methods for a service
type: string
type: object
serviceAccount:
description: 'Optional: ServiceAccount configuration for NVIDIA
DCGM Exporter'
properties:
create:
description: |-
Create indicates whether the operator manages the lifecycle of the DCGM
Exporter ServiceAccount. Defaults to true. When set to false, a
ServiceAccount with the configured name has to already exist in the
operator namespace; the operator then only references it and never
creates, adopts, mutates or deletes it.
type: boolean
name:
description: |-
Name of the ServiceAccount used by the NVIDIA DCGM Exporter DaemonSet.
Defaults to the operator-managed ServiceAccount when left empty.
type: string
type: object
x-kubernetes-validations:
- message: name is required when create is false
rule: '!has(self.create) || self.create || (has(self.name) &&
size(self.name) > 0)'
serviceMonitor:
description: 'Optional: ServiceMonitor configuration for NVIDIA
DCGM Exporter'
Expand Down
Loading