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
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ manager:
# Environment variables
env: []

# Image pull secrets
imagePullSecrets: []

# Pod-level security settings
podSecurityContext:
runAsNonRoot: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ manager:
# Environment variables
env: []

# Image pull secrets
imagePullSecrets: []

# Pod-level security settings
podSecurityContext:
runAsNonRoot: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ manager:
# Environment variables
env: []

# Image pull secrets
imagePullSecrets: []

# Pod-level security settings
podSecurityContext:
runAsNonRoot: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (c *ChartConverter) ExtractDeploymentConfig() map[string]interface{} {
}

extractPodSecurityContext(specMap, config)

extractImagePullSecrets(specMap, config)
container := firstManagerContainer(specMap)
if container == nil {
return config
Expand Down Expand Up @@ -135,6 +135,20 @@ func extractDeploymentSpec(deployment *unstructured.Unstructured) map[string]int
return specMap
}

func extractImagePullSecrets(specMap map[string]interface{}, config map[string]interface{}) {
imagePullSecrets, found, err := unstructured.NestedFieldNoCopy(specMap, "imagePullSecrets")
if !found || err != nil {
return
}

imagePullSecretsList, ok := imagePullSecrets.([]interface{})
if !ok || len(imagePullSecretsList) == 0 {
return
}

config["imagePullSecrets"] = imagePullSecretsList
}

