1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package crane_test
16
17 import (
18 "archive/tar"
19 "bytes"
20 "errors"
21 "fmt"
22 "io"
23 "net/http"
24 "net/http/httptest"
25 "net/url"
26 "os"
27 "path"
28 "strings"
29 "testing"
30
31 "github.com/google/go-containerregistry/internal/compare"
32 "github.com/google/go-containerregistry/pkg/authn"
33 "github.com/google/go-containerregistry/pkg/crane"
34 "github.com/google/go-containerregistry/pkg/name"
35 "github.com/google/go-containerregistry/pkg/registry"
36 v1 "github.com/google/go-containerregistry/pkg/v1"
37 "github.com/google/go-containerregistry/pkg/v1/empty"
38 "github.com/google/go-containerregistry/pkg/v1/mutate"
39 "github.com/google/go-containerregistry/pkg/v1/random"
40 "github.com/google/go-containerregistry/pkg/v1/remote"
41 )
42
43
44 func TestCraneRegistry(t *testing.T) {
45
46 s := httptest.NewServer(registry.New())
47 defer s.Close()
48 u, err := url.Parse(s.URL)
49 if err != nil {
50 t.Fatal(err)
51 }
52
53 src := fmt.Sprintf("%s/test/crane", u.Host)
54 dst := fmt.Sprintf("%s/test/crane/copy", u.Host)
55
56
57 img, err := random.Image(1024, 5)
58 if err != nil {
59 t.Fatal(err)
60 }
61 digest, err := img.Digest()
62 if err != nil {
63 t.Fatal(err)
64 }
65 rawManifest, err := img.RawManifest()
66 if err != nil {
67 t.Fatal(err)
68 }
69 manifest, err := img.Manifest()
70 if err != nil {
71 t.Fatal(err)
72 }
73 config, err := img.RawConfigFile()
74 if err != nil {
75 t.Fatal(err)
76 }
77 layer, err := img.LayerByDigest(manifest.Layers[0].Digest)
78 if err != nil {
79 t.Fatal(err)
80 }
81
82
83 if err := crane.Push(img, src); err != nil {
84 t.Fatal(err)
85 }
86
87
88 d, err := crane.Digest(src)
89 if err != nil {
90 t.Error(err)
91 } else if d != digest.String() {
92 t.Errorf("Digest(): %v != %v", d, digest)
93 }
94
95 m, err := crane.Manifest(src)
96 if err != nil {
97 t.Error(err)
98 } else if string(m) != string(rawManifest) {
99 t.Errorf("Manifest(): %v != %v", m, rawManifest)
100 }
101
102 c, err := crane.Config(src)
103 if err != nil {
104 t.Error(err)
105 } else if string(c) != string(config) {
106 t.Errorf("Config(): %v != %v", c, config)
107 }
108
109
110 pulled, err := crane.Pull(src)
111 if err != nil {
112 t.Error(err)
113 }
114 if err := compare.Images(img, pulled); err != nil {
115 t.Fatal(err)
116 }
117
118
119 if err := crane.Copy(src, dst); err != nil {
120 t.Fatal(err)
121 }
122
123
124
125 copied, err := crane.Pull(dst, crane.Insecure, crane.WithTransport(http.DefaultTransport), crane.WithAuth(authn.Anonymous), crane.WithAuthFromKeychain(authn.DefaultKeychain), crane.WithUserAgent("crane/tests"))
126 if err != nil {
127 t.Fatal(err)
128 }
129 if err := compare.Images(pulled, copied); err != nil {
130 t.Fatal(err)
131 }
132
133 if err := crane.Tag(dst, "crane-tag"); err != nil {
134 t.Fatal(err)
135 }
136
137
138 tagged, err := crane.Pull(fmt.Sprintf("%s:%s", dst, "crane-tag"))
139 if err != nil {
140 t.Fatal(err)
141 }
142 if err := compare.Images(pulled, tagged); err != nil {
143 t.Fatal(err)
144 }
145
146 layerRef := fmt.Sprintf("%s/test/crane@%s", u.Host, manifest.Layers[0].Digest)
147 pulledLayer, err := crane.PullLayer(layerRef)
148 if err != nil {
149 t.Fatal(err)
150 }
151
152 if err := compare.Layers(pulledLayer, layer); err != nil {
153 t.Fatal(err)
154 }
155
156
157
158 tags, err := crane.ListTags(dst)
159 if err != nil {
160 t.Fatal(err)
161 }
162 if len(tags) != 2 {
163 t.Fatalf("wanted 2 tags, got %d", len(tags))
164 }
165
166
167 for i := 1; i < 5; i++ {
168 if err := crane.Tag(dst, fmt.Sprintf("honk-tag-%d", i)); err != nil {
169 t.Fatal(err)
170 }
171 }
172
173 tags, err = crane.ListTags(dst)
174 if err != nil {
175 t.Fatal(err)
176 }
177 if len(tags) != 6 {
178 t.Fatalf("wanted 6 tags, got %d", len(tags))
179 }
180
181
182 if err := crane.Delete(dst + ":honk-image"); err == nil {
183 t.Fatal("wanted err, got nil")
184 }
185
186
187 if err := crane.Delete(src); err != nil {
188 t.Fatal(err)
189 }
190
191
192 if _, err := crane.Pull(src); err == nil {
193 t.Fatal("wanted err, got nil")
194 }
195
196
197 dstPulled, err := crane.Pull(dst)
198 if err != nil {
199 t.Fatal(err)
200 }
201 if err := compare.Images(dstPulled, copied); err != nil {
202 t.Fatal(err)
203 }
204
205
206 repos, err := crane.Catalog(u.Host)
207 if err != nil {
208 t.Fatal(err)
209 }
210 if len(repos) != 2 {
211 t.Fatalf("wanted 2 repos, got %d", len(repos))
212 }
213
214
215 layer, err = img.LayerByDigest(manifest.Layers[1].Digest)
216 if err != nil {
217 t.Fatal(err)
218 }
219 if err := crane.Upload(layer, dst); err != nil {
220 t.Fatal(err)
221 }
222 }
223
224 func TestCraneCopyIndex(t *testing.T) {
225
226 s := httptest.NewServer(registry.New())
227 defer s.Close()
228 u, err := url.Parse(s.URL)
229 if err != nil {
230 t.Fatal(err)
231 }
232
233 src := fmt.Sprintf("%s/test/crane", u.Host)
234 dst := fmt.Sprintf("%s/test/crane/copy", u.Host)
235
236
237 idx, err := random.Index(1024, 3, 3)
238 if err != nil {
239 t.Fatal(err)
240 }
241 ref, err := name.ParseReference(src)
242 if err != nil {
243 t.Fatal(err)
244 }
245 if err := remote.WriteIndex(ref, idx); err != nil {
246 t.Fatal(err)
247 }
248
249
250 if err := crane.Copy(src, dst); err != nil {
251 t.Fatal(err)
252 }
253
254 d, err := crane.Digest(src)
255 if err != nil {
256 t.Fatal(err)
257 }
258 cp, err := crane.Digest(dst)
259 if err != nil {
260 t.Fatal(err)
261 }
262 if d != cp {
263 t.Errorf("Copied Digest(): %v != %v", d, cp)
264 }
265 }
266
267 func TestWithPlatform(t *testing.T) {
268
269 s := httptest.NewServer(registry.New())
270 defer s.Close()
271 u, err := url.Parse(s.URL)
272 if err != nil {
273 t.Fatal(err)
274 }
275
276 imgs := []mutate.IndexAddendum{}
277 for _, plat := range []string{
278 "linux/amd64",
279 "linux/arm",
280 } {
281 img, err := crane.Image(map[string][]byte{
282 "platform.txt": []byte(plat),
283 })
284 if err != nil {
285 t.Fatal(err)
286 }
287 parts := strings.Split(plat, "/")
288 imgs = append(imgs, mutate.IndexAddendum{
289 Add: img,
290 Descriptor: v1.Descriptor{
291 Platform: &v1.Platform{
292 OS: parts[0],
293 Architecture: parts[1],
294 },
295 },
296 })
297 }
298
299 idx := mutate.AppendManifests(empty.Index, imgs...)
300
301 src := path.Join(u.Host, "src")
302 dst := path.Join(u.Host, "dst")
303
304 ref, err := name.ParseReference(src)
305 if err != nil {
306 t.Fatal(err)
307 }
308
309
310 if err := remote.WriteIndex(ref, idx); err != nil {
311 t.Fatal(err)
312 }
313
314 if err := crane.Copy(src, dst, crane.WithPlatform(imgs[1].Platform)); err != nil {
315 t.Fatal(err)
316 }
317
318 want, err := crane.Manifest(src, crane.WithPlatform(imgs[1].Platform))
319 if err != nil {
320 t.Fatal(err)
321 }
322 got, err := crane.Manifest(dst)
323 if err != nil {
324 t.Fatal(err)
325 }
326
327 if string(got) != string(want) {
328 t.Errorf("Manifest(%q) != Manifest(%q): (\n\n%s\n\n!=\n\n%s\n\n)", dst, src, string(got), string(want))
329 }
330
331 arch := "real fake doors"
332
333
334 if _, err := crane.Manifest(src, crane.WithPlatform(&v1.Platform{
335 OS: "does-not-exist",
336 Architecture: arch,
337 })); err == nil {
338 t.Error("crane.Manifest(fake platform): got nil want err")
339 } else if !strings.Contains(err.Error(), arch) {
340 t.Errorf("crane.Manifest(fake platform): expected %q in error, got: %v", arch, err)
341 }
342 }
343
344 func TestCraneTarball(t *testing.T) {
345 t.Parallel()
346
347 tmp, err := os.CreateTemp("", "")
348 if err != nil {
349 t.Fatal(err)
350 }
351 defer os.Remove(tmp.Name())
352
353 img, err := random.Image(1024, 5)
354 if err != nil {
355 t.Fatal(err)
356 }
357 digest, err := img.Digest()
358 if err != nil {
359 t.Fatal(err)
360 }
361 src := fmt.Sprintf("test/crane@%s", digest)
362
363 if err := crane.Save(img, src, tmp.Name()); err != nil {
364 t.Errorf("Save: %v", err)
365 }
366
367
368 img, err = crane.Load(tmp.Name())
369 if err != nil {
370 t.Fatal(err)
371 }
372
373 d, err := img.Digest()
374 if err != nil {
375 t.Fatal(err)
376 }
377 if d != digest {
378 t.Errorf("digest mismatch: %v != %v", d, digest)
379 }
380 }
381
382 func TestCraneSaveLegacy(t *testing.T) {
383 t.Parallel()
384
385 tmp, err := os.CreateTemp("", "")
386 if err != nil {
387 t.Fatal(err)
388 }
389 defer os.Remove(tmp.Name())
390
391 img, err := random.Image(1024, 5)
392 if err != nil {
393 t.Fatal(err)
394 }
395
396 if err := crane.SaveLegacy(img, "test/crane", tmp.Name()); err != nil {
397 t.Errorf("SaveOCI: %v", err)
398 }
399 }
400
401 func TestCraneSaveOCI(t *testing.T) {
402 t.Parallel()
403
404 tmp := t.TempDir()
405
406 img, err := random.Image(1024, 5)
407 if err != nil {
408 t.Fatal(err)
409 }
410 if err := crane.SaveOCI(img, tmp); err != nil {
411 t.Errorf("SaveLegacy: %v", err)
412 }
413 }
414
415 func TestCraneFilesystem(t *testing.T) {
416 t.Parallel()
417 tmp, err := os.CreateTemp("", "")
418 if err != nil {
419 t.Fatal(err)
420 }
421 img, err := random.Image(1024, 5)
422 if err != nil {
423 t.Fatal(err)
424 }
425
426 name := "/some/file"
427 content := []byte("sentinel")
428
429 tw := tar.NewWriter(tmp)
430 if err := tw.WriteHeader(&tar.Header{
431 Size: int64(len(content)),
432 Name: name,
433 }); err != nil {
434 t.Fatal(err)
435 }
436 if _, err := tw.Write(content); err != nil {
437 t.Fatal(err)
438 }
439 tw.Flush()
440 tw.Close()
441
442 img, err = crane.Append(img, tmp.Name())
443 if err != nil {
444 t.Fatal(err)
445 }
446
447 var buf bytes.Buffer
448 if err := crane.Export(img, &buf); err != nil {
449 t.Fatal(err)
450 }
451
452 tr := tar.NewReader(&buf)
453 for {
454 header, err := tr.Next()
455 if errors.Is(err, io.EOF) {
456 t.Fatalf("didn't find find")
457 } else if err != nil {
458 t.Fatal(err)
459 }
460 if header.Name == name {
461 b, err := io.ReadAll(tr)
462 if err != nil {
463 t.Fatal(err)
464 }
465 if string(b) != string(content) {
466 t.Fatalf("got back wrong content: %v != %v", string(b), string(content))
467 }
468 break
469 }
470 }
471 }
472
473 func TestStreamingAppend(t *testing.T) {
474
475 layer, err := crane.Layer(map[string][]byte{
476 "hello": []byte(`world`),
477 })
478 if err != nil {
479 t.Fatal(err)
480 }
481 rc, err := layer.Uncompressed()
482 if err != nil {
483 t.Fatal(err)
484 }
485
486 tmp, err := os.CreateTemp("", "crane-append")
487 if err != nil {
488 t.Fatal(err)
489 }
490 defer os.Remove(tmp.Name())
491
492 if _, err := io.Copy(tmp, rc); err != nil {
493 t.Fatal(err)
494 }
495
496 stdin := os.Stdin
497 defer func() {
498 os.Stdin = stdin
499 }()
500
501 os.Stdin = tmp
502
503 img, err := crane.Append(empty.Image, "-")
504 if err != nil {
505 t.Fatal(err)
506 }
507 ll, err := img.Layers()
508 if err != nil {
509 t.Fatal(err)
510 }
511 if want, got := 1, len(ll); want != got {
512 t.Errorf("crane.Append(stdin) - len(layers): want %d != got %d", want, got)
513 }
514 }
515
516 func TestBadInputs(t *testing.T) {
517 t.Parallel()
518 invalid := "/dev/null/@@@@@@"
519
520
521 s := httptest.NewServer(http.NotFoundHandler())
522 u, err := url.Parse(s.URL)
523 if err != nil {
524 t.Fatal(err)
525 }
526 valid404 := fmt.Sprintf("%s/some/image", u.Host)
527
528
529
530 e := func(_ any, err error) error {
531 return err
532 }
533
534 for _, tc := range []struct {
535 desc string
536 err error
537 }{
538 {"Push(_, invalid)", crane.Push(nil, invalid)},
539 {"Upload(_, invalid)", crane.Upload(nil, invalid)},
540 {"Delete(invalid)", crane.Delete(invalid)},
541 {"Delete: 404", crane.Delete(valid404)},
542 {"Save(_, invalid)", crane.Save(nil, invalid, "")},
543 {"SaveLegacy(_, invalid)", crane.SaveLegacy(nil, invalid, "")},
544 {"SaveLegacy(_, invalid)", crane.SaveLegacy(nil, valid404, invalid)},
545 {"SaveOCI(_, invalid)", crane.SaveOCI(nil, "")},
546 {"Copy(invalid, invalid)", crane.Copy(invalid, invalid)},
547 {"Copy(404, invalid)", crane.Copy(valid404, invalid)},
548 {"Copy(404, 404)", crane.Copy(valid404, valid404)},
549 {"Tag(invalid, invalid)", crane.Tag(invalid, invalid)},
550 {"Tag(404, invalid)", crane.Tag(valid404, invalid)},
551 {"Tag(404, 404)", crane.Tag(valid404, valid404)},
552
553 {"Pull(invalid)", e(crane.Pull(invalid))},
554 {"Digest(invalid)", e(crane.Digest(invalid))},
555 {"Manifest(invalid)", e(crane.Manifest(invalid))},
556 {"Config(invalid)", e(crane.Config(invalid))},
557 {"Config(404)", e(crane.Config(valid404))},
558 {"ListTags(invalid)", e(crane.ListTags(invalid))},
559 {"ListTags(404)", e(crane.ListTags(valid404))},
560 {"Append(_, invalid)", e(crane.Append(nil, invalid))},
561 {"Catalog(invalid)", e(crane.Catalog(invalid))},
562 {"Catalog(404)", e(crane.Catalog(u.Host))},
563 {"PullLayer(invalid)", e(crane.PullLayer(invalid))},
564 {"LoadTag(_, invalid)", e(crane.LoadTag("", invalid))},
565 {"LoadTag(invalid, 404)", e(crane.LoadTag(invalid, valid404))},
566 } {
567 if tc.err == nil {
568 t.Errorf("%s: expected err, got nil", tc.desc)
569 }
570 }
571 }
572
View as plain text