Skip to content

Commit 2729447

Browse files
committed
Allow additional node selector terms to be set
This enables additional node selector terms to be added aside from the provisioner node, enabling use-cases such as shared disks, as affinity can not be changed after provisioning the PV. Limitation in the current implementation is that the provisioner can /not/ be ANDed with additional terms. This change only allows for additional terms that will be /ORed/ with the provisioner name term.
1 parent 9fe0e17 commit 2729447

File tree

5 files changed

+87
-75
lines changed

5 files changed

+87
-75
lines changed

helm/provisioner/templates/configmap.yaml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ data:
2626
{{- end }}
2727
{{- if .Values.useJobForCleaning }}
2828
useJobForCleaning: "yes"
29-
{{- end}}
29+
{{- end }}
3030
{{- if .Values.tolerations }}
3131
jobTolerations: | {{ toYaml .Values.tolerations | nindent 4 }}
3232
{{- end }}
@@ -35,7 +35,7 @@ data:
3535
{{- end }}
3636
{{- if .Values.minResyncPeriod }}
3737
minResyncPeriod: {{ .Values.minResyncPeriod | quote }}
38-
{{- end}}
38+
{{- end }}
3939
storageClassMap: |
4040
{{- range $classConfig := .Values.classes }}
4141
{{ $classConfig.name }}:
@@ -45,7 +45,7 @@ data:
4545
blockCleanerCommand:
4646
{{- range $val := $classConfig.blockCleanerCommand }}
4747
- {{ $val | quote }}
48-
{{- end}}
48+
{{- end }}
4949
{{- end }}
5050
{{- if $classConfig.volumeMode }}
5151
volumeMode: {{ $classConfig.volumeMode }}
@@ -56,4 +56,8 @@ data:
5656
{{- if $classConfig.namePattern }}
5757
namePattern: {{ $classConfig.namePattern | quote }}
5858
{{- end }}
59+
{{- if $classConfig.selector }}
60+
selector:
61+
{{- toYaml $classConfig.selector | nindent 8 }}
62+
{{- end }}
5963
{{- end }}

pkg/common/common.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ type MountConfig struct {
140140
// NamePattern name pattern check
141141
// only discover file name matching pattern("*" by default)
142142
NamePattern string `json:"namePattern" yaml:"namePattern"`
143+
// Additional selector terms to set for node affinity in addition to the provisioner node name.
144+
// Useful for shared disks as affinity can not be changed after provisioning the PV.
145+
Selector []v1.NodeSelectorTerm `json:"selector" yaml:"selector"`
143146
}
144147

145148
// RuntimeConfig stores all the objects that the provisioner needs to run

