github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/pkg/buildpack/builder_test.go (about)

     1  package buildpack_test
     2  
     3  import (
     4  	"archive/tar"
     5  	"bytes"
     6  	"compress/gzip"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"path"
    12  	"path/filepath"
    13  	"testing"
    14  
    15  	"github.com/pkg/errors"
    16  
    17  	"github.com/buildpacks/imgutil/fakes"
    18  	"github.com/buildpacks/imgutil/layer"
    19  	"github.com/buildpacks/lifecycle/api"
    20  	"github.com/golang/mock/gomock"
    21  	"github.com/google/go-containerregistry/pkg/v1/stream"
    22  	"github.com/heroku/color"
    23  	v1 "github.com/opencontainers/image-spec/specs-go/v1"
    24  	"github.com/sclevine/spec"
    25  	"github.com/sclevine/spec/report"
    26  
    27  	"github.com/buildpacks/pack/pkg/archive"
    28  	"github.com/buildpacks/pack/pkg/logging"
    29  
    30  	ifakes "github.com/buildpacks/pack/internal/fakes"
    31  	"github.com/buildpacks/pack/pkg/buildpack"
    32  	"github.com/buildpacks/pack/pkg/dist"
    33  	"github.com/buildpacks/pack/pkg/testmocks"
    34  	h "github.com/buildpacks/pack/testhelpers"
    35  )
    36  
    37  func TestPackageBuilder(t *testing.T) {
    38  	color.Disable(true)
    39  	defer color.Disable(false)
    40  	spec.Run(t, "PackageBuilder", testPackageBuilder, spec.Parallel(), spec.Report(report.Terminal{}))
    41  }
    42  
    43  func testPackageBuilder(t *testing.T, when spec.G, it spec.S) {
    44  	var (
    45  		mockController   *gomock.Controller
    46  		mockImageFactory func(expectedImageOS string) *testmocks.MockImageFactory
    47  		tmpDir           string
    48  	)
    49  
    50  	it.Before(func() {
    51  		mockController = gomock.NewController(t)
    52  
    53  		mockImageFactory = func(expectedImageOS string) *testmocks.MockImageFactory {
    54  			imageFactory := testmocks.NewMockImageFactory(mockController)
    55  
    56  			if expectedImageOS != "" {
    57  				fakePackageImage := fakes.NewImage("some/package", "", nil)
    58  				imageFactory.EXPECT().NewImage("some/package", true, expectedImageOS).Return(fakePackageImage, nil).MaxTimes(1)
    59  			}
    60  
    61  			return imageFactory
    62  		}
    63  
    64  		var err error
    65  		tmpDir, err = os.MkdirTemp("", "package_builder_tests")
    66  		h.AssertNil(t, err)
    67  	})
    68  
    69  	it.After(func() {
    70  		h.AssertNilE(t, os.RemoveAll(tmpDir))
    71  		mockController.Finish()
    72  	})
    73  
    74  	when("validation", func() {
    75  		for _, _test := range []*struct {
    76  			name            string
    77  			expectedImageOS string
    78  			fn              func(*buildpack.PackageBuilder) error
    79  		}{
    80  			{name: "SaveAsImage", expectedImageOS: "linux", fn: func(builder *buildpack.PackageBuilder) error {
    81  				_, err := builder.SaveAsImage("some/package", false, "linux", map[string]string{})
    82  				return err
    83  			}},
    84  			{name: "SaveAsImage", expectedImageOS: "windows", fn: func(builder *buildpack.PackageBuilder) error {
    85  				_, err := builder.SaveAsImage("some/package", false, "windows", map[string]string{})
    86  				return err
    87  			}},
    88  			{name: "SaveAsFile", expectedImageOS: "linux", fn: func(builder *buildpack.PackageBuilder) error {
    89  				return builder.SaveAsFile(path.Join(tmpDir, "package.cnb"), "linux", map[string]string{})
    90  			}},
    91  			{name: "SaveAsFile", expectedImageOS: "windows", fn: func(builder *buildpack.PackageBuilder) error {
    92  				return builder.SaveAsFile(path.Join(tmpDir, "package.cnb"), "windows", map[string]string{})
    93  			}},
    94  		} {
    95  			// always use copies to avoid stale refs
    96  			testFn := _test.fn
    97  			expectedImageOS := _test.expectedImageOS
    98  			testName := _test.name
    99  			_test = nil
   100  
   101  			when(testName, func() {
   102  				when(expectedImageOS, func() {
   103  					when("validate buildpack", func() {
   104  						when("buildpack not set", func() {
   105  							it("returns error", func() {
   106  								builder := buildpack.NewBuilder(mockImageFactory(expectedImageOS))
   107  								err := testFn(builder)
   108  								h.AssertError(t, err, "buildpack or extension must be set")
   109  							})
   110  						})
   111  
   112  						when("there is a buildpack not referenced", func() {
   113  							it("should error", func() {
   114  								bp1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   115  									WithAPI: api.MustParse("0.2"),
   116  									WithInfo: dist.ModuleInfo{
   117  										ID:      "bp.1.id",
   118  										Version: "bp.1.version",
   119  									},
   120  									WithStacks: []dist.Stack{{ID: "some.stack"}},
   121  								}, 0644)
   122  								h.AssertNil(t, err)
   123  
   124  								builder := buildpack.NewBuilder(mockImageFactory(expectedImageOS))
   125  								builder.SetBuildpack(bp1)
   126  
   127  								bp2, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   128  									WithAPI:    api.MustParse("0.2"),
   129  									WithInfo:   dist.ModuleInfo{ID: "bp.2.id", Version: "bp.2.version"},
   130  									WithStacks: []dist.Stack{{ID: "some.stack"}},
   131  									WithOrder:  nil,
   132  								}, 0644)
   133  								h.AssertNil(t, err)
   134  								builder.AddDependency(bp2)
   135  
   136  								err = testFn(builder)
   137  								h.AssertError(t, err, "buildpack 'bp.2.id@bp.2.version' is not used by buildpack 'bp.1.id@bp.1.version'")
   138  							})
   139  						})
   140  
   141  						when("there is a referenced buildpack from main buildpack that is not present", func() {
   142  							it("should error", func() {
   143  								mainBP, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   144  									WithAPI: api.MustParse("0.2"),
   145  									WithInfo: dist.ModuleInfo{
   146  										ID:      "bp.1.id",
   147  										Version: "bp.1.version",
   148  									},
   149  									WithOrder: dist.Order{{
   150  										Group: []dist.ModuleRef{
   151  											{ModuleInfo: dist.ModuleInfo{ID: "bp.present.id", Version: "bp.present.version"}},
   152  											{ModuleInfo: dist.ModuleInfo{ID: "bp.missing.id", Version: "bp.missing.version"}},
   153  										},
   154  									}},
   155  								}, 0644)
   156  								h.AssertNil(t, err)
   157  
   158  								builder := buildpack.NewBuilder(mockImageFactory(expectedImageOS))
   159  								builder.SetBuildpack(mainBP)
   160  
   161  								presentBP, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   162  									WithAPI:    api.MustParse("0.2"),
   163  									WithInfo:   dist.ModuleInfo{ID: "bp.present.id", Version: "bp.present.version"},
   164  									WithStacks: []dist.Stack{{ID: "some.stack"}},
   165  									WithOrder:  nil,
   166  								}, 0644)
   167  								h.AssertNil(t, err)
   168  								builder.AddDependency(presentBP)
   169  
   170  								err = testFn(builder)
   171  								h.AssertError(t, err, "buildpack 'bp.1.id@bp.1.version' references buildpack 'bp.missing.id@bp.missing.version' which is not present")
   172  							})
   173  						})
   174  
   175  						when("there is a referenced buildpack from dependency buildpack that is not present", func() {
   176  							it("should error", func() {
   177  								mainBP, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   178  									WithAPI: api.MustParse("0.2"),
   179  									WithInfo: dist.ModuleInfo{
   180  										ID:      "bp.1.id",
   181  										Version: "bp.1.version",
   182  									},
   183  									WithOrder: dist.Order{{
   184  										Group: []dist.ModuleRef{
   185  											{ModuleInfo: dist.ModuleInfo{ID: "bp.present.id", Version: "bp.present.version"}},
   186  										},
   187  									}},
   188  								}, 0644)
   189  								h.AssertNil(t, err)
   190  								builder := buildpack.NewBuilder(mockImageFactory(expectedImageOS))
   191  								builder.SetBuildpack(mainBP)
   192  
   193  								presentBP, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   194  									WithAPI:  api.MustParse("0.2"),
   195  									WithInfo: dist.ModuleInfo{ID: "bp.present.id", Version: "bp.present.version"},
   196  									WithOrder: dist.Order{{
   197  										Group: []dist.ModuleRef{
   198  											{ModuleInfo: dist.ModuleInfo{ID: "bp.missing.id", Version: "bp.missing.version"}},
   199  										},
   200  									}},
   201  								}, 0644)
   202  								h.AssertNil(t, err)
   203  								builder.AddDependency(presentBP)
   204  
   205  								err = testFn(builder)
   206  								h.AssertError(t, err, "buildpack 'bp.present.id@bp.present.version' references buildpack 'bp.missing.id@bp.missing.version' which is not present")
   207  							})
   208  						})
   209  
   210  						when("there is a referenced buildpack from dependency buildpack that does not have proper version defined", func() {
   211  							it("should error", func() {
   212  								mainBP, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   213  									WithAPI: api.MustParse("0.2"),
   214  									WithInfo: dist.ModuleInfo{
   215  										ID:      "bp.1.id",
   216  										Version: "bp.1.version",
   217  									},
   218  									WithOrder: dist.Order{{
   219  										Group: []dist.ModuleRef{
   220  											{ModuleInfo: dist.ModuleInfo{ID: "bp.present.id", Version: "bp.present.version"}},
   221  										},
   222  									}},
   223  								}, 0644)
   224  								h.AssertNil(t, err)
   225  								builder := buildpack.NewBuilder(mockImageFactory(expectedImageOS))
   226  								builder.SetBuildpack(mainBP)
   227  
   228  								presentBP, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   229  									WithAPI:  api.MustParse("0.2"),
   230  									WithInfo: dist.ModuleInfo{ID: "bp.present.id", Version: "bp.present.version"},
   231  									WithOrder: dist.Order{{
   232  										Group: []dist.ModuleRef{
   233  											{ModuleInfo: dist.ModuleInfo{ID: "bp.missing.id"}},
   234  										},
   235  									}},
   236  								}, 0644)
   237  								h.AssertNil(t, err)
   238  								builder.AddDependency(presentBP)
   239  
   240  								err = testFn(builder)
   241  								h.AssertError(t, err, "buildpack 'bp.present.id@bp.present.version' must specify a version when referencing buildpack 'bp.missing.id'")
   242  							})
   243  						})
   244  					})
   245  
   246  					when("validate stacks", func() {
   247  						when("buildpack does not define stacks", func() {
   248  							it("should succeed", func() {
   249  								bp, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   250  									WithAPI: api.MustParse("0.10"),
   251  									WithInfo: dist.ModuleInfo{
   252  										ID:      "bp.1.id",
   253  										Version: "bp.1.version",
   254  									},
   255  									WithStacks: nil,
   256  									WithOrder:  nil,
   257  								}, 0644)
   258  								h.AssertNil(t, err)
   259  								builder := buildpack.NewBuilder(mockImageFactory(expectedImageOS))
   260  								builder.SetBuildpack(bp)
   261  								err = testFn(builder)
   262  								h.AssertNil(t, err)
   263  							})
   264  						})
   265  
   266  						when("buildpack is meta-buildpack", func() {
   267  							it("should succeed", func() {
   268  								bp, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   269  									WithAPI: api.MustParse("0.2"),
   270  									WithInfo: dist.ModuleInfo{
   271  										ID:      "bp.1.id",
   272  										Version: "bp.1.version",
   273  									},
   274  									WithStacks: nil,
   275  									WithOrder: dist.Order{{
   276  										Group: []dist.ModuleRef{
   277  											{ModuleInfo: dist.ModuleInfo{ID: "bp.nested.id", Version: "bp.nested.version"}},
   278  										},
   279  									}},
   280  								}, 0644)
   281  								h.AssertNil(t, err)
   282  
   283  								builder := buildpack.NewBuilder(mockImageFactory(expectedImageOS))
   284  								builder.SetBuildpack(bp)
   285  
   286  								dependency, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   287  									WithAPI: api.MustParse("0.2"),
   288  									WithInfo: dist.ModuleInfo{
   289  										ID:      "bp.nested.id",
   290  										Version: "bp.nested.version",
   291  									},
   292  									WithStacks: []dist.Stack{
   293  										{ID: "stack.id.1", Mixins: []string{"Mixin-A"}},
   294  									},
   295  									WithOrder: nil,
   296  								}, 0644)
   297  								h.AssertNil(t, err)
   298  
   299  								builder.AddDependency(dependency)
   300  
   301  								err = testFn(builder)
   302  								h.AssertNil(t, err)
   303  							})
   304  						})
   305  
   306  						when("dependencies don't have a common stack", func() {
   307  							it("should error", func() {
   308  								bp, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   309  									WithAPI: api.MustParse("0.2"),
   310  									WithInfo: dist.ModuleInfo{
   311  										ID:      "bp.1.id",
   312  										Version: "bp.1.version",
   313  									},
   314  									WithOrder: dist.Order{{
   315  										Group: []dist.ModuleRef{{
   316  											ModuleInfo: dist.ModuleInfo{ID: "bp.2.id", Version: "bp.2.version"},
   317  											Optional:   false,
   318  										}, {
   319  											ModuleInfo: dist.ModuleInfo{ID: "bp.3.id", Version: "bp.3.version"},
   320  											Optional:   false,
   321  										}},
   322  									}},
   323  								}, 0644)
   324  								h.AssertNil(t, err)
   325  
   326  								builder := buildpack.NewBuilder(mockImageFactory(expectedImageOS))
   327  								builder.SetBuildpack(bp)
   328  
   329  								dependency1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   330  									WithAPI: api.MustParse("0.2"),
   331  									WithInfo: dist.ModuleInfo{
   332  										ID:      "bp.2.id",
   333  										Version: "bp.2.version",
   334  									},
   335  									WithStacks: []dist.Stack{
   336  										{ID: "stack.id.1", Mixins: []string{"Mixin-A"}},
   337  										{ID: "stack.id.2", Mixins: []string{"Mixin-A"}},
   338  									},
   339  									WithOrder: nil,
   340  								}, 0644)
   341  								h.AssertNil(t, err)
   342  								builder.AddDependency(dependency1)
   343  
   344  								dependency2, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   345  									WithAPI: api.MustParse("0.2"),
   346  									WithInfo: dist.ModuleInfo{
   347  										ID:      "bp.3.id",
   348  										Version: "bp.3.version",
   349  									},
   350  									WithStacks: []dist.Stack{
   351  										{ID: "stack.id.3", Mixins: []string{"Mixin-A"}},
   352  									},
   353  									WithOrder: nil,
   354  								}, 0644)
   355  								h.AssertNil(t, err)
   356  								builder.AddDependency(dependency2)
   357  
   358  								err = testFn(builder)
   359  								h.AssertError(t, err, "no compatible stacks among provided buildpacks")
   360  							})
   361  						})
   362  
   363  						when("dependency has stacks that aren't supported by buildpack", func() {
   364  							it("should only support common stacks", func() {
   365  								bp, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   366  									WithAPI: api.MustParse("0.2"),
   367  									WithInfo: dist.ModuleInfo{
   368  										ID:      "bp.1.id",
   369  										Version: "bp.1.version",
   370  									},
   371  									WithOrder: dist.Order{{
   372  										Group: []dist.ModuleRef{{
   373  											ModuleInfo: dist.ModuleInfo{ID: "bp.2.id", Version: "bp.2.version"},
   374  											Optional:   false,
   375  										}, {
   376  											ModuleInfo: dist.ModuleInfo{ID: "bp.3.id", Version: "bp.3.version"},
   377  											Optional:   false,
   378  										}},
   379  									}},
   380  								}, 0644)
   381  								h.AssertNil(t, err)
   382  
   383  								builder := buildpack.NewBuilder(mockImageFactory(expectedImageOS))
   384  								builder.SetBuildpack(bp)
   385  
   386  								dependency1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   387  									WithAPI: api.MustParse("0.2"),
   388  									WithInfo: dist.ModuleInfo{
   389  										ID:      "bp.2.id",
   390  										Version: "bp.2.version",
   391  									},
   392  									WithStacks: []dist.Stack{
   393  										{ID: "stack.id.1", Mixins: []string{"Mixin-A"}},
   394  										{ID: "stack.id.2", Mixins: []string{"Mixin-A"}},
   395  									},
   396  									WithOrder: nil,
   397  								}, 0644)
   398  								h.AssertNil(t, err)
   399  								builder.AddDependency(dependency1)
   400  
   401  								dependency2, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   402  									WithAPI: api.MustParse("0.2"),
   403  									WithInfo: dist.ModuleInfo{
   404  										ID:      "bp.3.id",
   405  										Version: "bp.3.version",
   406  									},
   407  									WithStacks: []dist.Stack{
   408  										{ID: "stack.id.1", Mixins: []string{"Mixin-A"}},
   409  									},
   410  									WithOrder: nil,
   411  								}, 0644)
   412  								h.AssertNil(t, err)
   413  								builder.AddDependency(dependency2)
   414  
   415  								img, err := builder.SaveAsImage("some/package", false, expectedImageOS, map[string]string{})
   416  								h.AssertNil(t, err)
   417  
   418  								metadata := buildpack.Metadata{}
   419  								_, err = dist.GetLabel(img, "io.buildpacks.buildpackage.metadata", &metadata)
   420  								h.AssertNil(t, err)
   421  
   422  								h.AssertEq(t, metadata.Stacks, []dist.Stack{{ID: "stack.id.1", Mixins: []string{"Mixin-A"}}})
   423  							})
   424  						})
   425  
   426  						when("dependency has wildcard stacks", func() {
   427  							it("should support all the possible stacks", func() {
   428  								bp, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   429  									WithAPI: api.MustParse("0.2"),
   430  									WithInfo: dist.ModuleInfo{
   431  										ID:      "bp.1.id",
   432  										Version: "bp.1.version",
   433  									},
   434  									WithOrder: dist.Order{{
   435  										Group: []dist.ModuleRef{{
   436  											ModuleInfo: dist.ModuleInfo{ID: "bp.2.id", Version: "bp.2.version"},
   437  											Optional:   false,
   438  										}, {
   439  											ModuleInfo: dist.ModuleInfo{ID: "bp.3.id", Version: "bp.3.version"},
   440  											Optional:   false,
   441  										}},
   442  									}},
   443  								}, 0644)
   444  								h.AssertNil(t, err)
   445  
   446  								builder := buildpack.NewBuilder(mockImageFactory(expectedImageOS))
   447  								builder.SetBuildpack(bp)
   448  
   449  								dependency1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   450  									WithAPI: api.MustParse("0.2"),
   451  									WithInfo: dist.ModuleInfo{
   452  										ID:      "bp.2.id",
   453  										Version: "bp.2.version",
   454  									},
   455  									WithStacks: []dist.Stack{
   456  										{ID: "*", Mixins: []string{"Mixin-A"}},
   457  									},
   458  									WithOrder: nil,
   459  								}, 0644)
   460  								h.AssertNil(t, err)
   461  								builder.AddDependency(dependency1)
   462  
   463  								dependency2, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   464  									WithAPI: api.MustParse("0.2"),
   465  									WithInfo: dist.ModuleInfo{
   466  										ID:      "bp.3.id",
   467  										Version: "bp.3.version",
   468  									},
   469  									WithStacks: []dist.Stack{
   470  										{ID: "stack.id.1", Mixins: []string{"Mixin-A"}},
   471  									},
   472  									WithOrder: nil,
   473  								}, 0644)
   474  								h.AssertNil(t, err)
   475  								builder.AddDependency(dependency2)
   476  
   477  								img, err := builder.SaveAsImage("some/package", false, expectedImageOS, map[string]string{})
   478  								h.AssertNil(t, err)
   479  
   480  								metadata := buildpack.Metadata{}
   481  								_, err = dist.GetLabel(img, "io.buildpacks.buildpackage.metadata", &metadata)
   482  								h.AssertNil(t, err)
   483  
   484  								h.AssertEq(t, metadata.Stacks, []dist.Stack{{ID: "stack.id.1", Mixins: []string{"Mixin-A"}}})
   485  							})
   486  						})
   487  
   488  						when("dependency is meta-buildpack", func() {
   489  							it("should succeed and compute common stacks", func() {
   490  								bp, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   491  									WithAPI: api.MustParse("0.2"),
   492  									WithInfo: dist.ModuleInfo{
   493  										ID:      "bp.1.id",
   494  										Version: "bp.1.version",
   495  									},
   496  									WithStacks: nil,
   497  									WithOrder: dist.Order{{
   498  										Group: []dist.ModuleRef{
   499  											{ModuleInfo: dist.ModuleInfo{ID: "bp.nested.id", Version: "bp.nested.version"}},
   500  										},
   501  									}},
   502  								}, 0644)
   503  								h.AssertNil(t, err)
   504  
   505  								builder := buildpack.NewBuilder(mockImageFactory(expectedImageOS))
   506  								builder.SetBuildpack(bp)
   507  
   508  								dependencyOrder, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   509  									WithAPI: api.MustParse("0.2"),
   510  									WithInfo: dist.ModuleInfo{
   511  										ID:      "bp.nested.id",
   512  										Version: "bp.nested.version",
   513  									},
   514  									WithOrder: dist.Order{{
   515  										Group: []dist.ModuleRef{
   516  											{ModuleInfo: dist.ModuleInfo{
   517  												ID:      "bp.nested.nested.id",
   518  												Version: "bp.nested.nested.version",
   519  											}},
   520  										},
   521  									}},
   522  								}, 0644)
   523  								h.AssertNil(t, err)
   524  
   525  								builder.AddDependency(dependencyOrder)
   526  
   527  								dependencyNestedNested, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   528  									WithAPI: api.MustParse("0.2"),
   529  									WithInfo: dist.ModuleInfo{
   530  										ID:      "bp.nested.nested.id",
   531  										Version: "bp.nested.nested.version",
   532  									},
   533  									WithStacks: []dist.Stack{
   534  										{ID: "stack.id.1", Mixins: []string{"Mixin-A"}},
   535  									},
   536  									WithOrder: nil,
   537  								}, 0644)
   538  								h.AssertNil(t, err)
   539  
   540  								builder.AddDependency(dependencyNestedNested)
   541  
   542  								img, err := builder.SaveAsImage("some/package", false, expectedImageOS, map[string]string{})
   543  								h.AssertNil(t, err)
   544  
   545  								metadata := buildpack.Metadata{}
   546  								_, err = dist.GetLabel(img, "io.buildpacks.buildpackage.metadata", &metadata)
   547  								h.AssertNil(t, err)
   548  
   549  								h.AssertEq(t, metadata.Stacks, []dist.Stack{{ID: "stack.id.1", Mixins: []string{"Mixin-A"}}})
   550  							})
   551  						})
   552  					})
   553  				})
   554  			})
   555  		}
   556  	})
   557  
   558  	when("#SaveAsImage", func() {
   559  		it("sets metadata", func() {
   560  			buildpack1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   561  				WithAPI: api.MustParse("0.2"),
   562  				WithInfo: dist.ModuleInfo{
   563  					ID:          "bp.1.id",
   564  					Version:     "bp.1.version",
   565  					Name:        "One",
   566  					Description: "some description",
   567  					Homepage:    "https://example.com/homepage",
   568  					Keywords:    []string{"some-keyword"},
   569  					Licenses: []dist.License{
   570  						{
   571  							Type: "MIT",
   572  							URI:  "https://example.com/license",
   573  						},
   574  					},
   575  				},
   576  				WithStacks: []dist.Stack{
   577  					{ID: "stack.id.1"},
   578  					{ID: "stack.id.2"},
   579  				},
   580  				WithOrder: nil,
   581  			}, 0644)
   582  			h.AssertNil(t, err)
   583  
   584  			builder := buildpack.NewBuilder(mockImageFactory("linux"))
   585  			builder.SetBuildpack(buildpack1)
   586  
   587  			var customLabels = map[string]string{"test.label.one": "1", "test.label.two": "2"}
   588  
   589  			packageImage, err := builder.SaveAsImage("some/package", false, "linux", customLabels)
   590  			h.AssertNil(t, err)
   591  
   592  			labelData, err := packageImage.Label("io.buildpacks.buildpackage.metadata")
   593  			h.AssertNil(t, err)
   594  			var md buildpack.Metadata
   595  			h.AssertNil(t, json.Unmarshal([]byte(labelData), &md))
   596  
   597  			h.AssertEq(t, md.ID, "bp.1.id")
   598  			h.AssertEq(t, md.Version, "bp.1.version")
   599  			h.AssertEq(t, len(md.Stacks), 2)
   600  			h.AssertEq(t, md.Stacks[0].ID, "stack.id.1")
   601  			h.AssertEq(t, md.Stacks[1].ID, "stack.id.2")
   602  			h.AssertEq(t, md.Keywords[0], "some-keyword")
   603  			h.AssertEq(t, md.Homepage, "https://example.com/homepage")
   604  			h.AssertEq(t, md.Name, "One")
   605  			h.AssertEq(t, md.Description, "some description")
   606  			h.AssertEq(t, md.Licenses[0].Type, "MIT")
   607  			h.AssertEq(t, md.Licenses[0].URI, "https://example.com/license")
   608  
   609  			osVal, err := packageImage.OS()
   610  			h.AssertNil(t, err)
   611  			h.AssertEq(t, osVal, "linux")
   612  
   613  			imageLabels, err := packageImage.Labels()
   614  			h.AssertNil(t, err)
   615  			h.AssertEq(t, imageLabels["test.label.one"], "1")
   616  			h.AssertEq(t, imageLabels["test.label.two"], "2")
   617  		})
   618  
   619  		it("sets extension metadata", func() {
   620  			extension1, err := ifakes.NewFakeExtension(dist.ExtensionDescriptor{
   621  				WithAPI: api.MustParse("0.2"),
   622  				WithInfo: dist.ModuleInfo{
   623  					ID:          "ex.1.id",
   624  					Version:     "ex.1.version",
   625  					Name:        "One",
   626  					Description: "some description",
   627  					Homepage:    "https://example.com/homepage",
   628  					Keywords:    []string{"some-keyword"},
   629  					Licenses: []dist.License{
   630  						{
   631  							Type: "MIT",
   632  							URI:  "https://example.com/license",
   633  						},
   634  					},
   635  				},
   636  			}, 0644)
   637  			h.AssertNil(t, err)
   638  			builder := buildpack.NewBuilder(mockImageFactory("linux"))
   639  			builder.SetExtension(extension1)
   640  			packageImage, err := builder.SaveAsImage("some/package", false, "linux", map[string]string{})
   641  			h.AssertNil(t, err)
   642  			labelData, err := packageImage.Label("io.buildpacks.buildpackage.metadata")
   643  			h.AssertNil(t, err)
   644  			var md buildpack.Metadata
   645  			h.AssertNil(t, json.Unmarshal([]byte(labelData), &md))
   646  
   647  			h.AssertEq(t, md.ID, "ex.1.id")
   648  			h.AssertEq(t, md.Version, "ex.1.version")
   649  			h.AssertEq(t, md.Keywords[0], "some-keyword")
   650  			h.AssertEq(t, md.Homepage, "https://example.com/homepage")
   651  			h.AssertEq(t, md.Name, "One")
   652  			h.AssertEq(t, md.Description, "some description")
   653  			h.AssertEq(t, md.Licenses[0].Type, "MIT")
   654  			h.AssertEq(t, md.Licenses[0].URI, "https://example.com/license")
   655  
   656  			osVal, err := packageImage.OS()
   657  			h.AssertNil(t, err)
   658  			h.AssertEq(t, osVal, "linux")
   659  		})
   660  
   661  		it("sets buildpack layers label", func() {
   662  			buildpack1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   663  				WithAPI:    api.MustParse("0.2"),
   664  				WithInfo:   dist.ModuleInfo{ID: "bp.1.id", Version: "bp.1.version"},
   665  				WithStacks: []dist.Stack{{ID: "stack.id.1"}, {ID: "stack.id.2"}},
   666  				WithOrder:  nil,
   667  			}, 0644)
   668  			h.AssertNil(t, err)
   669  
   670  			builder := buildpack.NewBuilder(mockImageFactory("linux"))
   671  			builder.SetBuildpack(buildpack1)
   672  
   673  			packageImage, err := builder.SaveAsImage("some/package", false, "linux", map[string]string{})
   674  			h.AssertNil(t, err)
   675  
   676  			var bpLayers dist.ModuleLayers
   677  			_, err = dist.GetLabel(packageImage, "io.buildpacks.buildpack.layers", &bpLayers)
   678  			h.AssertNil(t, err)
   679  
   680  			bp1Info, ok1 := bpLayers["bp.1.id"]["bp.1.version"]
   681  			h.AssertEq(t, ok1, true)
   682  			h.AssertEq(t, bp1Info.Stacks, []dist.Stack{{ID: "stack.id.1"}, {ID: "stack.id.2"}})
   683  		})
   684  
   685  		it("adds buildpack layers for linux", func() {
   686  			buildpack1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   687  				WithAPI:    api.MustParse("0.2"),
   688  				WithInfo:   dist.ModuleInfo{ID: "bp.1.id", Version: "bp.1.version"},
   689  				WithStacks: []dist.Stack{{ID: "stack.id.1"}, {ID: "stack.id.2"}},
   690  				WithOrder:  nil,
   691  			}, 0644)
   692  			h.AssertNil(t, err)
   693  
   694  			builder := buildpack.NewBuilder(mockImageFactory("linux"))
   695  			builder.SetBuildpack(buildpack1)
   696  
   697  			packageImage, err := builder.SaveAsImage("some/package", false, "linux", map[string]string{})
   698  			h.AssertNil(t, err)
   699  
   700  			buildpackExists := func(name, version string) {
   701  				t.Helper()
   702  				dirPath := fmt.Sprintf("/cnb/buildpacks/%s/%s", name, version)
   703  				fakePackageImage := packageImage.(*fakes.Image)
   704  				layerTar, err := fakePackageImage.FindLayerWithPath(dirPath)
   705  				h.AssertNil(t, err)
   706  
   707  				h.AssertOnTarEntry(t, layerTar, dirPath,
   708  					h.IsDirectory(),
   709  				)
   710  
   711  				h.AssertOnTarEntry(t, layerTar, dirPath+"/bin/build",
   712  					h.ContentEquals("build-contents"),
   713  					h.HasOwnerAndGroup(0, 0),
   714  					h.HasFileMode(0644),
   715  				)
   716  
   717  				h.AssertOnTarEntry(t, layerTar, dirPath+"/bin/detect",
   718  					h.ContentEquals("detect-contents"),
   719  					h.HasOwnerAndGroup(0, 0),
   720  					h.HasFileMode(0644),
   721  				)
   722  			}
   723  
   724  			buildpackExists("bp.1.id", "bp.1.version")
   725  
   726  			fakePackageImage := packageImage.(*fakes.Image)
   727  			osVal, err := fakePackageImage.OS()
   728  			h.AssertNil(t, err)
   729  			h.AssertEq(t, osVal, "linux")
   730  		})
   731  
   732  		it("adds baselayer + buildpack layers for windows", func() {
   733  			buildpack1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   734  				WithAPI:    api.MustParse("0.2"),
   735  				WithInfo:   dist.ModuleInfo{ID: "bp.1.id", Version: "bp.1.version"},
   736  				WithStacks: []dist.Stack{{ID: "stack.id.1"}, {ID: "stack.id.2"}},
   737  				WithOrder:  nil,
   738  			}, 0644)
   739  			h.AssertNil(t, err)
   740  
   741  			builder := buildpack.NewBuilder(mockImageFactory("windows"))
   742  			builder.SetBuildpack(buildpack1)
   743  
   744  			_, err = builder.SaveAsImage("some/package", false, "windows", map[string]string{})
   745  			h.AssertNil(t, err)
   746  		})
   747  
   748  		it("should report an error when custom label cannot be set", func() {
   749  			mockImageFactory = func(expectedImageOS string) *testmocks.MockImageFactory {
   750  				var imageWithLabelError = &imageWithLabelError{Image: fakes.NewImage("some/package", "", nil)}
   751  				imageFactory := testmocks.NewMockImageFactory(mockController)
   752  				imageFactory.EXPECT().NewImage("some/package", true, expectedImageOS).Return(imageWithLabelError, nil).MaxTimes(1)
   753  				return imageFactory
   754  			}
   755  
   756  			buildpack1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   757  				WithAPI: api.MustParse("0.2"),
   758  				WithInfo: dist.ModuleInfo{
   759  					ID:          "bp.1.id",
   760  					Version:     "bp.1.version",
   761  					Name:        "One",
   762  					Description: "some description",
   763  					Homepage:    "https://example.com/homepage",
   764  					Keywords:    []string{"some-keyword"},
   765  					Licenses: []dist.License{
   766  						{
   767  							Type: "MIT",
   768  							URI:  "https://example.com/license",
   769  						},
   770  					},
   771  				},
   772  				WithStacks: []dist.Stack{
   773  					{ID: "stack.id.1"},
   774  					{ID: "stack.id.2"},
   775  				},
   776  				WithOrder: nil,
   777  			}, 0644)
   778  			h.AssertNil(t, err)
   779  
   780  			builder := buildpack.NewBuilder(mockImageFactory("linux"))
   781  			builder.SetBuildpack(buildpack1)
   782  
   783  			var customLabels = map[string]string{"test.label.fail": "true"}
   784  
   785  			_, err = builder.SaveAsImage("some/package", false, "linux", customLabels)
   786  			h.AssertError(t, err, "adding label test.label.fail=true")
   787  		})
   788  
   789  		when("flatten is set", func() {
   790  			var (
   791  				buildpack1   buildpack.BuildModule
   792  				bp1          buildpack.BuildModule
   793  				compositeBP2 buildpack.BuildModule
   794  				bp21         buildpack.BuildModule
   795  				bp22         buildpack.BuildModule
   796  				compositeBP3 buildpack.BuildModule
   797  				bp31         buildpack.BuildModule
   798  				logger       logging.Logger
   799  				outBuf       bytes.Buffer
   800  				err          error
   801  			)
   802  			it.Before(func() {
   803  				bp1, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   804  					WithAPI: api.MustParse("0.2"),
   805  					WithInfo: dist.ModuleInfo{
   806  						ID:      "buildpack-1-id",
   807  						Version: "buildpack-1-version",
   808  					},
   809  				}, 0644)
   810  				h.AssertNil(t, err)
   811  
   812  				bp21, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   813  					WithAPI: api.MustParse("0.2"),
   814  					WithInfo: dist.ModuleInfo{
   815  						ID:      "buildpack-21-id",
   816  						Version: "buildpack-21-version",
   817  					},
   818  				}, 0644)
   819  				h.AssertNil(t, err)
   820  
   821  				bp22, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   822  					WithAPI: api.MustParse("0.2"),
   823  					WithInfo: dist.ModuleInfo{
   824  						ID:      "buildpack-22-id",
   825  						Version: "buildpack-22-version",
   826  					},
   827  				}, 0644)
   828  				h.AssertNil(t, err)
   829  
   830  				bp31, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   831  					WithAPI: api.MustParse("0.2"),
   832  					WithInfo: dist.ModuleInfo{
   833  						ID:      "buildpack-31-id",
   834  						Version: "buildpack-31-version",
   835  					},
   836  				}, 0644)
   837  				h.AssertNil(t, err)
   838  
   839  				compositeBP3, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   840  					WithAPI: api.MustParse("0.2"),
   841  					WithInfo: dist.ModuleInfo{
   842  						ID:      "composite-buildpack-3-id",
   843  						Version: "composite-buildpack-3-version",
   844  					},
   845  					WithOrder: []dist.OrderEntry{{
   846  						Group: []dist.ModuleRef{
   847  							{
   848  								ModuleInfo: bp31.Descriptor().Info(),
   849  							},
   850  						},
   851  					}},
   852  				}, 0644)
   853  				h.AssertNil(t, err)
   854  
   855  				compositeBP2, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   856  					WithAPI: api.MustParse("0.2"),
   857  					WithInfo: dist.ModuleInfo{
   858  						ID:      "composite-buildpack-2-id",
   859  						Version: "composite-buildpack-2-version",
   860  					},
   861  					WithOrder: []dist.OrderEntry{{
   862  						Group: []dist.ModuleRef{
   863  							{
   864  								ModuleInfo: bp21.Descriptor().Info(),
   865  							},
   866  							{
   867  								ModuleInfo: bp22.Descriptor().Info(),
   868  							},
   869  							{
   870  								ModuleInfo: compositeBP3.Descriptor().Info(),
   871  							},
   872  						},
   873  					}},
   874  				}, 0644)
   875  				h.AssertNil(t, err)
   876  
   877  				buildpack1, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   878  					WithAPI:    api.MustParse("0.2"),
   879  					WithInfo:   dist.ModuleInfo{ID: "bp.1.id", Version: "bp.1.version"},
   880  					WithStacks: []dist.Stack{{ID: "stack.id.1"}, {ID: "stack.id.2"}},
   881  					WithOrder: []dist.OrderEntry{{
   882  						Group: []dist.ModuleRef{
   883  							{
   884  								ModuleInfo: bp1.Descriptor().Info(),
   885  							},
   886  							{
   887  								ModuleInfo: compositeBP2.Descriptor().Info(),
   888  							},
   889  						},
   890  					}},
   891  				}, 0644)
   892  				h.AssertNil(t, err)
   893  
   894  				logger = logging.NewLogWithWriters(&outBuf, &outBuf, logging.WithVerbose())
   895  			})
   896  
   897  			when("flatten all", func() {
   898  				var builder *buildpack.PackageBuilder
   899  
   900  				when("no exclusions", func() {
   901  					it.Before(func() {
   902  						builder = buildpack.NewBuilder(mockImageFactory("linux"),
   903  							buildpack.FlattenAll(),
   904  							buildpack.WithLogger(logger),
   905  							buildpack.WithLayerWriterFactory(archive.DefaultTarWriterFactory()))
   906  					})
   907  
   908  					it("flatten all buildpacks", func() {
   909  						builder.SetBuildpack(buildpack1)
   910  						builder.AddDependencies(bp1, nil)
   911  						builder.AddDependencies(compositeBP2, []buildpack.BuildModule{bp21, bp22, compositeBP3, bp31})
   912  
   913  						packageImage, err := builder.SaveAsImage("some/package", false, "linux", map[string]string{})
   914  						h.AssertNil(t, err)
   915  
   916  						fakePackageImage := packageImage.(*fakes.Image)
   917  						h.AssertEq(t, fakePackageImage.NumberOfAddedLayers(), 1)
   918  					})
   919  				})
   920  
   921  				when("exclude buildpacks", func() {
   922  					it.Before(func() {
   923  						excluded := []string{bp31.Descriptor().Info().FullName()}
   924  
   925  						builder = buildpack.NewBuilder(mockImageFactory("linux"),
   926  							buildpack.DoNotFlatten(excluded),
   927  							buildpack.WithLogger(logger),
   928  							buildpack.WithLayerWriterFactory(archive.DefaultTarWriterFactory()))
   929  					})
   930  
   931  					it("creates 2 layers", func() {
   932  						builder.SetBuildpack(buildpack1)
   933  						builder.AddDependencies(bp1, nil)
   934  						builder.AddDependencies(compositeBP2, []buildpack.BuildModule{bp21, bp22, compositeBP3, bp31})
   935  
   936  						packageImage, err := builder.SaveAsImage("some/package", false, "linux", map[string]string{})
   937  						h.AssertNil(t, err)
   938  
   939  						fakePackageImage := packageImage.(*fakes.Image)
   940  						h.AssertEq(t, fakePackageImage.NumberOfAddedLayers(), 2)
   941  					})
   942  				})
   943  			})
   944  		})
   945  	})
   946  
   947  	when("#SaveAsFile", func() {
   948  		it("sets metadata", func() {
   949  			buildpack1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   950  				WithAPI:    api.MustParse("0.2"),
   951  				WithInfo:   dist.ModuleInfo{ID: "bp.1.id", Version: "bp.1.version"},
   952  				WithStacks: []dist.Stack{{ID: "stack.id.1"}, {ID: "stack.id.2"}},
   953  				WithOrder:  nil,
   954  			}, 0644)
   955  			h.AssertNil(t, err)
   956  
   957  			builder := buildpack.NewBuilder(mockImageFactory(""))
   958  			builder.SetBuildpack(buildpack1)
   959  
   960  			var customLabels = map[string]string{"test.label.one": "1", "test.label.two": "2"}
   961  
   962  			outputFile := filepath.Join(tmpDir, fmt.Sprintf("package-%s.cnb", h.RandString(10)))
   963  			h.AssertNil(t, builder.SaveAsFile(outputFile, "linux", customLabels))
   964  
   965  			withContents := func(fn func(data []byte)) h.TarEntryAssertion {
   966  				return func(t *testing.T, header *tar.Header, data []byte) {
   967  					fn(data)
   968  				}
   969  			}
   970  
   971  			h.AssertOnTarEntry(t, outputFile, "/index.json",
   972  				h.HasOwnerAndGroup(0, 0),
   973  				h.HasFileMode(0755),
   974  				withContents(func(data []byte) {
   975  					index := v1.Index{}
   976  					err := json.Unmarshal(data, &index)
   977  					h.AssertNil(t, err)
   978  					h.AssertEq(t, len(index.Manifests), 1)
   979  
   980  					// manifest: application/vnd.docker.distribution.manifest.v2+json
   981  					h.AssertOnTarEntry(t, outputFile,
   982  						"/blobs/sha256/"+index.Manifests[0].Digest.Hex(),
   983  						h.HasOwnerAndGroup(0, 0),
   984  						h.IsJSON(),
   985  
   986  						withContents(func(data []byte) {
   987  							manifest := v1.Manifest{}
   988  							err := json.Unmarshal(data, &manifest)
   989  							h.AssertNil(t, err)
   990  
   991  							// config: application/vnd.docker.container.image.v1+json
   992  							h.AssertOnTarEntry(t, outputFile,
   993  								"/blobs/sha256/"+manifest.Config.Digest.Hex(),
   994  								h.HasOwnerAndGroup(0, 0),
   995  								h.IsJSON(),
   996  								// buildpackage metadata
   997  								h.ContentContains(`"io.buildpacks.buildpackage.metadata":"{\"id\":\"bp.1.id\",\"version\":\"bp.1.version\",\"stacks\":[{\"id\":\"stack.id.1\"},{\"id\":\"stack.id.2\"}]}"`),
   998  								// buildpack layers metadata
   999  								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:44447e95b06b73496d1891de5afb01936e9999b97ea03dad6337d9f5610807a7\"}}`),
  1000  								// image os
  1001  								h.ContentContains(`"os":"linux"`),
  1002  								// custom labels
  1003  								h.ContentContains(`"test.label.one":"1"`),
  1004  								h.ContentContains(`"test.label.two":"2"`),
  1005  							)
  1006  						}))
  1007  				}))
  1008  		})
  1009  
  1010  		it("adds buildpack layers", func() {
  1011  			buildpack1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
  1012  				WithAPI:    api.MustParse("0.2"),
  1013  				WithInfo:   dist.ModuleInfo{ID: "bp.1.id", Version: "bp.1.version"},
  1014  				WithStacks: []dist.Stack{{ID: "stack.id.1"}, {ID: "stack.id.2"}},
  1015  				WithOrder:  nil,
  1016  			}, 0644)
  1017  			h.AssertNil(t, err)
  1018  
  1019  			builder := buildpack.NewBuilder(mockImageFactory(""))
  1020  			builder.SetBuildpack(buildpack1)
  1021  
  1022  			outputFile := filepath.Join(tmpDir, fmt.Sprintf("package-%s.cnb", h.RandString(10)))
  1023  			h.AssertNil(t, builder.SaveAsFile(outputFile, "linux", map[string]string{}))
  1024  
  1025  			h.AssertOnTarEntry(t, outputFile, "/blobs",
  1026  				h.IsDirectory(),
  1027  				h.HasOwnerAndGroup(0, 0),
  1028  				h.HasFileMode(0755))
  1029  			h.AssertOnTarEntry(t, outputFile, "/blobs/sha256",
  1030  				h.IsDirectory(),
  1031  				h.HasOwnerAndGroup(0, 0),
  1032  				h.HasFileMode(0755))
  1033  
  1034  			bpReader, err := buildpack1.Open()
  1035  			h.AssertNil(t, err)
  1036  			defer bpReader.Close()
  1037  
  1038  			// layer: application/vnd.docker.image.rootfs.diff.tar.gzip
  1039  			buildpackLayerSHA, err := computeLayerSHA(bpReader)
  1040  			h.AssertNil(t, err)
  1041  			h.AssertOnTarEntry(t, outputFile,
  1042  				"/blobs/sha256/"+buildpackLayerSHA,
  1043  				h.HasOwnerAndGroup(0, 0),
  1044  				h.HasFileMode(0755),
  1045  				h.IsGzipped(),
  1046  				h.AssertOnNestedTar("/cnb/buildpacks/bp.1.id",
  1047  					h.IsDirectory(),
  1048  					h.HasOwnerAndGroup(0, 0),
  1049  					h.HasFileMode(0644)),
  1050  				h.AssertOnNestedTar("/cnb/buildpacks/bp.1.id/bp.1.version/bin/build",
  1051  					h.ContentEquals("build-contents"),
  1052  					h.HasOwnerAndGroup(0, 0),
  1053  					h.HasFileMode(0644)),
  1054  				h.AssertOnNestedTar("/cnb/buildpacks/bp.1.id/bp.1.version/bin/detect",
  1055  					h.ContentEquals("detect-contents"),
  1056  					h.HasOwnerAndGroup(0, 0),
  1057  					h.HasFileMode(0644)))
  1058  		})
  1059  
  1060  		it("adds baselayer + buildpack layers for windows", func() {
  1061  			buildpack1, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
  1062  				WithAPI:    api.MustParse("0.2"),
  1063  				WithInfo:   dist.ModuleInfo{ID: "bp.1.id", Version: "bp.1.version"},
  1064  				WithStacks: []dist.Stack{{ID: "stack.id.1"}, {ID: "stack.id.2"}},
  1065  				WithOrder:  nil,
  1066  			}, 0644)
  1067  			h.AssertNil(t, err)
  1068  
  1069  			builder := buildpack.NewBuilder(mockImageFactory(""))
  1070  			builder.SetBuildpack(buildpack1)
  1071  
  1072  			outputFile := filepath.Join(tmpDir, fmt.Sprintf("package-%s.cnb", h.RandString(10)))
  1073  			h.AssertNil(t, builder.SaveAsFile(outputFile, "windows", map[string]string{}))
  1074  
  1075  			// Windows baselayer content is constant
  1076  			expectedBaseLayerReader, err := layer.WindowsBaseLayer()
  1077  			h.AssertNil(t, err)
  1078  
  1079  			// layer: application/vnd.docker.image.rootfs.diff.tar.gzip
  1080  			expectedBaseLayerSHA, err := computeLayerSHA(io.NopCloser(expectedBaseLayerReader))
  1081  			h.AssertNil(t, err)
  1082  			h.AssertOnTarEntry(t, outputFile,
  1083  				"/blobs/sha256/"+expectedBaseLayerSHA,
  1084  				h.HasOwnerAndGroup(0, 0),
  1085  				h.HasFileMode(0755),
  1086  				h.IsGzipped(),
  1087  			)
  1088  
  1089  			bpReader, err := buildpack1.Open()
  1090  			h.AssertNil(t, err)
  1091  			defer bpReader.Close()
  1092  
  1093  			buildpackLayerSHA, err := computeLayerSHA(bpReader)
  1094  			h.AssertNil(t, err)
  1095  			h.AssertOnTarEntry(t, outputFile,
  1096  				"/blobs/sha256/"+buildpackLayerSHA,
  1097  				h.HasOwnerAndGroup(0, 0),
  1098  				h.HasFileMode(0755),
  1099  				h.IsGzipped(),
  1100  			)
  1101  		})
  1102  	})
  1103  }
  1104  
  1105  func computeLayerSHA(reader io.ReadCloser) (string, error) {
  1106  	bpLayer := stream.NewLayer(reader, stream.WithCompressionLevel(gzip.DefaultCompression))
  1107  	compressed, err := bpLayer.Compressed()
  1108  	if err != nil {
  1109  		return "", err
  1110  	}
  1111  	defer compressed.Close()
  1112  
  1113  	if _, err := io.Copy(io.Discard, compressed); err != nil {
  1114  		return "", err
  1115  	}
  1116  
  1117  	digest, err := bpLayer.Digest()
  1118  	if err != nil {
  1119  		return "", err
  1120  	}
  1121  
  1122  	return digest.Hex, nil
  1123  }
  1124  
  1125  type imageWithLabelError struct {
  1126  	*fakes.Image
  1127  }
  1128  
  1129  func (i *imageWithLabelError) SetLabel(string, string) error {
  1130  	return errors.New("Label could not be set")
  1131  }