mirror of
https://github.com/project-zot/zot.git
synced 2024-12-30 22:34:13 -05:00
commit
915c994c6c
6 changed files with 152 additions and 81 deletions
28
README.md
28
README.md
|
@ -59,6 +59,34 @@ Examples of config files are available in [examples/](examples/) dir.
|
|||
bin/zot compliance -H hostIP -P port [-V "all"] [--json]
|
||||
```
|
||||
|
||||
Compliance is important for the following reasons:
|
||||
|
||||
1. A standards-based client code can be implemented that can then interact with
|
||||
compliant registries.
|
||||
|
||||
2. Customers benefit from the ability to move and locate their images across
|
||||
compliant registries.
|
||||
|
||||
## Methodology
|
||||
|
||||
* A _positive_ compliance means the registry is compliant and meaningful work
|
||||
can be accomplished when interacting with that registry.
|
||||
|
||||
* A _negative_ compliance means the registry is compliant, however, it only
|
||||
returns errors that are compliant and no meaningful work can be performed when
|
||||
interacting with that registry.
|
||||
|
||||
The focus of compliance tests is _positive_ compliance.
|
||||
|
||||
## Compliance Reports
|
||||
|
||||
Registry | Notes
|
||||
---------|------
|
||||
zot | <ul><li>[Mount Blob](https://github.com/opencontainers/distribution-spec/blob/master/spec.md#mount-blob) is not implemented contingent upon [Issue #51](https://github.com/anuvu/zot/issues/51)</li></ul>
|
||||
docker | <ul><li>[Patch Blob Upload](https://github.com/opencontainers/distribution-spec/blob/master/spec.md#patch-blob-upload) is not [implemented](https://github.com/docker/distribution/blob/master/registry/handlers/blobupload.go#L136)</li><li>Repository names cannot be mixed case due to [Issue #2771](https://github.com/docker/distribution/issues/2771)</li></ul>
|
||||
quay | TBD
|
||||
|
||||
|
||||
# Ecosystem
|
||||
|
||||
## skopeo
|
||||
|
|
|
@ -42,6 +42,7 @@ func (c *Controller) Run() error {
|
|||
handlers.PrintRecoveryStack(false)))
|
||||
|
||||
c.Router = engine
|
||||
c.Router.UseEncodedPath()
|
||||
_ = NewRouteHandler(c)
|
||||
|
||||
c.ImageStore = storage.NewImageStore(c.Config.Storage.RootDirectory, c.Log)
|
||||
|
|
|
@ -6,7 +6,7 @@ import "regexp"
|
|||
var (
|
||||
// alphaNumericRegexp defines the alpha numeric atom, typically a
|
||||
// component of names. This only allows lower case characters and digits.
|
||||
alphaNumericRegexp = match(`[a-z0-9]+`)
|
||||
alphaNumericRegexp = match(`[a-zA-Z0-9]+`)
|
||||
|
||||
// separatorRegexp defines the separators allowed to be embedded in name
|
||||
// components. This allow one period, one or two underscore and multiple
|
||||
|
|
|
@ -838,7 +838,7 @@ func (rh *RouteHandler) DeleteBlobUpload(w http.ResponseWriter, r *http.Request)
|
|||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
type RepositoryList struct {
|
||||
|
|
|
@ -5,8 +5,9 @@ type Config struct {
|
|||
Port string
|
||||
Version string
|
||||
OutputJSON bool
|
||||
Compliance bool
|
||||
}
|
||||
|
||||
func NewConfig() *Config {
|
||||
return &Config{}
|
||||
return &Config{Compliance: true}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,20 @@ import (
|
|||
"gopkg.in/resty.v1"
|
||||
)
|
||||
|
||||
func Location(baseURL string, resp *resty.Response, config *compliance.Config) string {
|
||||
// For some API responses, the Location header is set and is supposed to
|
||||
// indicate an opaque value. However, it is not clear if this value is an
|
||||
// absolute URL (https://server:port/v2/...) or just a path (/v2/...)
|
||||
// zot implements the latter as per the spec, but some registries appear to
|
||||
// return the former - this needs to be clarified
|
||||
loc := resp.Header().Get("Location")
|
||||
if config.Compliance {
|
||||
return loc
|
||||
}
|
||||
|
||||
return baseURL + loc
|
||||
}
|
||||
|
||||
func CheckWorkflows(t *testing.T, config *compliance.Config) {
|
||||
if config == nil || config.Address == "" || config.Port == "" {
|
||||
panic("insufficient config")
|
||||
|
@ -70,140 +84,152 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
|
|||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
So(resp.String(), ShouldNotBeEmpty)
|
||||
r := resp.Result().(*api.RepositoryList)
|
||||
if !config.Compliance {
|
||||
// stricter check for zot ci/cd
|
||||
So(len(r.Repositories), ShouldBeGreaterThan, 0)
|
||||
So(r.Repositories[0], ShouldEqual, "a/b/c/d")
|
||||
So(r.Repositories[1], ShouldEqual, "z")
|
||||
}
|
||||
})
|
||||
|
||||
Convey("Get images in a repository", func() {
|
||||
Print("\nGet images in a repository")
|
||||
// non-existent repository should fail
|
||||
resp, err := resty.R().Get(baseURL + "/v2/repo/tags/list")
|
||||
resp, err := resty.R().Get(baseURL + "/v2/repo1/tags/list")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 404)
|
||||
So(resp.String(), ShouldNotBeEmpty)
|
||||
|
||||
// after newly created upload should succeed
|
||||
resp, err = resty.R().Post(baseURL + "/v2/repo/blobs/uploads/")
|
||||
resp, err = resty.R().Post(baseURL + "/v2/repo1/blobs/uploads/")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 202)
|
||||
|
||||
resp, err = resty.R().Get(baseURL + "/v2/repo/tags/list")
|
||||
resp, err = resty.R().Get(baseURL + "/v2/repo1/tags/list")
|
||||
So(err, ShouldBeNil)
|
||||
if !config.Compliance {
|
||||
// stricter check for zot ci/cd
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
So(resp.String(), ShouldNotBeEmpty)
|
||||
}
|
||||
})
|
||||
|
||||
Convey("Monolithic blob upload", func() {
|
||||
Print("\nMonolithic blob upload")
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repo/blobs/uploads/")
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repo2/blobs/uploads/")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 202)
|
||||
loc := resp.Header().Get("Location")
|
||||
loc := Location(baseURL, resp, config)
|
||||
So(loc, ShouldNotBeEmpty)
|
||||
|
||||
resp, err = resty.R().Get(baseURL + loc)
|
||||
resp, err = resty.R().Get(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 204)
|
||||
|
||||
resp, err = resty.R().Get(baseURL + "/v2/repo/tags/list")
|
||||
resp, err = resty.R().Get(baseURL + "/v2/repo2/tags/list")
|
||||
So(err, ShouldBeNil)
|
||||
if !config.Compliance {
|
||||
// stricter check for zot ci/cd
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
So(resp.String(), ShouldNotBeEmpty)
|
||||
}
|
||||
|
||||
// without a "?digest=<>" should fail
|
||||
content := []byte("this is a blob")
|
||||
digest := godigest.FromBytes(content)
|
||||
So(digest, ShouldNotBeNil)
|
||||
resp, err = resty.R().Put(baseURL + loc)
|
||||
resp, err = resty.R().Put(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 400)
|
||||
// without the Content-Length should fail
|
||||
resp, err = resty.R().SetQueryParam("digest", digest.String()).Put(baseURL + loc)
|
||||
resp, err = resty.R().SetQueryParam("digest", digest.String()).Put(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 400)
|
||||
// without any data to send, should fail
|
||||
resp, err = resty.R().SetQueryParam("digest", digest.String()).
|
||||
SetHeader("Content-Type", "application/octet-stream").Put(baseURL + loc)
|
||||
SetHeader("Content-Type", "application/octet-stream").Put(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 400)
|
||||
// monolithic blob upload: success
|
||||
resp, err = resty.R().SetQueryParam("digest", digest.String()).
|
||||
SetHeader("Content-Type", "application/octet-stream").SetBody(content).Put(baseURL + loc)
|
||||
SetHeader("Content-Type", "application/octet-stream").SetBody(content).Put(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 201)
|
||||
blobLoc := resp.Header().Get("Location")
|
||||
blobLoc := Location(baseURL, resp, config)
|
||||
So(blobLoc, ShouldNotBeEmpty)
|
||||
So(resp.Header().Get("Content-Length"), ShouldEqual, "0")
|
||||
So(resp.Header().Get(api.DistContentDigestKey), ShouldNotBeEmpty)
|
||||
// upload reference should now be removed
|
||||
resp, err = resty.R().Get(baseURL + loc)
|
||||
resp, err = resty.R().Get(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 404)
|
||||
// blob reference should be accessible
|
||||
resp, err = resty.R().Get(baseURL + blobLoc)
|
||||
resp, err = resty.R().Get(blobLoc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
})
|
||||
|
||||
Convey("Monolithic blob upload with multiple name components", func() {
|
||||
Print("\nMonolithic blob upload with multiple name components")
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repo1/repo2/repo3/blobs/uploads/")
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repo10/repo20/repo30/blobs/uploads/")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 202)
|
||||
loc := resp.Header().Get("Location")
|
||||
loc := Location(baseURL, resp, config)
|
||||
So(loc, ShouldNotBeEmpty)
|
||||
|
||||
resp, err = resty.R().Get(baseURL + loc)
|
||||
resp, err = resty.R().Get(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 204)
|
||||
|
||||
resp, err = resty.R().Get(baseURL + "/v2/repo1/repo2/repo3/tags/list")
|
||||
resp, err = resty.R().Get(baseURL + "/v2/repo10/repo20/repo30/tags/list")
|
||||
So(err, ShouldBeNil)
|
||||
if !config.Compliance {
|
||||
// stricter check for zot ci/cd
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
So(resp.String(), ShouldNotBeEmpty)
|
||||
}
|
||||
|
||||
// without a "?digest=<>" should fail
|
||||
content := []byte("this is a blob")
|
||||
digest := godigest.FromBytes(content)
|
||||
So(digest, ShouldNotBeNil)
|
||||
resp, err = resty.R().Put(baseURL + loc)
|
||||
resp, err = resty.R().Put(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 400)
|
||||
// without the Content-Length should fail
|
||||
resp, err = resty.R().SetQueryParam("digest", digest.String()).Put(baseURL + loc)
|
||||
resp, err = resty.R().SetQueryParam("digest", digest.String()).Put(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 400)
|
||||
// without any data to send, should fail
|
||||
resp, err = resty.R().SetQueryParam("digest", digest.String()).
|
||||
SetHeader("Content-Type", "application/octet-stream").Put(baseURL + loc)
|
||||
SetHeader("Content-Type", "application/octet-stream").Put(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 400)
|
||||
// monolithic blob upload: success
|
||||
resp, err = resty.R().SetQueryParam("digest", digest.String()).
|
||||
SetHeader("Content-Type", "application/octet-stream").SetBody(content).Put(baseURL + loc)
|
||||
SetHeader("Content-Type", "application/octet-stream").SetBody(content).Put(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 201)
|
||||
blobLoc := resp.Header().Get("Location")
|
||||
blobLoc := Location(baseURL, resp, config)
|
||||
So(blobLoc, ShouldNotBeEmpty)
|
||||
So(resp.Header().Get("Content-Length"), ShouldEqual, "0")
|
||||
So(resp.Header().Get(api.DistContentDigestKey), ShouldNotBeEmpty)
|
||||
// upload reference should now be removed
|
||||
resp, err = resty.R().Get(baseURL + loc)
|
||||
resp, err = resty.R().Get(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 404)
|
||||
// blob reference should be accessible
|
||||
resp, err = resty.R().Get(baseURL + blobLoc)
|
||||
resp, err = resty.R().Get(blobLoc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
})
|
||||
|
||||
Convey("Chunked blob upload", func() {
|
||||
Print("\nChunked blob upload")
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repo/blobs/uploads/")
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repo3/blobs/uploads/")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 202)
|
||||
loc := resp.Header().Get("Location")
|
||||
loc := Location(baseURL, resp, config)
|
||||
So(loc, ShouldNotBeEmpty)
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
@ -215,12 +241,12 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
|
|||
// write first chunk
|
||||
contentRange := fmt.Sprintf("%d-%d", 0, len(chunk1))
|
||||
resp, err = resty.R().SetHeader("Content-Type", "application/octet-stream").
|
||||
SetHeader("Content-Range", contentRange).SetBody(chunk1).Patch(baseURL + loc)
|
||||
SetHeader("Content-Range", contentRange).SetBody(chunk1).Patch(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 202)
|
||||
|
||||
// check progress
|
||||
resp, err = resty.R().Get(baseURL + loc)
|
||||
resp, err = resty.R().Get(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 204)
|
||||
r := resp.Header().Get("Range")
|
||||
|
@ -230,7 +256,7 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
|
|||
// write same chunk should fail
|
||||
contentRange = fmt.Sprintf("%d-%d", 0, len(chunk1))
|
||||
resp, err = resty.R().SetHeader("Content-Type", "application/octet-stream").
|
||||
SetHeader("Content-Range", contentRange).SetBody(chunk1).Patch(baseURL + loc)
|
||||
SetHeader("Content-Range", contentRange).SetBody(chunk1).Patch(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 400)
|
||||
So(resp.String(), ShouldNotBeEmpty)
|
||||
|
@ -247,31 +273,31 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
|
|||
contentRange = fmt.Sprintf("%d-%d", len(chunk1), len(buf.Bytes()))
|
||||
resp, err = resty.R().SetQueryParam("digest", digest.String()).
|
||||
SetHeader("Content-Range", contentRange).
|
||||
SetHeader("Content-Type", "application/octet-stream").SetBody(chunk2).Put(baseURL + loc)
|
||||
SetHeader("Content-Type", "application/octet-stream").SetBody(chunk2).Put(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 201)
|
||||
blobLoc := resp.Header().Get("Location")
|
||||
blobLoc := Location(baseURL, resp, config)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 201)
|
||||
So(blobLoc, ShouldNotBeEmpty)
|
||||
So(resp.Header().Get("Content-Length"), ShouldEqual, "0")
|
||||
So(resp.Header().Get(api.DistContentDigestKey), ShouldNotBeEmpty)
|
||||
// upload reference should now be removed
|
||||
resp, err = resty.R().Get(baseURL + loc)
|
||||
resp, err = resty.R().Get(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 404)
|
||||
// blob reference should be accessible
|
||||
resp, err = resty.R().Get(baseURL + blobLoc)
|
||||
resp, err = resty.R().Get(blobLoc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
})
|
||||
|
||||
Convey("Chunked blob upload with multiple name components", func() {
|
||||
Print("\nChunked blob upload with multiple name components")
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repo4/repo5/repo6/blobs/uploads/")
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repo40/repo50/repo60/blobs/uploads/")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 202)
|
||||
loc := resp.Header().Get("Location")
|
||||
loc := Location(baseURL, resp, config)
|
||||
So(loc, ShouldNotBeEmpty)
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
@ -283,12 +309,12 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
|
|||
// write first chunk
|
||||
contentRange := fmt.Sprintf("%d-%d", 0, len(chunk1))
|
||||
resp, err = resty.R().SetHeader("Content-Type", "application/octet-stream").
|
||||
SetHeader("Content-Range", contentRange).SetBody(chunk1).Patch(baseURL + loc)
|
||||
SetHeader("Content-Range", contentRange).SetBody(chunk1).Patch(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 202)
|
||||
|
||||
// check progress
|
||||
resp, err = resty.R().Get(baseURL + loc)
|
||||
resp, err = resty.R().Get(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 204)
|
||||
r := resp.Header().Get("Range")
|
||||
|
@ -298,7 +324,7 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
|
|||
// write same chunk should fail
|
||||
contentRange = fmt.Sprintf("%d-%d", 0, len(chunk1))
|
||||
resp, err = resty.R().SetHeader("Content-Type", "application/octet-stream").
|
||||
SetHeader("Content-Range", contentRange).SetBody(chunk1).Patch(baseURL + loc)
|
||||
SetHeader("Content-Range", contentRange).SetBody(chunk1).Patch(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 400)
|
||||
So(resp.String(), ShouldNotBeEmpty)
|
||||
|
@ -315,21 +341,21 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
|
|||
contentRange = fmt.Sprintf("%d-%d", len(chunk1), len(buf.Bytes()))
|
||||
resp, err = resty.R().SetQueryParam("digest", digest.String()).
|
||||
SetHeader("Content-Range", contentRange).
|
||||
SetHeader("Content-Type", "application/octet-stream").SetBody(chunk2).Put(baseURL + loc)
|
||||
SetHeader("Content-Type", "application/octet-stream").SetBody(chunk2).Put(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 201)
|
||||
blobLoc := resp.Header().Get("Location")
|
||||
blobLoc := Location(baseURL, resp, config)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 201)
|
||||
So(blobLoc, ShouldNotBeEmpty)
|
||||
So(resp.Header().Get("Content-Length"), ShouldEqual, "0")
|
||||
So(resp.Header().Get(api.DistContentDigestKey), ShouldNotBeEmpty)
|
||||
// upload reference should now be removed
|
||||
resp, err = resty.R().Get(baseURL + loc)
|
||||
resp, err = resty.R().Get(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 404)
|
||||
// blob reference should be accessible
|
||||
resp, err = resty.R().Get(baseURL + blobLoc)
|
||||
resp, err = resty.R().Get(blobLoc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
})
|
||||
|
@ -337,25 +363,25 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
|
|||
Convey("Create and delete uploads", func() {
|
||||
Print("\nCreate and delete uploads")
|
||||
// create a upload
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repo/blobs/uploads/")
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repo4/blobs/uploads/")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 202)
|
||||
loc := resp.Header().Get("Location")
|
||||
loc := Location(baseURL, resp, config)
|
||||
So(loc, ShouldNotBeEmpty)
|
||||
|
||||
// delete this upload
|
||||
resp, err = resty.R().Delete(baseURL + loc)
|
||||
resp, err = resty.R().Delete(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
So(resp.StatusCode(), ShouldEqual, 204)
|
||||
})
|
||||
|
||||
Convey("Create and delete blobs", func() {
|
||||
Print("\nCreate and delete blobs")
|
||||
// create a upload
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repo/blobs/uploads/")
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repo5/blobs/uploads/")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 202)
|
||||
loc := resp.Header().Get("Location")
|
||||
loc := Location(baseURL, resp, config)
|
||||
So(loc, ShouldNotBeEmpty)
|
||||
|
||||
content := []byte("this is a blob")
|
||||
|
@ -363,15 +389,15 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
|
|||
So(digest, ShouldNotBeNil)
|
||||
// monolithic blob upload
|
||||
resp, err = resty.R().SetQueryParam("digest", digest.String()).
|
||||
SetHeader("Content-Type", "application/octet-stream").SetBody(content).Put(baseURL + loc)
|
||||
SetHeader("Content-Type", "application/octet-stream").SetBody(content).Put(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 201)
|
||||
blobLoc := resp.Header().Get("Location")
|
||||
blobLoc := Location(baseURL, resp, config)
|
||||
So(blobLoc, ShouldNotBeEmpty)
|
||||
So(resp.Header().Get(api.DistContentDigestKey), ShouldNotBeEmpty)
|
||||
|
||||
// delete this blob
|
||||
resp, err = resty.R().Delete(baseURL + blobLoc)
|
||||
resp, err = resty.R().Delete(blobLoc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 202)
|
||||
So(resp.Header().Get("Content-Length"), ShouldEqual, "0")
|
||||
|
@ -380,21 +406,21 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
|
|||
Convey("Mount blobs", func() {
|
||||
Print("\nMount blobs from another repository")
|
||||
// create a upload
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repo/blobs/uploads/?digest=\"abc\"&&from=\"xyz\"")
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repo6/blobs/uploads/?digest=\"abc\"&&from=\"xyz\"")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 405)
|
||||
So(resp.StatusCode(), ShouldBeIn, []int{201, 202, 405})
|
||||
})
|
||||
|
||||
Convey("Manifests", func() {
|
||||
Print("\nManifests")
|
||||
// create a blob/layer
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repo/blobs/uploads/")
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repo7/blobs/uploads/")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 202)
|
||||
loc := resp.Header().Get("Location")
|
||||
loc := Location(baseURL, resp, config)
|
||||
So(loc, ShouldNotBeEmpty)
|
||||
|
||||
resp, err = resty.R().Get(baseURL + loc)
|
||||
resp, err = resty.R().Get(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 204)
|
||||
content := []byte("this is a blob")
|
||||
|
@ -402,7 +428,7 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
|
|||
So(digest, ShouldNotBeNil)
|
||||
// monolithic blob upload: success
|
||||
resp, err = resty.R().SetQueryParam("digest", digest.String()).
|
||||
SetHeader("Content-Type", "application/octet-stream").SetBody(content).Put(baseURL + loc)
|
||||
SetHeader("Content-Type", "application/octet-stream").SetBody(content).Put(loc)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 201)
|
||||
blobLoc := resp.Header().Get("Location")
|
||||
|
@ -417,7 +443,7 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
|
|||
digest = godigest.FromBytes(content)
|
||||
So(digest, ShouldNotBeNil)
|
||||
resp, err = resty.R().SetHeader("Content-Type", "application/vnd.oci.image.manifest.v1+json").
|
||||
SetBody(content).Put(baseURL + "/v2/repo/manifests/test:1.0")
|
||||
SetBody(content).Put(baseURL + "/v2/repo7/manifests/test:1.0")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 201)
|
||||
d := resp.Header().Get(api.DistContentDigestKey)
|
||||
|
@ -425,48 +451,63 @@ func CheckWorkflows(t *testing.T, config *compliance.Config) {
|
|||
So(d, ShouldEqual, digest.String())
|
||||
|
||||
// check/get by tag
|
||||
resp, err = resty.R().Head(baseURL + "/v2/repo/manifests/test:1.0")
|
||||
resp, err = resty.R().Head(baseURL + "/v2/repo7/manifests/test:1.0")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
resp, err = resty.R().Get(baseURL + "/v2/repo/manifests/test:1.0")
|
||||
resp, err = resty.R().Get(baseURL + "/v2/repo7/manifests/test:1.0")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
So(resp.Body(), ShouldNotBeEmpty)
|
||||
// check/get by reference
|
||||
resp, err = resty.R().Head(baseURL + "/v2/repo/manifests/" + digest.String())
|
||||
resp, err = resty.R().Head(baseURL + "/v2/repo7/manifests/" + digest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
resp, err = resty.R().Get(baseURL + "/v2/repo/manifests/" + digest.String())
|
||||
resp, err = resty.R().Get(baseURL + "/v2/repo7/manifests/" + digest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
So(resp.Body(), ShouldNotBeEmpty)
|
||||
|
||||
// delete manifest
|
||||
resp, err = resty.R().Delete(baseURL + "/v2/repo/manifests/test:1.0")
|
||||
resp, err = resty.R().Delete(baseURL + "/v2/repo7/manifests/test:1.0")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
// delete again should fail
|
||||
resp, err = resty.R().Delete(baseURL + "/v2/repo/manifests/" + digest.String())
|
||||
resp, err = resty.R().Delete(baseURL + "/v2/repo7/manifests/" + digest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 404)
|
||||
|
||||
// check/get by tag
|
||||
resp, err = resty.R().Head(baseURL + "/v2/repo/manifests/test:1.0")
|
||||
resp, err = resty.R().Head(baseURL + "/v2/repo7/manifests/test:1.0")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 404)
|
||||
resp, err = resty.R().Get(baseURL + "/v2/repo/manifests/test:1.0")
|
||||
resp, err = resty.R().Get(baseURL + "/v2/repo7/manifests/test:1.0")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 404)
|
||||
So(resp.Body(), ShouldNotBeEmpty)
|
||||
// check/get by reference
|
||||
resp, err = resty.R().Head(baseURL + "/v2/repo/manifests/" + digest.String())
|
||||
resp, err = resty.R().Head(baseURL + "/v2/repo7/manifests/" + digest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 404)
|
||||
resp, err = resty.R().Get(baseURL + "/v2/repo/manifests/" + digest.String())
|
||||
resp, err = resty.R().Get(baseURL + "/v2/repo7/manifests/" + digest.String())
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 404)
|
||||
So(resp.Body(), ShouldNotBeEmpty)
|
||||
})
|
||||
|
||||
// this is an additional test for repository names (alphanumeric)
|
||||
Convey("Repository names", func() {
|
||||
Print("\nRepository names")
|
||||
// create a blob/layer
|
||||
resp, err := resty.R().Post(baseURL + "/v2/repotest/blobs/uploads/")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 202)
|
||||
resp, err = resty.R().Post(baseURL + "/v2/repotest123/blobs/uploads/")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 202)
|
||||
resp, err = resty.R().Post(baseURL + "/v2/repoTest123/blobs/uploads/")
|
||||
So(err, ShouldBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 202)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue