diff --git a/README.md b/README.md
index 78dfccd0..d1c2d8a8 100644
--- a/README.md
+++ b/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 |
- [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)
+docker | - [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)
- Repository names cannot be mixed case due to [Issue #2771](https://github.com/docker/distribution/issues/2771)
+quay | TBD
+
+
# Ecosystem
## skopeo
diff --git a/pkg/api/controller.go b/pkg/api/controller.go
index 3a679705..6098ed92 100644
--- a/pkg/api/controller.go
+++ b/pkg/api/controller.go
@@ -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)
diff --git a/pkg/api/regexp.go b/pkg/api/regexp.go
index e2df041e..45b9a86b 100644
--- a/pkg/api/regexp.go
+++ b/pkg/api/regexp.go
@@ -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
diff --git a/pkg/api/routes.go b/pkg/api/routes.go
index 99bb69af..0f7e6433 100644
--- a/pkg/api/routes.go
+++ b/pkg/api/routes.go
@@ -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 {
diff --git a/pkg/compliance/config.go b/pkg/compliance/config.go
index 721d7312..8b17140e 100644
--- a/pkg/compliance/config.go
+++ b/pkg/compliance/config.go
@@ -5,8 +5,9 @@ type Config struct {
Port string
Version string
OutputJSON bool
+ Compliance bool
}
func NewConfig() *Config {
- return &Config{}
+ return &Config{Compliance: true}
}
diff --git a/pkg/compliance/v1_0_0/check.go b/pkg/compliance/v1_0_0/check.go
index 2458ea1f..cc5cf31b 100644
--- a/pkg/compliance/v1_0_0/check.go
+++ b/pkg/compliance/v1_0_0/check.go
@@ -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)
- So(len(r.Repositories), ShouldBeGreaterThan, 0)
- So(r.Repositories[0], ShouldEqual, "a/b/c/d")
- So(r.Repositories[1], ShouldEqual, "z")
+ 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)
- So(resp.StatusCode(), ShouldEqual, 200)
- So(resp.String(), ShouldNotBeEmpty)
+ 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)
- So(resp.StatusCode(), ShouldEqual, 200)
- So(resp.String(), ShouldNotBeEmpty)
+ 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)
- So(resp.StatusCode(), ShouldEqual, 200)
- So(resp.String(), ShouldNotBeEmpty)
+ 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)
+ })
})
}