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
15 changes: 15 additions & 0 deletions api/nvidia/v1/clusterpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,21 @@ type DaemonsetsSpec struct {
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.x-descriptors="urn:alm:descriptor:com.tectonic.ui:advanced,urn:alm:descriptor:io.kubernetes:Tolerations"
Tolerations []corev1.Toleration `json:"tolerations,omitempty"`

// Optional: Set nodeSelector merged with operand nodeSelector labels
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.displayName="NodeSelector for all Daemonsets"
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.x-descriptors="urn:alm:descriptor:com.tectonic.ui:advanced,urn:alm:descriptor:com.tectonic.ui:nodeSelector"
NodeSelector map[string]string `json:"nodeSelector,omitempty"`

// Optional: Set affinity for all Daemonsets
// +kubebuilder:validation:Type=object
// +kubebuilder:validation:Schemaless
// +kubebuilder:pruning:PreserveUnknownFields
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.displayName="Affinity for all Daemonsets"
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.x-descriptors="urn:alm:descriptor:com.tectonic.ui:advanced,urn:alm:descriptor:com.tectonic.ui:affinity"
Affinity *corev1.Affinity `json:"affinity,omitempty"`

// +kubebuilder:validation:Optional
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors=true
// +operator-sdk:gen-csv:customresourcedefinitions.specDescriptors.displayName="PriorityClassName"
Expand Down
12 changes: 12 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.

10 changes: 10 additions & 0 deletions bundle/manifests/nvidia.com_clusterpolicies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ spec:
daemonsets:
description: Daemonset defines common configuration for all Daemonsets
properties:
affinity:
description: 'Optional: Set affinity for all Daemonsets'
type: object
x-kubernetes-preserve-unknown-fields: true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I understand we want affinity to be schema less to prevent pulling in the entire affinity definition, but the way we have it now makes validation difficult, would it be so bad to inline the openAPI spec ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

kept it schemaless for now to avoid dragging the full affinity openapi into the crd bundle

annotations:
additionalProperties:
type: string
Expand All @@ -175,6 +179,12 @@ spec:
(scope and select) objects. May match selectors of replication controllers
and services.
type: object
nodeSelector:
additionalProperties:
type: string
description: 'Optional: Set nodeSelector merged with operand nodeSelector
labels'
type: object
podSecurityContext:
description: 'Optional: Set pod-level security context for all
DaemonSet pods (applies as defaults to all containers)'
Expand Down
10 changes: 10 additions & 0 deletions bundle/manifests/nvidia.com_gpuclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ spec:
Daemonsets defines the common configuration applied to all DaemonSets deployed
by the GPUCluster controller.
properties:
affinity:
description: 'Optional: Set affinity for all Daemonsets'
type: object
x-kubernetes-preserve-unknown-fields: true
annotations:
additionalProperties:
type: string
Expand All @@ -73,6 +77,12 @@ spec:
(scope and select) objects. May match selectors of replication controllers
and services.
type: object
nodeSelector:
additionalProperties:
type: string
description: 'Optional: Set nodeSelector merged with operand nodeSelector
labels'
type: object
podSecurityContext:
description: 'Optional: Set pod-level security context for all
DaemonSet pods (applies as defaults to all containers)'
Expand Down
10 changes: 10 additions & 0 deletions config/crd/bases/nvidia.com_clusterpolicies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ spec:
daemonsets:
description: Daemonset defines common configuration for all Daemonsets
properties:
affinity:
description: 'Optional: Set affinity for all Daemonsets'
type: object
x-kubernetes-preserve-unknown-fields: true
annotations:
additionalProperties:
type: string
Expand All @@ -175,6 +179,12 @@ spec:
(scope and select) objects. May match selectors of replication controllers
and services.
type: object
nodeSelector:
additionalProperties:
type: string
description: 'Optional: Set nodeSelector merged with operand nodeSelector
labels'
type: object
podSecurityContext:
description: 'Optional: Set pod-level security context for all
DaemonSet pods (applies as defaults to all containers)'
Expand Down
10 changes: 10 additions & 0 deletions config/crd/bases/nvidia.com_gpuclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ spec:
Daemonsets defines the common configuration applied to all DaemonSets deployed
by the GPUCluster controller.
properties:
affinity:
description: 'Optional: Set affinity for all Daemonsets'
type: object
x-kubernetes-preserve-unknown-fields: true
annotations:
additionalProperties:
type: string
Expand All @@ -73,6 +77,12 @@ spec:
(scope and select) objects. May match selectors of replication controllers
and services.
type: object
nodeSelector:
additionalProperties:
type: string
description: 'Optional: Set nodeSelector merged with operand nodeSelector
labels'
type: object
podSecurityContext:
description: 'Optional: Set pod-level security context for all
DaemonSet pods (applies as defaults to all containers)'
Expand Down
116 changes: 116 additions & 0 deletions controllers/object_controls.go
Original file line number Diff line number Diff line change
Expand Up @@ -829,13 +829,129 @@ func applyCommonDaemonsetConfig(obj *appsv1.DaemonSet, config *gpuv1.ClusterPoli
obj.Spec.Template.Spec.Tolerations = config.Daemonsets.Tolerations
}

if len(config.Daemonsets.NodeSelector) > 0 {
if obj.Spec.Template.Spec.NodeSelector == nil {
obj.Spec.Template.Spec.NodeSelector = make(map[string]string)
}
for key, value := range config.Daemonsets.NodeSelector {
if _, exists := obj.Spec.Template.Spec.NodeSelector[key]; exists {
continue
}
obj.Spec.Template.Spec.NodeSelector[key] = value
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}

if config.Daemonsets.Affinity != nil {
obj.Spec.Template.Spec.Affinity = mergeDaemonsetAffinity(obj.Spec.Template.Spec.Affinity, config.Daemonsets.Affinity)
}

// set pod-level security context if specified (applies as defaults to all containers in the pod)
if config.Daemonsets.PodSecurityContext != nil {
obj.Spec.Template.Spec.SecurityContext = config.Daemonsets.PodSecurityContext
}
return nil
}

func mergeDaemonsetAffinity(dst, src *corev1.Affinity) *corev1.Affinity {
if src == nil {
return dst
}
if dst == nil {
return src.DeepCopy()
}
merged := dst.DeepCopy()
if src.NodeAffinity != nil {
if merged.NodeAffinity == nil {
merged.NodeAffinity = src.NodeAffinity.DeepCopy()
} else {
merged.NodeAffinity = mergeNodeAffinity(merged.NodeAffinity, src.NodeAffinity)
}
}
if src.PodAffinity != nil {
if merged.PodAffinity == nil {
merged.PodAffinity = src.PodAffinity.DeepCopy()
} else {
if src.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
merged.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution = append(
merged.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution,
src.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution...,
)
}
if src.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution != nil {
merged.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution = append(
merged.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution,
src.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution...,
)
}
}
}
if src.PodAntiAffinity != nil {
if merged.PodAntiAffinity == nil {
merged.PodAntiAffinity = src.PodAntiAffinity.DeepCopy()
} else {
if src.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
merged.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution = append(
merged.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution,
src.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution...,
)
}
if src.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution != nil {
merged.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution = append(
merged.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution,
src.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution...,
)
}
}
}
return merged
}

