zotregistry.dev/zot@v1.4.4-0.20240314164342-eec277e14d20/pkg/storage/gc/gc_internal_test.go (about) 1 package gc 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "errors" 8 "os" 9 "path" 10 "testing" 11 "time" 12 13 godigest "github.com/opencontainers/go-digest" 14 ispec "github.com/opencontainers/image-spec/specs-go/v1" 15 . "github.com/smartystreets/goconvey/convey" 16 17 "zotregistry.dev/zot/pkg/api/config" 18 zcommon "zotregistry.dev/zot/pkg/common" 19 "zotregistry.dev/zot/pkg/extensions/monitoring" 20 zlog "zotregistry.dev/zot/pkg/log" 21 "zotregistry.dev/zot/pkg/meta/types" 22 "zotregistry.dev/zot/pkg/storage" 23 "zotregistry.dev/zot/pkg/storage/cache" 24 common "zotregistry.dev/zot/pkg/storage/common" 25 storageConstants "zotregistry.dev/zot/pkg/storage/constants" 26 "zotregistry.dev/zot/pkg/storage/local" 27 . "zotregistry.dev/zot/pkg/test/image-utils" 28 "zotregistry.dev/zot/pkg/test/mocks" 29 ) 30 31 var ( 32 errGC = errors.New("gc error") 33 repoName = "test" //nolint: gochecknoglobals 34 ) 35 36 func TestGarbageCollectManifestErrors(t *testing.T) { 37 Convey("Make imagestore and upload manifest", t, func(c C) { 38 dir := t.TempDir() 39 40 log := zlog.NewLogger("debug", "") 41 audit := zlog.NewAuditLogger("debug", "") 42 43 metrics := monitoring.NewMetricsServer(false, log) 44 45 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 46 RootDir: dir, 47 Name: "cache", 48 UseRelPaths: true, 49 }, log) 50 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 51 52 gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, Options{ 53 Delay: storageConstants.DefaultGCDelay, 54 ImageRetention: config.ImageRetention{ 55 Delay: storageConstants.DefaultRetentionDelay, 56 Policies: []config.RetentionPolicy{ 57 { 58 Repositories: []string{"**"}, 59 DeleteReferrers: true, 60 }, 61 }, 62 }, 63 }, audit, log) 64 65 Convey("trigger repo not found in addImageIndexBlobsToReferences()", func() { 66 err := gc.addIndexBlobsToReferences(repoName, ispec.Index{ 67 Manifests: []ispec.Descriptor{ 68 { 69 Digest: godigest.FromString("miss"), 70 MediaType: ispec.MediaTypeImageIndex, 71 }, 72 }, 73 }, map[string]bool{}) 74 So(err, ShouldNotBeNil) 75 }) 76 77 Convey("trigger repo not found in addImageManifestBlobsToReferences()", func() { 78 err := gc.addIndexBlobsToReferences(repoName, ispec.Index{ 79 Manifests: []ispec.Descriptor{ 80 { 81 Digest: godigest.FromString("miss"), 82 MediaType: ispec.MediaTypeImageManifest, 83 }, 84 }, 85 }, map[string]bool{}) 86 So(err, ShouldNotBeNil) 87 }) 88 89 content := []byte("this is a blob") 90 digest := godigest.FromBytes(content) 91 So(digest, ShouldNotBeNil) 92 93 _, blen, err := imgStore.FullBlobUpload(repoName, bytes.NewReader(content), digest) 94 So(err, ShouldBeNil) 95 So(blen, ShouldEqual, len(content)) 96 97 cblob, cdigest := GetRandomImageConfig() 98 _, clen, err := imgStore.FullBlobUpload(repoName, bytes.NewReader(cblob), cdigest) 99 So(err, ShouldBeNil) 100 So(clen, ShouldEqual, len(cblob)) 101 102 manifest := ispec.Manifest{ 103 Config: ispec.Descriptor{ 104 MediaType: ispec.MediaTypeImageConfig, 105 Digest: cdigest, 106 Size: int64(len(cblob)), 107 }, 108 Layers: []ispec.Descriptor{ 109 { 110 MediaType: ispec.MediaTypeImageLayer, 111 Digest: digest, 112 Size: int64(len(content)), 113 }, 114 }, 115 } 116 117 manifest.SchemaVersion = 2 118 119 body, err := json.Marshal(manifest) 120 So(err, ShouldBeNil) 121 122 manifestDigest := godigest.FromBytes(body) 123 124 _, _, err = imgStore.PutImageManifest(repoName, "1.0", ispec.MediaTypeImageManifest, body) 125 So(err, ShouldBeNil) 126 127 Convey("trigger GetIndex error in GetReferencedBlobs", func() { 128 index, err := common.GetIndex(imgStore, repoName, log) 129 So(err, ShouldBeNil) 130 131 err = os.Chmod(path.Join(imgStore.RootDir(), repoName), 0o000) 132 So(err, ShouldBeNil) 133 134 defer func() { 135 err := os.Chmod(path.Join(imgStore.RootDir(), repoName), 0o755) 136 So(err, ShouldBeNil) 137 }() 138 139 err = gc.addIndexBlobsToReferences(repoName, index, map[string]bool{}) 140 So(err, ShouldNotBeNil) 141 }) 142 143 Convey("trigger GetImageManifest error in AddIndexBlobsToReferences", func() { 144 index, err := common.GetIndex(imgStore, repoName, log) 145 So(err, ShouldBeNil) 146 147 err = os.Chmod(path.Join(imgStore.RootDir(), repoName, "blobs", "sha256", manifestDigest.Encoded()), 0o000) 148 So(err, ShouldBeNil) 149 150 defer func() { 151 err := os.Chmod(path.Join(imgStore.RootDir(), repoName, "blobs", "sha256", manifestDigest.Encoded()), 0o755) 152 So(err, ShouldBeNil) 153 }() 154 155 err = gc.addIndexBlobsToReferences(repoName, index, map[string]bool{}) 156 So(err, ShouldNotBeNil) 157 }) 158 }) 159 } 160 161 func TestGarbageCollectIndexErrors(t *testing.T) { 162 Convey("Make imagestore and upload manifest", t, func(c C) { 163 dir := t.TempDir() 164 165 log := zlog.NewLogger("debug", "") 166 audit := zlog.NewAuditLogger("debug", "") 167 168 metrics := monitoring.NewMetricsServer(false, log) 169 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 170 RootDir: dir, 171 Name: "cache", 172 UseRelPaths: true, 173 }, log) 174 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 175 176 gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, Options{ 177 Delay: storageConstants.DefaultGCDelay, 178 ImageRetention: config.ImageRetention{ 179 Delay: storageConstants.DefaultRetentionDelay, 180 Policies: []config.RetentionPolicy{ 181 { 182 Repositories: []string{"**"}, 183 DeleteReferrers: true, 184 }, 185 }, 186 }, 187 }, audit, log) 188 189 content := []byte("this is a blob") 190 bdgst := godigest.FromBytes(content) 191 So(bdgst, ShouldNotBeNil) 192 193 _, bsize, err := imgStore.FullBlobUpload(repoName, bytes.NewReader(content), bdgst) 194 So(err, ShouldBeNil) 195 So(bsize, ShouldEqual, len(content)) 196 197 var index ispec.Index 198 index.SchemaVersion = 2 199 index.MediaType = ispec.MediaTypeImageIndex 200 201 var digest godigest.Digest 202 for i := 0; i < 4; i++ { 203 // upload image config blob 204 upload, err := imgStore.NewBlobUpload(repoName) 205 So(err, ShouldBeNil) 206 So(upload, ShouldNotBeEmpty) 207 208 cblob, cdigest := GetRandomImageConfig() 209 buf := bytes.NewBuffer(cblob) 210 buflen := buf.Len() 211 blob, err := imgStore.PutBlobChunkStreamed(repoName, upload, buf) 212 So(err, ShouldBeNil) 213 So(blob, ShouldEqual, buflen) 214 215 err = imgStore.FinishBlobUpload(repoName, upload, buf, cdigest) 216 So(err, ShouldBeNil) 217 So(blob, ShouldEqual, buflen) 218 219 // create a manifest 220 manifest := ispec.Manifest{ 221 Config: ispec.Descriptor{ 222 MediaType: ispec.MediaTypeImageConfig, 223 Digest: cdigest, 224 Size: int64(len(cblob)), 225 }, 226 Layers: []ispec.Descriptor{ 227 { 228 MediaType: ispec.MediaTypeImageLayer, 229 Digest: bdgst, 230 Size: bsize, 231 }, 232 }, 233 } 234 manifest.SchemaVersion = 2 235 content, err = json.Marshal(manifest) 236 So(err, ShouldBeNil) 237 digest = godigest.FromBytes(content) 238 So(digest, ShouldNotBeNil) 239 _, _, err = imgStore.PutImageManifest(repoName, digest.String(), ispec.MediaTypeImageManifest, content) 240 So(err, ShouldBeNil) 241 242 index.Manifests = append(index.Manifests, ispec.Descriptor{ 243 Digest: digest, 244 MediaType: ispec.MediaTypeImageManifest, 245 Size: int64(len(content)), 246 }) 247 } 248 249 // upload index image 250 indexContent, err := json.Marshal(index) 251 So(err, ShouldBeNil) 252 indexDigest := godigest.FromBytes(indexContent) 253 So(indexDigest, ShouldNotBeNil) 254 255 _, _, err = imgStore.PutImageManifest(repoName, "1.0", ispec.MediaTypeImageIndex, indexContent) 256 So(err, ShouldBeNil) 257 258 index, err = common.GetIndex(imgStore, repoName, log) 259 So(err, ShouldBeNil) 260 261 err = gc.addIndexBlobsToReferences(repoName, index, map[string]bool{}) 262 So(err, ShouldBeNil) 263 264 Convey("trigger GetImageIndex error in GetReferencedBlobsInImageIndex", func() { 265 err := os.Chmod(path.Join(imgStore.RootDir(), repoName, "blobs", "sha256", indexDigest.Encoded()), 0o000) 266 So(err, ShouldBeNil) 267 268 defer func() { 269 err := os.Chmod(path.Join(imgStore.RootDir(), repoName, "blobs", "sha256", indexDigest.Encoded()), 0o755) 270 So(err, ShouldBeNil) 271 }() 272 273 err = gc.addIndexBlobsToReferences(repoName, index, map[string]bool{}) 274 So(err, ShouldNotBeNil) 275 }) 276 }) 277 } 278 279 func TestGarbageCollectWithMockedImageStore(t *testing.T) { 280 trueVal := true 281 282 ctx := context.Background() 283 284 Convey("Cover gc error paths", t, func(c C) { 285 log := zlog.NewLogger("debug", "") 286 audit := zlog.NewAuditLogger("debug", "") 287 288 gcOptions := Options{ 289 Delay: storageConstants.DefaultGCDelay, 290 ImageRetention: config.ImageRetention{ 291 Delay: storageConstants.DefaultRetentionDelay, 292 Policies: []config.RetentionPolicy{ 293 { 294 Repositories: []string{"**"}, 295 DeleteReferrers: true, 296 }, 297 }, 298 }, 299 } 300 301 Convey("Error on GetIndex in gc.cleanRepo()", func() { 302 gc := NewGarbageCollect(mocks.MockedImageStore{}, mocks.MetaDBMock{ 303 GetRepoMetaFn: func(ctx context.Context, repo string) (types.RepoMeta, error) { 304 return types.RepoMeta{}, errGC 305 }, 306 }, gcOptions, audit, log) 307 308 err := gc.cleanRepo(ctx, repoName) 309 So(err, ShouldNotBeNil) 310 }) 311 312 Convey("Error on GetIndex in gc.removeUnreferencedBlobs()", func() { 313 gc := NewGarbageCollect(mocks.MockedImageStore{}, mocks.MetaDBMock{ 314 GetRepoMetaFn: func(ctx context.Context, repo string) (types.RepoMeta, error) { 315 return types.RepoMeta{}, errGC 316 }, 317 }, gcOptions, audit, log) 318 319 err := gc.removeUnreferencedBlobs("repo", time.Hour, log) 320 So(err, ShouldNotBeNil) 321 }) 322 323 Convey("Error on gc.removeManifest()", func() { 324 gc := NewGarbageCollect(mocks.MockedImageStore{}, mocks.MetaDBMock{ 325 GetRepoMetaFn: func(ctx context.Context, repo string) (types.RepoMeta, error) { 326 return types.RepoMeta{}, errGC 327 }, 328 }, gcOptions, audit, log) 329 330 _, err := gc.removeManifest("", &ispec.Index{}, ispec.DescriptorEmptyJSON, "tag", "", "") 331 So(err, ShouldNotBeNil) 332 }) 333 334 Convey("Error on metaDB in gc.cleanRepo()", func() { 335 gcOptions := Options{ 336 Delay: storageConstants.DefaultGCDelay, 337 ImageRetention: config.ImageRetention{ 338 Delay: storageConstants.DefaultRetentionDelay, 339 Policies: []config.RetentionPolicy{ 340 { 341 Repositories: []string{"**"}, 342 KeepTags: []config.KeepTagsPolicy{ 343 { 344 Patterns: []string{".*"}, 345 }, 346 }, 347 }, 348 }, 349 }, 350 } 351 352 gc := NewGarbageCollect(mocks.MockedImageStore{}, mocks.MetaDBMock{ 353 GetRepoMetaFn: func(ctx context.Context, repo string) (types.RepoMeta, error) { 354 return types.RepoMeta{}, errGC 355 }, 356 }, gcOptions, audit, log) 357 358 err := gc.removeTagsPerRetentionPolicy(ctx, "name", &ispec.Index{}) 359 So(err, ShouldNotBeNil) 360 }) 361 362 Convey("Error on context done in removeTags...", func() { 363 gcOptions := Options{ 364 Delay: storageConstants.DefaultGCDelay, 365 ImageRetention: config.ImageRetention{ 366 Delay: storageConstants.DefaultRetentionDelay, 367 Policies: []config.RetentionPolicy{ 368 { 369 Repositories: []string{"**"}, 370 KeepTags: []config.KeepTagsPolicy{ 371 { 372 Patterns: []string{".*"}, 373 }, 374 }, 375 }, 376 }, 377 }, 378 } 379 380 gc := NewGarbageCollect(mocks.MockedImageStore{}, mocks.MetaDBMock{}, gcOptions, audit, log) 381 382 ctx, cancel := context.WithCancel(ctx) 383 cancel() 384 385 err := gc.removeTagsPerRetentionPolicy(ctx, "name", &ispec.Index{ 386 Manifests: []ispec.Descriptor{ 387 { 388 MediaType: ispec.MediaTypeImageManifest, 389 Digest: godigest.FromBytes([]byte("digest")), 390 }, 391 }, 392 }) 393 So(err, ShouldNotBeNil) 394 }) 395 396 Convey("Error on PutIndexContent in gc.cleanRepo()", func() { 397 returnedIndexJSON := ispec.Index{} 398 399 returnedIndexJSONBuf, err := json.Marshal(returnedIndexJSON) 400 So(err, ShouldBeNil) 401 402 imgStore := mocks.MockedImageStore{ 403 PutIndexContentFn: func(repo string, index ispec.Index) error { 404 return errGC 405 }, 406 GetIndexContentFn: func(repo string) ([]byte, error) { 407 return returnedIndexJSONBuf, nil 408 }, 409 } 410 411 gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gcOptions, audit, log) 412 413 err = gc.cleanRepo(ctx, repoName) 414 So(err, ShouldNotBeNil) 415 }) 416 417 Convey("Error on gc.cleanBlobs() in gc.cleanRepo()", func() { 418 returnedIndexJSON := ispec.Index{} 419 420 returnedIndexJSONBuf, err := json.Marshal(returnedIndexJSON) 421 So(err, ShouldBeNil) 422 423 imgStore := mocks.MockedImageStore{ 424 PutIndexContentFn: func(repo string, index ispec.Index) error { 425 return nil 426 }, 427 GetIndexContentFn: func(repo string) ([]byte, error) { 428 return returnedIndexJSONBuf, nil 429 }, 430 GetAllBlobsFn: func(repo string) ([]string, error) { 431 return []string{}, errGC 432 }, 433 } 434 435 gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gcOptions, audit, log) 436 437 err = gc.cleanRepo(ctx, repoName) 438 So(err, ShouldNotBeNil) 439 }) 440 441 Convey("False on imgStore.DirExists() in gc.cleanRepo()", func() { 442 imgStore := mocks.MockedImageStore{ 443 DirExistsFn: func(d string) bool { 444 return false 445 }, 446 } 447 448 gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gcOptions, audit, log) 449 450 err := gc.cleanRepo(ctx, repoName) 451 So(err, ShouldNotBeNil) 452 }) 453 454 Convey("Error on gc.identifyManifestsReferencedInIndex in gc.cleanManifests() with multiarch image", func() { 455 indexImageDigest := godigest.FromBytes([]byte("digest")) 456 457 returnedIndexImage := ispec.Index{ 458 Subject: &ispec.DescriptorEmptyJSON, 459 Manifests: []ispec.Descriptor{ 460 { 461 MediaType: ispec.MediaTypeImageIndex, 462 Digest: godigest.FromBytes([]byte("digest2")), 463 }, 464 }, 465 } 466 467 returnedIndexImageBuf, err := json.Marshal(returnedIndexImage) 468 So(err, ShouldBeNil) 469 470 imgStore := mocks.MockedImageStore{ 471 GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) { 472 if digest == indexImageDigest { 473 return returnedIndexImageBuf, nil 474 } else { 475 return nil, errGC 476 } 477 }, 478 } 479 480 gcOptions.ImageRetention = config.ImageRetention{ 481 Policies: []config.RetentionPolicy{ 482 { 483 Repositories: []string{"**"}, 484 DeleteUntagged: &trueVal, 485 }, 486 }, 487 } 488 gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gcOptions, audit, log) 489 490 err = gc.removeManifestsPerRepoPolicy(ctx, repoName, &ispec.Index{ 491 Manifests: []ispec.Descriptor{ 492 { 493 MediaType: ispec.MediaTypeImageIndex, 494 Digest: indexImageDigest, 495 }, 496 }, 497 }) 498 So(err, ShouldNotBeNil) 499 }) 500 501 Convey("Error on gc.identifyManifestsReferencedInIndex in gc.cleanManifests() with image", func() { 502 imgStore := mocks.MockedImageStore{ 503 GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) { 504 return nil, errGC 505 }, 506 } 507 508 gcOptions.ImageRetention = config.ImageRetention{ 509 Policies: []config.RetentionPolicy{ 510 { 511 Repositories: []string{"**"}, 512 DeleteUntagged: &trueVal, 513 }, 514 }, 515 } 516 517 gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gcOptions, audit, log) 518 519 err := gc.removeManifestsPerRepoPolicy(ctx, repoName, &ispec.Index{ 520 Manifests: []ispec.Descriptor{ 521 { 522 MediaType: ispec.MediaTypeImageManifest, 523 Digest: godigest.FromBytes([]byte("digest")), 524 }, 525 }, 526 }) 527 So(err, ShouldNotBeNil) 528 }) 529 530 Convey("Error on context done in removeManifests...", func() { 531 imgStore := mocks.MockedImageStore{} 532 533 gcOptions.ImageRetention = config.ImageRetention{ 534 Policies: []config.RetentionPolicy{ 535 { 536 Repositories: []string{"**"}, 537 DeleteUntagged: &trueVal, 538 }, 539 }, 540 } 541 542 gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gcOptions, audit, log) 543 544 ctx, cancel := context.WithCancel(ctx) 545 cancel() 546 547 err := gc.removeManifestsPerRepoPolicy(ctx, repoName, &ispec.Index{ 548 Manifests: []ispec.Descriptor{ 549 { 550 MediaType: ispec.MediaTypeImageManifest, 551 Digest: godigest.FromBytes([]byte("digest")), 552 }, 553 }, 554 }) 555 So(err, ShouldNotBeNil) 556 }) 557 558 Convey("Error on gc.gcManifest() in gc.cleanManifests() with image", func() { 559 returnedImage := ispec.Manifest{ 560 MediaType: ispec.MediaTypeImageManifest, 561 } 562 563 returnedImageBuf, err := json.Marshal(returnedImage) 564 So(err, ShouldBeNil) 565 566 imgStore := mocks.MockedImageStore{ 567 GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) { 568 return returnedImageBuf, nil 569 }, 570 } 571 572 metaDB := mocks.MetaDBMock{ 573 RemoveRepoReferenceFn: func(repo, reference string, manifestDigest godigest.Digest) error { 574 return errGC 575 }, 576 } 577 578 gcOptions.ImageRetention = config.ImageRetention{ 579 Policies: []config.RetentionPolicy{ 580 { 581 Repositories: []string{"**"}, 582 DeleteUntagged: &trueVal, 583 }, 584 }, 585 } 586 gc := NewGarbageCollect(imgStore, metaDB, gcOptions, audit, log) 587 588 err = gc.removeManifestsPerRepoPolicy(ctx, repoName, &ispec.Index{ 589 Manifests: []ispec.Descriptor{ 590 { 591 MediaType: ispec.MediaTypeImageManifest, 592 Digest: godigest.FromBytes([]byte("digest")), 593 }, 594 }, 595 }) 596 So(err, ShouldNotBeNil) 597 }) 598 Convey("Error on gc.gcManifest() in gc.cleanManifests() with signature", func() { 599 returnedImage := ispec.Manifest{ 600 MediaType: ispec.MediaTypeImageManifest, 601 ArtifactType: zcommon.NotationSignature, 602 } 603 604 returnedImageBuf, err := json.Marshal(returnedImage) 605 So(err, ShouldBeNil) 606 607 imgStore := mocks.MockedImageStore{ 608 GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) { 609 return returnedImageBuf, nil 610 }, 611 } 612 613 metaDB := mocks.MetaDBMock{ 614 DeleteSignatureFn: func(repo string, signedManifestDigest godigest.Digest, sm types.SignatureMetadata) error { 615 return errGC 616 }, 617 } 618 619 gcOptions.ImageRetention = config.ImageRetention{} 620 gc := NewGarbageCollect(imgStore, metaDB, gcOptions, audit, log) 621 622 desc := ispec.Descriptor{ 623 MediaType: ispec.MediaTypeImageManifest, 624 Digest: godigest.FromBytes([]byte("digest")), 625 } 626 627 index := &ispec.Index{ 628 Manifests: []ispec.Descriptor{desc}, 629 } 630 _, err = gc.removeManifest(repoName, index, desc, desc.Digest.String(), storage.NotationType, 631 godigest.FromBytes([]byte("digest2"))) 632 633 So(err, ShouldNotBeNil) 634 }) 635 636 Convey("Error on gc.gcReferrer() in gc.cleanManifests() with image index", func() { 637 manifestDesc := ispec.Descriptor{ 638 MediaType: ispec.MediaTypeImageIndex, 639 Digest: godigest.FromBytes([]byte("digest")), 640 } 641 642 returnedIndexImage := ispec.Index{ 643 MediaType: ispec.MediaTypeImageIndex, 644 Subject: &ispec.Descriptor{ 645 Digest: godigest.FromBytes([]byte("digest2")), 646 }, 647 Manifests: []ispec.Descriptor{ 648 manifestDesc, 649 }, 650 } 651 652 returnedIndexImageBuf, err := json.Marshal(returnedIndexImage) 653 So(err, ShouldBeNil) 654 655 imgStore := mocks.MockedImageStore{ 656 GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) { 657 return returnedIndexImageBuf, nil 658 }, 659 StatBlobFn: func(repo string, digest godigest.Digest) (bool, int64, time.Time, error) { 660 return false, -1, time.Time{}, errGC 661 }, 662 } 663 664 gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gcOptions, audit, log) 665 666 err = gc.removeManifestsPerRepoPolicy(ctx, repoName, &returnedIndexImage) 667 So(err, ShouldNotBeNil) 668 }) 669 670 Convey("Error on gc.gcReferrer() in gc.cleanManifests() with image", func() { 671 manifestDesc := ispec.Descriptor{ 672 MediaType: ispec.MediaTypeImageManifest, 673 Digest: godigest.FromBytes([]byte("digest")), 674 } 675 676 returnedImage := ispec.Manifest{ 677 Subject: &ispec.Descriptor{ 678 Digest: godigest.FromBytes([]byte("digest2")), 679 }, 680 MediaType: ispec.MediaTypeImageManifest, 681 } 682 683 returnedImageBuf, err := json.Marshal(returnedImage) 684 So(err, ShouldBeNil) 685 686 imgStore := mocks.MockedImageStore{ 687 GetBlobContentFn: func(repo string, digest godigest.Digest) ([]byte, error) { 688 return returnedImageBuf, nil 689 }, 690 StatBlobFn: func(repo string, digest godigest.Digest) (bool, int64, time.Time, error) { 691 return false, -1, time.Time{}, errGC 692 }, 693 } 694 695 gc := NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gcOptions, audit, log) 696 697 err = gc.removeManifestsPerRepoPolicy(ctx, repoName, &ispec.Index{ 698 Manifests: []ispec.Descriptor{ 699 manifestDesc, 700 }, 701 }) 702 So(err, ShouldNotBeNil) 703 }) 704 }) 705 }