1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package remote
16
17 import (
18 "bytes"
19 "context"
20 "errors"
21 "io"
22 "net/http"
23 "strings"
24
25 "github.com/google/go-containerregistry/pkg/name"
26 v1 "github.com/google/go-containerregistry/pkg/v1"
27 "github.com/google/go-containerregistry/pkg/v1/empty"
28 "github.com/google/go-containerregistry/pkg/v1/mutate"
29 "github.com/google/go-containerregistry/pkg/v1/remote/transport"
30 "github.com/google/go-containerregistry/pkg/v1/types"
31 )
32
33
34
35
36 func Referrers(d name.Digest, options ...Option) (v1.ImageIndex, error) {
37 o, err := makeOptions(options...)
38 if err != nil {
39 return nil, err
40 }
41 return newPuller(o).referrers(o.context, d, o.filter)
42 }
43
44
45 func fallbackTag(d name.Digest) name.Tag {
46 return d.Context().Tag(strings.Replace(d.DigestStr(), ":", "-", 1))
47 }
48
49 func (f *fetcher) fetchReferrers(ctx context.Context, filter map[string]string, d name.Digest) (v1.ImageIndex, error) {
50
51 u := f.url("referrers", d.DigestStr())
52 req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
53 if err != nil {
54 return nil, err
55 }
56 req.Header.Set("Accept", string(types.OCIImageIndex))
57
58 resp, err := f.client.Do(req)
59 if err != nil {
60 return nil, err
61 }
62 defer resp.Body.Close()
63
64 if err := transport.CheckError(resp, http.StatusOK, http.StatusNotFound, http.StatusBadRequest); err != nil {
65 return nil, err
66 }
67
68 var b []byte
69 if resp.StatusCode == http.StatusOK {
70 b, err = io.ReadAll(resp.Body)
71 if err != nil {
72 return nil, err
73 }
74 } else {
75
76 b, _, err = f.fetchManifest(ctx, fallbackTag(d), []types.MediaType{types.OCIImageIndex})
77 var terr *transport.Error
78 if errors.As(err, &terr) && terr.StatusCode == http.StatusNotFound {
79
80 return empty.Index, nil
81 } else if err != nil {
82 return nil, err
83 }
84 }
85
86 h, sz, err := v1.SHA256(bytes.NewReader(b))
87 if err != nil {
88 return nil, err
89 }
90 idx := &remoteIndex{
91 fetcher: *f,
92 ctx: ctx,
93 manifest: b,
94 mediaType: types.OCIImageIndex,
95 descriptor: &v1.Descriptor{
96 Digest: h,
97 MediaType: types.OCIImageIndex,
98 Size: sz,
99 },
100 }
101 return filterReferrersResponse(filter, idx), nil
102 }
103
104
105
106 func filterReferrersResponse(filter map[string]string, in v1.ImageIndex) v1.ImageIndex {
107 if filter == nil {
108 return in
109 }
110 v, ok := filter["artifactType"]
111 if !ok {
112 return in
113 }
114 return mutate.RemoveManifests(in, func(desc v1.Descriptor) bool {
115 return desc.ArtifactType != v
116 })
117 }
118
View as plain text