helm/v2-alpha: the generated chart exposes metrics values the manager never receives

Author: camilamacedo86Created Sep 5, 2026Updated Sep 13, 2026
Labelskind/bug

Summary

The chart the helm/v2-alpha plugin generates always writes a metrics section into values.yaml and always scaffolds the metrics Service, the ServiceMonitor and the metrics RBAC when the project has a metrics Service. But the manager Deployment only receives the metrics arguments when the project's own manager already carried a --metrics-bind-address.

For every other project the chart offers metrics.enabled, metrics.port and metrics.secure, and none of the three reaches the manager. The chart says one thing and the workload it installs does another.

Where it comes from

pkg/plugins/optional/helm/v2alpha/scaffolds/internal/kustomize/templater/appliers/manager.go, in templateControllerManagerArgs:

go
if metricsLine != "" {
    ...
    builder.WriteString("{{- if .Values.metrics.enabled }}\n")
    builder.WriteString(metricsLine)
    ...
}

metricsLine is the project's own --metrics-bind-address line. When the project has none, the whole block is skipped: no bind address, no --metrics-secure=false, and no {{- else }} branch to turn metrics off.

The values are written independently of that. metrics.enabled comes from a *-metrics-service existing in the kustomize output, not from the manager's arguments, so it can be true for a manager that receives nothing.

What happens today

The scaffolded cmd/main.go declares:

go
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", ...)

0 disables the metrics server, so a manager that receives no bind address serves no metrics at all.

The project's manager runs with values.yaml says The rendered manager gets Result
--metrics-bind-address=:8443 enabled: true, port: 8443, secure: true the templated block correct
--metrics-secure=false, no bind address enabled: true, port: 8443, secure: false nothing the Service, the ServiceMonitor and the http port name are all rendered for plain HTTP metrics, and the manager serves none
neither argument enabled: true, port: 8443, secure: true nothing the whole metrics stack is installed around a manager that never starts a metrics server
metrics Service on 7443, no bind address enabled: true, port: 7443, secure: true nothing same, and metrics.port is advertised but unused

In the last three rows, setting metrics.enabled, metrics.port or metrics.secure at install time changes the Service, the ServiceMonitor and the RBAC, and changes nothing about the manager.

Why it matters

A chart user has no way to turn the metrics endpoint on, off, or onto plain HTTP for these projects, even though the chart presents all three settings. The --metrics-secure=false case is the sharpest: the project asked for plain HTTP metrics, the chart records secure: false and renders the http port name and no metrics-auth ClusterRole, and the manager still serves nothing.

Reproduction

Take any project, remove --metrics-bind-address from the manager's args in the kustomize output, keep the metrics Service, and run kubebuilder edit --plugins=helm/v2-alpha. Then:

bash
helm template dist/chart | grep -A5 'args:'

The manager carries --leader-elect, the health probe address and the cert paths, and no metrics argument, while dist/chart/values.yaml carries a full metrics: section and the chart renders the metrics Service and ServiceMonitor.

Suggested fix

Emit the block for every manager, synthesising the bind address from the value when the project has none:

go
if metricsLine == "" {
    metricsLine = itemIndent + "- --metrics-bind-address=:{{ .Values.metrics.port }}"
}
if metricsIndent == "" {
    metricsIndent = itemIndent
}
builder.WriteString(metricsIndent)
builder.WriteString("{{- if .Values.metrics.enabled }}\n")
builder.WriteString(metricsLine)
...

The {{- else }} branch that already writes --metrics-bind-address=0 then covers metrics.enabled=false for these projects too, which is the same value the scaffolded manager defaults to.

Projects whose manager already sets a bind address are unaffected: they keep taking the existing path, and the four sample charts under testdata/ and docs/book/src/*/testdata/ regenerate byte for byte.

Test gaps worth closing with it

  • No unit test covers a manager with an args: block and no --metrics-bind-address. templater_test.go has one such fixture ("should template deployment fields when container name is not 'manager'") and it asserts nothing about metrics, so restoring the old gate leaves make test-unit green.
  • No test asserts that the bind address the chart hands the manager follows metrics.port rather than a literal 8443.
  • A manager with no args: key at all still receives nothing: the args regex finds no match and the function returns early. Worth deciding explicitly.

Source: kubernetes-sigs/kubebuilder