diff --git a/conformance/01_pull_test.go b/conformance/01_pull_test.go index c7c84a44..229ecc88 100644 --- a/conformance/01_pull_test.go +++ b/conformance/01_pull_test.go @@ -27,89 +27,72 @@ var test01Pull = func() { g.Context(titlePull, func() { var tag string + var blobRefs []string + var manifestRefs []string g.Context("Setup", func() { g.Specify("Populate registry with test blob", func() { SkipIfDisabled(pull) RunOnlyIf(runPullSetup) - req := client.NewRequest(reggie.POST, "/v2//blobs/uploads/") - resp, err := client.Do(req) - Expect(err).To(BeNil()) - req = client.NewRequest(reggie.PUT, resp.GetRelativeLocation()). - SetQueryParam("digest", configs[0].Digest). - SetHeader("Content-Type", "application/octet-stream"). - SetHeader("Content-Length", configs[0].ContentLength). - SetBody(configs[0].Content) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + blobRefs = pushBlob( + &BlobInfo{ + Digest: configs[0].Digest, + Content: configs[0].Content, + Length: configs[0].ContentLength, + }, + blobRefs, g.GinkgoT(), + ) }) g.Specify("Populate registry with test blob", func() { SkipIfDisabled(pull) RunOnlyIf(runPullSetup) - req := client.NewRequest(reggie.POST, "/v2//blobs/uploads/") - resp, err := client.Do(req) - Expect(err).To(BeNil()) - req = client.NewRequest(reggie.PUT, resp.GetRelativeLocation()). - SetQueryParam("digest", configs[1].Digest). - SetHeader("Content-Type", "application/octet-stream"). - SetHeader("Content-Length", configs[1].ContentLength). - SetBody(configs[1].Content) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + blobRefs = pushBlob( + &BlobInfo{ + Digest: configs[1].Digest, + Content: configs[1].Content, + Length: configs[1].ContentLength, + }, + blobRefs, g.GinkgoT(), + ) }) g.Specify("Populate registry with test layer", func() { SkipIfDisabled(pull) RunOnlyIf(runPullSetup) - req := client.NewRequest(reggie.POST, "/v2//blobs/uploads/") - resp, err := client.Do(req) - Expect(err).To(BeNil()) - req = client.NewRequest(reggie.PUT, resp.GetRelativeLocation()). - SetQueryParam("digest", layerBlobDigest). - SetHeader("Content-Type", "application/octet-stream"). - SetHeader("Content-Length", layerBlobContentLength). - SetBody(layerBlobData) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + blobRefs = pushBlob( + &BlobInfo{ + Digest: layerBlobDigest, + Content: layerBlobData, + Length: layerBlobContentLength, + }, + blobRefs, g.GinkgoT(), + ) }) g.Specify("Populate registry with test manifest", func() { SkipIfDisabled(pull) RunOnlyIf(runPullSetup) tag = testTagName - req := client.NewRequest(reggie.PUT, "/v2//manifests/", - reggie.WithReference(tag)). - SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). - SetBody(manifests[0].Content) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + manifestRefs = pushManifest( + &ManifestInfo{ + Tag: tag, + Digest: manifests[0].Digest, + Content: manifests[0].Content}, + manifestRefs, g.GinkgoT(), + ) }) g.Specify("Populate registry with test manifest", func() { SkipIfDisabled(pull) RunOnlyIf(runPullSetup) - req := client.NewRequest(reggie.PUT, "/v2//manifests/", - reggie.WithReference(manifests[1].Digest)). - SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). - SetBody(manifests[1].Content) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + manifestRefs = pushManifest( + &ManifestInfo{ + Digest: manifests[1].Digest, + Content: manifests[1].Content, + }, + manifestRefs, g.GinkgoT(), + ) }) g.Specify("Get tag name from environment", func() { @@ -271,111 +254,23 @@ var test01Pull = func() { g.Context("Teardown", func() { if deleteManifestBeforeBlobs { - g.Specify("Delete manifest[0] created in setup", func() { - SkipIfDisabled(pull) - RunOnlyIf(runPullSetup) - req := client.NewRequest(reggie.DELETE, "/v2//manifests/", reggie.WithDigest(manifests[0].Digest)) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusMethodNotAllowed), - )) - }) - g.Specify("Delete manifest[1] created in setup", func() { + g.Specify("Delete manifests created in setup", func() { SkipIfDisabled(pull) RunOnlyIf(runPullSetup) - req := client.NewRequest(reggie.DELETE, "/v2//manifests/", reggie.WithDigest(manifests[1].Digest)) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusMethodNotAllowed), - )) + deleteManifests(manifestRefs, g.GinkgoT()) }) } - g.Specify("Delete config[0] blob created in setup", func() { - SkipIfDisabled(pull) - RunOnlyIf(runPullSetup) - req := client.NewRequest(reggie.DELETE, "/v2//blobs/", reggie.WithDigest(configs[0].Digest)) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusNotFound), - Equal(http.StatusMethodNotAllowed), - )) - }) - g.Specify("Delete config[1] blob created in setup", func() { - SkipIfDisabled(pull) - RunOnlyIf(runPullSetup) - req := client.NewRequest(reggie.DELETE, "/v2//blobs/", reggie.WithDigest(configs[1].Digest)) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusNotFound), - Equal(http.StatusMethodNotAllowed), - )) - }) - - g.Specify("Delete layer blob created in setup", func() { + g.Specify("Delete blobs created in setup", func() { SkipIfDisabled(pull) RunOnlyIf(runPullSetup) - req := client.NewRequest(reggie.DELETE, "/v2//blobs/", reggie.WithDigest(layerBlobDigest)) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusNotFound), - Equal(http.StatusMethodNotAllowed), - )) + deleteBlobs(blobRefs, g.GinkgoT()) }) - if !deleteManifestBeforeBlobs { - g.Specify("Delete manifest[0] created in setup", func() { + g.Specify("Delete manifests created in setup", func() { SkipIfDisabled(pull) RunOnlyIf(runPullSetup) - req := client.NewRequest(reggie.DELETE, "/v2//manifests/", reggie.WithDigest(manifests[0].Digest)) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusMethodNotAllowed), - )) - }) - g.Specify("Delete manifest[1] created in setup", func() { - SkipIfDisabled(pull) - RunOnlyIf(runPullSetup) - req := client.NewRequest(reggie.DELETE, "/v2//manifests/", reggie.WithDigest(manifests[1].Digest)) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusMethodNotAllowed), - )) + deleteManifests(manifestRefs, g.GinkgoT()) }) } }) diff --git a/conformance/02_push_test.go b/conformance/02_push_test.go index 65ce7875..af32f393 100644 --- a/conformance/02_push_test.go +++ b/conformance/02_push_test.go @@ -29,7 +29,8 @@ var test02Push = func() { g.Context(titlePush, func() { var lastResponse, prevResponse *reggie.Response - var emptyLayerManifestRef string + var blobRefs []string + var manifestRefs []string g.Context("Setup", func() { // No setup required at this time for push tests @@ -65,6 +66,7 @@ var test02Push = func() { Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) location := resp.Header().Get("Location") Expect(location).ToNot(BeEmpty()) + blobRefs = append(blobRefs, testBlobADigest) }) }) @@ -93,6 +95,7 @@ var test02Push = func() { Equal(http.StatusCreated), Equal(http.StatusAccepted), )) + blobRefs = append(blobRefs, configs[1].Digest) lastResponse = resp }) @@ -130,6 +133,7 @@ var test02Push = func() { location := resp.Header().Get("Location") Expect(location).ToNot(BeEmpty()) Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) + blobRefs = append(blobRefs, configs[1].Digest) }) g.Specify("GET request to existing blob should yield 200 response", func() { @@ -155,6 +159,7 @@ var test02Push = func() { location := resp.Header().Get("Location") Expect(location).ToNot(BeEmpty()) Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) + blobRefs = append(blobRefs, layerBlobDigest) }) g.Specify("GET request to existing layer should yield 200 response", func() { @@ -267,33 +272,20 @@ var test02Push = func() { Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) location := resp.Header().Get("Location") Expect(location).ToNot(BeEmpty()) + blobRefs = append(blobRefs, testBlobBDigest) }) }) g.Context("Cross-Repository Blob Mount", func() { g.Specify("Cross-mounting of a blob without the from argument should yield session id", func() { SkipIfDisabled(push) - req := client.NewRequest(reggie.POST, "/v2//blobs/uploads/", - reggie.WithName(crossmountNamespace)). - SetQueryParam("mount", dummyDigest) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(Equal(http.StatusAccepted)) - Expect(resp.GetAbsoluteLocation()).To(Not(BeEmpty())) + blobRefs, _ = mountBlob(dummyDigest, "", blobRefs, g.GinkgoT()) }) g.Specify("POST request to mount another repository's blob should return 201 or 202", func() { SkipIfDisabled(push) - req := client.NewRequest(reggie.POST, "/v2//blobs/uploads/", - reggie.WithName(crossmountNamespace)). - SetQueryParam("mount", testBlobADigest). - SetQueryParam("from", client.Config.DefaultName) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - Equal(http.StatusCreated), - Equal(http.StatusAccepted), - )) + var resp *reggie.Response + blobRefs, resp = mountBlob(testBlobADigest, client.Config.DefaultName, blobRefs, g.GinkgoT()) lastResponse = resp }) @@ -318,12 +310,7 @@ var test02Push = func() { RunOnlyIf(runAutomaticCrossmountTest) RunOnlyIf(lastResponse.StatusCode() == http.StatusCreated) RunOnlyIf(automaticCrossmountEnabled) - req := client.NewRequest(reggie.POST, "/v2//blobs/uploads/", - reggie.WithName(crossmountNamespace)). - SetQueryParam("mount", testBlobADigest) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) + blobRefs, _ = mountBlob(testBlobADigest, "", blobRefs, g.GinkgoT()) }) g.Specify("Cross-mounting without from, and automatic content discovery disabled should return a 202", func() { @@ -331,12 +318,7 @@ var test02Push = func() { RunOnlyIf(runAutomaticCrossmountTest) RunOnlyIf(lastResponse.StatusCode() == http.StatusCreated) RunOnlyIfNot(automaticCrossmountEnabled) - req := client.NewRequest(reggie.POST, "/v2//blobs/uploads/", - reggie.WithName(crossmountNamespace)). - SetQueryParam("mount", testBlobADigest) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(Equal(http.StatusAccepted)) + blobRefs, _ = mountBlob(testBlobADigest, "", blobRefs, g.GinkgoT()) }) }) @@ -354,15 +336,14 @@ var test02Push = func() { SkipIfDisabled(push) for i := 0; i < 4; i++ { tag := fmt.Sprintf("test%d", i) - req := client.NewRequest(reggie.PUT, "/v2//manifests/", - reggie.WithReference(tag)). - SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). - SetBody(manifests[1].Content) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - location := resp.Header().Get("Location") - Expect(location).ToNot(BeEmpty()) - Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) + manifestRefs = pushManifest( + &ManifestInfo{ + Tag: tag, + Digest: manifests[1].Digest, + Content: manifests[1].Content, + }, + manifestRefs, g.GinkgoT(), + ) } }) @@ -376,9 +357,10 @@ var test02Push = func() { Expect(err).To(BeNil()) if resp.StatusCode() == http.StatusCreated { location := resp.Header().Get("Location") - emptyLayerManifestRef = location Expect(location).ToNot(BeEmpty()) Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) + manifestRefs = append(manifestRefs, emptyLayerTestTag) + manifestRefs = append(manifestRefs, emptyLayerManifestDigest) } else { Warn("image manifest with no layers is not supported") } @@ -399,89 +381,21 @@ var test02Push = func() { g.Specify("Delete manifest created in tests", func() { SkipIfDisabled(push) RunOnlyIf(runPushSetup) - req := client.NewRequest(reggie.DELETE, "/v2//manifests/", reggie.WithDigest(manifests[1].Digest)) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusMethodNotAllowed), - )) - if emptyLayerManifestRef != "" { - req = client.NewRequest(reggie.DELETE, "/v2//manifests/", reggie.WithReference(emptyLayerManifestDigest)) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusMethodNotAllowed), - )) - } + deleteManifests(manifestRefs, g.GinkgoT()) }) } - g.Specify("Delete config blob created in tests", func() { + g.Specify("Delete blobs created in tests", func() { SkipIfDisabled(push) RunOnlyIf(runPushSetup) - req := client.NewRequest(reggie.DELETE, "/v2//blobs/", reggie.WithDigest(configs[1].Digest)) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusNotFound), - Equal(http.StatusMethodNotAllowed), - )) - }) - - g.Specify("Delete layer blob created in setup", func() { - SkipIfDisabled(push) - RunOnlyIf(runPushSetup) - req := client.NewRequest(reggie.DELETE, "/v2//blobs/", reggie.WithDigest(layerBlobDigest)) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusNotFound), - Equal(http.StatusMethodNotAllowed), - )) + deleteBlobs(blobRefs, g.GinkgoT()) }) if !deleteManifestBeforeBlobs { g.Specify("Delete manifest created in tests", func() { SkipIfDisabled(push) RunOnlyIf(runPushSetup) - req := client.NewRequest(reggie.DELETE, "/v2//manifests/", reggie.WithDigest(manifests[1].Digest)) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusMethodNotAllowed), - )) - if emptyLayerManifestRef != "" { - req = client.NewRequest(reggie.DELETE, "/v2//manifests/", reggie.WithReference(emptyLayerManifestDigest)) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusMethodNotAllowed), - )) - } + deleteManifests(manifestRefs, g.GinkgoT()) }) } }) diff --git a/conformance/03_discovery_test.go b/conformance/03_discovery_test.go index 3ad2fbd5..9eadf65b 100644 --- a/conformance/03_discovery_test.go +++ b/conformance/03_discovery_test.go @@ -34,42 +34,34 @@ var test03ContentDiscovery = func() { var numTags = 4 var tagList []string + var blobRefs []string + var manifestRefs []string g.Context("Setup", func() { g.Specify("Populate registry with test blob", func() { SkipIfDisabled(contentDiscovery) RunOnlyIf(runContentDiscoverySetup) - req := client.NewRequest(reggie.POST, "/v2//blobs/uploads/") - resp, err := client.Do(req) - Expect(err).To(BeNil()) - req = client.NewRequest(reggie.PUT, resp.GetRelativeLocation()). - SetQueryParam("digest", configs[2].Digest). - SetHeader("Content-Type", "application/octet-stream"). - SetHeader("Content-Length", configs[2].ContentLength). - SetBody(configs[2].Content) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + blobRefs = pushBlob( + &BlobInfo{ + Digest: configs[2].Digest, + Content: configs[2].Content, + Length: configs[2].ContentLength, + }, + blobRefs, g.GinkgoT(), + ) }) g.Specify("Populate registry with test layer", func() { SkipIfDisabled(contentDiscovery) RunOnlyIf(runContentDiscoverySetup) - req := client.NewRequest(reggie.POST, "/v2//blobs/uploads/") - resp, err := client.Do(req) - Expect(err).To(BeNil()) - req = client.NewRequest(reggie.PUT, resp.GetRelativeLocation()). - SetQueryParam("digest", layerBlobDigest). - SetHeader("Content-Type", "application/octet-stream"). - SetHeader("Content-Length", layerBlobContentLength). - SetBody(layerBlobData) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + blobRefs = pushBlob( + &BlobInfo{ + Digest: layerBlobDigest, + Content: layerBlobData, + Length: layerBlobContentLength, + }, + blobRefs, g.GinkgoT(), + ) }) g.Specify("Populate registry with test tags", func() { @@ -78,15 +70,14 @@ var test03ContentDiscovery = func() { for i := 0; i < numTags; i++ { for _, tag := range []string{"test" + strconv.Itoa(i), "TEST" + strconv.Itoa(i)} { tagList = append(tagList, tag) - req := client.NewRequest(reggie.PUT, "/v2//manifests/", - reggie.WithReference(tag)). - SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). - SetBody(manifests[2].Content) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + manifestRefs = pushManifest( + &ManifestInfo{ + Tag: tag, + Digest: manifests[2].Digest, + Content: manifests[2].Content, + }, + manifestRefs, g.GinkgoT(), + ) } } req := client.NewRequest(reggie.GET, "/v2//tags/list") @@ -108,163 +99,126 @@ var test03ContentDiscovery = func() { // Populate registry with empty JSON blob // validate expected empty JSON blob digest Expect(emptyJSONDescriptor.Digest).To(Equal(godigest.Digest("sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"))) - req := client.NewRequest(reggie.POST, "/v2//blobs/uploads/") - resp, err := client.Do(req) - Expect(err).To(BeNil()) - req = client.NewRequest(reggie.PUT, resp.GetRelativeLocation()). - SetQueryParam("digest", emptyJSONDescriptor.Digest.String()). - SetHeader("Content-Type", "application/octet-stream"). - SetHeader("Content-Length", fmt.Sprintf("%d", emptyJSONDescriptor.Size)). - SetBody(emptyJSONBlob) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + blobRefs = pushBlob( + &BlobInfo{ + Digest: emptyJSONDescriptor.Digest.String(), + Content: emptyJSONBlob, + Length: fmt.Sprintf("%d", emptyJSONDescriptor.Size), + }, + blobRefs, g.GinkgoT(), + ) // Populate registry with reference blob before the image manifest is pushed - req = client.NewRequest(reggie.POST, "/v2//blobs/uploads/") - resp, err = client.Do(req) - Expect(err).To(BeNil()) - req = client.NewRequest(reggie.PUT, resp.GetRelativeLocation()). - SetQueryParam("digest", testRefBlobADigest). - SetHeader("Content-Type", "application/octet-stream"). - SetHeader("Content-Length", testRefBlobALength). - SetBody(testRefBlobA) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + blobRefs = pushBlob( + &BlobInfo{ + Digest: testRefBlobADigest, + Content: testRefBlobA, + Length: testRefBlobALength, + }, + blobRefs, g.GinkgoT(), + ) // Populate registry with test references manifest (config.MediaType = artifactType) - req = client.NewRequest(reggie.PUT, "/v2//manifests/", - reggie.WithReference(refsManifestAConfigArtifactDigest)). - SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). - SetBody(refsManifestAConfigArtifactContent) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) - Expect(resp.Header().Get("OCI-Subject")).To(Equal(manifests[4].Digest)) + manifestRefs = pushManifest( + &ManifestInfo{ + Digest: refsManifestAConfigArtifactDigest, + Content: refsManifestAConfigArtifactContent, + Subject: manifests[4].Digest, + }, + manifestRefs, g.GinkgoT(), + ) // Populate registry with test references manifest (ArtifactType, config.MediaType = emptyJSON) - req = client.NewRequest(reggie.PUT, "/v2//manifests/", - reggie.WithReference(refsManifestALayerArtifactDigest)). - SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). - SetBody(refsManifestALayerArtifactContent) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) - Expect(resp.Header().Get("OCI-Subject")).To(Equal(manifests[4].Digest)) + manifestRefs = pushManifest( + &ManifestInfo{ + Digest: refsManifestALayerArtifactDigest, + Content: refsManifestALayerArtifactContent, + Subject: manifests[4].Digest, + }, + manifestRefs, g.GinkgoT(), + ) // Populate registry with test index manifest - req = client.NewRequest(reggie.PUT, "/v2//manifests/", - reggie.WithReference(refsIndexArtifactDigest)). - SetHeader("Content-Type", "application/vnd.oci.image.index.v1+json"). - SetBody(refsIndexArtifactContent) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) - Expect(resp.Header().Get("OCI-Subject")).To(Equal(manifests[4].Digest)) + manifestRefs = pushManifest( + &ManifestInfo{ + Index: true, + Digest: refsIndexArtifactDigest, + Content: refsIndexArtifactContent, + Subject: manifests[4].Digest, + }, + manifestRefs, g.GinkgoT(), + ) // Populate registry with test blob - req = client.NewRequest(reggie.POST, "/v2//blobs/uploads/") - resp, err = client.Do(req) - Expect(err).To(BeNil()) - req = client.NewRequest(reggie.PUT, resp.GetRelativeLocation()). - SetQueryParam("digest", configs[4].Digest). - SetHeader("Content-Type", "application/octet-stream"). - SetHeader("Content-Length", configs[4].ContentLength). - SetBody(configs[4].Content) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + blobRefs = pushBlob( + &BlobInfo{ + Digest: configs[4].Digest, + Content: configs[4].Content, + Length: configs[4].ContentLength, + }, + blobRefs, g.GinkgoT(), + ) // Populate registry with test layer - req = client.NewRequest(reggie.POST, "/v2//blobs/uploads/") - resp, err = client.Do(req) - Expect(err).To(BeNil()) - req = client.NewRequest(reggie.PUT, resp.GetRelativeLocation()). - SetQueryParam("digest", layerBlobDigest). - SetHeader("Content-Type", "application/octet-stream"). - SetHeader("Content-Length", layerBlobContentLength). - SetBody(layerBlobData) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + blobRefs = pushBlob( + &BlobInfo{ + Digest: layerBlobDigest, + Content: layerBlobData, + Length: layerBlobContentLength, + }, + blobRefs, g.GinkgoT(), + ) // Populate registry with test manifest tag := testTagName - req = client.NewRequest(reggie.PUT, "/v2//manifests/", - reggie.WithReference(tag)). - SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). - SetBody(manifests[4].Content) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + manifestRefs = pushManifest( + &ManifestInfo{ + Tag: tag, + Digest: manifests[4].Digest, + Content: manifests[4].Content, + }, + manifestRefs, g.GinkgoT(), + ) // Populate registry with reference blob after the image manifest is pushed - req = client.NewRequest(reggie.POST, "/v2//blobs/uploads/") - resp, err = client.Do(req) - Expect(err).To(BeNil()) - req = client.NewRequest(reggie.PUT, resp.GetRelativeLocation()). - SetQueryParam("digest", testRefBlobBDigest). - SetHeader("Content-Type", "application/octet-stream"). - SetHeader("Content-Length", testRefBlobBLength). - SetBody(testRefBlobB) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + blobRefs = pushBlob( + &BlobInfo{ + Digest: testRefBlobBDigest, + Content: testRefBlobB, + Length: testRefBlobBLength, + }, + blobRefs, g.GinkgoT(), + ) // Populate registry with test references manifest (config.MediaType = artifactType) - req = client.NewRequest(reggie.PUT, "/v2//manifests/", - reggie.WithReference(refsManifestBConfigArtifactDigest)). - SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). - SetBody(refsManifestBConfigArtifactContent) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) - Expect(resp.Header().Get("OCI-Subject")).To(Equal(manifests[4].Digest)) + manifestRefs = pushManifest( + &ManifestInfo{ + Digest: refsManifestBConfigArtifactDigest, + Content: refsManifestBConfigArtifactContent, + Subject: manifests[4].Digest, + }, + manifestRefs, g.GinkgoT(), + ) // Populate registry with test references manifest (ArtifactType, config.MediaType = emptyJSON) - req = client.NewRequest(reggie.PUT, "/v2//manifests/", - reggie.WithReference(refsManifestBLayerArtifactDigest)). - SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). - SetBody(refsManifestBLayerArtifactContent) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) - Expect(resp.Header().Get("OCI-Subject")).To(Equal(manifests[4].Digest)) + manifestRefs = pushManifest( + &ManifestInfo{ + Digest: refsManifestBLayerArtifactDigest, + Content: refsManifestBLayerArtifactContent, + Subject: manifests[4].Digest, + }, + manifestRefs, g.GinkgoT(), + ) // Populate registry with test references manifest to a non-existent subject - req = client.NewRequest(reggie.PUT, "/v2//manifests/", - reggie.WithReference(refsManifestCLayerArtifactDigest)). - SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). - SetBody(refsManifestCLayerArtifactContent) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) - Expect(resp.Header().Get("OCI-Subject")).To(Equal(manifests[3].Digest)) + manifestRefs = pushManifest( + &ManifestInfo{ + Digest: refsManifestCLayerArtifactDigest, + Content: refsManifestCLayerArtifactContent, + Subject: manifests[3].Digest, + }, + manifestRefs, g.GinkgoT(), + ) }) }) @@ -412,166 +366,23 @@ var test03ContentDiscovery = func() { g.Specify("Delete created manifest & associated tags", func() { SkipIfDisabled(contentDiscovery) RunOnlyIf(runContentDiscoverySetup) - references := []string{ - refsIndexArtifactDigest, - manifests[2].Digest, - manifests[4].Digest, - refsManifestAConfigArtifactDigest, - refsManifestALayerArtifactDigest, - refsManifestBConfigArtifactDigest, - refsManifestBLayerArtifactDigest, - refsManifestCLayerArtifactDigest, - } - for _, ref := range references { - req := client.NewRequest(reggie.DELETE, "/v2//manifests/", reggie.WithDigest(ref)) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusNotFound), - Equal(http.StatusMethodNotAllowed), - )) - } + deleteManifests(manifestRefs, g.GinkgoT()) }) } - g.Specify("Delete config blob created in tests", func() { - SkipIfDisabled(contentDiscovery) - RunOnlyIf(runContentDiscoverySetup) - req := client.NewRequest(reggie.DELETE, "/v2//blobs/", reggie.WithDigest(configs[2].Digest)) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusNotFound), - Equal(http.StatusMethodNotAllowed), - )) - }) - - g.Specify("Delete layer blob created in setup", func() { + g.Specify("Delete blobs created in tests", func() { SkipIfDisabled(contentDiscovery) RunOnlyIf(runContentDiscoverySetup) - req := client.NewRequest(reggie.DELETE, "/v2//blobs/", reggie.WithDigest(layerBlobDigest)) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusNotFound), - Equal(http.StatusMethodNotAllowed), - )) + deleteBlobs(blobRefs, g.GinkgoT()) }) if !deleteManifestBeforeBlobs { g.Specify("Delete created manifest & associated tags", func() { SkipIfDisabled(contentDiscovery) RunOnlyIf(runContentDiscoverySetup) - references := []string{ - refsIndexArtifactDigest, - manifests[2].Digest, - manifests[4].Digest, - refsManifestAConfigArtifactDigest, - refsManifestALayerArtifactDigest, - refsManifestBConfigArtifactDigest, - refsManifestBLayerArtifactDigest, - refsManifestCLayerArtifactDigest, - } - for _, ref := range references { - req := client.NewRequest(reggie.DELETE, "/v2//manifests/", reggie.WithDigest(ref)) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusMethodNotAllowed), - Equal(http.StatusNotFound), - )) - } + deleteManifests(manifestRefs, g.GinkgoT()) }) } - - g.Specify("References teardown", func() { - SkipIfDisabled(contentDiscovery) - RunOnlyIf(runContentDiscoverySetup) - - deleteReq := func(req *reggie.Request) { - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300), - ), - Equal(http.StatusNotFound), - Equal(http.StatusMethodNotAllowed), - )) - } - - if deleteManifestBeforeBlobs { - req := client.NewRequest(reggie.DELETE, "/v2//manifests/", - reggie.WithReference(refsIndexArtifactDigest)) - deleteReq(req) - req = client.NewRequest(reggie.DELETE, "/v2//manifests/", - reggie.WithReference(refsManifestAConfigArtifactDigest)) - deleteReq(req) - req = client.NewRequest(reggie.DELETE, "/v2//manifests/", - reggie.WithReference(refsManifestALayerArtifactDigest)) - deleteReq(req) - req = client.NewRequest(reggie.DELETE, "/v2//manifests/", reggie.WithDigest(manifests[4].Digest)) - deleteReq(req) - req = client.NewRequest(reggie.DELETE, "/v2//manifests/", - reggie.WithReference(refsManifestBConfigArtifactDigest)) - deleteReq(req) - req = client.NewRequest(reggie.DELETE, "/v2//manifests/", - reggie.WithReference(refsManifestBLayerArtifactDigest)) - deleteReq(req) - } - - // Delete config blob created in setup - req := client.NewRequest(reggie.DELETE, "/v2//blobs/", reggie.WithDigest(configs[4].Digest)) - deleteReq(req) - - // Delete reference blob created in setup - req = client.NewRequest(reggie.DELETE, "/v2//blobs/", reggie.WithDigest(testRefBlobADigest)) - deleteReq(req) - req = client.NewRequest(reggie.DELETE, "/v2//blobs/", reggie.WithDigest(testRefBlobBDigest)) - deleteReq(req) - - // Delete empty JSON blob created in setup - req = client.NewRequest(reggie.DELETE, "/v2//blobs/", reggie.WithDigest(emptyJSONDescriptor.Digest.String())) - deleteReq(req) - - if !deleteManifestBeforeBlobs { - // Delete manifest created in setup - req = client.NewRequest(reggie.DELETE, "/v2//manifests/", - reggie.WithReference(refsIndexArtifactDigest)) - deleteReq(req) - req = client.NewRequest(reggie.DELETE, "/v2//manifests/", - reggie.WithReference(refsManifestAConfigArtifactDigest)) - deleteReq(req) - req = client.NewRequest(reggie.DELETE, "/v2//manifests/", - reggie.WithReference(refsManifestALayerArtifactDigest)) - deleteReq(req) - req = client.NewRequest(reggie.DELETE, "/v2//manifests/", reggie.WithDigest(manifests[4].Digest)) - deleteReq(req) - req = client.NewRequest(reggie.DELETE, "/v2//manifests/", - reggie.WithReference(refsManifestBConfigArtifactDigest)) - deleteReq(req) - req = client.NewRequest(reggie.DELETE, "/v2//manifests/", - reggie.WithReference(refsManifestBLayerArtifactDigest)) - deleteReq(req) - } - }) }) }) } diff --git a/conformance/04_management_test.go b/conformance/04_management_test.go index 00334b23..a281aa70 100644 --- a/conformance/04_management_test.go +++ b/conformance/04_management_test.go @@ -35,52 +35,41 @@ var test04ContentManagement = func() { g.Specify("Populate registry with test config blob", func() { SkipIfDisabled(contentManagement) RunOnlyIf(runContentManagementSetup) - req := client.NewRequest(reggie.POST, "/v2//blobs/uploads/") - resp, err := client.Do(req) - Expect(err).To(BeNil()) - req = client.NewRequest(reggie.PUT, resp.GetRelativeLocation()). - SetHeader("Content-Length", configs[3].ContentLength). - SetHeader("Content-Type", "application/octet-stream"). - SetQueryParam("digest", configs[3].Digest). - SetBody(configs[3].Content) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + pushBlob( + &BlobInfo{ + Digest: configs[3].Digest, + Content: configs[3].Content, + Length: configs[3].ContentLength, + }, + nil, g.GinkgoT(), + ) }) g.Specify("Populate registry with test layer", func() { SkipIfDisabled(contentManagement) RunOnlyIf(runContentManagementSetup) - req := client.NewRequest(reggie.POST, "/v2//blobs/uploads/") - resp, err := client.Do(req) - Expect(err).To(BeNil()) - req = client.NewRequest(reggie.PUT, resp.GetRelativeLocation()). - SetQueryParam("digest", layerBlobDigest). - SetHeader("Content-Type", "application/octet-stream"). - SetHeader("Content-Length", layerBlobContentLength). - SetBody(layerBlobData) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + pushBlob( + &BlobInfo{ + Digest: layerBlobDigest, + Content: layerBlobData, + Length: layerBlobContentLength, + }, + nil, g.GinkgoT(), + ) }) g.Specify("Populate registry with test tag", func() { SkipIfDisabled(contentManagement) RunOnlyIf(runContentManagementSetup) tagToDelete = defaultTagName - req := client.NewRequest(reggie.PUT, "/v2//manifests/", - reggie.WithReference(tagToDelete)). - SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json"). - SetBody(manifests[3].Content) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAll( - BeNumerically(">=", 200), - BeNumerically("<", 300))) + pushManifest( + &ManifestInfo{ + Tag: tagToDelete, + Digest: manifests[3].Digest, + Content: manifests[3].Content, + }, + nil, g.GinkgoT(), + ) }) g.Specify("Check how many tags there are before anything gets deleted", func() { @@ -106,8 +95,8 @@ var test04ContentManagement = func() { resp, err := client.Do(req) Expect(err).To(BeNil()) Expect(resp.StatusCode()).To(SatisfyAny( - Equal(http.StatusBadRequest), Equal(http.StatusAccepted), + Equal(http.StatusBadRequest), Equal(http.StatusMethodNotAllowed))) if resp.StatusCode() == http.StatusBadRequest { errorResponses, err := resp.Errors() @@ -117,7 +106,7 @@ var test04ContentManagement = func() { } }) - g.Specify("DELETE request to manifest (digest) should yield 202 response unless already deleted", func() { + g.Specify("DELETE request to manifest (digest) should yield 202 response, unless already deleted (404) or manifest deletion is disallowed (400/405)", func() { SkipIfDisabled(contentManagement) req := client.NewRequest(reggie.DELETE, "/v2//manifests/", reggie.WithDigest(manifests[3].Digest)) resp, err := client.Do(req) @@ -126,6 +115,8 @@ var test04ContentManagement = func() { Expect(resp.StatusCode()).To(SatisfyAny( Equal(http.StatusAccepted), Equal(http.StatusNotFound), + Equal(http.StatusBadRequest), + Equal(http.StatusMethodNotAllowed), )) }) @@ -164,29 +155,14 @@ var test04ContentManagement = func() { }) g.Context("Blob delete", func() { - g.Specify("DELETE request to blob URL should yield 202 response", func() { + g.Specify("DELETE request to blob URL should yield 202 response, unless blob deletion is disallowed (400/405)", func() { SkipIfDisabled(contentManagement) RunOnlyIf(runContentManagementSetup) // config blob - req := client.NewRequest(reggie.DELETE, "/v2//blobs/", reggie.WithDigest(configs[3].Digest)) - resp, err := client.Do(req) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - Equal(http.StatusAccepted), - Equal(http.StatusNotFound), - Equal(http.StatusMethodNotAllowed), - )) + deleteBlob(configs[3].Digest, g.GinkgoT()) // layer blob - req = client.NewRequest(reggie.DELETE, "/v2//blobs/", reggie.WithDigest(layerBlobDigest)) - resp, err = client.Do(req) - Expect(err).To(BeNil()) - Expect(err).To(BeNil()) - Expect(resp.StatusCode()).To(SatisfyAny( - Equal(http.StatusAccepted), - Equal(http.StatusNotFound), - Equal(http.StatusMethodNotAllowed), - )) - if resp.StatusCode() == http.StatusMethodNotAllowed { + respStatusCode := deleteBlob(layerBlobDigest, g.GinkgoT()) + if respStatusCode == http.StatusMethodNotAllowed { blobDeleteAllowed = false } }) diff --git a/conformance/setup.go b/conformance/setup.go index b9666a87..fad750f9 100644 --- a/conformance/setup.go +++ b/conformance/setup.go @@ -23,15 +23,18 @@ import ( "log" "math/big" mathrand "math/rand" + "net/http" "os" "path/filepath" "runtime" "strconv" + "strings" "github.com/bloodorangeio/reggie" "github.com/google/uuid" g "github.com/onsi/ginkgo/v2" "github.com/onsi/ginkgo/v2/formatter" + . "github.com/onsi/gomega" godigest "github.com/opencontainers/go-digest" ) @@ -675,3 +678,203 @@ func setupChunkedBlob(size int) { testBlobBChunk2Length = strconv.Itoa(len(testBlobBChunk2)) testBlobBChunk2Range = fmt.Sprintf("%d-%d", len(testBlobBChunk1), len(testBlobB)-1) } + +type ManifestInfo struct { + Index bool + Tag string + Digest string + Content []byte + Subject string +} + +func pushManifest( + manifestInfo *ManifestInfo, + manifestRefs []string, + t g.GinkgoTInterface, +) []string { + t.Helper() + // if tag passed, use it as reference, otherwise use digest + reference := manifestInfo.Tag + if reference == "" { + reference = manifestInfo.Digest + } + // if index, use corresponding mediaType + mediaType := "application/vnd.oci.image.manifest.v1+json" + if manifestInfo.Index { + mediaType = "application/vnd.oci.image.index.v1+json" + } + // push manifest + req := client.NewRequest(reggie.PUT, "/v2//manifests/", + reggie.WithReference(reference)). + SetHeader("Content-Type", mediaType). + SetBody(manifestInfo.Content) + resp, err := client.Do(req) + // check all expectations + Expect(err).To(BeNil()) + Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) + Expect(resp.Header().Get("Location")).ToNot(BeEmpty()) + // OCI-Subject is empty if referrers API unsupported + Expect(resp.Header().Get("OCI-Subject")).To(SatisfyAny( + Equal(manifestInfo.Subject), + Equal(""))) + // keep track of all reference(s) to manifest + manifestRefs = append(manifestRefs, reference) + if manifestInfo.Tag != "" { + manifestRefs = append(manifestRefs, manifestInfo.Digest) + } + return manifestRefs +} + +func deleteManifest( + ref string, + t g.GinkgoTInterface, +) { + t.Helper() + + req := client.NewRequest(reggie.GET, "/v2//manifests/", + reggie.WithReference(ref)) + resp, err := client.Do(req) + Expect(err).To(BeNil()) + if resp.StatusCode() == http.StatusOK { + req := client.NewRequest(reggie.DELETE, "/v2//manifests/", + reggie.WithReference(ref)) + resp, err := client.Do(req) + Expect(err).To(BeNil()) + Expect(resp.StatusCode()).To(SatisfyAny( + Equal(http.StatusAccepted), + Equal(http.StatusMethodNotAllowed), + Equal(http.StatusNotFound), + )) + req = client.NewRequest(reggie.GET, "/v2//manifests/", + reggie.WithReference(ref)) + resp, err = client.Do(req) + Expect(err).To(BeNil()) + Expect(resp.StatusCode()).To(Equal(http.StatusNotFound)) + } +} + +func deleteManifests( + manifestRefs []string, + t g.GinkgoTInterface, +) { + t.Helper() + + for len(manifestRefs) > 0 { + index := len(manifestRefs) - 1 + ref := manifestRefs[index] + manifestRefs = manifestRefs[:index] + deleteManifest(ref, t) + } +} + +type BlobInfo struct { + Digest string + Content []byte + Length string + Type string +} + +func pushBlob( + blobInfo *BlobInfo, + blobRefs []string, + t g.GinkgoTInterface, +) []string { + t.Helper() + + if blobInfo.Type == "" { + blobInfo.Type = "application/octet-stream" + } + req := client.NewRequest(reggie.POST, "/v2//blobs/uploads/") + resp, err := client.Do(req) + Expect(err).To(BeNil()) + Expect(resp.StatusCode()).To(SatisfyAny( + Equal(http.StatusAccepted), + Equal(http.StatusCreated), + )) + req = client.NewRequest(reggie.PUT, resp.GetRelativeLocation()). + SetQueryParam("digest", blobInfo.Digest). + SetHeader("Content-Type", blobInfo.Type). + SetHeader("Content-Length", blobInfo.Length). + SetBody(blobInfo.Content) + resp, err = client.Do(req) + Expect(err).To(BeNil()) + Expect(resp.StatusCode()).To(Equal(http.StatusCreated)) + blobRefs = append(blobRefs, blobInfo.Digest) + return blobRefs +} + +func mountBlob( + digest string, + from string, + blobRefs []string, + t g.GinkgoTInterface, +) ([]string, *reggie.Response) { + t.Helper() + + var req *reggie.Request + + if from == "" { + req = client.NewRequest(reggie.POST, "/v2//blobs/uploads/", + reggie.WithName(crossmountNamespace)). + SetQueryParam("mount", digest) + } else { + req = client.NewRequest(reggie.POST, "/v2//blobs/uploads/", + reggie.WithName(crossmountNamespace)). + SetQueryParam("mount", digest). + SetQueryParam("from", from) + } + resp, err := client.Do(req) + Expect(err).To(BeNil()) + Expect(resp.StatusCode()).To(SatisfyAny( + Equal(http.StatusCreated), + Equal(http.StatusAccepted), + )) + Expect(resp.GetAbsoluteLocation()).To(Not(BeEmpty())) + + digest = fmt.Sprintf("%s/%s", crossmountNamespace, digest) + blobRefs = append(blobRefs, digest) + return blobRefs, resp +} + +func deleteBlob( + ref string, + t g.GinkgoTInterface, +) int { + t.Helper() + + var req *reggie.Request + separator := "/" + if strings.Contains(ref, separator) { + // if blob not in default namespace, get it + parts := strings.Split(ref, separator) + namespace := parts[0] + ref = parts[1] + req = client.NewRequest(reggie.DELETE, "/v2//blobs/", reggie.WithName(namespace), reggie.WithReference(ref)) + } else { + // blob in default namespace + req = client.NewRequest(reggie.DELETE, "/v2//blobs/", reggie.WithReference(ref)) + } + resp, err := client.Do(req) + Expect(err).To(BeNil()) + Expect(resp.StatusCode()).To(SatisfyAny( + Equal(http.StatusAccepted), + Equal(http.StatusNotFound), + Equal(http.StatusBadRequest), + Equal(http.StatusMethodNotAllowed), + )) + return resp.StatusCode() +} + +func deleteBlobs( + blobRefs []string, + t g.GinkgoTInterface, +) { + t.Helper() + + for len(blobRefs) > 0 { + index := len(blobRefs) - 1 + ref := blobRefs[index] + blobRefs = blobRefs[:index] + deleteBlob(ref, t) + } +}