zotregistry.io/zot@v1.4.4-0.20231124084042-02a8ed785457/pkg/extensions/search/convert/convert_test.go (about) 1 //go:build search 2 3 package convert_test 4 5 import ( 6 "context" 7 "errors" 8 "testing" 9 "time" 10 11 godigest "github.com/opencontainers/go-digest" 12 ispec "github.com/opencontainers/image-spec/specs-go/v1" 13 . "github.com/smartystreets/goconvey/convey" 14 15 "zotregistry.io/zot/pkg/extensions/search/convert" 16 "zotregistry.io/zot/pkg/extensions/search/gql_generated" 17 "zotregistry.io/zot/pkg/extensions/search/pagination" 18 "zotregistry.io/zot/pkg/log" 19 "zotregistry.io/zot/pkg/meta/boltdb" 20 mTypes "zotregistry.io/zot/pkg/meta/types" 21 . "zotregistry.io/zot/pkg/test/image-utils" 22 "zotregistry.io/zot/pkg/test/mocks" 23 ociutils "zotregistry.io/zot/pkg/test/oci-utils" 24 ) 25 26 var ErrTestError = errors.New("TestError") 27 28 func TestUpdateLastUpdatedTimestamp(t *testing.T) { 29 Convey("Image summary is the first image checked for the repo", t, func() { 30 before := time.Time{} 31 after := time.Date(2023, time.April, 1, 11, 0, 0, 0, time.UTC) 32 img := convert.UpdateLastUpdatedTimestamp( 33 &before, 34 &gql_generated.ImageSummary{LastUpdated: &before}, 35 &gql_generated.ImageSummary{LastUpdated: &after}, 36 ) 37 38 So(*img.LastUpdated, ShouldResemble, after) 39 }) 40 41 Convey("Image summary is updated after the current latest image", t, func() { 42 before := time.Date(2022, time.April, 1, 11, 0, 0, 0, time.UTC) 43 after := time.Date(2023, time.April, 1, 11, 0, 0, 0, time.UTC) 44 img := convert.UpdateLastUpdatedTimestamp( 45 &before, 46 &gql_generated.ImageSummary{LastUpdated: &before}, 47 &gql_generated.ImageSummary{LastUpdated: &after}, 48 ) 49 50 So(*img.LastUpdated, ShouldResemble, after) 51 }) 52 53 Convey("Image summary is updated before the current latest image", t, func() { 54 before := time.Date(2022, time.April, 1, 11, 0, 0, 0, time.UTC) 55 after := time.Date(2023, time.April, 1, 11, 0, 0, 0, time.UTC) 56 img := convert.UpdateLastUpdatedTimestamp( 57 &after, 58 &gql_generated.ImageSummary{LastUpdated: &after}, 59 &gql_generated.ImageSummary{LastUpdated: &before}, 60 ) 61 62 So(*img.LastUpdated, ShouldResemble, after) 63 }) 64 } 65 66 func TestLabels(t *testing.T) { 67 Convey("Test labels", t, func() { 68 // Test various labels 69 labels := make(map[string]string) 70 71 desc := convert.GetDescription(labels) 72 So(desc, ShouldEqual, "") 73 74 license := convert.GetLicenses(labels) 75 So(license, ShouldEqual, "") 76 77 vendor := convert.GetVendor(labels) 78 So(vendor, ShouldEqual, "") 79 80 categories := convert.GetCategories(labels) 81 So(categories, ShouldEqual, "") 82 83 labels[ispec.AnnotationVendor] = "zot" 84 labels[ispec.AnnotationDescription] = "zot-desc" 85 labels[ispec.AnnotationLicenses] = "zot-license" 86 labels[convert.AnnotationLabels] = "zot-labels" 87 88 desc = convert.GetDescription(labels) 89 So(desc, ShouldEqual, "zot-desc") 90 91 license = convert.GetLicenses(labels) 92 So(license, ShouldEqual, "zot-license") 93 94 vendor = convert.GetVendor(labels) 95 So(vendor, ShouldEqual, "zot") 96 97 categories = convert.GetCategories(labels) 98 So(categories, ShouldEqual, "zot-labels") 99 100 labels = make(map[string]string) 101 102 // Use diff key 103 labels[convert.LabelAnnotationVendor] = "zot-vendor" 104 labels[convert.LabelAnnotationDescription] = "zot-label-desc" 105 labels[ispec.AnnotationLicenses] = "zot-label-license" 106 107 desc = convert.GetDescription(labels) 108 So(desc, ShouldEqual, "zot-label-desc") 109 110 license = convert.GetLicenses(labels) 111 So(license, ShouldEqual, "zot-label-license") 112 113 vendor = convert.GetVendor(labels) 114 So(vendor, ShouldEqual, "zot-vendor") 115 }) 116 } 117 118 func TestGetSignaturesInfo(t *testing.T) { 119 Convey("Test get signatures info - cosign", t, func() { 120 digest := godigest.FromString("dig") 121 signatures := map[string]mTypes.ManifestSignatures{ 122 digest.String(): { 123 "cosign": []mTypes.SignatureInfo{ 124 { 125 LayersInfo: []mTypes.LayerInfo{ 126 { 127 LayerContent: []byte{}, 128 LayerDigest: "", 129 SignatureKey: "", 130 Signer: "author", 131 }, 132 }, 133 }, 134 }, 135 }, 136 } 137 138 signaturesSummary := convert.GetSignaturesInfo(true, signatures[digest.String()]) 139 So(signaturesSummary, ShouldNotBeEmpty) 140 So(*signaturesSummary[0].Author, ShouldEqual, "author") 141 So(*signaturesSummary[0].IsTrusted, ShouldEqual, true) 142 So(*signaturesSummary[0].Tool, ShouldEqual, "cosign") 143 }) 144 145 Convey("Test get signatures info - notation", t, func() { 146 digest := godigest.FromString("dig") 147 signatures := map[string]mTypes.ManifestSignatures{ 148 digest.String(): { 149 "notation": []mTypes.SignatureInfo{ 150 { 151 LayersInfo: []mTypes.LayerInfo{ 152 { 153 LayerContent: []byte{}, 154 LayerDigest: "", 155 SignatureKey: "", 156 Signer: "author", 157 Date: time.Now().AddDate(0, 0, -1), 158 }, 159 }, 160 }, 161 }, 162 }, 163 } 164 165 signaturesSummary := convert.GetSignaturesInfo(true, signatures[digest.String()]) 166 So(signaturesSummary, ShouldNotBeEmpty) 167 So(*signaturesSummary[0].Author, ShouldEqual, "author") 168 So(*signaturesSummary[0].IsTrusted, ShouldEqual, false) 169 So(*signaturesSummary[0].Tool, ShouldEqual, "notation") 170 }) 171 } 172 173 func TestAcceptedByFilter(t *testing.T) { 174 Convey("Images", t, func() { 175 Convey("Os not found", func() { 176 found := convert.ImgSumAcceptedByFilter( 177 &gql_generated.ImageSummary{ 178 Manifests: []*gql_generated.ManifestSummary{ 179 {Platform: &gql_generated.Platform{Os: ref("os1")}}, 180 {Platform: &gql_generated.Platform{Os: ref("os2")}}, 181 }, 182 }, 183 mTypes.Filter{Os: []*string{ref("os3")}}, 184 ) 185 186 So(found, ShouldBeFalse) 187 }) 188 189 Convey("Has to be signed ", func() { 190 found := convert.ImgSumAcceptedByFilter( 191 &gql_generated.ImageSummary{ 192 Manifests: []*gql_generated.ManifestSummary{ 193 {IsSigned: ref(false)}, 194 }, 195 IsSigned: ref(false), 196 }, 197 mTypes.Filter{HasToBeSigned: ref(true)}, 198 ) 199 200 So(found, ShouldBeFalse) 201 }) 202 }) 203 204 Convey("Repos", t, func() { 205 Convey("Os not found", func() { 206 found := convert.RepoSumAcceptedByFilter( 207 &gql_generated.RepoSummary{ 208 Platforms: []*gql_generated.Platform{ 209 {Os: ref("os1")}, 210 {Os: ref("os2")}, 211 }, 212 }, 213 mTypes.Filter{Os: []*string{ref("os3")}}, 214 ) 215 216 So(found, ShouldBeFalse) 217 }) 218 219 Convey("Arch not found", func() { 220 found := convert.RepoSumAcceptedByFilter( 221 &gql_generated.RepoSummary{ 222 Platforms: []*gql_generated.Platform{ 223 {Arch: ref("Arch")}, 224 }, 225 }, 226 mTypes.Filter{Arch: []*string{ref("arch_not_found")}}, 227 ) 228 229 So(found, ShouldBeFalse) 230 }) 231 232 Convey("Has to be signed ", func() { 233 found := convert.ImgSumAcceptedByFilter( 234 &gql_generated.ImageSummary{ 235 Manifests: []*gql_generated.ManifestSummary{ 236 {IsSigned: ref(false)}, 237 }, 238 IsSigned: ref(false), 239 }, 240 mTypes.Filter{HasToBeSigned: ref(true)}, 241 ) 242 243 So(found, ShouldBeFalse) 244 }) 245 }) 246 } 247 248 func ref[T any](val T) *T { 249 ref := val 250 251 return &ref 252 } 253 254 func TestPaginatedConvert(t *testing.T) { 255 ctx := context.Background() 256 257 tempDir := t.TempDir() 258 259 driver, err := boltdb.GetBoltDriver(boltdb.DBParameters{RootDir: tempDir}) 260 if err != nil { 261 t.FailNow() 262 } 263 264 metaDB, err := boltdb.New(driver, log.NewLogger("debug", "")) 265 if err != nil { 266 t.FailNow() 267 } 268 269 var ( 270 badBothImage = CreateImageWith().DefaultLayers().ImageConfig( 271 ispec.Image{Platform: ispec.Platform{OS: "bad-os", Architecture: "bad-arch"}}).Build() 272 badOsImage = CreateImageWith().DefaultLayers().ImageConfig( 273 ispec.Image{Platform: ispec.Platform{OS: "bad-os", Architecture: "good-arch"}}).Build() 274 badArchImage = CreateImageWith().DefaultLayers().ImageConfig( 275 ispec.Image{Platform: ispec.Platform{OS: "good-os", Architecture: "bad-arch"}}).Build() 276 goodImage = CreateImageWith().DefaultLayers().ImageConfig( 277 ispec.Image{Platform: ispec.Platform{OS: "good-os", Architecture: "good-arch"}}).Build() 278 279 randomImage1 = CreateRandomImage() 280 randomImage2 = CreateRandomImage() 281 signatureDigest = godigest.FromString("signature") 282 283 badMultiArch = CreateMultiarchWith().Images( 284 []Image{badBothImage, badOsImage, badArchImage, randomImage1}).Build() 285 goodMultiArch = CreateMultiarchWith().Images( 286 []Image{badOsImage, badArchImage, randomImage2, goodImage}).Build() 287 ) 288 289 ctx, err = ociutils.InitializeTestMetaDB(ctx, metaDB, 290 ociutils.Repo{ 291 Name: "repo1-only-images", 292 Images: []ociutils.RepoImage{ 293 {Image: goodImage, Reference: "goodImage"}, 294 {Image: badOsImage, Reference: "badOsImage"}, 295 {Image: badArchImage, Reference: "badArchImage"}, 296 {Image: badBothImage, Reference: "badBothImage"}, 297 }, 298 IsBookmarked: true, 299 IsStarred: true, 300 }, 301 ociutils.Repo{ 302 Name: "repo2-only-bad-images", 303 Images: []ociutils.RepoImage{ 304 {Image: randomImage1, Reference: "randomImage1"}, 305 {Image: randomImage2, Reference: "randomImage2"}, 306 {Image: badBothImage, Reference: "badBothImage"}, 307 }, 308 IsBookmarked: true, 309 IsStarred: true, 310 }, 311 ociutils.Repo{ 312 Name: "repo3-only-multiarch", 313 MultiArchImages: []ociutils.RepoMultiArchImage{ 314 {MultiarchImage: badMultiArch, Reference: "badMultiArch"}, 315 {MultiarchImage: goodMultiArch, Reference: "goodMultiArch"}, 316 }, 317 IsBookmarked: true, 318 IsStarred: true, 319 }, 320 ociutils.Repo{ 321 Name: "repo4-not-bookmarked-or-starred", 322 Images: []ociutils.RepoImage{ 323 {Image: goodImage, Reference: "goodImage"}, 324 }, 325 MultiArchImages: []ociutils.RepoMultiArchImage{ 326 {MultiarchImage: goodMultiArch, Reference: "goodMultiArch"}, 327 }, 328 }, 329 ociutils.Repo{ 330 Name: "repo5-signed", 331 Images: []ociutils.RepoImage{ 332 {Image: goodImage, Reference: "goodImage"}, // is fake signed by the image below 333 }, 334 Signatures: map[string]mTypes.ManifestSignatures{ 335 goodImage.DigestStr(): ociutils.GetFakeSignatureInfo(signatureDigest.String()), 336 }, 337 }, 338 ) 339 if err != nil { 340 t.FailNow() 341 } 342 343 skipCVE := convert.SkipQGLField{Vulnerabilities: true} 344 345 Convey("PaginatedRepoMeta2RepoSummaries filtering and sorting", t, func() { 346 // Test different combinations of the filter 347 repoMetaList, err := metaDB.FilterRepos(ctx, mTypes.AcceptAllRepoNames, mTypes.AcceptAllRepoMeta) 348 So(err, ShouldBeNil) 349 imageMeta, err := metaDB.FilterImageMeta(ctx, mTypes.GetLatestImageDigests(repoMetaList)) 350 So(err, ShouldBeNil) 351 352 reposSum, pageInfo, err := convert.PaginatedRepoMeta2RepoSummaries( 353 ctx, repoMetaList, imageMeta, 354 mTypes.Filter{ 355 Os: []*string{ref("good-os")}, 356 Arch: []*string{ref("good-arch")}, 357 IsBookmarked: ref(true), 358 IsStarred: ref(true), 359 }, 360 pagination.PageInput{SortBy: pagination.AlphabeticAsc}, mocks.CveInfoMock{}, skipCVE, 361 ) 362 So(err, ShouldBeNil) 363 So(len(reposSum), ShouldEqual, 2) 364 So(*reposSum[0].Name, ShouldResemble, "repo1-only-images") 365 So(*reposSum[1].Name, ShouldResemble, "repo3-only-multiarch") 366 So(pageInfo.ItemCount, ShouldEqual, 2) 367 So(pageInfo.ItemCount, ShouldEqual, 2) 368 So(pageInfo.ItemCount, ShouldEqual, 2) 369 So(pageInfo.ItemCount, ShouldEqual, 2) 370 371 reposSum, pageInfo, err = convert.PaginatedRepoMeta2RepoSummaries( 372 ctx, repoMetaList, imageMeta, 373 mTypes.Filter{ 374 Os: []*string{ref("good-os")}, 375 Arch: []*string{ref("good-arch")}, 376 IsBookmarked: ref(true), 377 IsStarred: ref(true), 378 HasToBeSigned: ref(true), 379 }, 380 pagination.PageInput{SortBy: pagination.AlphabeticAsc}, mocks.CveInfoMock{}, skipCVE, 381 ) 382 So(err, ShouldBeNil) 383 So(len(reposSum), ShouldEqual, 0) 384 So(pageInfo.ItemCount, ShouldEqual, 0) 385 386 reposSum, pageInfo, err = convert.PaginatedRepoMeta2RepoSummaries( 387 ctx, repoMetaList, imageMeta, 388 mTypes.Filter{ 389 HasToBeSigned: ref(true), 390 }, 391 pagination.PageInput{SortBy: pagination.AlphabeticAsc}, mocks.CveInfoMock{}, skipCVE, 392 ) 393 So(err, ShouldBeNil) 394 So(len(reposSum), ShouldEqual, 1) 395 So(*reposSum[0].Name, ShouldResemble, "repo5-signed") 396 So(pageInfo.ItemCount, ShouldEqual, 1) 397 398 // no filter 399 reposSum, pageInfo, err = convert.PaginatedRepoMeta2RepoSummaries( 400 ctx, repoMetaList, imageMeta, 401 mTypes.Filter{}, pagination.PageInput{SortBy: pagination.AlphabeticAsc}, mocks.CveInfoMock{}, skipCVE, 402 ) 403 So(err, ShouldBeNil) 404 So(len(reposSum), ShouldEqual, 5) 405 So(*reposSum[0].Name, ShouldResemble, "repo1-only-images") 406 So(*reposSum[1].Name, ShouldResemble, "repo2-only-bad-images") 407 So(*reposSum[2].Name, ShouldResemble, "repo3-only-multiarch") 408 So(*reposSum[3].Name, ShouldResemble, "repo4-not-bookmarked-or-starred") 409 So(*reposSum[4].Name, ShouldResemble, "repo5-signed") 410 So(pageInfo.ItemCount, ShouldEqual, 5) 411 412 // no filter opposite sorting 413 reposSum, pageInfo, err = convert.PaginatedRepoMeta2RepoSummaries( 414 ctx, repoMetaList, imageMeta, 415 mTypes.Filter{}, pagination.PageInput{SortBy: pagination.AlphabeticDsc}, mocks.CveInfoMock{}, skipCVE, 416 ) 417 So(err, ShouldBeNil) 418 So(len(reposSum), ShouldEqual, 5) 419 So(*reposSum[0].Name, ShouldResemble, "repo5-signed") 420 So(*reposSum[1].Name, ShouldResemble, "repo4-not-bookmarked-or-starred") 421 So(*reposSum[2].Name, ShouldResemble, "repo3-only-multiarch") 422 So(*reposSum[3].Name, ShouldResemble, "repo2-only-bad-images") 423 So(*reposSum[4].Name, ShouldResemble, "repo1-only-images") 424 So(pageInfo.ItemCount, ShouldEqual, 5) 425 426 // add pagination 427 reposSum, pageInfo, err = convert.PaginatedRepoMeta2RepoSummaries( 428 ctx, repoMetaList, imageMeta, 429 mTypes.Filter{ 430 Os: []*string{ref("good-os")}, 431 Arch: []*string{ref("good-arch")}, 432 IsBookmarked: ref(true), 433 IsStarred: ref(true), 434 }, 435 pagination.PageInput{Limit: 1, Offset: 0, SortBy: pagination.AlphabeticAsc}, mocks.CveInfoMock{}, skipCVE, 436 ) 437 So(err, ShouldBeNil) 438 So(len(reposSum), ShouldEqual, 1) 439 So(*reposSum[0].Name, ShouldResemble, "repo1-only-images") 440 So(pageInfo.ItemCount, ShouldEqual, 1) 441 So(pageInfo.TotalCount, ShouldEqual, 2) 442 443 reposSum, pageInfo, err = convert.PaginatedRepoMeta2RepoSummaries( 444 ctx, repoMetaList, imageMeta, 445 mTypes.Filter{ 446 Os: []*string{ref("good-os")}, 447 Arch: []*string{ref("good-arch")}, 448 IsBookmarked: ref(true), 449 IsStarred: ref(true), 450 }, 451 pagination.PageInput{Limit: 1, Offset: 1, SortBy: pagination.AlphabeticAsc}, mocks.CveInfoMock{}, skipCVE, 452 ) 453 So(err, ShouldBeNil) 454 So(len(reposSum), ShouldEqual, 1) 455 So(*reposSum[0].Name, ShouldResemble, "repo3-only-multiarch") 456 So(pageInfo.ItemCount, ShouldEqual, 1) 457 So(pageInfo.TotalCount, ShouldEqual, 2) 458 }) 459 460 Convey("PaginatedRepoMeta2ImageSummaries filtering and sorting", t, func() { 461 fullImageMetaList, err := metaDB.FilterTags(ctx, mTypes.AcceptAllRepoTag, mTypes.AcceptAllImageMeta) 462 So(err, ShouldBeNil) 463 464 imgSum, pageInfo, err := convert.PaginatedFullImageMeta2ImageSummaries( 465 ctx, fullImageMetaList, skipCVE, mocks.CveInfoMock{}, 466 mTypes.Filter{ 467 Os: []*string{ref("good-os")}, 468 Arch: []*string{ref("good-arch")}, 469 }, 470 pagination.PageInput{SortBy: pagination.AlphabeticAsc}, 471 ) 472 So(err, ShouldBeNil) 473 So(len(imgSum), ShouldEqual, 5) 474 So(*imgSum[0].RepoName, ShouldResemble, "repo1-only-images") 475 So(*imgSum[0].Tag, ShouldResemble, "goodImage") 476 So(*imgSum[1].RepoName, ShouldResemble, "repo3-only-multiarch") 477 So(*imgSum[1].Tag, ShouldResemble, "goodMultiArch") 478 So(*imgSum[2].RepoName, ShouldResemble, "repo4-not-bookmarked-or-starred") 479 So(*imgSum[2].Tag, ShouldResemble, "goodImage") 480 So(*imgSum[3].RepoName, ShouldResemble, "repo4-not-bookmarked-or-starred") 481 So(*imgSum[3].Tag, ShouldResemble, "goodMultiArch") 482 So(*imgSum[4].RepoName, ShouldResemble, "repo5-signed") 483 So(*imgSum[4].Tag, ShouldResemble, "goodImage") 484 So(pageInfo.ItemCount, ShouldEqual, 5) 485 486 // add page of size 2 487 imgSum, pageInfo, err = convert.PaginatedFullImageMeta2ImageSummaries( 488 ctx, fullImageMetaList, skipCVE, mocks.CveInfoMock{}, 489 mTypes.Filter{ 490 Os: []*string{ref("good-os")}, 491 Arch: []*string{ref("good-arch")}, 492 }, 493 pagination.PageInput{Limit: 2, Offset: 0, SortBy: pagination.AlphabeticAsc}, 494 ) 495 So(err, ShouldBeNil) 496 So(len(imgSum), ShouldEqual, 2) 497 So(*imgSum[0].RepoName, ShouldResemble, "repo1-only-images") 498 So(*imgSum[0].Tag, ShouldResemble, "goodImage") 499 So(*imgSum[1].RepoName, ShouldResemble, "repo3-only-multiarch") 500 So(*imgSum[1].Tag, ShouldResemble, "goodMultiArch") 501 So(pageInfo.ItemCount, ShouldEqual, 2) 502 So(pageInfo.TotalCount, ShouldEqual, 5) 503 504 // next page 505 imgSum, pageInfo, err = convert.PaginatedFullImageMeta2ImageSummaries( 506 ctx, fullImageMetaList, skipCVE, mocks.CveInfoMock{}, 507 mTypes.Filter{ 508 Os: []*string{ref("good-os")}, 509 Arch: []*string{ref("good-arch")}, 510 }, 511 pagination.PageInput{Limit: 2, Offset: 2, SortBy: pagination.AlphabeticAsc}, 512 ) 513 So(err, ShouldBeNil) 514 So(len(imgSum), ShouldEqual, 2) 515 So(*imgSum[0].RepoName, ShouldResemble, "repo4-not-bookmarked-or-starred") 516 So(*imgSum[0].Tag, ShouldResemble, "goodImage") 517 So(*imgSum[1].RepoName, ShouldResemble, "repo4-not-bookmarked-or-starred") 518 So(*imgSum[1].Tag, ShouldResemble, "goodMultiArch") 519 So(pageInfo.ItemCount, ShouldEqual, 2) 520 So(pageInfo.TotalCount, ShouldEqual, 5) 521 522 // last page 523 imgSum, pageInfo, err = convert.PaginatedFullImageMeta2ImageSummaries( 524 ctx, fullImageMetaList, skipCVE, mocks.CveInfoMock{}, 525 mTypes.Filter{ 526 Os: []*string{ref("good-os")}, 527 Arch: []*string{ref("good-arch")}, 528 }, 529 pagination.PageInput{Limit: 2, Offset: 4, SortBy: pagination.AlphabeticAsc}, 530 ) 531 So(err, ShouldBeNil) 532 So(len(imgSum), ShouldEqual, 1) 533 So(*imgSum[0].RepoName, ShouldResemble, "repo5-signed") 534 So(*imgSum[0].Tag, ShouldResemble, "goodImage") 535 So(pageInfo.ItemCount, ShouldEqual, 1) 536 So(pageInfo.TotalCount, ShouldEqual, 5) 537 538 // has to be signed 539 imgSum, pageInfo, err = convert.PaginatedFullImageMeta2ImageSummaries( 540 ctx, fullImageMetaList, skipCVE, mocks.CveInfoMock{}, 541 mTypes.Filter{ 542 Os: []*string{ref("good-os")}, 543 Arch: []*string{ref("good-arch")}, 544 HasToBeSigned: ref(true), 545 }, 546 pagination.PageInput{SortBy: pagination.AlphabeticAsc}, 547 ) 548 So(err, ShouldBeNil) 549 So(len(imgSum), ShouldEqual, 1) 550 So(*imgSum[0].RepoName, ShouldResemble, "repo5-signed") 551 So(*imgSum[0].Tag, ShouldResemble, "goodImage") 552 So(pageInfo.ItemCount, ShouldEqual, 1) 553 }) 554 } 555 556 func TestIndexAnnotations(t *testing.T) { 557 Convey("Test ImageIndex2ImageSummary annotations logic", t, func() { 558 ctx := context.Background() 559 560 tempDir := t.TempDir() 561 562 driver, err := boltdb.GetBoltDriver(boltdb.DBParameters{RootDir: tempDir}) 563 if err != nil { 564 t.FailNow() 565 } 566 567 metaDB, err := boltdb.New(driver, log.NewLogger("debug", "")) 568 So(err, ShouldBeNil) 569 570 configLabels := map[string]string{ 571 ispec.AnnotationDescription: "ConfigDescription", 572 ispec.AnnotationLicenses: "ConfigLicenses", 573 ispec.AnnotationVendor: "ConfigVendor", 574 ispec.AnnotationAuthors: "ConfigAuthors", 575 ispec.AnnotationTitle: "ConfigTitle", 576 ispec.AnnotationDocumentation: "ConfigDocumentation", 577 ispec.AnnotationSource: "ConfigSource", 578 } 579 580 manifestAnnotations := map[string]string{ 581 ispec.AnnotationDescription: "ManifestDescription", 582 ispec.AnnotationLicenses: "ManifestLicenses", 583 ispec.AnnotationVendor: "ManifestVendor", 584 ispec.AnnotationAuthors: "ManifestAuthors", 585 ispec.AnnotationTitle: "ManifestTitle", 586 ispec.AnnotationDocumentation: "ManifestDocumentation", 587 ispec.AnnotationSource: "ManifestSource", 588 } 589 590 indexAnnotations := map[string]string{ 591 ispec.AnnotationDescription: "IndexDescription", 592 ispec.AnnotationLicenses: "IndexLicenses", 593 ispec.AnnotationVendor: "IndexVendor", 594 ispec.AnnotationAuthors: "IndexAuthors", 595 ispec.AnnotationTitle: "IndexTitle", 596 ispec.AnnotationDocumentation: "IndexDocumentation", 597 ispec.AnnotationSource: "IndexSource", 598 } 599 600 imageWithConfigAnnotations := CreateImageWith().DefaultLayers(). 601 ImageConfig(ispec.Image{ 602 Config: ispec.ImageConfig{ 603 Labels: configLabels, 604 }, 605 }).Build() 606 607 imageWithManifestAndConfigAnnotations := CreateImageWith().DefaultLayers(). 608 ImageConfig(ispec.Image{ 609 Config: ispec.ImageConfig{ 610 Labels: configLabels, 611 }, 612 }).Annotations(manifestAnnotations).Build() 613 614 // -------------------------------------------------------- 615 indexWithAnnotations := CreateMultiarchWith().Images( 616 []Image{imageWithManifestAndConfigAnnotations}, 617 ).Annotations(indexAnnotations).Build() 618 619 ctx, err = ociutils.InitializeTestMetaDB(ctx, metaDB, 620 ociutils.Repo{ 621 Name: "repo", 622 MultiArchImages: []ociutils.RepoMultiArchImage{ 623 {MultiarchImage: indexWithAnnotations, Reference: "tag"}, 624 }, 625 }) 626 So(err, ShouldBeNil) 627 628 repoMeta, err := metaDB.GetRepoMeta(ctx, "repo") 629 So(err, ShouldBeNil) 630 imageMeta, err := metaDB.FilterImageMeta(ctx, []string{indexWithAnnotations.DigestStr()}) 631 So(err, ShouldBeNil) 632 633 imageSummary, _, err := convert.ImageIndex2ImageSummary(ctx, convert.GetFullImageMeta("tag", repoMeta, 634 imageMeta[indexWithAnnotations.DigestStr()])) 635 So(err, ShouldBeNil) 636 So(*imageSummary.Description, ShouldResemble, "IndexDescription") 637 So(*imageSummary.Licenses, ShouldResemble, "IndexLicenses") 638 So(*imageSummary.Title, ShouldResemble, "IndexTitle") 639 So(*imageSummary.Source, ShouldResemble, "IndexSource") 640 So(*imageSummary.Documentation, ShouldResemble, "IndexDocumentation") 641 So(*imageSummary.Vendor, ShouldResemble, "IndexVendor") 642 So(*imageSummary.Authors, ShouldResemble, "IndexAuthors") 643 644 err = metaDB.ResetDB() 645 So(err, ShouldBeNil) 646 // -------------------------------------------------------- 647 indexWithManifestAndConfigAnnotations := CreateMultiarchWith().Images( 648 []Image{imageWithManifestAndConfigAnnotations, CreateRandomImage(), CreateRandomImage()}, 649 ).Build() 650 651 ctx, err = ociutils.InitializeTestMetaDB(ctx, metaDB, ociutils.Repo{ 652 Name: "repo", 653 MultiArchImages: []ociutils.RepoMultiArchImage{ 654 {MultiarchImage: indexWithManifestAndConfigAnnotations, Reference: "tag"}, 655 }, 656 }) 657 So(err, ShouldBeNil) 658 659 digest := indexWithManifestAndConfigAnnotations.DigestStr() 660 661 repoMeta, err = metaDB.GetRepoMeta(ctx, "repo") 662 So(err, ShouldBeNil) 663 imageMeta, err = metaDB.FilterImageMeta(ctx, []string{digest}) 664 So(err, ShouldBeNil) 665 666 imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, convert.GetFullImageMeta("tag", repoMeta, 667 imageMeta[digest])) 668 So(err, ShouldBeNil) 669 So(*imageSummary.Description, ShouldResemble, "ManifestDescription") 670 So(*imageSummary.Licenses, ShouldResemble, "ManifestLicenses") 671 So(*imageSummary.Title, ShouldResemble, "ManifestTitle") 672 So(*imageSummary.Source, ShouldResemble, "ManifestSource") 673 So(*imageSummary.Documentation, ShouldResemble, "ManifestDocumentation") 674 So(*imageSummary.Vendor, ShouldResemble, "ManifestVendor") 675 So(*imageSummary.Authors, ShouldResemble, "ManifestAuthors") 676 677 err = metaDB.ResetDB() 678 So(err, ShouldBeNil) 679 // -------------------------------------------------------- 680 indexWithConfigAnnotations := CreateMultiarchWith().Images( 681 []Image{imageWithConfigAnnotations, CreateRandomImage(), CreateRandomImage()}, 682 ).Build() 683 684 ctx, err = ociutils.InitializeTestMetaDB(ctx, metaDB, ociutils.Repo{ 685 Name: "repo", 686 MultiArchImages: []ociutils.RepoMultiArchImage{ 687 {MultiarchImage: indexWithConfigAnnotations, Reference: "tag"}, 688 }, 689 }) 690 So(err, ShouldBeNil) 691 692 digest = indexWithConfigAnnotations.DigestStr() 693 694 repoMeta, err = metaDB.GetRepoMeta(ctx, "repo") 695 So(err, ShouldBeNil) 696 imageMeta, err = metaDB.FilterImageMeta(ctx, []string{digest}) 697 So(err, ShouldBeNil) 698 699 imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, convert.GetFullImageMeta("tag", repoMeta, 700 imageMeta[digest])) 701 So(err, ShouldBeNil) 702 So(*imageSummary.Description, ShouldResemble, "ConfigDescription") 703 So(*imageSummary.Licenses, ShouldResemble, "ConfigLicenses") 704 So(*imageSummary.Title, ShouldResemble, "ConfigTitle") 705 So(*imageSummary.Source, ShouldResemble, "ConfigSource") 706 So(*imageSummary.Documentation, ShouldResemble, "ConfigDocumentation") 707 So(*imageSummary.Vendor, ShouldResemble, "ConfigVendor") 708 So(*imageSummary.Authors, ShouldResemble, "ConfigAuthors") 709 710 err = metaDB.ResetDB() 711 So(err, ShouldBeNil) 712 //-------------------------------------------------------- 713 714 indexWithMixAnnotations := CreateMultiarchWith().Images( 715 []Image{ 716 CreateImageWith().DefaultLayers().ImageConfig(ispec.Image{ 717 Config: ispec.ImageConfig{ 718 Labels: map[string]string{ 719 ispec.AnnotationDescription: "ConfigDescription", 720 ispec.AnnotationLicenses: "ConfigLicenses", 721 }, 722 }, 723 }).Annotations(map[string]string{ 724 ispec.AnnotationVendor: "ManifestVendor", 725 ispec.AnnotationAuthors: "ManifestAuthors", 726 }).Build(), 727 CreateRandomImage(), 728 CreateRandomImage(), 729 }, 730 ).Annotations( 731 map[string]string{ 732 ispec.AnnotationTitle: "IndexTitle", 733 ispec.AnnotationDocumentation: "IndexDocumentation", 734 ispec.AnnotationSource: "IndexSource", 735 }, 736 ).Build() 737 738 ctx, err = ociutils.InitializeTestMetaDB(ctx, metaDB, ociutils.Repo{ 739 Name: "repo", 740 MultiArchImages: []ociutils.RepoMultiArchImage{ 741 {MultiarchImage: indexWithMixAnnotations, Reference: "tag"}, 742 }, 743 }) 744 So(err, ShouldBeNil) 745 746 digest = indexWithMixAnnotations.DigestStr() 747 748 repoMeta, err = metaDB.GetRepoMeta(ctx, "repo") 749 So(err, ShouldBeNil) 750 imageMeta, err = metaDB.FilterImageMeta(ctx, []string{digest}) 751 So(err, ShouldBeNil) 752 753 imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, convert.GetFullImageMeta("tag", repoMeta, 754 imageMeta[digest])) 755 So(err, ShouldBeNil) 756 So(*imageSummary.Description, ShouldResemble, "ConfigDescription") 757 So(*imageSummary.Licenses, ShouldResemble, "ConfigLicenses") 758 So(*imageSummary.Vendor, ShouldResemble, "ManifestVendor") 759 So(*imageSummary.Authors, ShouldResemble, "ManifestAuthors") 760 So(*imageSummary.Title, ShouldResemble, "IndexTitle") 761 So(*imageSummary.Documentation, ShouldResemble, "IndexDocumentation") 762 So(*imageSummary.Source, ShouldResemble, "IndexSource") 763 764 err = metaDB.ResetDB() 765 So(err, ShouldBeNil) 766 //-------------------------------------------------------- 767 indexWithNoAnnotations := CreateRandomMultiarch() 768 769 ctx, err = ociutils.InitializeTestMetaDB(ctx, metaDB, ociutils.Repo{ 770 Name: "repo", 771 MultiArchImages: []ociutils.RepoMultiArchImage{ 772 {MultiarchImage: indexWithNoAnnotations, Reference: "tag"}, 773 }, 774 }) 775 So(err, ShouldBeNil) 776 777 digest = indexWithNoAnnotations.DigestStr() 778 779 repoMeta, err = metaDB.GetRepoMeta(ctx, "repo") 780 So(err, ShouldBeNil) 781 imageMeta, err = metaDB.FilterImageMeta(ctx, []string{digest}) 782 So(err, ShouldBeNil) 783 784 imageSummary, _, err = convert.ImageIndex2ImageSummary(ctx, convert.GetFullImageMeta("tag", repoMeta, 785 imageMeta[digest])) 786 So(err, ShouldBeNil) 787 So(*imageSummary.Description, ShouldBeBlank) 788 So(*imageSummary.Licenses, ShouldBeBlank) 789 So(*imageSummary.Vendor, ShouldBeBlank) 790 So(*imageSummary.Authors, ShouldBeBlank) 791 So(*imageSummary.Title, ShouldBeBlank) 792 So(*imageSummary.Documentation, ShouldBeBlank) 793 So(*imageSummary.Source, ShouldBeBlank) 794 795 err = metaDB.ResetDB() 796 So(err, ShouldBeNil) 797 }) 798 } 799 800 func TestConvertErrors(t *testing.T) { 801 ctx := context.Background() 802 log := log.NewLogger("debug", "") 803 804 Convey("Errors", t, func() { 805 Convey("RepoMeta2ExpandedRepoInfo", func() { 806 _, imgSums := convert.RepoMeta2ExpandedRepoInfo(ctx, 807 mTypes.RepoMeta{ 808 Tags: map[string]mTypes.Descriptor{"tag": {MediaType: "bad-type", Digest: "digest"}}, 809 }, 810 map[string]mTypes.ImageMeta{ 811 "digest": {}, 812 }, 813 convert.SkipQGLField{}, nil, 814 log, 815 ) 816 So(len(imgSums), ShouldEqual, 0) 817 }) 818 }) 819 }