github.com/YousefHaggyHeroku/pack@v1.5.5/internal/builder/builder_test.go (about)

     1  package builder_test
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os"
     9  	"path"
    10  	"path/filepath"
    11  	"testing"
    12  
    13  	"github.com/buildpacks/imgutil"
    14  	"github.com/buildpacks/imgutil/fakes"
    15  	"github.com/buildpacks/lifecycle/api"
    16  	"github.com/golang/mock/gomock"
    17  	"github.com/heroku/color"
    18  	"github.com/sclevine/spec"
    19  	"github.com/sclevine/spec/report"
    20  
    21  	pubbldr "github.com/YousefHaggyHeroku/pack/builder"
    22  	"github.com/YousefHaggyHeroku/pack/internal/archive"
    23  	"github.com/YousefHaggyHeroku/pack/internal/builder"
    24  	"github.com/YousefHaggyHeroku/pack/internal/builder/testmocks"
    25  	"github.com/YousefHaggyHeroku/pack/internal/dist"
    26  	ifakes "github.com/YousefHaggyHeroku/pack/internal/fakes"
    27  	ilogging "github.com/YousefHaggyHeroku/pack/internal/logging"
    28  	"github.com/YousefHaggyHeroku/pack/logging"
    29  	h "github.com/YousefHaggyHeroku/pack/testhelpers"
    30  )
    31  
    32  func TestBuilder(t *testing.T) {
    33  	color.Disable(true)
    34  	defer color.Disable(false)
    35  	spec.Run(t, "Builder", testBuilder, spec.Parallel(), spec.Report(report.Terminal{}))
    36  }
    37  
    38  func testBuilder(t *testing.T, when spec.G, it spec.S) {
    39  	var (
    40  		baseImage      *fakes.Image
    41  		subject        *builder.Builder
    42  		mockController *gomock.Controller
    43  		mockLifecycle  *testmocks.MockLifecycle
    44  		bp1v1          dist.Buildpack
    45  		bp1v2          dist.Buildpack
    46  		bp2v1          dist.Buildpack
    47  		bpOrder        dist.Buildpack
    48  		outBuf         bytes.Buffer
    49  		logger         logging.Logger
    50  	)
    51  
    52  	it.Before(func() {
    53  		logger = ilogging.NewLogWithWriters(&outBuf, &outBuf)
    54  		baseImage = fakes.NewImage("base/image", "", nil)
    55  		mockController = gomock.NewController(t)
    56  
    57  		lifecycleTarReader := archive.ReadDirAsTar(
    58  			filepath.Join("testdata", "lifecycle", "platform-0.4"),
    59  			".", 0, 0, 0755, true, nil,
    60  		)
    61  
    62  		descriptorContents, err := ioutil.ReadFile(filepath.Join("testdata", "lifecycle", "platform-0.4", "lifecycle.toml"))
    63  		h.AssertNil(t, err)
    64  
    65  		lifecycleDescriptor, err := builder.ParseDescriptor(string(descriptorContents))
    66  		h.AssertNil(t, err)
    67  
    68  		mockLifecycle = testmocks.NewMockLifecycle(mockController)
    69  		mockLifecycle.EXPECT().Open().Return(lifecycleTarReader, nil).AnyTimes()
    70  		mockLifecycle.EXPECT().Descriptor().Return(builder.CompatDescriptor(lifecycleDescriptor)).AnyTimes()
    71  
    72  		bp1v1, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
    73  			API: api.MustParse("0.2"),
    74  			Info: dist.BuildpackInfo{
    75  				ID:      "buildpack-1-id",
    76  				Version: "buildpack-1-version-1",
    77  			},
    78  			Stacks: []dist.Stack{{
    79  				ID:     "some.stack.id",
    80  				Mixins: []string{"mixinX", "mixinY"},
    81  			}},
    82  		}, 0644)
    83  		h.AssertNil(t, err)
    84  
    85  		bp1v2, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
    86  			API: api.MustParse("0.2"),
    87  			Info: dist.BuildpackInfo{
    88  				ID:      "buildpack-1-id",
    89  				Version: "buildpack-1-version-2",
    90  			},
    91  			Stacks: []dist.Stack{{
    92  				ID:     "some.stack.id",
    93  				Mixins: []string{"mixinX", "mixinY"},
    94  			}},
    95  		}, 0644)
    96  		h.AssertNil(t, err)
    97  
    98  		bp2v1, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
    99  			API: api.MustParse("0.2"),
   100  			Info: dist.BuildpackInfo{
   101  				ID:      "buildpack-2-id",
   102  				Version: "buildpack-2-version-1",
   103  			},
   104  			Stacks: []dist.Stack{{
   105  				ID:     "some.stack.id",
   106  				Mixins: []string{"build:mixinA", "run:mixinB"},
   107  			}},
   108  		}, 0644)
   109  		h.AssertNil(t, err)
   110  
   111  		bpOrder, err = ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   112  			API: api.MustParse("0.2"),
   113  			Info: dist.BuildpackInfo{
   114  				ID:      "order-buildpack-id",
   115  				Version: "order-buildpack-version",
   116  			},
   117  			Order: []dist.OrderEntry{{
   118  				Group: []dist.BuildpackRef{
   119  					{
   120  						BuildpackInfo: bp1v1.Descriptor().Info,
   121  						Optional:      true,
   122  					},
   123  					{
   124  						BuildpackInfo: bp2v1.Descriptor().Info,
   125  						Optional:      false,
   126  					},
   127  				},
   128  			}},
   129  		}, 0644)
   130  		h.AssertNil(t, err)
   131  	})
   132  
   133  	it.After(func() {
   134  		baseImage.Cleanup()
   135  		mockController.Finish()
   136  	})
   137  
   138  	when("the base image is not valid", func() {
   139  		when("#FromImage", func() {
   140  			when("metadata isn't valid", func() {
   141  				it("returns an error", func() {
   142  					h.AssertNil(t, baseImage.SetLabel(
   143  						"io.buildpacks.builder.metadata",
   144  						`{"something-random": ,}`,
   145  					))
   146  
   147  					_, err := builder.FromImage(baseImage)
   148  					h.AssertError(t, err, "getting label")
   149  				})
   150  			})
   151  		})
   152  
   153  		when("#New", func() {
   154  			when("metadata isn't valid", func() {
   155  				it("returns an error", func() {
   156  					h.AssertNil(t, baseImage.SetLabel(
   157  						"io.buildpacks.builder.metadata",
   158  						`{"something-random": ,}`,
   159  					))
   160  
   161  					_, err := builder.FromImage(baseImage)
   162  					h.AssertError(t, err, "getting label")
   163  				})
   164  			})
   165  
   166  			when("missing CNB_USER_ID", func() {
   167  				it("returns an error", func() {
   168  					_, err := builder.New(baseImage, "some/builder")
   169  					h.AssertError(t, err, "image 'base/image' missing required env var 'CNB_USER_ID'")
   170  				})
   171  			})
   172  
   173  			when("missing CNB_GROUP_ID", func() {
   174  				it.Before(func() {
   175  					h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "1234"))
   176  				})
   177  
   178  				it("returns an error", func() {
   179  					_, err := builder.New(baseImage, "some/builder")
   180  					h.AssertError(t, err, "image 'base/image' missing required env var 'CNB_GROUP_ID'")
   181  				})
   182  			})
   183  
   184  			when("CNB_USER_ID is not an int", func() {
   185  				it.Before(func() {
   186  					h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "not an int"))
   187  					h.AssertNil(t, baseImage.SetEnv("CNB_GROUP_ID", "4321"))
   188  				})
   189  
   190  				it("returns an error", func() {
   191  					_, err := builder.New(baseImage, "some/builder")
   192  					h.AssertError(t, err, "failed to parse 'CNB_USER_ID', value 'not an int' should be an integer")
   193  				})
   194  			})
   195  
   196  			when("CNB_GROUP_ID is not an int", func() {
   197  				it.Before(func() {
   198  					h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "1234"))
   199  					h.AssertNil(t, baseImage.SetEnv("CNB_GROUP_ID", "not an int"))
   200  				})
   201  
   202  				it("returns an error", func() {
   203  					_, err := builder.New(baseImage, "some/builder")
   204  					h.AssertError(t, err, "failed to parse 'CNB_GROUP_ID', value 'not an int' should be an integer")
   205  				})
   206  			})
   207  
   208  			when("missing stack id label", func() {
   209  				it.Before(func() {
   210  					h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "1234"))
   211  					h.AssertNil(t, baseImage.SetEnv("CNB_GROUP_ID", "4321"))
   212  				})
   213  
   214  				it("returns an error", func() {
   215  					_, err := builder.New(baseImage, "some/builder")
   216  					h.AssertError(t, err, "image 'base/image' missing label 'io.buildpacks.stack.id'")
   217  				})
   218  			})
   219  
   220  			when("mixins metadata is malformed", func() {
   221  				it.Before(func() {
   222  					h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "1234"))
   223  					h.AssertNil(t, baseImage.SetEnv("CNB_GROUP_ID", "4321"))
   224  					h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.id", "some-id"))
   225  				})
   226  
   227  				it("returns an error", func() {
   228  					h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.mixins", `{"mixinX", "mixinY", "build:mixinA"}`))
   229  					_, err := builder.New(baseImage, "some/builder")
   230  					h.AssertError(t, err, "getting label io.buildpacks.stack.mixins")
   231  				})
   232  			})
   233  
   234  			when("order metadata is malformed", func() {
   235  				it.Before(func() {
   236  					h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "1234"))
   237  					h.AssertNil(t, baseImage.SetEnv("CNB_GROUP_ID", "4321"))
   238  					h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.id", "some-id"))
   239  				})
   240  
   241  				it("returns an error", func() {
   242  					h.AssertNil(t, baseImage.SetLabel("io.buildpacks.buildpack.order", `{"something", }`))
   243  					_, err := builder.New(baseImage, "some/builder")
   244  					h.AssertError(t, err, "getting label io.buildpacks.buildpack.order")
   245  				})
   246  			})
   247  		})
   248  	})
   249  
   250  	when("the base image is a valid build image", func() {
   251  		it.Before(func() {
   252  			var err error
   253  			h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "1234"))
   254  			h.AssertNil(t, baseImage.SetEnv("CNB_GROUP_ID", "4321"))
   255  			h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.id", "some.stack.id"))
   256  			h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.mixins", `["mixinX", "mixinY", "build:mixinA"]`))
   257  			subject, err = builder.New(baseImage, "some/builder")
   258  			h.AssertNil(t, err)
   259  
   260  			subject.SetLifecycle(mockLifecycle)
   261  		})
   262  
   263  		it.After(func() {
   264  			baseImage.Cleanup()
   265  		})
   266  
   267  		when("#Save", func() {
   268  			it("creates a builder from the image and renames it", func() {
   269  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   270  				h.AssertEq(t, baseImage.IsSaved(), true)
   271  				h.AssertEq(t, baseImage.Name(), "some/builder")
   272  			})
   273  
   274  			it("adds creator metadata", func() {
   275  				testName := "test-name"
   276  				testVersion := "1.2.5"
   277  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{
   278  					Name:    testName,
   279  					Version: testVersion,
   280  				}))
   281  				h.AssertEq(t, baseImage.IsSaved(), true)
   282  
   283  				label, err := baseImage.Label("io.buildpacks.builder.metadata")
   284  				h.AssertNil(t, err)
   285  
   286  				var metadata builder.Metadata
   287  				h.AssertNil(t, json.Unmarshal([]byte(label), &metadata))
   288  
   289  				h.AssertEq(t, metadata.CreatedBy.Name, testName)
   290  				h.AssertEq(t, metadata.CreatedBy.Version, testVersion)
   291  			})
   292  
   293  			it("adds creator name if not provided", func() {
   294  				testVersion := "1.2.5"
   295  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{
   296  					Version: testVersion,
   297  				}))
   298  				h.AssertEq(t, baseImage.IsSaved(), true)
   299  
   300  				label, err := baseImage.Label("io.buildpacks.builder.metadata")
   301  				h.AssertNil(t, err)
   302  
   303  				var metadata builder.Metadata
   304  				h.AssertNil(t, json.Unmarshal([]byte(label), &metadata))
   305  
   306  				h.AssertEq(t, metadata.CreatedBy.Name, "Pack CLI")
   307  				h.AssertEq(t, metadata.CreatedBy.Version, testVersion)
   308  			})
   309  
   310  			it("creates the workspace dir with CNB user and group", func() {
   311  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   312  				h.AssertEq(t, baseImage.IsSaved(), true)
   313  
   314  				layerTar, err := baseImage.FindLayerWithPath("/workspace")
   315  				h.AssertNil(t, err)
   316  				h.AssertOnTarEntry(t, layerTar, "/workspace",
   317  					h.IsDirectory(),
   318  					h.HasFileMode(0755),
   319  					h.HasOwnerAndGroup(1234, 4321),
   320  					h.HasModTime(archive.NormalizedDateTime),
   321  				)
   322  			})
   323  
   324  			it("creates the layers dir with CNB user and group", func() {
   325  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   326  				h.AssertEq(t, baseImage.IsSaved(), true)
   327  
   328  				layerTar, err := baseImage.FindLayerWithPath("/layers")
   329  				h.AssertNil(t, err)
   330  				h.AssertOnTarEntry(t, layerTar, "/layers",
   331  					h.IsDirectory(),
   332  					h.HasOwnerAndGroup(1234, 4321),
   333  					h.HasFileMode(0755),
   334  					h.HasModTime(archive.NormalizedDateTime),
   335  				)
   336  			})
   337  
   338  			it("creates the cnb dir", func() {
   339  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   340  				h.AssertEq(t, baseImage.IsSaved(), true)
   341  
   342  				layerTar, err := baseImage.FindLayerWithPath("/cnb")
   343  				h.AssertNil(t, err)
   344  				h.AssertOnTarEntry(t, layerTar, "/cnb",
   345  					h.IsDirectory(),
   346  					h.HasOwnerAndGroup(0, 0),
   347  					h.HasFileMode(0755),
   348  					h.HasModTime(archive.NormalizedDateTime),
   349  				)
   350  			})
   351  
   352  			it("creates the buildpacks dir", func() {
   353  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   354  				h.AssertEq(t, baseImage.IsSaved(), true)
   355  
   356  				layerTar, err := baseImage.FindLayerWithPath("/cnb/buildpacks")
   357  				h.AssertNil(t, err)
   358  				h.AssertOnTarEntry(t, layerTar, "/cnb/buildpacks",
   359  					h.IsDirectory(),
   360  					h.HasOwnerAndGroup(0, 0),
   361  					h.HasFileMode(0755),
   362  					h.HasModTime(archive.NormalizedDateTime),
   363  				)
   364  			})
   365  
   366  			it("creates the platform dir", func() {
   367  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   368  				h.AssertEq(t, baseImage.IsSaved(), true)
   369  
   370  				layerTar, err := baseImage.FindLayerWithPath("/platform")
   371  				h.AssertNil(t, err)
   372  				h.AssertOnTarEntry(t, layerTar, "/platform",
   373  					h.IsDirectory(),
   374  					h.HasOwnerAndGroup(0, 0),
   375  					h.HasFileMode(0755),
   376  					h.HasModTime(archive.NormalizedDateTime),
   377  				)
   378  				h.AssertOnTarEntry(t, layerTar, "/platform/env",
   379  					h.IsDirectory(),
   380  					h.HasOwnerAndGroup(0, 0),
   381  					h.HasFileMode(0755),
   382  					h.HasModTime(archive.NormalizedDateTime),
   383  				)
   384  			})
   385  
   386  			it("sets the working dir to the layers dir", func() {
   387  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   388  				h.AssertEq(t, baseImage.IsSaved(), true)
   389  
   390  				h.AssertEq(t, baseImage.WorkingDir(), "/layers")
   391  			})
   392  
   393  			it("does not overwrite the order layer when SetOrder has not been called", func() {
   394  				tmpDir, err := ioutil.TempDir("", "")
   395  				h.AssertNil(t, err)
   396  				defer os.RemoveAll(tmpDir)
   397  
   398  				layerFile := filepath.Join(tmpDir, "order.tar")
   399  				err = archive.CreateSingleFileTar(layerFile, "/cnb/order.toml", "some content")
   400  				h.AssertNil(t, err)
   401  
   402  				h.AssertNil(t, baseImage.AddLayer(layerFile))
   403  				baseImage.Save()
   404  
   405  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   406  				h.AssertEq(t, baseImage.IsSaved(), true)
   407  
   408  				layerTar, err := baseImage.FindLayerWithPath("/cnb/order.toml")
   409  				h.AssertNil(t, err)
   410  				h.AssertOnTarEntry(t, layerTar, "/cnb/order.toml", h.ContentEquals("some content"))
   411  			})
   412  
   413  			when("validating order", func() {
   414  				it.Before(func() {
   415  					subject.SetLifecycle(mockLifecycle)
   416  				})
   417  
   418  				when("has single buildpack", func() {
   419  					it.Before(func() {
   420  						subject.AddBuildpack(bp1v1)
   421  					})
   422  
   423  					it("should resolve unset version (to legacy label and order.toml)", func() {
   424  						subject.SetOrder(dist.Order{{
   425  							Group: []dist.BuildpackRef{
   426  								{BuildpackInfo: dist.BuildpackInfo{ID: bp1v1.Descriptor().Info.ID}}},
   427  						}})
   428  
   429  						err := subject.Save(logger, builder.CreatorMetadata{})
   430  						h.AssertNil(t, err)
   431  
   432  						layerTar, err := baseImage.FindLayerWithPath("/cnb/order.toml")
   433  						h.AssertNil(t, err)
   434  						h.AssertOnTarEntry(t, layerTar, "/cnb/order.toml", h.ContentEquals(`[[order]]
   435  
   436    [[order.group]]
   437      id = "buildpack-1-id"
   438      version = "buildpack-1-version-1"
   439  `))
   440  					})
   441  
   442  					when("order points to missing buildpack id", func() {
   443  						it("should error", func() {
   444  							subject.SetOrder(dist.Order{{
   445  								Group: []dist.BuildpackRef{
   446  									{BuildpackInfo: dist.BuildpackInfo{ID: "missing-buildpack-id"}}},
   447  							}})
   448  
   449  							err := subject.Save(logger, builder.CreatorMetadata{})
   450  
   451  							h.AssertError(t, err, "no versions of buildpack 'missing-buildpack-id' were found on the builder")
   452  						})
   453  					})
   454  
   455  					when("order points to missing buildpack version", func() {
   456  						it("should error", func() {
   457  							subject.SetOrder(dist.Order{{
   458  								Group: []dist.BuildpackRef{
   459  									{BuildpackInfo: dist.BuildpackInfo{ID: "buildpack-1-id", Version: "missing-buildpack-version"}}},
   460  							}})
   461  
   462  							err := subject.Save(logger, builder.CreatorMetadata{})
   463  
   464  							h.AssertError(t, err, "buildpack 'buildpack-1-id' with version 'missing-buildpack-version' was not found on the builder")
   465  						})
   466  					})
   467  				})
   468  
   469  				when("has repeated buildpacks with the same ID and version", func() {
   470  					it.Before(func() {
   471  						subject.AddBuildpack(bp1v1)
   472  						subject.AddBuildpack(bp1v1)
   473  					})
   474  
   475  					when("order omits version", func() {
   476  						it("should de-duplicate identical buildpacks", func() {
   477  							subject.SetOrder(dist.Order{
   478  								{Group: []dist.BuildpackRef{{
   479  									BuildpackInfo: dist.BuildpackInfo{
   480  										ID:       bp1v1.Descriptor().Info.ID,
   481  										Homepage: bp1v1.Descriptor().Info.Homepage,
   482  									}}},
   483  								},
   484  								{Group: []dist.BuildpackRef{{
   485  									BuildpackInfo: dist.BuildpackInfo{
   486  										ID:       bp1v1.Descriptor().Info.ID,
   487  										Homepage: bp1v1.Descriptor().Info.Homepage,
   488  									}}},
   489  								},
   490  							})
   491  
   492  							err := subject.Save(logger, builder.CreatorMetadata{})
   493  							h.AssertNil(t, err)
   494  						})
   495  					})
   496  				})
   497  
   498  				when("has multiple buildpacks with same ID", func() {
   499  					it.Before(func() {
   500  						subject.AddBuildpack(bp1v1)
   501  						subject.AddBuildpack(bp1v2)
   502  					})
   503  
   504  					when("order explicitly sets version", func() {
   505  						it("should keep order version", func() {
   506  							subject.SetOrder(dist.Order{{
   507  								Group: []dist.BuildpackRef{
   508  									{BuildpackInfo: bp1v1.Descriptor().Info}},
   509  							}})
   510  
   511  							err := subject.Save(logger, builder.CreatorMetadata{})
   512  							h.AssertNil(t, err)
   513  
   514  							layerTar, err := baseImage.FindLayerWithPath("/cnb/order.toml")
   515  							h.AssertNil(t, err)
   516  							h.AssertOnTarEntry(t, layerTar, "/cnb/order.toml", h.ContentEquals(`[[order]]
   517  
   518    [[order.group]]
   519      id = "buildpack-1-id"
   520      version = "buildpack-1-version-1"
   521  `))
   522  						})
   523  					})
   524  
   525  					when("order version is empty", func() {
   526  						it("return error", func() {
   527  							subject.SetOrder(dist.Order{{
   528  								Group: []dist.BuildpackRef{
   529  									{BuildpackInfo: dist.BuildpackInfo{ID: "buildpack-1-id"}}},
   530  							}})
   531  
   532  							err := subject.Save(logger, builder.CreatorMetadata{})
   533  							h.AssertError(t, err, "multiple versions of 'buildpack-1-id' - must specify an explicit version")
   534  						})
   535  					})
   536  				})
   537  			})
   538  
   539  			when("validating buildpacks", func() {
   540  				when("nested buildpack does not exist", func() {
   541  					when("buildpack by id does not exist", func() {
   542  						it("returns an error", func() {
   543  							subject.AddBuildpack(bp1v1)
   544  							subject.AddBuildpack(bpOrder)
   545  
   546  							// order buildpack requires bp2v1
   547  							err := subject.Save(logger, builder.CreatorMetadata{})
   548  
   549  							h.AssertError(t, err, "buildpack 'buildpack-2-id@buildpack-2-version-1' not found on the builder")
   550  						})
   551  					})
   552  
   553  					when("buildpack version does not exist", func() {
   554  						it("returns an error", func() {
   555  							subject.AddBuildpack(bp1v2)
   556  							subject.AddBuildpack(bp2v1)
   557  
   558  							// order buildpack requires bp1v1 rather than bp1v2
   559  							subject.AddBuildpack(bpOrder)
   560  
   561  							err := subject.Save(logger, builder.CreatorMetadata{})
   562  
   563  							h.AssertError(t, err, "buildpack 'buildpack-1-id@buildpack-1-version-1' not found on the builder")
   564  						})
   565  					})
   566  				})
   567  
   568  				when("buildpack stack id does not match", func() {
   569  					it("returns an error", func() {
   570  						bp, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   571  							API:    api.MustParse("0.2"),
   572  							Info:   bp1v1.Descriptor().Info,
   573  							Stacks: []dist.Stack{{ID: "other.stack.id"}},
   574  						}, 0644)
   575  						h.AssertNil(t, err)
   576  
   577  						subject.AddBuildpack(bp)
   578  						err = subject.Save(logger, builder.CreatorMetadata{})
   579  
   580  						h.AssertError(t, err, "buildpack 'buildpack-1-id@buildpack-1-version-1' does not support stack 'some.stack.id'")
   581  					})
   582  				})
   583  
   584  				when("buildpack is not compatible with lifecycle", func() {
   585  					it("returns an error", func() {
   586  						bp, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   587  							API:    api.MustParse("0.1"),
   588  							Info:   bp1v1.Descriptor().Info,
   589  							Stacks: []dist.Stack{{ID: "some.stack.id"}},
   590  						}, 0644)
   591  						h.AssertNil(t, err)
   592  
   593  						subject.AddBuildpack(bp)
   594  						err = subject.Save(logger, builder.CreatorMetadata{})
   595  
   596  						h.AssertError(t,
   597  							err,
   598  							"buildpack 'buildpack-1-id@buildpack-1-version-1' (Buildpack API 0.1) is incompatible with lifecycle '0.0.0' (Buildpack API(s) 0.2, 0.3, 0.4)")
   599  					})
   600  				})
   601  
   602  				when("buildpack mixins are not satisfied", func() {
   603  					it("returns an error", func() {
   604  						bp, err := ifakes.NewFakeBuildpack(dist.BuildpackDescriptor{
   605  							API:  api.MustParse("0.2"),
   606  							Info: bp1v1.Descriptor().Info,
   607  							Stacks: []dist.Stack{{
   608  								ID:     "some.stack.id",
   609  								Mixins: []string{"missing"},
   610  							}},
   611  						}, 0644)
   612  						h.AssertNil(t, err)
   613  
   614  						subject.AddBuildpack(bp)
   615  						err = subject.Save(logger, builder.CreatorMetadata{})
   616  
   617  						h.AssertError(t, err, "buildpack 'buildpack-1-id@buildpack-1-version-1' requires missing mixin(s): missing")
   618  					})
   619  				})
   620  			})
   621  
   622  			when("getting layers label", func() {
   623  				it("fails if layers label isn't set correctly", func() {
   624  					h.AssertNil(t, baseImage.SetLabel(
   625  						"io.buildpacks.buildpack.layers",
   626  						`{"something-here: }`,
   627  					))
   628  
   629  					err := subject.Save(logger, builder.CreatorMetadata{})
   630  					h.AssertError(t, err, "getting label io.buildpacks.buildpack.layers")
   631  				})
   632  			})
   633  		})
   634  
   635  		when("#SetLifecycle", func() {
   636  			it.Before(func() {
   637  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   638  				h.AssertEq(t, baseImage.IsSaved(), true)
   639  			})
   640  
   641  			it("should set the lifecycle version successfully", func() {
   642  				h.AssertEq(t, subject.LifecycleDescriptor().Info.Version.String(), "0.0.0")
   643  			})
   644  
   645  			it("should add the lifecycle binaries as an image layer", func() {
   646  				layerTar, err := baseImage.FindLayerWithPath("/cnb/lifecycle")
   647  				h.AssertNil(t, err)
   648  				h.AssertOnTarEntry(t, layerTar, "/cnb/lifecycle",
   649  					h.IsDirectory(),
   650  					h.HasFileMode(0755),
   651  					h.HasModTime(archive.NormalizedDateTime),
   652  				)
   653  
   654  				h.AssertOnTarEntry(t, layerTar, "/cnb/lifecycle/detector",
   655  					h.ContentEquals("detector"),
   656  					h.HasFileMode(0755),
   657  					h.HasModTime(archive.NormalizedDateTime),
   658  				)
   659  
   660  				h.AssertOnTarEntry(t, layerTar, "/cnb/lifecycle/restorer",
   661  					h.ContentEquals("restorer"),
   662  					h.HasFileMode(0755),
   663  					h.HasModTime(archive.NormalizedDateTime),
   664  				)
   665  
   666  				h.AssertOnTarEntry(t, layerTar, "/cnb/lifecycle/analyzer",
   667  					h.ContentEquals("analyzer"),
   668  					h.HasFileMode(0755),
   669  					h.HasModTime(archive.NormalizedDateTime),
   670  				)
   671  
   672  				h.AssertOnTarEntry(t, layerTar, "/cnb/lifecycle/builder",
   673  					h.ContentEquals("builder"),
   674  					h.HasFileMode(0755),
   675  					h.HasModTime(archive.NormalizedDateTime),
   676  				)
   677  
   678  				h.AssertOnTarEntry(t, layerTar, "/cnb/lifecycle/exporter",
   679  					h.ContentEquals("exporter"),
   680  					h.HasFileMode(0755),
   681  					h.HasModTime(archive.NormalizedDateTime),
   682  				)
   683  
   684  				h.AssertOnTarEntry(t, layerTar, "/cnb/lifecycle/launcher",
   685  					h.ContentEquals("launcher"),
   686  					h.HasFileMode(0755),
   687  					h.HasModTime(archive.NormalizedDateTime),
   688  				)
   689  
   690  				it("should add lifecycle symlink", func() {
   691  					h.AssertOnTarEntry(t, layerTar, "/lifecycle",
   692  						h.SymlinksTo("/cnb/lifecycle"),
   693  						h.HasFileMode(0644),
   694  						h.HasModTime(archive.NormalizedDateTime),
   695  					)
   696  				})
   697  			})
   698  
   699  			it("sets the lifecycle version on the metadata", func() {
   700  				label, err := baseImage.Label("io.buildpacks.builder.metadata")
   701  				h.AssertNil(t, err)
   702  
   703  				var metadata builder.Metadata
   704  				h.AssertNil(t, json.Unmarshal([]byte(label), &metadata))
   705  				h.AssertEq(t, metadata.Lifecycle.Version.String(), "0.0.0")
   706  				h.AssertEq(t, metadata.Lifecycle.API.BuildpackVersion.String(), "0.2")
   707  				h.AssertEq(t, metadata.Lifecycle.API.PlatformVersion.String(), "0.2")
   708  				h.AssertNotNil(t, metadata.Lifecycle.APIs)
   709  				h.AssertEq(t, metadata.Lifecycle.APIs.Buildpack.Deprecated.AsStrings(), []string{})
   710  				h.AssertEq(t, metadata.Lifecycle.APIs.Buildpack.Supported.AsStrings(), []string{"0.2", "0.3", "0.4"})
   711  				h.AssertEq(t, metadata.Lifecycle.APIs.Platform.Deprecated.AsStrings(), []string{"0.2"})
   712  				h.AssertEq(t, metadata.Lifecycle.APIs.Platform.Supported.AsStrings(), []string{"0.3", "0.4"})
   713  			})
   714  		})
   715  
   716  		when("#AddBuildpack", func() {
   717  			it.Before(func() {
   718  				subject.AddBuildpack(bp1v1)
   719  				subject.AddBuildpack(bp1v2)
   720  				subject.AddBuildpack(bp2v1)
   721  				subject.AddBuildpack(bpOrder)
   722  			})
   723  
   724  			it("adds the buildpack as an image layer", func() {
   725  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   726  				h.AssertEq(t, baseImage.IsSaved(), true)
   727  				assertImageHasBPLayer(t, baseImage, bp1v1)
   728  				assertImageHasBPLayer(t, baseImage, bp1v2)
   729  				assertImageHasBPLayer(t, baseImage, bp2v1)
   730  				assertImageHasOrderBpLayer(t, baseImage, bpOrder)
   731  			})
   732  
   733  			it("adds the buildpack metadata", func() {
   734  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   735  				h.AssertEq(t, baseImage.IsSaved(), true)
   736  
   737  				label, err := baseImage.Label("io.buildpacks.builder.metadata")
   738  				h.AssertNil(t, err)
   739  
   740  				var metadata builder.Metadata
   741  				h.AssertNil(t, json.Unmarshal([]byte(label), &metadata))
   742  				h.AssertEq(t, len(metadata.Buildpacks), 4)
   743  
   744  				h.AssertEq(t, metadata.Buildpacks[0].ID, "buildpack-1-id")
   745  				h.AssertEq(t, metadata.Buildpacks[0].Version, "buildpack-1-version-1")
   746  
   747  				h.AssertEq(t, metadata.Buildpacks[1].ID, "buildpack-1-id")
   748  				h.AssertEq(t, metadata.Buildpacks[1].Version, "buildpack-1-version-2")
   749  
   750  				h.AssertEq(t, metadata.Buildpacks[2].ID, "buildpack-2-id")
   751  				h.AssertEq(t, metadata.Buildpacks[2].Version, "buildpack-2-version-1")
   752  
   753  				h.AssertEq(t, metadata.Buildpacks[3].ID, "order-buildpack-id")
   754  				h.AssertEq(t, metadata.Buildpacks[3].Version, "order-buildpack-version")
   755  			})
   756  
   757  			it("adds the buildpack layers label", func() {
   758  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   759  				h.AssertEq(t, baseImage.IsSaved(), true)
   760  
   761  				label, err := baseImage.Label("io.buildpacks.buildpack.layers")
   762  				h.AssertNil(t, err)
   763  
   764  				var layers dist.BuildpackLayers
   765  				h.AssertNil(t, json.Unmarshal([]byte(label), &layers))
   766  				h.AssertEq(t, len(layers), 3)
   767  				h.AssertEq(t, len(layers["buildpack-1-id"]), 2)
   768  				h.AssertEq(t, len(layers["buildpack-2-id"]), 1)
   769  
   770  				h.AssertEq(t, len(layers["buildpack-1-id"]["buildpack-1-version-1"].Order), 0)
   771  				h.AssertEq(t, len(layers["buildpack-1-id"]["buildpack-1-version-1"].Stacks), 1)
   772  				h.AssertEq(t, layers["buildpack-1-id"]["buildpack-1-version-1"].Stacks[0].ID, "some.stack.id")
   773  				h.AssertSliceContainsOnly(t, layers["buildpack-1-id"]["buildpack-1-version-1"].Stacks[0].Mixins, "mixinX", "mixinY")
   774  
   775  				h.AssertEq(t, len(layers["buildpack-1-id"]["buildpack-1-version-2"].Order), 0)
   776  				h.AssertEq(t, len(layers["buildpack-1-id"]["buildpack-1-version-2"].Stacks), 1)
   777  				h.AssertEq(t, layers["buildpack-1-id"]["buildpack-1-version-2"].Stacks[0].ID, "some.stack.id")
   778  				h.AssertSliceContainsOnly(t, layers["buildpack-1-id"]["buildpack-1-version-2"].Stacks[0].Mixins, "mixinX", "mixinY")
   779  
   780  				h.AssertEq(t, len(layers["buildpack-2-id"]["buildpack-2-version-1"].Order), 0)
   781  				h.AssertEq(t, len(layers["buildpack-2-id"]["buildpack-2-version-1"].Stacks), 1)
   782  				h.AssertEq(t, layers["buildpack-2-id"]["buildpack-2-version-1"].Stacks[0].ID, "some.stack.id")
   783  				h.AssertSliceContainsOnly(t, layers["buildpack-2-id"]["buildpack-2-version-1"].Stacks[0].Mixins, "build:mixinA", "run:mixinB")
   784  
   785  				h.AssertEq(t, len(layers["order-buildpack-id"]["order-buildpack-version"].Order), 1)
   786  				h.AssertEq(t, len(layers["order-buildpack-id"]["order-buildpack-version"].Order[0].Group), 2)
   787  				h.AssertEq(t, layers["order-buildpack-id"]["order-buildpack-version"].Order[0].Group[0].ID, "buildpack-1-id")
   788  				h.AssertEq(t, layers["order-buildpack-id"]["order-buildpack-version"].Order[0].Group[0].Version, "buildpack-1-version-1")
   789  				h.AssertEq(t, layers["order-buildpack-id"]["order-buildpack-version"].Order[0].Group[0].Optional, true)
   790  				h.AssertEq(t, layers["order-buildpack-id"]["order-buildpack-version"].Order[0].Group[1].ID, "buildpack-2-id")
   791  				h.AssertEq(t, layers["order-buildpack-id"]["order-buildpack-version"].Order[0].Group[1].Version, "buildpack-2-version-1")
   792  				h.AssertEq(t, layers["order-buildpack-id"]["order-buildpack-version"].Order[0].Group[1].Optional, false)
   793  			})
   794  
   795  			when("base image already has buildpack layers label", func() {
   796  				it.Before(func() {
   797  					var mdJSON bytes.Buffer
   798  					h.AssertNil(t, json.Compact(
   799  						&mdJSON,
   800  						[]byte(`{
   801    "buildpack-1-id": {
   802      "buildpack-1-version-1": {
   803        "layerDiffID": "sha256:buildpack-1-version-1-diff-id"
   804      },
   805      "buildpack-1-version-2": {
   806        "layerDiffID": "sha256:buildpack-1-version-2-diff-id"
   807      }
   808    }
   809  }
   810  `)))
   811  
   812  					h.AssertNil(t, baseImage.SetLabel(
   813  						"io.buildpacks.buildpack.layers",
   814  						mdJSON.String(),
   815  					))
   816  
   817  					var err error
   818  					subject, err = builder.New(baseImage, "some/builder")
   819  					h.AssertNil(t, err)
   820  
   821  					subject.AddBuildpack(bp1v2)
   822  					subject.AddBuildpack(bp2v1)
   823  
   824  					subject.SetLifecycle(mockLifecycle)
   825  				})
   826  
   827  				it("appends buildpack layer info", func() {
   828  					h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   829  					h.AssertEq(t, baseImage.IsSaved(), true)
   830  
   831  					label, err := baseImage.Label("io.buildpacks.buildpack.layers")
   832  					h.AssertNil(t, err)
   833  
   834  					var layers dist.BuildpackLayers
   835  					h.AssertNil(t, json.Unmarshal([]byte(label), &layers))
   836  					h.AssertEq(t, len(layers), 2)
   837  					h.AssertEq(t, len(layers["buildpack-1-id"]), 2)
   838  					h.AssertEq(t, len(layers["buildpack-2-id"]), 1)
   839  
   840  					h.AssertEq(t, layers["buildpack-1-id"]["buildpack-1-version-1"].LayerDiffID, "sha256:buildpack-1-version-1-diff-id")
   841  
   842  					h.AssertUnique(t,
   843  						layers["buildpack-1-id"]["buildpack-1-version-1"].LayerDiffID,
   844  						layers["buildpack-1-id"]["buildpack-1-version-2"].LayerDiffID,
   845  						layers["buildpack-2-id"]["buildpack-2-version-1"].LayerDiffID,
   846  					)
   847  
   848  					h.AssertEq(t, len(layers["buildpack-1-id"]["buildpack-1-version-1"].Order), 0)
   849  					h.AssertEq(t, len(layers["buildpack-1-id"]["buildpack-1-version-2"].Order), 0)
   850  					h.AssertEq(t, len(layers["buildpack-2-id"]["buildpack-2-version-1"].Order), 0)
   851  				})
   852  
   853  				it("informs when overriding existing buildpack, and log level is DEBUG", func() {
   854  					logger := ilogging.NewLogWithWriters(&outBuf, &outBuf, ilogging.WithVerbose())
   855  
   856  					h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   857  					h.AssertEq(t, baseImage.IsSaved(), true)
   858  
   859  					label, err := baseImage.Label("io.buildpacks.buildpack.layers")
   860  					h.AssertNil(t, err)
   861  
   862  					var layers dist.BuildpackLayers
   863  					h.AssertNil(t, json.Unmarshal([]byte(label), &layers))
   864  
   865  					h.AssertContains(t,
   866  						outBuf.String(),
   867  						"buildpack 'buildpack-1-id@buildpack-1-version-2' already exists on builder and will be overwritten",
   868  					)
   869  					h.AssertNotContains(t, layers["buildpack-1-id"]["buildpack-1-version-2"].LayerDiffID, "buildpack-1-version-2-diff-id")
   870  				})
   871  
   872  				it("doesn't message when overriding existing buildpack when log level is INFO", func() {
   873  					h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   874  					h.AssertEq(t, baseImage.IsSaved(), true)
   875  
   876  					label, err := baseImage.Label("io.buildpacks.buildpack.layers")
   877  					h.AssertNil(t, err)
   878  
   879  					var layers dist.BuildpackLayers
   880  					h.AssertNil(t, json.Unmarshal([]byte(label), &layers))
   881  
   882  					h.AssertNotContains(t,
   883  						outBuf.String(),
   884  						"buildpack 'buildpack-1-id@buildpack-1-version-2' already exists on builder and will be overwritten",
   885  					)
   886  					h.AssertNotContains(t, layers["buildpack-1-id"]["buildpack-1-version-2"].LayerDiffID, "buildpack-1-version-2-diff-id")
   887  				})
   888  			})
   889  
   890  			when("base image already has metadata", func() {
   891  				it.Before(func() {
   892  					h.AssertNil(t, baseImage.SetLabel(
   893  						"io.buildpacks.builder.metadata",
   894  						`{
   895  "buildpacks":[{"id":"prev.id"}],
   896  "groups":[{"buildpacks":[{"id":"prev.id"}]}],
   897  "stack":{"runImage":{"image":"prev/run","mirrors":["prev/mirror"]}},
   898  "lifecycle":{"version":"6.6.6","apis":{"buildpack":{"deprecated":["0.1"],"supported":["0.2","0.3"]},"platform":{"deprecated":[],"supported":["2.3","2.4"]}}}
   899  }`,
   900  					))
   901  
   902  					var err error
   903  					subject, err = builder.New(baseImage, "some/builder")
   904  					h.AssertNil(t, err)
   905  
   906  					subject.AddBuildpack(bp1v1)
   907  					h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   908  					h.AssertEq(t, baseImage.IsSaved(), true)
   909  				})
   910  
   911  				it("appends the buildpack to the metadata", func() {
   912  					label, err := baseImage.Label("io.buildpacks.builder.metadata")
   913  					h.AssertNil(t, err)
   914  
   915  					var metadata builder.Metadata
   916  					h.AssertNil(t, json.Unmarshal([]byte(label), &metadata))
   917  					h.AssertEq(t, len(metadata.Buildpacks), 2)
   918  
   919  					// keeps original metadata
   920  					h.AssertEq(t, metadata.Buildpacks[0].ID, "prev.id")
   921  					h.AssertEq(t, metadata.Stack.RunImage.Image, "prev/run")
   922  					h.AssertEq(t, metadata.Stack.RunImage.Mirrors[0], "prev/mirror")
   923  					h.AssertEq(t, subject.LifecycleDescriptor().Info.Version.String(), "6.6.6")
   924  
   925  					// adds new buildpack
   926  					h.AssertEq(t, metadata.Buildpacks[1].ID, "buildpack-1-id")
   927  					h.AssertEq(t, metadata.Buildpacks[1].Version, "buildpack-1-version-1")
   928  				})
   929  			})
   930  		})
   931  
   932  		when("#SetOrder", func() {
   933  			when("the buildpacks exist in the image", func() {
   934  				it.Before(func() {
   935  					subject.AddBuildpack(bp1v1)
   936  					subject.AddBuildpack(bp2v1)
   937  					subject.SetOrder(dist.Order{
   938  						{Group: []dist.BuildpackRef{
   939  							{
   940  								BuildpackInfo: dist.BuildpackInfo{
   941  									ID: bp1v1.Descriptor().Info.ID,
   942  									// Version excluded intentionally
   943  								},
   944  							},
   945  							{
   946  								BuildpackInfo: bp2v1.Descriptor().Info,
   947  								Optional:      true,
   948  							},
   949  						}},
   950  					})
   951  
   952  					h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   953  					h.AssertEq(t, baseImage.IsSaved(), true)
   954  				})
   955  
   956  				it("adds the order.toml to the image", func() {
   957  					layerTar, err := baseImage.FindLayerWithPath("/cnb/order.toml")
   958  					h.AssertNil(t, err)
   959  					h.AssertOnTarEntry(t, layerTar, "/cnb/order.toml",
   960  						h.ContentEquals(`[[order]]
   961  
   962    [[order.group]]
   963      id = "buildpack-1-id"
   964      version = "buildpack-1-version-1"
   965  
   966    [[order.group]]
   967      id = "buildpack-2-id"
   968      version = "buildpack-2-version-1"
   969      optional = true
   970  `),
   971  						h.HasModTime(archive.NormalizedDateTime),
   972  					)
   973  				})
   974  
   975  				it("adds the order to the order label", func() {
   976  					label, err := baseImage.Label("io.buildpacks.buildpack.order")
   977  					h.AssertNil(t, err)
   978  
   979  					var order dist.Order
   980  					h.AssertNil(t, json.Unmarshal([]byte(label), &order))
   981  					h.AssertEq(t, len(order), 1)
   982  					h.AssertEq(t, len(order[0].Group), 2)
   983  					h.AssertEq(t, order[0].Group[0].ID, "buildpack-1-id")
   984  					h.AssertEq(t, order[0].Group[0].Version, "")
   985  					h.AssertEq(t, order[0].Group[0].Optional, false)
   986  					h.AssertEq(t, order[0].Group[1].ID, "buildpack-2-id")
   987  					h.AssertEq(t, order[0].Group[1].Version, "buildpack-2-version-1")
   988  					h.AssertEq(t, order[0].Group[1].Optional, true)
   989  				})
   990  			})
   991  		})
   992  
   993  		when("#SetDescription", func() {
   994  			it.Before(func() {
   995  				subject.SetDescription("Some description")
   996  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
   997  				h.AssertEq(t, baseImage.IsSaved(), true)
   998  			})
   999  
  1000  			it("sets the description on the metadata", func() {
  1001  				label, err := baseImage.Label("io.buildpacks.builder.metadata")
  1002  				h.AssertNil(t, err)
  1003  
  1004  				var metadata builder.Metadata
  1005  				h.AssertNil(t, json.Unmarshal([]byte(label), &metadata))
  1006  				h.AssertEq(t, metadata.Description, "Some description")
  1007  			})
  1008  		})
  1009  
  1010  		when("#SetStack", func() {
  1011  			it.Before(func() {
  1012  				subject.SetStack(pubbldr.StackConfig{
  1013  					RunImage:        "some/run",
  1014  					RunImageMirrors: []string{"some/mirror", "other/mirror"},
  1015  				})
  1016  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
  1017  				h.AssertEq(t, baseImage.IsSaved(), true)
  1018  			})
  1019  
  1020  			it("adds the stack.toml to the image", func() {
  1021  				layerTar, err := baseImage.FindLayerWithPath("/cnb/stack.toml")
  1022  				h.AssertNil(t, err)
  1023  				h.AssertOnTarEntry(t, layerTar, "/cnb/stack.toml",
  1024  					h.ContentEquals(`[run-image]
  1025    image = "some/run"
  1026    mirrors = ["some/mirror", "other/mirror"]
  1027  `),
  1028  					h.HasModTime(archive.NormalizedDateTime),
  1029  				)
  1030  			})
  1031  
  1032  			it("adds the stack to the metadata", func() {
  1033  				label, err := baseImage.Label("io.buildpacks.builder.metadata")
  1034  				h.AssertNil(t, err)
  1035  
  1036  				var metadata builder.Metadata
  1037  				h.AssertNil(t, json.Unmarshal([]byte(label), &metadata))
  1038  				h.AssertEq(t, metadata.Stack.RunImage.Image, "some/run")
  1039  				h.AssertEq(t, metadata.Stack.RunImage.Mirrors[0], "some/mirror")
  1040  				h.AssertEq(t, metadata.Stack.RunImage.Mirrors[1], "other/mirror")
  1041  			})
  1042  		})
  1043  
  1044  		when("#SetEnv", func() {
  1045  			it.Before(func() {
  1046  				subject.SetEnv(map[string]string{
  1047  					"SOME_KEY":  "some-val",
  1048  					"OTHER_KEY": "other-val",
  1049  				})
  1050  				h.AssertNil(t, subject.Save(logger, builder.CreatorMetadata{}))
  1051  				h.AssertEq(t, baseImage.IsSaved(), true)
  1052  			})
  1053  
  1054  			it("adds the env vars as files to the image", func() {
  1055  				layerTar, err := baseImage.FindLayerWithPath("/platform/env/SOME_KEY")
  1056  				h.AssertNil(t, err)
  1057  				h.AssertOnTarEntry(t, layerTar, "/platform/env/SOME_KEY",
  1058  					h.ContentEquals(`some-val`),
  1059  					h.HasModTime(archive.NormalizedDateTime),
  1060  				)
  1061  				h.AssertOnTarEntry(t, layerTar, "/platform/env/OTHER_KEY",
  1062  					h.ContentEquals(`other-val`),
  1063  					h.HasModTime(archive.NormalizedDateTime),
  1064  				)
  1065  			})
  1066  		})
  1067  	})
  1068  
  1069  	when("builder exists", func() {
  1070  		var builderImage imgutil.Image
  1071  
  1072  		it.Before(func() {
  1073  			h.AssertNil(t, baseImage.SetEnv("CNB_USER_ID", "1234"))
  1074  			h.AssertNil(t, baseImage.SetEnv("CNB_GROUP_ID", "4321"))
  1075  			h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.id", "some.stack.id"))
  1076  			h.AssertNil(t, baseImage.SetLabel("io.buildpacks.stack.mixins", `["mixinX", "mixinY", "build:mixinA"]`))
  1077  			h.AssertNil(t, baseImage.SetLabel(
  1078  				"io.buildpacks.builder.metadata",
  1079  				`{"description": "some-description", "createdBy": {"name": "some-name", "version": "1.2.3"}, "buildpacks": [{"id": "buildpack-1-id"}, {"id": "buildpack-2-id"}], "groups": [{"buildpacks": [{"id": "buildpack-1-id", "version": "buildpack-1-version", "optional": false}, {"id": "buildpack-2-id", "version": "buildpack-2-version-1", "optional": true}]}], "stack": {"runImage": {"image": "prev/run", "mirrors": ["prev/mirror"]}}, "lifecycle": {"version": "6.6.6"}}`,
  1080  			))
  1081  			h.AssertNil(t, baseImage.SetLabel(
  1082  				"io.buildpacks.buildpack.order",
  1083  				`[{"group": [{"id": "buildpack-1-id", "optional": false}, {"id": "buildpack-2-id", "version": "buildpack-2-version-1", "optional": true}]}]`,
  1084  			))
  1085  
  1086  			builderImage = baseImage
  1087  		})
  1088  
  1089  		when("#FromImage", func() {
  1090  			var bldr *builder.Builder
  1091  
  1092  			it.Before(func() {
  1093  				var err error
  1094  				bldr, err = builder.FromImage(builderImage)
  1095  				h.AssertNil(t, err)
  1096  			})
  1097  
  1098  			it("gets builder from image", func() {
  1099  				h.AssertEq(t, bldr.Buildpacks()[0].ID, "buildpack-1-id")
  1100  				h.AssertEq(t, bldr.Buildpacks()[1].ID, "buildpack-2-id")
  1101  
  1102  				order := bldr.Order()
  1103  				h.AssertEq(t, len(order), 1)
  1104  				h.AssertEq(t, len(order[0].Group), 2)
  1105  				h.AssertEq(t, order[0].Group[0].ID, "buildpack-1-id")
  1106  				h.AssertEq(t, order[0].Group[0].Version, "")
  1107  				h.AssertEq(t, order[0].Group[0].Optional, false)
  1108  				h.AssertEq(t, order[0].Group[1].ID, "buildpack-2-id")
  1109  				h.AssertEq(t, order[0].Group[1].Version, "buildpack-2-version-1")
  1110  				h.AssertEq(t, order[0].Group[1].Optional, true)
  1111  			})
  1112  
  1113  			it("gets mixins from image", func() {
  1114  				h.AssertSliceContainsOnly(t, bldr.Mixins(), "mixinX", "mixinY", "build:mixinA")
  1115  			})
  1116  
  1117  			when("metadata is missing", func() {
  1118  				it.Before(func() {
  1119  					h.AssertNil(t, builderImage.SetLabel(
  1120  						"io.buildpacks.builder.metadata",
  1121  						"",
  1122  					))
  1123  				})
  1124  
  1125  				it("should error", func() {
  1126  					_, err := builder.FromImage(builderImage)
  1127  					h.AssertError(t, err, "missing label 'io.buildpacks.builder.metadata'")
  1128  				})
  1129  			})
  1130  
  1131  			when("#Description", func() {
  1132  				it("return description", func() {
  1133  					h.AssertEq(t, bldr.Description(), "some-description")
  1134  				})
  1135  			})
  1136  
  1137  			when("#CreatedBy", func() {
  1138  				it("return CreatedBy", func() {
  1139  					expectedCreatorMetadata := builder.CreatorMetadata{
  1140  						Name:    "some-name",
  1141  						Version: "1.2.3",
  1142  					}
  1143  					h.AssertEq(t, bldr.CreatedBy(), expectedCreatorMetadata)
  1144  				})
  1145  			})
  1146  
  1147  			when("#Name", func() {
  1148  				it("return Name", func() {
  1149  					h.AssertEq(t, bldr.Name(), "base/image")
  1150  				})
  1151  			})
  1152  
  1153  			when("#Image", func() {
  1154  				it("return Image", func() {
  1155  					h.AssertSameInstance(t, bldr.Image(), baseImage)
  1156  				})
  1157  			})
  1158  
  1159  			when("#Stack", func() {
  1160  				it("return Stack", func() {
  1161  					expectedStack := builder.StackMetadata{
  1162  						RunImage: builder.RunImageMetadata{
  1163  							Image:   "prev/run",
  1164  							Mirrors: []string{"prev/mirror"}}}
  1165  
  1166  					h.AssertEq(t, bldr.Stack(), expectedStack)
  1167  				})
  1168  			})
  1169  
  1170  			when("#UID", func() {
  1171  				it("return UID", func() {
  1172  					h.AssertEq(t, bldr.UID(), 1234)
  1173  				})
  1174  			})
  1175  
  1176  			when("#GID", func() {
  1177  				it("return GID", func() {
  1178  					h.AssertEq(t, bldr.GID(), 4321)
  1179  				})
  1180  			})
  1181  		})
  1182  	})
  1183  }
  1184  
  1185  func assertImageHasBPLayer(t *testing.T, image *fakes.Image, bp dist.Buildpack) {
  1186  	t.Helper()
  1187  
  1188  	dirPath := fmt.Sprintf("/cnb/buildpacks/%s/%s", bp.Descriptor().Info.ID, bp.Descriptor().Info.Version)
  1189  	layerTar, err := image.FindLayerWithPath(dirPath)
  1190  	h.AssertNil(t, err)
  1191  
  1192  	h.AssertOnTarEntry(t, layerTar, dirPath,
  1193  		h.IsDirectory(),
  1194  	)
  1195  
  1196  	h.AssertOnTarEntry(t, layerTar, path.Dir(dirPath),
  1197  		h.IsDirectory(),
  1198  	)
  1199  
  1200  	h.AssertOnTarEntry(t, layerTar, dirPath+"/bin/build",
  1201  		h.ContentEquals("build-contents"),
  1202  	)
  1203  
  1204  	h.AssertOnTarEntry(t, layerTar, dirPath+"/bin/detect",
  1205  		h.ContentEquals("detect-contents"),
  1206  	)
  1207  }
  1208  
  1209  func assertImageHasOrderBpLayer(t *testing.T, image *fakes.Image, bp dist.Buildpack) {
  1210  	t.Helper()
  1211  
  1212  	dirPath := fmt.Sprintf("/cnb/buildpacks/%s/%s", bp.Descriptor().Info.ID, bp.Descriptor().Info.Version)
  1213  	layerTar, err := image.FindLayerWithPath(dirPath)
  1214  	h.AssertNil(t, err)
  1215  
  1216  	h.AssertOnTarEntry(t, layerTar, dirPath,
  1217  		h.IsDirectory(),
  1218  	)
  1219  
  1220  	h.AssertOnTarEntry(t, layerTar, path.Dir(dirPath),
  1221  		h.IsDirectory(),
  1222  	)
  1223  }