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 "io"
21 "net/http"
22 "net/url"
23 "sync"
24
25 "github.com/google/go-containerregistry/internal/redact"
26 "github.com/google/go-containerregistry/internal/verify"
27 "github.com/google/go-containerregistry/pkg/name"
28 v1 "github.com/google/go-containerregistry/pkg/v1"
29 "github.com/google/go-containerregistry/pkg/v1/partial"
30 "github.com/google/go-containerregistry/pkg/v1/remote/transport"
31 "github.com/google/go-containerregistry/pkg/v1/types"
32 )
33
34 var acceptableImageMediaTypes = []types.MediaType{
35 types.DockerManifestSchema2,
36 types.OCIManifestSchema1,
37 }
38
39
40 type remoteImage struct {
41 fetcher fetcher
42 ref name.Reference
43 ctx context.Context
44 manifestLock sync.Mutex
45 manifest []byte
46 configLock sync.Mutex
47 config []byte
48 mediaType types.MediaType
49 descriptor *v1.Descriptor
50 }
51
52 func (r *remoteImage) ArtifactType() (string, error) {
53
54
55 if _, err := r.RawManifest(); err != nil {
56 return "", err
57 }
58 return r.descriptor.ArtifactType, nil
59 }
60
61 var _ partial.CompressedImageCore = (*remoteImage)(nil)
62
63
64 func Image(ref name.Reference, options ...Option) (v1.Image, error) {
65 desc, err := Get(ref, options...)
66 if err != nil {
67 return nil, err
68 }
69
70 return desc.Image()
71 }
72
73 func (r *remoteImage) MediaType() (types.MediaType, error) {
74 if string(r.mediaType) != "" {
75 return r.mediaType, nil
76 }
77 return types.DockerManifestSchema2, nil
78 }
79
80 func (r *remoteImage) RawManifest() ([]byte, error) {
81 r.manifestLock.Lock()
82 defer r.manifestLock.Unlock()
83 if r.manifest != nil {
84 return r.manifest, nil
85 }
86
87
88
89
90 manifest, desc, err := r.fetcher.fetchManifest(r.ctx, r.ref, acceptableImageMediaTypes)
91 if err != nil {
92 return nil, err
93 }
94
95 if r.descriptor == nil {
96 r.descriptor = desc
97 }
98 r.mediaType = desc.MediaType
99 r.manifest = manifest
100 return r.manifest, nil
101 }
102
103 func (r *remoteImage) RawConfigFile() ([]byte, error) {
104 r.configLock.Lock()
105 defer r.configLock.Unlock()
106 if r.config != nil {
107 return r.config, nil
108 }
109
110 m, err := partial.Manifest(r)
111 if err != nil {
112 return nil, err
113 }
114
115 if m.Config.Data != nil {
116 if err := verify.Descriptor(m.Config); err != nil {
117 return nil, err
118 }
119 r.config = m.Config.Data
120 return r.config, nil
121 }
122
123 body, err := r.fetcher.fetchBlob(r.ctx, m.Config.Size, m.Config.Digest)
124 if err != nil {
125 return nil, err
126 }
127 defer body.Close()
128
129 r.config, err = io.ReadAll(body)
130 if err != nil {
131 return nil, err
132 }
133 return r.config, nil
134 }
135
136
137
138 func (r *remoteImage) Descriptor() (*v1.Descriptor, error) {
139
140
141 _, err := r.RawManifest()
142 return r.descriptor, err
143 }
144
145 func (r *remoteImage) ConfigLayer() (v1.Layer, error) {
146 if _, err := r.RawManifest(); err != nil {
147 return nil, err
148 }
149 m, err := partial.Manifest(r)
150 if err != nil {
151 return nil, err
152 }
153
154 return partial.CompressedToLayer(&remoteImageLayer{
155 ri: r,
156 ctx: r.ctx,
157 digest: m.Config.Digest,
158 })
159 }
160
161
162 type remoteImageLayer struct {
163 ri *remoteImage
164 ctx context.Context
165 digest v1.Hash
166 }
167
168
169 func (rl *remoteImageLayer) Digest() (v1.Hash, error) {
170 return rl.digest, nil
171 }
172
173
174 func (rl *remoteImageLayer) Compressed() (io.ReadCloser, error) {
175 urls := []url.URL{rl.ri.fetcher.url("blobs", rl.digest.String())}
176
177
178 d, err := partial.BlobDescriptor(rl, rl.digest)
179 if err != nil {
180 return nil, err
181 }
182
183 if d.Data != nil {
184 return verify.ReadCloser(io.NopCloser(bytes.NewReader(d.Data)), d.Size, d.Digest)
185 }
186
187
188 ctx := redact.NewContext(rl.ctx, "omitting binary blobs from logs")
189
190 for _, s := range d.URLs {
191 u, err := url.Parse(s)
192 if err != nil {
193 return nil, err
194 }
195 urls = append(urls, *u)
196 }
197
198
199
200
201
202 var lastErr error
203 for _, u := range urls {
204 req, err := http.NewRequest(http.MethodGet, u.String(), nil)
205 if err != nil {
206 return nil, err
207 }
208
209 resp, err := rl.ri.fetcher.Do(req.WithContext(ctx))
210 if err != nil {
211 lastErr = err
212 continue
213 }
214
215 if err := transport.CheckError(resp, http.StatusOK); err != nil {
216 resp.Body.Close()
217 lastErr = err
218 continue
219 }
220
221 return verify.ReadCloser(resp.Body, d.Size, rl.digest)
222 }
223
224 return nil, lastErr
225 }
226
227
228 func (rl *remoteImageLayer) Manifest() (*v1.Manifest, error) {
229 return partial.Manifest(rl.ri)
230 }
231
232
233 func (rl *remoteImageLayer) MediaType() (types.MediaType, error) {
234 bd, err := partial.BlobDescriptor(rl, rl.digest)
235 if err != nil {
236 return "", err
237 }
238
239 return bd.MediaType, nil
240 }
241
242
243 func (rl *remoteImageLayer) Size() (int64, error) {
244
245 return partial.BlobSize(rl, rl.digest)
246 }
247
248
249 func (rl *remoteImageLayer) ConfigFile() (*v1.ConfigFile, error) {
250 return partial.ConfigFile(rl.ri)
251 }
252
253
254
255 func (rl *remoteImageLayer) DiffID() (v1.Hash, error) {
256 return partial.BlobToDiffID(rl, rl.digest)
257 }
258
259
260
261 func (rl *remoteImageLayer) Descriptor() (*v1.Descriptor, error) {
262 return partial.BlobDescriptor(rl, rl.digest)
263 }
264
265
266 func (rl *remoteImageLayer) Exists() (bool, error) {
267 return rl.ri.fetcher.blobExists(rl.ri.ctx, rl.digest)
268 }
269
270
271 func (r *remoteImage) LayerByDigest(h v1.Hash) (partial.CompressedLayer, error) {
272 return &remoteImageLayer{
273 ri: r,
274 ctx: r.ctx,
275 digest: h,
276 }, nil
277 }
278
View as plain text