func mergeNodeAffinity(dst, src *corev1.NodeAffinity) *corev1.NodeAffinity {
if src == nil {
return dst
}
if dst == nil {
return src.DeepCopy()
}
merged := dst.DeepCopy()
if src.RequiredDuringSchedulingIgnoredDuringExecution != nil {
if merged.RequiredDuringSchedulingIgnoredDuringExecution == nil {
merged.RequiredDuringSchedulingIgnoredDuringExecution = src.RequiredDuringSchedulingIgnoredDuringExecution.DeepCopy()
} else {
merged.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms = mergeNodeSelectorTerms(
merged.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms,
src.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms,
)
}
}
if src.PreferredDuringSchedulingIgnoredDuringExecution != nil {
merged.PreferredDuringSchedulingIgnoredDuringExecution = append(
merged.PreferredDuringSchedulingIgnoredDuringExecution,
src.PreferredDuringSchedulingIgnoredDuringExecution...,
)
}
return merged
}

func mergeNodeSelectorTerms(dst, src []corev1.NodeSelectorTerm) []corev1.NodeSelectorTerm {
if len(dst) == 0 {
return append([]corev1.NodeSelectorTerm(nil), src...)
}
if len(src) == 0 {
return dst
}
merged := make([]corev1.NodeSelectorTerm, 0, len(dst)*len(src))
for _, dstTerm := range dst {
for _, srcTerm := range src {
merged = append(merged, corev1.NodeSelectorTerm{
MatchExpressions: append(append([]corev1.NodeSelectorRequirement(nil), dstTerm.MatchExpressions...), srcTerm.MatchExpressions...),
MatchFields: append(append([]corev1.NodeSelectorRequirement(nil), dstTerm.MatchFields...), srcTerm.MatchFields...),
})
}
}
return merged
}

// apply necessary transforms if a custom host root path is configured
func transformForHostRoot(obj *appsv1.DaemonSet, hostRoot string) {
if hostRoot == "" || hostRoot == "/" {
Expand Down
Loading