mirror of
https://github.com/project-zot/zot.git
synced 2026-06-16 04:17:55 +08:00
ext: use distribution spec route prefix for extension api
Following the spec defined here https://github.com/opencontainers/distribution-spec/tree/main/extensions Signed-off-by: Shivam Mishra <shimish2@cisco.com>
This commit is contained in:
committed by
Ramkumar Chinchani
parent
c1bf4456d0
commit
36c9631000
+3
-1
@@ -3,6 +3,7 @@ package api
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
glob "github.com/bmatcuk/doublestar/v4"
|
||||
"github.com/gorilla/mux"
|
||||
"zotregistry.io/zot/pkg/api/config"
|
||||
"zotregistry.io/zot/pkg/api/constants"
|
||||
"zotregistry.io/zot/pkg/common"
|
||||
"zotregistry.io/zot/pkg/log"
|
||||
)
|
||||
@@ -191,7 +193,7 @@ func AuthzHandler(ctlr *Controller) mux.MiddlewareFunc {
|
||||
ctx := acCtrlr.getContext(username, request)
|
||||
|
||||
// will return only repos on which client is authorized to read
|
||||
if request.RequestURI == "/v2/_catalog" {
|
||||
if request.RequestURI == fmt.Sprintf("%s%s", constants.RoutePrefix, constants.ExtCatalogPrefix) {
|
||||
next.ServeHTTP(response, request.WithContext(ctx))
|
||||
|
||||
return
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package constants
|
||||
|
||||
const (
|
||||
ArtifactSpecRoutePrefix = "/oras/artifacts/v1"
|
||||
RoutePrefix = "/v2"
|
||||
DistAPIVersion = "Docker-Distribution-API-Version"
|
||||
DistContentDigestKey = "Docker-Content-Digest"
|
||||
BlobUploadUUID = "Blob-Upload-UUID"
|
||||
DefaultMediaType = "application/json"
|
||||
BinaryMediaType = "application/octet-stream"
|
||||
ArtifactSpecRoutePrefix = "/oras/artifacts/v1"
|
||||
RoutePrefix = "/v2"
|
||||
DistAPIVersion = "Docker-Distribution-API-Version"
|
||||
DistContentDigestKey = "Docker-Content-Digest"
|
||||
BlobUploadUUID = "Blob-Upload-UUID"
|
||||
DefaultMediaType = "application/json"
|
||||
BinaryMediaType = "application/octet-stream"
|
||||
DefaultMetricsExtensionRoute = "/metrics"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package constants
|
||||
|
||||
// https://github.com/opencontainers/distribution-spec/tree/main/extensions#extensions-api-for-distribution
|
||||
const (
|
||||
ExtCatalogPrefix = "/_catalog"
|
||||
ExtOciDiscoverPrefix = "/_oci/ext/discover"
|
||||
// zot specific extensions.
|
||||
ExtSearchPrefix = RoutePrefix + "/_search"
|
||||
)
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
vldap "github.com/nmcclain/ldap"
|
||||
notreg "github.com/notaryproject/notation/pkg/registry"
|
||||
distext "github.com/opencontainers/distribution-spec/specs-go/v1/extensions"
|
||||
godigest "github.com/opencontainers/go-digest"
|
||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1"
|
||||
@@ -47,6 +48,7 @@ import (
|
||||
"zotregistry.io/zot/pkg/api"
|
||||
"zotregistry.io/zot/pkg/api/config"
|
||||
"zotregistry.io/zot/pkg/api/constants"
|
||||
extconf "zotregistry.io/zot/pkg/extensions/config"
|
||||
"zotregistry.io/zot/pkg/storage"
|
||||
"zotregistry.io/zot/pkg/test"
|
||||
)
|
||||
@@ -1878,7 +1880,7 @@ func TestAuthorizationWithBasicAuth(t *testing.T) {
|
||||
|
||||
// everybody should have access to /v2/_catalog
|
||||
resp, err = resty.R().SetBasicAuth(username, passphrase).
|
||||
Get(baseURL + "/v2/_catalog")
|
||||
Get(baseURL + constants.RoutePrefix + constants.ExtCatalogPrefix)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
|
||||
@@ -3195,7 +3197,7 @@ func TestParallelRequests(t *testing.T) {
|
||||
assert.Equal(t, tagResponse.StatusCode(), http.StatusOK, "response status code should return success code")
|
||||
|
||||
repoResponse, err := client.R().SetBasicAuth(username, passphrase).
|
||||
Get(baseURL + "/v2/_catalog")
|
||||
Get(baseURL + constants.RoutePrefix + constants.ExtCatalogPrefix)
|
||||
assert.Equal(t, err, nil, "Error should be nil")
|
||||
assert.Equal(t, repoResponse.StatusCode(), http.StatusOK, "response status code should return success code")
|
||||
})
|
||||
@@ -4812,6 +4814,80 @@ func TestPeriodicGC(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestDistSpecExtensions(t *testing.T) {
|
||||
Convey("start zot server with search extension", t, func(c C) {
|
||||
conf := config.New()
|
||||
port := test.GetFreePort()
|
||||
baseURL := test.GetBaseURL(port)
|
||||
|
||||
conf.HTTP.Port = port
|
||||
|
||||
defaultVal := true
|
||||
|
||||
searchConfig := &extconf.SearchConfig{
|
||||
Enable: &defaultVal,
|
||||
}
|
||||
|
||||
conf.Extensions = &extconf.ExtensionConfig{
|
||||
Search: searchConfig,
|
||||
}
|
||||
|
||||
logFile, err := ioutil.TempFile("", "zot-log*.txt")
|
||||
So(err, ShouldBeNil)
|
||||
conf.Log.Output = logFile.Name()
|
||||
defer os.Remove(logFile.Name()) // clean up
|
||||
|
||||
ctlr := api.NewController(conf)
|
||||
|
||||
ctlr.Config.Storage.RootDirectory = t.TempDir()
|
||||
|
||||
go startServer(ctlr)
|
||||
defer stopServer(ctlr)
|
||||
test.WaitTillServerReady(baseURL)
|
||||
|
||||
var extensionList distext.ExtensionList
|
||||
|
||||
resp, err := resty.R().Get(baseURL + constants.RoutePrefix + constants.ExtOciDiscoverPrefix)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
err = json.Unmarshal(resp.Body(), &extensionList)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(extensionList.Extensions), ShouldEqual, 1)
|
||||
})
|
||||
|
||||
Convey("start minimal zot server", t, func(c C) {
|
||||
conf := config.New()
|
||||
port := test.GetFreePort()
|
||||
baseURL := test.GetBaseURL(port)
|
||||
|
||||
conf.HTTP.Port = port
|
||||
|
||||
logFile, err := ioutil.TempFile("", "zot-log*.txt")
|
||||
So(err, ShouldBeNil)
|
||||
conf.Log.Output = logFile.Name()
|
||||
defer os.Remove(logFile.Name()) // clean up
|
||||
|
||||
ctlr := api.NewController(conf)
|
||||
|
||||
ctlr.Config.Storage.RootDirectory = t.TempDir()
|
||||
|
||||
go startServer(ctlr)
|
||||
defer stopServer(ctlr)
|
||||
test.WaitTillServerReady(baseURL)
|
||||
|
||||
var extensionList distext.ExtensionList
|
||||
resp, err := resty.R().Get(baseURL + constants.RoutePrefix + constants.ExtOciDiscoverPrefix)
|
||||
So(err, ShouldBeNil)
|
||||
So(resp, ShouldNotBeNil)
|
||||
So(resp.StatusCode(), ShouldEqual, 200)
|
||||
|
||||
err = json.Unmarshal(resp.Body(), &extensionList)
|
||||
So(err, ShouldBeNil)
|
||||
So(len(extensionList.Extensions), ShouldEqual, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func getAllBlobs(imagePath string) []string {
|
||||
blobList := make([]string, 0)
|
||||
|
||||
|
||||
+22
-1
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/gorilla/mux"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
notreg "github.com/notaryproject/notation/pkg/registry"
|
||||
"github.com/opencontainers/distribution-spec/specs-go/v1/extensions"
|
||||
ispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1"
|
||||
httpSwagger "github.com/swaggo/http-swagger"
|
||||
@@ -69,6 +70,7 @@ func (rh *RouteHandler) SetupRoutes() {
|
||||
rh.c.Router.Use(AuthzHandler(rh.c))
|
||||
}
|
||||
|
||||
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#endpoints
|
||||
prefixedRouter := rh.c.Router.PathPrefix(constants.RoutePrefix).Subrouter()
|
||||
{
|
||||
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/tags/list", NameRegexp.String()),
|
||||
@@ -97,8 +99,10 @@ func (rh *RouteHandler) SetupRoutes() {
|
||||
rh.UpdateBlobUpload).Methods("PUT")
|
||||
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", NameRegexp.String()),
|
||||
rh.DeleteBlobUpload).Methods("DELETE")
|
||||
prefixedRouter.HandleFunc("/_catalog",
|
||||
prefixedRouter.HandleFunc(constants.ExtCatalogPrefix,
|
||||
rh.ListRepositories).Methods(allowedMethods("GET")...)
|
||||
prefixedRouter.HandleFunc(constants.ExtOciDiscoverPrefix,
|
||||
rh.ListExtensions).Methods(allowedMethods("GET")...)
|
||||
prefixedRouter.HandleFunc("/",
|
||||
rh.CheckVersionSupport).Methods(allowedMethods("GET")...)
|
||||
}
|
||||
@@ -336,6 +340,10 @@ type ImageManifest struct {
|
||||
ispec.Manifest
|
||||
}
|
||||
|
||||
type ExtensionList struct {
|
||||
extensions.ExtensionList
|
||||
}
|
||||
|
||||
// GetManifest godoc
|
||||
// @Summary Get image manifest
|
||||
// @Description Get an image's manifest given a reference or a digest
|
||||
@@ -1249,6 +1257,19 @@ func (rh *RouteHandler) ListRepositories(response http.ResponseWriter, request *
|
||||
WriteJSON(response, http.StatusOK, is)
|
||||
}
|
||||
|
||||
// ListExtensions godoc
|
||||
// @Summary List Registry level extensions
|
||||
// @Description List all extensions present on registry
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} api.ExtensionList
|
||||
// @Router /v2/_oci/ext/discover [get].
|
||||
func (rh *RouteHandler) ListExtensions(w http.ResponseWriter, r *http.Request) {
|
||||
extensionList := ext.GetExtensions(rh.c.Config)
|
||||
|
||||
WriteJSON(w, http.StatusOK, extensionList)
|
||||
}
|
||||
|
||||
func (rh *RouteHandler) GetMetrics(w http.ResponseWriter, r *http.Request) {
|
||||
m := rh.c.Metrics.ReceiveMetrics()
|
||||
WriteJSON(w, http.StatusOK, m)
|
||||
|
||||
Reference in New Issue
Block a user