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 @@ -99,7 +99,10 @@ var (
)

// FindManagerContainerRange returns the 0-based inclusive line range [start, end]
// of the manager container in yamlContent.
// of the manager container in yamlContent. The manager container is the one named by
// the kubectl.kubernetes.io/default-container annotation, falling back to "manager"
// when the annotation is absent (see GetDefaultContainerName) — the same detection the
// manager values extractor uses, so both passes scope to the same container.
// Returns (-1, -1) when not found; callers use this to restrict substitutions to the manager only.
func FindManagerContainerRange(yamlContent string) (int, int) {
name := GetDefaultContainerName(yamlContent)
Expand Down Expand Up @@ -147,6 +150,33 @@ func FindManagerContainerRange(yamlContent string) (int, int) {
return -1, -1
}

// applyToManagerContainer runs transform on only the manager container's block within
// yamlContent, leaving sidecar containers and the rest of the Deployment untouched.
// The manager container is located via FindManagerContainerRange (the
// kubectl.kubernetes.io/default-container annotation, falling back to "manager").
// When it cannot be located, yamlContent is returned unchanged: templating is skipped
// rather than applied document-wide, so brittle detection can never leak manager
// substitutions into sidecar containers.
func applyToManagerContainer(yamlContent string, transform func(string) string) string {
start, end := FindManagerContainerRange(yamlContent)
if start < 0 {
return yamlContent
}
Comment on lines +160 to +164

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@v47 could you check this one ??


lines := strings.Split(yamlContent, "\n")
before := lines[:start]
block := strings.Join(lines[start:end+1], "\n")
after := lines[end+1:]

transformed := strings.Split(transform(block), "\n")

result := make([]string, 0, len(before)+len(transformed)+len(after))
result = append(result, before...)
result = append(result, transformed...)
result = append(result, after...)
return strings.Join(result, "\n")
}

func findListField(lines []string, field string) (int, int) {
for i, line := range lines {
if strings.TrimSpace(line) == field {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,59 @@ import (
"sigs.k8s.io/kubebuilder/v4/pkg/plugins/optional/helm/v2alpha/internal/common"
)

// Port-templating regexes are compiled once at package initialization rather than on
// every call. Patterns that only differ by replacement string (port/targetPort) are
// shared across the webhook and metrics substitutions.
var (
webhookContainerPortRE = regexp.MustCompile(`(?m)(\s*- )?containerPort:\s*\d+(\s*\n\s*name:\s*webhook-server)`)
metricsBindAddressRE = regexp.MustCompile(`--metrics-bind-address=(\[[^\]]*\]|[^\s:]*):([0-9]+)`)
webhookPortArgRE = regexp.MustCompile(`--webhook-port=([0-9]+)`)
portRE = regexp.MustCompile(`(\s*)port:\s*\d+`)
targetPortRE = regexp.MustCompile(`(\s*)targetPort:\s*\d+`)
metricsHTTPSNameRE = regexp.MustCompile(`(\s*)- name:\s*https(\s+port:)`)

healthProbeBindAddressRE = regexp.MustCompile(`--health-probe-bind-address=(\[[^\]]*\]|[^\s:]*):([0-9]+)`)
healthContainerPortRE = regexp.MustCompile(`(?m)(\s*- )?containerPort:\s*\d+(\s*\n\s*name:\s*health\b)`)
healthProbeHTTPGetPortRE = regexp.MustCompile(`(path:\s*/(?:healthz|readyz)[ \t]*\n\s*port:\s*)\d+`)
)

// TemplatePorts templates port numbers for Services, Deployments, and NetworkPolicies using values.yaml.
func TemplatePorts(yamlContent string, resource *unstructured.Unstructured) string {
// For Deployments, port/probe/argument templating is scoped to the manager
// container so sidecars that happen to use the same ports, probe paths, or
// bind-address flags are left untouched.
if resource.GetKind() == common.KindDeployment {
return applyToManagerContainer(yamlContent, templateManagerContainerPorts)
}

return templateServicePorts(yamlContent, resource)
}

// templateManagerContainerPorts templates the manager container's port-related fields:
// the webhook containerPort, the metrics and webhook bind-address arguments, and the
// health probe port. It is always applied to the manager container block only, so
// sidecar ports and probes are never rewritten.
func templateManagerContainerPorts(managerContainer string) string {
// Replace containerPort for webhook-server with template (matches any numeric port).
// The regex is self-guarding: it only matches a containerPort named "webhook-server".
managerContainer = webhookContainerPortRE.
ReplaceAllString(managerContainer, "${1}containerPort: {{ .Values.webhook.port }}${2}")

// Replace --metrics-bind-address with templated port.
// Supports :PORT, HOST:PORT, and IPv6 [::1]:PORT formats.
managerContainer = metricsBindAddressRE.
ReplaceAllString(managerContainer, "--metrics-bind-address=$1:{{ .Values.metrics.port }}")

// Replace --webhook-port with templated version (matches any numeric port).
managerContainer = webhookPortArgRE.
ReplaceAllString(managerContainer, "--webhook-port={{ .Values.webhook.port }}")

return templateHealthProbePort(managerContainer)
}

// templateServicePorts templates the ports of the webhook and metrics Services and
// NetworkPolicies. These resources have no containers, so they are identified by name.
func templateServicePorts(yamlContent string, resource *unstructured.Unstructured) string {
resourceName := resource.GetName()
resourceKind := resource.GetKind()

Expand All @@ -40,68 +91,41 @@ func TemplatePorts(yamlContent string, resource *unstructured.Unstructured) stri
strings.HasSuffix(resourceName, "-metrics-service"))) ||
(resourceKind == common.KindNetworkPolicy && strings.HasSuffix(resourceName, "allow-metrics-traffic"))

// For Deployments, detect webhook ports from content
if resourceKind == common.KindDeployment {
if strings.Contains(yamlContent, "webhook-server") || strings.Contains(yamlContent, "name: webhook") {
isWebhook = true
}
}

// Template webhook ports
if isWebhook {
if resourceKind == common.KindNetworkPolicy {
yamlContent = regexp.MustCompile(`(\s*)port:\s*\d+`).
yamlContent = portRE.
ReplaceAllString(yamlContent, "${1}port: {{ .Values.webhook.port }}")
return yamlContent
}

// Replace containerPort for webhook-server with template (matches any numeric port)
if strings.Contains(yamlContent, "webhook-server") {
yamlContent = regexp.MustCompile(`(?m)(\s*- )?containerPort:\s*\d+(\s*\n\s*name:\s*webhook-server)`).
ReplaceAllString(yamlContent, "${1}containerPort: {{ .Values.webhook.port }}${2}")
}

// Replace targetPort with webhook.port template (matches any numeric port)
yamlContent = regexp.MustCompile(`(\s*)targetPort:\s*\d+`).
yamlContent = targetPortRE.
ReplaceAllString(yamlContent, "${1}targetPort: {{ .Values.webhook.port }}")
}

// Template metrics ports
if isMetrics {
// Replace port with metrics.port template (matches any numeric port)
yamlContent = regexp.MustCompile(`(\s*)port:\s*\d+`).
yamlContent = portRE.
ReplaceAllString(yamlContent, "${1}port: {{ .Values.metrics.port }}")

if resourceKind == common.KindNetworkPolicy {
return yamlContent
}

// Replace targetPort with metrics.port template (matches any numeric port)
yamlContent = regexp.MustCompile(`(\s*)targetPort:\s*\d+`).
yamlContent = targetPortRE.
ReplaceAllString(yamlContent, "${1}targetPort: {{ .Values.metrics.port }}")

// Template port name based on metrics.secure (http vs https)
// This ensures Service and ServiceMonitor use the correct scheme
if resource.GetKind() == common.KindService {
yamlContent = regexp.MustCompile(`(\s*)- name:\s*https(\s+port:)`).
if resourceKind == common.KindService {
yamlContent = metricsHTTPSNameRE.
ReplaceAllString(yamlContent, `${1}- name: {{ if .Values.metrics.secure }}https{{ else }}http{{ end }}${2}`)
}
}

// Template port-related arguments in Deployment
if resource.GetKind() == common.KindDeployment {
// Replace --metrics-bind-address with templated port
// Supports :PORT, HOST:PORT, and IPv6 [::1]:PORT formats
yamlContent = regexp.MustCompile(`--metrics-bind-address=(\[[^\]]*\]|[^\s:]*):([0-9]+)`).
ReplaceAllString(yamlContent, "--metrics-bind-address=$1:{{ .Values.metrics.port }}")

// Replace --webhook-port with templated version (matches any numeric port)
yamlContent = regexp.MustCompile(`--webhook-port=([0-9]+)`).
ReplaceAllString(yamlContent, "--webhook-port={{ .Values.webhook.port }}")

yamlContent = templateHealthProbePort(yamlContent)
}

return yamlContent
}

Expand All @@ -114,15 +138,15 @@ func templateHealthProbePort(yamlContent string) string {
const healthPortTemplate = "{{ .Values.manager.healthProbe.port }}"

// --health-probe-bind-address=:PORT (also HOST:PORT and IPv6 [::1]:PORT)
yamlContent = regexp.MustCompile(`--health-probe-bind-address=(\[[^\]]*\]|[^\s:]*):([0-9]+)`).
yamlContent = healthProbeBindAddressRE.
ReplaceAllString(yamlContent, "--health-probe-bind-address=$1:"+healthPortTemplate)

// containerPort for the port named "health"
yamlContent = regexp.MustCompile(`(?m)(\s*- )?containerPort:\s*\d+(\s*\n\s*name:\s*health\b)`).
yamlContent = healthContainerPortRE.
ReplaceAllString(yamlContent, "${1}containerPort: "+healthPortTemplate+"${2}")

// liveness (/healthz) and readiness (/readyz) httpGet ports
yamlContent = regexp.MustCompile(`(path:\s*/(?:healthz|readyz)[ \t]*\n\s*port:\s*)\d+`).
yamlContent = healthProbeHTTPGetPortRE.
ReplaceAllString(yamlContent, "${1}"+healthPortTemplate)

return yamlContent
Expand Down
Loading