pkg/common/common_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,46 @@ func TestLoadProvisionerConfigs(t *testing.T) {
236236
},
237237
nil,
238238
},
239+
{
240+
map[string]string{"storageClassMap": `local-storage:
241+
hostDir: /mnt/disks
242+
mountDir: /mnt/disks
243+
selector:
244+
- matchExpressions:
245+
- key: "kubernetes.io/hostname"
246+
operator: "In"
247+
values:
248+
- otherNode1
249+
`,
250+
},
251+
ProvisionerConfiguration{
252+
StorageClassConfig: map[string]MountConfig{
253+
"local-storage": {
254+
HostDir: "/mnt/disks",
255+
MountDir: "/mnt/disks",
256+
BlockCleanerCommand: []string{"/scripts/quick_reset.sh"},
257+
VolumeMode: "Filesystem",
258+
NamePattern: "*",
259+
Selector: []v1.NodeSelectorTerm{
260+
{
261+
MatchExpressions: []v1.NodeSelectorRequirement{
262+
{
263+
Key: "kubernetes.io/hostname",
264+
Operator: v1.NodeSelectorOpIn,
265+
Values: []string{"otherNode1"},
266+
},
267+
},
268+
},
269+
},
270+
},
271+
},
272+
UseAlphaAPI: true,
273+
MinResyncPeriod: metav1.Duration{
274+
Duration: time.Hour + time.Minute*30,
275+
},
276+
},
277+
nil,
278+
},
239279
}
240280
for _, v := range testcases {
241281
for name, value := range v.data {

pkg/discovery/discovery.go

Lines changed: 33 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"hash/fnv"
2424
"net/http"
2525
"path/filepath"
26+
"slices"
2627
"strings"
2728
"sync"
2829
"time"
@@ -46,11 +47,10 @@ type Discoverer struct {
4647
Labels map[string]string
4748
// ProcTable is a reference to running processes so that we can prevent PV from being created while
4849
// it is being cleaned
49-
CleanupTracker *deleter.CleanupStatusTracker
50-
nodeAffinityAnn string
51-
nodeAffinity *v1.VolumeNodeAffinity
52-
classLister storagev1listers.StorageClassLister
53-
ownerReference *metav1.OwnerReference
50+
CleanupTracker *deleter.CleanupStatusTracker
51+
nodeSelector *v1.NodeSelector
52+
classLister storagev1listers.StorageClassLister
53+
ownerReference *metav1.OwnerReference
5454

5555
Readyz *readyzCheck
5656
}
@@ -106,38 +106,17 @@ func NewDiscoverer(config *common.RuntimeConfig, cleanupTracker *deleter.Cleanup
106106
return nil, fmt.Errorf("Failed to generate owner reference: %v", err)
107107
}
108108

109-
if config.UseAlphaAPI {
110-
nodeAffinity, err := generateNodeAffinity(config.Node)
111-
if err != nil {
112-
return nil, fmt.Errorf("Failed to generate node affinity: %v", err)
113-
}
114-
tmpAnnotations := map[string]string{}
115-
err = StorageNodeAffinityToAlphaAnnotation(tmpAnnotations, nodeAffinity)
116-
if err != nil {
117-
return nil, fmt.Errorf("Failed to convert node affinity to alpha annotation: %v", err)
118-
}
119-
return &Discoverer{
120-
RuntimeConfig: config,
121-
Labels: labelMap,
122-
CleanupTracker: cleanupTracker,
123-
classLister: sharedInformer.Lister(),
124-
nodeAffinityAnn: tmpAnnotations[common.AlphaStorageNodeAffinityAnnotation],
125-
ownerReference: ownerRef,
126-
Readyz: &readyzCheck{},
127-
}, nil
128-
}
129-
130-
volumeNodeAffinity, err := generateVolumeNodeAffinity(config.Node)
109+
nodeSelector, err := generateNodeSelector(config.Node)
131110
if err != nil {
132-
return nil, fmt.Errorf("Failed to generate volume node affinity: %v", err)
111+
return nil, fmt.Errorf("Failed to generate node selector: %v", err)
133112
}
134113

135114
return &Discoverer{
136115
RuntimeConfig: config,
137116
Labels: labelMap,
138117
CleanupTracker: cleanupTracker,
139118
classLister: sharedInformer.Lister(),
140-
nodeAffinity: volumeNodeAffinity,
119+
nodeSelector: nodeSelector,
141120
ownerReference: ownerRef,
142121
Readyz: &readyzCheck{},
143122
}, nil
@@ -160,7 +139,7 @@ func generateOwnerReference(node *v1.Node) (*metav1.OwnerReference, error) {
160139
}, nil
161140
}
162141

163-
func generateNodeAffinity(node *v1.Node) (*v1.NodeAffinity, error) {
142+
func generateNodeSelector(node *v1.Node) (*v1.NodeSelector, error) {
164143
if node.Labels == nil {
165144
return nil, fmt.Errorf("Node does not have labels")
166145
}
@@ -169,42 +148,14 @@ func generateNodeAffinity(node *v1.Node) (*v1.NodeAffinity, error) {
169148
return nil, fmt.Errorf("Node does not have expected label %s", common.NodeLabelKey)
170149
}
171150

172-
return &v1.NodeAffinity{
173-
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
174-
NodeSelectorTerms: []v1.NodeSelectorTerm{
175-
{
176-
MatchExpressions: []v1.NodeSelectorRequirement{
177-
{
178-
Key: common.NodeLabelKey,
179-
Operator: v1.NodeSelectorOpIn,
180-
Values: []string{nodeValue},
181-
},
182-
},
183-
},
184-
},
185-
},
186-
}, nil
187-
}
188-
189-
func generateVolumeNodeAffinity(node *v1.Node) (*v1.VolumeNodeAffinity, error) {
190-
if node.Labels == nil {
191-
return nil, fmt.Errorf("Node does not have labels")
192-
}
193-
nodeValue, found := node.Labels[common.NodeLabelKey]
194-
if !found {
195-
return nil, fmt.Errorf("Node does not have expected label %s", common.NodeLabelKey)
196-
}
197-
198-
return &v1.VolumeNodeAffinity{
199-
Required: &v1.NodeSelector{
200-
NodeSelectorTerms: []v1.NodeSelectorTerm{
201-
{
202-
MatchExpressions: []v1.NodeSelectorRequirement{
203-
{
204-
Key: common.NodeLabelKey,
205-
Operator: v1.NodeSelectorOpIn,
206-
Values: []string{nodeValue},
207-
},
151+
return &v1.NodeSelector{
152+
NodeSelectorTerms: []v1.NodeSelectorTerm{
153+
{
154+
MatchExpressions: []v1.NodeSelectorRequirement{
155+
{
156+
Key: common.NodeLabelKey,
157+
Operator: v1.NodeSelectorOpIn,
158+
Values: []string{nodeValue},
208159
},
209160
},
210161
},
@@ -437,11 +388,25 @@ func (d *Discoverer) createPV(file, class string, reclaimPolicy v1.PersistentVol
437388
OwnerReference: d.ownerReference,
438389
}
439390

391+
volumeNodeSelector := &v1.NodeSelector{
392+
NodeSelectorTerms: slices.Concat(d.nodeSelector.NodeSelectorTerms, config.Selector),
393+
}
394+
440395
if d.UseAlphaAPI {
396+
nodeAffinity := &v1.NodeAffinity{
397+
RequiredDuringSchedulingIgnoredDuringExecution: volumeNodeSelector,
398+
}
399+
tmpAnnotations := map[string]string{}
400+
err := StorageNodeAffinityToAlphaAnnotation(tmpAnnotations, nodeAffinity)
401+
if err != nil {
402+
return fmt.Errorf("error converting volume affinity to alpha annotation: %v", err)
403+
}
441404
localPVConfig.UseAlphaAPI = true
442-
localPVConfig.AffinityAnn = d.nodeAffinityAnn
405+
localPVConfig.AffinityAnn = tmpAnnotations[common.AlphaStorageNodeAffinityAnnotation]
443406
} else {
444-
localPVConfig.NodeAffinity = d.nodeAffinity
407+
localPVConfig.NodeAffinity = &v1.VolumeNodeAffinity{
408+
Required: volumeNodeSelector,
409+
}
445410
}
446411

447412
if config.FsType != "" {

pkg/discovery/discovery_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -753,16 +753,16 @@ func TestUseAlphaAPI(t *testing.T) {
753753
if d.UseAlphaAPI {
754754
t.Fatal("UseAlphaAPI should be false")
755755
}
756-
if len(d.nodeAffinityAnn) != 0 || d.nodeAffinity == nil {
757-
t.Fatal("the value nodeAffinityAnn shouldn't be set while nodeAffinity should")
756+
if d.nodeSelector == nil {
757+
t.Fatal("the value nodeSelector should be set")
758758
}
759759

760760
d = testSetup(t, test, true, false)
761761
if !d.UseAlphaAPI {
762762
t.Fatal("UseAlphaAPI should be true")
763763
}
764-
if d.nodeAffinity != nil || len(d.nodeAffinityAnn) == 0 {
765-
t.Fatal("the value nodeAffinityAnn should be set while nodeAffinity should not")
764+
if d.nodeSelector == nil {
765+
t.Fatal("the value nodeSelector should be set")
766766
}
767767
}
768768

0 commit comments

Comments
 (0)