From 16627d0374eab66af818f4e23f2bf4b66f2612ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Vald=C3=A9s=20Castillo?= Date: Fri, 24 Jul 2026 03:31:54 -0700 Subject: [PATCH] Use toLabelValue instead of toDNSName in Kubernetes step label (#6890) --- pipeline/backend/kubernetes/pod.go | 8 +-- pipeline/backend/kubernetes/pod_test.go | 77 +++++++++++++++++++++++-- pipeline/backend/kubernetes/secrets.go | 4 +- 3 files changed, 75 insertions(+), 14 deletions(-) diff --git a/pipeline/backend/kubernetes/pod.go b/pipeline/backend/kubernetes/pod.go index 936e308b7..b865b37ae 100644 --- a/pipeline/backend/kubernetes/pod.go +++ b/pipeline/backend/kubernetes/pod.go @@ -140,11 +140,11 @@ func podLabels(step *types.Step, config *config, options BackendOptions, taskUUI if isService(step) { labels[ServiceLabel], _ = serviceName(step) } - labels[StepLabelLegacy], err = stepLabel(step) + labels[StepLabelLegacy], err = toLabelValue(step.Name) if err != nil { return labels, err } - labels[StepLabel], err = stepLabel(step) + labels[StepLabel], err = toLabelValue(step.Name) if err != nil { return labels, err } @@ -156,10 +156,6 @@ func podLabels(step *types.Step, config *config, options BackendOptions, taskUUI return labels, nil } -func stepLabel(step *types.Step) (string, error) { - return toDNSName(step.Name) -} - func podAnnotations(config *config, options BackendOptions) map[string]string { annotations := make(map[string]string) diff --git a/pipeline/backend/kubernetes/pod_test.go b/pipeline/backend/kubernetes/pod_test.go index 9fbf36782..2fb38c06e 100644 --- a/pipeline/backend/kubernetes/pod_test.go +++ b/pipeline/backend/kubernetes/pod_test.go @@ -16,6 +16,7 @@ package kubernetes import ( "encoding/json" + "strings" "testing" "github.com/kinbiko/jsonassert" @@ -115,14 +116,78 @@ func TestPodMeta(t *testing.T) { assert.EqualValues(t, "", meta.Labels[ServiceLabel]) } -func TestStepLabel(t *testing.T) { - name, err := stepLabel(&types.Step{Name: "Build image"}) - assert.NoError(t, err) - assert.EqualValues(t, "build-image", name) +// TestStepNameAsLabel verifies that step names are correctly converted to valid +// Kubernetes label values via toLabelValue (replaces the old stepLabel wrapper). +func TestStepNameAsLabel(t *testing.T) { + tests := []struct { + name string + stepName string + want string + }{ + { + name: "spaces converted to dashes and lowercased", + stepName: "Build image", + want: "build-image", + }, + { + name: "leading dot stripped", + stepName: ".build.image", + want: "build.image", + }, + { + name: "simple lowercase name unchanged", + stepName: "test", + want: "test", + }, + { + name: "underscores preserved", + stepName: "run_tests", + want: "run_tests", + }, + { + name: "mixed special characters", + stepName: "Deploy (production)", + want: "deploy-production", + }, + { + name: "consecutive spaces single dash", + stepName: "step name", + want: "step-name", + }, + } - name, err = stepLabel(&types.Step{Name: ".build.image"}) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := toLabelValue(tt.stepName) + assert.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} + +// TestStepNameAsLabelInPod verifies that step labels in a pod use toLabelValue +// and produce valid values that Kubernetes accepts. +func TestStepNameAsLabelInPod(t *testing.T) { + step := &types.Step{ + Name: "Build & Deploy (staging)", + Image: "alpine:latest", + UUID: "01he8bebctabr3kgk0qj36d2me-1", + WorkingDir: "/woodpecker/src", + Environment: map[string]string{}, + } + meta, err := podMeta(step, &config{Namespace: "woodpecker"}, BackendOptions{}, "wp-01he8bebctabr3kgk0qj36d2me-1", taskUUID) assert.NoError(t, err) - assert.EqualValues(t, "build.image", name) + assert.Equal(t, "build-deploy-staging", meta.Labels[StepLabel]) + assert.Equal(t, "build-deploy-staging", meta.Labels[StepLabelLegacy]) +} + +// TestStepNameAsLabelLongName verifies that long step names are truncated +// to 63 characters (Kubernetes label value limit). +func TestStepNameAsLabelLongName(t *testing.T) { + longName := strings.Repeat("a", 100) + got, err := toLabelValue(longName) + assert.NoError(t, err) + assert.LessOrEqual(t, len(got), 63) } func TestPodHostnameSanitized(t *testing.T) { diff --git a/pipeline/backend/kubernetes/secrets.go b/pipeline/backend/kubernetes/secrets.go index 9fb9bc566..166350dd9 100644 --- a/pipeline/backend/kubernetes/secrets.go +++ b/pipeline/backend/kubernetes/secrets.go @@ -270,11 +270,11 @@ func registrySecretLabels(step *types.Step, config *config) (map[string]string, if step.Type == types.StepTypeService { labels[ServiceLabel], _ = serviceName(step) } - labels[StepLabelLegacy], err = stepLabel(step) + labels[StepLabelLegacy], err = toLabelValue(step.Name) if err != nil { return labels, err } - labels[StepLabel], err = stepLabel(step) + labels[StepLabel], err = toLabelValue(step.Name) if err != nil { return labels, err }