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 "fmt"
21 "sync"
22
23 "github.com/google/go-containerregistry/internal/verify"
24 "github.com/google/go-containerregistry/pkg/name"
25 v1 "github.com/google/go-containerregistry/pkg/v1"
26 "github.com/google/go-containerregistry/pkg/v1/partial"
27 "github.com/google/go-containerregistry/pkg/v1/types"
28 )
29
30 var acceptableIndexMediaTypes = []types.MediaType{
31 types.DockerManifestList,
32 types.OCIImageIndex,
33 }
34
35
36 type remoteIndex struct {
37 fetcher fetcher
38 ref name.Reference
39 ctx context.Context
40 manifestLock sync.Mutex
41 manifest []byte
42 mediaType types.MediaType
43 descriptor *v1.Descriptor
44 }
45
46
47 func Index(ref name.Reference, options ...Option) (v1.ImageIndex, error) {
48 desc, err := get(ref, acceptableIndexMediaTypes, options...)
49 if err != nil {
50 return nil, err
51 }
52
53 return desc.ImageIndex()
54 }
55
56 func (r *remoteIndex) MediaType() (types.MediaType, error) {
57 if string(r.mediaType) != "" {
58 return r.mediaType, nil
59 }
60 return types.DockerManifestList, nil
61 }
62
63 func (r *remoteIndex) Digest() (v1.Hash, error) {
64 return partial.Digest(r)
65 }
66
67 func (r *remoteIndex) Size() (int64, error) {
68 return partial.Size(r)
69 }
70
71 func (r *remoteIndex) RawManifest() ([]byte, error) {
72 r.manifestLock.Lock()
73 defer r.manifestLock.Unlock()
74 if r.manifest != nil {
75 return r.manifest, nil
76 }
77
78
79
80
81 manifest, desc, err := r.fetcher.fetchManifest(r.ctx, r.ref, acceptableIndexMediaTypes)
82 if err != nil {
83 return nil, err
84 }
85
86 if r.descriptor == nil {
87 r.descriptor = desc
88 }
89 r.mediaType = desc.MediaType
90 r.manifest = manifest
91 return r.manifest, nil
92 }
93
94 func (r *remoteIndex) IndexManifest() (*v1.IndexManifest, error) {
95 b, err := r.RawManifest()
96 if err != nil {
97 return nil, err
98 }
99 return v1.ParseIndexManifest(bytes.NewReader(b))
100 }
101
102 func (r *remoteIndex) Image(h v1.Hash) (v1.Image, error) {
103 desc, err := r.childByHash(h)
104 if err != nil {
105 return nil, err
106 }
107
108
109 return desc.Image()
110 }
111
112
113
114 func (r *remoteIndex) Descriptor() (*v1.Descriptor, error) {
115
116
117 _, err := r.RawManifest()
118 return r.descriptor, err
119 }
120
121 func (r *remoteIndex) ImageIndex(h v1.Hash) (v1.ImageIndex, error) {
122 desc, err := r.childByHash(h)
123 if err != nil {
124 return nil, err
125 }
126 return desc.ImageIndex()
127 }
128
129
130 func (r *remoteIndex) Layer(h v1.Hash) (v1.Layer, error) {
131 index, err := r.IndexManifest()
132 if err != nil {
133 return nil, err
134 }
135 for _, childDesc := range index.Manifests {
136 if h == childDesc.Digest {
137 l, err := partial.CompressedToLayer(&remoteLayer{
138 fetcher: r.fetcher,
139 ctx: r.ctx,
140 digest: h,
141 })
142 if err != nil {
143 return nil, err
144 }
145 return &MountableLayer{
146 Layer: l,
147 Reference: r.ref.Context().Digest(h.String()),
148 }, nil
149 }
150 }
151 return nil, fmt.Errorf("layer not found: %s", h)
152 }
153
154 func (r *remoteIndex) imageByPlatform(platform v1.Platform) (v1.Image, error) {
155 desc, err := r.childByPlatform(platform)
156 if err != nil {
157 return nil, err
158 }
159
160
161 return desc.Image()
162 }
163
164
165
166
167
168
169
170
171
172
173 func (r *remoteIndex) childByPlatform(platform v1.Platform) (*Descriptor, error) {
174 index, err := r.IndexManifest()
175 if err != nil {
176 return nil, err
177 }
178 for _, childDesc := range index.Manifests {
179
180 p := defaultPlatform
181 if childDesc.Platform != nil {
182 p = *childDesc.Platform
183 }
184
185 if matchesPlatform(p, platform) {
186 return r.childDescriptor(childDesc, platform)
187 }
188 }
189 return nil, fmt.Errorf("no child with platform %+v in index %s", platform, r.ref)
190 }
191
192 func (r *remoteIndex) childByHash(h v1.Hash) (*Descriptor, error) {
193 index, err := r.IndexManifest()
194 if err != nil {
195 return nil, err
196 }
197 for _, childDesc := range index.Manifests {
198 if h == childDesc.Digest {
199 return r.childDescriptor(childDesc, defaultPlatform)
200 }
201 }
202 return nil, fmt.Errorf("no child with digest %s in index %s", h, r.ref)
203 }
204
205
206 func (r *remoteIndex) childDescriptor(child v1.Descriptor, platform v1.Platform) (*Descriptor, error) {
207 ref := r.ref.Context().Digest(child.Digest.String())
208 var (
209 manifest []byte
210 err error
211 )
212 if child.Data != nil {
213 if err := verify.Descriptor(child); err != nil {
214 return nil, err
215 }
216 manifest = child.Data
217 } else {
218 manifest, _, err = r.fetcher.fetchManifest(r.ctx, ref, []types.MediaType{child.MediaType})
219 if err != nil {
220 return nil, err
221 }
222 }
223
224 if child.MediaType.IsImage() {
225 mf, _ := v1.ParseManifest(bytes.NewReader(manifest))
226
227
228 if mf != nil && !mf.Config.MediaType.IsConfig() {
229 child.ArtifactType = string(mf.Config.MediaType)
230 }
231 }
232
233 return &Descriptor{
234 ref: ref,
235 ctx: r.ctx,
236 fetcher: r.fetcher,
237 Manifest: manifest,
238 Descriptor: child,
239 platform: platform,
240 }, nil
241 }
242
243
244
245
246
247
248 func matchesPlatform(given, required v1.Platform) bool {
249
250 if given.Architecture != required.Architecture || given.OS != required.OS {
251 return false
252 }
253
254
255 if required.OSVersion != "" && given.OSVersion != required.OSVersion {
256 return false
257 }
258 if required.Variant != "" && given.Variant != required.Variant {
259 return false
260 }
261
262
263 if !isSubset(given.OSFeatures, required.OSFeatures) {
264 return false
265 }
266 if !isSubset(given.Features, required.Features) {
267 return false
268 }
269
270 return true
271 }
272
273
274 func isSubset(lst, required []string) bool {
275 set := make(map[string]bool)
276 for _, value := range lst {
277 set[value] = true
278 }
279
280 for _, value := range required {
281 if _, ok := set[value]; !ok {
282 return false
283 }
284 }
285
286 return true
287 }
288
View as plain text