github.com/opcr-io/oras-go/v2@v2.0.0-20231122155130-eb4260d8a0ae/content/file/file_test.go (about) 1 /* 2 Copyright The ORAS Authors. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 */ 15 16 package file 17 18 import ( 19 "bytes" 20 "context" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "io" 25 "os" 26 "path/filepath" 27 "reflect" 28 "strings" 29 "sync/atomic" 30 "testing" 31 32 _ "crypto/sha256" 33 34 "github.com/opcr-io/oras-go/v2" 35 "github.com/opcr-io/oras-go/v2/content" 36 "github.com/opcr-io/oras-go/v2/content/memory" 37 "github.com/opcr-io/oras-go/v2/errdef" 38 "github.com/opcr-io/oras-go/v2/internal/descriptor" 39 "github.com/opencontainers/go-digest" 40 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 41 "golang.org/x/sync/errgroup" 42 ) 43 44 // storageTracker tracks storage API counts. 45 type storageTracker struct { 46 content.Storage 47 fetch int64 48 push int64 49 exists int64 50 } 51 52 func (t *storageTracker) Fetch(ctx context.Context, target ocispec.Descriptor) (io.ReadCloser, error) { 53 atomic.AddInt64(&t.fetch, 1) 54 return t.Storage.Fetch(ctx, target) 55 } 56 57 func (t *storageTracker) Push(ctx context.Context, expected ocispec.Descriptor, content io.Reader) error { 58 atomic.AddInt64(&t.push, 1) 59 return t.Storage.Push(ctx, expected, content) 60 } 61 62 func (t *storageTracker) Exists(ctx context.Context, target ocispec.Descriptor) (bool, error) { 63 atomic.AddInt64(&t.exists, 1) 64 return t.Storage.Exists(ctx, target) 65 } 66 67 func TestStoreInterface(t *testing.T) { 68 var store interface{} = &Store{} 69 if _, ok := store.(oras.Target); !ok { 70 t.Error("&Store{} does not conform oras.Target") 71 } 72 if _, ok := store.(content.PredecessorFinder); !ok { 73 t.Error("&Store{} does not conform content.PredecessorFinder") 74 } 75 } 76 77 func TestStore_Success(t *testing.T) { 78 tempDir := t.TempDir() 79 s, err := New(tempDir) 80 if err != nil { 81 t.Fatal("Store.New() error =", err) 82 } 83 defer s.Close() 84 ctx := context.Background() 85 86 blob := []byte("hello world") 87 name := "test.txt" 88 mediaType := "test" 89 desc := ocispec.Descriptor{ 90 MediaType: mediaType, 91 Digest: digest.FromBytes(blob), 92 Size: int64(len(blob)), 93 Annotations: map[string]string{ 94 ocispec.AnnotationTitle: name, 95 }, 96 } 97 98 path := filepath.Join(tempDir, name) 99 if err := os.WriteFile(path, blob, 0444); err != nil { 100 t.Fatal("error calling WriteFile(), error =", err) 101 } 102 103 // test blob add 104 gotDesc, err := s.Add(ctx, name, mediaType, path) 105 if err != nil { 106 t.Fatal("Store.Add() error =", err) 107 } 108 if descriptor.FromOCI(gotDesc) != descriptor.FromOCI(desc) { 109 t.Fatal("got descriptor mismatch") 110 } 111 112 // test blob exists 113 exists, err := s.Exists(ctx, gotDesc) 114 if err != nil { 115 t.Fatal("Store.Exists() error =", err) 116 } 117 if !exists { 118 t.Errorf("Store.Exists() = %v, want %v", exists, true) 119 } 120 121 // test blob fetch 122 rc, err := s.Fetch(ctx, gotDesc) 123 if err != nil { 124 t.Fatal("Store.Fetch() error =", err) 125 } 126 got, err := io.ReadAll(rc) 127 if err != nil { 128 t.Fatal("Store.Fetch().Read() error =", err) 129 } 130 err = rc.Close() 131 if err != nil { 132 t.Error("Store.Fetch().Close() error =", err) 133 } 134 if !bytes.Equal(got, blob) { 135 t.Errorf("Store.Fetch() = %v, want %v", got, blob) 136 } 137 138 // test push config 139 config := []byte("{}") 140 configDesc := ocispec.Descriptor{ 141 MediaType: "config", 142 Digest: digest.FromBytes(config), 143 Size: int64(len(config)), 144 Annotations: map[string]string{ 145 ocispec.AnnotationTitle: "config.blob", 146 }, 147 } 148 if err := s.Push(ctx, configDesc, bytes.NewReader(config)); err != nil { 149 t.Fatal("Store.Push() error =", err) 150 } 151 152 // test push manifest 153 manifest := ocispec.Manifest{ 154 MediaType: ocispec.MediaTypeImageManifest, 155 Config: configDesc, 156 Layers: []ocispec.Descriptor{ 157 gotDesc, 158 }, 159 } 160 manifestJSON, err := json.Marshal(manifest) 161 if err != nil { 162 t.Fatal("json.Marshal() error =", err) 163 } 164 manifestDesc := ocispec.Descriptor{ 165 MediaType: ocispec.MediaTypeImageManifest, 166 Digest: digest.FromBytes(manifestJSON), 167 Size: int64(len(manifestJSON)), 168 } 169 if err := s.Push(ctx, manifestDesc, bytes.NewReader(manifestJSON)); err != nil { 170 t.Fatal("Store.Push() error =", err) 171 } 172 173 // test tag 174 ref := "foobar" 175 if err := s.Tag(ctx, manifestDesc, ref); err != nil { 176 t.Fatal("Store.Tag() error =", err) 177 } 178 179 // test resolve 180 gotManifestDesc, err := s.Resolve(ctx, ref) 181 if err != nil { 182 t.Fatal("Store.Resolve() error =", err) 183 } 184 if !reflect.DeepEqual(gotManifestDesc, manifestDesc) { 185 t.Errorf("Store.Resolve() = %v, want %v", gotManifestDesc, manifestDesc) 186 } 187 188 // test fetch 189 exists, err = s.Exists(ctx, gotManifestDesc) 190 if err != nil { 191 t.Fatal("Store.Exists() error =", err) 192 } 193 if !exists { 194 t.Errorf("Store.Exists() = %v, want %v", exists, true) 195 } 196 197 mrc, err := s.Fetch(ctx, gotManifestDesc) 198 if err != nil { 199 t.Fatal("Store.Fetch() error =", err) 200 } 201 got, err = io.ReadAll(mrc) 202 if err != nil { 203 t.Fatal("Store.Fetch().Read() error =", err) 204 } 205 err = mrc.Close() 206 if err != nil { 207 t.Error("Store.Fetch().Close() error =", err) 208 } 209 if !bytes.Equal(got, manifestJSON) { 210 t.Errorf("Store.Fetch() = %v, want %v", got, manifestJSON) 211 } 212 } 213 214 func TestStore_RelativeRoot_Success(t *testing.T) { 215 tempDir, err := filepath.EvalSymlinks(t.TempDir()) 216 if err != nil { 217 t.Fatal("error calling filepath.EvalSymlinks(), error =", err) 218 } 219 currDir, err := os.Getwd() 220 if err != nil { 221 t.Fatal("error calling Getwd(), error=", err) 222 } 223 if err := os.Chdir(tempDir); err != nil { 224 t.Fatal("error calling Chdir(), error=", err) 225 } 226 s, err := New(".") 227 if err != nil { 228 t.Fatal("Store.New() error =", err) 229 } 230 defer s.Close() 231 if want := tempDir; s.workingDir != want { 232 t.Errorf("Store.workingDir = %s, want %s", s.workingDir, want) 233 } 234 // cd back to allow the temp directory to be removed 235 if err := os.Chdir(currDir); err != nil { 236 t.Fatal("error calling Chdir(), error=", err) 237 } 238 ctx := context.Background() 239 240 blob := []byte("hello world") 241 name := "test.txt" 242 mediaType := "test" 243 desc := ocispec.Descriptor{ 244 MediaType: mediaType, 245 Digest: digest.FromBytes(blob), 246 Size: int64(len(blob)), 247 Annotations: map[string]string{ 248 ocispec.AnnotationTitle: name, 249 }, 250 } 251 252 path := filepath.Join(tempDir, name) 253 if err := os.WriteFile(path, blob, 0444); err != nil { 254 t.Fatal("error calling WriteFile(), error =", err) 255 } 256 257 // test blob add 258 gotDesc, err := s.Add(ctx, name, mediaType, path) 259 if err != nil { 260 t.Fatal("Store.Add() error =", err) 261 } 262 if descriptor.FromOCI(gotDesc) != descriptor.FromOCI(desc) { 263 t.Fatal("got descriptor mismatch") 264 } 265 266 // test blob exists 267 exists, err := s.Exists(ctx, gotDesc) 268 if err != nil { 269 t.Fatal("Store.Exists() error =", err) 270 } 271 if !exists { 272 t.Errorf("Store.Exists() = %v, want %v", exists, true) 273 } 274 275 // test blob fetch 276 rc, err := s.Fetch(ctx, gotDesc) 277 if err != nil { 278 t.Fatal("Store.Fetch() error =", err) 279 } 280 got, err := io.ReadAll(rc) 281 if err != nil { 282 t.Fatal("Store.Fetch().Read() error =", err) 283 } 284 err = rc.Close() 285 if err != nil { 286 t.Error("Store.Fetch().Close() error =", err) 287 } 288 if !bytes.Equal(got, blob) { 289 t.Errorf("Store.Fetch() = %v, want %v", got, blob) 290 } 291 292 // test push config 293 config := []byte("{}") 294 configDesc := ocispec.Descriptor{ 295 MediaType: "config", 296 Digest: digest.FromBytes(config), 297 Size: int64(len(config)), 298 Annotations: map[string]string{ 299 ocispec.AnnotationTitle: "config.blob", 300 }, 301 } 302 if err := s.Push(ctx, configDesc, bytes.NewReader(config)); err != nil { 303 t.Fatal("Store.Push() error =", err) 304 } 305 306 // test push manifest 307 manifest := ocispec.Manifest{ 308 MediaType: ocispec.MediaTypeImageManifest, 309 Config: configDesc, 310 Layers: []ocispec.Descriptor{ 311 gotDesc, 312 }, 313 } 314 manifestJSON, err := json.Marshal(manifest) 315 if err != nil { 316 t.Fatal("json.Marshal() error =", err) 317 } 318 manifestDesc := ocispec.Descriptor{ 319 MediaType: ocispec.MediaTypeImageManifest, 320 Digest: digest.FromBytes(manifestJSON), 321 Size: int64(len(manifestJSON)), 322 } 323 if err := s.Push(ctx, manifestDesc, bytes.NewReader(manifestJSON)); err != nil { 324 t.Fatal("Store.Push() error =", err) 325 } 326 327 // test tag 328 ref := "foobar" 329 if err := s.Tag(ctx, manifestDesc, ref); err != nil { 330 t.Fatal("Store.Tag() error =", err) 331 } 332 333 // test resolve 334 gotManifestDesc, err := s.Resolve(ctx, ref) 335 if err != nil { 336 t.Fatal("Store.Resolve() error =", err) 337 } 338 if !reflect.DeepEqual(gotManifestDesc, manifestDesc) { 339 t.Errorf("Store.Resolve() = %v, want %v", gotManifestDesc, manifestDesc) 340 } 341 342 // test fetch 343 exists, err = s.Exists(ctx, gotManifestDesc) 344 if err != nil { 345 t.Fatal("Store.Exists() error =", err) 346 } 347 if !exists { 348 t.Errorf("Store.Exists() = %v, want %v", exists, true) 349 } 350 351 mrc, err := s.Fetch(ctx, gotManifestDesc) 352 if err != nil { 353 t.Fatal("Store.Fetch() error =", err) 354 } 355 got, err = io.ReadAll(mrc) 356 if err != nil { 357 t.Fatal("Store.Fetch().Read() error =", err) 358 } 359 err = mrc.Close() 360 if err != nil { 361 t.Error("Store.Fetch().Close() error =", err) 362 } 363 if !bytes.Equal(got, manifestJSON) { 364 t.Errorf("Store.Fetch() = %v, want %v", got, manifestJSON) 365 } 366 } 367 368 func TestStore_Close(t *testing.T) { 369 content := []byte("hello world") 370 name := "test.txt" 371 mediaType := "test" 372 desc := ocispec.Descriptor{ 373 MediaType: mediaType, 374 Digest: digest.FromBytes(content), 375 Size: int64(len(content)), 376 Annotations: map[string]string{ 377 ocispec.AnnotationTitle: name, 378 }, 379 } 380 ref := "foobar" 381 382 tempDir := t.TempDir() 383 s, err := New(tempDir) 384 if err != nil { 385 t.Fatal("Store.New() error =", err) 386 } 387 ctx := context.Background() 388 389 // test push 390 err = s.Push(ctx, desc, bytes.NewReader(content)) 391 if err != nil { 392 t.Fatal("Store.Push() error =", err) 393 } 394 395 // test exists 396 exists, err := s.Exists(ctx, desc) 397 if err != nil { 398 t.Fatal("Store.Exists() error =", err) 399 } 400 if !exists { 401 t.Errorf("Store.Exists() = %v, want %v", exists, true) 402 } 403 404 // test fetch 405 rc, err := s.Fetch(ctx, desc) 406 if err != nil { 407 t.Fatal("Store.Fetch() error =", err) 408 } 409 got, err := io.ReadAll(rc) 410 if err != nil { 411 t.Fatal("Store.Fetch().Read() error =", err) 412 } 413 err = rc.Close() 414 if err != nil { 415 t.Error("Store.Fetch().Close() error =", err) 416 } 417 if !bytes.Equal(got, content) { 418 t.Errorf("Store.Fetch() = %v, want %v", got, content) 419 } 420 421 // test close 422 if err := s.Close(); err != nil { 423 t.Error("Store.Close() error =", err) 424 } 425 // test close twice 426 if err := s.Close(); err != nil { 427 t.Error("Store.Close() error =", err) 428 } 429 430 // test add after closed 431 if _, err := s.Add(ctx, name, mediaType, ""); !errors.Is(err, ErrStoreClosed) { 432 t.Errorf("Store.Add() = %v, want %v", err, ErrStoreClosed) 433 } 434 435 // test push after closed 436 if err = s.Push(ctx, desc, bytes.NewReader(content)); !errors.Is(err, ErrStoreClosed) { 437 t.Errorf("Store.Push() = %v, want %v", err, ErrStoreClosed) 438 } 439 440 // test exists after closed 441 if _, err := s.Exists(ctx, desc); !errors.Is(err, ErrStoreClosed) { 442 t.Errorf("Store.Exists() = %v, want %v", err, ErrStoreClosed) 443 } 444 445 // test tag after closed 446 if err := s.Tag(ctx, desc, ref); !errors.Is(err, ErrStoreClosed) { 447 t.Errorf("Store.Tag() = %v, want %v", err, ErrStoreClosed) 448 } 449 450 // test resolve after closed 451 if _, err := s.Resolve(ctx, ref); !errors.Is(err, ErrStoreClosed) { 452 t.Errorf("Store.Resolve() = %v, want %v", err, ErrStoreClosed) 453 } 454 455 // test fetch after closed 456 if _, err := s.Fetch(ctx, desc); !errors.Is(err, ErrStoreClosed) { 457 t.Errorf("Store.Fetch() = %v, want %v", err, ErrStoreClosed) 458 } 459 460 // test Predecessors after closed 461 if _, err := s.Predecessors(ctx, desc); !errors.Is(err, ErrStoreClosed) { 462 t.Errorf("Store.Predecessors() = %v, want %v", err, ErrStoreClosed) 463 } 464 } 465 466 func TestStore_File_Push(t *testing.T) { 467 content := []byte("hello world") 468 desc := ocispec.Descriptor{ 469 MediaType: "test", 470 Digest: digest.FromBytes(content), 471 Size: int64(len(content)), 472 Annotations: map[string]string{ 473 ocispec.AnnotationTitle: "test.txt", 474 }, 475 } 476 tempDir := t.TempDir() 477 s, err := New(tempDir) 478 if err != nil { 479 t.Fatal("Store.New() error =", err) 480 } 481 defer s.Close() 482 ctx := context.Background() 483 484 // test push 485 err = s.Push(ctx, desc, bytes.NewReader(content)) 486 if err != nil { 487 t.Fatal("Store.Push() error =", err) 488 } 489 490 // test exists 491 exists, err := s.Exists(ctx, desc) 492 if err != nil { 493 t.Fatal("Store.Exists() error =", err) 494 } 495 if !exists { 496 t.Errorf("Store.Exists() = %v, want %v", exists, true) 497 } 498 499 // test fetch 500 rc, err := s.Fetch(ctx, desc) 501 if err != nil { 502 t.Fatal("Store.Fetch() error =", err) 503 } 504 got, err := io.ReadAll(rc) 505 if err != nil { 506 t.Fatal("Store.Fetch().Read() error =", err) 507 } 508 err = rc.Close() 509 if err != nil { 510 t.Error("Store.Fetch().Close() error =", err) 511 } 512 if !bytes.Equal(got, content) { 513 t.Errorf("Store.Fetch() = %v, want %v", got, content) 514 } 515 } 516 517 func TestStore_Dir_Push(t *testing.T) { 518 tempDir := t.TempDir() 519 dirName := "testdir" 520 dirPath := filepath.Join(tempDir, dirName) 521 if err := os.MkdirAll(dirPath, 0777); err != nil { 522 t.Fatal("error calling Mkdir(), error =", err) 523 } 524 525 content := []byte("hello world") 526 fileName := "test.txt" 527 if err := os.WriteFile(filepath.Join(dirPath, fileName), content, 0444); err != nil { 528 t.Fatal("error calling WriteFile(), error =", err) 529 } 530 531 s, err := New(tempDir) 532 if err != nil { 533 t.Fatal("Store.New() error =", err) 534 } 535 defer s.Close() 536 ctx := context.Background() 537 538 // test add 539 desc, err := s.Add(ctx, dirName, "", dirPath) 540 if err != nil { 541 t.Fatal("Store.Add() error=", err) 542 } 543 544 val, ok := s.digestToPath.Load(desc.Digest) 545 if !ok { 546 t.Fatal("failed to find internal gz") 547 } 548 tmpPath := val.(string) 549 zrc, err := os.Open(tmpPath) 550 if err != nil { 551 t.Fatal("failed to open internal gz") 552 } 553 gz, err := io.ReadAll(zrc) 554 if err != nil { 555 t.Fatal("failed to read internal gz") 556 } 557 if err := zrc.Close(); err != nil { 558 t.Fatal("failed to close internal gz reader") 559 } 560 561 anotherTempDir := t.TempDir() 562 // test with another file store instance to mock push gz 563 anotherS, err := New(anotherTempDir) 564 if err != nil { 565 t.Fatal("Store.New() error =", err) 566 } 567 defer anotherS.Close() 568 569 // test push 570 if err := anotherS.Push(ctx, desc, bytes.NewReader(gz)); err != nil { 571 t.Fatal("Store.Push() error =", err) 572 573 } 574 575 // test exists 576 exists, err := s.Exists(ctx, desc) 577 if err != nil { 578 t.Fatal("Store.Exists() error =", err) 579 } 580 if !exists { 581 t.Errorf("Store.Exists() = %v, want %v", exists, true) 582 } 583 584 // test fetch 585 rc, err := s.Fetch(ctx, desc) 586 if err != nil { 587 t.Fatal("Store.Fetch() error =", err) 588 } 589 got, err := io.ReadAll(rc) 590 if err != nil { 591 t.Fatal("Store.Fetch().Read() error =", err) 592 } 593 err = rc.Close() 594 if err != nil { 595 t.Error("Store.Fetch().Close() error =", err) 596 } 597 if !bytes.Equal(got, gz) { 598 t.Errorf("Store.Fetch() = %v, want %v", got, gz) 599 } 600 601 // test file content 602 path := filepath.Join(s.workingDir, dirName, fileName) 603 fp, err := os.Open(path) 604 if err != nil { 605 t.Fatalf("failed to open file %s:%v", path, err) 606 } 607 fc, err := io.ReadAll(fp) 608 if err != nil { 609 t.Fatalf("failed to read file %s:%v", path, err) 610 } 611 if err := fp.Close(); err != nil { 612 t.Fatalf("failed to close file %s:%v", path, err) 613 } 614 615 anotherFilePath := filepath.Join(anotherS.workingDir, dirName, fileName) 616 anotherFp, err := os.Open(anotherFilePath) 617 if err != nil { 618 t.Fatalf("failed to open file %s:%v", anotherFilePath, err) 619 } 620 anotherFc, err := io.ReadAll(anotherFp) 621 if err != nil { 622 t.Fatalf("failed to read file %s:%v", anotherFilePath, err) 623 } 624 if err := anotherFp.Close(); err != nil { 625 t.Fatalf("failed to close file %s:%v", anotherFilePath, err) 626 } 627 628 if !bytes.Equal(fc, anotherFc) { 629 t.Errorf("file content mismatch") 630 } 631 } 632 633 func TestStore_Push_NoName(t *testing.T) { 634 content := []byte("hello world") 635 desc := ocispec.Descriptor{ 636 MediaType: "test", 637 Digest: digest.FromBytes(content), 638 Size: int64(len(content)), 639 } 640 641 tempDir := t.TempDir() 642 s, err := New(tempDir) 643 if err != nil { 644 t.Fatal("Store.New() error =", err) 645 } 646 defer s.Close() 647 ctx := context.Background() 648 649 // test push 650 err = s.Push(ctx, desc, bytes.NewReader(content)) 651 if err != nil { 652 t.Fatal("Store.Push() error =", err) 653 } 654 655 // test exists 656 exists, err := s.Exists(ctx, desc) 657 if err != nil { 658 t.Fatal("Store.Exists() error =", err) 659 } 660 if !exists { 661 t.Errorf("Store.Exists() = %v, want %v", exists, true) 662 } 663 664 // test fetch 665 rc, err := s.Fetch(ctx, desc) 666 if err != nil { 667 t.Fatal("Store.Fetch() error =", err) 668 } 669 got, err := io.ReadAll(rc) 670 if err != nil { 671 t.Fatal("Store.Fetch().Read() error =", err) 672 } 673 err = rc.Close() 674 if err != nil { 675 t.Error("Store.Fetch().Close() error =", err) 676 } 677 if !bytes.Equal(got, content) { 678 t.Errorf("Store.Fetch() = %v, want %v", got, content) 679 } 680 } 681 682 func TestStore_Push_NoName_ExceedLimit(t *testing.T) { 683 blob := []byte("hello world") 684 desc := ocispec.Descriptor{ 685 MediaType: "test", 686 Digest: digest.FromBytes(blob), 687 Size: int64(len(blob)), 688 } 689 690 tempDir := t.TempDir() 691 s, err := NewWithFallbackLimit(tempDir, 1) 692 if err != nil { 693 t.Fatal("Store.NewWithFallbackLimit() error =", err) 694 } 695 defer s.Close() 696 ctx := context.Background() 697 698 // test push 699 err = s.Push(ctx, desc, bytes.NewReader(blob)) 700 if !errors.Is(err, errdef.ErrSizeExceedsLimit) { 701 t.Errorf("Store.Push() error = %v, want %v", err, errdef.ErrSizeExceedsLimit) 702 } 703 } 704 705 func TestStore_Push_NoName_SizeNotMatch(t *testing.T) { 706 blob := []byte("hello world") 707 desc := ocispec.Descriptor{ 708 MediaType: "test", 709 Digest: digest.FromBytes(blob), 710 Size: 1, 711 } 712 713 tempDir := t.TempDir() 714 s, err := NewWithFallbackLimit(tempDir, 1) 715 if err != nil { 716 t.Fatal("Store.NewWithFallbackLimit() error =", err) 717 } 718 defer s.Close() 719 ctx := context.Background() 720 721 // test push 722 err = s.Push(ctx, desc, bytes.NewReader(blob)) 723 if err == nil { 724 t.Errorf("Store.Push() error = nil, want: error") 725 } 726 } 727 728 func TestStore_File_NotFound(t *testing.T) { 729 content := []byte("hello world") 730 desc := ocispec.Descriptor{ 731 MediaType: "test", 732 Digest: digest.FromBytes(content), 733 Size: int64(len(content)), 734 Annotations: map[string]string{ 735 ocispec.AnnotationTitle: "test.txt", 736 }, 737 } 738 739 tempDir := t.TempDir() 740 s, err := New(tempDir) 741 if err != nil { 742 t.Fatal("Store.New() error =", err) 743 } 744 defer s.Close() 745 ctx := context.Background() 746 747 exists, err := s.Exists(ctx, desc) 748 if err != nil { 749 t.Error("Store.Exists() error =", err) 750 } 751 if exists { 752 t.Errorf("Store.Exists() = %v, want %v", exists, false) 753 } 754 755 _, err = s.Fetch(ctx, desc) 756 if !errors.Is(err, errdef.ErrNotFound) { 757 t.Errorf("Store.Fetch() error = %v, want %v", err, errdef.ErrNotFound) 758 } 759 } 760 761 func TestStore_File_ContentBadPush(t *testing.T) { 762 content := []byte("hello world") 763 desc := ocispec.Descriptor{ 764 MediaType: "test", 765 Digest: digest.FromBytes(content), 766 Size: int64(len(content)), 767 Annotations: map[string]string{ 768 ocispec.AnnotationTitle: "test.txt", 769 }, 770 } 771 772 tempDir := t.TempDir() 773 s, err := New(tempDir) 774 if err != nil { 775 t.Fatal("Store.New() error =", err) 776 } 777 defer s.Close() 778 ctx := context.Background() 779 780 err = s.Push(ctx, desc, strings.NewReader("foobar")) 781 if err == nil { 782 t.Errorf("Store.Push() error = %v, wantErr %v", err, true) 783 } 784 } 785 786 func TestStore_File_Add(t *testing.T) { 787 content := []byte("hello world") 788 name := "test.txt" 789 mediaType := "test" 790 desc := ocispec.Descriptor{ 791 MediaType: mediaType, 792 Digest: digest.FromBytes(content), 793 Size: int64(len(content)), 794 Annotations: map[string]string{ 795 ocispec.AnnotationTitle: name, 796 }, 797 } 798 799 tempDir := t.TempDir() 800 path := filepath.Join(tempDir, name) 801 if err := os.WriteFile(path, content, 0444); err != nil { 802 t.Fatal("error calling WriteFile(), error =", err) 803 } 804 805 s, err := New(tempDir) 806 if err != nil { 807 t.Fatal("Store.New() error =", err) 808 } 809 defer s.Close() 810 ctx := context.Background() 811 812 // test add 813 gotDesc, err := s.Add(ctx, name, mediaType, path) 814 if err != nil { 815 t.Fatal("Store.Add() error =", err) 816 } 817 if descriptor.FromOCI(gotDesc) != descriptor.FromOCI(desc) { 818 t.Fatal("got descriptor mismatch") 819 } 820 821 // test exists 822 exists, err := s.Exists(ctx, gotDesc) 823 if err != nil { 824 t.Fatal("Store.Exists() error =", err) 825 } 826 if !exists { 827 t.Errorf("Store.Exists() = %v, want %v", exists, true) 828 } 829 830 // test fetch 831 rc, err := s.Fetch(ctx, gotDesc) 832 if err != nil { 833 t.Fatal("Store.Fetch() error =", err) 834 } 835 got, err := io.ReadAll(rc) 836 if err != nil { 837 t.Fatal("Store.Fetch().Read() error =", err) 838 } 839 err = rc.Close() 840 if err != nil { 841 t.Error("Store.Fetch().Close() error =", err) 842 } 843 if !bytes.Equal(got, content) { 844 t.Errorf("Store.Fetch() = %v, want %v", got, content) 845 } 846 } 847 848 func TestStore_Dir_Add(t *testing.T) { 849 tempDir := t.TempDir() 850 dirName := "testdir" 851 dirPath := filepath.Join(tempDir, dirName) 852 if err := os.MkdirAll(dirPath, 0777); err != nil { 853 t.Fatal("error calling Mkdir(), error =", err) 854 } 855 856 content := []byte("hello world") 857 if err := os.WriteFile(filepath.Join(dirPath, "test.txt"), content, 0444); err != nil { 858 t.Fatal("error calling WriteFile(), error =", err) 859 } 860 861 s, err := New(tempDir) 862 if err != nil { 863 t.Fatal("Store.New() error =", err) 864 } 865 defer s.Close() 866 ctx := context.Background() 867 868 // test add 869 gotDesc, err := s.Add(ctx, dirName, "", dirPath) 870 if err != nil { 871 t.Fatal("Store.Add() error=", err) 872 } 873 874 // test exists 875 exists, err := s.Exists(ctx, gotDesc) 876 if err != nil { 877 t.Fatal("Store.Exists() error =", err) 878 } 879 if !exists { 880 t.Errorf("Store.Exists() = %v, want %v", exists, true) 881 } 882 883 val, ok := s.digestToPath.Load(gotDesc.Digest) 884 if !ok { 885 t.Fatal("failed to find internal gz") 886 } 887 tmpPath := val.(string) 888 zrc, err := os.Open(tmpPath) 889 if err != nil { 890 t.Fatal("failed to open internal gz") 891 } 892 gotgz, err := io.ReadAll(zrc) 893 if err != nil { 894 t.Fatal("failed to read internal gz") 895 } 896 897 // test fetch 898 rc, err := s.Fetch(ctx, gotDesc) 899 if err != nil { 900 t.Fatal("Store.Fetch() error =", err) 901 } 902 got, err := io.ReadAll(rc) 903 if err != nil { 904 t.Fatal("Store.Fetch().Read() error =", err) 905 } 906 err = rc.Close() 907 if err != nil { 908 t.Error("Store.Fetch().Close() error =", err) 909 } 910 if !bytes.Equal(got, gotgz) { 911 t.Errorf("Store.Fetch() = %v, want %v", got, gotgz) 912 } 913 } 914 func TestStore_File_SameContent_DuplicateName(t *testing.T) { 915 content := []byte("hello world") 916 name := "test.txt" 917 mediaType := "test" 918 desc := ocispec.Descriptor{ 919 MediaType: mediaType, 920 Digest: digest.FromBytes(content), 921 Size: int64(len(content)), 922 Annotations: map[string]string{ 923 ocispec.AnnotationTitle: name, 924 }, 925 } 926 927 tempDir := t.TempDir() 928 path := filepath.Join(tempDir, name) 929 if err := os.WriteFile(path, content, 0444); err != nil { 930 t.Fatal("error calling WriteFile(), error =", err) 931 } 932 933 s, err := New(tempDir) 934 if err != nil { 935 t.Fatal("Store.New() error =", err) 936 } 937 defer s.Close() 938 ctx := context.Background() 939 940 // test add 941 gotDesc, err := s.Add(ctx, name, mediaType, path) 942 if err != nil { 943 t.Fatal("Store.Add() error =", err) 944 } 945 if descriptor.FromOCI(gotDesc) != descriptor.FromOCI(desc) { 946 t.Fatal("got descriptor mismatch") 947 } 948 949 // test exists 950 exists, err := s.Exists(ctx, gotDesc) 951 if err != nil { 952 t.Fatal("Store.Exists() error =", err) 953 } 954 if !exists { 955 t.Errorf("Store.Exists() = %v, want %v", exists, true) 956 } 957 958 // test fetch 959 rc, err := s.Fetch(ctx, gotDesc) 960 if err != nil { 961 t.Fatal("Store.Fetch() error =", err) 962 } 963 got, err := io.ReadAll(rc) 964 if err != nil { 965 t.Fatal("Store.Fetch().Read() error =", err) 966 } 967 err = rc.Close() 968 if err != nil { 969 t.Error("Store.Fetch().Close() error =", err) 970 } 971 if !bytes.Equal(got, content) { 972 t.Errorf("Store.Fetch() = %v, want %v", got, content) 973 } 974 975 // test duplicate name 976 if _, err := s.Add(ctx, name, mediaType, path); !errors.Is(err, ErrDuplicateName) { 977 t.Errorf("Store.Add() = %v, want %v", err, ErrDuplicateName) 978 } 979 } 980 981 func TestStore_File_DifferentContent_DuplicateName(t *testing.T) { 982 content_1 := []byte("hello world") 983 content_2 := []byte("goodbye world") 984 985 name_1 := "test_1.txt" 986 name_2 := "test_2.txt" 987 988 mediaType_1 := "test" 989 mediaType_2 := "test_2" 990 desc_1 := ocispec.Descriptor{ 991 MediaType: mediaType_1, 992 Digest: digest.FromBytes(content_1), 993 Size: int64(len(content_1)), 994 Annotations: map[string]string{ 995 ocispec.AnnotationTitle: name_1, 996 }, 997 } 998 999 tempDir := t.TempDir() 1000 path_1 := filepath.Join(tempDir, name_1) 1001 if err := os.WriteFile(path_1, content_1, 0444); err != nil { 1002 t.Fatal("error calling WriteFile(), error =", err) 1003 } 1004 1005 s, err := New(tempDir) 1006 if err != nil { 1007 t.Fatal("Store.New() error =", err) 1008 } 1009 defer s.Close() 1010 ctx := context.Background() 1011 1012 // test add 1013 gotDesc, err := s.Add(ctx, name_1, mediaType_1, path_1) 1014 if err != nil { 1015 t.Fatal("Store.Add() error =", err) 1016 } 1017 if descriptor.FromOCI(gotDesc) != descriptor.FromOCI(desc_1) { 1018 t.Fatal("got descriptor mismatch") 1019 } 1020 1021 // test exists 1022 exists, err := s.Exists(ctx, gotDesc) 1023 if err != nil { 1024 t.Fatal("Store.Exists() error =", err) 1025 } 1026 if !exists { 1027 t.Errorf("Store.Exists() = %v, want %v", exists, true) 1028 } 1029 1030 // test fetch 1031 rc, err := s.Fetch(ctx, gotDesc) 1032 if err != nil { 1033 t.Fatal("Store.Fetch() error =", err) 1034 } 1035 got, err := io.ReadAll(rc) 1036 if err != nil { 1037 t.Fatal("Store.Fetch().Read() error =", err) 1038 } 1039 err = rc.Close() 1040 if err != nil { 1041 t.Error("Store.Fetch().Close() error =", err) 1042 } 1043 if !bytes.Equal(got, content_1) { 1044 t.Errorf("Store.Fetch() = %v, want %v", got, content_1) 1045 } 1046 1047 // test add duplicate name 1048 path_2 := filepath.Join(tempDir, name_2) 1049 if err := os.WriteFile(path_2, content_2, 0444); err != nil { 1050 t.Fatal("error calling WriteFile(), error =", err) 1051 } 1052 1053 if _, err := s.Add(ctx, name_1, mediaType_2, path_2); !errors.Is(err, ErrDuplicateName) { 1054 t.Errorf("Store.Add() = %v, want %v", err, ErrDuplicateName) 1055 } 1056 } 1057 1058 func TestStore_File_Add_MissingName(t *testing.T) { 1059 content := []byte("hello world") 1060 name := "test.txt" 1061 mediaType := "test" 1062 1063 tempDir := t.TempDir() 1064 path := filepath.Join(tempDir, name) 1065 if err := os.WriteFile(path, content, 0444); err != nil { 1066 t.Fatal("error calling WriteFile(), error =", err) 1067 } 1068 1069 s, err := New(tempDir) 1070 if err != nil { 1071 t.Fatal("Store.New() error =", err) 1072 } 1073 defer s.Close() 1074 ctx := context.Background() 1075 1076 // test add with empty name 1077 _, err = s.Add(ctx, "", mediaType, path) 1078 if !errors.Is(err, ErrMissingName) { 1079 t.Errorf("Store.Add() error = %v, want %v", err, ErrMissingName) 1080 } 1081 } 1082 1083 func TestStore_File_Add_SameContent(t *testing.T) { 1084 mediaType := "test" 1085 content := []byte("hello world") 1086 1087 name_1 := "test_1.txt" 1088 desc_1 := ocispec.Descriptor{ 1089 MediaType: mediaType, 1090 Digest: digest.FromBytes(content), 1091 Size: int64(len(content)), 1092 Annotations: map[string]string{ 1093 ocispec.AnnotationTitle: name_1, 1094 }, 1095 } 1096 1097 name_2 := "test_2.txt" 1098 desc_2 := ocispec.Descriptor{ 1099 MediaType: mediaType, 1100 Digest: digest.FromBytes(content), 1101 Size: int64(len(content)), 1102 Annotations: map[string]string{ 1103 ocispec.AnnotationTitle: name_2, 1104 }, 1105 } 1106 1107 tempDir := t.TempDir() 1108 path_1 := filepath.Join(tempDir, name_1) 1109 if err := os.WriteFile(path_1, content, 0444); err != nil { 1110 t.Fatal("error calling WriteFile(), error =", err) 1111 } 1112 path_2 := filepath.Join(tempDir, name_2) 1113 if err := os.WriteFile(path_2, content, 0444); err != nil { 1114 t.Fatal("error calling WriteFile(), error =", err) 1115 } 1116 1117 s, err := New(tempDir) 1118 if err != nil { 1119 t.Fatal("Store.New() error =", err) 1120 } 1121 defer s.Close() 1122 ctx := context.Background() 1123 1124 // test add 1125 gotDesc_1, err := s.Add(ctx, name_1, mediaType, path_1) 1126 if err != nil { 1127 t.Fatal("Store.Add() error =", err) 1128 } 1129 if descriptor.FromOCI(gotDesc_1) != descriptor.FromOCI(desc_1) { 1130 t.Fatal("got descriptor mismatch") 1131 } 1132 1133 gotDesc_2, err := s.Add(ctx, name_2, mediaType, path_2) 1134 if err != nil { 1135 t.Fatal("Store.Add() error =", err) 1136 } 1137 if descriptor.FromOCI(gotDesc_2) != descriptor.FromOCI(desc_2) { 1138 t.Fatal("got descriptor mismatch") 1139 } 1140 1141 // test exists 1142 exists, err := s.Exists(ctx, gotDesc_1) 1143 if err != nil { 1144 t.Fatal("Store.Exists() error =", err) 1145 } 1146 if !exists { 1147 t.Errorf("Store.Exists() = %v, want %v", exists, true) 1148 } 1149 1150 exists, err = s.Exists(ctx, gotDesc_2) 1151 if err != nil { 1152 t.Fatal("Store.Exists() error =", err) 1153 } 1154 if !exists { 1155 t.Errorf("Store.Exists() = %v, want %v", exists, true) 1156 } 1157 1158 // test fetch 1159 rc_1, err := s.Fetch(ctx, gotDesc_1) 1160 if err != nil { 1161 t.Fatal("Store.Fetch() error =", err) 1162 } 1163 got_1, err := io.ReadAll(rc_1) 1164 if err != nil { 1165 t.Fatal("Store.Fetch().Read() error =", err) 1166 } 1167 err = rc_1.Close() 1168 if err != nil { 1169 t.Error("Store.Fetch().Close() error =", err) 1170 } 1171 if !bytes.Equal(got_1, content) { 1172 t.Errorf("Store.Fetch() = %v, want %v", got_1, content) 1173 } 1174 1175 rc_2, err := s.Fetch(ctx, gotDesc_2) 1176 if err != nil { 1177 t.Fatal("Store.Fetch() error =", err) 1178 } 1179 got_2, err := io.ReadAll(rc_2) 1180 if err != nil { 1181 t.Fatal("Store.Fetch().Read() error =", err) 1182 } 1183 err = rc_2.Close() 1184 if err != nil { 1185 t.Error("Store.Fetch().Close() error =", err) 1186 } 1187 if !bytes.Equal(got_2, content) { 1188 t.Errorf("Store.Fetch() = %v, want %v", got_2, content) 1189 } 1190 } 1191 1192 func TestStore_File_Push_SameContent(t *testing.T) { 1193 mediaType := "test" 1194 content := []byte("hello world") 1195 1196 name_1 := "test_1.txt" 1197 desc_1 := ocispec.Descriptor{ 1198 MediaType: mediaType, 1199 Digest: digest.FromBytes(content), 1200 Size: int64(len(content)), 1201 Annotations: map[string]string{ 1202 ocispec.AnnotationTitle: name_1, 1203 }, 1204 } 1205 1206 name_2 := "test_2.txt" 1207 desc_2 := ocispec.Descriptor{ 1208 MediaType: mediaType, 1209 Digest: digest.FromBytes(content), 1210 Size: int64(len(content)), 1211 Annotations: map[string]string{ 1212 ocispec.AnnotationTitle: name_2, 1213 }, 1214 } 1215 1216 tempDir := t.TempDir() 1217 s, err := New(tempDir) 1218 if err != nil { 1219 t.Fatal("Store.New() error =", err) 1220 } 1221 defer s.Close() 1222 ctx := context.Background() 1223 1224 // test push 1225 if err := s.Push(ctx, desc_1, bytes.NewReader(content)); err != nil { 1226 t.Fatal("Store.Push() error =", err) 1227 } 1228 if err := s.Push(ctx, desc_2, bytes.NewReader(content)); err != nil { 1229 t.Fatal("Store.Push() error =", err) 1230 } 1231 1232 // test exists 1233 exists, err := s.Exists(ctx, desc_1) 1234 if err != nil { 1235 t.Fatal("Store.Exists() error =", err) 1236 } 1237 if !exists { 1238 t.Errorf("Store.Exists() = %v, want %v", exists, true) 1239 } 1240 1241 exists, err = s.Exists(ctx, desc_2) 1242 if err != nil { 1243 t.Fatal("Store.Exists() error =", err) 1244 } 1245 if !exists { 1246 t.Errorf("Store.Exists() = %v, want %v", exists, true) 1247 } 1248 1249 // test fetch 1250 rc_1, err := s.Fetch(ctx, desc_1) 1251 if err != nil { 1252 t.Fatal("Store.Fetch() error =", err) 1253 } 1254 got_1, err := io.ReadAll(rc_1) 1255 if err != nil { 1256 t.Fatal("Store.Fetch().Read() error =", err) 1257 } 1258 err = rc_1.Close() 1259 if err != nil { 1260 t.Error("Store.Fetch().Close() error =", err) 1261 } 1262 if !bytes.Equal(got_1, content) { 1263 t.Errorf("Store.Fetch() = %v, want %v", got_1, content) 1264 } 1265 1266 rc_2, err := s.Fetch(ctx, desc_2) 1267 if err != nil { 1268 t.Fatal("Store.Fetch() error =", err) 1269 } 1270 got_2, err := io.ReadAll(rc_2) 1271 if err != nil { 1272 t.Fatal("Store.Fetch().Read() error =", err) 1273 } 1274 err = rc_2.Close() 1275 if err != nil { 1276 t.Error("Store.Fetch().Close() error =", err) 1277 } 1278 if !bytes.Equal(got_2, content) { 1279 t.Errorf("Store.Fetch() = %v, want %v", got_2, content) 1280 } 1281 } 1282 1283 func TestStore_File_Push_DuplicateName(t *testing.T) { 1284 mediaType := "test" 1285 name := "test.txt" 1286 content_1 := []byte("hello world") 1287 content_2 := []byte("goodbye world") 1288 desc_1 := ocispec.Descriptor{ 1289 MediaType: mediaType, 1290 Digest: digest.FromBytes(content_1), 1291 Size: int64(len(content_1)), 1292 Annotations: map[string]string{ 1293 ocispec.AnnotationTitle: name, 1294 }, 1295 } 1296 desc_2 := ocispec.Descriptor{ 1297 MediaType: mediaType, 1298 Digest: digest.FromBytes(content_2), 1299 Size: int64(len(content_2)), 1300 Annotations: map[string]string{ 1301 ocispec.AnnotationTitle: name, 1302 }, 1303 } 1304 1305 tempDir := t.TempDir() 1306 s, err := New(tempDir) 1307 if err != nil { 1308 t.Fatal("Store.New() error =", err) 1309 } 1310 defer s.Close() 1311 ctx := context.Background() 1312 1313 // test push 1314 err = s.Push(ctx, desc_1, bytes.NewReader(content_1)) 1315 if err != nil { 1316 t.Fatal("Store.Push() error =", err) 1317 } 1318 1319 // test exists 1320 exists, err := s.Exists(ctx, desc_1) 1321 if err != nil { 1322 t.Fatal("Store.Exists() error =", err) 1323 } 1324 if !exists { 1325 t.Errorf("Store.Exists() = %v, want %v", exists, true) 1326 } 1327 1328 // test fetch 1329 rc, err := s.Fetch(ctx, desc_1) 1330 if err != nil { 1331 t.Fatal("Store.Fetch() error =", err) 1332 } 1333 got, err := io.ReadAll(rc) 1334 if err != nil { 1335 t.Fatal("Store.Fetch().Read() error =", err) 1336 } 1337 err = rc.Close() 1338 if err != nil { 1339 t.Error("Store.Fetch().Close() error =", err) 1340 } 1341 if !bytes.Equal(got, content_1) { 1342 t.Errorf("Store.Fetch() = %v, want %v", got, content_1) 1343 } 1344 1345 // test push with duplicate name 1346 err = s.Push(ctx, desc_2, bytes.NewBuffer(content_2)) 1347 if !errors.Is(err, ErrDuplicateName) { 1348 t.Errorf("Store.Push() error = %v, want %v", err, ErrDuplicateName) 1349 } 1350 } 1351 1352 func TestStore_File_Push_ForceCAS(t *testing.T) { 1353 mediaType := "test" 1354 content := []byte("hello world") 1355 desc1 := ocispec.Descriptor{ 1356 MediaType: mediaType, 1357 Digest: digest.FromBytes(content), 1358 Size: int64(len(content)), 1359 Annotations: map[string]string{ 1360 ocispec.AnnotationTitle: "blob1", 1361 }, 1362 } 1363 desc2 := ocispec.Descriptor{ 1364 MediaType: mediaType, 1365 Digest: digest.FromBytes(content), 1366 Size: int64(len(content)), 1367 Annotations: map[string]string{ 1368 ocispec.AnnotationTitle: "blob2", 1369 }, 1370 } 1371 config := []byte("{}") 1372 configDesc := ocispec.Descriptor{ 1373 MediaType: ocispec.MediaTypeImageConfig, 1374 Digest: digest.FromBytes(config), 1375 Size: int64(len(config)), 1376 } 1377 manifest := ocispec.Manifest{ 1378 MediaType: ocispec.MediaTypeImageManifest, 1379 Config: configDesc, 1380 Layers: []ocispec.Descriptor{desc1, desc2}, 1381 } 1382 manifestJSON, err := json.Marshal(manifest) 1383 if err != nil { 1384 t.Fatal("json.Marshal() error =", err) 1385 } 1386 manifestDesc := ocispec.Descriptor{ 1387 MediaType: ocispec.MediaTypeImageManifest, 1388 Digest: digest.FromBytes(manifestJSON), 1389 Size: int64(len(manifestJSON)), 1390 } 1391 tempDir := t.TempDir() 1392 s, err := New(tempDir) 1393 if err != nil { 1394 t.Fatal("Store.New() error =", err) 1395 } 1396 s.ForceCAS = true 1397 defer s.Close() 1398 ctx := context.Background() 1399 1400 // push blob1 1401 if err := s.Push(ctx, desc1, bytes.NewReader(content)); err != nil { 1402 t.Fatal("Store.Push() error =", err) 1403 } 1404 // push manifest 1405 if err := s.Push(ctx, manifestDesc, bytes.NewReader(manifestJSON)); err != nil { 1406 t.Fatal("Store.Push() error =", err) 1407 } 1408 1409 // verify blob2 not exists 1410 exists, err := s.Exists(ctx, desc2) 1411 if err != nil { 1412 t.Fatal("Store.Exists() error =", err) 1413 } 1414 if exists { 1415 t.Error("Blob2 is restored") 1416 } 1417 } 1418 1419 func TestStore_File_Push_RestoreDuplicates(t *testing.T) { 1420 mediaType := "test" 1421 content := []byte("hello world") 1422 desc1 := ocispec.Descriptor{ 1423 MediaType: mediaType, 1424 Digest: digest.FromBytes(content), 1425 Size: int64(len(content)), 1426 Annotations: map[string]string{ 1427 ocispec.AnnotationTitle: "blob1", 1428 }, 1429 } 1430 desc2 := ocispec.Descriptor{ 1431 MediaType: mediaType, 1432 Digest: digest.FromBytes(content), 1433 Size: int64(len(content)), 1434 Annotations: map[string]string{ 1435 ocispec.AnnotationTitle: "blob2", 1436 }, 1437 } 1438 config := []byte("{}") 1439 configDesc := ocispec.Descriptor{ 1440 MediaType: ocispec.MediaTypeImageConfig, 1441 Digest: digest.FromBytes(config), 1442 Size: int64(len(config)), 1443 } 1444 manifest := ocispec.Manifest{ 1445 MediaType: ocispec.MediaTypeImageManifest, 1446 Config: configDesc, 1447 Layers: []ocispec.Descriptor{desc1, desc2}, 1448 } 1449 manifestJSON, err := json.Marshal(manifest) 1450 if err != nil { 1451 t.Fatal("json.Marshal() error =", err) 1452 } 1453 manifestDesc := ocispec.Descriptor{ 1454 MediaType: ocispec.MediaTypeImageManifest, 1455 Digest: digest.FromBytes(manifestJSON), 1456 Size: int64(len(manifestJSON)), 1457 } 1458 tempDir := t.TempDir() 1459 s, err := New(tempDir) 1460 if err != nil { 1461 t.Fatal("Store.New() error =", err) 1462 } 1463 defer s.Close() 1464 ctx := context.Background() 1465 1466 // push blob1 1467 if err := s.Push(ctx, desc1, bytes.NewReader(content)); err != nil { 1468 t.Fatal("Store.Push() error =", err) 1469 } 1470 // push manifest 1471 if err := s.Push(ctx, manifestDesc, bytes.NewReader(manifestJSON)); err != nil { 1472 t.Fatal("Store.Push() error =", err) 1473 } 1474 1475 // verify blob2 is restored 1476 exists, err := s.Exists(ctx, desc2) 1477 if err != nil { 1478 t.Fatal("Store.Exists() error =", err) 1479 } 1480 if !exists { 1481 t.Error("Blob2 is not restored") 1482 } 1483 rc, err := s.Fetch(ctx, desc2) 1484 if err != nil { 1485 t.Fatal("Store.Fetch() error =", err) 1486 } 1487 got, err := io.ReadAll(rc) 1488 if err != nil { 1489 t.Fatal("Store.Fetch().Read() error =", err) 1490 } 1491 err = rc.Close() 1492 if err != nil { 1493 t.Error("Store.Fetch().Close() error =", err) 1494 } 1495 if !bytes.Equal(got, content) { 1496 t.Errorf("Store.Fetch() = %v, want %v", got, content) 1497 } 1498 } 1499 1500 func TestStore_File_Push_RestoreDuplicates_NotFound(t *testing.T) { 1501 mediaType := "test" 1502 content := []byte("hello world") 1503 desc := ocispec.Descriptor{ 1504 MediaType: mediaType, 1505 Digest: digest.FromBytes(content), 1506 Size: int64(len(content)), 1507 Annotations: map[string]string{ 1508 ocispec.AnnotationTitle: "blob1", 1509 }, 1510 } 1511 config := []byte("{}") 1512 configDesc := ocispec.Descriptor{ 1513 MediaType: ocispec.MediaTypeImageConfig, 1514 Digest: digest.FromBytes(config), 1515 Size: int64(len(config)), 1516 } 1517 manifest := ocispec.Manifest{ 1518 MediaType: ocispec.MediaTypeImageManifest, 1519 Config: configDesc, 1520 Layers: []ocispec.Descriptor{desc}, 1521 } 1522 manifestJSON, err := json.Marshal(manifest) 1523 if err != nil { 1524 t.Fatal("json.Marshal() error =", err) 1525 } 1526 manifestDesc := ocispec.Descriptor{ 1527 MediaType: ocispec.MediaTypeImageManifest, 1528 Digest: digest.FromBytes(manifestJSON), 1529 Size: int64(len(manifestJSON)), 1530 } 1531 tempDir := t.TempDir() 1532 s, err := New(tempDir) 1533 if err != nil { 1534 t.Fatal("Store.New() error =", err) 1535 } 1536 defer s.Close() 1537 ctx := context.Background() 1538 1539 // push manifest before blob is fine 1540 if err := s.Push(ctx, manifestDesc, bytes.NewReader(manifestJSON)); err != nil { 1541 t.Error("Store.Push(): error = ", err) 1542 } 1543 } 1544 1545 func TestStore_File_Fetch_SameDigest_NoName(t *testing.T) { 1546 mediaType := "test" 1547 content := []byte("hello world") 1548 1549 name_1 := "test_1.txt" 1550 desc_1 := ocispec.Descriptor{ 1551 MediaType: mediaType, 1552 Digest: digest.FromBytes(content), 1553 Size: int64(len(content)), 1554 Annotations: map[string]string{ 1555 ocispec.AnnotationTitle: name_1, 1556 }, 1557 } 1558 1559 desc_2 := ocispec.Descriptor{ 1560 MediaType: mediaType, 1561 Digest: digest.FromBytes(content), 1562 Size: int64(len(content)), 1563 } 1564 1565 tempDir := t.TempDir() 1566 s, err := New(tempDir) 1567 if err != nil { 1568 t.Fatal("Store.New() error =", err) 1569 } 1570 defer s.Close() 1571 ctx := context.Background() 1572 1573 // test push 1574 if err := s.Push(ctx, desc_1, bytes.NewReader(content)); err != nil { 1575 t.Fatal("Store.Push() error =", err) 1576 } 1577 if err := s.Push(ctx, desc_2, bytes.NewReader(content)); err != nil { 1578 t.Fatal("Store.Push() error =", err) 1579 } 1580 1581 // test exists 1582 exists, err := s.Exists(ctx, desc_1) 1583 if err != nil { 1584 t.Fatal("Store.Exists() error =", err) 1585 } 1586 if !exists { 1587 t.Errorf("Store.Exists() = %v, want %v", exists, true) 1588 } 1589 1590 exists, err = s.Exists(ctx, desc_2) 1591 if err != nil { 1592 t.Fatal("Store.Exists() error =", err) 1593 } 1594 if !exists { 1595 t.Errorf("Store.Exists() = %v, want %v", exists, true) 1596 } 1597 1598 // test fetch 1599 rc_1, err := s.Fetch(ctx, desc_1) 1600 if err != nil { 1601 t.Fatal("Store.Fetch() error =", err) 1602 } 1603 got_1, err := io.ReadAll(rc_1) 1604 if err != nil { 1605 t.Fatal("Store.Fetch().Read() error =", err) 1606 } 1607 err = rc_1.Close() 1608 if err != nil { 1609 t.Error("Store.Fetch().Close() error =", err) 1610 } 1611 if !bytes.Equal(got_1, content) { 1612 t.Errorf("Store.Fetch() = %v, want %v", got_1, content) 1613 } 1614 1615 rc_2, err := s.Fetch(ctx, desc_2) 1616 if err != nil { 1617 t.Fatal("Store.Fetch() error =", err) 1618 } 1619 got_2, err := io.ReadAll(rc_2) 1620 if err != nil { 1621 t.Fatal("Store.Fetch().Read() error =", err) 1622 } 1623 err = rc_2.Close() 1624 if err != nil { 1625 t.Error("Store.Fetch().Close() error =", err) 1626 } 1627 if !bytes.Equal(got_2, content) { 1628 t.Errorf("Store.Fetch() = %v, want %v", got_2, content) 1629 } 1630 } 1631 1632 func TestStore_File_Fetch_SameDigest_DifferentName(t *testing.T) { 1633 mediaType := "test" 1634 content := []byte("hello world") 1635 1636 name_1 := "test_1.txt" 1637 desc_1 := ocispec.Descriptor{ 1638 MediaType: mediaType, 1639 Digest: digest.FromBytes(content), 1640 Size: int64(len(content)), 1641 Annotations: map[string]string{ 1642 ocispec.AnnotationTitle: name_1, 1643 }, 1644 } 1645 1646 name_2 := "test_2.txt" 1647 desc_2 := ocispec.Descriptor{ 1648 MediaType: mediaType, 1649 Digest: digest.FromBytes(content), 1650 Size: int64(len(content)), 1651 Annotations: map[string]string{ 1652 ocispec.AnnotationTitle: name_2, 1653 }, 1654 } 1655 1656 tempDir := t.TempDir() 1657 s, err := New(tempDir) 1658 if err != nil { 1659 t.Fatal("Store.New() error =", err) 1660 } 1661 defer s.Close() 1662 ctx := context.Background() 1663 1664 // test desc_1 1665 if err := s.Push(ctx, desc_1, bytes.NewReader(content)); err != nil { 1666 t.Fatal("Store.Push() error =", err) 1667 } 1668 1669 exists, err := s.Exists(ctx, desc_1) 1670 if err != nil { 1671 t.Fatal("Store.Exists() error =", err) 1672 } 1673 if !exists { 1674 t.Errorf("Store.Exists() = %v, want %v", exists, true) 1675 } 1676 1677 rc_1, err := s.Fetch(ctx, desc_1) 1678 if err != nil { 1679 t.Fatal("Store.Fetch() error =", err) 1680 } 1681 got_1, err := io.ReadAll(rc_1) 1682 if err != nil { 1683 t.Fatal("Store.Fetch().Read() error =", err) 1684 } 1685 err = rc_1.Close() 1686 if err != nil { 1687 t.Error("Store.Fetch().Close() error =", err) 1688 } 1689 if !bytes.Equal(got_1, content) { 1690 t.Errorf("Store.Fetch() = %v, want %v", got_1, content) 1691 } 1692 1693 // test desc_2 1694 exists, err = s.Exists(ctx, desc_2) 1695 if err != nil { 1696 t.Fatal("Store.Exists() error =", err) 1697 } 1698 if exists { 1699 t.Errorf("Store.Exists() = %v, want %v", exists, false) 1700 } 1701 1702 _, err = s.Fetch(ctx, desc_2) 1703 if !errors.Is(err, errdef.ErrNotFound) { 1704 t.Errorf("Store.Fetch() error = %v, want %v", err, errdef.ErrNotFound) 1705 } 1706 } 1707 1708 func TestStore_File_Push_Overwrite(t *testing.T) { 1709 mediaType := "test" 1710 name := "test.txt" 1711 old_content := []byte("hello world") 1712 new_content := []byte("goodbye world") 1713 desc := ocispec.Descriptor{ 1714 MediaType: mediaType, 1715 Digest: digest.FromBytes(new_content), 1716 Size: int64(len(new_content)), 1717 Annotations: map[string]string{ 1718 ocispec.AnnotationTitle: name, 1719 }, 1720 } 1721 1722 tempDir := t.TempDir() 1723 path := filepath.Join(tempDir, name) 1724 if err := os.WriteFile(path, old_content, 0666); err != nil { 1725 t.Fatal("error calling WriteFile(), error =", err) 1726 } 1727 1728 s, err := New(tempDir) 1729 if err != nil { 1730 t.Fatal("Store.New() error =", err) 1731 } 1732 defer s.Close() 1733 ctx := context.Background() 1734 1735 // test push 1736 err = s.Push(ctx, desc, bytes.NewReader(new_content)) 1737 if err != nil { 1738 t.Fatal("Store.Push() error =", err) 1739 } 1740 1741 // test exists 1742 exists, err := s.Exists(ctx, desc) 1743 if err != nil { 1744 t.Fatal("Store.Exists() error =", err) 1745 } 1746 if !exists { 1747 t.Errorf("Store.Exists() = %v, want %v", exists, true) 1748 } 1749 1750 // test fetch 1751 rc, err := s.Fetch(ctx, desc) 1752 if err != nil { 1753 t.Fatal("Store.Fetch() error =", err) 1754 } 1755 got, err := io.ReadAll(rc) 1756 if err != nil { 1757 t.Fatal("Store.Fetch().Read() error =", err) 1758 } 1759 err = rc.Close() 1760 if err != nil { 1761 t.Error("Store.Fetch().Close() error =", err) 1762 } 1763 if !bytes.Equal(got, new_content) { 1764 t.Errorf("Store.Fetch() = %v, want %v", got, new_content) 1765 } 1766 1767 } 1768 1769 func TestStore_File_Push_DisableOverwrite(t *testing.T) { 1770 content := []byte("hello world") 1771 name := "test.txt" 1772 desc := ocispec.Descriptor{ 1773 MediaType: "test", 1774 Digest: digest.FromBytes(content), 1775 Size: int64(len(content)), 1776 Annotations: map[string]string{ 1777 ocispec.AnnotationTitle: name, 1778 }, 1779 } 1780 1781 tempDir := t.TempDir() 1782 path := filepath.Join(tempDir, name) 1783 if err := os.WriteFile(path, content, 0444); err != nil { 1784 t.Fatal("error calling WriteFile(), error =", err) 1785 } 1786 1787 s, err := New(tempDir) 1788 if err != nil { 1789 t.Fatal("Store.New() error =", err) 1790 } 1791 defer s.Close() 1792 s.DisableOverwrite = true 1793 1794 ctx := context.Background() 1795 err = s.Push(ctx, desc, bytes.NewReader(content)) 1796 if !errors.Is(err, ErrOverwriteDisallowed) { 1797 t.Errorf("Store.Push() error = %v, want %v", err, ErrOverwriteDisallowed) 1798 } 1799 } 1800 1801 func TestStore_File_Push_IgnoreNoName(t *testing.T) { 1802 config := []byte("{}") 1803 configDesc := ocispec.Descriptor{ 1804 MediaType: "config", 1805 Digest: digest.FromBytes(config), 1806 Size: int64(len(config)), 1807 } 1808 manifest := ocispec.Manifest{ 1809 MediaType: ocispec.MediaTypeImageManifest, 1810 Config: configDesc, 1811 Layers: []ocispec.Descriptor{}, 1812 } 1813 manifestJSON, err := json.Marshal(manifest) 1814 if err != nil { 1815 t.Fatal("json.Marshal() error =", err) 1816 } 1817 manifestDesc := ocispec.Descriptor{ 1818 MediaType: ocispec.MediaTypeImageManifest, 1819 Digest: digest.FromBytes(manifestJSON), 1820 Size: int64(len(manifestJSON)), 1821 } 1822 tempDir := t.TempDir() 1823 s, err := New(tempDir) 1824 if err != nil { 1825 t.Fatal("Store.New() error =", err) 1826 } 1827 defer s.Close() 1828 s.IgnoreNoName = true 1829 1830 // push an OCI manifest 1831 ctx := context.Background() 1832 err = s.Push(ctx, manifestDesc, bytes.NewReader(manifestJSON)) 1833 if err != nil { 1834 t.Fatal("Store.Push() error = ", err) 1835 } 1836 1837 // verify the manifest is not saved 1838 exists, err := s.Exists(ctx, manifestDesc) 1839 if err != nil { 1840 t.Fatal("Store.Exists() error =", err) 1841 } 1842 if exists { 1843 t.Errorf("Unnamed manifest is saved in file store") 1844 } 1845 // verify the manifest is not indexed 1846 predecessors, err := s.Predecessors(ctx, configDesc) 1847 if err != nil { 1848 t.Fatal("Store.Predecessors() error = ", err) 1849 } 1850 if len(predecessors) != 0 { 1851 t.Errorf("Unnamed manifest is indexed in file store") 1852 } 1853 } 1854 1855 func TestStore_File_Push_DisallowPathTraversal(t *testing.T) { 1856 content := []byte("hello world") 1857 name := "../test.txt" 1858 desc := ocispec.Descriptor{ 1859 MediaType: "test", 1860 Digest: digest.FromBytes(content), 1861 Size: int64(len(content)), 1862 Annotations: map[string]string{ 1863 ocispec.AnnotationTitle: name, 1864 }, 1865 } 1866 1867 tempDir := t.TempDir() 1868 s, err := New(tempDir) 1869 if err != nil { 1870 t.Fatal("Store.New() error =", err) 1871 } 1872 defer s.Close() 1873 1874 ctx := context.Background() 1875 err = s.Push(ctx, desc, bytes.NewReader(content)) 1876 if !errors.Is(err, ErrPathTraversalDisallowed) { 1877 t.Errorf("Store.Push() error = %v, want %v", err, ErrPathTraversalDisallowed) 1878 } 1879 } 1880 1881 func TestStore_Dir_Push_DisallowPathTraversal(t *testing.T) { 1882 tempDir := t.TempDir() 1883 dirName := "../testdir" 1884 dirPath := filepath.Join(tempDir, dirName) 1885 if err := os.MkdirAll(dirPath, 0777); err != nil { 1886 t.Fatal("error calling Mkdir(), error =", err) 1887 } 1888 1889 content := []byte("hello world") 1890 fileName := "test.txt" 1891 if err := os.WriteFile(filepath.Join(dirPath, fileName), content, 0444); err != nil { 1892 t.Fatal("error calling WriteFile(), error =", err) 1893 } 1894 1895 s, err := New(tempDir) 1896 if err != nil { 1897 t.Fatal("Store.New() error =", err) 1898 } 1899 defer s.Close() 1900 ctx := context.Background() 1901 1902 // test add 1903 desc, err := s.Add(ctx, dirName, "", dirPath) 1904 if err != nil { 1905 t.Fatal("Store.Add() error=", err) 1906 } 1907 1908 val, ok := s.digestToPath.Load(desc.Digest) 1909 if !ok { 1910 t.Fatal("failed to find internal gz") 1911 } 1912 tmpPath := val.(string) 1913 zrc, err := os.Open(tmpPath) 1914 if err != nil { 1915 t.Fatal("failed to open internal gz") 1916 } 1917 gz, err := io.ReadAll(zrc) 1918 if err != nil { 1919 t.Fatal("failed to read internal gz") 1920 } 1921 if err := zrc.Close(); err != nil { 1922 t.Fatal("failed to close internal gz reader") 1923 } 1924 1925 anotherTempDir := t.TempDir() 1926 // test with another file store instance to mock push gz 1927 anotherS, err := New(anotherTempDir) 1928 if err != nil { 1929 t.Fatal("Store.New() error =", err) 1930 } 1931 defer anotherS.Close() 1932 1933 // test push 1934 err = anotherS.Push(ctx, desc, bytes.NewReader(gz)) 1935 if !errors.Is(err, ErrPathTraversalDisallowed) { 1936 t.Errorf("Store.Push() error = %v, want %v", err, ErrPathTraversalDisallowed) 1937 } 1938 } 1939 1940 func TestStore_File_Push_PathTraversal(t *testing.T) { 1941 content := []byte("hello world") 1942 name := "../test.txt" 1943 desc := ocispec.Descriptor{ 1944 MediaType: "test", 1945 Digest: digest.FromBytes(content), 1946 Size: int64(len(content)), 1947 Annotations: map[string]string{ 1948 ocispec.AnnotationTitle: name, 1949 }, 1950 } 1951 1952 tempDir := t.TempDir() 1953 subTempDir, err := os.MkdirTemp(tempDir, "oras_filestore_*") 1954 if err != nil { 1955 t.Fatal("error creating temp dir, error =", err) 1956 } 1957 1958 s, err := New(subTempDir) 1959 if err != nil { 1960 t.Fatal("Store.New() error =", err) 1961 } 1962 defer s.Close() 1963 s.AllowPathTraversalOnWrite = true 1964 1965 ctx := context.Background() 1966 // test push 1967 err = s.Push(ctx, desc, bytes.NewReader(content)) 1968 if err != nil { 1969 t.Fatal("Store.Push() error =", err) 1970 } 1971 1972 // test exists 1973 exists, err := s.Exists(ctx, desc) 1974 if err != nil { 1975 t.Fatal("Store.Exists() error =", err) 1976 } 1977 if !exists { 1978 t.Errorf("Store.Exists() = %v, want %v", exists, true) 1979 } 1980 1981 // test fetch 1982 rc, err := s.Fetch(ctx, desc) 1983 if err != nil { 1984 t.Fatal("Store.Fetch() error =", err) 1985 } 1986 got, err := io.ReadAll(rc) 1987 if err != nil { 1988 t.Fatal("Store.Fetch().Read() error =", err) 1989 } 1990 err = rc.Close() 1991 if err != nil { 1992 t.Error("Store.Fetch().Close() error =", err) 1993 } 1994 if !bytes.Equal(got, content) { 1995 t.Errorf("Store.Fetch() = %v, want %v", got, content) 1996 } 1997 } 1998 1999 func TestStore_File_Push_Concurrent(t *testing.T) { 2000 content := []byte("hello world") 2001 desc := ocispec.Descriptor{ 2002 MediaType: "test", 2003 Digest: digest.FromBytes(content), 2004 Size: int64(len(content)), 2005 Annotations: map[string]string{ 2006 ocispec.AnnotationTitle: "test.txt", 2007 }, 2008 } 2009 2010 tempDir := t.TempDir() 2011 s, err := New(tempDir) 2012 if err != nil { 2013 t.Fatal("Store.New() error =", err) 2014 } 2015 defer s.Close() 2016 ctx := context.Background() 2017 2018 concurrency := 64 2019 eg, egCtx := errgroup.WithContext(ctx) 2020 for i := 0; i < concurrency; i++ { 2021 eg.Go(func(i int) func() error { 2022 return func() error { 2023 if err := s.Push(egCtx, desc, bytes.NewReader(content)); err != nil { 2024 if errors.Is(err, ErrDuplicateName) { 2025 return nil 2026 } 2027 return fmt.Errorf("failed to push content: %v", err) 2028 } 2029 return nil 2030 } 2031 }(i)) 2032 } 2033 2034 if err := eg.Wait(); err != nil { 2035 t.Fatal(err) 2036 } 2037 2038 exists, err := s.Exists(ctx, desc) 2039 if err != nil { 2040 t.Fatal("Store.Exists() error =", err) 2041 } 2042 if !exists { 2043 t.Errorf("Store.Exists() = %v, want %v", exists, true) 2044 } 2045 2046 rc, err := s.Fetch(ctx, desc) 2047 if err != nil { 2048 t.Fatal("Store.Fetch() error =", err) 2049 } 2050 got, err := io.ReadAll(rc) 2051 if err != nil { 2052 t.Fatal("Store.Fetch().Read() error =", err) 2053 } 2054 err = rc.Close() 2055 if err != nil { 2056 t.Error("Store.Fetch().Close() error =", err) 2057 } 2058 if !bytes.Equal(got, content) { 2059 t.Errorf("Store.Fetch() = %v, want %v", got, content) 2060 } 2061 } 2062 2063 func TestStore_File_Fetch_Concurrent(t *testing.T) { 2064 content := []byte("hello world") 2065 desc := ocispec.Descriptor{ 2066 MediaType: "test", 2067 Digest: digest.FromBytes(content), 2068 Size: int64(len(content)), 2069 Annotations: map[string]string{ 2070 ocispec.AnnotationTitle: "test.txt", 2071 }, 2072 } 2073 2074 tempDir := t.TempDir() 2075 s, err := New(tempDir) 2076 if err != nil { 2077 t.Fatal("Store.New() error =", err) 2078 } 2079 defer s.Close() 2080 ctx := context.Background() 2081 2082 if err := s.Push(ctx, desc, bytes.NewReader(content)); err != nil { 2083 t.Fatal("Store.Push() error =", err) 2084 } 2085 2086 concurrency := 64 2087 eg, egCtx := errgroup.WithContext(ctx) 2088 2089 for i := 0; i < concurrency; i++ { 2090 eg.Go(func(i int) func() error { 2091 return func() error { 2092 rc, err := s.Fetch(egCtx, desc) 2093 if err != nil { 2094 return fmt.Errorf("failed to fetch content: %v", err) 2095 } 2096 got, err := io.ReadAll(rc) 2097 if err != nil { 2098 t.Fatal("Store.Fetch().Read() error =", err) 2099 } 2100 err = rc.Close() 2101 if err != nil { 2102 t.Error("Store.Fetch().Close() error =", err) 2103 } 2104 if !bytes.Equal(got, content) { 2105 t.Errorf("Store.Fetch() = %v, want %v", got, content) 2106 } 2107 return nil 2108 } 2109 }(i)) 2110 } 2111 2112 if err := eg.Wait(); err != nil { 2113 t.Fatal(err) 2114 } 2115 } 2116 2117 func TestStore_TagNotFound(t *testing.T) { 2118 ref := "foobar" 2119 2120 tempDir := t.TempDir() 2121 s, err := New(tempDir) 2122 if err != nil { 2123 t.Fatal("Store.New() error =", err) 2124 } 2125 defer s.Close() 2126 ctx := context.Background() 2127 2128 _, err = s.Resolve(ctx, ref) 2129 if !errors.Is(err, errdef.ErrNotFound) { 2130 t.Errorf("Store.Resolve() error = %v, want %v", err, errdef.ErrNotFound) 2131 } 2132 } 2133 2134 func TestStore_TagUnknownContent(t *testing.T) { 2135 content := []byte(`{"layers":[]}`) 2136 desc := ocispec.Descriptor{ 2137 MediaType: ocispec.MediaTypeImageManifest, 2138 Digest: digest.FromBytes(content), 2139 Size: int64(len(content)), 2140 } 2141 ref := "foobar" 2142 2143 tempDir := t.TempDir() 2144 s, err := New(tempDir) 2145 if err != nil { 2146 t.Fatal("Store.New() error =", err) 2147 } 2148 defer s.Close() 2149 ctx := context.Background() 2150 2151 err = s.Tag(ctx, desc, ref) 2152 if !errors.Is(err, errdef.ErrNotFound) { 2153 t.Errorf("Store.Resolve() error = %v, want %v", err, errdef.ErrNotFound) 2154 } 2155 } 2156 2157 func TestStore_RepeatTag(t *testing.T) { 2158 tempDir := t.TempDir() 2159 s, err := New(tempDir) 2160 if err != nil { 2161 t.Fatal("Store.New() error =", err) 2162 } 2163 defer s.Close() 2164 ctx := context.Background() 2165 2166 generate := func(content []byte) ocispec.Descriptor { 2167 dgst := digest.FromBytes(content) 2168 desc := ocispec.Descriptor{ 2169 MediaType: "test", 2170 Digest: dgst, 2171 Size: int64(len(content)), 2172 Annotations: map[string]string{ 2173 ocispec.AnnotationTitle: dgst.Encoded() + ".blob", 2174 }, 2175 } 2176 return desc 2177 } 2178 ref := "foobar" 2179 2180 // initial tag 2181 content := []byte("hello world") 2182 desc := generate(content) 2183 err = s.Push(ctx, desc, bytes.NewReader(content)) 2184 if err != nil { 2185 t.Fatal("Store.Push() error =", err) 2186 } 2187 2188 err = s.Tag(ctx, desc, ref) 2189 if err != nil { 2190 t.Fatal("Store.Tag() error =", err) 2191 } 2192 2193 gotDesc, err := s.Resolve(ctx, ref) 2194 if err != nil { 2195 t.Fatal("Store.Resolve() error =", err) 2196 } 2197 if !reflect.DeepEqual(gotDesc, desc) { 2198 t.Errorf("Store.Resolve() = %v, want %v", gotDesc, desc) 2199 } 2200 2201 // repeat tag 2202 content = []byte("foo") 2203 desc = generate(content) 2204 err = s.Push(ctx, desc, bytes.NewReader(content)) 2205 if err != nil { 2206 t.Fatal("Store.Push() error =", err) 2207 } 2208 2209 err = s.Tag(ctx, desc, ref) 2210 if err != nil { 2211 t.Fatal("Store.Tag() error =", err) 2212 } 2213 2214 gotDesc, err = s.Resolve(ctx, ref) 2215 if err != nil { 2216 t.Fatal("Store.Resolve() error =", err) 2217 } 2218 if !reflect.DeepEqual(gotDesc, desc) { 2219 t.Errorf("Store.Resolve() = %v, want %v", gotDesc, desc) 2220 } 2221 2222 // repeat tag 2223 content = []byte("bar") 2224 desc = generate(content) 2225 err = s.Push(ctx, desc, bytes.NewReader(content)) 2226 if err != nil { 2227 t.Fatal("Store.Push() error =", err) 2228 } 2229 2230 err = s.Tag(ctx, desc, ref) 2231 if err != nil { 2232 t.Fatal("Store.Tag() error =", err) 2233 } 2234 2235 gotDesc, err = s.Resolve(ctx, ref) 2236 if err != nil { 2237 t.Fatal("Store.Resolve() error =", err) 2238 } 2239 if !reflect.DeepEqual(gotDesc, desc) { 2240 t.Errorf("Store.Resolve() = %v, want %v", gotDesc, desc) 2241 } 2242 } 2243 2244 func TestStore_Predecessors(t *testing.T) { 2245 tempDir := t.TempDir() 2246 s, err := New(tempDir) 2247 if err != nil { 2248 t.Fatal("Store.New() error =", err) 2249 } 2250 defer s.Close() 2251 ctx := context.Background() 2252 2253 // generate test content 2254 var blobs [][]byte 2255 var descs []ocispec.Descriptor 2256 appendBlob := func(mediaType string, blob []byte) { 2257 blobs = append(blobs, blob) 2258 dgst := digest.FromBytes(blob) 2259 descs = append(descs, ocispec.Descriptor{ 2260 MediaType: mediaType, 2261 Digest: dgst, 2262 Size: int64(len(blob)), 2263 Annotations: map[string]string{ 2264 ocispec.AnnotationTitle: dgst.Encoded() + ".blob", 2265 }, 2266 }) 2267 } 2268 generateManifest := func(config ocispec.Descriptor, layers ...ocispec.Descriptor) { 2269 manifest := ocispec.Manifest{ 2270 Config: config, 2271 Layers: layers, 2272 } 2273 manifestJSON, err := json.Marshal(manifest) 2274 if err != nil { 2275 t.Fatal(err) 2276 } 2277 appendBlob(ocispec.MediaTypeImageManifest, manifestJSON) 2278 } 2279 generateIndex := func(manifests ...ocispec.Descriptor) { 2280 index := ocispec.Index{ 2281 Manifests: manifests, 2282 } 2283 indexJSON, err := json.Marshal(index) 2284 if err != nil { 2285 t.Fatal(err) 2286 } 2287 appendBlob(ocispec.MediaTypeImageIndex, indexJSON) 2288 } 2289 generateArtifactManifest := func(subject ocispec.Descriptor, blobs ...ocispec.Descriptor) { 2290 var manifest ocispec.Artifact 2291 manifest.Subject = &subject 2292 manifest.Blobs = append(manifest.Blobs, blobs...) 2293 manifestJSON, err := json.Marshal(manifest) 2294 if err != nil { 2295 t.Fatal(err) 2296 } 2297 appendBlob(ocispec.MediaTypeArtifactManifest, manifestJSON) 2298 } 2299 2300 appendBlob(ocispec.MediaTypeImageConfig, []byte("config")) // Blob 0 2301 appendBlob(ocispec.MediaTypeImageLayer, []byte("foo")) // Blob 1 2302 appendBlob(ocispec.MediaTypeImageLayer, []byte("bar")) // Blob 2 2303 appendBlob(ocispec.MediaTypeImageLayer, []byte("hello")) // Blob 3 2304 generateManifest(descs[0], descs[1:3]...) // Blob 4 2305 generateManifest(descs[0], descs[3]) // Blob 5 2306 generateManifest(descs[0], descs[1:4]...) // Blob 6 2307 generateIndex(descs[4:6]...) // Blob 7 2308 generateIndex(descs[6]) // Blob 8 2309 generateIndex() // Blob 9 2310 generateIndex(descs[7:10]...) // Blob 10 2311 appendBlob(ocispec.MediaTypeImageLayer, []byte("sig_1")) // Blob 11 2312 generateArtifactManifest(descs[6], descs[11]) // Blob 12 2313 appendBlob(ocispec.MediaTypeImageLayer, []byte("sig_2")) // Blob 13 2314 generateArtifactManifest(descs[10], descs[13]) // Blob 14 2315 2316 eg, egCtx := errgroup.WithContext(ctx) 2317 for i := range blobs { 2318 eg.Go(func(i int) func() error { 2319 return func() error { 2320 err := s.Push(egCtx, descs[i], bytes.NewReader(blobs[i])) 2321 if err != nil { 2322 return fmt.Errorf("failed to push test content to src: %d: %v", i, err) 2323 } 2324 return nil 2325 } 2326 }(i)) 2327 } 2328 if err := eg.Wait(); err != nil { 2329 t.Fatal(err) 2330 } 2331 2332 // verify predecessors 2333 wants := [][]ocispec.Descriptor{ 2334 descs[4:7], // Blob 0 2335 {descs[4], descs[6]}, // Blob 1 2336 {descs[4], descs[6]}, // Blob 2 2337 {descs[5], descs[6]}, // Blob 3 2338 {descs[7]}, // Blob 4 2339 {descs[7]}, // Blob 5 2340 {descs[8], descs[12]}, // Blob 6 2341 {descs[10]}, // Blob 7 2342 {descs[10]}, // Blob 8 2343 {descs[10]}, // Blob 9 2344 {descs[14]}, // Blob 10 2345 {descs[12]}, // Blob 11 2346 nil, // Blob 12 2347 {descs[14]}, // Blob 13 2348 nil, // Blob 14 2349 } 2350 for i, want := range wants { 2351 predecessors, err := s.Predecessors(ctx, descs[i]) 2352 if err != nil { 2353 t.Errorf("Store.Predecessors(%d) error = %v", i, err) 2354 } 2355 if !equalDescriptorSet(predecessors, want) { 2356 t.Errorf("Store.Predecessors(%d) = %v, want %v", i, predecessors, want) 2357 } 2358 } 2359 } 2360 2361 func TestCopy_File_MemoryToFile_FullCopy(t *testing.T) { 2362 src := memory.New() 2363 2364 tempDir := t.TempDir() 2365 dst, err := New(tempDir) 2366 if err != nil { 2367 t.Fatal("Store.New() error =", err) 2368 } 2369 defer dst.Close() 2370 2371 // generate test content 2372 var blobs [][]byte 2373 var descs []ocispec.Descriptor 2374 appendBlob := func(mediaType string, blob []byte) { 2375 blobs = append(blobs, blob) 2376 dgst := digest.FromBytes(blob) 2377 descs = append(descs, ocispec.Descriptor{ 2378 MediaType: mediaType, 2379 Digest: dgst, 2380 Size: int64(len(blob)), 2381 Annotations: map[string]string{ 2382 ocispec.AnnotationTitle: dgst.Encoded() + ".blob", 2383 }, 2384 }) 2385 } 2386 generateManifest := func(config ocispec.Descriptor, layers ...ocispec.Descriptor) { 2387 manifest := ocispec.Manifest{ 2388 Config: config, 2389 Layers: layers, 2390 } 2391 manifestJSON, err := json.Marshal(manifest) 2392 if err != nil { 2393 t.Fatal(err) 2394 } 2395 appendBlob(ocispec.MediaTypeImageManifest, manifestJSON) 2396 } 2397 2398 appendBlob(ocispec.MediaTypeImageConfig, []byte("config")) // Blob 0 2399 appendBlob(ocispec.MediaTypeImageLayer, []byte("foo")) // Blob 1 2400 appendBlob(ocispec.MediaTypeImageLayer, []byte("bar")) // Blob 2 2401 generateManifest(descs[0], descs[1:3]...) // Blob 3 2402 2403 ctx := context.Background() 2404 for i := range blobs { 2405 err := src.Push(ctx, descs[i], bytes.NewReader(blobs[i])) 2406 if err != nil { 2407 t.Fatalf("failed to push test content to src: %d: %v", i, err) 2408 } 2409 } 2410 2411 root := descs[3] 2412 ref := "foobar" 2413 err = src.Tag(ctx, root, ref) 2414 if err != nil { 2415 t.Fatal("fail to tag root node", err) 2416 } 2417 2418 // test copy 2419 gotDesc, err := oras.Copy(ctx, src, ref, dst, "", oras.CopyOptions{}) 2420 if err != nil { 2421 t.Fatalf("Copy() error = %v, wantErr %v", err, false) 2422 } 2423 if !reflect.DeepEqual(gotDesc, root) { 2424 t.Errorf("Copy() = %v, want %v", gotDesc, root) 2425 } 2426 2427 // verify contents 2428 for i, desc := range descs { 2429 exists, err := dst.Exists(ctx, desc) 2430 if err != nil { 2431 t.Fatalf("dst.Exists(%d) error = %v", i, err) 2432 } 2433 if !exists { 2434 t.Errorf("dst.Exists(%d) = %v, want %v", i, exists, true) 2435 } 2436 } 2437 2438 // verify tag 2439 gotDesc, err = dst.Resolve(ctx, ref) 2440 if err != nil { 2441 t.Fatal("dst.Resolve() error =", err) 2442 } 2443 if !reflect.DeepEqual(gotDesc, root) { 2444 t.Errorf("dst.Resolve() = %v, want %v", gotDesc, root) 2445 } 2446 } 2447 2448 func TestCopyGraph_MemoryToFile_FullCopy(t *testing.T) { 2449 src := memory.New() 2450 2451 tempDir := t.TempDir() 2452 dst, err := New(tempDir) 2453 if err != nil { 2454 t.Fatal("Store.New() error =", err) 2455 } 2456 defer dst.Close() 2457 2458 // generate test content 2459 var blobs [][]byte 2460 var descs []ocispec.Descriptor 2461 appendBlob := func(mediaType string, blob []byte) { 2462 blobs = append(blobs, blob) 2463 dgst := digest.FromBytes(blob) 2464 descs = append(descs, ocispec.Descriptor{ 2465 MediaType: mediaType, 2466 Digest: dgst, 2467 Size: int64(len(blob)), 2468 Annotations: map[string]string{ 2469 ocispec.AnnotationTitle: dgst.Encoded() + ".blob", 2470 }, 2471 }) 2472 } 2473 generateManifest := func(config ocispec.Descriptor, layers ...ocispec.Descriptor) { 2474 manifest := ocispec.Manifest{ 2475 Config: config, 2476 Layers: layers, 2477 } 2478 manifestJSON, err := json.Marshal(manifest) 2479 if err != nil { 2480 t.Fatal(err) 2481 } 2482 appendBlob(ocispec.MediaTypeImageManifest, manifestJSON) 2483 } 2484 generateIndex := func(manifests ...ocispec.Descriptor) { 2485 index := ocispec.Index{ 2486 Manifests: manifests, 2487 } 2488 indexJSON, err := json.Marshal(index) 2489 if err != nil { 2490 t.Fatal(err) 2491 } 2492 appendBlob(ocispec.MediaTypeImageIndex, indexJSON) 2493 } 2494 2495 appendBlob(ocispec.MediaTypeImageConfig, []byte("config")) // Blob 0 2496 appendBlob(ocispec.MediaTypeImageLayer, []byte("foo")) // Blob 1 2497 appendBlob(ocispec.MediaTypeImageLayer, []byte("bar")) // Blob 2 2498 appendBlob(ocispec.MediaTypeImageLayer, []byte("hello")) // Blob 3 2499 generateManifest(descs[0], descs[1:3]...) // Blob 4 2500 generateManifest(descs[0], descs[3]) // Blob 5 2501 generateManifest(descs[0], descs[1:4]...) // Blob 6 2502 generateIndex(descs[4:6]...) // Blob 7 2503 generateIndex(descs[6]) // Blob 8 2504 generateIndex() // Blob 9 2505 generateIndex(descs[7:10]...) // Blob 10 2506 2507 ctx := context.Background() 2508 for i := range blobs { 2509 err := src.Push(ctx, descs[i], bytes.NewReader(blobs[i])) 2510 if err != nil { 2511 t.Fatalf("failed to push test content to src: %d: %v", i, err) 2512 } 2513 } 2514 2515 // test copy 2516 srcTracker := &storageTracker{Storage: src} 2517 dstTracker := &storageTracker{Storage: dst} 2518 root := descs[len(descs)-1] 2519 if err := oras.CopyGraph(ctx, srcTracker, dstTracker, root, oras.CopyGraphOptions{}); err != nil { 2520 t.Fatalf("CopyGraph() error = %v, wantErr %v", err, false) 2521 } 2522 2523 // verify contents 2524 for i := range blobs { 2525 got, err := content.FetchAll(ctx, dst, descs[i]) 2526 if err != nil { 2527 t.Errorf("content[%d] error = %v, wantErr %v", i, err, false) 2528 continue 2529 } 2530 if want := blobs[i]; !bytes.Equal(got, want) { 2531 t.Errorf("content[%d] = %v, want %v", i, got, want) 2532 } 2533 } 2534 2535 // verify API counts 2536 if got, want := srcTracker.fetch, int64(len(blobs)); got != want { 2537 t.Errorf("count(src.Fetch()) = %v, want %v", got, want) 2538 } 2539 if got, want := srcTracker.push, int64(0); got != want { 2540 t.Errorf("count(src.Push()) = %v, want %v", got, want) 2541 } 2542 if got, want := srcTracker.exists, int64(0); got != want { 2543 t.Errorf("count(src.Exists()) = %v, want %v", got, want) 2544 } 2545 if got, want := dstTracker.fetch, int64(0); got != want { 2546 t.Errorf("count(dst.Fetch()) = %v, want %v", got, want) 2547 } 2548 if got, want := dstTracker.push, int64(len(blobs)); got != want { 2549 t.Errorf("count(dst.Push()) = %v, want %v", got, want) 2550 } 2551 if got, want := dstTracker.exists, int64(len(blobs)); got != want { 2552 t.Errorf("count(dst.Exists()) = %v, want %v", got, want) 2553 } 2554 } 2555 2556 func TestCopyGraph_MemoryToFile_PartialCopy(t *testing.T) { 2557 src := memory.New() 2558 2559 tempDir := t.TempDir() 2560 dst, err := New(tempDir) 2561 if err != nil { 2562 t.Fatal("Store.New() error =", err) 2563 } 2564 defer dst.Close() 2565 2566 // generate test content 2567 var blobs [][]byte 2568 var descs []ocispec.Descriptor 2569 appendBlob := func(mediaType string, blob []byte) { 2570 blobs = append(blobs, blob) 2571 dgst := digest.FromBytes(blob) 2572 descs = append(descs, ocispec.Descriptor{ 2573 MediaType: mediaType, 2574 Digest: dgst, 2575 Size: int64(len(blob)), 2576 Annotations: map[string]string{ 2577 ocispec.AnnotationTitle: dgst.Encoded() + ".blob", 2578 }, 2579 }) 2580 } 2581 generateManifest := func(config ocispec.Descriptor, layers ...ocispec.Descriptor) { 2582 manifest := ocispec.Manifest{ 2583 Config: config, 2584 Layers: layers, 2585 } 2586 manifestJSON, err := json.Marshal(manifest) 2587 if err != nil { 2588 t.Fatal(err) 2589 } 2590 appendBlob(ocispec.MediaTypeImageManifest, manifestJSON) 2591 } 2592 generateIndex := func(manifests ...ocispec.Descriptor) { 2593 index := ocispec.Index{ 2594 Manifests: manifests, 2595 } 2596 indexJSON, err := json.Marshal(index) 2597 if err != nil { 2598 t.Fatal(err) 2599 } 2600 appendBlob(ocispec.MediaTypeImageIndex, indexJSON) 2601 } 2602 2603 appendBlob(ocispec.MediaTypeImageConfig, []byte("config")) // Blob 0 2604 appendBlob(ocispec.MediaTypeImageLayer, []byte("foo")) // Blob 1 2605 appendBlob(ocispec.MediaTypeImageLayer, []byte("bar")) // Blob 2 2606 generateManifest(descs[0], descs[1:3]...) // Blob 3 2607 appendBlob(ocispec.MediaTypeImageLayer, []byte("hello")) // Blob 4 2608 generateManifest(descs[0], descs[4]) // Blob 5 2609 generateIndex(descs[3], descs[5]) // Blob 6 2610 2611 ctx := context.Background() 2612 for i := range blobs { 2613 err := src.Push(ctx, descs[i], bytes.NewReader(blobs[i])) 2614 if err != nil { 2615 t.Fatalf("failed to push test content to src: %d: %v", i, err) 2616 } 2617 } 2618 2619 // initial copy 2620 root := descs[3] 2621 if err := oras.CopyGraph(ctx, src, dst, root, oras.CopyGraphOptions{}); err != nil { 2622 t.Fatalf("CopyGraph() error = %v, wantErr %v", err, false) 2623 } 2624 // verify contents 2625 for i := range blobs[:4] { 2626 got, err := content.FetchAll(ctx, dst, descs[i]) 2627 if err != nil { 2628 t.Fatalf("content[%d] error = %v, wantErr %v", i, err, false) 2629 } 2630 if want := blobs[i]; !bytes.Equal(got, want) { 2631 t.Fatalf("content[%d] = %v, want %v", i, got, want) 2632 } 2633 } 2634 2635 // test copy 2636 srcTracker := &storageTracker{Storage: src} 2637 dstTracker := &storageTracker{Storage: dst} 2638 root = descs[len(descs)-1] 2639 if err := oras.CopyGraph(ctx, srcTracker, dstTracker, root, oras.CopyGraphOptions{}); err != nil { 2640 t.Fatalf("CopyGraph() error = %v, wantErr %v", err, false) 2641 } 2642 2643 // verify contents 2644 for i := range blobs { 2645 got, err := content.FetchAll(ctx, dst, descs[i]) 2646 if err != nil { 2647 t.Errorf("content[%d] error = %v, wantErr %v", i, err, false) 2648 continue 2649 } 2650 if want := blobs[i]; !bytes.Equal(got, want) { 2651 t.Errorf("content[%d] = %v, want %v", i, got, want) 2652 } 2653 } 2654 2655 // verify API counts 2656 if got, want := srcTracker.fetch, int64(3); got != want { 2657 t.Errorf("count(src.Fetch()) = %v, want %v", got, want) 2658 } 2659 if got, want := srcTracker.push, int64(0); got != want { 2660 t.Errorf("count(src.Push()) = %v, want %v", got, want) 2661 } 2662 if got, want := srcTracker.exists, int64(0); got != want { 2663 t.Errorf("count(src.Exists()) = %v, want %v", got, want) 2664 } 2665 if got, want := dstTracker.fetch, int64(0); got != want { 2666 t.Errorf("count(dst.Fetch()) = %v, want %v", got, want) 2667 } 2668 if got, want := dstTracker.push, int64(3); got != want { 2669 t.Errorf("count(dst.Push()) = %v, want %v", got, want) 2670 } 2671 if got, want := dstTracker.exists, int64(5); got != want { 2672 t.Errorf("count(dst.Exists()) = %v, want %v", got, want) 2673 } 2674 } 2675 2676 func TestCopy_File_FileToMemory_FullCopy(t *testing.T) { 2677 tempDir := t.TempDir() 2678 src, err := New(tempDir) 2679 if err != nil { 2680 t.Fatal("Store.New() error =", err) 2681 } 2682 defer src.Close() 2683 2684 dst := memory.New() 2685 2686 // generate test content 2687 var blobs [][]byte 2688 var descs []ocispec.Descriptor 2689 appendBlob := func(mediaType string, blob []byte) { 2690 blobs = append(blobs, blob) 2691 dgst := digest.FromBytes(blob) 2692 descs = append(descs, ocispec.Descriptor{ 2693 MediaType: mediaType, 2694 Digest: dgst, 2695 Size: int64(len(blob)), 2696 Annotations: map[string]string{ 2697 ocispec.AnnotationTitle: dgst.Encoded() + ".blob", 2698 }, 2699 }) 2700 } 2701 generateManifest := func(config ocispec.Descriptor, layers ...ocispec.Descriptor) { 2702 manifest := ocispec.Manifest{ 2703 Config: config, 2704 Layers: layers, 2705 } 2706 manifestJSON, err := json.Marshal(manifest) 2707 if err != nil { 2708 t.Fatal(err) 2709 } 2710 appendBlob(ocispec.MediaTypeImageManifest, manifestJSON) 2711 } 2712 2713 appendBlob(ocispec.MediaTypeImageConfig, []byte("config")) // Blob 0 2714 appendBlob(ocispec.MediaTypeImageLayer, []byte("foo")) // Blob 1 2715 appendBlob(ocispec.MediaTypeImageLayer, []byte("bar")) // Blob 2 2716 generateManifest(descs[0], descs[1:3]...) // Blob 3 2717 2718 ctx := context.Background() 2719 for i := range blobs { 2720 err := src.Push(ctx, descs[i], bytes.NewReader(blobs[i])) 2721 if err != nil { 2722 t.Fatalf("failed to push test content to src: %d: %v", i, err) 2723 } 2724 } 2725 2726 root := descs[3] 2727 ref := "foobar" 2728 err = src.Tag(ctx, root, ref) 2729 if err != nil { 2730 t.Fatal("fail to tag root node", err) 2731 } 2732 2733 // test copy 2734 gotDesc, err := oras.Copy(ctx, src, ref, dst, "", oras.CopyOptions{}) 2735 if err != nil { 2736 t.Fatalf("Copy() error = %v, wantErr %v", err, false) 2737 } 2738 if !reflect.DeepEqual(gotDesc, root) { 2739 t.Errorf("Copy() = %v, want %v", gotDesc, root) 2740 } 2741 2742 // verify contents 2743 for i, desc := range descs { 2744 exists, err := dst.Exists(ctx, desc) 2745 if err != nil { 2746 t.Fatalf("dst.Exists(%d) error = %v", i, err) 2747 } 2748 if !exists { 2749 t.Errorf("dst.Exists(%d) = %v, want %v", i, exists, true) 2750 } 2751 } 2752 2753 // verify tag 2754 gotDesc, err = dst.Resolve(ctx, ref) 2755 if err != nil { 2756 t.Fatal("dst.Resolve() error =", err) 2757 } 2758 if !reflect.DeepEqual(gotDesc, root) { 2759 t.Errorf("dst.Resolve() = %v, want %v", gotDesc, root) 2760 } 2761 } 2762 2763 func TestCopyGraph_FileToMemory_FullCopy(t *testing.T) { 2764 tempDir := t.TempDir() 2765 src, err := New(tempDir) 2766 if err != nil { 2767 t.Fatal("Store.New() error =", err) 2768 } 2769 defer src.Close() 2770 2771 dst := memory.New() 2772 2773 // generate test content 2774 var blobs [][]byte 2775 var descs []ocispec.Descriptor 2776 appendBlob := func(mediaType string, blob []byte) { 2777 blobs = append(blobs, blob) 2778 dgst := digest.FromBytes(blob) 2779 descs = append(descs, ocispec.Descriptor{ 2780 MediaType: mediaType, 2781 Digest: dgst, 2782 Size: int64(len(blob)), 2783 Annotations: map[string]string{ 2784 ocispec.AnnotationTitle: dgst.Encoded() + ".blob", 2785 }, 2786 }) 2787 } 2788 generateManifest := func(config ocispec.Descriptor, layers ...ocispec.Descriptor) { 2789 manifest := ocispec.Manifest{ 2790 Config: config, 2791 Layers: layers, 2792 } 2793 manifestJSON, err := json.Marshal(manifest) 2794 if err != nil { 2795 t.Fatal(err) 2796 } 2797 appendBlob(ocispec.MediaTypeImageManifest, manifestJSON) 2798 } 2799 generateIndex := func(manifests ...ocispec.Descriptor) { 2800 index := ocispec.Index{ 2801 Manifests: manifests, 2802 } 2803 indexJSON, err := json.Marshal(index) 2804 if err != nil { 2805 t.Fatal(err) 2806 } 2807 appendBlob(ocispec.MediaTypeImageIndex, indexJSON) 2808 } 2809 2810 appendBlob(ocispec.MediaTypeImageConfig, []byte("config")) // Blob 0 2811 appendBlob(ocispec.MediaTypeImageLayer, []byte("foo")) // Blob 1 2812 appendBlob(ocispec.MediaTypeImageLayer, []byte("bar")) // Blob 2 2813 appendBlob(ocispec.MediaTypeImageLayer, []byte("hello")) // Blob 3 2814 generateManifest(descs[0], descs[1:3]...) // Blob 4 2815 generateManifest(descs[0], descs[3]) // Blob 5 2816 generateManifest(descs[0], descs[1:4]...) // Blob 6 2817 generateIndex(descs[4:6]...) // Blob 7 2818 generateIndex(descs[6]) // Blob 8 2819 generateIndex() // Blob 9 2820 generateIndex(descs[7:10]...) // Blob 10 2821 2822 ctx := context.Background() 2823 for i := range blobs { 2824 err := src.Push(ctx, descs[i], bytes.NewReader(blobs[i])) 2825 if err != nil { 2826 t.Fatalf("failed to push test content to src: %d: %v", i, err) 2827 } 2828 } 2829 2830 // test copy 2831 srcTracker := &storageTracker{Storage: src} 2832 dstTracker := &storageTracker{Storage: dst} 2833 root := descs[len(descs)-1] 2834 if err := oras.CopyGraph(ctx, srcTracker, dstTracker, root, oras.CopyGraphOptions{}); err != nil { 2835 t.Fatalf("CopyGraph() error = %v, wantErr %v", err, false) 2836 } 2837 2838 // verify contents 2839 for i := range blobs { 2840 got, err := content.FetchAll(ctx, dst, descs[i]) 2841 if err != nil { 2842 t.Errorf("content[%d] error = %v, wantErr %v", i, err, false) 2843 continue 2844 } 2845 if want := blobs[i]; !bytes.Equal(got, want) { 2846 t.Errorf("content[%d] = %v, want %v", i, got, want) 2847 } 2848 } 2849 2850 // verify API counts 2851 if got, want := srcTracker.fetch, int64(len(blobs)); got != want { 2852 t.Errorf("count(src.Fetch()) = %v, want %v", got, want) 2853 } 2854 if got, want := srcTracker.push, int64(0); got != want { 2855 t.Errorf("count(src.Push()) = %v, want %v", got, want) 2856 } 2857 if got, want := srcTracker.exists, int64(0); got != want { 2858 t.Errorf("count(src.Exists()) = %v, want %v", got, want) 2859 } 2860 if got, want := dstTracker.fetch, int64(0); got != want { 2861 t.Errorf("count(dst.Fetch()) = %v, want %v", got, want) 2862 } 2863 if got, want := dstTracker.push, int64(len(blobs)); got != want { 2864 t.Errorf("count(dst.Push()) = %v, want %v", got, want) 2865 } 2866 if got, want := dstTracker.exists, int64(len(blobs)); got != want { 2867 t.Errorf("count(dst.Exists()) = %v, want %v", got, want) 2868 } 2869 } 2870 2871 func TestCopyGraph_FileToMemory_PartialCopy(t *testing.T) { 2872 tempDir := t.TempDir() 2873 src, err := New(tempDir) 2874 if err != nil { 2875 t.Fatal("Store.New() error =", err) 2876 } 2877 defer src.Close() 2878 2879 dst := memory.New() 2880 2881 // generate test content 2882 var blobs [][]byte 2883 var descs []ocispec.Descriptor 2884 appendBlob := func(mediaType string, blob []byte) { 2885 blobs = append(blobs, blob) 2886 dgst := digest.FromBytes(blob) 2887 descs = append(descs, ocispec.Descriptor{ 2888 MediaType: mediaType, 2889 Digest: dgst, 2890 Size: int64(len(blob)), 2891 Annotations: map[string]string{ 2892 ocispec.AnnotationTitle: dgst.Encoded() + ".blob", 2893 }, 2894 }) 2895 } 2896 generateManifest := func(config ocispec.Descriptor, layers ...ocispec.Descriptor) { 2897 manifest := ocispec.Manifest{ 2898 Config: config, 2899 Layers: layers, 2900 } 2901 manifestJSON, err := json.Marshal(manifest) 2902 if err != nil { 2903 t.Fatal(err) 2904 } 2905 appendBlob(ocispec.MediaTypeImageManifest, manifestJSON) 2906 } 2907 generateIndex := func(manifests ...ocispec.Descriptor) { 2908 index := ocispec.Index{ 2909 Manifests: manifests, 2910 } 2911 indexJSON, err := json.Marshal(index) 2912 if err != nil { 2913 t.Fatal(err) 2914 } 2915 appendBlob(ocispec.MediaTypeImageIndex, indexJSON) 2916 } 2917 2918 appendBlob(ocispec.MediaTypeImageConfig, []byte("config")) // Blob 0 2919 appendBlob(ocispec.MediaTypeImageLayer, []byte("foo")) // Blob 1 2920 appendBlob(ocispec.MediaTypeImageLayer, []byte("bar")) // Blob 2 2921 generateManifest(descs[0], descs[1:3]...) // Blob 3 2922 appendBlob(ocispec.MediaTypeImageLayer, []byte("hello")) // Blob 4 2923 generateManifest(descs[0], descs[4]) // Blob 5 2924 generateIndex(descs[3], descs[5]) // Blob 6 2925 2926 ctx := context.Background() 2927 for i := range blobs { 2928 err := src.Push(ctx, descs[i], bytes.NewReader(blobs[i])) 2929 if err != nil { 2930 t.Fatalf("failed to push test content to src: %d: %v", i, err) 2931 } 2932 } 2933 2934 // initial copy 2935 root := descs[3] 2936 if err := oras.CopyGraph(ctx, src, dst, root, oras.CopyGraphOptions{}); err != nil { 2937 t.Fatalf("CopyGraph() error = %v, wantErr %v", err, false) 2938 } 2939 // verify contents 2940 for i := range blobs[:4] { 2941 got, err := content.FetchAll(ctx, dst, descs[i]) 2942 if err != nil { 2943 t.Fatalf("content[%d] error = %v, wantErr %v", i, err, false) 2944 } 2945 if want := blobs[i]; !bytes.Equal(got, want) { 2946 t.Fatalf("content[%d] = %v, want %v", i, got, want) 2947 } 2948 } 2949 2950 // test copy 2951 srcTracker := &storageTracker{Storage: src} 2952 dstTracker := &storageTracker{Storage: dst} 2953 root = descs[len(descs)-1] 2954 if err := oras.CopyGraph(ctx, srcTracker, dstTracker, root, oras.CopyGraphOptions{}); err != nil { 2955 t.Fatalf("CopyGraph() error = %v, wantErr %v", err, false) 2956 } 2957 2958 // verify contents 2959 for i := range blobs { 2960 got, err := content.FetchAll(ctx, dst, descs[i]) 2961 if err != nil { 2962 t.Errorf("content[%d] error = %v, wantErr %v", i, err, false) 2963 continue 2964 } 2965 if want := blobs[i]; !bytes.Equal(got, want) { 2966 t.Errorf("content[%d] = %v, want %v", i, got, want) 2967 } 2968 } 2969 2970 // verify API counts 2971 if got, want := srcTracker.fetch, int64(3); got != want { 2972 t.Errorf("count(src.Fetch()) = %v, want %v", got, want) 2973 } 2974 if got, want := srcTracker.push, int64(0); got != want { 2975 t.Errorf("count(src.Push()) = %v, want %v", got, want) 2976 } 2977 if got, want := srcTracker.exists, int64(0); got != want { 2978 t.Errorf("count(src.Exists()) = %v, want %v", got, want) 2979 } 2980 if got, want := dstTracker.fetch, int64(0); got != want { 2981 t.Errorf("count(dst.Fetch()) = %v, want %v", got, want) 2982 } 2983 if got, want := dstTracker.push, int64(3); got != want { 2984 t.Errorf("count(dst.Push()) = %v, want %v", got, want) 2985 } 2986 if got, want := dstTracker.exists, int64(5); got != want { 2987 t.Errorf("count(dst.Exists()) = %v, want %v", got, want) 2988 } 2989 } 2990 2991 func equalDescriptorSet(actual []ocispec.Descriptor, expected []ocispec.Descriptor) bool { 2992 if len(actual) != len(expected) { 2993 return false 2994 } 2995 contains := func(node ocispec.Descriptor) bool { 2996 for _, candidate := range actual { 2997 if reflect.DeepEqual(candidate, node) { 2998 return true 2999 } 3000 } 3001 return false 3002 } 3003 for _, node := range expected { 3004 if !contains(node) { 3005 return false 3006 } 3007 } 3008 return true 3009 }