github.com/YousefHaggyHeroku/pack@v1.5.5/internal/buildpackage/builder_test.go (about) 1 package buildpackage_test 2 3 import ( 4 "archive/tar" 5 "compress/gzip" 6 "encoding/json" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "path" 12 "path/filepath" 13 "testing" 14 15 "github.com/buildpacks/imgutil/layer" 16 17 "github.com/buildpacks/imgutil/fakes" 18 "github.com/buildpacks/lifecycle/api" 19 "github.com/golang/mock/gomock" 20 "github.com/google/go-containerregistry/pkg/v1/stream" 21 "github.com/heroku/color" 22 v1 "github.com/opencontainers/image-spec/specs-go/v1" 23 "github.com/sclevine/spec" 24 "github.com/sclevine/spec/report" 25 26 "github.com/YousefHaggyHeroku/pack/internal/buildpackage" 27 "github.com/YousefHaggyHeroku/pack/internal/dist" 28 ifakes "github.com/YousefHaggyHeroku/pack/internal/fakes" 29 h "github.com/YousefHaggyHeroku/pack/testhelpers" 30 "github.com/YousefHaggyHeroku/pack/testmocks" 31 ) 32 33 func TestPackageBuilder(t *testing.T) { 34 color.Disable(true) 35 defer color.Disable(false) 36 spec.Run(t, "PackageBuilder", testPackageBuilder, spec.Parallel(), spec.Report(report.Terminal{})) 37 } 38 39 func testPackageBuilder(t *testing.T, when spec.G, it spec.S) { 40 var ( 41 mockController *gomock.Controller 42 mockImageFactory *testmocks.MockImageFactory 43 subject *buildpackage.PackageBuilder 44 tmpDir string 45 ) 46 47 it.Before(func() { 48 mockController = gomock.NewController(t) 49 mockImageFactory = testmocks.NewMockImageFactory(mockController) 50 51 fakePackageImage := fakes.NewImage("some/package", "", nil) 52 mockImageFactory.EXPECT().NewImage("some/package", true).Return(fakePackageImage, nil).AnyTimes() 53 54 subject = buildpackage.NewBuilder(mockImageFactory) 55 56 var err error 57 tmpDir, err = ioutil.TempDir("", "package_builder_tests") 58 h.AssertNil(t, err) 59 }) 60 61 it.After(func() { 62 h.AssertNil(t, os.RemoveAll(tmpDir)) 63 mockController.Finish() 64 }) 65 66 when("validation", func() { 67 for _, test := range []struct { 68 name string 69 fn func() error 70 }{ 71 {name: "SaveAsImage", fn: func() error { 72 _, err := subject.SaveAsImage("some/package", false, "linux") 73 return err 74 }}, 75 {name: "SaveAsImage", fn: func() error { 76 _, err := subject.SaveAsImage("some/package", false, "windows") 77 return err 78 }}, 79 {name: "SaveAsFile", fn: func() error { 80 return subject.SaveAsFile(path.Join(tmpDir, "package.cnb"), "windows") 81 }}, 82 {name: "SaveAsFile", fn: func() error { 83 return subject.SaveAsFile(path.Join(tmpDir, "package.cnb"), "linux") 84 }}, 85 } { 86 testFn := test.fn 87 when(test.name, func() { 88 when("validate buildpack", func() { 89 when("buildpack not set", func() { 90 it("returns error", func() { 91 err := testFn() 92 h.AssertError(t, err, "buildpack must be set") 93 }) 94 }) 95 96 when("there is a buildpack not referenced", func() { 97 it("should error", func() { 98 bp1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 99 API: api.MustParse("0.2"), 100 Info: dist.BuildpackInfo{ 101 ID: "bp.1.id", 102 Version: "bp.1.version", 103 }, 104 Stacks: []dist.Stack{{ID: "some.stack"}}, 105 }, 0644) 106 h.AssertNil(t, err) 107 subject.SetBuildpack(bp1) 108 109 bp2, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 110 API: api.MustParse("0.2"), 111 Info: dist.BuildpackInfo{ID: "bp.2.id", Version: "bp.2.version"}, 112 Stacks: []dist.Stack{{ID: "some.stack"}}, 113 Order: nil, 114 }, 0644) 115 h.AssertNil(t, err) 116 subject.AddDependency(bp2) 117 118 err = testFn() 119 h.AssertError(t, err, "buildpack 'bp.2.id@bp.2.version' is not used by buildpack 'bp.1.id@bp.1.version'") 120 }) 121 }) 122 123 when("there is a referenced buildpack from main buildpack that is not present", func() { 124 it("should error", func() { 125 mainBP, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 126 API: api.MustParse("0.2"), 127 Info: dist.BuildpackInfo{ 128 ID: "bp.1.id", 129 Version: "bp.1.version", 130 }, 131 Order: dist.Order{{ 132 Group: []dist.BuildpackRef{ 133 {BuildpackInfo: dist.BuildpackInfo{ID: "bp.present.id", Version: "bp.present.version"}}, 134 {BuildpackInfo: dist.BuildpackInfo{ID: "bp.missing.id", Version: "bp.missing.version"}}, 135 }, 136 }}, 137 }, 0644) 138 h.AssertNil(t, err) 139 subject.SetBuildpack(mainBP) 140 141 presentBP, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 142 API: api.MustParse("0.2"), 143 Info: dist.BuildpackInfo{ID: "bp.present.id", Version: "bp.present.version"}, 144 Stacks: []dist.Stack{{ID: "some.stack"}}, 145 Order: nil, 146 }, 0644) 147 h.AssertNil(t, err) 148 subject.AddDependency(presentBP) 149 150 err = testFn() 151 h.AssertError(t, err, "buildpack 'bp.1.id@bp.1.version' references buildpack 'bp.missing.id@bp.missing.version' which is not present") 152 }) 153 }) 154 155 when("there is a referenced buildpack from dependency buildpack that is not present", func() { 156 it("should error", func() { 157 mainBP, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 158 API: api.MustParse("0.2"), 159 Info: dist.BuildpackInfo{ 160 ID: "bp.1.id", 161 Version: "bp.1.version", 162 }, 163 Order: dist.Order{{ 164 Group: []dist.BuildpackRef{ 165 {BuildpackInfo: dist.BuildpackInfo{ID: "bp.present.id", Version: "bp.present.version"}}, 166 }, 167 }}, 168 }, 0644) 169 h.AssertNil(t, err) 170 subject.SetBuildpack(mainBP) 171 172 presentBP, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 173 API: api.MustParse("0.2"), 174 Info: dist.BuildpackInfo{ID: "bp.present.id", Version: "bp.present.version"}, 175 Order: dist.Order{{ 176 Group: []dist.BuildpackRef{ 177 {BuildpackInfo: dist.BuildpackInfo{ID: "bp.missing.id", Version: "bp.missing.version"}}, 178 }, 179 }}, 180 }, 0644) 181 h.AssertNil(t, err) 182 subject.AddDependency(presentBP) 183 184 err = testFn() 185 h.AssertError(t, err, "buildpack 'bp.present.id@bp.present.version' references buildpack 'bp.missing.id@bp.missing.version' which is not present") 186 }) 187 }) 188 }) 189 190 when("validate stacks", func() { 191 when("buildpack is meta-buildpack", func() { 192 it("should succeed", func() { 193 buildpack, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 194 API: api.MustParse("0.2"), 195 Info: dist.BuildpackInfo{ 196 ID: "bp.1.id", 197 Version: "bp.1.version", 198 }, 199 Stacks: nil, 200 Order: dist.Order{{ 201 Group: []dist.BuildpackRef{ 202 {BuildpackInfo: dist.BuildpackInfo{ID: "bp.nested.id", Version: "bp.nested.version"}}, 203 }, 204 }}, 205 }, 0644) 206 h.AssertNil(t, err) 207 208 subject.SetBuildpack(buildpack) 209 210 dependency, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 211 API: api.MustParse("0.2"), 212 Info: dist.BuildpackInfo{ 213 ID: "bp.nested.id", 214 Version: "bp.nested.version", 215 }, 216 Stacks: []dist.Stack{ 217 {ID: "stack.id.1", Mixins: []string{"Mixin-A"}}, 218 }, 219 Order: nil, 220 }, 0644) 221 h.AssertNil(t, err) 222 223 subject.AddDependency(dependency) 224 225 err = testFn() 226 h.AssertNil(t, err) 227 }) 228 }) 229 230 when("dependencies don't have a common stack", func() { 231 it("should error", func() { 232 buildpack, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 233 API: api.MustParse("0.2"), 234 Info: dist.BuildpackInfo{ 235 ID: "bp.1.id", 236 Version: "bp.1.version", 237 }, 238 Order: dist.Order{{ 239 Group: []dist.BuildpackRef{{ 240 BuildpackInfo: dist.BuildpackInfo{ID: "bp.2.id", Version: "bp.2.version"}, 241 Optional: false, 242 }, { 243 BuildpackInfo: dist.BuildpackInfo{ID: "bp.3.id", Version: "bp.3.version"}, 244 Optional: false, 245 }}, 246 }}, 247 }, 0644) 248 h.AssertNil(t, err) 249 subject.SetBuildpack(buildpack) 250 251 dependency1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 252 API: api.MustParse("0.2"), 253 Info: dist.BuildpackInfo{ 254 ID: "bp.2.id", 255 Version: "bp.2.version", 256 }, 257 Stacks: []dist.Stack{ 258 {ID: "stack.id.1", Mixins: []string{"Mixin-A"}}, 259 {ID: "stack.id.2", Mixins: []string{"Mixin-A"}}, 260 }, 261 Order: nil, 262 }, 0644) 263 h.AssertNil(t, err) 264 subject.AddDependency(dependency1) 265 266 dependency2, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 267 API: api.MustParse("0.2"), 268 Info: dist.BuildpackInfo{ 269 ID: "bp.3.id", 270 Version: "bp.3.version", 271 }, 272 Stacks: []dist.Stack{ 273 {ID: "stack.id.3", Mixins: []string{"Mixin-A"}}, 274 }, 275 Order: nil, 276 }, 0644) 277 h.AssertNil(t, err) 278 subject.AddDependency(dependency2) 279 280 _, err = subject.SaveAsImage("some/package", false, "linux") 281 h.AssertError(t, err, "no compatible stacks among provided buildpacks") 282 }) 283 }) 284 285 when("dependency has stacks that aren't supported by buildpack", func() { 286 it("should only support common stacks", func() { 287 buildpack, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 288 API: api.MustParse("0.2"), 289 Info: dist.BuildpackInfo{ 290 ID: "bp.1.id", 291 Version: "bp.1.version", 292 }, 293 Order: dist.Order{{ 294 Group: []dist.BuildpackRef{{ 295 BuildpackInfo: dist.BuildpackInfo{ID: "bp.2.id", Version: "bp.2.version"}, 296 Optional: false, 297 }, { 298 BuildpackInfo: dist.BuildpackInfo{ID: "bp.3.id", Version: "bp.3.version"}, 299 Optional: false, 300 }}, 301 }}, 302 }, 0644) 303 h.AssertNil(t, err) 304 subject.SetBuildpack(buildpack) 305 306 dependency1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 307 API: api.MustParse("0.2"), 308 Info: dist.BuildpackInfo{ 309 ID: "bp.2.id", 310 Version: "bp.2.version", 311 }, 312 Stacks: []dist.Stack{ 313 {ID: "stack.id.1", Mixins: []string{"Mixin-A"}}, 314 {ID: "stack.id.2", Mixins: []string{"Mixin-A"}}, 315 }, 316 Order: nil, 317 }, 0644) 318 h.AssertNil(t, err) 319 subject.AddDependency(dependency1) 320 321 dependency2, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 322 API: api.MustParse("0.2"), 323 Info: dist.BuildpackInfo{ 324 ID: "bp.3.id", 325 Version: "bp.3.version", 326 }, 327 Stacks: []dist.Stack{ 328 {ID: "stack.id.1", Mixins: []string{"Mixin-A"}}, 329 }, 330 Order: nil, 331 }, 0644) 332 h.AssertNil(t, err) 333 subject.AddDependency(dependency2) 334 335 img, err := subject.SaveAsImage("some/package", false, "linux") 336 h.AssertNil(t, err) 337 338 metadata := buildpackage.Metadata{} 339 _, err = dist.GetLabel(img, "io.buildpacks.buildpackage.metadata", &metadata) 340 h.AssertNil(t, err) 341 342 h.AssertEq(t, metadata.Stacks, []dist.Stack{{ID: "stack.id.1", Mixins: []string{"Mixin-A"}}}) 343 }) 344 }) 345 346 when("dependency is meta-buildpack", func() { 347 it("should succeed and compute common stacks", func() { 348 buildpack, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 349 API: api.MustParse("0.2"), 350 Info: dist.BuildpackInfo{ 351 ID: "bp.1.id", 352 Version: "bp.1.version", 353 }, 354 Stacks: nil, 355 Order: dist.Order{{ 356 Group: []dist.BuildpackRef{ 357 {BuildpackInfo: dist.BuildpackInfo{ID: "bp.nested.id", Version: "bp.nested.version"}}, 358 }, 359 }}, 360 }, 0644) 361 h.AssertNil(t, err) 362 363 subject.SetBuildpack(buildpack) 364 365 dependencyOrder, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 366 API: api.MustParse("0.2"), 367 Info: dist.BuildpackInfo{ 368 ID: "bp.nested.id", 369 Version: "bp.nested.version", 370 }, 371 Order: dist.Order{{ 372 Group: []dist.BuildpackRef{ 373 {BuildpackInfo: dist.BuildpackInfo{ 374 ID: "bp.nested.nested.id", 375 Version: "bp.nested.nested.version", 376 }}, 377 }, 378 }}, 379 }, 0644) 380 h.AssertNil(t, err) 381 382 subject.AddDependency(dependencyOrder) 383 384 dependencyNestedNested, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 385 API: api.MustParse("0.2"), 386 Info: dist.BuildpackInfo{ 387 ID: "bp.nested.nested.id", 388 Version: "bp.nested.nested.version", 389 }, 390 Stacks: []dist.Stack{ 391 {ID: "stack.id.1", Mixins: []string{"Mixin-A"}}, 392 }, 393 Order: nil, 394 }, 0644) 395 h.AssertNil(t, err) 396 397 subject.AddDependency(dependencyNestedNested) 398 399 img, err := subject.SaveAsImage("some/package", false, "linux") 400 h.AssertNil(t, err) 401 402 metadata := buildpackage.Metadata{} 403 _, err = dist.GetLabel(img, "io.buildpacks.buildpackage.metadata", &metadata) 404 h.AssertNil(t, err) 405 406 h.AssertEq(t, metadata.Stacks, []dist.Stack{{ID: "stack.id.1", Mixins: []string{"Mixin-A"}}}) 407 }) 408 }) 409 }) 410 }) 411 } 412 }) 413 414 when("#SaveAsImage", func() { 415 it("sets metadata", func() { 416 buildpack1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 417 API: api.MustParse("0.2"), 418 Info: dist.BuildpackInfo{ 419 ID: "bp.1.id", 420 Version: "bp.1.version", 421 }, 422 Stacks: []dist.Stack{ 423 {ID: "stack.id.1"}, 424 {ID: "stack.id.2"}, 425 }, 426 Order: nil, 427 }, 0644) 428 h.AssertNil(t, err) 429 430 subject.SetBuildpack(buildpack1) 431 432 packageImage, err := subject.SaveAsImage("some/package", false, "linux") 433 h.AssertNil(t, err) 434 435 labelData, err := packageImage.Label("io.buildpacks.buildpackage.metadata") 436 h.AssertNil(t, err) 437 var md buildpackage.Metadata 438 h.AssertNil(t, json.Unmarshal([]byte(labelData), &md)) 439 440 h.AssertEq(t, md.ID, "bp.1.id") 441 h.AssertEq(t, md.Version, "bp.1.version") 442 h.AssertEq(t, len(md.Stacks), 2) 443 h.AssertEq(t, md.Stacks[0].ID, "stack.id.1") 444 h.AssertEq(t, md.Stacks[1].ID, "stack.id.2") 445 446 osVal, err := packageImage.OS() 447 h.AssertNil(t, err) 448 h.AssertEq(t, osVal, "linux") 449 }) 450 451 it("sets buildpack layers label", func() { 452 buildpack1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 453 API: api.MustParse("0.2"), 454 Info: dist.BuildpackInfo{ID: "bp.1.id", Version: "bp.1.version"}, 455 Stacks: []dist.Stack{{ID: "stack.id.1"}, {ID: "stack.id.2"}}, 456 Order: nil, 457 }, 0644) 458 h.AssertNil(t, err) 459 subject.SetBuildpack(buildpack1) 460 461 packageImage, err := subject.SaveAsImage("some/package", false, "linux") 462 h.AssertNil(t, err) 463 464 var bpLayers dist.BuildpackLayers 465 _, err = dist.GetLabel(packageImage, "io.buildpacks.buildpack.layers", &bpLayers) 466 h.AssertNil(t, err) 467 468 bp1Info, ok1 := bpLayers["bp.1.id"]["bp.1.version"] 469 h.AssertEq(t, ok1, true) 470 h.AssertEq(t, bp1Info.Stacks, []dist.Stack{{ID: "stack.id.1"}, {ID: "stack.id.2"}}) 471 }) 472 473 it("adds buildpack layers for linux", func() { 474 buildpack1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 475 API: api.MustParse("0.2"), 476 Info: dist.BuildpackInfo{ID: "bp.1.id", Version: "bp.1.version"}, 477 Stacks: []dist.Stack{{ID: "stack.id.1"}, {ID: "stack.id.2"}}, 478 Order: nil, 479 }, 0644) 480 h.AssertNil(t, err) 481 subject.SetBuildpack(buildpack1) 482 483 packageImage, err := subject.SaveAsImage("some/package", false, "linux") 484 h.AssertNil(t, err) 485 486 buildpackExists := func(name, version string) { 487 t.Helper() 488 dirPath := fmt.Sprintf("/cnb/buildpacks/%s/%s", name, version) 489 fakePackageImage := packageImage.(*fakes.Image) 490 layerTar, err := fakePackageImage.FindLayerWithPath(dirPath) 491 h.AssertNil(t, err) 492 493 h.AssertOnTarEntry(t, layerTar, dirPath, 494 h.IsDirectory(), 495 ) 496 497 h.AssertOnTarEntry(t, layerTar, dirPath+"/bin/build", 498 h.ContentEquals("build-contents"), 499 h.HasOwnerAndGroup(0, 0), 500 h.HasFileMode(0644), 501 ) 502 503 h.AssertOnTarEntry(t, layerTar, dirPath+"/bin/detect", 504 h.ContentEquals("detect-contents"), 505 h.HasOwnerAndGroup(0, 0), 506 h.HasFileMode(0644), 507 ) 508 } 509 510 buildpackExists("bp.1.id", "bp.1.version") 511 512 fakePackageImage := packageImage.(*fakes.Image) 513 h.AssertEq(t, fakePackageImage.NumberOfAddedLayers(), 1) 514 }) 515 516 it("adds baselayer + buildpack layers for windows", func() { 517 buildpack1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 518 API: api.MustParse("0.2"), 519 Info: dist.BuildpackInfo{ID: "bp.1.id", Version: "bp.1.version"}, 520 Stacks: []dist.Stack{{ID: "stack.id.1"}, {ID: "stack.id.2"}}, 521 Order: nil, 522 }, 0644) 523 h.AssertNil(t, err) 524 subject.SetBuildpack(buildpack1) 525 526 packageImage, err := subject.SaveAsImage("some/package", false, "windows") 527 h.AssertNil(t, err) 528 529 fakePackageImage := packageImage.(*fakes.Image) 530 531 osVal, err := fakePackageImage.OS() 532 h.AssertNil(t, err) 533 h.AssertEq(t, osVal, "windows") 534 535 h.AssertEq(t, fakePackageImage.NumberOfAddedLayers(), 2) 536 }) 537 }) 538 539 when("#SaveAsFile", func() { 540 it("sets metadata", func() { 541 buildpack1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 542 API: api.MustParse("0.2"), 543 Info: dist.BuildpackInfo{ID: "bp.1.id", Version: "bp.1.version"}, 544 Stacks: []dist.Stack{{ID: "stack.id.1"}, {ID: "stack.id.2"}}, 545 Order: nil, 546 }, 0644) 547 h.AssertNil(t, err) 548 subject.SetBuildpack(buildpack1) 549 550 outputFile := filepath.Join(tmpDir, fmt.Sprintf("package-%s.cnb", h.RandString(10))) 551 h.AssertNil(t, subject.SaveAsFile(outputFile, "linux")) 552 553 withContents := func(fn func(data []byte)) h.TarEntryAssertion { 554 return func(t *testing.T, header *tar.Header, data []byte) { 555 fn(data) 556 } 557 } 558 559 h.AssertOnTarEntry(t, outputFile, "/index.json", 560 h.HasOwnerAndGroup(0, 0), 561 h.HasFileMode(0755), 562 withContents(func(data []byte) { 563 index := v1.Index{} 564 err := json.Unmarshal(data, &index) 565 h.AssertNil(t, err) 566 h.AssertEq(t, len(index.Manifests), 1) 567 568 // manifest: application/vnd.docker.distribution.manifest.v2+json 569 h.AssertOnTarEntry(t, outputFile, 570 "/blobs/sha256/"+index.Manifests[0].Digest.Hex(), 571 h.HasOwnerAndGroup(0, 0), 572 h.IsJSON(), 573 574 withContents(func(data []byte) { 575 manifest := v1.Manifest{} 576 err := json.Unmarshal(data, &manifest) 577 h.AssertNil(t, err) 578 579 // config: application/vnd.docker.container.image.v1+json 580 h.AssertOnTarEntry(t, outputFile, 581 "/blobs/sha256/"+manifest.Config.Digest.Hex(), 582 h.HasOwnerAndGroup(0, 0), 583 h.IsJSON(), 584 // buildpackage metadata 585 h.ContentContains(`"io.buildpacks.buildpackage.metadata":"{\"id\":\"bp.1.id\",\"version\":\"bp.1.version\",\"stacks\":[{\"id\":\"stack.id.1\"},{\"id\":\"stack.id.2\"}]}"`), 586 // buildpack layers metadata 587 h.ContentContains(`"io.buildpacks.buildpack.layers":"{\"bp.1.id\":{\"bp.1.version\":{\"api\":\"0.2\",\"stacks\":[{\"id\":\"stack.id.1\"},{\"id\":\"stack.id.2\"}],\"layerDiffID\":\"sha256:a10862daec7a8a62fd04cc5d4520fdb80d4d5c07a3c146fb604a9c23c22fd5b0\"}}}"`), 588 // image os 589 h.ContentContains(`"os":"linux"`), 590 ) 591 })) 592 })) 593 }) 594 595 it("adds buildpack layers for linux", func() { 596 buildpack1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 597 API: api.MustParse("0.2"), 598 Info: dist.BuildpackInfo{ID: "bp.1.id", Version: "bp.1.version"}, 599 Stacks: []dist.Stack{{ID: "stack.id.1"}, {ID: "stack.id.2"}}, 600 Order: nil, 601 }, 0644) 602 h.AssertNil(t, err) 603 subject.SetBuildpack(buildpack1) 604 605 outputFile := filepath.Join(tmpDir, fmt.Sprintf("package-%s.cnb", h.RandString(10))) 606 h.AssertNil(t, subject.SaveAsFile(outputFile, "linux")) 607 608 h.AssertOnTarEntry(t, outputFile, "/blobs", 609 h.IsDirectory(), 610 h.HasOwnerAndGroup(0, 0), 611 h.HasFileMode(0755)) 612 h.AssertOnTarEntry(t, outputFile, "/blobs/sha256", 613 h.IsDirectory(), 614 h.HasOwnerAndGroup(0, 0), 615 h.HasFileMode(0755)) 616 617 bpReader, err := buildpack1.Open() 618 h.AssertNil(t, err) 619 defer bpReader.Close() 620 621 // layer: application/vnd.docker.image.rootfs.diff.tar.gzip 622 buildpackLayerSHA, err := computeLayerSHA(bpReader) 623 h.AssertNil(t, err) 624 h.AssertOnTarEntry(t, outputFile, 625 "/blobs/sha256/"+buildpackLayerSHA, 626 h.HasOwnerAndGroup(0, 0), 627 h.HasFileMode(0755), 628 h.IsGzipped(), 629 h.AssertOnNestedTar("/cnb/buildpacks/bp.1.id", 630 h.IsDirectory(), 631 h.HasOwnerAndGroup(0, 0), 632 h.HasFileMode(0644)), 633 h.AssertOnNestedTar("/cnb/buildpacks/bp.1.id/bp.1.version/bin/build", 634 h.ContentEquals("build-contents"), 635 h.HasOwnerAndGroup(0, 0), 636 h.HasFileMode(0644)), 637 h.AssertOnNestedTar("/cnb/buildpacks/bp.1.id/bp.1.version/bin/detect", 638 h.ContentEquals("detect-contents"), 639 h.HasOwnerAndGroup(0, 0), 640 h.HasFileMode(0644))) 641 }) 642 643 it("adds baselayer + buildpack layers for windows", func() { 644 buildpack1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{ 645 API: api.MustParse("0.2"), 646 Info: dist.BuildpackInfo{ID: "bp.1.id", Version: "bp.1.version"}, 647 Stacks: []dist.Stack{{ID: "stack.id.1"}, {ID: "stack.id.2"}}, 648 Order: nil, 649 }, 0644) 650 h.AssertNil(t, err) 651 subject.SetBuildpack(buildpack1) 652 653 outputFile := filepath.Join(tmpDir, fmt.Sprintf("package-%s.cnb", h.RandString(10))) 654 h.AssertNil(t, subject.SaveAsFile(outputFile, "windows")) 655 656 // Windows baselayer content is constant 657 expectedBaseLayerReader, err := layer.WindowsBaseLayer() 658 h.AssertNil(t, err) 659 660 // layer: application/vnd.docker.image.rootfs.diff.tar.gzip 661 expectedBaseLayerSHA, err := computeLayerSHA(ioutil.NopCloser(expectedBaseLayerReader)) 662 h.AssertNil(t, err) 663 h.AssertOnTarEntry(t, outputFile, 664 "/blobs/sha256/"+expectedBaseLayerSHA, 665 h.HasOwnerAndGroup(0, 0), 666 h.HasFileMode(0755), 667 h.IsGzipped(), 668 ) 669 670 bpReader, err := buildpack1.Open() 671 h.AssertNil(t, err) 672 defer bpReader.Close() 673 674 buildpackLayerSHA, err := computeLayerSHA(bpReader) 675 h.AssertNil(t, err) 676 h.AssertOnTarEntry(t, outputFile, 677 "/blobs/sha256/"+buildpackLayerSHA, 678 h.HasOwnerAndGroup(0, 0), 679 h.HasFileMode(0755), 680 h.IsGzipped(), 681 ) 682 }) 683 }) 684 } 685 686 func computeLayerSHA(reader io.ReadCloser) (string, error) { 687 bpLayer := stream.NewLayer(reader, stream.WithCompressionLevel(gzip.DefaultCompression)) 688 compressed, err := bpLayer.Compressed() 689 if err != nil { 690 return "", err 691 } 692 defer compressed.Close() 693 694 if _, err := io.Copy(ioutil.Discard, compressed); err != nil { 695 return "", err 696 } 697 698 digest, err := bpLayer.Digest() 699 if err != nil { 700 return "", err 701 } 702 703 return digest.Hex, nil 704 }