Skip to content

Commit 895231c

Browse files
robinlioretRobin LIORET
authored andcommitted
feat: helm v2-alpha: add nodeSelector, tolerations and affinity
Signed-off-by: Robin LIORET <[email protected]>
1 parent b5a66b0 commit 895231c

File tree

5 files changed

+246
-1
lines changed

5 files changed

+246
-1
lines changed

pkg/plugins/common/kustomize/v2/scaffolds/internal/templates/config/manager/config.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ spec:
8080
# according to the platforms which are supported by your solution.
8181
# It is considered best practice to support multiple architectures. You can
8282
# build your manager image using the makefile target docker-buildx.
83-
# affinity:
83+
affinity: {}
8484
# nodeAffinity:
8585
# requiredDuringSchedulingIgnoredDuringExecution:
8686
# nodeSelectorTerms:
@@ -96,6 +96,8 @@ spec:
9696
# operator: In
9797
# values:
9898
# - linux
99+
nodeSelector: {}
100+
tolerations: []
99101
securityContext:
100102
# Projects are configured by default to adhere to the "restricted" Pod Security Standards.
101103
# This ensures that deployments meet the highest security requirements for Kubernetes.

pkg/plugins/optional/helm/v2alpha/scaffolds/internal/kustomize/chart_converter.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ func (c *ChartConverter) ExtractDeploymentConfig() map[string]interface{} {
105105
}
106106

107107
extractPodSecurityContext(specMap, config)
108+
extractPodNodeSelector(specMap, config)
109+
extractPodTolerations(specMap, config)
110+
extractPodAffinity(specMap, config)
108111

