From 9aa45a1ab7f8a66e4d5a868e7d343304d1dc2384 Mon Sep 17 00:00:00 2001 From: Jon Johnson Date: Mon, 17 Apr 2023 15:29:44 -0700 Subject: [PATCH] Change return type of remote.Referrers (#1652) * Change return type of remote.Referrers Actually using this was cumbersome because it just returns a struct. This is a breaking change, but I don't think it's a huge deal because not too many people should be using this yet (and we're < 1.0.0). * go mod tidy k8schain? --- pkg/authn/k8schain/go.mod | 2 + pkg/authn/k8schain/go.sum | 9 +++++ pkg/v1/empty/index.go | 1 + pkg/v1/mutate/mutate_test.go | 2 +- pkg/v1/remote/descriptor.go | 67 ++++++++++++++++++--------------- pkg/v1/remote/referrers.go | 2 +- pkg/v1/remote/referrers_test.go | 37 +++++++++++++++--- 7 files changed, 81 insertions(+), 39 deletions(-) diff --git a/pkg/authn/k8schain/go.mod b/pkg/authn/k8schain/go.mod index b49beacd2..79544b2ad 100644 --- a/pkg/authn/k8schain/go.mod +++ b/pkg/authn/k8schain/go.mod @@ -42,6 +42,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.4 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.18.5 // indirect github.com/aws/smithy-go v1.13.5 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/docker/cli v23.0.1+incompatible // indirect @@ -73,6 +74,7 @@ require ( github.com/opencontainers/image-spec v1.1.0-rc2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/sirupsen/logrus v1.9.0 // indirect + github.com/vbatts/tar-split v0.11.2 // indirect golang.org/x/crypto v0.7.0 // indirect golang.org/x/net v0.8.0 // indirect golang.org/x/oauth2 v0.6.0 // indirect diff --git a/pkg/authn/k8schain/go.sum b/pkg/authn/k8schain/go.sum index e5cb66037..358d2626e 100644 --- a/pkg/authn/k8schain/go.sum +++ b/pkg/authn/k8schain/go.sum @@ -71,6 +71,8 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= +github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -191,6 +193,9 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -199,6 +204,7 @@ github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -207,7 +213,9 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= +github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= @@ -260,6 +268,7 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/pkg/v1/empty/index.go b/pkg/v1/empty/index.go index 10665356e..18b414891 100644 --- a/pkg/v1/empty/index.go +++ b/pkg/v1/empty/index.go @@ -60,5 +60,6 @@ func base() *v1.IndexManifest { return &v1.IndexManifest{ SchemaVersion: 2, MediaType: types.OCIImageIndex, + Manifests: []v1.Descriptor{}, } } diff --git a/pkg/v1/mutate/mutate_test.go b/pkg/v1/mutate/mutate_test.go index c4fdba630..17c8ee990 100644 --- a/pkg/v1/mutate/mutate_test.go +++ b/pkg/v1/mutate/mutate_test.go @@ -289,7 +289,7 @@ func TestAnnotations(t *testing.T) { }, { desc: "index", in: empty.Index, - want: `{"schemaVersion":2,"mediaType":"application/vnd.oci.image.index.v1+json","manifests":null,"annotations":{"foo":"bar"}}`, + want: `{"schemaVersion":2,"mediaType":"application/vnd.oci.image.index.v1+json","manifests":[],"annotations":{"foo":"bar"}}`, }, { desc: "arbitrary", in: arbitrary{}, diff --git a/pkg/v1/remote/descriptor.go b/pkg/v1/remote/descriptor.go index 7db91a743..ed942717b 100644 --- a/pkg/v1/remote/descriptor.go +++ b/pkg/v1/remote/descriptor.go @@ -17,7 +17,6 @@ package remote import ( "bytes" "context" - "encoding/json" "errors" "fmt" "io" @@ -31,6 +30,8 @@ import ( "github.com/google/go-containerregistry/pkg/logs" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/empty" + "github.com/google/go-containerregistry/pkg/v1/mutate" "github.com/google/go-containerregistry/pkg/v1/partial" "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/google/go-containerregistry/pkg/v1/types" @@ -301,7 +302,7 @@ func fallbackTag(d name.Digest) name.Tag { return d.Context().Tag(strings.Replace(d.DigestStr(), ":", "-", 1)) } -func (f *fetcher) fetchReferrers(ctx context.Context, filter map[string]string, d name.Digest) (*v1.IndexManifest, error) { +func (f *fetcher) fetchReferrers(ctx context.Context, filter map[string]string, d name.Digest) (v1.ImageIndex, error) { // Check the Referrers API endpoint first. u := f.url("referrers", d.DigestStr()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) @@ -319,32 +320,40 @@ func (f *fetcher) fetchReferrers(ctx context.Context, filter map[string]string, if err := transport.CheckError(resp, http.StatusOK, http.StatusNotFound, http.StatusBadRequest); err != nil { return nil, err } + + var b []byte if resp.StatusCode == http.StatusOK { - var im v1.IndexManifest - if err := json.NewDecoder(resp.Body).Decode(&im); err != nil { + b, err = io.ReadAll(resp.Body) + if err != nil { return nil, err } - return filterReferrersResponse(filter, &im), nil - } - - // The registry doesn't support the Referrers API endpoint, so we'll use the fallback tag scheme. - b, _, err := f.fetchManifest(ctx, fallbackTag(d), []types.MediaType{types.OCIImageIndex}) - if err != nil { + } else { + // The registry doesn't support the Referrers API endpoint, so we'll use the fallback tag scheme. + b, _, err = f.fetchManifest(ctx, fallbackTag(d), []types.MediaType{types.OCIImageIndex}) var terr *transport.Error - if ok := errors.As(err, &terr); ok && terr.StatusCode == http.StatusNotFound { + if errors.As(err, &terr) && terr.StatusCode == http.StatusNotFound { // Not found just means there are no attachments yet. Start with an empty manifest. - return &v1.IndexManifest{MediaType: types.OCIImageIndex}, nil + return empty.Index, nil + } else if err != nil { + return nil, err } - - return nil, err } - var im v1.IndexManifest - if err := json.Unmarshal(b, &im); err != nil { + h, sz, err := v1.SHA256(bytes.NewReader(b)) + if err != nil { return nil, err } - - return filterReferrersResponse(filter, &im), nil + idx := &remoteIndex{ + fetcher: *f, + manifest: b, + mediaType: types.OCIImageIndex, + descriptor: &v1.Descriptor{ + Digest: h, + MediaType: types.OCIImageIndex, + Size: sz, + }, + } + return filterReferrersResponse(filter, idx), nil } func (f *fetcher) fetchManifest(ctx context.Context, ref name.Reference, acceptable []types.MediaType) ([]byte, *v1.Descriptor, error) { @@ -551,19 +560,15 @@ func (f *fetcher) blobExists(h v1.Hash) (bool, error) { // If filter applied, filter out by artifactType. // See https://github.com/opencontainers/distribution-spec/blob/main/spec.md#listing-referrers -func filterReferrersResponse(filter map[string]string, origIndex *v1.IndexManifest) *v1.IndexManifest { - newIndex := origIndex +func filterReferrersResponse(filter map[string]string, in v1.ImageIndex) v1.ImageIndex { if filter == nil { - return newIndex - } - if v, ok := filter["artifactType"]; ok { - tmp := []v1.Descriptor{} - for _, desc := range newIndex.Manifests { - if desc.ArtifactType == v { - tmp = append(tmp, desc) - } - } - newIndex.Manifests = tmp + return in + } + v, ok := filter["artifactType"] + if !ok { + return in } - return newIndex + return mutate.RemoveManifests(in, func(desc v1.Descriptor) bool { + return desc.ArtifactType != v + }) } diff --git a/pkg/v1/remote/referrers.go b/pkg/v1/remote/referrers.go index 159785f10..546940e8a 100644 --- a/pkg/v1/remote/referrers.go +++ b/pkg/v1/remote/referrers.go @@ -22,7 +22,7 @@ import ( // Referrers returns a list of descriptors that refer to the given manifest digest. // // The subject manifest doesn't have to exist in the registry for there to be descriptors that refer to it. -func Referrers(d name.Digest, options ...Option) (*v1.IndexManifest, error) { +func Referrers(d name.Digest, options ...Option) (v1.ImageIndex, error) { o, err := makeOptions(options...) if err != nil { return nil, err diff --git a/pkg/v1/remote/referrers_test.go b/pkg/v1/remote/referrers_test.go index 75521df2f..7fb566a88 100644 --- a/pkg/v1/remote/referrers_test.go +++ b/pkg/v1/remote/referrers_test.go @@ -100,7 +100,11 @@ func TestReferrers(t *testing.T) { if err != nil { t.Fatal(err) } - if numManifests := len(index.Manifests); numManifests != 0 { + m, err := index.IndexManifest() + if err != nil { + t.Fatal(err) + } + if numManifests := len(m.Manifests); numManifests != 0 { t.Fatalf("expected index to contain 0 manifests, but had %d", numManifests) } @@ -126,7 +130,12 @@ func TestReferrers(t *testing.T) { if err != nil { t.Fatal(err) } - if d := cmp.Diff([]v1.Descriptor{leafDesc}, index.Manifests); d != "" { + m2, err := index.IndexManifest() + if err != nil { + t.Fatal(err) + } + if d := cmp.Diff([]v1.Descriptor{leafDesc}, m2.Manifests); d != "" { + t.Logf("m2.Manifests: %v", m2.Manifests) t.Fatalf("referrers diff (-want,+got): %s", d) } @@ -144,7 +153,11 @@ func TestReferrers(t *testing.T) { if err != nil { t.Fatal(err) } - if d := cmp.Diff(index.Manifests, mf.Manifests); d != "" { + m2, err := index.IndexManifest() + if err != nil { + t.Fatal(err) + } + if d := cmp.Diff(m2.Manifests, mf.Manifests); d != "" { t.Fatalf("fallback tag diff (-want,+got): %s", d) } } @@ -166,7 +179,11 @@ func TestReferrers(t *testing.T) { if err != nil { t.Fatal(err) } - if d := cmp.Diff([]v1.Descriptor{leafDesc}, index.Manifests); d != "" { + m3, err := index.IndexManifest() + if err != nil { + t.Fatal(err) + } + if d := cmp.Diff([]v1.Descriptor{leafDesc}, m3.Manifests); d != "" { t.Fatalf("referrers diff after second push (-want,+got): %s", d) } @@ -176,7 +193,11 @@ func TestReferrers(t *testing.T) { if err != nil { t.Fatal(err) } - if numManifests := len(index.Manifests); numManifests == 0 { + m4, err := index.IndexManifest() + if err != nil { + t.Fatal(err) + } + if numManifests := len(m4.Manifests); numManifests == 0 { t.Fatal("index contained 0 manifests") } @@ -185,7 +206,11 @@ func TestReferrers(t *testing.T) { if err != nil { t.Fatal(err) } - if numManifests := len(index.Manifests); numManifests != 0 { + m5, err := index.IndexManifest() + if err != nil { + t.Fatal(err) + } + if numManifests := len(m5.Manifests); numManifests != 0 { t.Fatalf("expected index to contain 0 manifests, but had %d", numManifests) } }