zotregistry.io/zot@v1.4.4-0.20231124084042-02a8ed785457/pkg/storage/local/local_test.go (about) 1 package local_test 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/rand" 7 _ "crypto/sha256" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "io" 12 "io/fs" 13 "math/big" 14 "os" 15 "path" 16 "strings" 17 "syscall" 18 "testing" 19 "time" 20 21 godigest "github.com/opencontainers/go-digest" 22 imeta "github.com/opencontainers/image-spec/specs-go" 23 ispec "github.com/opencontainers/image-spec/specs-go/v1" 24 artifactspec "github.com/oras-project/artifacts-spec/specs-go/v1" 25 "github.com/rs/zerolog" 26 . "github.com/smartystreets/goconvey/convey" 27 28 zerr "zotregistry.io/zot/errors" 29 "zotregistry.io/zot/pkg/api/config" 30 "zotregistry.io/zot/pkg/common" 31 "zotregistry.io/zot/pkg/extensions/monitoring" 32 zlog "zotregistry.io/zot/pkg/log" 33 "zotregistry.io/zot/pkg/scheduler" 34 "zotregistry.io/zot/pkg/storage" 35 "zotregistry.io/zot/pkg/storage/cache" 36 storageConstants "zotregistry.io/zot/pkg/storage/constants" 37 "zotregistry.io/zot/pkg/storage/gc" 38 "zotregistry.io/zot/pkg/storage/local" 39 storageTypes "zotregistry.io/zot/pkg/storage/types" 40 . "zotregistry.io/zot/pkg/test/image-utils" 41 "zotregistry.io/zot/pkg/test/mocks" 42 "zotregistry.io/zot/pkg/test/signature" 43 ) 44 45 const ( 46 tag = "1.0" 47 repoName = "test" 48 ) 49 50 var trueVal bool = true //nolint: gochecknoglobals 51 52 var DeleteReferrers = config.ImageRetention{ //nolint: gochecknoglobals 53 Delay: storageConstants.DefaultRetentionDelay, 54 Policies: []config.RetentionPolicy{ 55 { 56 Repositories: []string{"**"}, 57 DeleteReferrers: true, 58 DeleteUntagged: &trueVal, 59 }, 60 }, 61 } 62 63 var errCache = errors.New("new cache error") 64 65 func runAndGetScheduler() (*scheduler.Scheduler, context.CancelFunc) { 66 taskScheduler := scheduler.NewScheduler(config.New(), zlog.Logger{}) 67 taskScheduler.RateLimit = 50 * time.Millisecond 68 69 ctx, cancel := context.WithCancel(context.Background()) 70 taskScheduler.RunScheduler(ctx) 71 72 return taskScheduler, cancel 73 } 74 75 func TestStorageFSAPIs(t *testing.T) { 76 dir := t.TempDir() 77 78 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 79 metrics := monitoring.NewMetricsServer(false, log) 80 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 81 RootDir: dir, 82 Name: "cache", 83 UseRelPaths: true, 84 }, log) 85 86 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 87 88 Convey("Repo layout", t, func(c C) { 89 Convey("Bad image manifest", func() { 90 upload, err := imgStore.NewBlobUpload(repoName) 91 So(err, ShouldBeNil) 92 So(upload, ShouldNotBeEmpty) 93 94 content := []byte("test-data1") 95 buf := bytes.NewBuffer(content) 96 buflen := buf.Len() 97 digest := godigest.FromBytes(content) 98 99 blob, err := imgStore.PutBlobChunk(repoName, upload, 0, int64(buflen), buf) 100 So(err, ShouldBeNil) 101 So(blob, ShouldEqual, buflen) 102 103 err = imgStore.FinishBlobUpload(repoName, upload, buf, digest) 104 So(err, ShouldBeNil) 105 106 annotationsMap := make(map[string]string) 107 annotationsMap[ispec.AnnotationRefName] = tag 108 109 cblob, cdigest := GetRandomImageConfig() 110 _, clen, err := imgStore.FullBlobUpload(repoName, bytes.NewReader(cblob), cdigest) 111 So(err, ShouldBeNil) 112 So(clen, ShouldEqual, len(cblob)) 113 hasBlob, _, err := imgStore.CheckBlob(repoName, cdigest) 114 So(err, ShouldBeNil) 115 So(hasBlob, ShouldEqual, true) 116 117 manifest := ispec.Manifest{ 118 Config: ispec.Descriptor{ 119 MediaType: "application/vnd.oci.image.config.v1+json", 120 Digest: cdigest, 121 Size: int64(len(cblob)), 122 }, 123 Layers: []ispec.Descriptor{ 124 { 125 MediaType: "application/vnd.oci.image.layer.v1.tar", 126 Digest: digest, 127 Size: int64(buflen), 128 }, 129 }, 130 Annotations: annotationsMap, 131 } 132 133 manifest.SchemaVersion = 2 134 manifestBuf, err := json.Marshal(manifest) 135 So(err, ShouldBeNil) 136 digest = godigest.FromBytes(manifestBuf) 137 138 err = os.Chmod(path.Join(imgStore.RootDir(), repoName, "index.json"), 0o000) 139 if err != nil { 140 panic(err) 141 } 142 143 _, _, err = imgStore.PutImageManifest(repoName, "1.0", ispec.MediaTypeImageManifest, manifestBuf) 144 So(err, ShouldNotBeNil) 145 146 err = os.Chmod(path.Join(imgStore.RootDir(), repoName, "index.json"), 0o755) 147 if err != nil { 148 panic(err) 149 } 150 151 _, _, err = imgStore.PutImageManifest(repoName, "1.0", ispec.MediaTypeImageManifest, manifestBuf) 152 So(err, ShouldBeNil) 153 154 manifestPath := path.Join(imgStore.RootDir(), repoName, "blobs", digest.Algorithm().String(), digest.Encoded()) 155 156 err = os.Chmod(manifestPath, 0o000) 157 if err != nil { 158 panic(err) 159 } 160 161 _, _, _, err = imgStore.GetImageManifest(repoName, digest.String()) 162 So(err, ShouldNotBeNil) 163 164 err = os.Remove(manifestPath) 165 if err != nil { 166 panic(err) 167 } 168 169 _, _, _, err = imgStore.GetImageManifest(repoName, digest.String()) 170 So(err, ShouldNotBeNil) 171 172 err = os.Chmod(path.Join(imgStore.RootDir(), repoName), 0o000) 173 if err != nil { 174 panic(err) 175 } 176 177 _, _, err = imgStore.PutImageManifest(repoName, "2.0", ispec.MediaTypeImageManifest, manifestBuf) 178 So(err, ShouldNotBeNil) 179 err = os.Chmod(path.Join(imgStore.RootDir(), repoName), 0o755) 180 if err != nil { 181 panic(err) 182 } 183 184 // invalid GetOrasReferrers 185 _, err = imgStore.GetOrasReferrers("invalid", "invalid", "invalid") 186 So(err, ShouldNotBeNil) 187 188 _, err = imgStore.GetOrasReferrers(repoName, "invalid", "invalid") 189 So(err, ShouldNotBeNil) 190 191 _, err = imgStore.GetOrasReferrers(repoName, digest, "invalid") 192 So(err, ShouldNotBeNil) 193 194 // invalid DeleteImageManifest 195 indexPath := path.Join(imgStore.RootDir(), repoName, "index.json") 196 err = os.Chmod(indexPath, 0o000) 197 if err != nil { 198 panic(err) 199 } 200 201 err = imgStore.DeleteImageManifest(repoName, digest.String(), false) 202 So(err, ShouldNotBeNil) 203 204 err = os.RemoveAll(path.Join(imgStore.RootDir(), repoName)) 205 if err != nil { 206 panic(err) 207 } 208 }) 209 }) 210 } 211 212 func TestGetOrasReferrers(t *testing.T) { 213 dir := t.TempDir() 214 215 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 216 metrics := monitoring.NewMetricsServer(false, log) 217 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 218 RootDir: dir, 219 Name: "cache", 220 UseRelPaths: true, 221 }, log) 222 223 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 224 225 Convey("Get referrers", t, func(c C) { 226 err := WriteImageToFileSystem(CreateDefaultVulnerableImage(), "zot-test", "0.0.1", storage.StoreController{ 227 DefaultStore: imgStore, 228 }) 229 So(err, ShouldBeNil) 230 231 body := []byte("this is a blob") 232 digest := godigest.FromBytes(body) 233 buf := bytes.NewBuffer(body) 234 buflen := buf.Len() 235 err = os.WriteFile(path.Join(imgStore.RootDir(), //nolint: gosec 236 "zot-test", "blobs", digest.Algorithm().String(), digest.Encoded()), 237 buf.Bytes(), 0o644) 238 So(err, ShouldBeNil) 239 _, n, err := imgStore.FullBlobUpload("zot-test", buf, digest) 240 So(err, ShouldBeNil) 241 So(n, ShouldEqual, buflen) 242 243 artifactManifest := artifactspec.Manifest{} 244 artifactManifest.ArtifactType = "signature-example" 245 artifactManifest.Subject = &artifactspec.Descriptor{ 246 MediaType: ispec.MediaTypeImageManifest, 247 Digest: digest, 248 Size: int64(buflen), 249 } 250 artifactManifest.Blobs = []artifactspec.Descriptor{} 251 manBuf, err := json.Marshal(artifactManifest) 252 manBufLen := len(manBuf) 253 So(err, ShouldBeNil) 254 manDigest := godigest.FromBytes(manBuf) 255 _, _, err = imgStore.PutImageManifest("zot-test", manDigest.Encoded(), artifactspec.MediaTypeArtifactManifest, manBuf) 256 So(err, ShouldBeNil) 257 258 So(err, ShouldBeNil) 259 descriptors, err := imgStore.GetOrasReferrers("zot-test", digest, "signature-example") 260 So(err, ShouldBeNil) 261 So(descriptors, ShouldNotBeEmpty) 262 So(descriptors[0].ArtifactType, ShouldEqual, "signature-example") 263 So(descriptors[0].MediaType, ShouldEqual, artifactspec.MediaTypeArtifactManifest) 264 So(descriptors[0].Size, ShouldEqual, manBufLen) 265 So(descriptors[0].Digest, ShouldEqual, manDigest) 266 }) 267 } 268 269 func FuzzNewBlobUpload(f *testing.F) { 270 f.Fuzz(func(t *testing.T, data string) { 271 dir := t.TempDir() 272 defer os.RemoveAll(dir) 273 t.Logf("Input argument is %s", data) 274 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 275 metrics := monitoring.NewMetricsServer(false, log) 276 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 277 RootDir: dir, 278 Name: "cache", 279 UseRelPaths: true, 280 }, log) 281 282 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 283 284 _, err := imgStore.NewBlobUpload(data) 285 if err != nil { 286 if isKnownErr(err) { 287 return 288 } 289 290 t.Error(err) 291 } 292 }) 293 } 294 295 func FuzzPutBlobChunk(f *testing.F) { 296 f.Fuzz(func(t *testing.T, data string) { 297 dir := t.TempDir() 298 defer os.RemoveAll(dir) 299 t.Logf("Input argument is %s", data) 300 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 301 302 metrics := monitoring.NewMetricsServer(false, log) 303 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 304 RootDir: dir, 305 Name: "cache", 306 UseRelPaths: true, 307 }, log) 308 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 309 310 repoName := data 311 uuid, err := imgStore.NewBlobUpload(repoName) 312 if err != nil { 313 if isKnownErr(err) { 314 return 315 } 316 317 t.Error(err) 318 } 319 320 buf := bytes.NewBufferString(data) 321 buflen := buf.Len() 322 _, err = imgStore.PutBlobChunk(repoName, uuid, 0, int64(buflen), buf) 323 if err != nil { 324 t.Error(err) 325 } 326 }) 327 } 328 329 func FuzzPutBlobChunkStreamed(f *testing.F) { 330 f.Fuzz(func(t *testing.T, data string) { 331 dir := t.TempDir() 332 defer os.RemoveAll(dir) 333 t.Logf("Input argument is %s", data) 334 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 335 metrics := monitoring.NewMetricsServer(false, log) 336 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 337 RootDir: dir, 338 Name: "cache", 339 UseRelPaths: true, 340 }, log) 341 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 342 343 repoName := data 344 345 uuid, err := imgStore.NewBlobUpload(repoName) 346 if err != nil { 347 if isKnownErr(err) { 348 return 349 } 350 351 t.Error(err) 352 } 353 354 buf := bytes.NewBufferString(data) 355 _, err = imgStore.PutBlobChunkStreamed(repoName, uuid, buf) 356 if err != nil { 357 t.Error(err) 358 } 359 }) 360 } 361 362 func FuzzGetBlobUpload(f *testing.F) { 363 f.Fuzz(func(t *testing.T, data1 string, data2 string) { 364 dir := t.TempDir() 365 defer os.RemoveAll(dir) 366 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 367 metrics := monitoring.NewMetricsServer(false, log) 368 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 369 RootDir: dir, 370 Name: "cache", 371 UseRelPaths: true, 372 }, log) 373 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, 374 cacheDriver) 375 376 _, err := imgStore.GetBlobUpload(data1, data2) 377 if err != nil { 378 if errors.Is(err, zerr.ErrUploadNotFound) || isKnownErr(err) { 379 return 380 } 381 t.Error(err) 382 } 383 }) 384 } 385 386 func FuzzTestPutGetImageManifest(f *testing.F) { 387 f.Fuzz(func(t *testing.T, data []byte) { 388 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 389 metrics := monitoring.NewMetricsServer(false, *log) 390 391 dir := t.TempDir() 392 defer os.RemoveAll(dir) 393 394 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 395 RootDir: dir, 396 Name: "cache", 397 UseRelPaths: true, 398 }, *log) 399 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 400 401 cblob, cdigest := GetRandomImageConfig() 402 403 ldigest, lblob, err := newRandomBlobForFuzz(data) 404 if err != nil { 405 t.Errorf("error occurred while generating random blob, %v", err) 406 } 407 408 _, _, err = imgStore.FullBlobUpload(repoName, bytes.NewReader(cblob), cdigest) 409 if err != nil { 410 t.Error(err) 411 } 412 _, _, err = imgStore.FullBlobUpload(repoName, bytes.NewReader(lblob), ldigest) 413 if err != nil { 414 t.Error(err) 415 } 416 417 manifest, err := NewRandomImgManifest(data, cdigest, ldigest, cblob, lblob) 418 if err != nil { 419 t.Error(err) 420 } 421 manifestBuf, err := json.Marshal(manifest) 422 if err != nil { 423 t.Errorf("Error %v occurred while marshaling manifest", err) 424 } 425 mdigest := godigest.FromBytes(manifestBuf) 426 _, _, err = imgStore.PutImageManifest(repoName, mdigest.String(), ispec.MediaTypeImageManifest, manifestBuf) 427 if err != nil && errors.Is(err, zerr.ErrBadManifest) { 428 t.Errorf("the error that occurred is %v \n", err) 429 } 430 _, _, _, err = imgStore.GetImageManifest(repoName, mdigest.String()) 431 if err != nil { 432 t.Errorf("the error that occurred is %v \n", err) 433 } 434 }) 435 } 436 437 func FuzzTestPutDeleteImageManifest(f *testing.F) { 438 f.Fuzz(func(t *testing.T, data []byte) { 439 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 440 metrics := monitoring.NewMetricsServer(false, *log) 441 442 dir := t.TempDir() 443 defer os.RemoveAll(dir) 444 445 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 446 RootDir: dir, 447 Name: "cache", 448 UseRelPaths: true, 449 }, *log) 450 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 451 452 cblob, cdigest := GetRandomImageConfig() 453 454 ldigest, lblob, err := newRandomBlobForFuzz(data) 455 if err != nil { 456 t.Errorf("error occurred while generating random blob, %v", err) 457 } 458 459 _, _, err = imgStore.FullBlobUpload(repoName, bytes.NewReader(cblob), cdigest) 460 if err != nil { 461 t.Error(err) 462 } 463 464 _, _, err = imgStore.FullBlobUpload(repoName, bytes.NewReader(lblob), ldigest) 465 if err != nil { 466 t.Error(err) 467 } 468 469 manifest, err := NewRandomImgManifest(data, cdigest, ldigest, cblob, lblob) 470 if err != nil { 471 t.Error(err) 472 } 473 474 manifestBuf, err := json.Marshal(manifest) 475 if err != nil { 476 t.Errorf("Error %v occurred while marshaling manifest", err) 477 } 478 mdigest := godigest.FromBytes(manifestBuf) 479 _, _, err = imgStore.PutImageManifest(repoName, mdigest.String(), ispec.MediaTypeImageManifest, manifestBuf) 480 if err != nil && errors.Is(err, zerr.ErrBadManifest) { 481 t.Errorf("the error that occurred is %v \n", err) 482 } 483 484 err = imgStore.DeleteImageManifest(repoName, mdigest.String(), false) 485 if err != nil { 486 if isKnownErr(err) { 487 return 488 } 489 t.Errorf("the error that occurred is %v \n", err) 490 } 491 }) 492 } 493 494 // no integration with PutImageManifest, just throw fuzz data. 495 func FuzzTestDeleteImageManifest(f *testing.F) { 496 f.Fuzz(func(t *testing.T, data []byte) { 497 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 498 metrics := monitoring.NewMetricsServer(false, *log) 499 500 dir := t.TempDir() 501 defer os.RemoveAll(dir) 502 503 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 504 RootDir: dir, 505 Name: "cache", 506 UseRelPaths: true, 507 }, *log) 508 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 509 510 digest, _, err := newRandomBlobForFuzz(data) 511 if err != nil { 512 return 513 } 514 err = imgStore.DeleteImageManifest(string(data), digest.String(), false) 515 if err != nil { 516 if errors.Is(err, zerr.ErrRepoNotFound) || isKnownErr(err) { 517 return 518 } 519 t.Error(err) 520 } 521 }) 522 } 523 524 func FuzzDirExists(f *testing.F) { 525 f.Fuzz(func(t *testing.T, data string) { 526 _ = common.DirExists(data) 527 }) 528 } 529 530 func FuzzInitRepo(f *testing.F) { 531 f.Fuzz(func(t *testing.T, data string) { 532 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 533 metrics := monitoring.NewMetricsServer(false, *log) 534 535 dir := t.TempDir() 536 defer os.RemoveAll(dir) 537 538 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 539 RootDir: dir, 540 Name: "cache", 541 UseRelPaths: true, 542 }, *log) 543 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 544 err := imgStore.InitRepo(data) 545 if err != nil { 546 if isKnownErr(err) { 547 return 548 } 549 t.Error(err) 550 } 551 }) 552 } 553 554 func FuzzInitValidateRepo(f *testing.F) { 555 f.Fuzz(func(t *testing.T, data string) { 556 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 557 metrics := monitoring.NewMetricsServer(false, *log) 558 559 dir := t.TempDir() 560 defer os.RemoveAll(dir) 561 562 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 563 RootDir: dir, 564 Name: "cache", 565 UseRelPaths: true, 566 }, *log) 567 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 568 err := imgStore.InitRepo(data) 569 if err != nil { 570 if isKnownErr(err) { 571 return 572 } 573 t.Error(err) 574 } 575 _, err = imgStore.ValidateRepo(data) 576 if err != nil { 577 if errors.Is(err, zerr.ErrRepoNotFound) || errors.Is(err, zerr.ErrRepoBadVersion) || isKnownErr(err) { 578 return 579 } 580 t.Error(err) 581 } 582 }) 583 } 584 585 func FuzzGetImageTags(f *testing.F) { 586 f.Fuzz(func(t *testing.T, data string) { 587 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 588 metrics := monitoring.NewMetricsServer(false, *log) 589 590 dir := t.TempDir() 591 defer os.RemoveAll(dir) 592 593 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 594 RootDir: dir, 595 Name: "cache", 596 UseRelPaths: true, 597 }, *log) 598 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 599 _, err := imgStore.GetImageTags(data) 600 if err != nil { 601 if errors.Is(err, zerr.ErrRepoNotFound) || isKnownErr(err) { 602 return 603 } 604 t.Error(err) 605 } 606 }) 607 } 608 609 func FuzzBlobUploadPath(f *testing.F) { 610 f.Fuzz(func(t *testing.T, repo, uuid string) { 611 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 612 metrics := monitoring.NewMetricsServer(false, *log) 613 614 dir := t.TempDir() 615 defer os.RemoveAll(dir) 616 617 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 618 RootDir: dir, 619 Name: "cache", 620 UseRelPaths: true, 621 }, *log) 622 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 623 624 _ = imgStore.BlobUploadPath(repo, uuid) 625 }) 626 } 627 628 func FuzzBlobUploadInfo(f *testing.F) { 629 f.Fuzz(func(t *testing.T, data string, uuid string) { 630 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 631 metrics := monitoring.NewMetricsServer(false, *log) 632 633 dir := t.TempDir() 634 defer os.RemoveAll(dir) 635 636 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 637 RootDir: dir, 638 Name: "cache", 639 UseRelPaths: true, 640 }, *log) 641 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 642 repo := data 643 644 _, err := imgStore.BlobUploadInfo(repo, uuid) 645 if err != nil { 646 if isKnownErr(err) { 647 return 648 } 649 t.Error(err) 650 } 651 }) 652 } 653 654 func FuzzTestGetImageManifest(f *testing.F) { 655 f.Fuzz(func(t *testing.T, data string) { 656 dir := t.TempDir() 657 defer os.RemoveAll(dir) 658 659 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 660 metrics := monitoring.NewMetricsServer(false, log) 661 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 662 RootDir: dir, 663 Name: "cache", 664 UseRelPaths: true, 665 }, log) 666 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 667 668 repoName := data 669 670 digest := godigest.FromBytes([]byte(data)) 671 672 _, _, _, err := imgStore.GetImageManifest(repoName, digest.String()) 673 if err != nil { 674 if isKnownErr(err) { 675 return 676 } 677 t.Error(err) 678 } 679 }) 680 } 681 682 func FuzzFinishBlobUpload(f *testing.F) { 683 f.Fuzz(func(t *testing.T, data string) { 684 dir := t.TempDir() 685 defer os.RemoveAll(dir) 686 687 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 688 metrics := monitoring.NewMetricsServer(false, log) 689 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 690 RootDir: dir, 691 Name: "cache", 692 UseRelPaths: true, 693 }, log) 694 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 695 696 repoName := data 697 698 upload, err := imgStore.NewBlobUpload(repoName) 699 if err != nil { 700 if isKnownErr(err) { 701 return 702 } 703 t.Error(err) 704 } 705 706 content := []byte(data) 707 buf := bytes.NewBuffer(content) 708 buflen := buf.Len() 709 digest := godigest.FromBytes(content) 710 711 _, err = imgStore.PutBlobChunk(repoName, upload, 0, int64(buflen), buf) 712 if err != nil { 713 if isKnownErr(err) { 714 return 715 } 716 t.Error(err) 717 } 718 719 err = imgStore.FinishBlobUpload(repoName, upload, buf, digest) 720 if err != nil { 721 if isKnownErr(err) { 722 return 723 } 724 t.Error(err) 725 } 726 }) 727 } 728 729 func FuzzFullBlobUpload(f *testing.F) { 730 f.Fuzz(func(t *testing.T, data []byte) { 731 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 732 metrics := monitoring.NewMetricsServer(false, *log) 733 repoName := "test" 734 735 dir := t.TempDir() 736 defer os.RemoveAll(dir) 737 738 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 739 RootDir: dir, 740 Name: "cache", 741 UseRelPaths: true, 742 }, *log) 743 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 744 745 ldigest, lblob, err := newRandomBlobForFuzz(data) 746 if err != nil { 747 t.Errorf("error occurred while generating random blob, %v", err) 748 } 749 750 _, _, err = imgStore.FullBlobUpload(repoName, bytes.NewReader(lblob), ldigest) 751 if err != nil { 752 if isKnownErr(err) { 753 return 754 } 755 t.Error(err) 756 } 757 }) 758 } 759 760 func TestStorageCacheErrors(t *testing.T) { 761 Convey("get error in DedupeBlob() when cache.Put() deduped blob", t, func() { 762 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 763 metrics := monitoring.NewMetricsServer(false, log) 764 765 dir := t.TempDir() 766 767 originRepo := "dedupe1" 768 dedupedRepo := "dedupe2" 769 770 cblob, cdigest := GetRandomImageConfig() 771 772 getBlobPath := "" 773 774 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, &mocks.CacheMock{ 775 PutBlobFn: func(digest godigest.Digest, path string) error { 776 if strings.Contains(path, dedupedRepo) { 777 return errCache 778 } 779 780 return nil 781 }, 782 GetBlobFn: func(digest godigest.Digest) (string, error) { 783 return getBlobPath, nil 784 }, 785 }) 786 787 err := imgStore.InitRepo(originRepo) 788 So(err, ShouldBeNil) 789 790 err = imgStore.InitRepo(dedupedRepo) 791 So(err, ShouldBeNil) 792 793 _, _, err = imgStore.FullBlobUpload(originRepo, bytes.NewReader(cblob), cdigest) 794 So(err, ShouldBeNil) 795 796 getBlobPath = imgStore.BlobPath(originRepo, cdigest) 797 _, _, err = imgStore.FullBlobUpload(dedupedRepo, bytes.NewReader(cblob), cdigest) 798 So(err, ShouldNotBeNil) 799 }) 800 } 801 802 func FuzzDedupeBlob(f *testing.F) { 803 f.Fuzz(func(t *testing.T, data string) { 804 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 805 metrics := monitoring.NewMetricsServer(false, *log) 806 807 dir := t.TempDir() 808 defer os.RemoveAll(dir) 809 810 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 811 RootDir: dir, 812 Name: "cache", 813 UseRelPaths: true, 814 }, *log) 815 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 816 817 blobDigest := godigest.FromString(data) 818 819 // replacement for .uploads folder, usually retrieved from BlobUploadPath 820 src := path.Join(imgStore.RootDir(), "src") 821 blob := bytes.NewReader([]byte(data)) 822 823 _, _, err := imgStore.FullBlobUpload("repoName", blob, blobDigest) 824 if err != nil { 825 t.Error(err) 826 } 827 828 dst := imgStore.BlobPath("repoName", blobDigest) 829 830 err = os.MkdirAll(src, 0o755) 831 if err != nil { 832 t.Error(err) 833 } 834 835 err = imgStore.DedupeBlob(src, blobDigest, "repoName", dst) 836 if err != nil { 837 t.Error(err) 838 } 839 }) 840 } 841 842 func FuzzDeleteBlobUpload(f *testing.F) { 843 f.Fuzz(func(t *testing.T, data string) { 844 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 845 metrics := monitoring.NewMetricsServer(false, *log) 846 repoName := data 847 848 dir := t.TempDir() 849 defer os.RemoveAll(dir) 850 851 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 852 RootDir: dir, 853 Name: "cache", 854 UseRelPaths: true, 855 }, *log) 856 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 857 858 uuid, err := imgStore.NewBlobUpload(repoName) 859 if err != nil { 860 if isKnownErr(err) { 861 return 862 } 863 t.Error(err) 864 } 865 866 err = imgStore.DeleteBlobUpload(repoName, uuid) 867 if err != nil { 868 t.Error(err) 869 } 870 }) 871 } 872 873 func FuzzBlobPath(f *testing.F) { 874 f.Fuzz(func(t *testing.T, data string) { 875 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 876 metrics := monitoring.NewMetricsServer(false, *log) 877 repoName := data 878 879 dir := t.TempDir() 880 defer os.RemoveAll(dir) 881 882 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 883 RootDir: dir, 884 Name: "cache", 885 UseRelPaths: true, 886 }, *log) 887 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 888 digest := godigest.FromString(data) 889 890 _ = imgStore.BlobPath(repoName, digest) 891 }) 892 } 893 894 func FuzzCheckBlob(f *testing.F) { 895 f.Fuzz(func(t *testing.T, data string) { 896 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 897 metrics := monitoring.NewMetricsServer(false, *log) 898 repoName := data 899 900 dir := t.TempDir() 901 defer os.RemoveAll(dir) 902 903 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 904 RootDir: dir, 905 Name: "cache", 906 UseRelPaths: true, 907 }, *log) 908 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 909 digest := godigest.FromString(data) 910 911 _, _, err := imgStore.FullBlobUpload(repoName, bytes.NewReader([]byte(data)), digest) 912 if err != nil { 913 if isKnownErr(err) { 914 return 915 } 916 t.Error(err) 917 } 918 _, _, err = imgStore.CheckBlob(repoName, digest) 919 if err != nil { 920 t.Error(err) 921 } 922 }) 923 } 924 925 func FuzzGetBlob(f *testing.F) { 926 f.Fuzz(func(t *testing.T, data string) { 927 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 928 metrics := monitoring.NewMetricsServer(false, *log) 929 repoName := data 930 931 dir := t.TempDir() 932 defer os.RemoveAll(dir) 933 934 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 935 RootDir: dir, 936 Name: "cache", 937 UseRelPaths: true, 938 }, *log) 939 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 940 digest := godigest.FromString(data) 941 942 _, _, err := imgStore.FullBlobUpload(repoName, bytes.NewReader([]byte(data)), digest) 943 if err != nil { 944 if isKnownErr(err) { 945 return 946 } 947 t.Error(err) 948 } 949 950 blobReadCloser, _, err := imgStore.GetBlob(repoName, digest, "application/vnd.oci.image.layer.v1.tar+gzip") 951 if err != nil { 952 if isKnownErr(err) { 953 return 954 } 955 t.Error(err) 956 } 957 if err = blobReadCloser.Close(); err != nil { 958 t.Error(err) 959 } 960 }) 961 } 962 963 func FuzzDeleteBlob(f *testing.F) { 964 f.Fuzz(func(t *testing.T, data string) { 965 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 966 metrics := monitoring.NewMetricsServer(false, *log) 967 repoName := data 968 969 dir := t.TempDir() 970 defer os.RemoveAll(dir) 971 972 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 973 RootDir: dir, 974 Name: "cache", 975 UseRelPaths: true, 976 }, *log) 977 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 978 digest := godigest.FromString(data) 979 980 _, _, err := imgStore.FullBlobUpload(repoName, bytes.NewReader([]byte(data)), digest) 981 if err != nil { 982 if isKnownErr(err) { 983 return 984 } 985 t.Error(err) 986 } 987 988 err = imgStore.DeleteBlob(repoName, digest) 989 if err != nil { 990 if isKnownErr(err) { 991 return 992 } 993 t.Error(err) 994 } 995 }) 996 } 997 998 func FuzzGetIndexContent(f *testing.F) { 999 f.Fuzz(func(t *testing.T, data string) { 1000 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 1001 metrics := monitoring.NewMetricsServer(false, *log) 1002 repoName := data 1003 1004 dir := t.TempDir() 1005 defer os.RemoveAll(dir) 1006 1007 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 1008 RootDir: dir, 1009 Name: "cache", 1010 UseRelPaths: true, 1011 }, *log) 1012 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 1013 digest := godigest.FromString(data) 1014 1015 _, _, err := imgStore.FullBlobUpload(repoName, bytes.NewReader([]byte(data)), digest) 1016 if err != nil { 1017 if isKnownErr(err) { 1018 return 1019 } 1020 t.Error(err) 1021 } 1022 1023 _, err = imgStore.GetIndexContent(repoName) 1024 if err != nil { 1025 if isKnownErr(err) { 1026 return 1027 } 1028 t.Error(err) 1029 } 1030 }) 1031 } 1032 1033 func FuzzGetBlobContent(f *testing.F) { 1034 f.Fuzz(func(t *testing.T, data string) { 1035 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 1036 metrics := monitoring.NewMetricsServer(false, *log) 1037 repoName := data 1038 1039 dir := t.TempDir() 1040 defer os.RemoveAll(dir) 1041 1042 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 1043 RootDir: dir, 1044 Name: "cache", 1045 UseRelPaths: true, 1046 }, *log) 1047 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 1048 digest := godigest.FromString(data) 1049 1050 _, _, err := imgStore.FullBlobUpload(repoName, bytes.NewReader([]byte(data)), digest) 1051 if err != nil { 1052 if isKnownErr(err) { 1053 return 1054 } 1055 t.Error(err) 1056 } 1057 1058 _, err = imgStore.GetBlobContent(repoName, digest) 1059 if err != nil { 1060 if isKnownErr(err) { 1061 return 1062 } 1063 t.Error(err) 1064 } 1065 }) 1066 } 1067 1068 func FuzzGetOrasReferrers(f *testing.F) { 1069 f.Fuzz(func(t *testing.T, data string) { 1070 log := &zlog.Logger{Logger: zerolog.New(os.Stdout)} 1071 metrics := monitoring.NewMetricsServer(false, *log) 1072 1073 dir := t.TempDir() 1074 defer os.RemoveAll(dir) 1075 1076 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 1077 RootDir: dir, 1078 Name: "cache", 1079 UseRelPaths: true, 1080 }, *log) 1081 1082 imgStore := local.NewImageStore(dir, true, true, *log, metrics, nil, cacheDriver) 1083 1084 storageCtlr := storage.StoreController{DefaultStore: imgStore} 1085 err := WriteImageToFileSystem(CreateDefaultVulnerableImage(), "zot-test", "0.0.1", storageCtlr) 1086 if err != nil { 1087 t.Error(err) 1088 } 1089 digest := godigest.FromBytes([]byte(data)) 1090 buf := bytes.NewBufferString(data) 1091 buflen := buf.Len() 1092 err = os.WriteFile(path.Join(imgStore.RootDir(), //nolint: gosec 1093 "zot-test", "blobs", digest.Algorithm().String(), digest.Encoded()), 1094 buf.Bytes(), 0o644) 1095 if err != nil { 1096 t.Error(err) 1097 } 1098 _, _, err = imgStore.FullBlobUpload("zot-test", buf, digest) 1099 if err != nil { 1100 t.Error(err) 1101 } 1102 1103 artifactManifest := artifactspec.Manifest{} 1104 artifactManifest.ArtifactType = data 1105 artifactManifest.Subject = &artifactspec.Descriptor{ 1106 MediaType: ispec.MediaTypeImageManifest, 1107 Digest: digest, 1108 Size: int64(buflen), 1109 } 1110 artifactManifest.Blobs = []artifactspec.Descriptor{} 1111 1112 manBuf, err := json.Marshal(artifactManifest) 1113 if err != nil { 1114 t.Error(err) 1115 } 1116 manDigest := godigest.FromBytes(manBuf) 1117 _, _, err = imgStore.PutImageManifest("zot-test", manDigest.Encoded(), artifactspec.MediaTypeArtifactManifest, manBuf) 1118 if err != nil { 1119 t.Error(err) 1120 } 1121 _, err = imgStore.GetOrasReferrers("zot-test", digest, data) 1122 if err != nil { 1123 if errors.Is(err, zerr.ErrManifestNotFound) || isKnownErr(err) { 1124 return 1125 } 1126 t.Error(err) 1127 } 1128 }) 1129 } 1130 1131 func FuzzRunGCRepo(f *testing.F) { 1132 f.Fuzz(func(t *testing.T, data string) { 1133 log := zlog.NewLogger("debug", "") 1134 audit := zlog.NewAuditLogger("debug", "") 1135 1136 metrics := monitoring.NewMetricsServer(false, log) 1137 dir := t.TempDir() 1138 defer os.RemoveAll(dir) 1139 1140 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 1141 RootDir: dir, 1142 Name: "cache", 1143 UseRelPaths: true, 1144 }, log) 1145 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 1146 1147 gc := gc.NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gc.Options{ 1148 Delay: storageConstants.DefaultGCDelay, 1149 ImageRetention: DeleteReferrers, 1150 }, audit, log) 1151 1152 if err := gc.CleanRepo(context.Background(), data); err != nil { 1153 t.Error(err) 1154 } 1155 }) 1156 } 1157 1158 func TestDedupeLinks(t *testing.T) { 1159 testCases := []struct { 1160 dedupe bool 1161 expected bool 1162 }{ 1163 { 1164 dedupe: true, 1165 expected: true, 1166 }, 1167 { 1168 dedupe: false, 1169 expected: false, 1170 }, 1171 } 1172 1173 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 1174 metrics := monitoring.NewMetricsServer(false, log) 1175 1176 for _, testCase := range testCases { 1177 Convey(fmt.Sprintf("Dedupe %t", testCase.dedupe), t, func(c C) { 1178 dir := t.TempDir() 1179 1180 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 1181 RootDir: dir, 1182 Name: "cache", 1183 UseRelPaths: true, 1184 }, log) 1185 1186 var imgStore storageTypes.ImageStore 1187 1188 if testCase.dedupe { 1189 imgStore = local.NewImageStore(dir, testCase.dedupe, true, log, metrics, nil, cacheDriver) 1190 } else { 1191 imgStore = local.NewImageStore(dir, testCase.dedupe, true, log, metrics, nil, nil) 1192 } 1193 1194 // run on empty image store 1195 // switch dedupe to true from false 1196 taskScheduler, cancel := runAndGetScheduler() 1197 1198 // rebuild with dedupe true 1199 imgStore.RunDedupeBlobs(time.Duration(0), taskScheduler) 1200 // wait until rebuild finishes 1201 time.Sleep(1 * time.Second) 1202 1203 cancel() 1204 1205 // manifest1 1206 upload, err := imgStore.NewBlobUpload("dedupe1") 1207 So(err, ShouldBeNil) 1208 So(upload, ShouldNotBeEmpty) 1209 1210 content := []byte("test-data3") 1211 buf := bytes.NewBuffer(content) 1212 buflen := buf.Len() 1213 digest := godigest.FromBytes(content) 1214 blob, err := imgStore.PutBlobChunkStreamed("dedupe1", upload, buf) 1215 So(err, ShouldBeNil) 1216 So(blob, ShouldEqual, buflen) 1217 blobDigest1 := strings.Split(digest.String(), ":")[1] 1218 So(blobDigest1, ShouldNotBeEmpty) 1219 1220 err = imgStore.FinishBlobUpload("dedupe1", upload, buf, digest) 1221 So(err, ShouldBeNil) 1222 So(blob, ShouldEqual, buflen) 1223 1224 _, _, err = imgStore.CheckBlob("dedupe1", digest) 1225 So(err, ShouldBeNil) 1226 1227 blobrc, _, err := imgStore.GetBlob("dedupe1", digest, "application/vnd.oci.image.layer.v1.tar+gzip") 1228 So(err, ShouldBeNil) 1229 err = blobrc.Close() 1230 So(err, ShouldBeNil) 1231 1232 cblob, cdigest := GetRandomImageConfig() 1233 _, clen, err := imgStore.FullBlobUpload("dedupe1", bytes.NewReader(cblob), cdigest) 1234 So(err, ShouldBeNil) 1235 So(clen, ShouldEqual, len(cblob)) 1236 hasBlob, _, err := imgStore.CheckBlob("dedupe1", cdigest) 1237 So(err, ShouldBeNil) 1238 So(hasBlob, ShouldEqual, true) 1239 1240 manifest := ispec.Manifest{ 1241 Config: ispec.Descriptor{ 1242 MediaType: "application/vnd.oci.image.config.v1+json", 1243 Digest: cdigest, 1244 Size: int64(len(cblob)), 1245 }, 1246 Layers: []ispec.Descriptor{ 1247 { 1248 MediaType: "application/vnd.oci.image.layer.v1.tar", 1249 Digest: digest, 1250 Size: int64(buflen), 1251 }, 1252 }, 1253 } 1254 manifest.SchemaVersion = 2 1255 manifestBuf, err := json.Marshal(manifest) 1256 So(err, ShouldBeNil) 1257 manifestDigest := godigest.FromBytes(manifestBuf) 1258 _, _, err = imgStore.PutImageManifest("dedupe1", manifestDigest.String(), 1259 ispec.MediaTypeImageManifest, manifestBuf) 1260 So(err, ShouldBeNil) 1261 1262 _, _, _, err = imgStore.GetImageManifest("dedupe1", manifestDigest.String()) 1263 So(err, ShouldBeNil) 1264 1265 // manifest2 1266 upload, err = imgStore.NewBlobUpload("dedupe2") 1267 So(err, ShouldBeNil) 1268 So(upload, ShouldNotBeEmpty) 1269 1270 content = []byte("test-data3") 1271 buf = bytes.NewBuffer(content) 1272 buflen = buf.Len() 1273 digest = godigest.FromBytes(content) 1274 blob, err = imgStore.PutBlobChunkStreamed("dedupe2", upload, buf) 1275 So(err, ShouldBeNil) 1276 So(blob, ShouldEqual, buflen) 1277 blobDigest2 := strings.Split(digest.String(), ":")[1] 1278 So(blobDigest2, ShouldNotBeEmpty) 1279 1280 err = imgStore.FinishBlobUpload("dedupe2", upload, buf, digest) 1281 So(err, ShouldBeNil) 1282 So(blob, ShouldEqual, buflen) 1283 1284 _, _, err = imgStore.CheckBlob("dedupe2", digest) 1285 So(err, ShouldBeNil) 1286 1287 blobrc, _, err = imgStore.GetBlob("dedupe2", digest, "application/vnd.oci.image.layer.v1.tar+gzip") 1288 So(err, ShouldBeNil) 1289 err = blobrc.Close() 1290 So(err, ShouldBeNil) 1291 1292 cblob, cdigest = GetRandomImageConfig() 1293 _, clen, err = imgStore.FullBlobUpload("dedupe2", bytes.NewReader(cblob), cdigest) 1294 So(err, ShouldBeNil) 1295 So(clen, ShouldEqual, len(cblob)) 1296 hasBlob, _, err = imgStore.CheckBlob("dedupe2", cdigest) 1297 So(err, ShouldBeNil) 1298 So(hasBlob, ShouldEqual, true) 1299 1300 manifest = ispec.Manifest{ 1301 Config: ispec.Descriptor{ 1302 MediaType: "application/vnd.oci.image.config.v1+json", 1303 Digest: cdigest, 1304 Size: int64(len(cblob)), 1305 }, 1306 Layers: []ispec.Descriptor{ 1307 { 1308 MediaType: "application/vnd.oci.image.layer.v1.tar", 1309 Digest: digest, 1310 Size: int64(buflen), 1311 }, 1312 }, 1313 } 1314 manifest.SchemaVersion = 2 1315 manifestBuf, err = json.Marshal(manifest) 1316 So(err, ShouldBeNil) 1317 digest = godigest.FromBytes(manifestBuf) 1318 _, _, err = imgStore.PutImageManifest("dedupe2", "1.0", ispec.MediaTypeImageManifest, manifestBuf) 1319 So(err, ShouldBeNil) 1320 1321 _, _, _, err = imgStore.GetImageManifest("dedupe2", digest.String()) 1322 So(err, ShouldBeNil) 1323 1324 // verify that dedupe with hard links happened 1325 fi1, err := os.Stat(path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest1)) 1326 So(err, ShouldBeNil) 1327 fi2, err := os.Stat(path.Join(dir, "dedupe2", "blobs", "sha256", blobDigest2)) 1328 So(err, ShouldBeNil) 1329 So(os.SameFile(fi1, fi2), ShouldEqual, testCase.expected) 1330 1331 if !testCase.dedupe { 1332 Convey("delete blobs from storage/cache should work when dedupe is false", func() { 1333 So(blobDigest1, ShouldEqual, blobDigest2) 1334 1335 // to not trigger BlobInUse err, delete manifest first 1336 err = imgStore.DeleteImageManifest("dedupe1", manifestDigest.String(), false) 1337 So(err, ShouldBeNil) 1338 1339 err = imgStore.DeleteImageManifest("dedupe2", "1.0", false) 1340 So(err, ShouldBeNil) 1341 1342 err = imgStore.DeleteBlob("dedupe1", godigest.NewDigestFromEncoded(godigest.SHA256, blobDigest1)) 1343 So(err, ShouldBeNil) 1344 1345 err = imgStore.DeleteBlob("dedupe2", godigest.NewDigestFromEncoded(godigest.SHA256, blobDigest2)) 1346 So(err, ShouldBeNil) 1347 }) 1348 1349 Convey("test RunDedupeForDigest directly, trigger stat error on original blob", func() { 1350 // rebuild with dedupe true 1351 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 1352 1353 duplicateBlobs := []string{ 1354 path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest1), 1355 path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest2), 1356 } 1357 1358 // remove original blob so that it can not be statted 1359 err := os.Remove(path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest1)) 1360 So(err, ShouldBeNil) 1361 1362 err = imgStore.RunDedupeForDigest(context.TODO(), godigest.Digest(blobDigest1), true, duplicateBlobs) 1363 So(err, ShouldNotBeNil) 1364 }) 1365 1366 Convey("Intrerrupt rebuilding and restart, checking idempotency", func() { 1367 for i := 0; i < 10; i++ { 1368 taskScheduler, cancel := runAndGetScheduler() 1369 // rebuild with dedupe true 1370 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 1371 1372 imgStore.RunDedupeBlobs(time.Duration(0), taskScheduler) 1373 sleepValue := i * 5 1374 time.Sleep(time.Duration(sleepValue) * time.Millisecond) 1375 1376 cancel() 1377 } 1378 1379 taskScheduler, cancel := runAndGetScheduler() 1380 1381 // rebuild with dedupe true 1382 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 1383 imgStore.RunDedupeBlobs(time.Duration(0), taskScheduler) 1384 1385 // wait until rebuild finishes 1386 time.Sleep(10 * time.Second) 1387 1388 cancel() 1389 1390 fi1, err := os.Stat(path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest1)) 1391 So(err, ShouldBeNil) 1392 fi2, err := os.Stat(path.Join(dir, "dedupe2", "blobs", "sha256", blobDigest2)) 1393 So(err, ShouldBeNil) 1394 So(os.SameFile(fi1, fi2), ShouldEqual, true) 1395 }) 1396 1397 Convey("rebuild dedupe index error cache nil", func() { 1398 // switch dedupe to true from false 1399 taskScheduler, cancel := runAndGetScheduler() 1400 1401 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, nil) 1402 1403 // rebuild with dedupe true 1404 imgStore.RunDedupeBlobs(time.Duration(0), taskScheduler) 1405 // wait until rebuild finishes 1406 1407 time.Sleep(3 * time.Second) 1408 1409 cancel() 1410 1411 fi1, err := os.Stat(path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest1)) 1412 So(err, ShouldBeNil) 1413 fi2, err := os.Stat(path.Join(dir, "dedupe2", "blobs", "sha256", blobDigest2)) 1414 So(err, ShouldBeNil) 1415 1416 So(os.SameFile(fi1, fi2), ShouldEqual, false) 1417 }) 1418 1419 Convey("rebuild dedupe index cache error on original blob", func() { 1420 // switch dedupe to true from false 1421 taskScheduler, cancel := runAndGetScheduler() 1422 1423 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, &mocks.CacheMock{ 1424 HasBlobFn: func(digest godigest.Digest, path string) bool { 1425 return false 1426 }, 1427 PutBlobFn: func(digest godigest.Digest, path string) error { 1428 return errCache 1429 }, 1430 }) 1431 // rebuild with dedupe true, should have samefile blobs 1432 imgStore.RunDedupeBlobs(time.Duration(0), taskScheduler) 1433 // wait until rebuild finishes 1434 1435 time.Sleep(10 * time.Second) 1436 1437 cancel() 1438 1439 fi1, err := os.Stat(path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest1)) 1440 So(err, ShouldBeNil) 1441 fi2, err := os.Stat(path.Join(dir, "dedupe2", "blobs", "sha256", blobDigest2)) 1442 So(err, ShouldBeNil) 1443 1444 So(os.SameFile(fi1, fi2), ShouldEqual, false) 1445 }) 1446 1447 Convey("rebuild dedupe index cache error on duplicate blob", func() { 1448 // switch dedupe to true from false 1449 taskScheduler, cancel := runAndGetScheduler() 1450 1451 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, &mocks.CacheMock{ 1452 HasBlobFn: func(digest godigest.Digest, path string) bool { 1453 return false 1454 }, 1455 PutBlobFn: func(digest godigest.Digest, path string) error { 1456 if strings.Contains(path, "dedupe2") { 1457 return errCache 1458 } 1459 1460 return nil 1461 }, 1462 }) 1463 // rebuild with dedupe true, should have samefile blobs 1464 imgStore.RunDedupeBlobs(time.Duration(0), taskScheduler) 1465 // wait until rebuild finishes 1466 1467 time.Sleep(15 * time.Second) 1468 1469 cancel() 1470 1471 fi1, err := os.Stat(path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest1)) 1472 So(err, ShouldBeNil) 1473 fi2, err := os.Stat(path.Join(dir, "dedupe2", "blobs", "sha256", blobDigest2)) 1474 So(err, ShouldBeNil) 1475 1476 So(os.SameFile(fi1, fi2), ShouldEqual, true) 1477 }) 1478 } 1479 1480 Convey("delete blobs from storage/cache should work when dedupe is true", func() { 1481 So(blobDigest1, ShouldEqual, blobDigest2) 1482 1483 // to not trigger BlobInUse err, delete manifest first 1484 err = imgStore.DeleteImageManifest("dedupe1", manifestDigest.String(), false) 1485 So(err, ShouldBeNil) 1486 1487 err = imgStore.DeleteImageManifest("dedupe2", "1.0", false) 1488 So(err, ShouldBeNil) 1489 1490 err = imgStore.DeleteBlob("dedupe1", godigest.NewDigestFromEncoded(godigest.SHA256, blobDigest1)) 1491 So(err, ShouldBeNil) 1492 1493 err = imgStore.DeleteBlob("dedupe2", godigest.NewDigestFromEncoded(godigest.SHA256, blobDigest2)) 1494 So(err, ShouldBeNil) 1495 }) 1496 1497 Convey("storage and cache inconsistency", func() { 1498 // delete blobs 1499 err = os.Remove(path.Join(dir, "dedupe1", "blobs", "sha256", blobDigest1)) 1500 So(err, ShouldBeNil) 1501 1502 err := os.Remove(path.Join(dir, "dedupe2", "blobs", "sha256", blobDigest2)) 1503 So(err, ShouldBeNil) 1504 1505 // now cache is inconsistent with storage (blobs present in cache but not in storage) 1506 upload, err = imgStore.NewBlobUpload("dedupe3") 1507 So(err, ShouldBeNil) 1508 So(upload, ShouldNotBeEmpty) 1509 1510 content = []byte("test-data3") 1511 buf = bytes.NewBuffer(content) 1512 buflen = buf.Len() 1513 digest = godigest.FromBytes(content) 1514 blob, err = imgStore.PutBlobChunkStreamed("dedupe3", upload, buf) 1515 So(err, ShouldBeNil) 1516 So(blob, ShouldEqual, buflen) 1517 blobDigest2 := strings.Split(digest.String(), ":")[1] 1518 So(blobDigest2, ShouldNotBeEmpty) 1519 1520 err = imgStore.FinishBlobUpload("dedupe3", upload, buf, digest) 1521 So(err, ShouldBeNil) 1522 So(blob, ShouldEqual, buflen) 1523 }) 1524 }) 1525 } 1526 } 1527 1528 func TestDedupe(t *testing.T) { 1529 Convey("Dedupe", t, func(c C) { 1530 Convey("Nil ImageStore", func() { 1531 var is storageTypes.ImageStore 1532 So(func() { _ = is.DedupeBlob("", "", "", "") }, ShouldPanic) 1533 }) 1534 1535 Convey("Valid ImageStore", func() { 1536 dir := t.TempDir() 1537 1538 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 1539 metrics := monitoring.NewMetricsServer(false, log) 1540 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 1541 RootDir: dir, 1542 Name: "cache", 1543 UseRelPaths: true, 1544 }, log) 1545 1546 il := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 1547 1548 So(il.DedupeBlob("", "", "", ""), ShouldNotBeNil) 1549 }) 1550 }) 1551 } 1552 1553 //nolint:gocyclo 1554 func TestNegativeCases(t *testing.T) { 1555 Convey("Invalid root dir", t, func(c C) { 1556 dir := t.TempDir() 1557 1558 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 1559 metrics := monitoring.NewMetricsServer(false, log) 1560 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 1561 RootDir: dir, 1562 Name: "cache", 1563 UseRelPaths: true, 1564 }, log) 1565 1566 So(local.NewImageStore(dir, true, 1567 true, log, metrics, nil, cacheDriver), ShouldNotBeNil) 1568 if os.Geteuid() != 0 { 1569 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 1570 RootDir: "/deadBEEF", 1571 Name: "cache", 1572 UseRelPaths: true, 1573 }, log) 1574 So(local.NewImageStore("/deadBEEF", true, true, log, metrics, nil, cacheDriver), ShouldBeNil) 1575 } 1576 }) 1577 1578 Convey("Invalid init repo", t, func(c C) { 1579 dir := t.TempDir() 1580 1581 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 1582 metrics := monitoring.NewMetricsServer(false, log) 1583 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 1584 RootDir: dir, 1585 Name: "cache", 1586 UseRelPaths: true, 1587 }, log) 1588 1589 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 1590 1591 err := os.Chmod(dir, 0o000) // remove all perms 1592 if err != nil { 1593 panic(err) 1594 } 1595 1596 if os.Geteuid() != 0 { 1597 err = imgStore.InitRepo("test") 1598 So(err, ShouldNotBeNil) 1599 } 1600 1601 err = os.Chmod(dir, 0o755) 1602 if err != nil { 1603 panic(err) 1604 } 1605 1606 // Init repo should fail if repo is a file. 1607 err = os.WriteFile(path.Join(dir, "file-test"), []byte("this is test file"), 0o755) //nolint:gosec 1608 So(err, ShouldBeNil) 1609 err = imgStore.InitRepo("file-test") 1610 So(err, ShouldNotBeNil) 1611 1612 err = os.Mkdir(path.Join(dir, "test-dir"), 0o755) 1613 So(err, ShouldBeNil) 1614 1615 err = imgStore.InitRepo("test-dir") 1616 So(err, ShouldBeNil) 1617 1618 // Init repo should fail if repo is invalid UTF-8 1619 err = imgStore.InitRepo("hi \255") 1620 So(err, ShouldNotBeNil) 1621 1622 // Init repo should fail if repo name does not match spec 1623 err = imgStore.InitRepo("_trivy") 1624 So(err, ShouldNotBeNil) 1625 So(errors.Is(err, zerr.ErrInvalidRepositoryName), ShouldBeTrue) 1626 }) 1627 1628 Convey("Invalid validate repo", t, func(c C) { 1629 dir := t.TempDir() 1630 1631 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 1632 metrics := monitoring.NewMetricsServer(false, log) 1633 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 1634 RootDir: dir, 1635 Name: "cache", 1636 UseRelPaths: true, 1637 }, log) 1638 1639 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 1640 1641 So(imgStore, ShouldNotBeNil) 1642 So(imgStore.InitRepo("test"), ShouldBeNil) 1643 1644 err := os.MkdirAll(path.Join(dir, "invalid-test"), 0o755) 1645 So(err, ShouldBeNil) 1646 1647 err = os.Chmod(path.Join(dir, "invalid-test"), 0o000) // remove all perms 1648 if err != nil { 1649 panic(err) 1650 } 1651 _, err = imgStore.ValidateRepo("invalid-test") 1652 So(err, ShouldNotBeNil) 1653 So(err, ShouldEqual, zerr.ErrRepoNotFound) 1654 1655 err = os.Chmod(path.Join(dir, "invalid-test"), 0o755) // remove all perms 1656 if err != nil { 1657 panic(err) 1658 } 1659 1660 err = os.WriteFile(path.Join(dir, "invalid-test", "blobs"), []byte{}, 0o755) //nolint: gosec 1661 if err != nil { 1662 panic(err) 1663 } 1664 1665 err = os.WriteFile(path.Join(dir, "invalid-test", "index.json"), []byte{}, 0o755) //nolint: gosec 1666 if err != nil { 1667 panic(err) 1668 } 1669 1670 err = os.WriteFile(path.Join(dir, "invalid-test", ispec.ImageLayoutFile), []byte{}, 0o755) //nolint: gosec 1671 if err != nil { 1672 panic(err) 1673 } 1674 1675 isValid, err := imgStore.ValidateRepo("invalid-test") 1676 So(err, ShouldBeNil) 1677 So(isValid, ShouldEqual, false) 1678 1679 err = os.Remove(path.Join(dir, "invalid-test", "blobs")) 1680 if err != nil { 1681 panic(err) 1682 } 1683 err = os.Mkdir(path.Join(dir, "invalid-test", "blobs"), 0o755) 1684 if err != nil { 1685 panic(err) 1686 } 1687 isValid, err = imgStore.ValidateRepo("invalid-test") 1688 So(err, ShouldNotBeNil) 1689 So(isValid, ShouldEqual, false) 1690 1691 err = os.WriteFile(path.Join(dir, "invalid-test", ispec.ImageLayoutFile), []byte("{}"), 0o755) //nolint: gosec 1692 if err != nil { 1693 panic(err) 1694 } 1695 1696 isValid, err = imgStore.ValidateRepo("invalid-test") 1697 So(err, ShouldNotBeNil) 1698 So(err, ShouldEqual, zerr.ErrRepoBadVersion) 1699 So(isValid, ShouldEqual, false) 1700 1701 files, err := os.ReadDir(path.Join(dir, "test")) 1702 if err != nil { 1703 panic(err) 1704 } 1705 1706 for _, f := range files { 1707 os.Remove(path.Join(dir, "test", f.Name())) 1708 } 1709 1710 _, err = imgStore.ValidateRepo("test") 1711 So(err, ShouldNotBeNil) 1712 1713 err = os.RemoveAll(path.Join(dir, "test")) 1714 if err != nil { 1715 panic(err) 1716 } 1717 1718 _, err = imgStore.ValidateRepo("test") 1719 So(err, ShouldNotBeNil) 1720 1721 err = os.Chmod(dir, 0o000) // remove all perms 1722 if err != nil { 1723 panic(err) 1724 } 1725 1726 _, err = imgStore.GetRepositories() 1727 So(err, ShouldNotBeNil) 1728 1729 err = os.Chmod(dir, 0o755) // add perms 1730 if err != nil { 1731 panic(err) 1732 } 1733 1734 err = os.RemoveAll(dir) 1735 if err != nil { 1736 panic(err) 1737 } 1738 }) 1739 1740 Convey("Invalid get image tags", t, func(c C) { 1741 dir := t.TempDir() 1742 1743 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 1744 metrics := monitoring.NewMetricsServer(false, log) 1745 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 1746 RootDir: dir, 1747 Name: "cache", 1748 UseRelPaths: true, 1749 }, log) 1750 1751 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 1752 1753 So(imgStore, ShouldNotBeNil) 1754 So(imgStore.InitRepo("test"), ShouldBeNil) 1755 So(os.Remove(path.Join(dir, "test", "index.json")), ShouldBeNil) 1756 _, err := imgStore.GetImageTags("test") 1757 So(err, ShouldNotBeNil) 1758 So(os.RemoveAll(path.Join(dir, "test")), ShouldBeNil) 1759 So(imgStore.InitRepo("test"), ShouldBeNil) 1760 So(os.WriteFile(path.Join(dir, "test", "index.json"), []byte{}, 0o600), ShouldBeNil) 1761 _, err = imgStore.GetImageTags("test") 1762 So(err, ShouldNotBeNil) 1763 }) 1764 1765 Convey("Invalid get image manifest", t, func(c C) { 1766 dir := t.TempDir() 1767 1768 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 1769 metrics := monitoring.NewMetricsServer(false, log) 1770 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 1771 RootDir: dir, 1772 Name: "cache", 1773 UseRelPaths: true, 1774 }, log) 1775 1776 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 1777 1778 So(imgStore, ShouldNotBeNil) 1779 So(imgStore.InitRepo("test"), ShouldBeNil) 1780 1781 err := os.Chmod(path.Join(dir, "test", "index.json"), 0o000) 1782 if err != nil { 1783 panic(err) 1784 } 1785 1786 _, _, _, err = imgStore.GetImageManifest("test", "") 1787 So(err, ShouldNotBeNil) 1788 1789 err = os.Remove(path.Join(dir, "test", "index.json")) 1790 if err != nil { 1791 panic(err) 1792 } 1793 1794 _, _, _, err = imgStore.GetImageManifest("test", "") 1795 So(err, ShouldNotBeNil) 1796 1797 err = os.RemoveAll(path.Join(dir, "test")) 1798 if err != nil { 1799 panic(err) 1800 } 1801 1802 So(imgStore.InitRepo("test"), ShouldBeNil) 1803 1804 err = os.WriteFile(path.Join(dir, "test", "index.json"), []byte{}, 0o600) 1805 if err != nil { 1806 panic(err) 1807 } 1808 _, _, _, err = imgStore.GetImageManifest("test", "") 1809 So(err, ShouldNotBeNil) 1810 }) 1811 1812 Convey("Invalid new blob upload", t, func(c C) { 1813 dir := t.TempDir() 1814 1815 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 1816 metrics := monitoring.NewMetricsServer(false, log) 1817 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 1818 RootDir: dir, 1819 Name: "cache", 1820 UseRelPaths: true, 1821 }, log) 1822 1823 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 1824 1825 So(imgStore, ShouldNotBeNil) 1826 So(imgStore.InitRepo("test"), ShouldBeNil) 1827 1828 err := os.Chmod(path.Join(dir, "test", ".uploads"), 0o000) 1829 if err != nil { 1830 panic(err) 1831 } 1832 _, err = imgStore.NewBlobUpload("test") 1833 So(err, ShouldNotBeNil) 1834 1835 err = os.Chmod(path.Join(dir, "test"), 0o000) 1836 if err != nil { 1837 panic(err) 1838 } 1839 1840 _, err = imgStore.NewBlobUpload("test") 1841 So(err, ShouldNotBeNil) 1842 1843 err = os.Chmod(path.Join(dir, "test"), 0o755) 1844 if err != nil { 1845 panic(err) 1846 } 1847 1848 So(imgStore.InitRepo("test"), ShouldBeNil) 1849 1850 _, err = imgStore.NewBlobUpload("test") 1851 So(err, ShouldNotBeNil) 1852 1853 err = os.Chmod(path.Join(dir, "test", ".uploads"), 0o755) 1854 if err != nil { 1855 panic(err) 1856 } 1857 1858 upload, err := imgStore.NewBlobUpload("test") 1859 So(err, ShouldBeNil) 1860 1861 err = os.Chmod(path.Join(dir, "test", ".uploads"), 0o000) 1862 if err != nil { 1863 panic(err) 1864 } 1865 t.Cleanup(func() { 1866 err = os.Chmod(path.Join(dir, "test", ".uploads"), 0o700) 1867 if err != nil { 1868 panic(err) 1869 } 1870 }) 1871 1872 content := []byte("test-data3") 1873 buf := bytes.NewBuffer(content) 1874 l := buf.Len() 1875 _, err = imgStore.PutBlobChunkStreamed("test", upload, buf) 1876 So(err, ShouldNotBeNil) 1877 1878 _, err = imgStore.PutBlobChunk("test", upload, 0, int64(l), buf) 1879 So(err, ShouldNotBeNil) 1880 }) 1881 1882 Convey("DirExists call with a filename as argument", t, func(c C) { 1883 dir := t.TempDir() 1884 1885 filePath := path.Join(dir, "file.txt") 1886 err := os.WriteFile(filePath, []byte("some dummy file content"), 0o644) //nolint: gosec 1887 if err != nil { 1888 panic(err) 1889 } 1890 1891 ok := common.DirExists(filePath) 1892 So(ok, ShouldBeFalse) 1893 }) 1894 1895 Convey("DirExists call with invalid UTF-8 as argument", t, func(c C) { 1896 dir := t.TempDir() 1897 1898 filePath := path.Join(dir, "hi \255") 1899 ok := common.DirExists(filePath) 1900 So(ok, ShouldBeFalse) 1901 }) 1902 1903 Convey("DirExists call with name too long as argument", t, func(c C) { 1904 var builder strings.Builder 1905 for i := 0; i < 1025; i++ { 1906 _, err := builder.WriteString("0") 1907 if err != nil { 1908 t.Fatal(err) 1909 } 1910 } 1911 path := builder.String() 1912 ok := common.DirExists(path) 1913 So(ok, ShouldBeFalse) 1914 }) 1915 } 1916 1917 func TestHardLink(t *testing.T) { 1918 Convey("Test that ValidateHardLink creates rootDir if it does not exist", t, func() { 1919 var randomDir string 1920 1921 for { 1922 nBig, err := rand.Int(rand.Reader, big.NewInt(100)) 1923 if err != nil { 1924 panic(err) 1925 } 1926 randomDir = "/tmp/" + randSeq(int(nBig.Int64())) 1927 1928 if _, err := os.Stat(randomDir); os.IsNotExist(err) { 1929 break 1930 } 1931 } 1932 defer os.RemoveAll(randomDir) 1933 1934 err := local.ValidateHardLink(randomDir) 1935 So(err, ShouldBeNil) 1936 }) 1937 Convey("Test that ValidateHardLink returns error if rootDir is a file", t, func() { 1938 dir := t.TempDir() 1939 1940 filePath := path.Join(dir, "file.txt") 1941 err := os.WriteFile(filePath, []byte("some dummy file content"), 0o644) //nolint: gosec 1942 if err != nil { 1943 panic(err) 1944 } 1945 1946 err = local.ValidateHardLink(filePath) 1947 So(err, ShouldNotBeNil) 1948 }) 1949 Convey("Test if filesystem supports hardlink", t, func() { 1950 dir := t.TempDir() 1951 1952 err := local.ValidateHardLink(dir) 1953 So(err, ShouldBeNil) 1954 1955 err = os.WriteFile(path.Join(dir, "hardtest.txt"), []byte("testing hard link code"), 0o644) //nolint: gosec 1956 if err != nil { 1957 panic(err) 1958 } 1959 1960 err = os.Chmod(dir, 0o400) 1961 if err != nil { 1962 panic(err) 1963 } 1964 // Allow hardtest.txt to be cleaned up by t.TempDir() 1965 t.Cleanup(func() { 1966 err = os.Chmod(dir, 0o700) 1967 if err != nil { 1968 t.Fatal(err) 1969 } 1970 }) 1971 1972 err = os.Link(path.Join(dir, "hardtest.txt"), path.Join(dir, "duphardtest.txt")) 1973 So(err, ShouldNotBeNil) 1974 1975 err = os.Chmod(dir, 0o644) 1976 if err != nil { 1977 panic(err) 1978 } 1979 }) 1980 } 1981 1982 func TestInjectWriteFile(t *testing.T) { 1983 Convey("writeFile without commit", t, func() { 1984 dir := t.TempDir() 1985 1986 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 1987 metrics := monitoring.NewMetricsServer(false, log) 1988 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 1989 RootDir: dir, 1990 Name: "cache", 1991 UseRelPaths: true, 1992 }, log) 1993 1994 imgStore := local.NewImageStore(dir, true, false, log, metrics, nil, cacheDriver) 1995 1996 Convey("Failure path not reached", func() { 1997 err := imgStore.InitRepo("repo1") 1998 So(err, ShouldBeNil) 1999 }) 2000 }) 2001 } 2002 2003 func TestGarbageCollectForImageStore(t *testing.T) { 2004 //nolint: contextcheck 2005 Convey("Garbage collect for a specific repo from an ImageStore", t, func(c C) { 2006 dir := t.TempDir() 2007 2008 ctx := context.Background() 2009 2010 Convey("Garbage collect error for repo with config removed", func() { 2011 logFile, _ := os.CreateTemp("", "zot-log*.txt") 2012 2013 defer os.Remove(logFile.Name()) // clean up 2014 2015 log := zlog.NewLogger("debug", logFile.Name()) 2016 audit := zlog.NewAuditLogger("debug", "") 2017 2018 metrics := monitoring.NewMetricsServer(false, log) 2019 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 2020 RootDir: dir, 2021 Name: "cache", 2022 UseRelPaths: true, 2023 }, log) 2024 2025 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 2026 repoName := "gc-all-repos-short" 2027 2028 gc := gc.NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gc.Options{ 2029 Delay: 1 * time.Second, 2030 ImageRetention: DeleteReferrers, 2031 }, audit, log) 2032 2033 image := CreateDefaultVulnerableImage() 2034 err := WriteImageToFileSystem(image, repoName, "0.0.1", storage.StoreController{ 2035 DefaultStore: imgStore, 2036 }) 2037 So(err, ShouldBeNil) 2038 2039 manifestDigest := image.ManifestDescriptor.Digest 2040 err = os.Remove(path.Join(dir, repoName, "blobs/sha256", manifestDigest.Encoded())) 2041 if err != nil { 2042 panic(err) 2043 } 2044 2045 err = gc.CleanRepo(ctx, repoName) 2046 So(err, ShouldNotBeNil) 2047 2048 time.Sleep(500 * time.Millisecond) 2049 2050 data, err := os.ReadFile(logFile.Name()) 2051 So(err, ShouldBeNil) 2052 So(string(data), ShouldContainSubstring, 2053 fmt.Sprintf("error while running GC for %s", path.Join(imgStore.RootDir(), repoName))) 2054 }) 2055 2056 Convey("Garbage collect error - not enough permissions to access index.json", func() { 2057 logFile, _ := os.CreateTemp("", "zot-log*.txt") 2058 2059 defer os.Remove(logFile.Name()) // clean up 2060 2061 log := zlog.NewLogger("debug", logFile.Name()) 2062 audit := zlog.NewAuditLogger("debug", "") 2063 2064 metrics := monitoring.NewMetricsServer(false, log) 2065 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 2066 RootDir: dir, 2067 Name: "cache", 2068 UseRelPaths: true, 2069 }, log) 2070 2071 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 2072 repoName := "gc-all-repos-short" 2073 2074 gc := gc.NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gc.Options{ 2075 Delay: 1 * time.Second, 2076 ImageRetention: DeleteReferrers, 2077 }, audit, log) 2078 2079 image := CreateDefaultVulnerableImage() 2080 err := WriteImageToFileSystem(image, repoName, "0.0.1", storage.StoreController{ 2081 DefaultStore: imgStore, 2082 }) 2083 So(err, ShouldBeNil) 2084 2085 So(os.Chmod(path.Join(dir, repoName, "index.json"), 0o000), ShouldBeNil) 2086 2087 err = gc.CleanRepo(ctx, repoName) 2088 So(err, ShouldNotBeNil) 2089 2090 time.Sleep(500 * time.Millisecond) 2091 2092 data, err := os.ReadFile(logFile.Name()) 2093 So(err, ShouldBeNil) 2094 So(string(data), ShouldContainSubstring, 2095 fmt.Sprintf("error while running GC for %s", path.Join(imgStore.RootDir(), repoName))) 2096 So(os.Chmod(path.Join(dir, repoName, "index.json"), 0o755), ShouldBeNil) 2097 }) 2098 2099 Convey("Garbage collect - the manifest which the reference points to can be found", func() { 2100 logFile, _ := os.CreateTemp("", "zot-log*.txt") 2101 2102 defer os.Remove(logFile.Name()) // clean up 2103 2104 log := zlog.NewLogger("debug", logFile.Name()) 2105 audit := zlog.NewAuditLogger("debug", "") 2106 2107 metrics := monitoring.NewMetricsServer(false, log) 2108 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 2109 RootDir: dir, 2110 Name: "cache", 2111 UseRelPaths: true, 2112 }, log) 2113 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 2114 repoName := "gc-sig" 2115 2116 gc := gc.NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gc.Options{ 2117 Delay: 1 * time.Second, 2118 ImageRetention: DeleteReferrers, 2119 }, audit, log) 2120 2121 storeController := storage.StoreController{DefaultStore: imgStore} 2122 img := CreateRandomImage() 2123 2124 err := WriteImageToFileSystem(img, repoName, "tag1", storeController) 2125 So(err, ShouldBeNil) 2126 2127 // add fake signature for tag1 2128 cosignTag, err := signature.GetCosignSignatureTagForManifest(img.Manifest) 2129 So(err, ShouldBeNil) 2130 2131 cosignSig := CreateRandomImage() 2132 So(err, ShouldBeNil) 2133 2134 err = WriteImageToFileSystem(cosignSig, repoName, cosignTag, storeController) 2135 So(err, ShouldBeNil) 2136 2137 // add sbom 2138 manifestBlob, err := json.Marshal(img.Manifest) 2139 So(err, ShouldBeNil) 2140 2141 manifestDigest := godigest.FromBytes(manifestBlob) 2142 sbomTag := fmt.Sprintf("sha256-%s.%s", manifestDigest.Encoded(), "sbom") 2143 So(err, ShouldBeNil) 2144 2145 sbomImg := CreateRandomImage() 2146 So(err, ShouldBeNil) 2147 2148 err = WriteImageToFileSystem(sbomImg, repoName, sbomTag, storeController) 2149 So(err, ShouldBeNil) 2150 2151 // add fake signature for tag1 2152 notationSig := CreateImageWith(). 2153 RandomLayers(1, 10). 2154 ArtifactConfig("application/vnd.cncf.notary.signature"). 2155 Subject(img.DescriptorRef()).Build() 2156 2157 err = WriteImageToFileSystem(notationSig, repoName, "notation", storeController) 2158 So(err, ShouldBeNil) 2159 2160 // add fake signature for tag1 2161 cosignWithReferrersSig := CreateImageWith(). 2162 RandomLayers(1, 10). 2163 ArtifactConfig(common.ArtifactTypeCosign). 2164 Subject(img.DescriptorRef()).Build() 2165 2166 err = WriteImageToFileSystem(cosignWithReferrersSig, repoName, "cosign", storeController) 2167 So(err, ShouldBeNil) 2168 2169 err = gc.CleanRepo(ctx, repoName) 2170 So(err, ShouldBeNil) 2171 }) 2172 }) 2173 } 2174 2175 func TestGarbageCollectImageUnknownManifest(t *testing.T) { 2176 Convey("Garbage collect with short delay", t, func() { 2177 ctx := context.Background() 2178 2179 dir := t.TempDir() 2180 2181 log := zlog.NewLogger("debug", "") 2182 audit := zlog.NewAuditLogger("debug", "") 2183 2184 metrics := monitoring.NewMetricsServer(false, log) 2185 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 2186 RootDir: dir, 2187 Name: "cache", 2188 UseRelPaths: true, 2189 }, log) 2190 2191 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 2192 2193 storeController := storage.StoreController{ 2194 DefaultStore: imgStore, 2195 } 2196 2197 gc := gc.NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gc.Options{ 2198 Delay: 1 * time.Second, 2199 ImageRetention: DeleteReferrers, 2200 }, audit, log) 2201 2202 unsupportedMediaType := "application/vnd.oci.artifact.manifest.v1+json" 2203 2204 img := CreateRandomImage() 2205 2206 err := WriteImageToFileSystem(img, repoName, "v1", storeController) 2207 So(err, ShouldBeNil) 2208 2209 // add image with unsupported media type 2210 artifact := CreateRandomImage() 2211 2212 err = WriteImageToFileSystem(artifact, repoName, "artifact", storeController) 2213 So(err, ShouldBeNil) 2214 2215 // add referrer with unsupported media type 2216 subjectDesc := img.Descriptor() 2217 referrer := CreateRandomImageWith().Subject(&subjectDesc).Build() 2218 2219 err = WriteImageToFileSystem(referrer, repoName, referrer.Digest().String(), storeController) 2220 So(err, ShouldBeNil) 2221 2222 // modify artifact media type 2223 artifactBuf, err := os.ReadFile(imgStore.BlobPath(repoName, artifact.Digest())) 2224 So(err, ShouldBeNil) 2225 2226 var artifactManifest ispec.Manifest 2227 err = json.Unmarshal(artifactBuf, &artifactManifest) 2228 So(err, ShouldBeNil) 2229 2230 artifactManifest.MediaType = unsupportedMediaType 2231 artifactBuf, err = json.Marshal(artifactManifest) 2232 So(err, ShouldBeNil) 2233 2234 artifactDigest := godigest.FromBytes(artifactBuf) 2235 err = os.WriteFile(path.Join(imgStore.RootDir(), repoName, "blobs", "sha256", artifactDigest.Encoded()), 2236 artifactBuf, storageConstants.DefaultFilePerms) 2237 So(err, ShouldBeNil) 2238 2239 // modify referrer media type 2240 referrerBuf, err := os.ReadFile(imgStore.BlobPath(repoName, referrer.Digest())) 2241 So(err, ShouldBeNil) 2242 2243 var referrerManifest ispec.Manifest 2244 err = json.Unmarshal(referrerBuf, &referrerManifest) 2245 So(err, ShouldBeNil) 2246 2247 referrerManifest.MediaType = unsupportedMediaType 2248 referrerBuf, err = json.Marshal(referrerManifest) 2249 So(err, ShouldBeNil) 2250 2251 referrerDigest := godigest.FromBytes(referrerBuf) 2252 err = os.WriteFile(path.Join(imgStore.RootDir(), repoName, "blobs", "sha256", referrerDigest.Encoded()), 2253 referrerBuf, storageConstants.DefaultFilePerms) 2254 So(err, ShouldBeNil) 2255 2256 indexJSONBuf, err := os.ReadFile(path.Join(imgStore.RootDir(), repoName, "index.json")) 2257 So(err, ShouldBeNil) 2258 2259 var indexJSON ispec.Index 2260 err = json.Unmarshal(indexJSONBuf, &indexJSON) 2261 So(err, ShouldBeNil) 2262 2263 for idx, desc := range indexJSON.Manifests { 2264 if desc.Digest == artifact.Digest() { 2265 indexJSON.Manifests[idx].Digest = artifactDigest 2266 indexJSON.Manifests[idx].MediaType = unsupportedMediaType 2267 } else if desc.Digest == referrer.Digest() { 2268 indexJSON.Manifests[idx].Digest = referrerDigest 2269 indexJSON.Manifests[idx].MediaType = unsupportedMediaType 2270 } 2271 } 2272 2273 indexJSONBuf, err = json.Marshal(indexJSON) 2274 So(err, ShouldBeNil) 2275 2276 err = os.WriteFile(path.Join(imgStore.RootDir(), repoName, "index.json"), 2277 indexJSONBuf, storageConstants.DefaultFilePerms) 2278 So(err, ShouldBeNil) 2279 2280 // sleep so orphan blob can be GC'ed 2281 time.Sleep(1 * time.Second) 2282 2283 Convey("Garbage collect blobs referenced by manifest with unsupported media type", func() { 2284 err = gc.CleanRepo(ctx, repoName) 2285 So(err, ShouldBeNil) 2286 2287 _, _, _, err = imgStore.GetImageManifest(repoName, img.DigestStr()) 2288 So(err, ShouldBeNil) 2289 2290 hasBlob, _, err := imgStore.CheckBlob(repoName, img.ConfigDescriptor.Digest) 2291 So(err, ShouldBeNil) 2292 So(hasBlob, ShouldEqual, true) 2293 2294 _, _, _, err = imgStore.GetImageManifest(repoName, artifactDigest.String()) 2295 So(err, ShouldNotBeNil) 2296 2297 _, _, _, err = imgStore.GetImageManifest(repoName, referrerDigest.String()) 2298 So(err, ShouldNotBeNil) 2299 2300 hasBlob, _, err = imgStore.CheckBlob(repoName, artifactDigest) 2301 So(err, ShouldNotBeNil) 2302 So(hasBlob, ShouldEqual, false) 2303 2304 hasBlob, _, err = imgStore.CheckBlob(repoName, referrerDigest) 2305 So(err, ShouldNotBeNil) 2306 So(hasBlob, ShouldEqual, false) 2307 2308 hasBlob, _, err = imgStore.CheckBlob(repoName, artifact.ConfigDescriptor.Digest) 2309 So(err, ShouldNotBeNil) 2310 So(hasBlob, ShouldEqual, false) 2311 2312 hasBlob, _, err = imgStore.CheckBlob(repoName, referrer.ConfigDescriptor.Digest) 2313 So(err, ShouldNotBeNil) 2314 So(hasBlob, ShouldEqual, false) 2315 }) 2316 2317 Convey("Garbage collect - gc repo after manifest delete", func() { 2318 err = imgStore.DeleteImageManifest(repoName, img.DigestStr(), true) 2319 So(err, ShouldBeNil) 2320 2321 err = gc.CleanRepo(ctx, repoName) 2322 So(err, ShouldBeNil) 2323 2324 _, _, _, err = imgStore.GetImageManifest(repoName, img.DigestStr()) 2325 So(err, ShouldNotBeNil) 2326 2327 hasBlob, _, err := imgStore.CheckBlob(repoName, img.ConfigDescriptor.Digest) 2328 So(err, ShouldNotBeNil) 2329 So(hasBlob, ShouldEqual, false) 2330 2331 _, _, _, err = imgStore.GetImageManifest(repoName, artifactDigest.String()) 2332 So(err, ShouldNotBeNil) 2333 2334 _, _, _, err = imgStore.GetImageManifest(repoName, referrerDigest.String()) 2335 So(err, ShouldNotBeNil) 2336 2337 hasBlob, _, err = imgStore.CheckBlob(repoName, artifactDigest) 2338 So(err, ShouldNotBeNil) 2339 So(hasBlob, ShouldEqual, false) 2340 2341 hasBlob, _, err = imgStore.CheckBlob(repoName, referrerDigest) 2342 So(err, ShouldNotBeNil) 2343 So(hasBlob, ShouldEqual, false) 2344 2345 hasBlob, _, err = imgStore.CheckBlob(repoName, artifact.ConfigDescriptor.Digest) 2346 So(err, ShouldNotBeNil) 2347 So(hasBlob, ShouldEqual, false) 2348 2349 hasBlob, _, err = imgStore.CheckBlob(repoName, referrer.ConfigDescriptor.Digest) 2350 So(err, ShouldNotBeNil) 2351 So(hasBlob, ShouldEqual, false) 2352 }) 2353 }) 2354 } 2355 2356 func TestGarbageCollectErrors(t *testing.T) { 2357 Convey("Make image store", t, func(c C) { 2358 ctx := context.Background() 2359 2360 dir := t.TempDir() 2361 2362 log := zlog.NewLogger("debug", "") 2363 audit := zlog.NewAuditLogger("debug", "") 2364 2365 metrics := monitoring.NewMetricsServer(false, log) 2366 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 2367 RootDir: dir, 2368 Name: "cache", 2369 UseRelPaths: true, 2370 }, log) 2371 2372 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 2373 repoName := "gc-index" 2374 2375 gc := gc.NewGarbageCollect(imgStore, mocks.MetaDBMock{}, gc.Options{ 2376 Delay: 500 * time.Millisecond, 2377 ImageRetention: DeleteReferrers, 2378 }, audit, log) 2379 2380 // create a blob/layer 2381 upload, err := imgStore.NewBlobUpload(repoName) 2382 So(err, ShouldBeNil) 2383 So(upload, ShouldNotBeEmpty) 2384 2385 content := []byte("this is a blob1") 2386 buf := bytes.NewBuffer(content) 2387 buflen := buf.Len() 2388 digest := godigest.FromBytes(content) 2389 So(digest, ShouldNotBeNil) 2390 blob, err := imgStore.PutBlobChunkStreamed(repoName, upload, buf) 2391 So(err, ShouldBeNil) 2392 So(blob, ShouldEqual, buflen) 2393 bdgst1 := digest 2394 bsize1 := len(content) 2395 2396 err = imgStore.FinishBlobUpload(repoName, upload, buf, digest) 2397 So(err, ShouldBeNil) 2398 So(blob, ShouldEqual, buflen) 2399 2400 Convey("Trigger error on GetImageIndex", func() { 2401 var index ispec.Index 2402 index.SchemaVersion = 2 2403 index.MediaType = ispec.MediaTypeImageIndex 2404 2405 for i := 0; i < 4; i++ { 2406 // upload image config blob 2407 upload, err = imgStore.NewBlobUpload(repoName) 2408 So(err, ShouldBeNil) 2409 So(upload, ShouldNotBeEmpty) 2410 2411 cblob, cdigest := GetRandomImageConfig() 2412 buf = bytes.NewBuffer(cblob) 2413 buflen = buf.Len() 2414 blob, err = imgStore.PutBlobChunkStreamed(repoName, upload, buf) 2415 So(err, ShouldBeNil) 2416 So(blob, ShouldEqual, buflen) 2417 2418 err = imgStore.FinishBlobUpload(repoName, upload, buf, cdigest) 2419 So(err, ShouldBeNil) 2420 So(blob, ShouldEqual, buflen) 2421 2422 // create a manifest 2423 manifest := ispec.Manifest{ 2424 Config: ispec.Descriptor{ 2425 MediaType: ispec.MediaTypeImageConfig, 2426 Digest: cdigest, 2427 Size: int64(len(cblob)), 2428 }, 2429 Layers: []ispec.Descriptor{ 2430 { 2431 MediaType: ispec.MediaTypeImageLayer, 2432 Digest: bdgst1, 2433 Size: int64(bsize1), 2434 }, 2435 }, 2436 } 2437 manifest.SchemaVersion = 2 2438 content, err = json.Marshal(manifest) 2439 So(err, ShouldBeNil) 2440 digest = godigest.FromBytes(content) 2441 So(digest, ShouldNotBeNil) 2442 _, _, err = imgStore.PutImageManifest(repoName, digest.String(), ispec.MediaTypeImageManifest, content) 2443 So(err, ShouldBeNil) 2444 2445 index.Manifests = append(index.Manifests, ispec.Descriptor{ 2446 Digest: digest, 2447 MediaType: ispec.MediaTypeImageManifest, 2448 Size: int64(len(content)), 2449 }) 2450 } 2451 2452 // upload index image 2453 indexContent, err := json.Marshal(index) 2454 So(err, ShouldBeNil) 2455 indexDigest := godigest.FromBytes(indexContent) 2456 So(indexDigest, ShouldNotBeNil) 2457 2458 _, _, err = imgStore.PutImageManifest(repoName, "1.0", ispec.MediaTypeImageIndex, indexContent) 2459 So(err, ShouldBeNil) 2460 2461 err = os.Chmod(imgStore.BlobPath(repoName, indexDigest), 0o000) 2462 So(err, ShouldBeNil) 2463 2464 time.Sleep(500 * time.Millisecond) 2465 2466 err = gc.CleanRepo(ctx, repoName) 2467 So(err, ShouldNotBeNil) 2468 }) 2469 2470 Convey("Trigger error on GetBlobContent and Unmarshal for untagged manifest", func() { 2471 // upload image config blob 2472 upload, err = imgStore.NewBlobUpload(repoName) 2473 So(err, ShouldBeNil) 2474 So(upload, ShouldNotBeEmpty) 2475 2476 cblob, cdigest := GetRandomImageConfig() 2477 buf = bytes.NewBuffer(cblob) 2478 buflen = buf.Len() 2479 blob, err = imgStore.PutBlobChunkStreamed(repoName, upload, buf) 2480 So(err, ShouldBeNil) 2481 So(blob, ShouldEqual, buflen) 2482 2483 err = imgStore.FinishBlobUpload(repoName, upload, buf, cdigest) 2484 So(err, ShouldBeNil) 2485 So(blob, ShouldEqual, buflen) 2486 2487 // create a manifest 2488 manifest := ispec.Manifest{ 2489 Config: ispec.Descriptor{ 2490 MediaType: ispec.MediaTypeImageConfig, 2491 Digest: cdigest, 2492 Size: int64(len(cblob)), 2493 }, 2494 Layers: []ispec.Descriptor{ 2495 { 2496 MediaType: ispec.MediaTypeImageLayer, 2497 Digest: bdgst1, 2498 Size: int64(bsize1), 2499 }, 2500 }, 2501 } 2502 manifest.SchemaVersion = 2 2503 content, err = json.Marshal(manifest) 2504 So(err, ShouldBeNil) 2505 digest = godigest.FromBytes(content) 2506 So(digest, ShouldNotBeNil) 2507 2508 _, _, err = imgStore.PutImageManifest(repoName, digest.String(), ispec.MediaTypeImageManifest, content) 2509 So(err, ShouldBeNil) 2510 2511 // trigger GetBlobContent error 2512 err = os.Remove(imgStore.BlobPath(repoName, digest)) 2513 So(err, ShouldBeNil) 2514 2515 time.Sleep(500 * time.Millisecond) 2516 2517 err = gc.CleanRepo(ctx, repoName) 2518 So(err, ShouldNotBeNil) 2519 2520 // trigger Unmarshal error 2521 _, err = os.Create(imgStore.BlobPath(repoName, digest)) 2522 So(err, ShouldBeNil) 2523 2524 err = gc.CleanRepo(ctx, repoName) 2525 So(err, ShouldNotBeNil) 2526 }) 2527 2528 Convey("Trigger manifest conflict error", func() { 2529 // upload image config blob 2530 upload, err = imgStore.NewBlobUpload(repoName) 2531 So(err, ShouldBeNil) 2532 So(upload, ShouldNotBeEmpty) 2533 2534 cblob, cdigest := GetRandomImageConfig() 2535 buf = bytes.NewBuffer(cblob) 2536 buflen = buf.Len() 2537 blob, err = imgStore.PutBlobChunkStreamed(repoName, upload, buf) 2538 So(err, ShouldBeNil) 2539 So(blob, ShouldEqual, buflen) 2540 2541 err = imgStore.FinishBlobUpload(repoName, upload, buf, cdigest) 2542 So(err, ShouldBeNil) 2543 So(blob, ShouldEqual, buflen) 2544 2545 // create a manifest 2546 manifest := ispec.Manifest{ 2547 Config: ispec.Descriptor{ 2548 MediaType: ispec.MediaTypeImageConfig, 2549 Digest: cdigest, 2550 Size: int64(len(cblob)), 2551 }, 2552 Layers: []ispec.Descriptor{ 2553 { 2554 MediaType: ispec.MediaTypeImageLayer, 2555 Digest: bdgst1, 2556 Size: int64(bsize1), 2557 }, 2558 }, 2559 } 2560 manifest.SchemaVersion = 2 2561 content, err = json.Marshal(manifest) 2562 So(err, ShouldBeNil) 2563 digest = godigest.FromBytes(content) 2564 So(digest, ShouldNotBeNil) 2565 2566 _, _, err = imgStore.PutImageManifest(repoName, digest.String(), ispec.MediaTypeImageManifest, content) 2567 So(err, ShouldBeNil) 2568 // upload again same manifest so that we trigger manifest conflict 2569 _, _, err = imgStore.PutImageManifest(repoName, "1.0", ispec.MediaTypeImageManifest, content) 2570 So(err, ShouldBeNil) 2571 2572 time.Sleep(500 * time.Millisecond) 2573 2574 err = gc.CleanRepo(ctx, repoName) 2575 So(err, ShouldBeNil) 2576 2577 // blob shouldn't be gc'ed //TODO check this one 2578 found, _, err := imgStore.CheckBlob(repoName, digest) 2579 So(err, ShouldBeNil) 2580 So(found, ShouldEqual, true) 2581 }) 2582 }) 2583 } 2584 2585 func randSeq(n int) string { 2586 letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 2587 2588 buf := make([]rune, n) 2589 for index := range buf { 2590 nBig, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters)))) 2591 if err != nil { 2592 panic(err) 2593 } 2594 2595 buf[index] = letters[int(nBig.Int64())] 2596 } 2597 2598 return string(buf) 2599 } 2600 2601 func TestInitRepo(t *testing.T) { 2602 Convey("Get error when creating BlobUploadDir subdir on initRepo", t, func() { 2603 dir := t.TempDir() 2604 2605 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 2606 metrics := monitoring.NewMetricsServer(false, log) 2607 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 2608 RootDir: dir, 2609 Name: "cache", 2610 UseRelPaths: true, 2611 }, log) 2612 2613 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 2614 2615 err := os.Mkdir(path.Join(dir, "test-dir"), 0o000) 2616 So(err, ShouldBeNil) 2617 2618 err = imgStore.InitRepo("test-dir") 2619 So(err, ShouldNotBeNil) 2620 }) 2621 } 2622 2623 func TestValidateRepo(t *testing.T) { 2624 Convey("Get error when unable to read directory", t, func() { 2625 dir := t.TempDir() 2626 2627 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 2628 metrics := monitoring.NewMetricsServer(false, log) 2629 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 2630 RootDir: dir, 2631 Name: "cache", 2632 UseRelPaths: true, 2633 }, log) 2634 2635 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 2636 2637 err := os.Mkdir(path.Join(dir, "test-dir"), 0o000) 2638 So(err, ShouldBeNil) 2639 2640 _, err = imgStore.ValidateRepo("test-dir") 2641 So(err, ShouldNotBeNil) 2642 }) 2643 2644 Convey("Get error when repo name is not compliant with repo spec", t, func() { 2645 dir := t.TempDir() 2646 2647 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 2648 metrics := monitoring.NewMetricsServer(false, log) 2649 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 2650 RootDir: dir, 2651 Name: "cache", 2652 UseRelPaths: true, 2653 }, log) 2654 2655 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 2656 2657 _, err := imgStore.ValidateRepo(".") 2658 So(err, ShouldNotBeNil) 2659 So(errors.Is(err, zerr.ErrInvalidRepositoryName), ShouldBeTrue) 2660 2661 _, err = imgStore.ValidateRepo("..") 2662 So(err, ShouldNotBeNil) 2663 So(errors.Is(err, zerr.ErrInvalidRepositoryName), ShouldBeTrue) 2664 2665 err = os.Mkdir(path.Join(dir, "_test-dir"), 0o755) 2666 So(err, ShouldBeNil) 2667 2668 _, err = imgStore.ValidateRepo("_test-dir") 2669 So(err, ShouldNotBeNil) 2670 So(errors.Is(err, zerr.ErrInvalidRepositoryName), ShouldBeTrue) 2671 2672 err = os.Mkdir(path.Join(dir, ".test-dir"), 0o755) 2673 So(err, ShouldBeNil) 2674 2675 _, err = imgStore.ValidateRepo(".test-dir") 2676 So(err, ShouldNotBeNil) 2677 So(errors.Is(err, zerr.ErrInvalidRepositoryName), ShouldBeTrue) 2678 2679 err = os.Mkdir(path.Join(dir, "-test-dir"), 0o755) 2680 So(err, ShouldBeNil) 2681 2682 _, err = imgStore.ValidateRepo("-test-dir") 2683 So(err, ShouldNotBeNil) 2684 So(errors.Is(err, zerr.ErrInvalidRepositoryName), ShouldBeTrue) 2685 }) 2686 } 2687 2688 func TestGetRepositories(t *testing.T) { 2689 Convey("Verify errors and repos returned by GetRepositories()", t, func() { 2690 dir := t.TempDir() 2691 2692 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 2693 metrics := monitoring.NewMetricsServer(false, log) 2694 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 2695 RootDir: dir, 2696 Name: "cache", 2697 UseRelPaths: true, 2698 }, log) 2699 2700 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 2701 2702 // Create valid directory with permissions 2703 err := os.Mkdir(path.Join(dir, "test-dir"), 0o755) //nolint: gosec 2704 So(err, ShouldBeNil) 2705 2706 err = os.WriteFile(path.Join(dir, "test-dir/test-file"), []byte("this is test file"), 0o755) //nolint: gosec 2707 So(err, ShouldBeNil) 2708 2709 // Folder is not a repo as it is missing the requires files/subfolder 2710 repos, err := imgStore.GetRepositories() 2711 So(err, ShouldBeNil) 2712 So(len(repos), ShouldEqual, 0) 2713 2714 il := ispec.ImageLayout{Version: ispec.ImageLayoutVersion} 2715 layoutFileContent, err := json.Marshal(il) 2716 So(err, ShouldBeNil) 2717 2718 // Folder becomes a repo after the missing content is added 2719 err = os.Mkdir(path.Join(dir, "test-dir", "blobs"), 0o755) //nolint: gosec 2720 So(err, ShouldBeNil) 2721 2722 err = os.Mkdir(path.Join(dir, "test-dir", storageConstants.BlobUploadDir), 0o755) //nolint: gosec 2723 So(err, ShouldBeNil) 2724 2725 err = os.WriteFile(path.Join(dir, "test-dir", "index.json"), []byte{}, 0o755) //nolint: gosec 2726 So(err, ShouldBeNil) 2727 2728 err = os.WriteFile(path.Join(dir, "test-dir", ispec.ImageLayoutFile), layoutFileContent, 0o755) //nolint: gosec 2729 So(err, ShouldBeNil) 2730 2731 // Verify the new repo is turned 2732 repos, err = imgStore.GetRepositories() 2733 So(err, ShouldBeNil) 2734 So(len(repos), ShouldEqual, 1) 2735 So(repos[0], ShouldEqual, "test-dir") 2736 2737 // create directory starting with underscore, which is not OCI a dist spec compliant repo name 2738 // [a-z0-9]+([._-][a-z0-9]+)*(/[a-z0-9]+([._-][a-z0-9]+)*)* 2739 err = os.MkdirAll(path.Join(dir, "_trivy", "db"), 0o755) 2740 So(err, ShouldBeNil) 2741 2742 err = os.WriteFile(path.Join(dir, "_trivy", "db", "trivy.db"), []byte("this is test file"), 0o755) //nolint: gosec 2743 So(err, ShouldBeNil) 2744 2745 // Folder with invalid name is not a repo as it is missing the requires files/subfolder 2746 repos, err = imgStore.GetRepositories() 2747 So(err, ShouldBeNil) 2748 So(len(repos), ShouldEqual, 1) 2749 So(repos[0], ShouldEqual, "test-dir") 2750 2751 // Add missing content to folder with invalid name 2752 err = os.Mkdir(path.Join(dir, "_trivy", "blobs"), 0o755) //nolint: gosec 2753 So(err, ShouldBeNil) 2754 2755 err = os.Mkdir(path.Join(dir, "_trivy", storageConstants.BlobUploadDir), 0o755) //nolint: gosec 2756 So(err, ShouldBeNil) 2757 2758 err = os.WriteFile(path.Join(dir, "_trivy", "index.json"), []byte{}, 0o755) //nolint: gosec 2759 So(err, ShouldBeNil) 2760 2761 err = os.WriteFile(path.Join(dir, "_trivy", ispec.ImageLayoutFile), layoutFileContent, 0o755) //nolint: gosec 2762 So(err, ShouldBeNil) 2763 2764 // Folder with invalid name doesn't become a repo after the missing content is added 2765 repos, err = imgStore.GetRepositories() 2766 So(err, ShouldBeNil) 2767 t.Logf("repos %v", repos) 2768 So(len(repos), ShouldEqual, 1) 2769 So(repos[0], ShouldEqual, "test-dir") 2770 2771 // Rename folder with invalid name to a valid one 2772 err = os.Rename(path.Join(dir, "_trivy"), path.Join(dir, "test-dir-2")) 2773 So(err, ShouldBeNil) 2774 2775 // Verify both repos are now visible 2776 repos, err = imgStore.GetRepositories() 2777 So(err, ShouldBeNil) 2778 t.Logf("repos %v", repos) 2779 So(len(repos), ShouldEqual, 2) 2780 So(repos, ShouldContain, "test-dir") 2781 So(repos, ShouldContain, "test-dir-2") 2782 }) 2783 2784 Convey("Verify GetRepositories() doesn't return '.' when having an oci layout as root directory ", t, func() { 2785 dir := t.TempDir() 2786 2787 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 2788 metrics := monitoring.NewMetricsServer(false, log) 2789 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 2790 RootDir: dir, 2791 Name: "cache", 2792 UseRelPaths: true, 2793 }, log) 2794 2795 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 2796 2797 // Root dir does not contain repos 2798 repos, err := imgStore.GetRepositories() 2799 So(err, ShouldBeNil) 2800 So(len(repos), ShouldEqual, 0) 2801 2802 // Configure root directory as an oci layout 2803 err = os.Mkdir(path.Join(dir, "blobs"), 0o755) //nolint: gosec 2804 So(err, ShouldBeNil) 2805 2806 err = os.Mkdir(path.Join(dir, storageConstants.BlobUploadDir), 0o755) //nolint: gosec 2807 So(err, ShouldBeNil) 2808 2809 err = os.WriteFile(path.Join(dir, "index.json"), []byte{}, 0o755) //nolint: gosec 2810 So(err, ShouldBeNil) 2811 2812 il := ispec.ImageLayout{Version: ispec.ImageLayoutVersion} 2813 layoutFileContent, err := json.Marshal(il) 2814 So(err, ShouldBeNil) 2815 2816 err = os.WriteFile(path.Join(dir, ispec.ImageLayoutFile), layoutFileContent, 0o755) //nolint: gosec 2817 So(err, ShouldBeNil) 2818 2819 // Verify root directory is not returned as a repo 2820 repos, err = imgStore.GetRepositories() 2821 So(err, ShouldBeNil) 2822 t.Logf("repos %v", repos) 2823 So(len(repos), ShouldEqual, 0) 2824 }) 2825 2826 Convey("Verify GetRepositories() doesn't return '..'", t, func() { 2827 dir := t.TempDir() 2828 2829 rootDir := path.Join(dir, "rootDir") 2830 err := os.Mkdir(rootDir, 0o755) 2831 So(err, ShouldBeNil) 2832 2833 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 2834 metrics := monitoring.NewMetricsServer(false, log) 2835 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 2836 RootDir: rootDir, 2837 Name: "cache", 2838 UseRelPaths: true, 2839 }, log) 2840 2841 imgStore := local.NewImageStore(rootDir, 2842 true, true, log, metrics, nil, cacheDriver, 2843 ) 2844 2845 // Root dir does not contain repos 2846 repos, err := imgStore.GetRepositories() 2847 So(err, ShouldBeNil) 2848 So(len(repos), ShouldEqual, 0) 2849 2850 // Configure parent of root directory as an oci layout 2851 err = os.Mkdir(path.Join(dir, "blobs"), 0o755) //nolint: gosec 2852 So(err, ShouldBeNil) 2853 2854 err = os.Mkdir(path.Join(dir, storageConstants.BlobUploadDir), 0o755) //nolint: gosec 2855 So(err, ShouldBeNil) 2856 2857 err = os.WriteFile(path.Join(dir, "index.json"), []byte{}, 0o755) //nolint: gosec 2858 So(err, ShouldBeNil) 2859 2860 il := ispec.ImageLayout{Version: ispec.ImageLayoutVersion} 2861 layoutFileContent, err := json.Marshal(il) 2862 So(err, ShouldBeNil) 2863 2864 err = os.WriteFile(path.Join(dir, ispec.ImageLayoutFile), layoutFileContent, 0o755) //nolint: gosec 2865 So(err, ShouldBeNil) 2866 2867 // Verify root directory is not returned as a repo 2868 repos, err = imgStore.GetRepositories() 2869 So(err, ShouldBeNil) 2870 t.Logf("repos %v", repos) 2871 So(len(repos), ShouldEqual, 0) 2872 }) 2873 } 2874 2875 func TestGetNextRepository(t *testing.T) { 2876 dir := t.TempDir() 2877 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 2878 metrics := monitoring.NewMetricsServer(false, log) 2879 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 2880 RootDir: dir, 2881 Name: "cache", 2882 UseRelPaths: true, 2883 }, log) 2884 2885 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 2886 firstRepoName := "repo1" 2887 secondRepoName := "repo2" 2888 2889 srcStorageCtlr := storage.StoreController{DefaultStore: imgStore} 2890 image := CreateDefaultImage() 2891 2892 err := WriteImageToFileSystem(image, firstRepoName, "0.0.1", srcStorageCtlr) 2893 if err != nil { 2894 t.Log(err) 2895 t.FailNow() 2896 } 2897 2898 err = WriteImageToFileSystem(image, secondRepoName, "0.0.1", srcStorageCtlr) 2899 if err != nil { 2900 t.Log(err) 2901 t.FailNow() 2902 } 2903 2904 Convey("Return first repository", t, func() { 2905 firstRepo, err := imgStore.GetNextRepository("") 2906 So(firstRepo, ShouldEqual, firstRepoName) 2907 So(err, ShouldBeNil) 2908 }) 2909 2910 Convey("Return second repository", t, func() { 2911 secondRepo, err := imgStore.GetNextRepository(firstRepoName) 2912 So(secondRepo, ShouldEqual, secondRepoName) 2913 So(err, ShouldBeNil) 2914 }) 2915 2916 Convey("Return error", t, func() { 2917 err := os.Chmod(imgStore.RootDir(), 0o000) 2918 So(err, ShouldBeNil) 2919 _, err = imgStore.GetNextRepository(firstRepoName) 2920 So(err, ShouldNotBeNil) 2921 err = os.Chmod(imgStore.RootDir(), 0o755) 2922 So(err, ShouldBeNil) 2923 }) 2924 } 2925 2926 func TestPutBlobChunkStreamed(t *testing.T) { 2927 Convey("Get error on opening file", t, func() { 2928 dir := t.TempDir() 2929 2930 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 2931 metrics := monitoring.NewMetricsServer(false, log) 2932 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 2933 RootDir: dir, 2934 Name: "cache", 2935 UseRelPaths: true, 2936 }, log) 2937 2938 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 2939 2940 uuid, err := imgStore.NewBlobUpload("test") 2941 So(err, ShouldBeNil) 2942 2943 var reader io.Reader 2944 blobPath := imgStore.BlobUploadPath("test", uuid) 2945 err = os.Chmod(blobPath, 0o000) 2946 So(err, ShouldBeNil) 2947 2948 _, err = imgStore.PutBlobChunkStreamed("test", uuid, reader) 2949 So(err, ShouldNotBeNil) 2950 }) 2951 } 2952 2953 func TestPullRange(t *testing.T) { 2954 Convey("Repo layout", t, func(c C) { 2955 dir := t.TempDir() 2956 2957 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 2958 metrics := monitoring.NewMetricsServer(false, log) 2959 2960 Convey("Negative cases", func() { 2961 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 2962 RootDir: dir, 2963 Name: "cache", 2964 UseRelPaths: true, 2965 }, log) 2966 2967 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 2968 repoName := "pull-range" 2969 2970 upload, err := imgStore.NewBlobUpload(repoName) 2971 So(err, ShouldBeNil) 2972 So(upload, ShouldNotBeEmpty) 2973 2974 content := []byte("test-data1") 2975 buf := bytes.NewBuffer(content) 2976 buflen := buf.Len() 2977 bdigest := godigest.FromBytes(content) 2978 2979 blob, err := imgStore.PutBlobChunk(repoName, upload, 0, int64(buflen), buf) 2980 So(err, ShouldBeNil) 2981 So(blob, ShouldEqual, buflen) 2982 2983 err = imgStore.FinishBlobUpload(repoName, upload, buf, bdigest) 2984 So(err, ShouldBeNil) 2985 2986 _, _, _, err = imgStore.GetBlobPartial(repoName, "", "application/octet-stream", 0, 1) 2987 So(err, ShouldNotBeNil) 2988 2989 _, _, _, err = imgStore.GetBlobPartial(repoName, bdigest, "application/octet-stream", 1, 0) 2990 So(err, ShouldNotBeNil) 2991 2992 _, _, _, err = imgStore.GetBlobPartial(repoName, bdigest, "application/octet-stream", 1, 0) 2993 So(err, ShouldNotBeNil) 2994 2995 blobPath := path.Join(imgStore.RootDir(), repoName, "blobs", bdigest.Algorithm().String(), bdigest.Encoded()) 2996 err = os.Chmod(blobPath, 0o000) 2997 So(err, ShouldBeNil) 2998 _, _, _, err = imgStore.GetBlobPartial(repoName, bdigest, "application/octet-stream", -1, 1) 2999 So(err, ShouldNotBeNil) 3000 }) 3001 }) 3002 } 3003 3004 func TestStatIndex(t *testing.T) { 3005 Convey("NewImageStore", t, func() { 3006 dir := t.TempDir() 3007 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 3008 metrics := monitoring.NewMetricsServer(false, log) 3009 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, nil) 3010 3011 err := WriteImageToFileSystem(CreateRandomImage(), "repo", "tag", 3012 storage.StoreController{DefaultStore: imgStore}) 3013 So(err, ShouldBeNil) 3014 3015 Convey("StatIndex PathNotFoundError", func() { 3016 _, _, _, err := imgStore.StatIndex("not-found") 3017 So(err, ShouldNotBeNil) 3018 }) 3019 }) 3020 } 3021 3022 func TestStorageDriverErr(t *testing.T) { 3023 dir := t.TempDir() 3024 3025 log := zlog.Logger{Logger: zerolog.New(os.Stdout)} 3026 metrics := monitoring.NewMetricsServer(false, log) 3027 cacheDriver, _ := storage.Create("boltdb", cache.BoltDBDriverParameters{ 3028 RootDir: dir, 3029 Name: "cache", 3030 UseRelPaths: true, 3031 }, log) 3032 3033 imgStore := local.NewImageStore(dir, true, true, log, metrics, nil, cacheDriver) 3034 3035 Convey("Init repo", t, func() { 3036 err := imgStore.InitRepo(repoName) 3037 So(err, ShouldBeNil) 3038 3039 Convey("New blob upload error", func() { 3040 err := os.Chmod(path.Join(imgStore.RootDir(), repoName, storageConstants.BlobUploadDir), 0o000) 3041 So(err, ShouldBeNil) 3042 3043 _, err = imgStore.NewBlobUpload(repoName) 3044 So(err, ShouldNotBeNil) 3045 3046 err = os.Chmod(path.Join(imgStore.RootDir(), repoName, storageConstants.BlobUploadDir), 3047 storageConstants.DefaultDirPerms) 3048 So(err, ShouldBeNil) 3049 3050 uuid, err := imgStore.NewBlobUpload(repoName) 3051 So(err, ShouldBeNil) 3052 3053 size, err := imgStore.GetBlobUpload(repoName, uuid) 3054 So(err, ShouldBeNil) 3055 So(size, ShouldEqual, 0) 3056 3057 content := []byte("test-blob") 3058 buf := bytes.NewBuffer(content) 3059 bufLen := buf.Len() 3060 digest := godigest.FromBytes(content) 3061 3062 size, err = imgStore.PutBlobChunkStreamed(repoName, uuid, buf) 3063 So(err, ShouldBeNil) 3064 So(size, ShouldEqual, bufLen) 3065 3066 size, err = imgStore.GetBlobUpload(repoName, uuid) 3067 So(err, ShouldBeNil) 3068 So(size, ShouldEqual, bufLen) 3069 3070 err = imgStore.DeleteBlobUpload(repoName, uuid) 3071 So(err, ShouldBeNil) 3072 3073 err = imgStore.DeleteBlobUpload(repoName, uuid) 3074 So(err, ShouldNotBeNil) 3075 3076 _, err = imgStore.GetBlobUpload(repoName, uuid) 3077 So(err, ShouldNotBeNil) 3078 3079 // push again 3080 buf = bytes.NewBuffer(content) 3081 3082 uuid, err = imgStore.NewBlobUpload(repoName) 3083 So(err, ShouldBeNil) 3084 3085 size, err = imgStore.PutBlobChunkStreamed(repoName, uuid, buf) 3086 So(err, ShouldBeNil) 3087 So(size, ShouldEqual, bufLen) 3088 3089 // finish blob upload 3090 err = os.Chmod(path.Join(imgStore.BlobUploadPath(repoName, uuid)), 0o000) 3091 So(err, ShouldBeNil) 3092 3093 err = imgStore.FinishBlobUpload(repoName, uuid, &io.LimitedReader{}, digest) 3094 So(err, ShouldNotBeNil) 3095 3096 err = os.Chmod(path.Join(imgStore.BlobUploadPath(repoName, uuid)), storageConstants.DefaultFilePerms) 3097 So(err, ShouldBeNil) 3098 3099 err = imgStore.FinishBlobUpload(repoName, uuid, &io.LimitedReader{}, digest) 3100 So(err, ShouldBeNil) 3101 3102 err = imgStore.FinishBlobUpload(repoName, uuid, &io.LimitedReader{}, digest) 3103 So(err, ShouldNotBeNil) 3104 3105 // delete blob 3106 err = imgStore.DeleteBlob(repoName, digest) 3107 So(err, ShouldBeNil) 3108 3109 err = imgStore.DeleteBlob(repoName, digest) 3110 So(err, ShouldNotBeNil) 3111 }) 3112 }) 3113 } 3114 3115 func NewRandomImgManifest(data []byte, cdigest, ldigest godigest.Digest, cblob, lblob []byte) (*ispec.Manifest, error) { 3116 annotationsMap := make(map[string]string) 3117 3118 key := string(data) 3119 val := string(data) 3120 annotationsMap[key] = val 3121 3122 schemaVersion := 2 3123 3124 manifest := ispec.Manifest{ 3125 MediaType: "application/vnd.oci.image.manifest.v1+json", 3126 Config: ispec.Descriptor{ 3127 MediaType: "application/vnd.oci.image.config.v1+json", 3128 Digest: cdigest, 3129 Size: int64(len(cblob)), 3130 }, 3131 Layers: []ispec.Descriptor{ 3132 { 3133 MediaType: "application/vnd.oci.image.layer.v1.tar", 3134 Digest: ldigest, 3135 Size: int64(len(lblob)), 3136 }, 3137 }, 3138 Annotations: annotationsMap, 3139 Versioned: imeta.Versioned{ 3140 SchemaVersion: schemaVersion, 3141 }, 3142 } 3143 3144 return &manifest, nil 3145 } 3146 3147 func newRandomBlobForFuzz(data []byte) (godigest.Digest, []byte, error) { //nolint:unparam 3148 return godigest.FromBytes(data), data, nil 3149 } 3150 3151 func isKnownErr(err error) bool { 3152 if errors.Is(err, zerr.ErrInvalidRepositoryName) || errors.Is(err, zerr.ErrManifestNotFound) || 3153 errors.Is(err, zerr.ErrRepoNotFound) || 3154 errors.Is(err, zerr.ErrBadManifest) { 3155 return true 3156 } 3157 3158 if err, ok := err.(*fs.PathError); ok && errors.Is(err.Err, syscall.EACCES) || //nolint: errorlint 3159 errors.Is(err.Err, syscall.ENAMETOOLONG) || 3160 errors.Is(err.Err, syscall.EINVAL) || 3161 errors.Is(err.Err, syscall.ENOENT) { 3162 return true 3163 } 3164 3165 return false 3166 }