diff --git a/pkg/build/api/validation/validation_test.go b/pkg/build/api/validation/validation_test.go index dc4f17377bf5..dc129366a13d 100644 --- a/pkg/build/api/validation/validation_test.go +++ b/pkg/build/api/validation/validation_test.go @@ -1269,7 +1269,7 @@ func TestValidateCommonSpec(t *testing.T) { Output: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "DockerImage", - Name: "some/long/value/with/no/meaning", + Name: "///some/long/value/with/no/meaning", }, }, }, @@ -1290,7 +1290,7 @@ func TestValidateCommonSpec(t *testing.T) { Output: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "ImageStream", - Name: "some/long/value/with/no/meaning", + Name: "///some/long/value/with/no/meaning", }, }, }, @@ -1311,7 +1311,7 @@ func TestValidateCommonSpec(t *testing.T) { Output: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "ImageStreamTag", - Name: "some/long/value/with/no/meaning", + Name: "///some/long/value/with/no/meaning", }, }, }, @@ -1332,7 +1332,7 @@ func TestValidateCommonSpec(t *testing.T) { Output: buildapi.BuildOutput{ To: &kapi.ObjectReference{ Kind: "ImageStreamTag", - Name: "some/long/value/with/no/meaning:latest", + Name: "///some/long/value/with/no/meaning:latest", }, }, }, @@ -2051,6 +2051,54 @@ func TestValidateCommonSpecSuccess(t *testing.T) { }, }, }, + // 6 + { + CommonSpec: buildapi.CommonSpec{ + Source: buildapi.BuildSource{ + Git: &buildapi.GitBuildSource{ + URI: "http://github.com/my/repository", + }, + }, + Strategy: buildapi.BuildStrategy{ + SourceStrategy: &buildapi.SourceBuildStrategy{ + From: kapi.ObjectReference{ + Kind: "DockerImage", + Name: "reponame", + }, + }, + }, + Output: buildapi.BuildOutput{ + To: &kapi.ObjectReference{ + Kind: "DockerImage", + Name: "registry/project/repository/data", + }, + }, + }, + }, + // 7 + { + CommonSpec: buildapi.CommonSpec{ + Source: buildapi.BuildSource{ + Git: &buildapi.GitBuildSource{ + URI: "http://github.com/my/repository", + }, + }, + Strategy: buildapi.BuildStrategy{ + SourceStrategy: &buildapi.SourceBuildStrategy{ + From: kapi.ObjectReference{ + Kind: "DockerImage", + Name: "registry/project/repository/data", + }, + }, + }, + Output: buildapi.BuildOutput{ + To: &kapi.ObjectReference{ + Kind: "DockerImage", + Name: "repository/data", + }, + }, + }, + }, } for count, config := range testCases { errors := validateCommonSpec(&config.CommonSpec, nil) diff --git a/pkg/image/api/helper.go b/pkg/image/api/helper.go index 60338f5b8c71..19feb25d9e4e 100644 --- a/pkg/image/api/helper.go +++ b/pkg/image/api/helper.go @@ -184,7 +184,34 @@ func ParseDockerImageReference(spec string) (DockerImageReference, error) { ref.ID = id break default: - // TODO: this is no longer true with V2 + // Handle multiple segments in form: registry/namespace/namesegment1/namesegment2/.../name + // but not this form: http://registry/namespace/name. We don't support using the schema in + // the DockerImageReference. + if len(repoParts) > 3 && !strings.HasSuffix(repoParts[0], ":") { + if len(repoParts[0]) == 0 { + return ref, fmt.Errorf("the docker pull spec %q cannot have empty segments", spec) + } + ref.Registry = repoParts[0] + + if len(repoParts[1]) == 0 { + return ref, fmt.Errorf("the docker pull spec %q cannot have empty segments", spec) + } + ref.Namespace = repoParts[1] + + for i, part := range repoParts[2:] { + if len(part) == 0 { + return ref, fmt.Errorf("the docker pull spec %q cannot have empty segments", spec) + } + if i > 0 { + ref.Name += "/" + } + ref.Name += part + } + + ref.Tag = tag + ref.ID = id + break + } return ref, fmt.Errorf("the docker pull spec %q must be two or three segments separated by slashes", spec) } diff --git a/pkg/image/api/helper_test.go b/pkg/image/api/helper_test.go index e7b93fa88efc..f152744e7e5a 100644 --- a/pkg/image/api/helper_test.go +++ b/pkg/image/api/helper_test.go @@ -243,6 +243,12 @@ func TestParseDockerImageReference(t *testing.T) { Namespace: "user", Name: "myapp", }, + { + From: "docker.io/user/project/myapp", + Registry: "docker.io", + Namespace: "user", + Name: "project/myapp", + }, { From: "index.docker.io/bar", Registry: "index.docker.io", @@ -291,7 +297,17 @@ func TestParseDockerImageReference(t *testing.T) { Err: true, }, { - From: "bar/foo/baz/biz", + From: "bar/foo/baz/biz", + Registry: "bar", + Namespace: "foo", + Name: "baz/biz", + }, + { + From: "bar/foo/baz////biz", + Err: true, + }, + { + From: "//foo/baz/biz", Err: true, }, { @@ -313,6 +329,8 @@ func TestParseDockerImageReference(t *testing.T) { case err == nil && testCase.Err: t.Errorf("%s: unexpected non-error", testCase.From) continue + case err != nil && testCase.Err: + continue } if e, a := testCase.Registry, ref.Registry; e != a { t.Errorf("%s: registry: expected %q, got %q", testCase.From, e, a) diff --git a/pkg/image/api/validation/validation_test.go b/pkg/image/api/validation/validation_test.go index 1a6eb2c6f13e..3650b05eacc6 100644 --- a/pkg/image/api/validation/validation_test.go +++ b/pkg/image/api/validation/validation_test.go @@ -320,7 +320,7 @@ func TestValidateImageStreamMappingNotOK(t *testing.T) { ObjectMeta: kapi.ObjectMeta{ Namespace: "default", }, - DockerImageRepository: "registry/extra/openshift/ruby-19-centos", + DockerImageRepository: "registry/extra/openshift//ruby-19-centos", Tag: api.DefaultImageTag, Image: api.Image{ ObjectMeta: kapi.ObjectMeta{ @@ -416,7 +416,7 @@ func TestValidateImageStream(t *testing.T) { name: "foo", dockerImageRepository: "a-|///bbb", expected: field.ErrorList{ - field.Invalid(field.NewPath("spec", "dockerImageRepository"), "a-|///bbb", "the docker pull spec \"a-|///bbb\" must be two or three segments separated by slashes"), + field.Invalid(field.NewPath("spec", "dockerImageRepository"), "a-|///bbb", "the docker pull spec \"a-|///bbb\" cannot have empty segments"), }, }, "invalid dockerImageRepository with tag": { @@ -719,7 +719,7 @@ func TestValidateImageStreamImport(t *testing.T) { "invalid dockerImageRepository": { isi: &api.ImageStreamImport{ObjectMeta: validMeta, Spec: repoFn("a-|///bbb")}, expected: field.ErrorList{ - field.Invalid(field.NewPath("spec", "repository", "from", "name"), "a-|///bbb", "the docker pull spec \"a-|///bbb\" must be two or three segments separated by slashes"), + field.Invalid(field.NewPath("spec", "repository", "from", "name"), "a-|///bbb", "the docker pull spec \"a-|///bbb\" cannot have empty segments"), }, }, "invalid dockerImageRepository with tag": { diff --git a/pkg/image/importer/importer_test.go b/pkg/image/importer/importer_test.go index a40ff35082a0..c1a3aab5ab62 100644 --- a/pkg/image/importer/importer_test.go +++ b/pkg/image/importer/importer_test.go @@ -92,7 +92,7 @@ func TestImport(t *testing.T) { Images: []api.ImageImportSpec{ {From: kapi.ObjectReference{Kind: "DockerImage", Name: "test"}}, {From: kapi.ObjectReference{Kind: "DockerImage", Name: "test@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}}, - {From: kapi.ObjectReference{Kind: "DockerImage", Name: "test/un/parse/able/image"}}, + {From: kapi.ObjectReference{Kind: "DockerImage", Name: "test///un/parse/able/image"}}, {From: kapi.ObjectReference{Kind: "ImageStreamTag", Name: "test:other"}}, }, }, @@ -104,7 +104,7 @@ func TestImport(t *testing.T) { if !expectStatusError(isi.Status.Images[1].Status, "Internal error occurred: no such digest") { t.Errorf("unexpected status: %#v", isi.Status.Images[1].Status) } - if !expectStatusError(isi.Status.Images[2].Status, " \"\" is invalid: from.name: Invalid value: \"test/un/parse/able/image\": invalid name: the docker pull spec \"test/un/parse/able/image\" must be two or three segments separated by slashes") { + if !expectStatusError(isi.Status.Images[2].Status, " \"\" is invalid: from.name: Invalid value: \"test///un/parse/able/image\": invalid name: the docker pull spec \"test///un/parse/able/image\" cannot have empty segments") { t.Errorf("unexpected status: %#v", isi.Status.Images[2].Status) } // non DockerImage refs are no-ops diff --git a/test/integration/imageimporter_test.go b/test/integration/imageimporter_test.go index 9459b9d30385..9901bdf089d6 100644 --- a/test/integration/imageimporter_test.go +++ b/test/integration/imageimporter_test.go @@ -54,7 +54,7 @@ func TestImageStreamImport(t *testing.T) { }, Spec: api.ImageStreamImportSpec{ Images: []api.ImageImportSpec{ - {From: kapi.ObjectReference{Kind: "DockerImage", Name: "a/a/a/a/a/redis:latest"}, To: &kapi.LocalObjectReference{Name: "tag"}}, + {From: kapi.ObjectReference{Kind: "DockerImage", Name: "///a/a/a/a/a/redis:latest"}, To: &kapi.LocalObjectReference{Name: "tag"}}, {From: kapi.ObjectReference{Kind: "DockerImage", Name: "redis:latest"}}, }, }, @@ -182,7 +182,7 @@ func mockRegistryHandler(t *testing.T, requireAuth bool, count *int) http.Handle }) } -func TestImageStreamImportOfV1ImageFromV2Repository(t *testing.T) { +func testImageStreamImportWithPath(t *testing.T, reponame string) { imageDigest := "sha256:815d06b56f4138afacd0009b8e3799fcdce79f0507bf8d0588e219b93ab6fd4d" descriptors := map[string]int64{ "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4": 3000, @@ -219,9 +219,9 @@ func TestImageStreamImportOfV1ImageFromV2Repository(t *testing.T) { switch r.URL.Path { case "/v2/": w.Write([]byte(`{}`)) - case "/v2/test/image/tags/list": - w.Write([]byte("{\"name\": \"test/image\", \"tags\": [\"testtag\"]}")) - case "/v2/test/image/manifests/testtag", "/v2/test/image/manifests/" + imageDigest: + case "/v2/" + reponame + "/tags/list": + w.Write([]byte("{\"name\": \"" + reponame + "\", \"tags\": [\"testtag\"]}")) + case "/v2/" + reponame + "/manifests/testtag", "/v2/" + reponame + "/manifests/" + imageDigest: if r.Method == "HEAD" { w.Header().Set("Content-Length", fmt.Sprintf("%d", len(convertedManifest))) w.Header().Set("Docker-Content-Digest", imageDigest) @@ -230,9 +230,9 @@ func TestImageStreamImportOfV1ImageFromV2Repository(t *testing.T) { w.Write([]byte(convertedManifest)) } default: - if strings.HasPrefix(r.URL.Path, "/v2/test/image/blobs/") { + if strings.HasPrefix(r.URL.Path, "/v2/"+reponame+"/blobs/") { for dgst, size := range descriptors { - if r.URL.Path != "/v2/test/image/blobs/"+dgst { + if r.URL.Path != "/v2/"+reponame+"/blobs/"+dgst { continue } if r.Method == "HEAD" { @@ -276,7 +276,7 @@ func TestImageStreamImportOfV1ImageFromV2Repository(t *testing.T) { Import: true, Images: []api.ImageImportSpec{ { - From: kapi.ObjectReference{Kind: "DockerImage", Name: url.Host + "/test/image:testtag"}, + From: kapi.ObjectReference{Kind: "DockerImage", Name: url.Host + "/" + reponame + ":testtag"}, To: &kapi.LocalObjectReference{Name: "other"}, ImportPolicy: api.TagImportPolicy{Insecure: true}, }, @@ -316,6 +316,14 @@ func TestImageStreamImportOfV1ImageFromV2Repository(t *testing.T) { } } +func TestImageStreamImportOfV1ImageFromV2Repository(t *testing.T) { + testImageStreamImportWithPath(t, "test/image") +} + +func TestImageStreamImportOfMultiSegmentDockerReference(t *testing.T) { + testImageStreamImportWithPath(t, "test/foo/bar/image") +} + func TestImageStreamImportAuthenticated(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t)