github.com/rrashidov/libpak@v0.0.0-20230911084305-75119185bb4d/layer_test.go (about) 1 /* 2 * Copyright 2018-2020 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * https://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package libpak_test 18 19 import ( 20 "fmt" 21 "net/http" 22 "os" 23 "path/filepath" 24 "testing" 25 "time" 26 27 "github.com/buildpacks/libcnb" 28 . "github.com/onsi/gomega" 29 "github.com/onsi/gomega/ghttp" 30 "github.com/sclevine/spec" 31 32 "github.com/paketo-buildpacks/libpak" 33 "github.com/paketo-buildpacks/libpak/bard" 34 ) 35 36 func testLayer(t *testing.T, context spec.G, it spec.S) { 37 var ( 38 Expect = NewWithT(t).Expect 39 40 layersDir string 41 layer libcnb.Layer 42 ) 43 44 it.Before(func() { 45 layersDir = t.TempDir() 46 layer.Path = filepath.Join(layersDir, "test-layer") 47 48 layer.Exec.Path = layer.Path 49 layer.Metadata = map[string]interface{}{} 50 layer.Profile = libcnb.Profile{} 51 }) 52 53 it.After(func() { 54 Expect(os.RemoveAll(layersDir)).To(Succeed()) 55 }) 56 57 context("LayerContributor", func() { 58 var ( 59 lc libpak.LayerContributor 60 ) 61 62 it.Before(func() { 63 lc.ExpectedMetadata = map[string]interface{}{ 64 "alpha": "test-alpha", 65 "bravo": map[string]interface{}{ 66 "bravo-1": "test-bravo-1", 67 "bravo-2": "test-bravo-2", 68 }, 69 } 70 }) 71 72 it("calls function with no existing metadata", func() { 73 var called bool 74 75 _, err := lc.Contribute(layer, func() (libcnb.Layer, error) { 76 called = true 77 return layer, nil 78 }) 79 Expect(err).NotTo(HaveOccurred()) 80 81 Expect(called).To(BeTrue()) 82 }) 83 84 it("calls function with non-matching metadata", func() { 85 layer.Metadata["alpha"] = "test-alpha" 86 87 var called bool 88 89 _, err := lc.Contribute(layer, func() (libcnb.Layer, error) { 90 called = true 91 return layer, nil 92 }) 93 Expect(err).NotTo(HaveOccurred()) 94 95 Expect(called).To(BeTrue()) 96 }) 97 98 context("reloads layers not restored", func() { 99 var called bool 100 101 it.Before(func() { 102 layer.Metadata = map[string]interface{}{ 103 "alpha": "test-alpha", 104 "bravo": map[string]interface{}{ 105 "bravo-1": "test-bravo-1", 106 "bravo-2": "test-bravo-2", 107 }, 108 } 109 }) 110 111 it("calls function with matching metadata but no layer directory on cache layer", func() { 112 Expect(os.WriteFile(fmt.Sprintf("%s.toml", layer.Path), []byte{}, 0644)).To(Succeed()) 113 Expect(os.RemoveAll(layer.Path)).To(Succeed()) 114 lc.ExpectedTypes.Cache = true 115 116 _, err := lc.Contribute(layer, func() (libcnb.Layer, error) { 117 called = true 118 return layer, nil 119 }) 120 Expect(err).NotTo(HaveOccurred()) 121 122 Expect(called).To(BeTrue()) 123 }) 124 125 it("calls function with matching metadata but no layer directory on build layer", func() { 126 Expect(os.WriteFile(fmt.Sprintf("%s.toml", layer.Path), []byte{}, 0644)).To(Succeed()) 127 Expect(os.RemoveAll(layer.Path)).To(Succeed()) 128 lc.ExpectedTypes.Build = true 129 130 _, err := lc.Contribute(layer, func() (libcnb.Layer, error) { 131 called = true 132 return layer, nil 133 }) 134 Expect(err).NotTo(HaveOccurred()) 135 136 Expect(called).To(BeTrue()) 137 }) 138 139 it("calls function with matching metadata but an empty layer directory on build layer", func() { 140 Expect(os.WriteFile(fmt.Sprintf("%s.toml", layer.Path), []byte{}, 0644)).To(Succeed()) 141 Expect(os.MkdirAll(layer.Path, 0755)).To(Succeed()) 142 lc.ExpectedTypes.Build = true 143 144 _, err := lc.Contribute(layer, func() (libcnb.Layer, error) { 145 called = true 146 return layer, nil 147 }) 148 Expect(err).NotTo(HaveOccurred()) 149 150 Expect(called).To(BeTrue()) 151 }) 152 153 it("does not call function with matching metadata when layer directory exists and has a file in it", func() { 154 Expect(os.WriteFile(fmt.Sprintf("%s.toml", layer.Path), []byte{}, 0644)).To(Succeed()) 155 Expect(os.MkdirAll(layer.Path, 0755)).To(Succeed()) 156 Expect(os.WriteFile(filepath.Join(layer.Path, "foo"), []byte{}, 0644)).To(Succeed()) 157 lc.ExpectedTypes.Build = true 158 159 _, err := lc.Contribute(layer, func() (libcnb.Layer, error) { 160 called = true 161 return layer, nil 162 }) 163 Expect(err).NotTo(HaveOccurred()) 164 165 Expect(called).To(BeFalse()) 166 }) 167 168 it("does not call function with matching metadata when layer TOML missing", func() { 169 Expect(os.MkdirAll(layer.Path, 0755)).To(Succeed()) 170 layer.Build = true 171 172 _, err := lc.Contribute(layer, func() (libcnb.Layer, error) { 173 called = true 174 return layer, nil 175 }) 176 Expect(err).NotTo(HaveOccurred()) 177 178 Expect(called).To(BeFalse()) 179 }) 180 }) 181 182 it("does not call function with matching metadata", func() { 183 layer.Metadata = map[string]interface{}{ 184 "alpha": "test-alpha", 185 "bravo": map[string]interface{}{ 186 "bravo-1": "test-bravo-1", 187 "bravo-2": "test-bravo-2", 188 }, 189 } 190 191 var called bool 192 193 _, err := lc.Contribute(layer, func() (libcnb.Layer, error) { 194 called = true 195 return layer, nil 196 }) 197 Expect(err).NotTo(HaveOccurred()) 198 199 Expect(called).To(BeFalse()) 200 }) 201 202 it("returns function error", func() { 203 _, err := lc.Contribute(layer, func() (libcnb.Layer, error) { 204 return libcnb.Layer{}, fmt.Errorf("test-error") 205 }) 206 Expect(err).To(MatchError("test-error")) 207 }) 208 209 it("adds expected metadata to layer", func() { 210 layer, err := lc.Contribute(layer, func() (libcnb.Layer, error) { 211 return layer, nil 212 }) 213 Expect(err).NotTo(HaveOccurred()) 214 215 Expect(layer.Metadata).To(Equal(map[string]interface{}{ 216 "alpha": "test-alpha", 217 "bravo": map[string]interface{}{ 218 "bravo-1": "test-bravo-1", 219 "bravo-2": "test-bravo-2", 220 }, 221 })) 222 }) 223 224 it("sets build layer flag", func() { 225 lc.ExpectedTypes.Build = true 226 layer, err := lc.Contribute(layer, func() (libcnb.Layer, error) { 227 return layer, nil 228 }) 229 Expect(err).NotTo(HaveOccurred()) 230 231 Expect(layer.LayerTypes.Build).To(BeTrue()) 232 }) 233 234 it("sets cache layer flag", func() { 235 lc.ExpectedTypes.Cache = true 236 layer, err := lc.Contribute(layer, func() (libcnb.Layer, error) { 237 return layer, nil 238 }) 239 Expect(err).NotTo(HaveOccurred()) 240 241 Expect(layer.LayerTypes.Cache).To(BeTrue()) 242 }) 243 244 it("sets launch layer flag", func() { 245 lc.ExpectedTypes.Launch = true 246 layer, err := lc.Contribute(layer, func() (libcnb.Layer, error) { 247 return layer, nil 248 }) 249 Expect(err).NotTo(HaveOccurred()) 250 251 Expect(layer.LayerTypes.Launch).To(BeTrue()) 252 }) 253 254 it("sets layer flags regardless of caching behavior (required for 0.6 API)", func() { 255 lc.ExpectedTypes.Launch = true 256 lc.ExpectedTypes.Cache = true 257 lc.ExpectedTypes.Build = true 258 259 layer.Metadata = map[string]interface{}{ 260 "alpha": "test-alpha", 261 "bravo": map[string]interface{}{ 262 "bravo-1": "test-bravo-1", 263 "bravo-2": "test-bravo-2", 264 }, 265 } 266 267 var called bool 268 269 layer, err := lc.Contribute(layer, func() (libcnb.Layer, error) { 270 called = true 271 return layer, nil 272 }) 273 Expect(err).NotTo(HaveOccurred()) 274 Expect(called).To(BeFalse()) 275 276 Expect(layer.LayerTypes.Launch).To(BeTrue()) 277 Expect(layer.LayerTypes.Cache).To(BeTrue()) 278 Expect(layer.LayerTypes.Build).To(BeTrue()) 279 }) 280 }) 281 282 context("NewDependencyLayer", func() { 283 var dep libpak.BuildpackDependency 284 285 it.Before(func() { 286 dep = libpak.BuildpackDependency{ 287 ID: "test-id", 288 Name: "test-name", 289 Version: "1.1.1", 290 URI: "test-uri", 291 SHA256: "576dd8416de5619ea001d9662291d62444d1292a38e96956bc4651c01f14bca1", 292 Stacks: []string{"test-stack"}, 293 Licenses: []libpak.BuildpackDependencyLicense{ 294 { 295 Type: "test-type", 296 URI: "test-uri", 297 }, 298 }, 299 } 300 }) 301 302 it("returns a BOM entry for the layer", func() { 303 _, entry := libpak.NewDependencyLayer(dep, libpak.DependencyCache{}, libcnb.LayerTypes{}) 304 Expect(entry.Name).To(Equal("test-id")) 305 Expect(entry.Metadata["name"]).To(Equal("test-name")) 306 Expect(entry.Metadata["version"]).To(Equal("1.1.1")) 307 Expect(entry.Metadata["uri"]).To(Equal("test-uri")) 308 Expect(entry.Metadata["sha256"]).To(Equal("576dd8416de5619ea001d9662291d62444d1292a38e96956bc4651c01f14bca1")) 309 Expect(entry.Metadata["licenses"]).To(Equal([]libpak.BuildpackDependencyLicense{ 310 { 311 Type: "test-type", 312 URI: "test-uri", 313 }, 314 })) 315 }) 316 317 context("launch layer type", func() { 318 it("only sets launch on the entry", func() { 319 _, entry := libpak.NewDependencyLayer(dep, libpak.DependencyCache{}, libcnb.LayerTypes{ 320 Launch: true, 321 }) 322 Expect(entry.Launch).To(BeTrue()) 323 Expect(entry.Build).To(BeFalse()) 324 }) 325 }) 326 327 context("launch and build layer type", func() { 328 it("sets launch and build on the entry", func() { 329 _, entry := libpak.NewDependencyLayer(dep, libpak.DependencyCache{}, libcnb.LayerTypes{ 330 Launch: true, 331 Build: true, 332 }) 333 Expect(entry.Launch).To(BeTrue()) 334 Expect(entry.Build).To(BeTrue()) 335 }) 336 }) 337 338 context("launch and cache layer type", func() { 339 it("sets launch and build on the entry", func() { 340 _, entry := libpak.NewDependencyLayer(dep, libpak.DependencyCache{}, libcnb.LayerTypes{ 341 Launch: true, 342 Cache: true, 343 }) 344 Expect(entry.Launch).To(BeTrue()) 345 Expect(entry.Build).To(BeTrue()) 346 }) 347 }) 348 349 context("build layer type", func() { 350 it("sets build on the entry", func() { 351 _, entry := libpak.NewDependencyLayer(dep, libpak.DependencyCache{}, libcnb.LayerTypes{ 352 Build: true, 353 }) 354 Expect(entry.Launch).To(BeFalse()) 355 Expect(entry.Build).To(BeTrue()) 356 }) 357 }) 358 359 context("cache layer type", func() { 360 it("sets build on the entry", func() { 361 _, entry := libpak.NewDependencyLayer(dep, libpak.DependencyCache{}, libcnb.LayerTypes{ 362 Cache: true, 363 }) 364 Expect(entry.Launch).To(BeFalse()) 365 Expect(entry.Build).To(BeTrue()) 366 }) 367 }) 368 369 context("no layer types", func() { 370 it("sets build on the entry", func() { 371 _, entry := libpak.NewDependencyLayer(dep, libpak.DependencyCache{}, libcnb.LayerTypes{}) 372 Expect(entry.Launch).To(BeFalse()) 373 Expect(entry.Build).To(BeTrue()) 374 }) 375 }) 376 }) 377 378 context("DependencyLayerContributor", func() { 379 var ( 380 dependency libpak.BuildpackDependency 381 dlc libpak.DependencyLayerContributor 382 server *ghttp.Server 383 ) 384 385 it.Before(func() { 386 RegisterTestingT(t) 387 server = ghttp.NewServer() 388 389 deprecationDate, err := time.Parse(time.RFC3339, "2021-04-01T00:00:00Z") 390 Expect(err).ToNot(HaveOccurred()) 391 392 dependency = libpak.BuildpackDependency{ 393 ID: "test-id", 394 Name: "test-name", 395 Version: "1.1.1", 396 URI: fmt.Sprintf("%s/test-path", server.URL()), 397 SHA256: "576dd8416de5619ea001d9662291d62444d1292a38e96956bc4651c01f14bca1", 398 Stacks: []string{"test-stack"}, 399 Licenses: []libpak.BuildpackDependencyLicense{ 400 { 401 Type: "test-type", 402 URI: "test-uri", 403 }, 404 }, 405 CPEs: []string{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"}, 406 PURL: "pkg:generic/some-java11@11.0.2?arch=amd64", 407 DeprecationDate: deprecationDate, 408 } 409 410 layer.Metadata = map[string]interface{}{} 411 412 dlc.ExpectedMetadata = dependency 413 dlc.Dependency = dependency 414 dlc.DependencyCache.CachePath = layer.Path 415 dlc.DependencyCache.DownloadPath = layer.Path 416 }) 417 418 it.After(func() { 419 server.Close() 420 }) 421 422 it("calls function with no existing metadata", func() { 423 server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture")) 424 425 var called bool 426 427 _, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 428 defer artifact.Close() 429 430 called = true 431 return layer, nil 432 }) 433 Expect(err).NotTo(HaveOccurred()) 434 435 Expect(called).To(BeTrue()) 436 }) 437 438 it("modifies request", func() { 439 server.AppendHandlers(ghttp.CombineHandlers( 440 ghttp.VerifyHeaderKV("Test-Key", "test-value"), 441 ghttp.RespondWith(http.StatusOK, "test-fixture"), 442 )) 443 444 dlc.RequestModifierFuncs = append(dlc.RequestModifierFuncs, func(request *http.Request) (*http.Request, error) { 445 request.Header.Add("Test-Key", "test-value") 446 return request, nil 447 }) 448 449 _, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 450 defer artifact.Close() 451 return layer, nil 452 }) 453 Expect(err).NotTo(HaveOccurred()) 454 }) 455 456 it("calls function with non-matching metadata", func() { 457 layer.Metadata["alpha"] = "test-alpha" 458 459 server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture")) 460 461 var called bool 462 463 _, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 464 defer artifact.Close() 465 466 called = true 467 return layer, nil 468 }) 469 Expect(err).NotTo(HaveOccurred()) 470 471 Expect(called).To(BeTrue()) 472 }) 473 474 it("does not call function with matching metadata", func() { 475 layer.Metadata = map[string]interface{}{ 476 "id": dependency.ID, 477 "name": dependency.Name, 478 "version": dependency.Version, 479 "uri": dependency.URI, 480 "sha256": dependency.SHA256, 481 "stacks": []interface{}{dependency.Stacks[0]}, 482 "licenses": []map[string]interface{}{ 483 { 484 "type": dependency.Licenses[0].Type, 485 "uri": dependency.Licenses[0].URI, 486 }, 487 }, 488 "cpes": []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"}, 489 "purl": "pkg:generic/some-java11@11.0.2?arch=amd64", 490 "deprecation_date": dependency.DeprecationDate, 491 } 492 493 var called bool 494 495 _, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 496 defer artifact.Close() 497 498 called = true 499 return layer, nil 500 }) 501 Expect(err).NotTo(HaveOccurred()) 502 503 Expect(called).To(BeFalse()) 504 }) 505 506 it("does not call function with non-matching deprecation_date format", func() { 507 dependency = libpak.BuildpackDependency{ 508 ID: "test-id", 509 Name: "test-name", 510 Version: "1.1.1", 511 URI: fmt.Sprintf("%s/test-path", server.URL()), 512 SHA256: "576dd8416de5619ea001d9662291d62444d1292a38e96956bc4651c01f14bca1", 513 Stacks: []string{"test-stack"}, 514 Licenses: []libpak.BuildpackDependencyLicense{ 515 { 516 Type: "test-type", 517 URI: "test-uri", 518 }, 519 }, 520 CPEs: []string{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"}, 521 PURL: "pkg:generic/some-java11@11.0.2?arch=amd64", 522 DeprecationDate: dependency.DeprecationDate, // parsed as '2021-04-01 00:00:00 +0000 UTC' 523 } 524 dlc.ExpectedMetadata = map[string]interface{}{ "dependency":dependency} 525 526 layer.Metadata = map[string]interface{}{ "dependency": map[string]interface{}{ 527 "id": dependency.ID, 528 "name": dependency.Name, 529 "version": dependency.Version, 530 "uri": dependency.URI, 531 "sha256": dependency.SHA256, 532 "stacks": []interface{}{dependency.Stacks[0]}, 533 "licenses": []map[string]interface{}{ 534 { 535 "type": dependency.Licenses[0].Type, 536 "uri": dependency.Licenses[0].URI, 537 }, 538 }, 539 "cpes": []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"}, 540 "purl": "pkg:generic/some-java11@11.0.2?arch=amd64", 541 "deprecation_date": "2021-04-01T00:00:00Z", // does not match without truncation 542 }} 543 544 var called bool 545 546 _, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 547 defer artifact.Close() 548 549 called = true 550 return layer, nil 551 }) 552 Expect(err).NotTo(HaveOccurred()) 553 554 Expect(called).To(BeFalse()) 555 }) 556 557 it("does not call function with missing deprecation_date", func() { 558 dependency = libpak.BuildpackDependency{ 559 ID: "test-id", 560 Name: "test-name", 561 Version: "1.1.1", 562 URI: fmt.Sprintf("%s/test-path", server.URL()), 563 SHA256: "576dd8416de5619ea001d9662291d62444d1292a38e96956bc4651c01f14bca1", 564 Stacks: []string{"test-stack"}, 565 Licenses: []libpak.BuildpackDependencyLicense{ 566 { 567 Type: "test-type", 568 URI: "test-uri", 569 }, 570 }, 571 CPEs: []string{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"}, 572 PURL: "pkg:generic/some-java11@11.0.2?arch=amd64", 573 } 574 dlc.ExpectedMetadata = map[string]interface{}{ "dependency":dependency} 575 576 layer.Metadata = map[string]interface{}{ "dependency": map[string]interface{}{ 577 "id": dependency.ID, 578 "name": dependency.Name, 579 "version": dependency.Version, 580 "uri": dependency.URI, 581 "sha256": dependency.SHA256, 582 "stacks": []interface{}{dependency.Stacks[0]}, 583 "licenses": []map[string]interface{}{ 584 { 585 "type": dependency.Licenses[0].Type, 586 "uri": dependency.Licenses[0].URI, 587 }, 588 }, 589 "cpes": []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"}, 590 "purl": "pkg:generic/some-java11@11.0.2?arch=amd64", 591 "deprecation_date": "0001-01-01T00:00:00Z", 592 }} 593 594 var called bool 595 596 _, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 597 defer artifact.Close() 598 599 called = true 600 return layer, nil 601 }) 602 Expect(err).NotTo(HaveOccurred()) 603 604 Expect(called).To(BeFalse()) 605 }) 606 607 it("returns function error", func() { 608 server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture")) 609 610 _, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 611 defer artifact.Close() 612 613 return libcnb.Layer{}, fmt.Errorf("test-error") 614 }) 615 Expect(err).To(MatchError("test-error")) 616 }) 617 618 it("adds expected metadata to layer", func() { 619 server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture")) 620 621 layer, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 622 defer artifact.Close() 623 return layer, nil 624 }) 625 Expect(err).NotTo(HaveOccurred()) 626 627 Expect(layer.Metadata).To(Equal(map[string]interface{}{ 628 "id": dependency.ID, 629 "name": dependency.Name, 630 "version": dependency.Version, 631 "uri": dependency.URI, 632 "sha256": dependency.SHA256, 633 "stacks": []interface{}{dependency.Stacks[0]}, 634 "licenses": []map[string]interface{}{ 635 { 636 "type": dependency.Licenses[0].Type, 637 "uri": dependency.Licenses[0].URI, 638 }, 639 }, 640 "cpes": []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"}, 641 "purl": "pkg:generic/some-java11@11.0.2?arch=amd64", 642 "deprecation_date": dependency.DeprecationDate, 643 })) 644 }) 645 646 it("sets layer flags regardless of caching behavior (required for 0.6 API)", func() { 647 layer.Metadata = map[string]interface{}{ 648 "id": dependency.ID, 649 "name": dependency.Name, 650 "version": dependency.Version, 651 "uri": dependency.URI, 652 "sha256": dependency.SHA256, 653 "stacks": []interface{}{dependency.Stacks[0]}, 654 "licenses": []map[string]interface{}{ 655 { 656 "type": dependency.Licenses[0].Type, 657 "uri": dependency.Licenses[0].URI, 658 }, 659 }, 660 "cpes": []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"}, 661 "purl": "pkg:generic/some-java11@11.0.2?arch=amd64", 662 "deprecation_date": dependency.DeprecationDate, 663 } 664 dlc.ExpectedTypes.Launch = true 665 dlc.ExpectedTypes.Cache = true 666 dlc.ExpectedTypes.Build = true 667 668 var called bool 669 670 layer, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 671 defer artifact.Close() 672 673 called = true 674 return layer, nil 675 }) 676 Expect(err).NotTo(HaveOccurred()) 677 678 Expect(called).To(BeFalse()) 679 680 Expect(layer.LayerTypes.Launch).To(BeTrue()) 681 Expect(layer.LayerTypes.Cache).To(BeTrue()) 682 Expect(layer.LayerTypes.Build).To(BeTrue()) 683 }) 684 685 it("adds expected Syft SBOM file", func() { 686 server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture")) 687 688 layer, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 689 defer artifact.Close() 690 return layer, nil 691 }) 692 Expect(err).NotTo(HaveOccurred()) 693 694 outputFile := layer.SBOMPath(libcnb.SyftJSON) 695 Expect(outputFile).To(BeARegularFile()) 696 697 data, err := os.ReadFile(outputFile) 698 Expect(err).ToNot(HaveOccurred()) 699 Expect(string(data)).To(ContainSubstring(`"Artifacts":[`)) 700 Expect(string(data)).To(ContainSubstring(`"FoundBy":"libpak",`)) 701 Expect(string(data)).To(ContainSubstring(`"PURL":"pkg:generic/some-java11@11.0.2?arch=amd64"`)) 702 Expect(string(data)).To(ContainSubstring(`"Schema":{`)) 703 Expect(string(data)).To(ContainSubstring(`"Descriptor":{`)) 704 Expect(string(data)).To(ContainSubstring(`"Source":{`)) 705 }) 706 }) 707 708 context("NewHelperLayer", func() { 709 it("returns a BOM entry with version equal to buildpack version", func() { 710 _, entry := libpak.NewHelperLayer(libcnb.Buildpack{ 711 API: "0.6", 712 Info: libcnb.BuildpackInfo{ 713 Version: "test-version", 714 }, 715 }, "test-name-1", "test-name-2") 716 Expect(entry).To(Equal( 717 libcnb.BOMEntry{ 718 Name: filepath.Base("helper"), 719 Metadata: map[string]interface{}{ 720 "layer": "helper", 721 "names": []string{"test-name-1", "test-name-2"}, 722 "version": "test-version", 723 }, 724 Launch: true, 725 Build: false, 726 }, 727 )) 728 }) 729 730 it("returns a BOM entry on API 0.7 too", func() { 731 _, entry := libpak.NewHelperLayer(libcnb.Buildpack{ 732 API: "0.7", 733 Info: libcnb.BuildpackInfo{ 734 Version: "test-version", 735 }, 736 }, "test-name-1", "test-name-2") 737 Expect(entry).To(Equal(libcnb.BOMEntry{ 738 Name: filepath.Base("helper"), 739 Metadata: map[string]interface{}{ 740 "layer": "helper", 741 "names": []string{"test-name-1", "test-name-2"}, 742 "version": "test-version", 743 }, 744 Launch: true, 745 Build: false, 746 })) 747 }) 748 }) 749 750 context("HelperLayerContributor", func() { 751 var ( 752 buildpack libcnb.Buildpack 753 hlc libpak.HelperLayerContributor 754 ) 755 756 it.Before(func() { 757 buildpack.Info = libcnb.BuildpackInfo{ 758 ID: "test-id", 759 Name: "test-name", 760 Version: "test-version", 761 Homepage: "test-homepage", 762 } 763 764 buildpack.Path = t.TempDir() 765 file := filepath.Join(buildpack.Path, "bin") 766 Expect(os.MkdirAll(file, 0755)).To(Succeed()) 767 768 file = filepath.Join(file, "helper") 769 Expect(os.WriteFile(file, []byte{}, 0755)).To(Succeed()) 770 771 hlc = libpak.HelperLayerContributor{ 772 Path: file, 773 BuildpackInfo: buildpack.Info, 774 Logger: bard.Logger{}, 775 Names: []string{"test-name-1", "test-name-2"}, 776 } 777 }) 778 779 it.After(func() { 780 Expect(os.RemoveAll(buildpack.Path)).To(Succeed()) 781 }) 782 783 it("calls function with no existing metadata", func() { 784 _, err := hlc.Contribute(layer) 785 Expect(err).NotTo(HaveOccurred()) 786 787 Expect(filepath.Join(layer.Exec.FilePath("test-name-1"))).To(BeAnExistingFile()) 788 }) 789 790 it("calls function with non-matching metadata", func() { 791 layer.Metadata["alpha"] = "other-alpha" 792 793 _, err := hlc.Contribute(layer) 794 Expect(err).NotTo(HaveOccurred()) 795 796 file := filepath.Join(layer.Exec.FilePath("test-name-1")) 797 Expect(file).To(BeAnExistingFile()) 798 Expect(os.Readlink(file)).To(Equal(filepath.Join(layer.Path, "helper"))) 799 800 file = filepath.Join(layer.Exec.FilePath("test-name-2")) 801 Expect(file).To(BeAnExistingFile()) 802 Expect(os.Readlink(file)).To(Equal(filepath.Join(layer.Path, "helper"))) 803 }) 804 805 it("does not call function with matching metadata", func() { 806 buildpackInfo := map[string]interface{}{ 807 "id": buildpack.Info.ID, 808 "name": buildpack.Info.Name, 809 "version": buildpack.Info.Version, 810 "homepage": buildpack.Info.Homepage, 811 "clear-env": buildpack.Info.ClearEnvironment, 812 "description": "", 813 } 814 layer.Metadata["buildpackInfo"] = buildpackInfo 815 layer.Metadata["helperNames"] = []interface{}{hlc.Names[0], hlc.Names[1]} 816 817 _, err := hlc.Contribute(layer) 818 819 Expect(err).NotTo(HaveOccurred()) 820 821 Expect(filepath.Join(layer.Exec.FilePath("test-name-1"))).NotTo(BeAnExistingFile()) 822 Expect(filepath.Join(layer.Exec.FilePath("test-name-2"))).NotTo(BeAnExistingFile()) 823 }) 824 825 it("adds expected metadata to layer", func() { 826 layer, err := hlc.Contribute(layer) 827 Expect(err).NotTo(HaveOccurred()) 828 829 buildpackInfo := map[string]interface{}{ 830 "id": buildpack.Info.ID, 831 "name": buildpack.Info.Name, 832 "version": buildpack.Info.Version, 833 "homepage": buildpack.Info.Homepage, 834 "clear-env": buildpack.Info.ClearEnvironment, 835 "description": "", 836 } 837 Expect(layer.Metadata).To(Equal(map[string]interface{}{"buildpackInfo": buildpackInfo, "helperNames": []interface{}{hlc.Names[0], hlc.Names[1]}})) 838 }) 839 840 it("sets layer flags regardless of caching behavior (required for 0.6 API)", func() { 841 buildpackInfo := map[string]interface{}{ 842 "id": buildpack.Info.ID, 843 "name": buildpack.Info.Name, 844 "version": buildpack.Info.Version, 845 "homepage": buildpack.Info.Homepage, 846 "clear-env": buildpack.Info.ClearEnvironment, 847 "description": "", 848 } 849 layer.Metadata["buildpackInfo"] = buildpackInfo 850 layer.Metadata["helperNames"] = []interface{}{hlc.Names[0], hlc.Names[1]} 851 852 // Launch is the only one set & always true 853 854 layer, err := hlc.Contribute(layer) 855 Expect(err).NotTo(HaveOccurred()) 856 857 Expect(filepath.Join(layer.Exec.FilePath("test-name-1"))).NotTo(BeAnExistingFile()) 858 Expect(filepath.Join(layer.Exec.FilePath("test-name-2"))).NotTo(BeAnExistingFile()) 859 860 Expect(layer.LayerTypes.Launch).To(BeTrue()) 861 Expect(layer.LayerTypes.Cache).To(BeFalse()) 862 Expect(layer.LayerTypes.Build).To(BeFalse()) 863 }) 864 865 it("adds expected Syft SBOM file", func() { 866 layer.Metadata = map[string]interface{}{} 867 868 _, err := hlc.Contribute(layer) 869 Expect(err).NotTo(HaveOccurred()) 870 871 Expect(filepath.Join(layer.Exec.FilePath("test-name-1"))).To(BeAnExistingFile()) 872 Expect(filepath.Join(layer.Exec.FilePath("test-name-2"))).To(BeAnExistingFile()) 873 874 outputFile := layer.SBOMPath(libcnb.SyftJSON) 875 Expect(outputFile).To(BeARegularFile()) 876 877 data, err := os.ReadFile(outputFile) 878 Expect(err).ToNot(HaveOccurred()) 879 Expect(string(data)).To(ContainSubstring(`"Artifacts":[`)) 880 Expect(string(data)).To(ContainSubstring(`"FoundBy":"libpak",`)) 881 Expect(string(data)).To(ContainSubstring(`"PURL":"pkg:generic/test-id@test-version"`)) 882 Expect(string(data)).To(ContainSubstring(`"CPEs":["cpe:2.3:a:test-id:test-name-1:test-version:*:*:*:*:*:*:*","cpe:2.3:a:test-id:test-name-2:test-version:*:*:*:*:*:*:*"]`)) 883 Expect(string(data)).To(ContainSubstring(`"Schema":{`)) 884 Expect(string(data)).To(ContainSubstring(`"Descriptor":{`)) 885 Expect(string(data)).To(ContainSubstring(`"Source":{`)) 886 }) 887 }) 888 }