109112
container := firstManagerContainer(specMap)
110113
if container == nil {
@@ -149,6 +152,48 @@ func extractPodSecurityContext(specMap map[string]interface{}, config map[string
149152
config["podSecurityContext"] = podSecurityContext
150153
}
151154

155+
func extractPodNodeSelector(specMap map[string]interface{}, config map[string]interface{}) {
156+
raw, found, err := unstructured.NestedFieldNoCopy(specMap, "nodeSelector")
157+
if !found || err != nil {
158+
return
159+
}
160+
161+
result, ok := raw.(map[string]interface{})
162+
if !ok || len(result) == 0 {
163+
return
164+
}
165+
166+
config["podNodeSelector"] = result
167+
}
168+
169+
func extractPodTolerations(specMap map[string]interface{}, config map[string]interface{}) {
170+
raw, found, err := unstructured.NestedFieldNoCopy(specMap, "tolerations")
171+
if !found || err != nil {
172+
return
173+
}
174+
175+
result, ok := raw.([]interface{})
176+
if !ok || len(result) == 0 {
177+
return
178+
}
179+
180+
config["podTolerations"] = result
181+
}
182+
183+
func extractPodAffinity(specMap map[string]interface{}, config map[string]interface{}) {
184+
raw, found, err := unstructured.NestedFieldNoCopy(specMap, "affinity")
185+
if !found || err != nil {
186+
return
187+
}
188+
189+
result, ok := raw.(map[string]interface{})
190+
if !ok || len(result) == 0 {
191+
return
192+
}
193+
194+
config["podAffinity"] = result
195+
}
196+
152197
func firstManagerContainer(specMap map[string]interface{}) map[string]interface{} {
153198
containers, found, err := unstructured.NestedFieldNoCopy(specMap, "containers")
154199
if !found || err != nil {

pkg/plugins/optional/helm/v2alpha/scaffolds/internal/kustomize/helm_templater.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,9 @@ func (t *HelmTemplater) templateDeploymentFields(yamlContent string) string {
190190
yamlContent = t.templateVolumeMounts(yamlContent)
191191
yamlContent = t.templateVolumes(yamlContent)
192192
yamlContent = t.templateControllerManagerArgs(yamlContent)
193+
yamlContent = t.templateNodeSelector(yamlContent)
194+
yamlContent = t.templateAffinity(yamlContent)
195+
yamlContent = t.templateTolerations(yamlContent)
193196

194197
return yamlContent
195198
}
@@ -620,6 +623,141 @@ func (t *HelmTemplater) templateImageReference(yamlContent string) string {
620623
return yamlContent
621624
}
622625

626+
func (t *HelmTemplater) templateNodeSelector(yamlContent string) string {
627+
if !strings.Contains(yamlContent, "nodeSelector:") {
628+
return yamlContent
629+
}
630+
lines := strings.Split(yamlContent, "\n")
631+
for i := 0; i < len(lines); i++ {
632+
if !strings.HasPrefix(strings.TrimSpace(lines[i]), "nodeSelector") {
633+
continue
634+
}
635+
end := i + 1
636+
trimmed := strings.TrimSpace(lines[i])
637+
if len(trimmed) == len("nodeSelector:") {
638+
_, indentLen := leadingWhitespace(lines[i])
639+
for j := end; j < len(lines); j++ {
640+
_, indentLenLine := leadingWhitespace(lines[j])
641+
if indentLenLine <= indentLen {
642+
end = j
643+
break
644+
}
645+
}
646+
}
647+
648+
indentStr, indentLen := leadingWhitespace(lines[i])
649+
650+
var builder strings.Builder
651+
builder.WriteString(indentStr)
652+
builder.WriteString("{{- with .Values.manager.nodeSelector }}\n")
653+
builder.WriteString(indentStr)
654+
builder.WriteString("nodeSelector: ")
655+
builder.WriteString("{{ toYaml . | nindent ")
656+
builder.WriteString(strconv.Itoa(indentLen + 2))
657+
builder.WriteString(" }}\n")
658+
builder.WriteString(indentStr)
659+
builder.WriteString("{{- end }}\n")
660+
661+
newBlock := strings.TrimRight(builder.String(), "\n")
662+
663+
newLines := append([]string{}, lines[:i]...)
664+
newLines = append(newLines, strings.Split(newBlock, "\n")...)
665+
newLines = append(newLines, lines[end:]...)
666+
return strings.Join(newLines, "\n")
667+
}
668+
return yamlContent
669+
}
670+
671+
func (t *HelmTemplater) templateAffinity(yamlContent string) string {
672+
if !strings.Contains(yamlContent, "affinity:") {
673+
return yamlContent
674+
}
675+
lines := strings.Split(yamlContent, "\n")
676+
for i := 0; i < len(lines); i++ {
677+
if !strings.HasPrefix(strings.TrimSpace(lines[i]), "affinity") {
678+
continue
679+
}
680+
end := i + 1
681+
trimmed := strings.TrimSpace(lines[i])
682+
if len(trimmed) == len("affinity:") {
683+
_, indentLen := leadingWhitespace(lines[i])
684+
for j := end; j < len(lines); j++ {
685+
_, indentLenLine := leadingWhitespace(lines[j])
686+
if indentLenLine <= indentLen {
687+
end = j
688+
break
689+
}
690+
}
691+
}
692+
693+
indentStr, indentLen := leadingWhitespace(lines[i])
694+
695+
var builder strings.Builder
696+
builder.WriteString(indentStr)
697+
builder.WriteString("{{- with .Values.manager.affinity }}\n")
698+
builder.WriteString(indentStr)
699+
builder.WriteString("affinity: ")
700+
builder.WriteString("{{ toYaml . | nindent ")
701+
builder.WriteString(strconv.Itoa(indentLen + 2))
702+
builder.WriteString(" }}\n")
703+
builder.WriteString(indentStr)
704+
builder.WriteString("{{- end }}\n")
705+
706+
newBlock := strings.TrimRight(builder.String(), "\n")
707+
708+
newLines := append([]string{}, lines[:i]...)
709+
newLines = append(newLines, strings.Split(newBlock, "\n")...)
710+
newLines = append(newLines, lines[end:]...)
711+
return strings.Join(newLines, "\n")
712+
}
713+
return yamlContent
714+
}
715+
716+
func (t *HelmTemplater) templateTolerations(yamlContent string) string {
717+
if !strings.Contains(yamlContent, "tolerations:") {
718+
return yamlContent
719+
}
720+
lines := strings.Split(yamlContent, "\n")
721+
for i := 0; i < len(lines); i++ {
722+
if !strings.HasPrefix(strings.TrimSpace(lines[i]), "tolerations") {
723+
continue
724+
}
725+
end := i + 1
726+
trimmed := strings.TrimSpace(lines[i])
727+
if len(trimmed) == len("tolerations:") {
728+
_, indentLen := leadingWhitespace(lines[i])
729+
for j := end; j < len(lines); j++ {
730+
_, indentLenLine := leadingWhitespace(lines[j])
731+
if indentLenLine <= indentLen {
732+
end = j
733+
break
734+
}
735+
}
736+
}
737+
738+
indentStr, indentLen := leadingWhitespace(lines[i])
739+
740+
var builder strings.Builder
741+
builder.WriteString(indentStr)
742+
builder.WriteString("{{- with .Values.manager.tolerations }}\n")
743+
builder.WriteString(indentStr)
744+
builder.WriteString("tolerations: ")
745+
builder.WriteString("{{ toYaml . | nindent ")
746+
builder.WriteString(strconv.Itoa(indentLen + 2))
747+
builder.WriteString(" }}\n")
748+
builder.WriteString(indentStr)
749+
builder.WriteString("{{- end }}\n")
750+
751+
newBlock := strings.TrimRight(builder.String(), "\n")
752+
753+
newLines := append([]string{}, lines[:i]...)
754+
newLines = append(newLines, strings.Split(newBlock, "\n")...)
755+
newLines = append(newLines, lines[end:]...)
756+
return strings.Join(newLines, "\n")
757+
}
758+
return yamlContent
759+
}
760+
623761
// makeWebhookAnnotationsConditional makes only cert-manager annotations conditional, not the entire webhook
624762
func (t *HelmTemplater) makeWebhookAnnotationsConditional(yamlContent string) string {
625763
// Find cert-manager.io/inject-ca-from annotation and make it conditional

pkg/plugins/optional/helm/v2alpha/scaffolds/internal/templates/values_basic.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,63 @@ func (f *HelmValuesBasic) addDeploymentConfig(buf *bytes.Buffer) {
284284
} else {
285285
f.addDefaultResources(buf)
286286
}
287+
288+
buf.WriteString(" # Pod's affinity\n")
289+
if affinity, exists := f.DeploymentConfig["podAffinity"]; exists && affinity != nil {
290+
buf.WriteString(" affinity:\n")
291+
if affYaml, err := yaml.Marshal(affinity); err == nil {
292+
lines := bytes.Split(affYaml, []byte("\n"))
293+
for _, line := range lines {
294+
if len(line) > 0 {
295+
buf.WriteString(" ")
296+
buf.Write(line)
297+
buf.WriteString("\n")
298+
}
299+
}
300+
}
301+
buf.WriteString("\n")
302+
} else {
303+
buf.WriteString(" affinity: {}\n")
304+
buf.WriteString("\n")
305+
}
306+
307+
buf.WriteString(" # Pod's node selector\n")
308+
if nodeSelector, exists := f.DeploymentConfig["podNodeSelector"]; exists && nodeSelector != nil {
309+
buf.WriteString(" nodeSelector:\n")
310+
if nodYaml, err := yaml.Marshal(nodeSelector); err == nil {
311+
lines := bytes.Split(nodYaml, []byte("\n"))
312+
for _, line := range lines {
313+
if len(line) > 0 {
314+
buf.WriteString(" ")
315+
buf.Write(line)
316+
buf.WriteString("\n")
317+
}
318+
}
319+
}
320+
buf.WriteString("\n")
321+
} else {
322+
buf.WriteString(" nodeSelector: {}\n")
323+
buf.WriteString("\n")
324+
}
325+
326+
buf.WriteString(" # Pod's tolerations\n")
327+
if tolerations, exists := f.DeploymentConfig["podTolerations"]; exists && tolerations != nil {
328+
buf.WriteString(" tolerations:\n")
329+
if tolYaml, err := yaml.Marshal(tolerations); err == nil {
330+
lines := bytes.Split(tolYaml, []byte("\n"))
331+
for _, line := range lines {
332+
if len(line) > 0 {
333+
buf.WriteString(" ")
334+
buf.Write(line)
335+
buf.WriteString("\n")
336+
}
337+
}
338+
}
339+
buf.WriteString("\n")
340+
} else {
341+
buf.WriteString(" tolerations: []\n")
342+
buf.WriteString("\n")
343+
}
287344
}
288345

289346
// addDefaultDeploymentSections adds default sections when no deployment config is available

pkg/plugins/optional/helm/v2alpha/scaffolds/internal/templates/values_basic_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,9 @@ var _ = Describe("HelmValuesBasic", func() {
159159
Expect(content).To(ContainSubstring("resources:"))
160160
Expect(content).To(ContainSubstring("cpu: 100m"))
161161
Expect(content).To(ContainSubstring("memory: 128Mi"))
162+
Expect(content).To(ContainSubstring("affinity: {}"))
163+
Expect(content).To(ContainSubstring("nodeSelector: {}"))
164+
Expect(content).To(ContainSubstring("tolerations: []"))
162165
})
163166
})
164167

0 commit comments

Comments
 (0)