Skip to content

Commit

Permalink
fix: check content-type when using referrers API
Browse files Browse the repository at this point in the history
Return an ErrUnsupported to when the referrers API is not supported.

Signed-off-by: Kyle M. Tarplee <kmtarplee@ieee.org>
  • Loading branch information
ktarplee committed Nov 16, 2023
1 parent 9febd7b commit fc50894
Show file tree
Hide file tree
Showing 5 changed files with 265 additions and 124 deletions.
1 change: 1 addition & 0 deletions example_copy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ func TestMain(m *testing.M) {
MediaType: ocispec.MediaTypeImageIndex,
Manifests: referrers,
}
w.Header().Set("Content-Type", ocispec.MediaTypeImageIndex)
if err := json.NewEncoder(w).Encode(result); err != nil {
panic(err)
}
Expand Down
22 changes: 18 additions & 4 deletions extendedcopy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1031,15 +1031,20 @@ func TestExtendedCopyGraph_FilterAnnotationWithMultipleRegex_Referrers(t *testin
// set up test server
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
p := r.URL.Path
var manifests []ocispec.Descriptor
switch {
case p == "/v2/test/referrers/"+descs[0].Digest.String():
manifests = descs[1:]
fallthrough
case strings.HasPrefix(p, "/v2/test/referrers/"):
result := ocispec.Index{
Versioned: specs.Versioned{
SchemaVersion: 2, // historical value. does not pertain to OCI or docker version
},
MediaType: ocispec.MediaTypeImageIndex,
Manifests: descs[1:],
Manifests: manifests,
}
w.Header().Set("Content-Type", ocispec.MediaTypeImageIndex)
if err := json.NewEncoder(w).Encode(result); err != nil {
t.Errorf("failed to write response: %v", err)
}
Expand Down Expand Up @@ -1076,7 +1081,6 @@ func TestExtendedCopyGraph_FilterAnnotationWithMultipleRegex_Referrers(t *testin
default:
t.Errorf("unexpected access: %s %s", r.Method, r.URL)
w.WriteHeader(http.StatusNotFound)
return
}
}))
defer ts.Close()
Expand Down Expand Up @@ -1454,15 +1458,20 @@ func TestExtendedCopyGraph_FilterArtifactTypeWithMultipleRegex_Referrers(t *test
// set up test server
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
p := r.URL.Path
var manifests []ocispec.Descriptor
switch {
case p == "/v2/test/referrers/"+descs[0].Digest.String():
manifests = descs[1:]
fallthrough
case strings.HasPrefix(p, "/v2/test/referrers/"):
result := ocispec.Index{
Versioned: specs.Versioned{
SchemaVersion: 2, // historical value. does not pertain to OCI or docker version
},
MediaType: ocispec.MediaTypeImageIndex,
Manifests: descs[1:],
Manifests: manifests,
}
w.Header().Set("Content-Type", ocispec.MediaTypeImageIndex)
if err := json.NewEncoder(w).Encode(result); err != nil {
t.Errorf("failed to write response: %v", err)
}
Expand Down Expand Up @@ -1695,15 +1704,20 @@ func TestExtendedCopyGraph_FilterArtifactTypeAndAnnotationWithMultipleRegex_Refe
// set up test server
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
p := r.URL.Path
var manifests []ocispec.Descriptor
switch {
case p == "/v2/test/referrers/"+descs[0].Digest.String():
manifests = descs[1:]
fallthrough
case strings.HasPrefix(p, "/v2/test/referrers/"):
result := ocispec.Index{
Versioned: specs.Versioned{
SchemaVersion: 2, // historical value. does not pertain to OCI or docker version
},
MediaType: ocispec.MediaTypeImageIndex,
Manifests: descs[1:],
Manifests: manifests,
}
w.Header().Set("Content-Type", ocispec.MediaTypeImageIndex)
if err := json.NewEncoder(w).Encode(result); err != nil {
t.Errorf("failed to write response: %v", err)
}
Expand Down
12 changes: 12 additions & 0 deletions registry/remote/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,17 @@ func TestMain(m *testing.M) {
w.Header().Set("Content-Digest", string(blobDescriptor.Digest))
w.Header().Set("Content-Length", strconv.Itoa(len(blobContent)))
w.Write([]byte(blobContent))
case p == fmt.Sprintf("/v2/%s/referrers/%s", exampleRepositoryName, "sha256:0000000000000000000000000000000000000000000000000000000000000000"):
result := ocispec.Index{
Versioned: specs.Versioned{
SchemaVersion: 2, // historical value. does not pertain to OCI or docker version
},
MediaType: ocispec.MediaTypeImageIndex,
}
w.Header().Set("Content-Type", ocispec.MediaTypeImageIndex)
if err := json.NewEncoder(w).Encode(result); err != nil {
panic(err)
}
case p == fmt.Sprintf("/v2/%s/referrers/%s", exampleRepositoryName, exampleManifestDescriptor.Digest.String()):
q := r.URL.Query()
var referrers []ocispec.Descriptor
Expand All @@ -191,6 +202,7 @@ func TestMain(m *testing.M) {
MediaType: ocispec.MediaTypeImageIndex,
Manifests: referrers,
}
w.Header().Set("Content-Type", ocispec.MediaTypeImageIndex)
if err := json.NewEncoder(w).Encode(result); err != nil {
panic(err)
}
Expand Down
26 changes: 18 additions & 8 deletions registry/remote/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,16 +483,14 @@ func (r *Repository) Referrers(ctx context.Context, desc ocispec.Descriptor, art

// The referrers state is unknown.
if err != nil {
var errResp *errcode.ErrorResponse
if !errors.As(err, &errResp) || errResp.StatusCode != http.StatusNotFound {
if !errors.Is(err, errdef.ErrUnsupported) {
return err
}
if errutil.IsErrorCode(errResp, errcode.ErrorCodeNameUnknown) {
if errutil.IsErrorCode(err, errcode.ErrorCodeNameUnknown) {
// The repository is not found, no fallback.
return err
}
// A 404 returned by Referrers API indicates that Referrers API is
// not supported. Fallback to referrers tag schema.
// Fallback to referrers tag schema.
r.SetReferrersCapability(false)
return r.referrersByTagSchema(ctx, desc, artifactType, fn)
}
Expand Down Expand Up @@ -544,10 +542,21 @@ func (r *Repository) referrersPageByAPI(ctx context.Context, artifactType string
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
switch resp.StatusCode {
case http.StatusOK:
case http.StatusNotFound:
// A 404 returned by Referrers API indicates that Referrers API is not supported.
return "", errors.Join(errdef.ErrUnsupported, errutil.ParseErrorResponse(resp))
default:
return "", errutil.ParseErrorResponse(resp)
}

// also check the content type
if ct := resp.Header.Get("Content-Type"); ct != ocispec.MediaTypeImageIndex {
err := errors.Join(errdef.ErrUnsupported, errutil.ParseErrorResponse(resp))
return "", fmt.Errorf("unknown content returned (%s), expecting image index: %w", ct, err)
}

var index ocispec.Index
lr := limitReader(resp.Body, r.MaxMetadataBytes)
if err := json.NewDecoder(lr).Decode(&index); err != nil {
Expand Down Expand Up @@ -657,8 +666,9 @@ func (r *Repository) pingReferrers(ctx context.Context) (bool, error) {

switch resp.StatusCode {
case http.StatusOK:
r.SetReferrersCapability(true)
return true, nil
supported := resp.Header.Get("Content-Type") == ocispec.MediaTypeImageIndex
r.SetReferrersCapability(supported)
return supported, nil
case http.StatusNotFound:
if err := errutil.ParseErrorResponse(resp); errutil.IsErrorCode(err, errcode.ErrorCodeNameUnknown) {
// repository not found
Expand Down
Loading

0 comments on commit fc50894

Please sign in to comment.