0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2024-12-30 22:34:13 -05:00

Merge pull request #52 from rchincha/compl

Compliance cleanup
This commit is contained in:
Serge Hallyn 2019-12-26 21:19:58 -06:00 committed by GitHub
commit 915c994c6c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 152 additions and 81 deletions

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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 {

View file

@ -5,8 +5,9 @@ type Config struct {
Port string
Version string
OutputJSON bool
Compliance bool
}
func NewConfig() *Config {
return &Config{}
return &Config{Compliance: true}
}

View file

@ -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)
})
})
}