1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package remote
16
17 import (
18 "context"
19 "io"
20 "log"
21 "net/http"
22 "net/http/httptest"
23 "net/url"
24 "os"
25 "strings"
26 "testing"
27
28 "github.com/google/go-containerregistry/pkg/authn"
29 "github.com/google/go-containerregistry/pkg/logs"
30 "github.com/google/go-containerregistry/pkg/name"
31 "github.com/google/go-containerregistry/pkg/registry"
32 v1 "github.com/google/go-containerregistry/pkg/v1"
33 "github.com/google/go-containerregistry/pkg/v1/empty"
34 "github.com/google/go-containerregistry/pkg/v1/mutate"
35 "github.com/google/go-containerregistry/pkg/v1/random"
36 "github.com/google/go-containerregistry/pkg/v1/remote/transport"
37 "github.com/google/go-containerregistry/pkg/v1/stream"
38 "github.com/google/go-containerregistry/pkg/v1/types"
39 "github.com/google/go-containerregistry/pkg/v1/validate"
40 )
41
42 func streamable(t *testing.T) v1.Layer {
43 t.Helper()
44 rl, err := random.Layer(1024, types.OCIUncompressedLayer)
45 if err != nil {
46 t.Fatal("random.Layer:", err)
47 }
48 rc, err := rl.Uncompressed()
49 if err != nil {
50 t.Fatalf("Uncompressed(): %v", err)
51 }
52
53 return stream.NewLayer(rc)
54 }
55
56 type rawManifest struct {
57 b []byte
58 }
59
60 func (r *rawManifest) RawManifest() ([]byte, error) {
61 return r.b, nil
62 }
63
64 func TestMultiWrite(t *testing.T) {
65 c := make(chan v1.Update, 1000)
66
67 logs.Progress.SetOutput(os.Stderr)
68 logs.Warn.SetOutput(os.Stderr)
69
70
71 img1, err := random.Image(1024, 2)
72 if err != nil {
73 t.Fatal("random.Image:", err)
74 }
75
76
77 rl, err := random.Layer(1024, types.OCIUncompressedLayer)
78 if err != nil {
79 t.Fatal("random.Layer:", err)
80 }
81 img2, err := mutate.AppendLayers(img1, rl, streamable(t))
82 if err != nil {
83 t.Fatal("mutate.AppendLayers:", err)
84 }
85
86
87 subidx, err := random.Index(1024, 2, 3)
88 if err != nil {
89 t.Fatal("random.Index:", err)
90 }
91
92
93 subsubidx, err := random.Index(1024, 3, 4)
94 if err != nil {
95 t.Fatal("random.Index:", err)
96 }
97 subidx = mutate.AppendManifests(subidx, mutate.IndexAddendum{Add: subsubidx})
98
99
100 idx := mutate.AppendManifests(empty.Index,
101 mutate.IndexAddendum{Add: img1},
102 mutate.IndexAddendum{Add: img2},
103 mutate.IndexAddendum{Add: subidx},
104 mutate.IndexAddendum{Add: rl},
105 )
106
107
108 nopLog := log.New(io.Discard, "", 0)
109 s := httptest.NewServer(registry.New(registry.Logger(nopLog)))
110 defer s.Close()
111 u, err := url.Parse(s.URL)
112 if err != nil {
113 t.Fatal(err)
114 }
115
116
117 tag1, tag2, tag3, tag4, tag5 := mustNewTag(t, u.Host+"/repo2:tag1"), mustNewTag(t, u.Host+"/repo:tag2"), mustNewTag(t, u.Host+"/repo:tag3"), mustNewTag(t, u.Host+"/repo:tag4"), mustNewTag(t, u.Host+"/repo1:tag4")
118
119 if err := MultiWrite(map[name.Reference]Taggable{
120 tag1: img1,
121 tag2: img2,
122 tag3: idx,
123 }, WithProgress(c)); err != nil {
124 t.Fatal("MultiWrite:", err)
125 }
126
127
128 for _, tag := range []name.Tag{tag1, tag2} {
129 got, err := Image(tag)
130 if err != nil {
131 t.Error(err)
132 continue
133 }
134 if err := validate.Image(got); err != nil {
135 t.Error("Validate() =", err)
136 }
137 }
138
139
140 got, err := Index(tag3)
141 if err != nil {
142 t.Fatal(err)
143 }
144 if err := validate.Index(got); err != nil {
145 t.Error("Validate() =", err)
146 }
147
148 if err := checkUpdates(c); err != nil {
149 t.Fatal(err)
150 }
151
152 desc1, err := Get(tag1)
153 if err != nil {
154 t.Fatal(err)
155 }
156 desc2, err := Get(tag3)
157 if err != nil {
158 t.Fatal(err)
159 }
160
161 rm := &rawManifest{[]byte("{}")}
162
163
164 if err := MultiWrite(map[name.Reference]Taggable{
165 tag1: img2,
166 tag2: img1,
167 tag3: desc2,
168 tag4: desc1,
169 tag5: rm,
170 }); err != nil {
171 t.Fatal("MultiWrite:", err)
172 }
173 }
174
175 func TestMultiWriteWithNondistributableLayer(t *testing.T) {
176
177 img1, err := random.Image(1024, 2)
178 if err != nil {
179 t.Fatal("random.Image:", err)
180 }
181
182
183 rl, err := random.Layer(1024, types.OCIRestrictedLayer)
184 if err != nil {
185 t.Fatal("random.Layer:", err)
186 }
187 img, err := mutate.AppendLayers(img1, rl)
188 if err != nil {
189 t.Fatal("mutate.AppendLayers:", err)
190 }
191
192
193 s := httptest.NewServer(registry.New())
194 defer s.Close()
195 u, err := url.Parse(s.URL)
196 if err != nil {
197 t.Fatal(err)
198 }
199
200
201 tag1 := mustNewTag(t, u.Host+"/repo:tag1")
202 if err := MultiWrite(map[name.Reference]Taggable{tag1: img}, WithNondistributable); err != nil {
203 t.Error("Write:", err)
204 }
205
206
207 got, err := Image(tag1)
208 if err != nil {
209 t.Error(err)
210 }
211 if err := validate.Image(got); err != nil {
212 t.Error("Validate() =", err)
213 }
214 }
215
216 func TestMultiWrite_Retry(t *testing.T) {
217
218 img1, err := random.Image(1024, 2)
219 if err != nil {
220 t.Fatal("random.Image:", err)
221 }
222
223 t.Run("retry http error 500", func(t *testing.T) {
224
225 handler := registry.New()
226
227 numOfInternalServerErrors := 0
228 registryThatFailsOnFirstUpload := http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
229 if strings.Contains(request.URL.Path, "/manifests/") && numOfInternalServerErrors < 1 {
230 numOfInternalServerErrors++
231 responseWriter.WriteHeader(500)
232 return
233 }
234 handler.ServeHTTP(responseWriter, request)
235 })
236
237 s := httptest.NewServer(registryThatFailsOnFirstUpload)
238 defer s.Close()
239 u, err := url.Parse(s.URL)
240 if err != nil {
241 t.Fatal(err)
242 }
243
244 tag1 := mustNewTag(t, u.Host+"/repo:tag1")
245 if err := MultiWrite(map[name.Reference]Taggable{
246 tag1: img1,
247 }, WithRetryBackoff(fastBackoff)); err != nil {
248 t.Error("Write:", err)
249 }
250 })
251
252 t.Run("do not retry transport errors if transport.Wrapper is used", func(t *testing.T) {
253
254 onlyHandlesPing := http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
255 if strings.HasSuffix(request.URL.Path, "/v2/") {
256 responseWriter.WriteHeader(200)
257 return
258 }
259 })
260 s := httptest.NewServer(onlyHandlesPing)
261 defer s.Close()
262
263 u, err := url.Parse(s.URL)
264 if err != nil {
265 t.Fatal(err)
266 }
267
268 tag1 := mustNewTag(t, u.Host+"/repo:tag1")
269
270
271 doesNotRetryTransport := &countTransport{inner: http.DefaultTransport}
272 transportWrapper, err := transport.NewWithContext(context.Background(), tag1.Repository.Registry, authn.Anonymous, doesNotRetryTransport, nil)
273 if err != nil {
274 t.Fatal(err)
275 }
276
277 noRetry := func(error) bool { return false }
278
279 if err := MultiWrite(map[name.Reference]Taggable{
280 tag1: img1,
281 }, WithTransport(transportWrapper), WithJobs(1), WithRetryPredicate(noRetry)); err == nil {
282 t.Errorf("Expected an error, got nil")
283 }
284
285
286 if doesNotRetryTransport.count != 1 {
287 t.Errorf("Incorrect count, got %d, want %d", doesNotRetryTransport.count, 1)
288 }
289 })
290
291 t.Run("do not add UserAgent if transport.Wrapper is used", func(t *testing.T) {
292 expectedNotUsedUserAgent := "TEST_USER_AGENT"
293
294 handler := registry.New()
295
296 registryThatAssertsUserAgentIsCorrect := http.HandlerFunc(func(responseWriter http.ResponseWriter, request *http.Request) {
297 if strings.Contains(request.Header.Get("User-Agent"), expectedNotUsedUserAgent) {
298 t.Fatalf("Should not contain User-Agent: %s, Got: %s", expectedNotUsedUserAgent, request.Header.Get("User-Agent"))
299 }
300
301 handler.ServeHTTP(responseWriter, request)
302 })
303
304 s := httptest.NewServer(registryThatAssertsUserAgentIsCorrect)
305
306 defer s.Close()
307 u, err := url.Parse(s.URL)
308 if err != nil {
309 t.Fatal(err)
310 }
311
312 tag1 := mustNewTag(t, u.Host+"/repo:tag1")
313
314 transportWrapper, err := transport.NewWithContext(context.Background(), tag1.Repository.Registry, authn.Anonymous, http.DefaultTransport, nil)
315 if err != nil {
316 t.Fatal(err)
317 }
318
319 if err := MultiWrite(map[name.Reference]Taggable{
320 tag1: img1,
321 }, WithTransport(transportWrapper), WithUserAgent(expectedNotUsedUserAgent)); err != nil {
322 t.Fatal(err)
323 }
324 })
325 }
326
327
328
329 func TestMultiWrite_Deep(t *testing.T) {
330 idx, err := random.Index(1024, 2, 2)
331 if err != nil {
332 t.Fatal("random.Image:", err)
333 }
334 for i := 0; i < 4; i++ {
335 idx = mutate.AppendManifests(idx, mutate.IndexAddendum{Add: idx})
336 }
337
338
339 nopLog := log.New(io.Discard, "", 0)
340 s := httptest.NewServer(registry.New(registry.Logger(nopLog)))
341 defer s.Close()
342 u, err := url.Parse(s.URL)
343 if err != nil {
344 t.Fatal(err)
345 }
346
347
348 tag := mustNewTag(t, u.Host+"/repo:tag")
349 if err := MultiWrite(map[name.Reference]Taggable{
350 tag: idx,
351 }); err != nil {
352 t.Error("Write:", err)
353 }
354
355
356 got, err := Index(tag)
357 if err != nil {
358 t.Fatal(err)
359 }
360 if err := validate.Index(got); err != nil {
361 t.Error("Validate() =", err)
362 }
363 }
364
365 type countTransport struct {
366 count int
367 inner http.RoundTripper
368 }
369
370 func (t *countTransport) RoundTrip(req *http.Request) (*http.Response, error) {
371 if strings.HasSuffix(req.URL.Path, "/v2/") {
372 return t.inner.RoundTrip(req)
373 }
374
375 t.count++
376 return nil, io.ErrUnexpectedEOF
377 }
378
View as plain text