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 "encoding/json"
21 "fmt"
22 "io"
23 "net/http"
24 "net/http/httptest"
25 "net/url"
26 "path"
27 "strings"
28 "testing"
29
30 "github.com/google/go-cmp/cmp"
31 "github.com/google/go-containerregistry/pkg/authn"
32 "github.com/google/go-containerregistry/pkg/logs"
33 "github.com/google/go-containerregistry/pkg/name"
34 "github.com/google/go-containerregistry/pkg/registry"
35 v1 "github.com/google/go-containerregistry/pkg/v1"
36 "github.com/google/go-containerregistry/pkg/v1/mutate"
37 "github.com/google/go-containerregistry/pkg/v1/partial"
38 "github.com/google/go-containerregistry/pkg/v1/random"
39 "github.com/google/go-containerregistry/pkg/v1/types"
40 "github.com/google/go-containerregistry/pkg/v1/validate"
41 )
42
43 const bogusDigest = "sha256:deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
44
45 type withDigest interface {
46 Digest() (v1.Hash, error)
47 }
48
49 func mustDigest(t *testing.T, img withDigest) v1.Hash {
50 h, err := img.Digest()
51 if err != nil {
52 t.Fatalf("Digest() = %v", err)
53 }
54 return h
55 }
56
57 func mustManifest(t *testing.T, img v1.Image) *v1.Manifest {
58 m, err := img.Manifest()
59 if err != nil {
60 t.Fatalf("Manifest() = %v", err)
61 }
62 return m
63 }
64
65 func mustRawManifest(t *testing.T, img Taggable) []byte {
66 m, err := img.RawManifest()
67 if err != nil {
68 t.Fatalf("RawManifest() = %v", err)
69 }
70 return m
71 }
72
73 func mustRawConfigFile(t *testing.T, img v1.Image) []byte {
74 c, err := img.RawConfigFile()
75 if err != nil {
76 t.Fatalf("RawConfigFile() = %v", err)
77 }
78 return c
79 }
80
81 func randomImage(t *testing.T) v1.Image {
82 rnd, err := random.Image(1024, 1)
83 if err != nil {
84 t.Fatalf("random.Image() = %v", err)
85 }
86 return rnd
87 }
88
89 func newReference(host, repo, ref string) (name.Reference, error) {
90 tag, err := name.NewTag(fmt.Sprintf("%s/%s:%s", host, repo, ref), name.WeakValidation)
91 if err == nil {
92 return tag, nil
93 }
94 return name.NewDigest(fmt.Sprintf("%s/%s@%s", host, repo, ref), name.WeakValidation)
95 }
96
97
98 func TestMediaType(t *testing.T) {
99 img := remoteImage{}
100 got, err := img.MediaType()
101 if err != nil {
102 t.Fatalf("MediaType() = %v", err)
103 }
104 want := types.DockerManifestSchema2
105 if got != want {
106 t.Errorf("MediaType() = %v, want %v", got, want)
107 }
108 }
109
110 func TestRawManifestDigests(t *testing.T) {
111 img := randomImage(t)
112 expectedRepo := "foo/bar"
113
114 cases := []struct {
115 name string
116 ref string
117 responseBody []byte
118 contentDigest string
119 wantErr bool
120 }{{
121 name: "normal pull, by tag",
122 ref: "latest",
123 responseBody: mustRawManifest(t, img),
124 contentDigest: mustDigest(t, img).String(),
125 wantErr: false,
126 }, {
127 name: "normal pull, by digest",
128 ref: mustDigest(t, img).String(),
129 responseBody: mustRawManifest(t, img),
130 contentDigest: mustDigest(t, img).String(),
131 wantErr: false,
132 }, {
133 name: "right content-digest, wrong body, by digest",
134 ref: mustDigest(t, img).String(),
135 responseBody: []byte("not even json"),
136 contentDigest: mustDigest(t, img).String(),
137 wantErr: true,
138 }, {
139 name: "right body, wrong content-digest, by tag",
140 ref: "latest",
141 responseBody: mustRawManifest(t, img),
142 contentDigest: bogusDigest,
143 wantErr: false,
144 }, {
145
146 name: "right body, wrong content-digest, by digest",
147 ref: mustDigest(t, img).String(),
148 responseBody: mustRawManifest(t, img),
149 contentDigest: bogusDigest,
150 wantErr: false,
151 }}
152
153 for _, tc := range cases {
154 t.Run(tc.name, func(t *testing.T) {
155 manifestPath := fmt.Sprintf("/v2/%s/manifests/%s", expectedRepo, tc.ref)
156 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
157 switch r.URL.Path {
158 case manifestPath:
159 if r.Method != http.MethodGet {
160 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
161 }
162
163 w.Header().Set("Docker-Content-Digest", tc.contentDigest)
164 w.Write(tc.responseBody)
165 default:
166 t.Fatalf("Unexpected path: %v", r.URL.Path)
167 }
168 }))
169 defer server.Close()
170 u, err := url.Parse(server.URL)
171 if err != nil {
172 t.Fatalf("url.Parse(%v) = %v", server.URL, err)
173 }
174
175 ref, err := newReference(u.Host, expectedRepo, tc.ref)
176 if err != nil {
177 t.Fatalf("url.Parse(%v, %v, %v) = %v", u.Host, expectedRepo, tc.ref, err)
178 }
179
180 rmt := remoteImage{
181 ref: ref,
182 ctx: context.Background(),
183 fetcher: fetcher{
184 target: ref.Context(),
185 client: http.DefaultClient,
186 },
187 }
188
189 if _, err := rmt.RawManifest(); (err != nil) != tc.wantErr {
190 t.Errorf("RawManifest() wrong error: %v, want %v: %v\n", (err != nil), tc.wantErr, err)
191 }
192 })
193 }
194 }
195
196 func TestRawManifestNotFound(t *testing.T) {
197 expectedRepo := "foo/bar"
198 manifestPath := fmt.Sprintf("/v2/%s/manifests/latest", expectedRepo)
199 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
200 switch r.URL.Path {
201 case manifestPath:
202 if r.Method != http.MethodGet {
203 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
204 }
205 w.WriteHeader(http.StatusNotFound)
206 default:
207 t.Fatalf("Unexpected path: %v", r.URL.Path)
208 }
209 }))
210 defer server.Close()
211 u, err := url.Parse(server.URL)
212 if err != nil {
213 t.Fatalf("url.Parse(%v) = %v", server.URL, err)
214 }
215
216 ref := mustNewTag(t, fmt.Sprintf("%s/%s:latest", u.Host, expectedRepo))
217 img := remoteImage{
218 ref: ref,
219 ctx: context.Background(),
220 fetcher: fetcher{
221 target: ref.Context(),
222 client: http.DefaultClient,
223 },
224 }
225
226 if _, err := img.RawManifest(); err == nil {
227 t.Error("RawManifest() = nil; wanted error")
228 }
229 }
230
231 func TestRawConfigFileNotFound(t *testing.T) {
232 img := randomImage(t)
233 expectedRepo := "foo/bar"
234 manifestPath := fmt.Sprintf("/v2/%s/manifests/latest", expectedRepo)
235 configPath := fmt.Sprintf("/v2/%s/blobs/%s", expectedRepo, mustConfigName(t, img))
236 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
237 switch r.URL.Path {
238 case configPath:
239 if r.Method != http.MethodGet {
240 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
241 }
242 w.WriteHeader(http.StatusNotFound)
243 case manifestPath:
244 if r.Method != http.MethodGet {
245 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
246 }
247 w.Write(mustRawManifest(t, img))
248 default:
249 t.Fatalf("Unexpected path: %v", r.URL.Path)
250 }
251 }))
252 defer server.Close()
253 u, err := url.Parse(server.URL)
254 if err != nil {
255 t.Fatalf("url.Parse(%v) = %v", server.URL, err)
256 }
257
258 ref := mustNewTag(t, fmt.Sprintf("%s/%s:latest", u.Host, expectedRepo))
259 rmt := remoteImage{
260 ref: ref,
261 ctx: context.Background(),
262 fetcher: fetcher{
263 target: ref.Context(),
264 client: http.DefaultClient,
265 },
266 }
267
268 if _, err := rmt.RawConfigFile(); err == nil {
269 t.Error("RawConfigFile() = nil; wanted error")
270 }
271 }
272
273 func TestAcceptHeaders(t *testing.T) {
274 img := randomImage(t)
275 expectedRepo := "foo/bar"
276 manifestPath := fmt.Sprintf("/v2/%s/manifests/latest", expectedRepo)
277 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
278 switch r.URL.Path {
279 case manifestPath:
280 if r.Method != http.MethodGet {
281 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
282 }
283 wantAccept := strings.Join([]string{
284 string(types.DockerManifestSchema2),
285 string(types.OCIManifestSchema1),
286 }, ",")
287 if got, want := r.Header.Get("Accept"), wantAccept; got != want {
288 t.Errorf("Accept header; got %v, want %v", got, want)
289 }
290 w.Write(mustRawManifest(t, img))
291 default:
292 t.Fatalf("Unexpected path: %v", r.URL.Path)
293 }
294 }))
295 defer server.Close()
296 u, err := url.Parse(server.URL)
297 if err != nil {
298 t.Fatalf("url.Parse(%v) = %v", server.URL, err)
299 }
300 ref := mustNewTag(t, fmt.Sprintf("%s/%s:latest", u.Host, expectedRepo))
301 rmt := &remoteImage{
302 ref: ref,
303 ctx: context.Background(),
304 fetcher: fetcher{
305 target: ref.Context(),
306 client: http.DefaultClient,
307 },
308 }
309 manifest, err := rmt.RawManifest()
310 if err != nil {
311 t.Errorf("RawManifest() = %v", err)
312 }
313 if got, want := manifest, mustRawManifest(t, img); !bytes.Equal(got, want) {
314 t.Errorf("RawManifest() = %v, want %v", got, want)
315 }
316 }
317
318 func TestImage(t *testing.T) {
319 img := randomImage(t)
320 expectedRepo := "foo/bar"
321 layerDigest := mustManifest(t, img).Layers[0].Digest
322 layerSize := mustManifest(t, img).Layers[0].Size
323 configPath := fmt.Sprintf("/v2/%s/blobs/%s", expectedRepo, mustConfigName(t, img))
324 manifestPath := fmt.Sprintf("/v2/%s/manifests/latest", expectedRepo)
325 layerPath := fmt.Sprintf("/v2/%s/blobs/%s", expectedRepo, layerDigest)
326 manifestReqCount := 0
327
328 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
329 switch r.URL.Path {
330 case "/v2/":
331 w.WriteHeader(http.StatusOK)
332 case configPath:
333 if r.Method != http.MethodGet {
334 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
335 }
336 w.Write(mustRawConfigFile(t, img))
337 case manifestPath:
338 manifestReqCount++
339 if r.Method != http.MethodGet {
340 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
341 }
342 w.Write(mustRawManifest(t, img))
343 case layerPath:
344 t.Fatalf("BlobSize should not make any request: %v", r.URL.Path)
345 default:
346 t.Fatalf("Unexpected path: %v", r.URL.Path)
347 }
348 }))
349 defer server.Close()
350 u, err := url.Parse(server.URL)
351 if err != nil {
352 t.Fatalf("url.Parse(%v) = %v", server.URL, err)
353 }
354
355 tag := mustNewTag(t, fmt.Sprintf("%s/%s:latest", u.Host, expectedRepo))
356 rmt, err := Image(tag, WithTransport(http.DefaultTransport), WithAuthFromKeychain(authn.DefaultKeychain))
357 if err != nil {
358 t.Errorf("Image() = %v", err)
359 }
360
361 if got, want := mustRawManifest(t, rmt), mustRawManifest(t, img); !bytes.Equal(got, want) {
362 t.Errorf("RawManifest() = %v, want %v", got, want)
363 }
364 if got, want := mustRawConfigFile(t, rmt), mustRawConfigFile(t, img); !bytes.Equal(got, want) {
365 t.Errorf("RawConfigFile() = %v, want %v", got, want)
366 }
367
368 if manifestReqCount != 1 {
369 t.Errorf("RawManifest made %v requests, expected 1", manifestReqCount)
370 }
371
372 l, err := rmt.LayerByDigest(layerDigest)
373 if err != nil {
374 t.Errorf("LayerByDigest() = %v", err)
375 }
376
377 size, err := l.Size()
378 if err != nil {
379 t.Errorf("BlobSize() = %v", err)
380 }
381 if got, want := size, layerSize; want != got {
382 t.Errorf("BlobSize() = %v want %v", got, want)
383 }
384 }
385
386 func TestPullingManifestList(t *testing.T) {
387 idx := randomIndex(t)
388 expectedRepo := "foo/bar"
389 manifestPath := fmt.Sprintf("/v2/%s/manifests/latest", expectedRepo)
390 childDigest := mustIndexManifest(t, idx).Manifests[1].Digest
391 child := mustChild(t, idx, childDigest)
392 childPath := fmt.Sprintf("/v2/%s/manifests/%s", expectedRepo, childDigest)
393 fakePlatformChildDigest := mustIndexManifest(t, idx).Manifests[0].Digest
394 fakePlatformChild := mustChild(t, idx, fakePlatformChildDigest)
395 fakePlatformChildPath := fmt.Sprintf("/v2/%s/manifests/%s", expectedRepo, fakePlatformChildDigest)
396 configPath := fmt.Sprintf("/v2/%s/blobs/%s", expectedRepo, mustConfigName(t, child))
397
398 fakePlatform := v1.Platform{
399 Architecture: "not-real-arch",
400 OS: "not-real-os",
401 }
402
403
404 manifest, err := idx.IndexManifest()
405 if err != nil {
406 t.Fatal(err)
407 }
408
409 manifest.Manifests[0].Platform = &fakePlatform
410
411 manifest.Manifests[1].Platform = &defaultPlatform
412
413 manifest.Manifests[1].Data = mustRawManifest(t, child)
414 rawManifest, err := json.Marshal(manifest)
415 if err != nil {
416 t.Fatal(err)
417 }
418
419 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
420 switch r.URL.Path {
421 case "/v2/":
422 w.WriteHeader(http.StatusOK)
423 case manifestPath:
424 if r.Method != http.MethodGet {
425 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
426 }
427 w.Header().Set("Content-Type", string(mustMediaType(t, idx)))
428 w.Write(rawManifest)
429 case childPath:
430 if r.Method != http.MethodGet {
431 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
432 }
433 w.Write(mustRawManifest(t, child))
434 case configPath:
435 if r.Method != http.MethodGet {
436 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
437 }
438 w.Write(mustRawConfigFile(t, child))
439 case fakePlatformChildPath:
440 if r.Method != http.MethodGet {
441 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
442 }
443 w.Write(mustRawManifest(t, fakePlatformChild))
444 default:
445 t.Fatalf("Unexpected path: %v", r.URL.Path)
446 }
447 }))
448 defer server.Close()
449 u, err := url.Parse(server.URL)
450 if err != nil {
451 t.Fatalf("url.Parse(%v) = %v", server.URL, err)
452 }
453
454 tag := mustNewTag(t, fmt.Sprintf("%s/%s:latest", u.Host, expectedRepo))
455 rmtChild, err := Image(tag)
456 if err != nil {
457 t.Errorf("Image() = %v", err)
458 }
459
460
461 if got, want := mustRawManifest(t, rmtChild), mustRawManifest(t, child); !bytes.Equal(got, want) {
462 t.Errorf("RawManifest() = %v, want %v", string(got), string(want))
463 }
464 if got, want := mustRawConfigFile(t, rmtChild), mustRawConfigFile(t, child); !bytes.Equal(got, want) {
465 t.Errorf("RawConfigFile() = %v, want %v", got, want)
466 }
467
468
469 img, err := Image(tag, WithPlatform(fakePlatform))
470 if err != nil {
471 t.Fatal(err)
472 }
473 desc, err := partial.Descriptor(img)
474 if err != nil {
475 t.Fatal(err)
476 }
477
478 if diff := cmp.Diff(*desc.Platform, fakePlatform); diff != "" {
479 t.Errorf("Desciptor() (-want +got) = %v", diff)
480 }
481 }
482
483 func TestPullingManifestListNoMatch(t *testing.T) {
484 idx := randomIndex(t)
485 expectedRepo := "foo/bar"
486 manifestPath := fmt.Sprintf("/v2/%s/manifests/latest", expectedRepo)
487 childDigest := mustIndexManifest(t, idx).Manifests[1].Digest
488 child := mustChild(t, idx, childDigest)
489 childPath := fmt.Sprintf("/v2/%s/manifests/%s", expectedRepo, childDigest)
490 configPath := fmt.Sprintf("/v2/%s/blobs/%s", expectedRepo, mustConfigName(t, child))
491
492 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
493 switch r.URL.Path {
494 case "/v2/":
495 w.WriteHeader(http.StatusOK)
496 case manifestPath:
497 if r.Method != http.MethodGet {
498 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
499 }
500 w.Header().Set("Content-Type", string(mustMediaType(t, idx)))
501 w.Write(mustRawManifest(t, idx))
502 case childPath:
503 if r.Method != http.MethodGet {
504 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
505 }
506 w.Write(mustRawManifest(t, child))
507 case configPath:
508 if r.Method != http.MethodGet {
509 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
510 }
511 w.Write(mustRawConfigFile(t, child))
512 default:
513 t.Fatalf("Unexpected path: %v", r.URL.Path)
514 }
515 }))
516 defer server.Close()
517 u, err := url.Parse(server.URL)
518 if err != nil {
519 t.Fatalf("url.Parse(%v) = %v", server.URL, err)
520 }
521 platform := v1.Platform{
522 Architecture: "not-real-arch",
523 OS: "not-real-os",
524 }
525 tag := mustNewTag(t, fmt.Sprintf("%s/%s:latest", u.Host, expectedRepo))
526 if _, err := Image(tag, WithPlatform(platform)); err == nil {
527 t.Errorf("Image succeeded, wanted err")
528 }
529 }
530
531 func TestValidate(t *testing.T) {
532 img, err := random.Image(1024, 5)
533 if err != nil {
534 t.Fatal(err)
535 }
536
537 s := httptest.NewServer(registry.New())
538 defer s.Close()
539 u, err := url.Parse(s.URL)
540 if err != nil {
541 t.Fatal(err)
542 }
543
544 tag, err := name.NewTag(u.Host + "/foo/bar")
545 if err != nil {
546 t.Fatal(err)
547 }
548
549 if err := Write(tag, img); err != nil {
550 t.Fatal(err)
551 }
552
553 img, err = Image(tag)
554 if err != nil {
555 t.Fatal(err)
556 }
557
558 if err := validate.Image(img); err != nil {
559 t.Errorf("failed to validate remote.Image: %v", err)
560 }
561 }
562
563 func TestPullingForeignLayer(t *testing.T) {
564
565 var b bytes.Buffer
566 logs.Debug.SetOutput(&b)
567
568 img := randomImage(t)
569 expectedRepo := "foo/bar"
570 foreignPath := "/foreign/path"
571
572 foreignLayer, err := random.Layer(1024, types.DockerForeignLayer)
573 if err != nil {
574 t.Fatal(err)
575 }
576
577 foreignServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
578 switch r.URL.Path {
579 case foreignPath:
580 compressed, err := foreignLayer.Compressed()
581 if err != nil {
582 t.Fatal(err)
583 }
584 if _, err := io.Copy(w, compressed); err != nil {
585 t.Fatal(err)
586 }
587 w.WriteHeader(http.StatusOK)
588 default:
589 t.Fatalf("Unexpected path: %v", r.URL.Path)
590 }
591 }))
592 defer foreignServer.Close()
593 fu, err := url.Parse(foreignServer.URL)
594 if err != nil {
595 t.Fatalf("url.Parse(%v) = %v", foreignServer.URL, err)
596 }
597
598 img, err = mutate.Append(img, mutate.Addendum{
599 Layer: foreignLayer,
600 URLs: []string{
601 "http://" + path.Join(fu.Host, foreignPath),
602 },
603 })
604 if err != nil {
605 t.Fatal(err)
606 }
607
608
609
610 configPath := fmt.Sprintf("/v2/%s/blobs/%s", expectedRepo, mustConfigName(t, img))
611 manifestPath := fmt.Sprintf("/v2/%s/manifests/latest", expectedRepo)
612 foreignLayerDigest := mustManifest(t, img).Layers[1].Digest
613 foreignLayerPath := fmt.Sprintf("/v2/%s/blobs/%s", expectedRepo, foreignLayerDigest)
614 layerDigest := mustManifest(t, img).Layers[0].Digest
615 layerPath := fmt.Sprintf("/v2/%s/blobs/%s", expectedRepo, layerDigest)
616
617 layer, err := img.LayerByDigest(layerDigest)
618 if err != nil {
619 t.Fatal(err)
620 }
621
622 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
623 switch r.URL.Path {
624 case "/v2/":
625 w.WriteHeader(http.StatusOK)
626 case configPath:
627 if r.Method != http.MethodGet {
628 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
629 }
630 w.Write(mustRawConfigFile(t, img))
631 case manifestPath:
632 if r.Method != http.MethodGet {
633 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
634 }
635 w.Write(mustRawManifest(t, img))
636 case layerPath:
637 compressed, err := layer.Compressed()
638 if err != nil {
639 t.Fatal(err)
640 }
641 if _, err := io.Copy(w, compressed); err != nil {
642 t.Fatal(err)
643 }
644 w.WriteHeader(http.StatusOK)
645 case foreignLayerPath:
646
647 w.WriteHeader(http.StatusNotFound)
648 default:
649 t.Fatalf("Unexpected path: %v", r.URL.Path)
650 }
651 }))
652 defer server.Close()
653 u, err := url.Parse(server.URL)
654 if err != nil {
655 t.Fatalf("url.Parse(%v) = %v", server.URL, err)
656 }
657
658
659
660 tag := mustNewTag(t, fmt.Sprintf("%s/%s:latest", u.Host, expectedRepo))
661 rmt, err := Image(tag, WithTransport(http.DefaultTransport))
662 if err != nil {
663 t.Errorf("Image() = %v", err)
664 }
665
666 if err := validate.Image(rmt); err != nil {
667 t.Errorf("failed to validate foreign image: %v", err)
668 }
669
670
671
672 s := httptest.NewServer(registry.New())
673 defer s.Close()
674 u, err = url.Parse(s.URL)
675 if err != nil {
676 t.Fatal(err)
677 }
678 dst := fmt.Sprintf("%s/test/foreign/upload", u.Host)
679 ref, err := name.ParseReference(dst)
680 if err != nil {
681 t.Fatal(err)
682 }
683
684 if err := Write(ref, rmt); err != nil {
685 t.Errorf("failed to Write: %v", err)
686 }
687 }
688
689 func TestData(t *testing.T) {
690 img := randomImage(t)
691 manifest, err := img.Manifest()
692 if err != nil {
693 t.Fatal(err)
694 }
695 layers, err := img.Layers()
696 if err != nil {
697 t.Fatal(err)
698 }
699 cb, err := img.RawConfigFile()
700 if err != nil {
701 t.Fatal(err)
702 }
703
704 manifest.Config.Data = cb
705 rc, err := layers[0].Compressed()
706 if err != nil {
707 t.Fatal(err)
708 }
709 lb, err := io.ReadAll(rc)
710 if err != nil {
711 t.Fatal(err)
712 }
713 manifest.Layers[0].Data = lb
714 rawManifest, err := json.Marshal(manifest)
715 if err != nil {
716 t.Fatal(err)
717 }
718
719 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
720 switch r.URL.Path {
721 case "/v2/":
722 w.WriteHeader(http.StatusOK)
723 case "/v2/test/manifests/latest":
724 if r.Method != http.MethodGet {
725 t.Errorf("Method; got %v, want %v", r.Method, http.MethodGet)
726 }
727 w.Write(rawManifest)
728 default:
729
730 t.Fatalf("Unexpected path: %v", r.URL.Path)
731 }
732 }))
733 defer server.Close()
734 u, err := url.Parse(server.URL)
735 if err != nil {
736 t.Fatalf("url.Parse(%v) = %v", server.URL, err)
737 }
738 ref, err := newReference(u.Host, "test", "latest")
739 if err != nil {
740 t.Fatal(err)
741 }
742 rmt, err := Image(ref)
743 if err != nil {
744 t.Fatal(err)
745 }
746 if err := validate.Image(rmt); err != nil {
747 t.Fatal(err)
748 }
749 }
750
View as plain text