func extractPodSecurityContext(specMap map[string]interface{}, config map[string]interface{}) {
podSecurityContext, found, err := unstructured.NestedFieldNoCopy(specMap, "securityContext")
if !found || err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,39 @@ var _ = Describe("ChartConverter", func() {
Expect(config["webhookPort"]).To(Equal(9444))
})

It("should extract imagePullSecrets", func() {
// Set up deployment with image pull secrets
containers := []interface{}{
map[string]interface{}{
"name": "manager",
"image": "controller:latest",
},
}
imagePullSecrets := []interface{}{
map[string]interface{}{
"name": "test-secret",
},
}
// Set the image pull secrets
err := unstructured.SetNestedSlice(
resources.Deployment.Object,
imagePullSecrets,
"spec", "template", "spec", "imagePullSecrets",
)
Expect(err).NotTo(HaveOccurred())
// Set the containers
err = unstructured.SetNestedSlice(
resources.Deployment.Object,
containers,
"spec", "template", "spec", "containers",
)
Expect(err).NotTo(HaveOccurred())

config := converter.ExtractDeploymentConfig()
Expect(config).To(HaveKey("imagePullSecrets"))
Expect(config["imagePullSecrets"]).To(Equal(imagePullSecrets))
})

It("should handle deployment without containers", func() {
config := converter.ExtractDeploymentConfig()
Expect(config).To(BeEmpty())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ func (t *HelmTemplater) templateDeploymentFields(yamlContent string) string {
// Template configuration fields
yamlContent = t.templateImageReference(yamlContent)
yamlContent = t.templateEnvironmentVariables(yamlContent)
yamlContent = t.templateImagePullSecrets(yamlContent)
yamlContent = t.templatePodSecurityContext(yamlContent)
yamlContent = t.templateContainerSecurityContext(yamlContent)
yamlContent = t.templateResources(yamlContent)
Expand Down Expand Up @@ -325,6 +326,57 @@ func (t *HelmTemplater) templateVolumes(yamlContent string) string {
return yamlContent
}

// templateImagePullSecrets exposes imagePullSecrets via values.yaml
func (t *HelmTemplater) templateImagePullSecrets(yamlContent string) string {
if !strings.Contains(yamlContent, "imagePullSecrets:") {
return yamlContent
}

lines := strings.Split(yamlContent, "\n")
for i := 0; i < len(lines); i++ {
// Use prefix to allow `imagePullSecrets: []` to be preserved
if !strings.HasPrefix(strings.TrimSpace(lines[i]), "imagePullSecrets:") {
continue
}
indentStr, indentLen := leadingWhitespace(lines[i])
end := i + 1
for ; end < len(lines); end++ {
trimmed := strings.TrimSpace(lines[end])
if trimmed == "" {
break
}
lineIndent := len(lines[end]) - len(strings.TrimLeft(lines[end], " \t"))
if lineIndent < indentLen {
break
}
if lineIndent == indentLen && !strings.HasPrefix(trimmed, "-") {
break
}
}

if i+1 < len(lines) && strings.Contains(lines[i+1], ".Values.manager.imagePullSecrets") {
return yamlContent
}

childIndent := indentStr + " "
childIndentWidth := strconv.Itoa(len(childIndent))

block := []string{
indentStr + "{{- if .Values.manager.imagePullSecrets }}",
indentStr + "imagePullSecrets:",
childIndent + "{{- toYaml .Values.manager.imagePullSecrets | nindent " + childIndentWidth + " }}",
indentStr + "{{- end }}",
}

newLines := append([]string{}, lines[:i]...)
newLines = append(newLines, block...)
newLines = append(newLines, lines[end:]...)
return strings.Join(newLines, "\n")
}

return yamlContent
}

// templatePodSecurityContext exposes podSecurityContext via values.yaml
func (t *HelmTemplater) templatePodSecurityContext(yamlContent string) string {
if !strings.Contains(yamlContent, "securityContext:") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,58 @@ metadata:
Expect(result).NotTo(ContainSubstring(`{{- include "chart.labels"`))
Expect(result).NotTo(ContainSubstring(`{{- include "chart.annotations"`))
})

It("should template imagePullSecrets", func() {
deploymentResource := &unstructured.Unstructured{}
deploymentResource.SetAPIVersion("apps/v1")
deploymentResource.SetKind("Deployment")
deploymentResource.SetName("test-project-controller-manager")

content := `apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
imagePullSecrets:
- name: test-secret
containers:
- args:
- --metrics-bind-address=:8443
- --health-probe-bind-address=:8081
- --webhook-cert-path=/tmp/k8s-webhook-server/serving-certs/tls.crt
- --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs/tls.crt
- --leader-elect`

result := templater.ApplyHelmSubstitutions(content, deploymentResource)

Expect(result).To(ContainSubstring("imagePullSecrets:"))
Expect(result).NotTo(ContainSubstring("test-secret"))
})

It("should template empty imagePullSecrets", func() {
deploymentResource := &unstructured.Unstructured{}
deploymentResource.SetAPIVersion("apps/v1")
deploymentResource.SetKind("Deployment")
deploymentResource.SetName("test-project-controller-manager")

content := `apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
imagePullSecrets: []
containers:
- args:
- --metrics-bind-address=:8443
- --health-probe-bind-address=:8081
- --webhook-cert-path=/tmp/k8s-webhook-server/serving-certs/tls.crt
- --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs/tls.crt
- --leader-elect`

result := templater.ApplyHelmSubstitutions(content, deploymentResource)

Expect(result).To(ContainSubstring("imagePullSecrets:"))
})
})

Context("conditional wrapping", func() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,25 @@ func (f *HelmValuesBasic) addDeploymentConfig(buf *bytes.Buffer) {
buf.WriteString(" env: []\n\n")
}

// Add image pull secrets
if imagePullSecrets, exists := f.DeploymentConfig["imagePullSecrets"]; exists && imagePullSecrets != nil {
buf.WriteString(" # Image pull secrets\n")
buf.WriteString(" imagePullSecrets:\n")
if imagePullSecretsYaml, err := yaml.Marshal(imagePullSecrets); err == nil {
lines := bytes.Split(imagePullSecretsYaml, []byte("\n"))
for _, line := range lines {
if len(line) > 0 {
buf.WriteString(" ")
buf.Write(line)
buf.WriteString("\n")
}
}
}
buf.WriteString("\n")
} else {
f.addDefaultImagePullSecrets(buf)
}

// Add podSecurityContext
if podSecCtx, exists := f.DeploymentConfig["podSecurityContext"]; exists && podSecCtx != nil {
buf.WriteString(" # Pod-level security settings\n")
Expand Down Expand Up @@ -291,6 +310,7 @@ func (f *HelmValuesBasic) addDefaultDeploymentSections(buf *bytes.Buffer) {
buf.WriteString(" # Environment variables\n")
buf.WriteString(" env: []\n\n")

f.addDefaultImagePullSecrets(buf)
f.addDefaultPodSecurityContext(buf)
f.addDefaultSecurityContext(buf)
f.addDefaultResources(buf)
Expand Down Expand Up @@ -323,6 +343,12 @@ func (f *HelmValuesBasic) addArgsSection(buf *bytes.Buffer) {
buf.WriteString(" args: []\n\n")
}

// addDefaultImagePullSecrets adds default imagePullSecrets section
func (f *HelmValuesBasic) addDefaultImagePullSecrets(buf *bytes.Buffer) {
buf.WriteString(" # Image pull secrets\n")
buf.WriteString(" imagePullSecrets: []\n\n")
}

// addDefaultPodSecurityContext adds default podSecurityContext section
func (f *HelmValuesBasic) addDefaultPodSecurityContext(buf *bytes.Buffer) {
buf.WriteString(" # Pod-level security settings\n")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ var _ = Describe("HelmValuesBasic", func() {
Expect(content).To(ContainSubstring("metrics:"))
Expect(content).To(ContainSubstring("prometheus:"))
Expect(content).To(ContainSubstring("rbacHelpers:"))
Expect(content).To(ContainSubstring("imagePullSecrets: []"))
})
})

Expand Down Expand Up @@ -84,6 +85,7 @@ var _ = Describe("HelmValuesBasic", func() {
Expect(content).To(ContainSubstring("metrics:"))
Expect(content).To(ContainSubstring("prometheus:"))
Expect(content).To(ContainSubstring("rbacHelpers:"))
Expect(content).To(ContainSubstring("imagePullSecrets: []"))
})
})

Expand Down Expand Up @@ -162,6 +164,33 @@ var _ = Describe("HelmValuesBasic", func() {
})
})

Context("with multiple imagePullSecrets", func() {
BeforeEach(func() {
valuesTemplate = &HelmValuesBasic{
DeploymentConfig: map[string]interface{}{
"imagePullSecrets": []interface{}{
map[string]interface{}{
"name": "test-secret",
},
map[string]interface{}{
"name": "test-secret2",
},
},
},
}
valuesTemplate.InjectProjectName("test-private-project")
err := valuesTemplate.SetTemplateDefaults()
Expect(err).NotTo(HaveOccurred())
})

It("should render multiple imagePullSecrets", func() {
content := valuesTemplate.GetBody()
Expect(content).To(ContainSubstring("imagePullSecrets:"))
Expect(content).To(ContainSubstring("- name: test-secret"))
Expect(content).To(ContainSubstring("- name: test-secret2"))
})
})

Context("with complex env variables", func() {
BeforeEach(func() {
valuesTemplate = &HelmValuesBasic{
Expand Down
3 changes: 3 additions & 0 deletions testdata/project-v4-with-plugins/dist/chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ manager:
- name: MEMCACHED_IMAGE
value: memcached:1.6.26-alpine3.19

# Image pull secrets
imagePullSecrets: []

# Pod-level security settings
podSecurityContext:
runAsNonRoot: true
Expand Down
Loading