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