Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: implement the referrers route in the v2 package #32

Merged
merged 3 commits into from
Nov 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions registry/api/v2/descriptors.go
Original file line number Diff line number Diff line change
Expand Up @@ -1595,6 +1595,134 @@ var routeDescriptors = []RouteDescriptor{
},
},
},
{
Name: RouteNameReferrers,
Path: "/v2/{name:" + reference.NameRegexp.String() + "}/referrers/{digest:" + digest.DigestRegexp.String() + "}",
Entity: "Referrers",
Description: `Retrieve information about referrers.`,
Methods: []MethodDescriptor{
{
Method: "GET",
Description: "Fetch the referrers of the artifact identified by `digest`.",
Requests: []RequestDescriptor{
{
Name: "Referrers",
Description: "Request an unabridged list of referrers.",
Successes: []ResponseDescriptor{
{
Description: "Returns an image index containing all referrers as a json response.",
StatusCode: http.StatusOK,
Headers: []ParameterDescriptor{
{
Name: "Content-Length",
Type: "integer",
Description: "Length of the JSON response body.",
Format: "<length>",
},
linkHeader,
},
Body: BodyDescriptor{
ContentType: "application/vnd.oci.image.index.v1+json",
Format: `{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
<manifest>,
...
]
}`,
},
},
},
Failures: []ResponseDescriptor{
{
Description: "The registry does not support referrers API.",
StatusCode: http.StatusNotFound,
},
Comment on lines +1638 to +1641

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is also possible that the repository does not exists.

/cc @Wwwsylvia @m5i-work

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should return 404 and error code NAME_UNKNOWN when the repository does not exist.
References:

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the variable repositoryNotFoundResponseDescriptor defined in descriptors.go to address this scenario. It satisfies your specifications.
image
Now the Failures responses look like this, note the added line:
image

{
Description: "There was a problem with the request that needs to be addressed by the client, such as an invalid `name` or `digest`.",
StatusCode: http.StatusBadRequest,
ErrorCodes: []errcode.ErrorCode{
ErrorCodeDigestInvalid,
},
Body: BodyDescriptor{
ContentType: "application/json",
Format: errorsBody,
},
},
deniedResponseDescriptor,
tooManyRequestsDescriptor,
},
},
{
// may need to change to accommodate multiple filters applied.
// spec is not clear regarding applying multiple filters
Name: "referrers with filtering",
Description: "Request a list of referrers filtered on artifact type.",
QueryParameters: []ParameterDescriptor{
{
Name: "artifactType",
Type: "string",
Description: "This is the artifact type to be appied on the filter.",
Format: "<string>",
Required: false,
},
},
Successes: []ResponseDescriptor{
{
Description: "Returns an image index containing all referrers as a json response with the filter applied.",
StatusCode: http.StatusOK,
Headers: []ParameterDescriptor{
{
Name: "Content-Length",
Type: "integer",
Description: "Length of the JSON response body.",
Format: "<length>",
},
linkHeader,
},
Body: BodyDescriptor{
ContentType: "application/vnd.oci.image.index.v1+json",
Format: `{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
<manifest>,
...
],
"annotations": {
"org.opencontainers.referrers.filtersApplied": <filter>,
...
}
}`,
},
},
},
Failures: []ResponseDescriptor{
{
Description: "The registry does not support referrers API.",
StatusCode: http.StatusNotFound,
},
{
Description: "There was a problem with the request that needs to be addressed by the client, such as an invalid `name` or `digest`.",
StatusCode: http.StatusBadRequest,
ErrorCodes: []errcode.ErrorCode{
ErrorCodeDigestInvalid,
},
Body: BodyDescriptor{
ContentType: "application/json",
Format: errorsBody,
},
},
repositoryNotFoundResponseDescriptor,
deniedResponseDescriptor,
tooManyRequestsDescriptor,
},
},
},
},
},
},
}

var routeDescriptorsMap map[string]RouteDescriptor
Expand Down
1 change: 1 addition & 0 deletions registry/api/v2/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
RouteNameBlobUpload = "blob-upload"
RouteNameBlobUploadChunk = "blob-upload-chunk"
RouteNameCatalog = "catalog"
RouteNameReferrers = "referrers"
)

var (
Expand Down
8 changes: 8 additions & 0 deletions registry/api/v2/routes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,14 @@ func TestRouter(t *testing.T) {
"reference": "tag",
},
},
{
RouteName: RouteNameReferrers,
RequestURI: "/v2/foo/referrers/sha256:abcdef0919234",
Vars: map[string]string{
"name": "foo",
"digest": "sha256:abcdef0919234",
},
},
}

checkTestRouter(t, testCases, "", true)
Expand Down
12 changes: 12 additions & 0 deletions registry/api/v2/urls.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,18 @@ func (ub *URLBuilder) BuildManifestURL(ref reference.Named) (string, error) {
return manifestURL.String(), nil
}

// BuildReferrersURL constructs the url to fetch a list of referrers
func (ub *URLBuilder) BuildReferrersURL(ref reference.Canonical, values ...url.Values) (string, error) {
route := ub.cloneRoute(RouteNameReferrers)

referrersURL, err := route.URL("name", ref.Name(), "digest", ref.Digest().String())
if err != nil {
return "", err
}

return appendValuesURL(referrersURL, values...).String(), nil
}

// BuildBlobURL constructs the url for the blob identified by name and dgst.
func (ub *URLBuilder) BuildBlobURL(ref reference.Canonical) (string, error) {
route := ub.cloneRoute(RouteNameBlob)
Expand Down
20 changes: 20 additions & 0 deletions registry/api/v2/urls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,26 @@ func makeURLBuilderTestCases(urlBuilder *URLBuilder) []urlBuilderTestCase {
})
},
},
{
description: "build referrers url",
expectedPath: "/v2/foo/bar/referrers/sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5",
expectedErr: nil,
build: func() (string, error) {
ref, _ := reference.WithDigest(fooBarRef, "sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5")
return urlBuilder.BuildReferrersURL(ref)
},
},
{
description: "build referrers url with artifact type parameter",
expectedPath: "/v2/foo/bar/referrers/sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5?artifactType=example.test.type",
expectedErr: nil,
build: func() (string, error) {
ref, _ := reference.WithDigest(fooBarRef, "sha256:3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5")
return urlBuilder.BuildReferrersURL(ref, url.Values{
"artifactType": []string{"example.test.type"},
})
},
},
}
}

Expand Down