Skip to content

Commit

Permalink
conformance: initial commit for refererrers test
Browse files Browse the repository at this point in the history
  • Loading branch information
rchincha committed Jun 2, 2023
1 parent 58a1fe9 commit 4683bf1
Show file tree
Hide file tree
Showing 5 changed files with 549 additions and 40 deletions.
1 change: 1 addition & 0 deletions conformance/00_conformance_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ func TestConformance(t *testing.T) {
test02Push()
test03ContentDiscovery()
test04ContentManagement()
test05Referrers()
})

RegisterFailHandler(g.Fail)
Expand Down
286 changes: 286 additions & 0 deletions conformance/05_referrers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
package conformance

import (
"encoding/json"
"net/http"

"github.com/bloodorangeio/reggie"
g "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var test05Referrers = func() {
g.Context(titleReferrers, func() {

g.Context("Setup", func() {

g.Specify("Populate registry with reference blob before the image manifest is pushed", func() {
SkipIfDisabled(referrers)
RunOnlyIf(runReferencesSetup)
req := client.NewRequest(reggie.POST, "/v2/<name>/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)))
})

g.Specify("Populate registry with test references manifest (config.MediaType = artifactType)", func() {
SkipIfDisabled(referrers)
RunOnlyIf(runReferencesSetup)
req := client.NewRequest(reggie.PUT, "/v2/<name>/manifests/<reference>",
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))
})

g.Specify("Populate registry with test references manifest (ArtifactType, config.MediaType = emptyJSON)", func() {
SkipIfDisabled(referrers)
RunOnlyIf(runReferencesSetup)
req := client.NewRequest(reggie.PUT, "/v2/<name>/manifests/<reference>",
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))
})

g.Specify("Populate registry with test blob", func() {
SkipIfDisabled(referrers)
RunOnlyIf(runReferencesSetup)
req := client.NewRequest(reggie.POST, "/v2/<name>/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)))
})

g.Specify("Populate registry with test layer", func() {
SkipIfDisabled(referrers)
RunOnlyIf(runReferencesSetup)
req := client.NewRequest(reggie.POST, "/v2/<name>/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)))
})

g.Specify("Populate registry with test manifest", func() {
SkipIfDisabled(referrers)
RunOnlyIf(runReferencesSetup)
tag := testTagName
req := client.NewRequest(reggie.PUT, "/v2/<name>/manifests/<reference>",
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)))
})

g.Specify("Populate registry with reference blob after the image manifest is pushed", func() {
SkipIfDisabled(referrers)
RunOnlyIf(runReferencesSetup)
req := client.NewRequest(reggie.POST, "/v2/<name>/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)))
})

g.Specify("Populate registry with test references manifest (config.MediaType = artifactType)", func() {
SkipIfDisabled(referrers)
RunOnlyIf(runReferencesSetup)
req := client.NewRequest(reggie.PUT, "/v2/<name>/manifests/<reference>",
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))
})

g.Specify("Populate registry with test references manifest (ArtifactType, config.MediaType = emptyJSON)", func() {
SkipIfDisabled(referrers)
RunOnlyIf(runReferencesSetup)
req := client.NewRequest(reggie.PUT, "/v2/<name>/manifests/<reference>",
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))
})
})

g.Context("Get references", func() {
g.Specify("GET request to nonexistent blob should result in empty 200 response", func() {
SkipIfDisabled(referrers)
req := client.NewRequest(reggie.GET, "/v2/<name>/referrers/<digest>",
reggie.WithDigest(dummyDigest))
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusOK))

var index Index
err = json.Unmarshal(resp.Body(), &index)
Expect(err).To(BeNil())
Expect(len(index.Manifests)).To(BeZero())
})

g.Specify("GET request to existing blob should yield 200", func() {
SkipIfDisabled(referrers)
req := client.NewRequest(reggie.GET, "/v2/<name>/referrers/<digest>",
reggie.WithDigest(manifests[4].Digest))
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
if h := resp.Header().Get("Docker-Content-Digest"); h != "" {
Expect(h).To(Equal(configs[4].Digest))
}

var index Index
err = json.Unmarshal(resp.Body(), &index)
Expect(err).To(BeNil())
Expect(len(index.Manifests)).To(Equal(4))
Expect(index.Manifests[0].Digest).ToNot(Equal(index.Manifests[1].Digest))
})

g.Specify("GET request to existing blob with filter should yield 200", func() {
SkipIfDisabled(referrers)
req := client.NewRequest(reggie.GET, "/v2/<name>/referrers/<digest>",
reggie.WithDigest(manifests[4].Digest)).
SetQueryParam("artifactType", testRefArtifactTypeA)
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(Equal(http.StatusOK))
if h := resp.Header().Get("Docker-Content-Digest"); h != "" {
Expect(h).To(Equal(configs[4].Digest))
}

var index Index
err = json.Unmarshal(resp.Body(), &index)
Expect(err).To(BeNil())
Expect(len(index.Manifests)).To(Equal(2))

// also check resp header "OCI-Filters-Applied: artifactType" denoting that an artifactType filter was applied
Expect(resp.Header().Get("OCI-Filters-Applied")).To(EqualOrWarn(testRefArtifactTypeA))
})
})

