diff --git a/Dockerfile-conformance b/Dockerfile-conformance new file mode 100644 index 00000000..58eb4995 --- /dev/null +++ b/Dockerfile-conformance @@ -0,0 +1,29 @@ +# --- +# Stage 1: Install certs, build binary, create default config file +# --- +FROM docker.io/golang:1.13.6-alpine3.11 AS builder +RUN apk --update add git make ca-certificates +RUN mkdir -p /go/src/github.com/anuvu/zot +WORKDIR /go/src/github.com/anuvu/zot +COPY . . +RUN CGO_ENABLED=0 make clean binary +RUN echo -e '# Default config file for zot server\n\ +http:\n\ + address: 0.0.0.0\n\ + port: 5000\n\ +storage:\n\ + rootDirectory: /var/lib/registry\n\ + gc: false\n\ + dedupe: false' > config.yml && cat config.yml + +# --- +# Stage 2: Final image with nothing but certs, binary, and default config file +# --- +FROM scratch AS final +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY --from=builder /go/src/github.com/anuvu/zot/bin/zot /zot +COPY --from=builder /go/src/github.com/anuvu/zot/config.yml /etc/zot/config.yml +ENTRYPOINT ["/zot"] +EXPOSE 5000 +VOLUME ["/var/lib/registry"] +CMD ["serve", "/etc/zot/config.yml"] \ No newline at end of file diff --git a/pkg/api/controller_test.go b/pkg/api/controller_test.go index b73b2e75..a2dfe64d 100644 --- a/pkg/api/controller_test.go +++ b/pkg/api/controller_test.go @@ -1525,6 +1525,78 @@ func TestHTTPReadOnly(t *testing.T) { }) } +func TestCrossRepoMount(t *testing.T) { + Convey("Cross Repo Mount", t, func() { + config := api.NewConfig() + config.HTTP.Port = SecurePort1 + htpasswdPath := makeHtpasswdFileFromString(getCredString(username, passphrase)) + + // defer os.Remove(htpasswdPath) + + config.HTTP.Auth = &api.AuthConfig{ + HTPasswd: api.AuthHTPasswd{ + Path: htpasswdPath, + }, + } + + c := api.NewController(config) + + dir, err := ioutil.TempDir("", "oci-repo-test") + if err != nil { + panic(err) + } + + err = copyFiles("../../test/data", dir) + if err != nil { + panic(err) + } + // defer os.RemoveAll(dir) + + c.Config.Storage.RootDirectory = dir + + go func() { + // this blocks + if err := c.Run(); err != nil { + return + } + }() + + // wait till ready + for { + _, err := resty.R().Get(BaseURL1) + if err == nil { + break + } + + time.Sleep(100 * time.Millisecond) + } + + params := make(map[string]string) + + params["mount"] = "63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29" + params["from"] = "zot-test" + + client := resty.New() + postResponse, err := client.R(). + SetBasicAuth(username, passphrase).SetQueryParams(params). + Post(BaseURL1 + "/v2/zot-c-test/blobs/uploads/") + So(err, ShouldBeNil) + So(postResponse.StatusCode(), ShouldEqual, 202) + + postResponse, err = client.R(). + SetBasicAuth(username, passphrase).SetQueryParams(params). + Post(BaseURL1 + "/v2/zot-cve-test/blobs/uploads/") + So(err, ShouldBeNil) + So(postResponse.StatusCode(), ShouldEqual, 500) + + postResponse, err = client.R(). + SetBasicAuth(username, passphrase).SetQueryParams(params). + Post(BaseURL1 + "/v2/ /blobs/uploads/") + So(err, ShouldBeNil) + So(postResponse.StatusCode(), ShouldEqual, 404) + }) +} + func TestParallelRequests(t *testing.T) { testCases := []struct { srcImageName string diff --git a/pkg/api/routes.go b/pkg/api/routes.go index f2410699..fa1ea675 100644 --- a/pkg/api/routes.go +++ b/pkg/api/routes.go @@ -626,10 +626,23 @@ func (rh *RouteHandler) CreateBlobUpload(w http.ResponseWriter, r *http.Request) return } - // blob mounts not allowed since we don't have access control yet, and this - // may be a uncommon use case, but remain compliant - if _, ok := r.URL.Query()["mount"]; ok { - w.WriteHeader(http.StatusMethodNotAllowed) + // currently zot does not support cross-repository mounting, following dist-spec and returning 202 + if mountDigests, ok := r.URL.Query()["mount"]; ok { + if len(mountDigests) != 1 { + w.WriteHeader(http.StatusBadRequest) + return + } + + u, err := rh.c.ImageStore.NewBlobUpload(name) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.Header().Set("Location", path.Join(r.URL.String(), u)) + w.Header().Set(BlobUploadUUID, u) + w.WriteHeader(http.StatusAccepted) + return } @@ -908,7 +921,6 @@ func (rh *RouteHandler) UpdateBlobUpload(w http.ResponseWriter, r *http.Request) contentPresent := true contentLen, err := strconv.ParseInt(r.Header.Get("Content-Length"), 10, 64) - if err != nil { contentPresent = false } @@ -1070,7 +1082,6 @@ func getContentRange(r *http.Request) (int64 /* from */, int64 /* to */, error) tokens := strings.Split(contentRange, "-") from, err := strconv.ParseInt(tokens[0], 10, 64) - if err != nil { return -1, -1, errors.ErrBadUploadRange } @@ -1089,8 +1100,8 @@ func getContentRange(r *http.Request) (int64 /* from */, int64 /* to */, error) func WriteJSON(w http.ResponseWriter, status int, data interface{}) { var json = jsoniter.ConfigCompatibleWithStandardLibrary - body, err := json.Marshal(data) + body, err := json.Marshal(data) if err != nil { panic(err) } diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 565a7d1c..30a4744e 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -636,9 +636,10 @@ func (is *ImageStore) NewBlobUpload(repo string) (string, error) { } u := uuid.String() - blobUploadPath := is.BlobUploadPath(repo, u) - file, err := os.OpenFile(blobUploadPath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600) + blobUploadPath := is.BlobUploadPath(repo, u) + + file, err := os.OpenFile(blobUploadPath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0600) if err != nil { return "", errors.ErrRepoNotFound }