From d5f9198eef20e5cdf5361e789760348bdef2e666 Mon Sep 17 00:00:00 2001 From: Mulham Raee Date: Fri, 15 May 2026 17:16:39 +0200 Subject: [PATCH] fix(metrics-proxy): sanitize volume names to replace dots with dashes The certVolumesFromMonitors function used Secret/ConfigMap resource names directly as Kubernetes volume names. Volume names must conform to RFC 1123 DNS label rules which prohibit dots. When a ServiceMonitor references a ConfigMap named "openshift-service-ca.crt" in its TLS config, the resulting volume name is rejected by the API server. Replace dots with dashes in volume and volumeMount names while preserving the original resource name in ConfigMap/Secret source references and mount paths. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../v2/metrics_proxy/deployment.go | 13 ++++++-- .../v2/metrics_proxy/deployment_test.go | 32 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/deployment.go b/control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/deployment.go index ca2e18af7af5..5b368e4db42f 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/deployment.go +++ b/control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/deployment.go @@ -3,6 +3,7 @@ package metricsproxy import ( "fmt" "sort" + "strings" component "github.com/openshift/hypershift/support/controlplane-component" "github.com/openshift/hypershift/support/metrics" @@ -121,8 +122,9 @@ func certVolumesFromMonitors(cpContext component.WorkloadContext, namespace stri for _, name := range names { ref := refs[name] + volName := sanitizeVolumeName(name) vol := corev1.Volume{ - Name: name, + Name: volName, } if ref.isSecret { @@ -144,7 +146,7 @@ func certVolumesFromMonitors(cpContext component.WorkloadContext, namespace stri volumes = append(volumes, vol) mounts = append(mounts, corev1.VolumeMount{ - Name: name, + Name: volName, MountPath: certBasePath + "/" + name, }) } @@ -152,6 +154,13 @@ func certVolumesFromMonitors(cpContext component.WorkloadContext, namespace stri return volumes, mounts, nil } +// sanitizeVolumeName converts a resource name into a valid Kubernetes volume +// name by replacing dots with dashes. Kubernetes volume names must conform to +// DNS label rules which do not allow dots. +func sanitizeVolumeName(name string) string { + return strings.ReplaceAll(name, ".", "-") +} + // collectSecretOrConfigMapRef adds a SecretOrConfigMap reference to the refs map. func collectSecretOrConfigMapRef(refs map[string]*certRef, ref prometheusoperatorv1.SecretOrConfigMap) { if ref.Secret != nil { diff --git a/control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/deployment_test.go b/control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/deployment_test.go index cfb173c7403f..c1ddb5fcbe40 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/deployment_test.go +++ b/control-plane-operator/controllers/hostedcontrolplane/v2/metrics_proxy/deployment_test.go @@ -309,6 +309,38 @@ func TestCertVolumesFromMonitors(t *testing.T) { } }) + t.Run("When resource names contain dots, it should sanitize volume names but preserve mount paths", func(t *testing.T) { + t.Parallel() + + sm := newServiceMonitorWithTLS("cno", namespace, &prometheusoperatorv1.TLSConfig{ + SafeTLSConfig: prometheusoperatorv1.SafeTLSConfig{ + CA: prometheusoperatorv1.SecretOrConfigMap{ + ConfigMap: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: "openshift-service-ca.crt"}, + Key: "service-ca.crt", + }, + }, + }, + }) + + cpContext := newCertVolumeTestContext(namespace, scheme, sm) + volumes, mounts := assertCertVolumeCount(t, cpContext, namespace, 1, 1) + + if volumes[0].Name != "openshift-service-ca-crt" { + t.Errorf("expected sanitized volume name openshift-service-ca-crt, got %s", volumes[0].Name) + } + if mounts[0].Name != "openshift-service-ca-crt" { + t.Errorf("expected sanitized mount name openshift-service-ca-crt, got %s", mounts[0].Name) + } + expectedPath := certBasePath + "/openshift-service-ca.crt" + if mounts[0].MountPath != expectedPath { + t.Errorf("expected mount path to preserve dots %q, got %q", expectedPath, mounts[0].MountPath) + } + if volumes[0].VolumeSource.ConfigMap.Name != "openshift-service-ca.crt" { + t.Errorf("expected ConfigMap source name to preserve original name, got %s", volumes[0].VolumeSource.ConfigMap.Name) + } + }) + t.Run("When PodMonitor has no TLS config, it should be skipped", func(t *testing.T) { t.Parallel()