feat(artifact): add OCI references support (#936)

Thanks @jdolitsky et al for kicking off these changes at:
https://github.com/oci-playground/zot/commits/main

Thanks @sudo-bmitch for reviewing the patch

Signed-off-by: Ramkumar Chinchani <rchincha@cisco.com>
This commit is contained in:
Ramkumar Chinchani
2022-11-08 00:38:16 -08:00
committed by GitHub
parent eb722905cb
commit c0f93caacb
26 changed files with 865 additions and 118 deletions
+300 -5
View File
@@ -4060,7 +4060,7 @@ func TestImageSignatures(t *testing.T) {
resp, err = resty.R().SetQueryParam("artifactType", notreg.ArtifactTypeNotation).Get(
fmt.Sprintf("%s/oras/artifacts/v1/%s/manifests/%s/referrers", baseURL, repoName, digest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest)
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
cmd = exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
out, err = cmd.CombinedOutput()
So(err, ShouldNotBeNil)
@@ -4084,7 +4084,7 @@ func TestImageSignatures(t *testing.T) {
resp, err = resty.R().SetQueryParam("artifactType", notreg.ArtifactTypeNotation).Get(
fmt.Sprintf("%s/oras/artifacts/v1/%s/manifests/%s/referrers", baseURL, repoName, digest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest)
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
cmd = exec.Command("notation", "verify", "--cert", "good", "--plain-http", image)
out, err = cmd.CombinedOutput()
So(err, ShouldNotBeNil)
@@ -4093,7 +4093,7 @@ func TestImageSignatures(t *testing.T) {
})
})
Convey("GetReferrers", func() {
Convey("GetOrasReferrers", func() {
// cover error paths
resp, err := resty.R().Get(
fmt.Sprintf("%s/oras/artifacts/v1/%s/manifests/%s/referrers", baseURL, "badRepo", "badDigest"))
@@ -4123,6 +4123,301 @@ func TestImageSignatures(t *testing.T) {
})
}
func TestArtifactReferences(t *testing.T) {
Convey("Validate Artifact References", t, func() {
// start a new server
port := test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
ctlr := api.NewController(conf)
dir := t.TempDir()
ctlr.Config.Storage.RootDirectory = dir
go func(controller *api.Controller) {
// this blocks
if err := controller.Run(context.Background()); err != nil {
return
}
}(ctlr)
// wait till ready
for {
_, err := resty.R().Get(baseURL)
if err == nil {
break
}
time.Sleep(100 * time.Millisecond)
}
defer func(controller *api.Controller) {
ctx := context.Background()
_ = controller.Server.Shutdown(ctx)
}(ctlr)
repoName := "artifact-repo"
// create a blob/layer
resp, err := resty.R().Post(baseURL + fmt.Sprintf("/v2/%s/blobs/uploads/", repoName))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusAccepted)
loc := test.Location(baseURL, resp)
So(loc, ShouldNotBeEmpty)
resp, err = resty.R().Get(loc)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, 204)
content := []byte("this is a blob")
digest := godigest.FromBytes(content)
So(digest, ShouldNotBeNil)
// monolithic blob upload: success
resp, err = resty.R().SetQueryParam("digest", digest.String()).
SetHeader("Content-Type", "application/octet-stream").SetBody(content).Put(loc)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusCreated)
blobLoc := resp.Header().Get("Location")
So(blobLoc, ShouldNotBeEmpty)
So(resp.Header().Get("Content-Length"), ShouldEqual, "0")
So(resp.Header().Get(constants.DistContentDigestKey), ShouldNotBeEmpty)
// upload image config blob
resp, err = resty.R().Post(baseURL + fmt.Sprintf("/v2/%s/blobs/uploads/", repoName))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusAccepted)
loc = test.Location(baseURL, resp)
cblob, cdigest := test.GetRandomImageConfig()
resp, err = resty.R().
SetContentLength(true).
SetHeader("Content-Length", fmt.Sprintf("%d", len(cblob))).
SetHeader("Content-Type", "application/octet-stream").
SetQueryParam("digest", cdigest.String()).
SetBody(cblob).
Put(loc)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusCreated)
// create a manifest
manifest := ispec.Manifest{
Config: ispec.Descriptor{
MediaType: "application/vnd.oci.image.config.v1+json",
Digest: cdigest,
Size: int64(len(cblob)),
},
Layers: []ispec.Descriptor{
{
MediaType: "application/vnd.oci.image.layer.v1.tar",
Digest: digest,
Size: int64(len(content)),
},
},
}
manifest.SchemaVersion = 2
content, err = json.Marshal(manifest)
So(err, ShouldBeNil)
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 + fmt.Sprintf("/v2/%s/manifests/1.0", repoName))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusCreated)
d := resp.Header().Get(constants.DistContentDigestKey)
So(d, ShouldNotBeEmpty)
So(d, ShouldEqual, digest.String())
artifactType := "application/vnd.example.icecream.v1"
Convey("Validate Image Manifest Reference", func() {
resp, err = resty.R().Get(baseURL + fmt.Sprintf("/v2/%s/referrers/%s", repoName, digest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
// now upload a reference
// upload image config blob
resp, err = resty.R().Post(baseURL + fmt.Sprintf("/v2/%s/blobs/uploads/", repoName))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusAccepted)
loc = test.Location(baseURL, resp)
cblob, cdigest := test.GetEmptyImageConfig()
resp, err = resty.R().
SetContentLength(true).
SetHeader("Content-Length", fmt.Sprintf("%d", len(cblob))).
SetHeader("Content-Type", "application/octet-stream").
SetQueryParam("digest", cdigest.String()).
SetBody(cblob).
Put(loc)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusCreated)
// create a manifest
manifest := ispec.Manifest{
Config: ispec.Descriptor{
MediaType: artifactType,
Digest: cdigest,
Size: int64(len(cblob)),
},
Layers: []ispec.Descriptor{
{
MediaType: "application/vnd.oci.image.layer.v1.tar",
Digest: digest,
Size: int64(len(content)),
},
},
Subject: &ispec.Descriptor{
MediaType: ispec.MediaTypeImageManifest,
Digest: digest,
Size: int64(len(content)),
},
Annotations: map[string]string{
"key": "val",
},
}
manifest.SchemaVersion = 2
Convey("Using invalid content", func() {
resp, err = resty.R().SetHeader("Content-Type", ispec.MediaTypeImageManifest).
SetBody([]byte("invalid data")).Put(baseURL + fmt.Sprintf("/v2/%s/manifests/1.0", repoName))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest)
resp, err = resty.R().Get(baseURL + fmt.Sprintf("/v2/%s/referrers/%s", repoName, digest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
resp, err = resty.R().SetQueryParams(map[string]string{"artifactType": artifactType}).
Get(baseURL + fmt.Sprintf("/v2/%s/referrers/%s", repoName, digest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
})
Convey("Using valid content", func() {
content, err = json.Marshal(manifest)
So(err, ShouldBeNil)
resp, err = resty.R().SetHeader("Content-Type", ispec.MediaTypeImageManifest).
SetBody(content).Put(baseURL + fmt.Sprintf("/v2/%s/manifests/1.0", repoName))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusCreated)
resp, err = resty.R().Get(baseURL + fmt.Sprintf("/v2/%s/referrers/%s", repoName, digest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
resp, err = resty.R().SetQueryParams(map[string]string{"artifact": "invalid"}).
Get(baseURL + fmt.Sprintf("/v2/%s/referrers/%s", repoName, digest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
resp, err = resty.R().SetQueryParams(map[string]string{"artifactType": "invalid"}).
Get(baseURL + fmt.Sprintf("/v2/%s/referrers/%s", repoName, digest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
resp, err = resty.R().SetQueryParams(map[string]string{"artifactType": artifactType}).
Get(baseURL + fmt.Sprintf("/v2/%s/referrers/%s", repoName, digest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
So(resp.Header().Get("Content-Type"), ShouldEqual, ispec.MediaTypeImageIndex)
})
})
Convey("Validate Artifact Manifest Reference", func() {
resp, err = resty.R().Get(baseURL + fmt.Sprintf("/v2/%s/referrers/%s", repoName, digest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
// now upload a reference
// upload image config blob
resp, err = resty.R().Post(baseURL + fmt.Sprintf("/v2/%s/blobs/uploads/", repoName))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusAccepted)
loc = test.Location(baseURL, resp)
cblob, cdigest := test.GetEmptyImageConfig()
resp, err = resty.R().
SetContentLength(true).
SetHeader("Content-Length", fmt.Sprintf("%d", len(cblob))).
SetHeader("Content-Type", "application/octet-stream").
SetQueryParam("digest", cdigest.String()).
SetBody(cblob).
Put(loc)
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusCreated)
// create a artifact
manifest := ispec.Artifact{
MediaType: ispec.MediaTypeArtifactManifest,
ArtifactType: artifactType,
Blobs: []ispec.Descriptor{
{
MediaType: "application/vnd.oci.image.layer.v1.tar",
Digest: digest,
Size: int64(len(content)),
},
},
Subject: &ispec.Descriptor{
MediaType: ispec.MediaTypeImageManifest,
Digest: digest,
Size: int64(len(content)),
},
Annotations: map[string]string{
"key": "val",
},
}
Convey("Using invalid content", func() {
content := []byte("invalid data")
So(err, ShouldBeNil)
mdigest := godigest.FromBytes(content)
So(mdigest, ShouldNotBeNil)
resp, err = resty.R().SetHeader("Content-Type", ispec.MediaTypeArtifactManifest).
SetBody(content).Put(baseURL + fmt.Sprintf("/v2/%s/manifests/%s", repoName, mdigest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusBadRequest)
resp, err = resty.R().Get(baseURL + fmt.Sprintf("/v2/%s/referrers/%s", repoName, digest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
resp, err = resty.R().SetQueryParams(map[string]string{"artifactType": artifactType}).
Get(baseURL + fmt.Sprintf("/v2/%s/referrers/%s", repoName, digest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
})
Convey("Using valid content", func() {
content, err = json.Marshal(manifest)
So(err, ShouldBeNil)
mdigest := godigest.FromBytes(content)
So(mdigest, ShouldNotBeNil)
resp, err = resty.R().SetHeader("Content-Type", ispec.MediaTypeArtifactManifest).
SetBody(content).Put(baseURL + fmt.Sprintf("/v2/%s/manifests/%s", repoName, mdigest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusCreated)
resp, err = resty.R().Get(baseURL + fmt.Sprintf("/v2/%s/referrers/%s", repoName, digest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
resp, err = resty.R().SetQueryParams(map[string]string{"artifact": "invalid"}).
Get(baseURL + fmt.Sprintf("/v2/%s/referrers/%s", repoName, digest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
resp, err = resty.R().SetQueryParams(map[string]string{"artifactType": "invalid"}).
Get(baseURL + fmt.Sprintf("/v2/%s/referrers/%s", repoName, digest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
resp, err = resty.R().SetQueryParams(map[string]string{"artifactType": artifactType}).
Get(baseURL + fmt.Sprintf("/v2/%s/referrers/%s", repoName, digest.String()))
So(err, ShouldBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
So(resp.Header().Get("Content-Type"), ShouldEqual, ispec.MediaTypeImageIndex)
})
})
})
}
//nolint:dupl // duplicated test code
func TestRouteFailures(t *testing.T) {
Convey("Make a new controller", t, func() {
@@ -4685,7 +4980,7 @@ func TestRouteFailures(t *testing.T) {
request = mux.SetURLVars(request, map[string]string{})
response := httptest.NewRecorder()
rthdlr.GetReferrers(response, request)
rthdlr.GetOrasReferrers(response, request)
resp := response.Result()
defer resp.Body.Close()
@@ -4696,7 +4991,7 @@ func TestRouteFailures(t *testing.T) {
request = mux.SetURLVars(request, map[string]string{"name": "foo"})
response = httptest.NewRecorder()
rthdlr.GetReferrers(response, request)
rthdlr.GetOrasReferrers(response, request)
resp = response.Result()
defer resp.Body.Close()
+144 -20
View File
@@ -8,6 +8,8 @@
package api
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
@@ -96,6 +98,9 @@ func (rh *RouteHandler) SetupRoutes() {
rh.UpdateBlobUpload).Methods("PUT")
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/blobs/uploads/{session_id}", NameRegexp.String()),
rh.DeleteBlobUpload).Methods("DELETE")
// support for OCI artifact references
prefixedRouter.HandleFunc(fmt.Sprintf("/{name:%s}/referrers/{digest}", NameRegexp.String()),
rh.GetReferrers).Methods(allowedMethods("GET")...)
prefixedRouter.HandleFunc(constants.ExtCatalogPrefix,
rh.ListRepositories).Methods(allowedMethods("GET")...)
prefixedRouter.HandleFunc(constants.ExtOciDiscoverPrefix,
@@ -104,9 +109,9 @@ func (rh *RouteHandler) SetupRoutes() {
rh.CheckVersionSupport).Methods(allowedMethods("GET")...)
}
// support for oras artifact reference types (alpha 1) - image signature use case
// support for ORAS artifact reference types (alpha 1) - image signature use case
rh.c.Router.HandleFunc(fmt.Sprintf("%s/{name:%s}/manifests/{digest}/referrers",
constants.ArtifactSpecRoutePrefix, NameRegexp.String()), rh.GetReferrers).Methods("GET")
constants.ArtifactSpecRoutePrefix, NameRegexp.String()), rh.GetOrasReferrers).Methods("GET")
// swagger
debug.SetupSwaggerRoutes(rh.c.Config, rh.c.Router, rh.c.Log)
@@ -310,7 +315,8 @@ func (rh *RouteHandler) CheckManifest(response http.ResponseWriter, request *htt
return
}
content, digest, mediaType, err := getImageManifest(rh, imgStore, name, reference) //nolint:contextcheck
content, digest, mediaType, err := getImageManifest(request.Context(), rh, imgStore,
name, reference) //nolint:contextcheck
if err != nil {
if errors.Is(err, zerr.ErrRepoNotFound) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
WriteJSON(response, http.StatusNotFound,
@@ -375,7 +381,8 @@ func (rh *RouteHandler) GetManifest(response http.ResponseWriter, request *http.
return
}
content, digest, mediaType, err := getImageManifest(rh, imgStore, name, reference) //nolint: contextcheck
content, digest, mediaType, err := getImageManifest(request.Context(), rh,
imgStore, name, reference) //nolint: contextcheck
if err != nil {
if errors.Is(err, zerr.ErrRepoNotFound) { //nolint:gocritic // errorslint conflicts with gocritic:IfElseChain
WriteJSON(response, http.StatusNotFound,
@@ -398,6 +405,117 @@ func (rh *RouteHandler) GetManifest(response http.ResponseWriter, request *http.
WriteData(response, http.StatusOK, mediaType, content)
}
type ImageIndex struct {
ispec.Index
}
func getReferrers(ctx context.Context, routeHandler *RouteHandler,
imgStore storage.ImageStore, name string, digest godigest.Digest,
artifactType string,
) (ispec.Index, error) {
// first get the subject and then all its referrers
references, err := imgStore.GetReferrers(name, digest, artifactType)
if err != nil {
if routeHandler.c.Config.Extensions != nil &&
routeHandler.c.Config.Extensions.Sync != nil &&
*routeHandler.c.Config.Extensions.Sync.Enable {
routeHandler.c.Log.Info().Msgf("referrers not found, trying to get referrers to %s:%s by syncing on demand",
name, digest)
errSync := ext.SyncOneImage(ctx, routeHandler.c.Config, routeHandler.c.StoreController,
name, digest.String(), false, routeHandler.c.Log)
if errSync != nil {
routeHandler.c.Log.Error().Err(err).Str("name", name).Str("digest", digest.String()).Msg("unable to get references")
return ispec.Index{}, err
}
for _, ref := range references.Manifests {
errSync := ext.SyncOneImage(ctx, routeHandler.c.Config, routeHandler.c.StoreController,
name, ref.Digest.String(), false, routeHandler.c.Log)
if errSync != nil {
routeHandler.c.Log.Error().Err(err).Str("name", name).
Str("digest", ref.Digest.String()).Msg("unable to get references")
return ispec.Index{}, err
}
}
references, err = imgStore.GetReferrers(name, digest, artifactType)
}
}
return references, err
}
// GetReferrers godoc
// @Summary Get references for a given digest
// @Description Get references given a digest
// @Accept json
// @Produce application/vnd.oci.image.index.v1+json
// @Param name path string true "repository name"
// @Param digest path string true "digest"
// @Success 200 {object} api.ImageIndex
// @Failure 404 {string} string "not found"
// @Failure 500 {string} string "internal server error"
// @Router /v2/{name}/references/{digest} [get].
func (rh *RouteHandler) GetReferrers(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
name, ok := vars["name"]
if !ok || name == "" {
response.WriteHeader(http.StatusNotFound)
return
}
digestStr, ok := vars["digest"]
digest, err := godigest.Parse(digestStr)
if !ok || digestStr == "" || err != nil {
response.WriteHeader(http.StatusBadRequest)
return
}
// filter by artifact type
artifactType := ""
artifactTypes, ok := request.URL.Query()["artifactType"]
if ok {
if len(artifactTypes) != 1 {
rh.c.Log.Error().Msg("invalid artifact types")
response.WriteHeader(http.StatusBadRequest)
return
}
artifactType = artifactTypes[0]
}
rh.c.Log.Info().Str("digest", digest.String()).Str("artifactType", artifactType).Msg("getting manifest")
imgStore := rh.getImageStore(name)
referrers, err := getReferrers(request.Context(), rh, imgStore, name, digest, artifactType)
if err != nil {
rh.c.Log.Error().Err(err).Str("name", name).Str("digest", digest.String()).Msg("unable to get references")
response.WriteHeader(http.StatusNotFound)
return
}
out, err := json.Marshal(referrers)
if err != nil {
rh.c.Log.Error().Err(err).Str("name", name).Str("digest", digest.String()).Msg("unable to marshal json")
response.WriteHeader(http.StatusInternalServerError)
return
}
WriteData(response, http.StatusOK, ispec.MediaTypeImageIndex, out)
}
// UpdateManifest godoc
// @Summary Update image manifest
// @Description Update an image's manifest given a reference or a digest
@@ -1458,7 +1576,7 @@ func (rh *RouteHandler) getImageStore(name string) storage.ImageStore {
}
// will sync on demand if an image is not found, in case sync extensions is enabled.
func getImageManifest(routeHandler *RouteHandler, imgStore storage.ImageStore, name,
func getImageManifest(ctx context.Context, routeHandler *RouteHandler, imgStore storage.ImageStore, name,
reference string,
) ([]byte, godigest.Digest, string, error) {
content, digest, mediaType, err := imgStore.GetImageManifest(name, reference)
@@ -1470,7 +1588,7 @@ func getImageManifest(routeHandler *RouteHandler, imgStore storage.ImageStore, n
routeHandler.c.Log.Info().Msgf("image not found, trying to get image %s:%s by syncing on demand",
name, reference)
errSync := ext.SyncOneImage(routeHandler.c.Config, routeHandler.c.StoreController,
errSync := ext.SyncOneImage(ctx, routeHandler.c.Config, routeHandler.c.StoreController,
name, reference, false, routeHandler.c.Log)
if errSync != nil {
routeHandler.c.Log.Err(errSync).Msgf("error encounter while syncing image %s:%s",
@@ -1488,10 +1606,11 @@ func getImageManifest(routeHandler *RouteHandler, imgStore storage.ImageStore, n
}
// will sync referrers on demand if they are not found, in case sync extensions is enabled.
func getReferrers(routeHandler *RouteHandler, imgStore storage.ImageStore, name string, digest godigest.Digest,
func getOrasReferrers(ctx context.Context, routeHandler *RouteHandler,
imgStore storage.ImageStore, name string, digest godigest.Digest,
artifactType string,
) ([]artifactspec.Descriptor, error) {
refs, err := imgStore.GetReferrers(name, digest, artifactType)
refs, err := imgStore.GetOrasReferrers(name, digest, artifactType)
if err != nil {
if routeHandler.c.Config.Extensions != nil &&
routeHandler.c.Config.Extensions.Sync != nil &&
@@ -1499,7 +1618,7 @@ func getReferrers(routeHandler *RouteHandler, imgStore storage.ImageStore, name
routeHandler.c.Log.Info().Msgf("signature not found, trying to get signature %s:%s by syncing on demand",
name, digest.String())
errSync := ext.SyncOneImage(routeHandler.c.Config, routeHandler.c.StoreController,
errSync := ext.SyncOneImage(ctx, routeHandler.c.Config, routeHandler.c.StoreController,
name, digest.String(), true, routeHandler.c.Log)
if errSync != nil {
routeHandler.c.Log.Error().Err(err).Str("name", name).Str("digest", digest.String()).Msg("unable to get references")
@@ -1507,7 +1626,7 @@ func getReferrers(routeHandler *RouteHandler, imgStore storage.ImageStore, name
return []artifactspec.Descriptor{}, err
}
refs, err = imgStore.GetReferrers(name, digest, artifactType)
refs, err = imgStore.GetOrasReferrers(name, digest, artifactType)
}
}
@@ -1518,7 +1637,7 @@ type ReferenceList struct {
References []artifactspec.Descriptor `json:"references"`
}
// GetReferrers godoc
// GetOrasReferrers godoc
// @Summary Get references for an image
// @Description Get references for an image given a digest and artifact type
// @Accept json
@@ -1530,7 +1649,7 @@ type ReferenceList struct {
// @Failure 404 {string} string "not found"
// @Failure 500 {string} string "internal server error"
// @Router /oras/artifacts/v1/{name:%s}/manifests/{digest}/referrers [get].
func (rh *RouteHandler) GetReferrers(response http.ResponseWriter, request *http.Request) {
func (rh *RouteHandler) GetOrasReferrers(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
name, ok := vars["name"]
@@ -1549,16 +1668,21 @@ func (rh *RouteHandler) GetReferrers(response http.ResponseWriter, request *http
return
}
// filter by artifact type
artifactType := ""
artifactTypes, ok := request.URL.Query()["artifactType"]
if !ok || len(artifactTypes) != 1 {
rh.c.Log.Error().Msg("invalid artifact types")
response.WriteHeader(http.StatusBadRequest)
if ok {
if len(artifactTypes) != 1 {
rh.c.Log.Error().Msg("invalid artifact types")
response.WriteHeader(http.StatusBadRequest)
return
return
}
artifactType = artifactTypes[0]
}
artifactType := artifactTypes[0]
if artifactType != notreg.ArtifactTypeNotation {
rh.c.Log.Error().Str("artifactType", artifactType).Msg("invalid artifact type")
response.WriteHeader(http.StatusBadRequest)
@@ -1570,10 +1694,10 @@ func (rh *RouteHandler) GetReferrers(response http.ResponseWriter, request *http
rh.c.Log.Info().Str("digest", digest.String()).Str("artifactType", artifactType).Msg("getting manifest")
refs, err := getReferrers(rh, imgStore, name, digest, artifactType) //nolint:contextcheck
refs, err := getOrasReferrers(request.Context(), rh, imgStore, name, digest, artifactType) //nolint:contextcheck
if err != nil {
rh.c.Log.Error().Err(err).Str("name", name).Str("digest", digest.String()).Msg("unable to get references")
response.WriteHeader(http.StatusBadRequest)
response.WriteHeader(http.StatusNotFound)
return
}