g.Context("Teardown", func() {
if deleteManifestBeforeBlobs {
g.Specify("Delete manifest created in setup", func() {
SkipIfDisabled(referrers)
RunOnlyIf(runReferencesSetup)
req := client.NewRequest(reggie.DELETE, "/v2/<name>/manifests/<digest>", reggie.WithDigest(manifests[4].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 config blob created in setup", func() {
SkipIfDisabled(referrers)
RunOnlyIf(runReferencesSetup)
req := client.NewRequest(reggie.DELETE, "/v2/<name>/blobs/<digest>", reggie.WithDigest(configs[4].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 layer blob created in setup", func() {
SkipIfDisabled(referrers)
RunOnlyIf(runReferencesSetup)
req := client.NewRequest(reggie.DELETE, "/v2/<name>/blobs/<digest>", reggie.WithDigest(layerBlobDigest))
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(SatisfyAny(
SatisfyAll(
BeNumerically(">=", 200),
BeNumerically("<", 300),
),
Equal(http.StatusMethodNotAllowed),
))
})

if !deleteManifestBeforeBlobs {
g.Specify("Delete manifest created in setup", func() {
SkipIfDisabled(referrers)
RunOnlyIf(runReferencesSetup)
req := client.NewRequest(reggie.DELETE, "/v2/<name>/manifests/<digest>", reggie.WithDigest(manifests[4].Digest))
resp, err := client.Do(req)
Expect(err).To(BeNil())
Expect(resp.StatusCode()).To(SatisfyAny(
SatisfyAll(
BeNumerically(">=", 200),
BeNumerically("<", 300),
),
Equal(http.StatusMethodNotAllowed),
))
})
}
})
})
}
41 changes: 41 additions & 0 deletions conformance/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@ type Manifest struct {
// SchemaVersion is the image manifest schema that this image follows
SchemaVersion int `json:"schemaVersion"`

// ArtifactType specifies the IANA media type of artifact when the manifest is used for an artifact.
ArtifactType string `json:"artifactType,omitempty"`

// Config references a configuration object for a container, by digest.
// The referenced configuration object is a JSON blob that the runtime uses to set up the container.
Config Descriptor `json:"config"`

// Layers is an indexed list of layers referenced by the manifest.
Layers []Descriptor `json:"layers"`

// Subject is an optional link from the image manifest to another manifest forming an association between the image manifest and the other manifest.
Subject *Descriptor `json:"subject,omitempty"`
}

// Descriptor describes the disposition of targeted content.
Expand Down Expand Up @@ -71,3 +77,38 @@ type Image struct {
// RootFS references the layer content addresses used by the image.
RootFS RootFS `json:"rootfs"`
}

// Artifact describes an artifact manifest.
// This structure provides `application/vnd.oci.artifact.manifest.v1+json` mediatype when marshalled to JSON.
type Artifact struct {
// MediaType is the media type of the object this schema refers to.
MediaType string `json:"mediaType"`

// ArtifactType is the IANA media type of the artifact this schema refers to.
ArtifactType string `json:"artifactType,omitempty"`

// Blobs is a collection of blobs referenced by this manifest.
Blobs []Descriptor `json:"blobs,omitempty"`

// Subject (reference) is an optional link from the artifact to another manifest forming an association between the artifact and the other manifest.
Subject *Descriptor `json:"subject,omitempty"`

// Annotations contains arbitrary metadata for the artifact manifest.
Annotations map[string]string `json:"annotations,omitempty"`
}

// Index references manifests for various platforms.
// This structure provides `application/vnd.oci.image.index.v1+json` mediatype when marshalled to JSON.
type Index struct {
// SchemaVersion is the image manifest schema that this image follows
SchemaVersion int `json:"schemaVersion"`

// MediaType specifies the type of this document data structure e.g. `application/vnd.oci.image.index.v1+json`
MediaType string `json:"mediaType,omitempty"`

// Manifests references platform specific manifests.
Manifests []Descriptor `json:"manifests"`

// Annotations contains arbitrary metadata for the image index.
Annotations map[string]string `json:"annotations,omitempty"`
}
Loading

0 comments on commit 4683bf1

Please sign in to comment.