github.com/paketo-buildpacks/libpak@v1.70.0/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("gracefully handles a deprecationDate in time.Time format in actual layer metadata", func() { 558 // reusing It: does not call function with non-matching deprecation_date format 559 // but this time with a deprecationDate formatted as time.Time in the actual layer metadata 560 actualDeprecationDate, _ := time.Parse(time.RFC3339, "2021-04-01T00:00:00Z") 561 562 dependency = libpak.BuildpackDependency{ 563 ID: "test-id", 564 Name: "test-name", 565 Version: "1.1.1", 566 URI: fmt.Sprintf("%s/test-path", server.URL()), 567 SHA256: "576dd8416de5619ea001d9662291d62444d1292a38e96956bc4651c01f14bca1", 568 Stacks: []string{"test-stack"}, 569 Licenses: []libpak.BuildpackDependencyLicense{ 570 { 571 Type: "test-type", 572 URI: "test-uri", 573 }, 574 }, 575 CPEs: []string{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"}, 576 PURL: "pkg:generic/some-java11@11.0.2?arch=amd64", 577 DeprecationDate: dependency.DeprecationDate, // parsed as '2021-04-01 00:00:00 +0000 UTC' 578 } 579 dlc.ExpectedMetadata = map[string]interface{}{"dependency": dependency} 580 581 layer.Metadata = map[string]interface{}{"dependency": map[string]interface{}{ 582 "id": dependency.ID, 583 "name": dependency.Name, 584 "version": dependency.Version, 585 "uri": dependency.URI, 586 "sha256": dependency.SHA256, 587 "stacks": []interface{}{dependency.Stacks[0]}, 588 "licenses": []map[string]interface{}{ 589 { 590 "type": dependency.Licenses[0].Type, 591 "uri": dependency.Licenses[0].URI, 592 }, 593 }, 594 "cpes": []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"}, 595 "purl": "pkg:generic/some-java11@11.0.2?arch=amd64", 596 "deprecation_date": actualDeprecationDate, // does not match without truncation 597 }} 598 599 var called bool 600 601 _, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 602 defer artifact.Close() 603 604 called = true 605 return layer, nil 606 }) 607 Expect(err).NotTo(HaveOccurred()) 608 609 Expect(called).To(BeFalse()) 610 }) 611 612 it("does not panic on unsupported deprecationDate format in layer metadata", func() { 613 // Unexpected type (not string or time.Time) 614 actualDeprecationDate := 1234 615 616 dependency = libpak.BuildpackDependency{ 617 ID: "test-id", 618 DeprecationDate: dependency.DeprecationDate, // parsed as '2021-04-01 00:00:00 +0000 UTC' 619 } 620 dlc.ExpectedMetadata = map[string]interface{}{"dependency": dependency} 621 622 layer.Metadata = map[string]interface{}{"dependency": map[string]interface{}{ 623 "id": dependency.ID, 624 "deprecation_date": actualDeprecationDate, // does not match without truncation 625 }} 626 627 var called bool 628 629 _, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 630 defer artifact.Close() 631 632 called = true 633 return layer, nil 634 }) 635 Expect(err).To(MatchError(ContainSubstring("unexpected type int for deprecation_date"))) 636 Expect(called).To(BeFalse()) 637 }) 638 639 it("does not contribute when deprecation_date is found on metadata map root", func() { 640 dependency = libpak.BuildpackDependency{ 641 ID: "test-id", 642 Name: "test-name", 643 Version: "1.1.1", 644 URI: fmt.Sprintf("%s/test-path", server.URL()), 645 SHA256: "576dd8416de5619ea001d9662291d62444d1292a38e96956bc4651c01f14bca1", 646 Stacks: []string{"test-stack"}, 647 Licenses: []libpak.BuildpackDependencyLicense{ 648 { 649 Type: "test-type", 650 URI: "test-uri", 651 }, 652 }, 653 CPEs: []string{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"}, 654 PURL: "pkg:generic/some-java11@11.0.2?arch=amd64", 655 } 656 dlc.ExpectedMetadata = dependency 657 658 layer.Metadata = map[string]interface{}{ 659 "id": dependency.ID, 660 "name": dependency.Name, 661 "version": dependency.Version, 662 "uri": dependency.URI, 663 "sha256": dependency.SHA256, 664 "stacks": []interface{}{dependency.Stacks[0]}, 665 "licenses": []map[string]interface{}{ 666 { 667 "type": dependency.Licenses[0].Type, 668 "uri": dependency.Licenses[0].URI, 669 }, 670 }, 671 "cpes": []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"}, 672 "purl": "pkg:generic/some-java11@11.0.2?arch=amd64", 673 "deprecation_date": "0001-01-01T00:00:00Z", 674 } 675 676 var called bool 677 678 _, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 679 defer artifact.Close() 680 681 called = true 682 return layer, nil 683 }) 684 Expect(err).NotTo(HaveOccurred()) 685 686 Expect(called).To(BeFalse()) 687 }) 688 689 it("does not call function with missing deprecation_date", func() { 690 dependency = libpak.BuildpackDependency{ 691 ID: "test-id", 692 Name: "test-name", 693 Version: "1.1.1", 694 URI: fmt.Sprintf("%s/test-path", server.URL()), 695 SHA256: "576dd8416de5619ea001d9662291d62444d1292a38e96956bc4651c01f14bca1", 696 Stacks: []string{"test-stack"}, 697 Licenses: []libpak.BuildpackDependencyLicense{ 698 { 699 Type: "test-type", 700 URI: "test-uri", 701 }, 702 }, 703 CPEs: []string{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"}, 704 PURL: "pkg:generic/some-java11@11.0.2?arch=amd64", 705 } 706 dlc.ExpectedMetadata = map[string]interface{}{"dependency": dependency} 707 708 layer.Metadata = map[string]interface{}{"dependency": map[string]interface{}{ 709 "id": dependency.ID, 710 "name": dependency.Name, 711 "version": dependency.Version, 712 "uri": dependency.URI, 713 "sha256": dependency.SHA256, 714 "stacks": []interface{}{dependency.Stacks[0]}, 715 "licenses": []map[string]interface{}{ 716 { 717 "type": dependency.Licenses[0].Type, 718 "uri": dependency.Licenses[0].URI, 719 }, 720 }, 721 "cpes": []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"}, 722 "purl": "pkg:generic/some-java11@11.0.2?arch=amd64", 723 "deprecation_date": "0001-01-01T00:00:00Z", 724 }} 725 726 var called bool 727 728 _, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 729 defer artifact.Close() 730 731 called = true 732 return layer, nil 733 }) 734 Expect(err).NotTo(HaveOccurred()) 735 736 Expect(called).To(BeFalse()) 737 }) 738 739 it("returns function error", func() { 740 server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture")) 741 742 _, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 743 defer artifact.Close() 744 745 return libcnb.Layer{}, fmt.Errorf("test-error") 746 }) 747 Expect(err).To(MatchError("test-error")) 748 }) 749 750 it("adds expected metadata to layer", func() { 751 server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture")) 752 753 layer, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 754 defer artifact.Close() 755 return layer, nil 756 }) 757 Expect(err).NotTo(HaveOccurred()) 758 759 Expect(layer.Metadata).To(Equal(map[string]interface{}{ 760 "id": dependency.ID, 761 "name": dependency.Name, 762 "version": dependency.Version, 763 "uri": dependency.URI, 764 "sha256": dependency.SHA256, 765 "stacks": []interface{}{dependency.Stacks[0]}, 766 "licenses": []map[string]interface{}{ 767 { 768 "type": dependency.Licenses[0].Type, 769 "uri": dependency.Licenses[0].URI, 770 }, 771 }, 772 "cpes": []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"}, 773 "purl": "pkg:generic/some-java11@11.0.2?arch=amd64", 774 "deprecation_date": dependency.DeprecationDate, 775 })) 776 }) 777 778 it("sets layer flags regardless of caching behavior (required for 0.6 API)", func() { 779 layer.Metadata = map[string]interface{}{ 780 "id": dependency.ID, 781 "name": dependency.Name, 782 "version": dependency.Version, 783 "uri": dependency.URI, 784 "sha256": dependency.SHA256, 785 "stacks": []interface{}{dependency.Stacks[0]}, 786 "licenses": []map[string]interface{}{ 787 { 788 "type": dependency.Licenses[0].Type, 789 "uri": dependency.Licenses[0].URI, 790 }, 791 }, 792 "cpes": []interface{}{"cpe:2.3:a:some:jre:11.0.2:*:*:*:*:*:*:*"}, 793 "purl": "pkg:generic/some-java11@11.0.2?arch=amd64", 794 "deprecation_date": dependency.DeprecationDate, 795 } 796 dlc.ExpectedTypes.Launch = true 797 dlc.ExpectedTypes.Cache = true 798 dlc.ExpectedTypes.Build = true 799 800 var called bool 801 802 layer, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 803 defer artifact.Close() 804 805 called = true 806 return layer, nil 807 }) 808 Expect(err).NotTo(HaveOccurred()) 809 810 Expect(called).To(BeFalse()) 811 812 Expect(layer.LayerTypes.Launch).To(BeTrue()) 813 Expect(layer.LayerTypes.Cache).To(BeTrue()) 814 Expect(layer.LayerTypes.Build).To(BeTrue()) 815 }) 816 817 it("adds expected Syft SBOM file", func() { 818 server.AppendHandlers(ghttp.RespondWith(http.StatusOK, "test-fixture")) 819 820 layer, err := dlc.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) { 821 defer artifact.Close() 822 return layer, nil 823 }) 824 Expect(err).NotTo(HaveOccurred()) 825 826 outputFile := layer.SBOMPath(libcnb.SyftJSON) 827 Expect(outputFile).To(BeARegularFile()) 828 829 data, err := os.ReadFile(outputFile) 830 Expect(err).ToNot(HaveOccurred()) 831 Expect(string(data)).To(ContainSubstring(`"Artifacts":[`)) 832 Expect(string(data)).To(ContainSubstring(`"FoundBy":"libpak",`)) 833 Expect(string(data)).To(ContainSubstring(`"PURL":"pkg:generic/some-java11@11.0.2?arch=amd64"`)) 834 Expect(string(data)).To(ContainSubstring(`"Schema":{`)) 835 Expect(string(data)).To(ContainSubstring(`"Descriptor":{`)) 836 Expect(string(data)).To(ContainSubstring(`"Source":{`)) 837 }) 838 }) 839 840 context("NewHelperLayer", func() { 841 it("returns a BOM entry with version equal to buildpack version", func() { 842 _, entry := libpak.NewHelperLayer(libcnb.Buildpack{ 843 API: "0.6", 844 Info: libcnb.BuildpackInfo{ 845 Version: "test-version", 846 }, 847 }, "test-name-1", "test-name-2") 848 Expect(entry).To(Equal( 849 libcnb.BOMEntry{ 850 Name: filepath.Base("helper"), 851 Metadata: map[string]interface{}{ 852 "layer": "helper", 853 "names": []string{"test-name-1", "test-name-2"}, 854 "version": "test-version", 855 }, 856 Launch: true, 857 Build: false, 858 }, 859 )) 860 }) 861 862 it("returns a BOM entry on API 0.7 too", func() { 863 _, entry := libpak.NewHelperLayer(libcnb.Buildpack{ 864 API: "0.7", 865 Info: libcnb.BuildpackInfo{ 866 Version: "test-version", 867 }, 868 }, "test-name-1", "test-name-2") 869 Expect(entry).To(Equal(libcnb.BOMEntry{ 870 Name: filepath.Base("helper"), 871 Metadata: map[string]interface{}{ 872 "layer": "helper", 873 "names": []string{"test-name-1", "test-name-2"}, 874 "version": "test-version", 875 }, 876 Launch: true, 877 Build: false, 878 })) 879 }) 880 }) 881 882 context("HelperLayerContributor", func() { 883 var ( 884 buildpack libcnb.Buildpack 885 hlc libpak.HelperLayerContributor 886 ) 887 888 it.Before(func() { 889 buildpack.Info = libcnb.BuildpackInfo{ 890 ID: "test-id", 891 Name: "test-name", 892 Version: "test-version", 893 Homepage: "test-homepage", 894 } 895 896 buildpack.Path = t.TempDir() 897 file := filepath.Join(buildpack.Path, "bin") 898 Expect(os.MkdirAll(file, 0755)).To(Succeed()) 899 900 file = filepath.Join(file, "helper") 901 Expect(os.WriteFile(file, []byte{}, 0755)).To(Succeed()) 902 903 hlc = libpak.HelperLayerContributor{ 904 Path: file, 905 BuildpackInfo: buildpack.Info, 906 Logger: bard.Logger{}, 907 Names: []string{"test-name-1", "test-name-2"}, 908 } 909 }) 910 911 it.After(func() { 912 Expect(os.RemoveAll(buildpack.Path)).To(Succeed()) 913 }) 914 915 it("calls function with no existing metadata", func() { 916 _, err := hlc.Contribute(layer) 917 Expect(err).NotTo(HaveOccurred()) 918 919 Expect(filepath.Join(layer.Exec.FilePath("test-name-1"))).To(BeAnExistingFile()) 920 }) 921 922 it("calls function with non-matching metadata", func() { 923 layer.Metadata["alpha"] = "other-alpha" 924 925 _, err := hlc.Contribute(layer) 926 Expect(err).NotTo(HaveOccurred()) 927 928 file := filepath.Join(layer.Exec.FilePath("test-name-1")) 929 Expect(file).To(BeAnExistingFile()) 930 Expect(os.Readlink(file)).To(Equal(filepath.Join(layer.Path, "helper"))) 931 932 file = filepath.Join(layer.Exec.FilePath("test-name-2")) 933 Expect(file).To(BeAnExistingFile()) 934 Expect(os.Readlink(file)).To(Equal(filepath.Join(layer.Path, "helper"))) 935 }) 936 937 it("does not call function with matching metadata", func() { 938 buildpackInfo := map[string]interface{}{ 939 "id": buildpack.Info.ID, 940 "name": buildpack.Info.Name, 941 "version": buildpack.Info.Version, 942 "homepage": buildpack.Info.Homepage, 943 "clear-env": buildpack.Info.ClearEnvironment, 944 "description": "", 945 } 946 layer.Metadata["buildpackInfo"] = buildpackInfo 947 layer.Metadata["helperNames"] = []interface{}{hlc.Names[0], hlc.Names[1]} 948 949 _, err := hlc.Contribute(layer) 950 951 Expect(err).NotTo(HaveOccurred()) 952 953 Expect(filepath.Join(layer.Exec.FilePath("test-name-1"))).NotTo(BeAnExistingFile()) 954 Expect(filepath.Join(layer.Exec.FilePath("test-name-2"))).NotTo(BeAnExistingFile()) 955 }) 956 957 it("adds expected metadata to layer", func() { 958 layer, err := hlc.Contribute(layer) 959 Expect(err).NotTo(HaveOccurred()) 960 961 buildpackInfo := map[string]interface{}{ 962 "id": buildpack.Info.ID, 963 "name": buildpack.Info.Name, 964 "version": buildpack.Info.Version, 965 "homepage": buildpack.Info.Homepage, 966 "clear-env": buildpack.Info.ClearEnvironment, 967 "description": "", 968 } 969 Expect(layer.Metadata).To(Equal(map[string]interface{}{"buildpackInfo": buildpackInfo, "helperNames": []interface{}{hlc.Names[0], hlc.Names[1]}})) 970 }) 971 972 it("sets layer flags regardless of caching behavior (required for 0.6 API)", func() { 973 buildpackInfo := map[string]interface{}{ 974 "id": buildpack.Info.ID, 975 "name": buildpack.Info.Name, 976 "version": buildpack.Info.Version, 977 "homepage": buildpack.Info.Homepage, 978 "clear-env": buildpack.Info.ClearEnvironment, 979 "description": "", 980 } 981 layer.Metadata["buildpackInfo"] = buildpackInfo 982 layer.Metadata["helperNames"] = []interface{}{hlc.Names[0], hlc.Names[1]} 983 984 // Launch is the only one set & always true 985 986 layer, err := hlc.Contribute(layer) 987 Expect(err).NotTo(HaveOccurred()) 988 989 Expect(filepath.Join(layer.Exec.FilePath("test-name-1"))).NotTo(BeAnExistingFile()) 990 Expect(filepath.Join(layer.Exec.FilePath("test-name-2"))).NotTo(BeAnExistingFile()) 991 992 Expect(layer.LayerTypes.Launch).To(BeTrue()) 993 Expect(layer.LayerTypes.Cache).To(BeFalse()) 994 Expect(layer.LayerTypes.Build).To(BeFalse()) 995 }) 996 997 it("adds expected Syft SBOM file", func() { 998 layer.Metadata = map[string]interface{}{} 999 1000 _, err := hlc.Contribute(layer) 1001 Expect(err).NotTo(HaveOccurred()) 1002 1003 Expect(filepath.Join(layer.Exec.FilePath("test-name-1"))).To(BeAnExistingFile()) 1004 Expect(filepath.Join(layer.Exec.FilePath("test-name-2"))).To(BeAnExistingFile()) 1005 1006 outputFile := layer.SBOMPath(libcnb.SyftJSON) 1007 Expect(outputFile).To(BeARegularFile()) 1008 1009 data, err := os.ReadFile(outputFile) 1010 Expect(err).ToNot(HaveOccurred()) 1011 Expect(string(data)).To(ContainSubstring(`"Artifacts":[`)) 1012 Expect(string(data)).To(ContainSubstring(`"FoundBy":"libpak",`)) 1013 Expect(string(data)).To(ContainSubstring(`"PURL":"pkg:generic/test-id@test-version"`)) 1014 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:*:*:*:*:*:*:*"]`)) 1015 Expect(string(data)).To(ContainSubstring(`"Schema":{`)) 1016 Expect(string(data)).To(ContainSubstring(`"Descriptor":{`)) 1017 Expect(string(data)).To(ContainSubstring(`"Source":{`)) 1018 }) 1019 }) 1020 }