diff --git a/config/config.go b/config/config.go index 004a251f3f..3e73385ca2 100644 --- a/config/config.go +++ b/config/config.go @@ -2121,8 +2121,8 @@ func (i *Image) String() (ref string) { if i.Registry != "" { ref = i.Registry + "/" } - - if i.Registry != "" && i.Repository != "" && !strings.ContainsRune(i.Repository, '/') { + + if isDockerHub(i.Registry) && i.Repository != "" && !strings.ContainsRune(i.Repository, '/') { ref += "library/" } ref += i.Repository @@ -2134,6 +2134,18 @@ func (i *Image) String() (ref string) { return ref } +const ( + dockerHubRegistry = "index.docker.io" + dockerHubRegistryAlias = "docker.io" +) + +// isDockerHub reports whether the registry refers to Docker Hub. The +// "library/" namespace is a Docker Hub implementation detail and must not be +// applied to any other registry +func isDockerHub(registry string) bool { + return registry == dockerHubRegistry || registry == dockerHubRegistryAlias +} + type ImagePullSecretName struct { // Name of the image pull secret to use. Name string `json:"name,omitempty"` diff --git a/config/config_test.go b/config/config_test.go index 4da35387c7..50aee026ff 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -443,13 +443,57 @@ func TestImage_String(t *testing.T) { expected: "alpine", }, { - name: "omit repo but not registry is library", + name: "omit repo on non-docker-hub registry does not add library prefix", image: Image{ Registry: "ghcr.io", Repository: "alpine", Tag: "3.20", }, - expected: "ghcr.io/library/alpine:3.20", + expected: "ghcr.io/alpine:3.20", + }, + { + name: "docker hub canonical registry preserves library prefix", + image: Image{ + Registry: "index.docker.io", + Repository: "alpine", + Tag: "3.20", + }, + expected: "index.docker.io/library/alpine:3.20", + }, + { + name: "docker hub alias registry preserves library prefix", + image: Image{ + Registry: "docker.io", + Repository: "alpine", + Tag: "3.20", + }, + expected: "docker.io/library/alpine:3.20", + }, + { + name: "registry.k8s.io single-segment repo does not add library prefix", + image: Image{ + Registry: "registry.k8s.io", + Repository: "pause", + Tag: "3.9", + }, + expected: "registry.k8s.io/pause:3.9", + }, + { + name: "private registry with port and single-segment repo", + image: Image{ + Registry: "my-registry.example.com:443", + Repository: "app", + }, + expected: "my-registry.example.com:443/app", + }, + { + name: "non docker hub multi-segment repo unchanged", + image: Image{ + Registry: "ghcr.io", + Repository: "loft-sh/vcluster", + Tag: "0.20.0", + }, + expected: "ghcr.io/loft-sh/vcluster:0.20.0", }, { name: "registry may have port",