diff --git a/changelogs/unreleased/9445-mpryc b/changelogs/unreleased/9445-mpryc new file mode 100644 index 000000000..9bef666cb --- /dev/null +++ b/changelogs/unreleased/9445-mpryc @@ -0,0 +1 @@ +Fix plugin init container names exceeding DNS-1123 limit diff --git a/pkg/builder/container_builder.go b/pkg/builder/container_builder.go index da6215637..762462c86 100644 --- a/pkg/builder/container_builder.go +++ b/pkg/builder/container_builder.go @@ -22,6 +22,8 @@ import ( corev1api "k8s.io/api/core/v1" apimachineryRuntime "k8s.io/apimachinery/pkg/runtime" + + "github.com/vmware-tanzu/velero/pkg/label" ) // ContainerBuilder builds Container objects @@ -45,9 +47,9 @@ func ForPluginContainer(image string, pullPolicy corev1api.PullPolicy) *Containe return ForContainer(getName(image), image).PullPolicy(pullPolicy).VolumeMounts(volumeMount) } -// getName returns the 'name' component of a docker -// image that includes the entire string except the registry name, and transforms the combined -// string into a RFC-1123 compatible name. +// getName returns the 'name' component of a docker image that includes the entire string +// except the registry name, and transforms the combined string into a DNS-1123 compatible name +// that fits within the 63-character limit for Kubernetes container names. func getName(image string) string { slashIndex := strings.Index(image, "/") slashCount := 0 @@ -83,7 +85,10 @@ func getName(image string) string { re := strings.NewReplacer("/", "-", "_", "-", ".", "-") - return re.Replace(image[start:end]) + name := re.Replace(image[start:end]) + + // Ensure the name doesn't exceed Kubernetes container name length limit + return label.GetValidName(name) } // Result returns the built Container. diff --git a/pkg/builder/container_builder_test.go b/pkg/builder/container_builder_test.go index d2d48dbdc..b23cbddfd 100644 --- a/pkg/builder/container_builder_test.go +++ b/pkg/builder/container_builder_test.go @@ -100,3 +100,50 @@ func TestGetName(t *testing.T) { }) } } + +func TestGetNameWithLongPaths(t *testing.T) { + tests := []struct { + name string + image string + validate func(t *testing.T, result string) + }{ + { + name: "plugin with deeply nested repository path exceeding 63 characters", + image: "arohcpsvcdev.azurecr.io/redhat-user-workloads/ocp-art-tenant/oadp-hypershift-oadp-plugin-main@sha256:adb840bf3890b4904a8cdda1a74c82cf8d96c52eba9944ac10e795335d6fd450", + validate: func(t *testing.T, result string) { + t.Helper() + // Should not exceed DNS-1123 label limit of 63 characters + assert.LessOrEqual(t, len(result), 63, "Container name must satisfy DNS-1123 label constraints (max 63 chars)") + // Should be exactly 63 characters (truncated with hash) + assert.Len(t, result, 63) + // Should be deterministic + result2 := getName("arohcpsvcdev.azurecr.io/redhat-user-workloads/ocp-art-tenant/oadp-hypershift-oadp-plugin-main@sha256:adb840bf3890b4904a8cdda1a74c82cf8d96c52eba9944ac10e795335d6fd450") + assert.Equal(t, result, result2) + }, + }, + { + name: "plugin with normal path length (should remain unchanged)", + image: "arohcpsvcdev.azurecr.io/konveyor/velero-plugin-for-microsoft-azure@sha256:b2db5f09da514e817a74c992dcca5f90b77c2ab0b2797eba947d224271d6070e", + validate: func(t *testing.T, result string) { + t.Helper() + assert.Equal(t, "konveyor-velero-plugin-for-microsoft-azure", result) + assert.LessOrEqual(t, len(result), 63) + }, + }, + { + name: "very long nested path", + image: "registry.example.com/org/team/project/subproject/component/service/application-name-with-many-words:v1.2.3", + validate: func(t *testing.T, result string) { + t.Helper() + assert.LessOrEqual(t, len(result), 63) + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := getName(test.image) + test.validate(t, result) + }) + } +}