github.com/YousefHaggyHeroku/pack@v1.5.5/internal/build/lifecycle_execution_test.go (about)

     1  package build_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"io/ioutil"
     7  	"math/rand"
     8  	"os"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/google/go-containerregistry/pkg/name"
    13  
    14  	"github.com/apex/log"
    15  	"github.com/buildpacks/lifecycle/api"
    16  	"github.com/docker/docker/api/types/container"
    17  	"github.com/docker/docker/client"
    18  	"github.com/heroku/color"
    19  	"github.com/sclevine/spec"
    20  	"github.com/sclevine/spec/report"
    21  
    22  	"github.com/YousefHaggyHeroku/pack/internal/build"
    23  	"github.com/YousefHaggyHeroku/pack/internal/build/fakes"
    24  	ilogging "github.com/YousefHaggyHeroku/pack/internal/logging"
    25  	h "github.com/YousefHaggyHeroku/pack/testhelpers"
    26  )
    27  
    28  // TestLifecycleExecution are unit tests that test each possible phase to ensure they are executed with the proper parameters
    29  func TestLifecycleExecution(t *testing.T) {
    30  	rand.Seed(time.Now().UTC().UnixNano())
    31  
    32  	color.Disable(true)
    33  	defer color.Disable(false)
    34  
    35  	spec.Run(t, "phases", testLifecycleExecution, spec.Report(report.Terminal{}), spec.Sequential())
    36  }
    37  
    38  func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) {
    39  	// Avoid contaminating tests with existing docker configuration.
    40  	// GGCR resolves the default keychain by inspecting DOCKER_CONFIG - this is used by the Analyze step
    41  	// when constructing the auth config (see `auth.BuildEnvVar` in phases.go).
    42  	var dockerConfigDir string
    43  	it.Before(func() {
    44  		var err error
    45  		dockerConfigDir, err = ioutil.TempDir("", "empty-docker-config-dir")
    46  		h.AssertNil(t, err)
    47  
    48  		h.AssertNil(t, os.Setenv("DOCKER_CONFIG", dockerConfigDir))
    49  	})
    50  
    51  	it.After(func() {
    52  		h.AssertNil(t, os.Unsetenv("DOCKER_CONFIG"))
    53  		h.AssertNil(t, os.RemoveAll(dockerConfigDir))
    54  	})
    55  
    56  	when("#NewLifecycleExecution", func() {
    57  		when("lifecycle supports multiple platform APIs", func() {
    58  			it("select the latest supported version", func() {
    59  				fakeBuilder, err := fakes.NewFakeBuilder(fakes.WithSupportedPlatformAPIs([]*api.Version{
    60  					api.MustParse("0.2"),
    61  					api.MustParse("0.3"),
    62  					api.MustParse("0.4"),
    63  					api.MustParse("0.5"),
    64  				}))
    65  				h.AssertNil(t, err)
    66  
    67  				lifecycleExec := newTestLifecycleExec(t, false, fakes.WithBuilder(fakeBuilder))
    68  				h.AssertEq(t, lifecycleExec.PlatformAPI().String(), "0.4")
    69  			})
    70  		})
    71  
    72  		when("supported platform API is deprecated", func() {
    73  			it("select the deprecated version", func() {
    74  				fakeBuilder, err := fakes.NewFakeBuilder(
    75  					fakes.WithDeprecatedPlatformAPIs([]*api.Version{api.MustParse("0.4")}),
    76  					fakes.WithSupportedPlatformAPIs([]*api.Version{api.MustParse("1.2")}),
    77  				)
    78  				h.AssertNil(t, err)
    79  
    80  				lifecycleExec := newTestLifecycleExec(t, false, fakes.WithBuilder(fakeBuilder))
    81  				h.AssertEq(t, lifecycleExec.PlatformAPI().String(), "0.4")
    82  			})
    83  		})
    84  
    85  		when("pack doesn't support any lifecycle supported platform API", func() {
    86  			it("errors", func() {
    87  				fakeBuilder, err := fakes.NewFakeBuilder(
    88  					fakes.WithSupportedPlatformAPIs([]*api.Version{api.MustParse("1.2")}),
    89  				)
    90  				h.AssertNil(t, err)
    91  
    92  				_, err = newTestLifecycleExecErr(t, false, fakes.WithBuilder(fakeBuilder))
    93  				h.AssertError(t, err, "unable to find a supported Platform API version")
    94  			})
    95  		})
    96  	})
    97  
    98  	when("Run", func() {
    99  		var (
   100  			imageName        name.Tag
   101  			fakeBuilder      *fakes.FakeBuilder
   102  			outBuf           bytes.Buffer
   103  			logger           *ilogging.LogWithWriters
   104  			docker           *client.Client
   105  			fakePhaseFactory *fakes.FakePhaseFactory
   106  		)
   107  
   108  		it.Before(func() {
   109  			var err error
   110  			imageName, err = name.NewTag("/some/image", name.WeakValidation)
   111  			h.AssertNil(t, err)
   112  
   113  			fakeBuilder, err = fakes.NewFakeBuilder(fakes.WithSupportedPlatformAPIs([]*api.Version{api.MustParse("0.3")}))
   114  			h.AssertNil(t, err)
   115  			logger = ilogging.NewLogWithWriters(&outBuf, &outBuf)
   116  			docker, err = client.NewClientWithOpts(client.FromEnv, client.WithVersion("1.38"))
   117  			h.AssertNil(t, err)
   118  			fakePhaseFactory = fakes.NewFakePhaseFactory()
   119  		})
   120  
   121  		when("Run using creator", func() {
   122  			it("succeeds", func() {
   123  				opts := build.LifecycleOptions{
   124  					Publish:      false,
   125  					ClearCache:   false,
   126  					RunImage:     "test",
   127  					Image:        imageName,
   128  					Builder:      fakeBuilder,
   129  					TrustBuilder: false,
   130  					UseCreator:   true,
   131  				}
   132  
   133  				lifecycle, err := build.NewLifecycleExecution(logger, docker, opts)
   134  				h.AssertNil(t, err)
   135  
   136  				err = lifecycle.Run(context.Background(), func(execution *build.LifecycleExecution) build.PhaseFactory {
   137  					return fakePhaseFactory
   138  				})
   139  				h.AssertNil(t, err)
   140  
   141  				h.AssertEq(t, len(fakePhaseFactory.NewCalledWithProvider), 1)
   142  
   143  				for _, entry := range fakePhaseFactory.NewCalledWithProvider {
   144  					if entry.Name() == "creator" {
   145  						h.AssertSliceContains(t, entry.ContainerConfig().Cmd, "/some/image")
   146  					}
   147  				}
   148  			})
   149  		})
   150  		when("Run without using creator", func() {
   151  			it("succeeds", func() {
   152  				opts := build.LifecycleOptions{
   153  					Publish:      false,
   154  					ClearCache:   false,
   155  					RunImage:     "test",
   156  					Image:        imageName,
   157  					Builder:      fakeBuilder,
   158  					TrustBuilder: false,
   159  					UseCreator:   false,
   160  				}
   161  
   162  				lifecycle, err := build.NewLifecycleExecution(logger, docker, opts)
   163  				h.AssertNil(t, err)
   164  
   165  				err = lifecycle.Run(context.Background(), func(execution *build.LifecycleExecution) build.PhaseFactory {
   166  					return fakePhaseFactory
   167  				})
   168  				h.AssertNil(t, err)
   169  
   170  				h.AssertEq(t, len(fakePhaseFactory.NewCalledWithProvider), 5)
   171  
   172  				for _, entry := range fakePhaseFactory.NewCalledWithProvider {
   173  					switch entry.Name() {
   174  					case "exporter":
   175  						h.AssertSliceContains(t, entry.ContainerConfig().Cmd, "/some/image")
   176  					case "analyzer":
   177  						h.AssertSliceContains(t, entry.ContainerConfig().Cmd, "/some/image")
   178  					}
   179  				}
   180  			})
   181  		})
   182  	})
   183  
   184  	when("#Create", func() {
   185  		it("creates a phase and then run it", func() {
   186  			lifecycle := newTestLifecycleExec(t, false)
   187  			fakePhase := &fakes.FakePhase{}
   188  			fakePhaseFactory := fakes.NewFakePhaseFactory(fakes.WhichReturnsForNew(fakePhase))
   189  
   190  			err := lifecycle.Create(
   191  				context.Background(),
   192  				false,
   193  				false,
   194  				"test",
   195  				"test",
   196  				"test",
   197  				"test",
   198  				"test",
   199  				[]string{},
   200  				fakePhaseFactory,
   201  			)
   202  			h.AssertNil(t, err)
   203  
   204  			h.AssertEq(t, fakePhase.CleanupCallCount, 1)
   205  			h.AssertEq(t, fakePhase.RunCallCount, 1)
   206  		})
   207  
   208  		it("configures the phase with the expected arguments", func() {
   209  			verboseLifecycle := newTestLifecycleExec(t, true)
   210  			fakePhaseFactory := fakes.NewFakePhaseFactory()
   211  			expectedRepoName := "some-repo-name"
   212  			expectedRunImage := "some-run-image"
   213  
   214  			err := verboseLifecycle.Create(
   215  				context.Background(),
   216  				false,
   217  				false,
   218  				expectedRunImage,
   219  				"test",
   220  				"test",
   221  				expectedRepoName,
   222  				"test",
   223  				[]string{},
   224  				fakePhaseFactory,
   225  			)
   226  			h.AssertNil(t, err)
   227  
   228  			lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   229  			h.AssertNotEq(t, lastCallIndex, -1)
   230  
   231  			configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   232  			h.AssertEq(t, configProvider.Name(), "creator")
   233  			h.AssertIncludeAllExpectedPatterns(t,
   234  				configProvider.ContainerConfig().Cmd,
   235  				[]string{"-log-level", "debug"},
   236  				[]string{"-run-image", expectedRunImage},
   237  				[]string{expectedRepoName},
   238  			)
   239  		})
   240  
   241  		it("configures the phase with the expected network mode", func() {
   242  			lifecycle := newTestLifecycleExec(t, false)
   243  			fakePhaseFactory := fakes.NewFakePhaseFactory()
   244  			expectedNetworkMode := "some-network-mode"
   245  
   246  			err := lifecycle.Create(
   247  				context.Background(),
   248  				false,
   249  				false,
   250  				"test",
   251  				"test",
   252  				"test",
   253  				"test",
   254  				expectedNetworkMode,
   255  				[]string{},
   256  				fakePhaseFactory,
   257  			)
   258  			h.AssertNil(t, err)
   259  
   260  			lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   261  			h.AssertNotEq(t, lastCallIndex, -1)
   262  
   263  			configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   264  			h.AssertEq(t, configProvider.HostConfig().NetworkMode, container.NetworkMode(expectedNetworkMode))
   265  		})
   266  
   267  		when("clear cache", func() {
   268  			it("configures the phase with the expected arguments", func() {
   269  				verboseLifecycle := newTestLifecycleExec(t, true)
   270  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   271  
   272  				err := verboseLifecycle.Create(
   273  					context.Background(),
   274  					false,
   275  					true,
   276  					"test",
   277  					"test",
   278  					"test",
   279  					"test",
   280  					"test",
   281  					[]string{},
   282  					fakePhaseFactory,
   283  				)
   284  				h.AssertNil(t, err)
   285  
   286  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   287  				h.AssertNotEq(t, lastCallIndex, -1)
   288  
   289  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   290  				h.AssertEq(t, configProvider.Name(), "creator")
   291  				h.AssertIncludeAllExpectedPatterns(t,
   292  					configProvider.ContainerConfig().Cmd,
   293  					[]string{"-skip-restore"},
   294  				)
   295  			})
   296  		})
   297  
   298  		when("clear cache is false", func() {
   299  			it("configures the phase with the expected arguments", func() {
   300  				verboseLifecycle := newTestLifecycleExec(t, true)
   301  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   302  
   303  				err := verboseLifecycle.Create(
   304  					context.Background(),
   305  					false,
   306  					false,
   307  					"test",
   308  					"test",
   309  					"test",
   310  					"test",
   311  					"test",
   312  					[]string{},
   313  					fakePhaseFactory,
   314  				)
   315  				h.AssertNil(t, err)
   316  
   317  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   318  				h.AssertNotEq(t, lastCallIndex, -1)
   319  
   320  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   321  				h.AssertEq(t, configProvider.Name(), "creator")
   322  				h.AssertIncludeAllExpectedPatterns(t,
   323  					configProvider.ContainerConfig().Cmd,
   324  					[]string{"-cache-dir", "/cache"},
   325  				)
   326  			})
   327  		})
   328  
   329  		when("publish", func() {
   330  			it("configures the phase with binds", func() {
   331  				lifecycle := newTestLifecycleExec(t, false)
   332  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   333  				volumeMount := "custom-mount-source:/custom-mount-target"
   334  				expectedBinds := []string{volumeMount, "some-cache:/cache"}
   335  
   336  				err := lifecycle.Create(
   337  					context.Background(),
   338  					true,
   339  					false,
   340  					"test",
   341  					"test",
   342  					"some-cache",
   343  					"test",
   344  					"test",
   345  					[]string{volumeMount},
   346  					fakePhaseFactory,
   347  				)
   348  				h.AssertNil(t, err)
   349  
   350  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   351  				h.AssertNotEq(t, lastCallIndex, -1)
   352  
   353  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   354  				h.AssertSliceContains(t, configProvider.HostConfig().Binds, expectedBinds...)
   355  			})
   356  
   357  			it("configures the phase with root", func() {
   358  				lifecycle := newTestLifecycleExec(t, false)
   359  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   360  
   361  				err := lifecycle.Create(
   362  					context.Background(),
   363  					true,
   364  					false,
   365  					"test",
   366  					"test",
   367  					"test",
   368  					"test",
   369  					"test",
   370  					[]string{},
   371  					fakePhaseFactory,
   372  				)
   373  				h.AssertNil(t, err)
   374  
   375  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   376  				h.AssertNotEq(t, lastCallIndex, -1)
   377  
   378  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   379  				h.AssertEq(t, configProvider.ContainerConfig().User, "root")
   380  			})
   381  
   382  			it("configures the phase with registry access", func() {
   383  				lifecycle := newTestLifecycleExec(t, false)
   384  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   385  				expectedRepos := "some-repo-name"
   386  
   387  				err := lifecycle.Create(
   388  					context.Background(),
   389  					true,
   390  					false,
   391  					"test",
   392  					"test",
   393  					"test",
   394  					expectedRepos,
   395  					"test",
   396  					[]string{},
   397  					fakePhaseFactory,
   398  				)
   399  				h.AssertNil(t, err)
   400  
   401  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   402  				h.AssertNotEq(t, lastCallIndex, -1)
   403  
   404  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   405  				h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "CNB_REGISTRY_AUTH={}")
   406  			})
   407  
   408  			when("platform 0.3", func() {
   409  				it("doesn't hint at default process type", func() {
   410  					fakeBuilder, err := fakes.NewFakeBuilder(fakes.WithSupportedPlatformAPIs([]*api.Version{api.MustParse("0.3")}))
   411  					h.AssertNil(t, err)
   412  					lifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder))
   413  					fakePhaseFactory := fakes.NewFakePhaseFactory()
   414  
   415  					err = lifecycle.Export(context.Background(), "test", "test", true, "test", "test", "test", fakePhaseFactory)
   416  					h.AssertNil(t, err)
   417  
   418  					lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   419  					h.AssertNotEq(t, lastCallIndex, -1)
   420  
   421  					configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   422  					h.AssertSliceNotContains(t, configProvider.ContainerConfig().Cmd, "-process-type")
   423  				})
   424  			})
   425  
   426  			when("platform 0.4", func() {
   427  				it("hints at default process type", func() {
   428  					fakeBuilder, err := fakes.NewFakeBuilder(fakes.WithSupportedPlatformAPIs([]*api.Version{api.MustParse("0.4")}))
   429  					h.AssertNil(t, err)
   430  					lifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder))
   431  					fakePhaseFactory := fakes.NewFakePhaseFactory()
   432  
   433  					err = lifecycle.Export(context.Background(), "test", "test", true, "test", "test", "test", fakePhaseFactory)
   434  					h.AssertNil(t, err)
   435  
   436  					lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   437  					h.AssertNotEq(t, lastCallIndex, -1)
   438  
   439  					configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   440  					h.AssertIncludeAllExpectedPatterns(t, configProvider.ContainerConfig().Cmd, []string{"-process-type", "web"})
   441  				})
   442  			})
   443  		})
   444  
   445  		when("publish is false", func() {
   446  			it("configures the phase with the expected arguments", func() {
   447  				verboseLifecycle := newTestLifecycleExec(t, true)
   448  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   449  
   450  				err := verboseLifecycle.Create(
   451  					context.Background(),
   452  					false,
   453  					false,
   454  					"test",
   455  					"test",
   456  					"test",
   457  					"test",
   458  					"test",
   459  					[]string{},
   460  					fakePhaseFactory,
   461  				)
   462  				h.AssertNil(t, err)
   463  
   464  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   465  				h.AssertNotEq(t, lastCallIndex, -1)
   466  
   467  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   468  				h.AssertEq(t, configProvider.Name(), "creator")
   469  				h.AssertIncludeAllExpectedPatterns(t,
   470  					configProvider.ContainerConfig().Cmd,
   471  					[]string{"-daemon"},
   472  					[]string{"-launch-cache", "/launch-cache"},
   473  				)
   474  			})
   475  
   476  			it("configures the phase with daemon access", func() {
   477  				lifecycle := newTestLifecycleExec(t, false)
   478  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   479  
   480  				err := lifecycle.Create(
   481  					context.Background(),
   482  					false,
   483  					false,
   484  					"test",
   485  					"some-launch-cache",
   486  					"some-cache",
   487  					"test",
   488  					"test",
   489  					[]string{},
   490  					fakePhaseFactory,
   491  				)
   492  				h.AssertNil(t, err)
   493  
   494  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   495  				h.AssertNotEq(t, lastCallIndex, -1)
   496  
   497  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   498  				h.AssertEq(t, configProvider.ContainerConfig().User, "root")
   499  				h.AssertSliceContains(t, configProvider.HostConfig().Binds, "/var/run/docker.sock:/var/run/docker.sock")
   500  			})
   501  
   502  			it("configures the phase with binds", func() {
   503  				lifecycle := newTestLifecycleExec(t, false)
   504  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   505  				volumeMount := "custom-mount-source:/custom-mount-target"
   506  				expectedBinds := []string{volumeMount, "some-cache:/cache", "some-launch-cache:/launch-cache"}
   507  
   508  				err := lifecycle.Create(
   509  					context.Background(),
   510  					false,
   511  					false,
   512  					"test",
   513  					"some-launch-cache",
   514  					"some-cache",
   515  					"test",
   516  					"test",
   517  					[]string{volumeMount},
   518  					fakePhaseFactory,
   519  				)
   520  				h.AssertNil(t, err)
   521  
   522  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   523  				h.AssertNotEq(t, lastCallIndex, -1)
   524  
   525  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   526  				h.AssertSliceContains(t, configProvider.HostConfig().Binds, expectedBinds...)
   527  			})
   528  
   529  			when("platform 0.3", func() {
   530  				it("doesn't hint at default process type", func() {
   531  					fakeBuilder, err := fakes.NewFakeBuilder(fakes.WithSupportedPlatformAPIs([]*api.Version{api.MustParse("0.3")}))
   532  					h.AssertNil(t, err)
   533  					lifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder))
   534  					fakePhaseFactory := fakes.NewFakePhaseFactory()
   535  
   536  					err = lifecycle.Export(context.Background(), "test", "test", false, "test", "test", "test", fakePhaseFactory)
   537  					h.AssertNil(t, err)
   538  
   539  					lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   540  					h.AssertNotEq(t, lastCallIndex, -1)
   541  
   542  					configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   543  					h.AssertSliceNotContains(t, configProvider.ContainerConfig().Cmd, "-process-type")
   544  				})
   545  			})
   546  
   547  			when("platform 0.4", func() {
   548  				it("hints at default process type", func() {
   549  					fakeBuilder, err := fakes.NewFakeBuilder(fakes.WithSupportedPlatformAPIs([]*api.Version{api.MustParse("0.4")}))
   550  					h.AssertNil(t, err)
   551  					lifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder))
   552  					fakePhaseFactory := fakes.NewFakePhaseFactory()
   553  
   554  					err = lifecycle.Export(context.Background(), "test", "test", false, "test", "test", "test", fakePhaseFactory)
   555  					h.AssertNil(t, err)
   556  
   557  					lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   558  					h.AssertNotEq(t, lastCallIndex, -1)
   559  
   560  					configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   561  					h.AssertIncludeAllExpectedPatterns(t, configProvider.ContainerConfig().Cmd, []string{"-process-type", "web"})
   562  				})
   563  			})
   564  		})
   565  	})
   566  
   567  	when("#Detect", func() {
   568  		it("creates a phase and then runs it", func() {
   569  			lifecycle := newTestLifecycleExec(t, false)
   570  			fakePhase := &fakes.FakePhase{}
   571  			fakePhaseFactory := fakes.NewFakePhaseFactory(fakes.WhichReturnsForNew(fakePhase))
   572  
   573  			err := lifecycle.Detect(context.Background(), "test", []string{}, fakePhaseFactory)
   574  			h.AssertNil(t, err)
   575  
   576  			h.AssertEq(t, fakePhase.CleanupCallCount, 1)
   577  			h.AssertEq(t, fakePhase.RunCallCount, 1)
   578  		})
   579  
   580  		it("configures the phase with the expected arguments", func() {
   581  			verboseLifecycle := newTestLifecycleExec(t, true)
   582  			fakePhaseFactory := fakes.NewFakePhaseFactory()
   583  
   584  			err := verboseLifecycle.Detect(context.Background(), "test", []string{"test"}, fakePhaseFactory)
   585  			h.AssertNil(t, err)
   586  
   587  			lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   588  			h.AssertNotEq(t, lastCallIndex, -1)
   589  
   590  			configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   591  			h.AssertEq(t, configProvider.Name(), "detector")
   592  			h.AssertIncludeAllExpectedPatterns(t,
   593  				configProvider.ContainerConfig().Cmd,
   594  				[]string{"-log-level", "debug"},
   595  				[]string{"-app", "/workspace"},
   596  				[]string{"-platform", "/platform"},
   597  			)
   598  		})
   599  
   600  		it("configures the phase with the expected network mode", func() {
   601  			lifecycle := newTestLifecycleExec(t, false)
   602  			fakePhaseFactory := fakes.NewFakePhaseFactory()
   603  			expectedNetworkMode := "some-network-mode"
   604  
   605  			err := lifecycle.Detect(context.Background(), expectedNetworkMode, []string{}, fakePhaseFactory)
   606  			h.AssertNil(t, err)
   607  
   608  			lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   609  			h.AssertNotEq(t, lastCallIndex, -1)
   610  
   611  			configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   612  			h.AssertEq(t, configProvider.HostConfig().NetworkMode, container.NetworkMode(expectedNetworkMode))
   613  		})
   614  
   615  		it("configures the phase to copy app dir", func() {
   616  			lifecycle := newTestLifecycleExec(t, false)
   617  			fakePhaseFactory := fakes.NewFakePhaseFactory()
   618  			expectedBind := "some-mount-source:/some-mount-target"
   619  
   620  			err := lifecycle.Detect(context.Background(), "test", []string{expectedBind}, fakePhaseFactory)
   621  			h.AssertNil(t, err)
   622  
   623  			lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   624  			h.AssertNotEq(t, lastCallIndex, -1)
   625  
   626  			configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   627  			h.AssertSliceContains(t, configProvider.HostConfig().Binds, expectedBind)
   628  
   629  			h.AssertEq(t, len(configProvider.ContainerOps()), 2)
   630  			h.AssertFunctionName(t, configProvider.ContainerOps()[0], "EnsureVolumeAccess")
   631  			h.AssertFunctionName(t, configProvider.ContainerOps()[1], "CopyDir")
   632  		})
   633  	})
   634  
   635  	when("#Analyze", func() {
   636  		it("creates a phase and then runs it", func() {
   637  			lifecycle := newTestLifecycleExec(t, false)
   638  			fakePhase := &fakes.FakePhase{}
   639  			fakePhaseFactory := fakes.NewFakePhaseFactory(fakes.WhichReturnsForNew(fakePhase))
   640  
   641  			err := lifecycle.Analyze(context.Background(), "test", "test", "test", false, false, fakePhaseFactory)
   642  			h.AssertNil(t, err)
   643  
   644  			h.AssertEq(t, fakePhase.CleanupCallCount, 1)
   645  			h.AssertEq(t, fakePhase.RunCallCount, 1)
   646  		})
   647  
   648  		when("clear cache", func() {
   649  			it("configures the phase with the expected arguments", func() {
   650  				lifecycle := newTestLifecycleExec(t, false)
   651  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   652  				expectedRepoName := "some-repo-name"
   653  
   654  				err := lifecycle.Analyze(context.Background(), expectedRepoName, "test", "test", false, true, fakePhaseFactory)
   655  				h.AssertNil(t, err)
   656  
   657  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   658  				h.AssertNotEq(t, lastCallIndex, -1)
   659  
   660  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   661  				h.AssertEq(t, configProvider.Name(), "analyzer")
   662  				h.AssertSliceContains(t, configProvider.ContainerConfig().Cmd, "-skip-layers")
   663  			})
   664  		})
   665  
   666  		when("clear cache is false", func() {
   667  			it("configures the phase with the expected arguments", func() {
   668  				lifecycle := newTestLifecycleExec(t, false)
   669  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   670  				expectedRepoName := "some-repo-name"
   671  
   672  				err := lifecycle.Analyze(context.Background(), expectedRepoName, "test", "test", false, false, fakePhaseFactory)
   673  				h.AssertNil(t, err)
   674  
   675  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   676  				h.AssertNotEq(t, lastCallIndex, -1)
   677  
   678  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   679  				h.AssertEq(t, configProvider.Name(), "analyzer")
   680  				h.AssertIncludeAllExpectedPatterns(t,
   681  					configProvider.ContainerConfig().Cmd,
   682  					[]string{"-cache-dir", "/cache"},
   683  				)
   684  			})
   685  		})
   686  
   687  		when("publish", func() {
   688  			it("runs the phase with the lifecycle image", func() {
   689  				lifecycle := newTestLifecycleExec(t, true, func(options *build.LifecycleOptions) {
   690  					options.LifecycleImage = "some-lifecycle-image"
   691  				})
   692  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   693  
   694  				err := lifecycle.Analyze(context.Background(), "test", "test", "test", true, false, fakePhaseFactory)
   695  				h.AssertNil(t, err)
   696  
   697  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   698  				h.AssertNotEq(t, lastCallIndex, -1)
   699  
   700  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   701  				h.AssertEq(t, configProvider.ContainerConfig().Image, "some-lifecycle-image")
   702  			})
   703  
   704  			it("sets the CNB_USER_ID and CNB_GROUP_ID in the environment", func() {
   705  				fakeBuilder, err := fakes.NewFakeBuilder(fakes.WithUID(2222), fakes.WithGID(3333))
   706  				h.AssertNil(t, err)
   707  				lifecycle := newTestLifecycleExec(t, false, fakes.WithBuilder(fakeBuilder))
   708  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   709  
   710  				err = lifecycle.Analyze(context.Background(), "test", "test", "test", true, false, fakePhaseFactory)
   711  				h.AssertNil(t, err)
   712  
   713  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   714  				h.AssertNotEq(t, lastCallIndex, -1)
   715  
   716  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   717  				h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "CNB_USER_ID=2222")
   718  				h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "CNB_GROUP_ID=3333")
   719  			})
   720  
   721  			it("configures the phase with registry access", func() {
   722  				lifecycle := newTestLifecycleExec(t, false)
   723  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   724  				expectedRepos := "some-repo-name"
   725  				expectedNetworkMode := "some-network-mode"
   726  
   727  				err := lifecycle.Analyze(context.Background(), expectedRepos, "test", expectedNetworkMode, true, false, fakePhaseFactory)
   728  				h.AssertNil(t, err)
   729  
   730  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   731  				h.AssertNotEq(t, lastCallIndex, -1)
   732  
   733  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   734  				h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "CNB_REGISTRY_AUTH={}")
   735  				h.AssertEq(t, configProvider.HostConfig().NetworkMode, container.NetworkMode(expectedNetworkMode))
   736  			})
   737  
   738  			it("configures the phase with root", func() {
   739  				lifecycle := newTestLifecycleExec(t, false)
   740  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   741  
   742  				err := lifecycle.Analyze(context.Background(), "test", "test", "test", true, false, fakePhaseFactory)
   743  				h.AssertNil(t, err)
   744  
   745  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   746  				h.AssertNotEq(t, lastCallIndex, -1)
   747  
   748  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   749  				h.AssertEq(t, configProvider.ContainerConfig().User, "root")
   750  			})
   751  
   752  			it("configures the phase with the expected arguments", func() {
   753  				verboseLifecycle := newTestLifecycleExec(t, true)
   754  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   755  				expectedRepoName := "some-repo-name"
   756  
   757  				err := verboseLifecycle.Analyze(context.Background(), expectedRepoName, "test", "test", true, false, fakePhaseFactory)
   758  				h.AssertNil(t, err)
   759  
   760  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   761  				h.AssertNotEq(t, lastCallIndex, -1)
   762  
   763  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   764  				h.AssertEq(t, configProvider.Name(), "analyzer")
   765  				h.AssertIncludeAllExpectedPatterns(t,
   766  					configProvider.ContainerConfig().Cmd,
   767  					[]string{"-log-level", "debug"},
   768  					[]string{"-layers", "/layers"},
   769  					[]string{expectedRepoName},
   770  				)
   771  			})
   772  
   773  			it("configures the phase with binds", func() {
   774  				lifecycle := newTestLifecycleExec(t, false)
   775  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   776  				expectedBind := "some-cache:/cache"
   777  
   778  				err := lifecycle.Analyze(context.Background(), "test", "some-cache", "test", true, false, fakePhaseFactory)
   779  				h.AssertNil(t, err)
   780  
   781  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   782  				h.AssertNotEq(t, lastCallIndex, -1)
   783  
   784  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   785  				h.AssertSliceContains(t, configProvider.HostConfig().Binds, expectedBind)
   786  			})
   787  		})
   788  
   789  		when("publish is false", func() {
   790  			it("runs the phase with the lifecycle image", func() {
   791  				lifecycle := newTestLifecycleExec(t, true, func(options *build.LifecycleOptions) {
   792  					options.LifecycleImage = "some-lifecycle-image"
   793  				})
   794  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   795  
   796  				err := lifecycle.Analyze(context.Background(), "test", "test", "test", false, false, fakePhaseFactory)
   797  				h.AssertNil(t, err)
   798  
   799  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   800  				h.AssertNotEq(t, lastCallIndex, -1)
   801  
   802  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   803  				h.AssertEq(t, configProvider.ContainerConfig().Image, "some-lifecycle-image")
   804  			})
   805  
   806  			it("sets the CNB_USER_ID and CNB_GROUP_ID in the environment", func() {
   807  				fakeBuilder, err := fakes.NewFakeBuilder(fakes.WithUID(2222), fakes.WithGID(3333))
   808  				h.AssertNil(t, err)
   809  				lifecycle := newTestLifecycleExec(t, false, fakes.WithBuilder(fakeBuilder))
   810  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   811  
   812  				err = lifecycle.Analyze(context.Background(), "test", "test", "test", false, false, fakePhaseFactory)
   813  				h.AssertNil(t, err)
   814  
   815  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   816  				h.AssertNotEq(t, lastCallIndex, -1)
   817  
   818  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   819  				h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "CNB_USER_ID=2222")
   820  				h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "CNB_GROUP_ID=3333")
   821  			})
   822  
   823  			it("configures the phase with daemon access", func() {
   824  				lifecycle := newTestLifecycleExec(t, false)
   825  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   826  
   827  				err := lifecycle.Analyze(context.Background(), "test", "test", "test", false, false, fakePhaseFactory)
   828  				h.AssertNil(t, err)
   829  
   830  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   831  				h.AssertNotEq(t, lastCallIndex, -1)
   832  
   833  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   834  				h.AssertEq(t, configProvider.ContainerConfig().User, "root")
   835  				h.AssertSliceContains(t, configProvider.HostConfig().Binds, "/var/run/docker.sock:/var/run/docker.sock")
   836  			})
   837  
   838  			it("configures the phase with the expected arguments", func() {
   839  				verboseLifecycle := newTestLifecycleExec(t, true)
   840  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   841  				expectedRepoName := "some-repo-name"
   842  
   843  				err := verboseLifecycle.Analyze(context.Background(), expectedRepoName, "test", "test", false, true, fakePhaseFactory)
   844  				h.AssertNil(t, err)
   845  
   846  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   847  				h.AssertNotEq(t, lastCallIndex, -1)
   848  
   849  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   850  				h.AssertEq(t, configProvider.Name(), "analyzer")
   851  				h.AssertIncludeAllExpectedPatterns(t,
   852  					configProvider.ContainerConfig().Cmd,
   853  					[]string{"-log-level", "debug"},
   854  					[]string{"-daemon"},
   855  					[]string{"-layers", "/layers"},
   856  					[]string{expectedRepoName},
   857  				)
   858  			})
   859  
   860  			it("configures the phase with the expected network mode", func() {
   861  				lifecycle := newTestLifecycleExec(t, false)
   862  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   863  				expectedNetworkMode := "some-network-mode"
   864  
   865  				err := lifecycle.Analyze(context.Background(), "test", "test", expectedNetworkMode, false, false, fakePhaseFactory)
   866  				h.AssertNil(t, err)
   867  
   868  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   869  				h.AssertNotEq(t, lastCallIndex, -1)
   870  
   871  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   872  				h.AssertEq(t, configProvider.HostConfig().NetworkMode, container.NetworkMode(expectedNetworkMode))
   873  			})
   874  
   875  			it("configures the phase with binds", func() {
   876  				lifecycle := newTestLifecycleExec(t, false)
   877  				fakePhaseFactory := fakes.NewFakePhaseFactory()
   878  				expectedBind := "some-cache:/cache"
   879  
   880  				err := lifecycle.Analyze(context.Background(), "test", "some-cache", "test", false, true, fakePhaseFactory)
   881  				h.AssertNil(t, err)
   882  
   883  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   884  				h.AssertNotEq(t, lastCallIndex, -1)
   885  
   886  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   887  				h.AssertSliceContains(t, configProvider.HostConfig().Binds, expectedBind)
   888  			})
   889  		})
   890  	})
   891  
   892  	when("#Restore", func() {
   893  		it("runs the phase with the lifecycle image", func() {
   894  			lifecycle := newTestLifecycleExec(t, true, func(options *build.LifecycleOptions) {
   895  				options.LifecycleImage = "some-lifecycle-image"
   896  			})
   897  			fakePhaseFactory := fakes.NewFakePhaseFactory()
   898  
   899  			err := lifecycle.Restore(context.Background(), "test", "test", fakePhaseFactory)
   900  			h.AssertNil(t, err)
   901  
   902  			lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   903  			h.AssertNotEq(t, lastCallIndex, -1)
   904  
   905  			configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   906  			h.AssertEq(t, configProvider.ContainerConfig().Image, "some-lifecycle-image")
   907  		})
   908  
   909  		it("sets the CNB_USER_ID and CNB_GROUP_ID in the environment", func() {
   910  			fakeBuilder, err := fakes.NewFakeBuilder(fakes.WithUID(2222), fakes.WithGID(3333))
   911  			h.AssertNil(t, err)
   912  			lifecycle := newTestLifecycleExec(t, false, fakes.WithBuilder(fakeBuilder))
   913  			fakePhaseFactory := fakes.NewFakePhaseFactory()
   914  
   915  			err = lifecycle.Restore(context.Background(), "test", "test", fakePhaseFactory)
   916  			h.AssertNil(t, err)
   917  
   918  			lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   919  			h.AssertNotEq(t, lastCallIndex, -1)
   920  
   921  			configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   922  			h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "CNB_USER_ID=2222")
   923  			h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "CNB_GROUP_ID=3333")
   924  		})
   925  
   926  		it("creates a phase and then runs it", func() {
   927  			lifecycle := newTestLifecycleExec(t, false)
   928  			fakePhase := &fakes.FakePhase{}
   929  			fakePhaseFactory := fakes.NewFakePhaseFactory(fakes.WhichReturnsForNew(fakePhase))
   930  
   931  			err := lifecycle.Restore(context.Background(), "test", "test", fakePhaseFactory)
   932  			h.AssertNil(t, err)
   933  
   934  			h.AssertEq(t, fakePhase.CleanupCallCount, 1)
   935  			h.AssertEq(t, fakePhase.RunCallCount, 1)
   936  		})
   937  
   938  		it("configures the phase with root access", func() {
   939  			lifecycle := newTestLifecycleExec(t, false)
   940  			fakePhaseFactory := fakes.NewFakePhaseFactory()
   941  
   942  			err := lifecycle.Restore(context.Background(), "test", "test", fakePhaseFactory)
   943  			h.AssertNil(t, err)
   944  
   945  			lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   946  			h.AssertNotEq(t, lastCallIndex, -1)
   947  
   948  			configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   949  			h.AssertEq(t, configProvider.ContainerConfig().User, "root")
   950  		})
   951  
   952  		it("configures the phase with the expected arguments", func() {
   953  			verboseLifecycle := newTestLifecycleExec(t, true)
   954  			fakePhaseFactory := fakes.NewFakePhaseFactory()
   955  
   956  			err := verboseLifecycle.Restore(context.Background(), "test", "test", fakePhaseFactory)
   957  			h.AssertNil(t, err)
   958  
   959  			lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   960  			h.AssertNotEq(t, lastCallIndex, -1)
   961  
   962  			configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   963  			h.AssertEq(t, configProvider.Name(), "restorer")
   964  			h.AssertIncludeAllExpectedPatterns(t,
   965  				configProvider.ContainerConfig().Cmd,
   966  				[]string{"-log-level", "debug"},
   967  				[]string{"-cache-dir", "/cache"},
   968  				[]string{"-layers", "/layers"},
   969  			)
   970  		})
   971  
   972  		it("configures the phase with the expected network mode", func() {
   973  			lifecycle := newTestLifecycleExec(t, false)
   974  			fakePhaseFactory := fakes.NewFakePhaseFactory()
   975  			expectedNetworkMode := "some-network-mode"
   976  
   977  			err := lifecycle.Restore(context.Background(), "test", expectedNetworkMode, fakePhaseFactory)
   978  			h.AssertNil(t, err)
   979  
   980  			lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   981  			h.AssertNotEq(t, lastCallIndex, -1)
   982  
   983  			configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   984  			h.AssertEq(t, configProvider.HostConfig().NetworkMode, container.NetworkMode(expectedNetworkMode))
   985  		})
   986  
   987  		it("configures the phase with binds", func() {
   988  			lifecycle := newTestLifecycleExec(t, false)
   989  			fakePhaseFactory := fakes.NewFakePhaseFactory()
   990  			expectedBind := "some-cache:/cache"
   991  
   992  			err := lifecycle.Restore(context.Background(), "some-cache", "test", fakePhaseFactory)
   993  			h.AssertNil(t, err)
   994  
   995  			lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
   996  			h.AssertNotEq(t, lastCallIndex, -1)
   997  
   998  			configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
   999  			h.AssertSliceContains(t, configProvider.HostConfig().Binds, expectedBind)
  1000  		})
  1001  	})
  1002  
  1003  	when("#Build", func() {
  1004  		it("creates a phase and then runs it", func() {
  1005  			lifecycle := newTestLifecycleExec(t, false)
  1006  			fakePhase := &fakes.FakePhase{}
  1007  			fakePhaseFactory := fakes.NewFakePhaseFactory(fakes.WhichReturnsForNew(fakePhase))
  1008  
  1009  			err := lifecycle.Build(context.Background(), "test", []string{}, fakePhaseFactory)
  1010  			h.AssertNil(t, err)
  1011  
  1012  			h.AssertEq(t, fakePhase.CleanupCallCount, 1)
  1013  			h.AssertEq(t, fakePhase.RunCallCount, 1)
  1014  		})
  1015  
  1016  		it("configures the phase with the expected arguments", func() {
  1017  			fakeBuilder, err := fakes.NewFakeBuilder()
  1018  			h.AssertNil(t, err)
  1019  			verboseLifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder))
  1020  			fakePhaseFactory := fakes.NewFakePhaseFactory()
  1021  
  1022  			err = verboseLifecycle.Build(context.Background(), "test", []string{}, fakePhaseFactory)
  1023  			h.AssertNil(t, err)
  1024  
  1025  			lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1026  			h.AssertNotEq(t, lastCallIndex, -1)
  1027  
  1028  			configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1029  			h.AssertEq(t, configProvider.Name(), "builder")
  1030  			h.AssertIncludeAllExpectedPatterns(t,
  1031  				configProvider.ContainerConfig().Cmd,
  1032  				[]string{"-log-level", "debug"},
  1033  				[]string{"-layers", "/layers"},
  1034  				[]string{"-app", "/workspace"},
  1035  				[]string{"-platform", "/platform"},
  1036  			)
  1037  		})
  1038  
  1039  		it("configures the phase with the expected network mode", func() {
  1040  			lifecycle := newTestLifecycleExec(t, false)
  1041  			fakePhaseFactory := fakes.NewFakePhaseFactory()
  1042  			expectedNetworkMode := "some-network-mode"
  1043  
  1044  			err := lifecycle.Build(context.Background(), expectedNetworkMode, []string{}, fakePhaseFactory)
  1045  			h.AssertNil(t, err)
  1046  
  1047  			lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1048  			h.AssertNotEq(t, lastCallIndex, -1)
  1049  
  1050  			configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1051  			h.AssertEq(t, configProvider.HostConfig().NetworkMode, container.NetworkMode(expectedNetworkMode))
  1052  		})
  1053  
  1054  		it("configures the phase with binds", func() {
  1055  			lifecycle := newTestLifecycleExec(t, false)
  1056  			fakePhaseFactory := fakes.NewFakePhaseFactory()
  1057  			expectedBind := "some-mount-source:/some-mount-target"
  1058  
  1059  			err := lifecycle.Build(context.Background(), "test", []string{expectedBind}, fakePhaseFactory)
  1060  			h.AssertNil(t, err)
  1061  
  1062  			lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1063  			h.AssertNotEq(t, lastCallIndex, -1)
  1064  
  1065  			configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1066  			h.AssertSliceContains(t, configProvider.HostConfig().Binds, expectedBind)
  1067  		})
  1068  	})
  1069  
  1070  	when("#Export", func() {
  1071  		it("creates a phase and then runs it", func() {
  1072  			lifecycle := newTestLifecycleExec(t, false)
  1073  			fakePhase := &fakes.FakePhase{}
  1074  			fakePhaseFactory := fakes.NewFakePhaseFactory(fakes.WhichReturnsForNew(fakePhase))
  1075  
  1076  			err := lifecycle.Export(context.Background(), "test", "test", false, "test", "test", "test", fakePhaseFactory)
  1077  			h.AssertNil(t, err)
  1078  
  1079  			h.AssertEq(t, fakePhase.CleanupCallCount, 1)
  1080  			h.AssertEq(t, fakePhase.RunCallCount, 1)
  1081  		})
  1082  
  1083  		it("configures the phase with the expected arguments", func() {
  1084  			verboseLifecycle := newTestLifecycleExec(t, true)
  1085  			fakePhaseFactory := fakes.NewFakePhaseFactory()
  1086  			expectedRepoName := "some-repo-name"
  1087  			expectedRunImage := "some-run-image"
  1088  
  1089  			err := verboseLifecycle.Export(context.Background(), expectedRepoName, expectedRunImage, false, "test", "test", "test", fakePhaseFactory)
  1090  			h.AssertNil(t, err)
  1091  
  1092  			lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1093  			h.AssertNotEq(t, lastCallIndex, -1)
  1094  
  1095  			configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1096  			h.AssertEq(t, configProvider.Name(), "exporter")
  1097  			h.AssertIncludeAllExpectedPatterns(t,
  1098  				configProvider.ContainerConfig().Cmd,
  1099  				[]string{"-log-level", "debug"},
  1100  				[]string{"-cache-dir", "/cache"},
  1101  				[]string{"-layers", "/layers"},
  1102  				[]string{"-app", "/workspace"},
  1103  				[]string{"-run-image", expectedRunImage},
  1104  				[]string{expectedRepoName},
  1105  			)
  1106  		})
  1107  
  1108  		when("publish", func() {
  1109  			it("runs the phase with the lifecycle image", func() {
  1110  				lifecycle := newTestLifecycleExec(t, true, func(options *build.LifecycleOptions) {
  1111  					options.LifecycleImage = "some-lifecycle-image"
  1112  				})
  1113  				fakePhaseFactory := fakes.NewFakePhaseFactory()
  1114  
  1115  				err := lifecycle.Export(context.Background(), "test", "test", true, "test", "test", "test", fakePhaseFactory)
  1116  				h.AssertNil(t, err)
  1117  
  1118  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1119  				h.AssertNotEq(t, lastCallIndex, -1)
  1120  
  1121  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1122  				h.AssertEq(t, configProvider.ContainerConfig().Image, "some-lifecycle-image")
  1123  			})
  1124  
  1125  			it("sets the CNB_USER_ID and CNB_GROUP_ID in the environment", func() {
  1126  				fakeBuilder, err := fakes.NewFakeBuilder(fakes.WithUID(2222), fakes.WithGID(3333))
  1127  				h.AssertNil(t, err)
  1128  				lifecycle := newTestLifecycleExec(t, false, fakes.WithBuilder(fakeBuilder))
  1129  				fakePhaseFactory := fakes.NewFakePhaseFactory()
  1130  
  1131  				err = lifecycle.Export(context.Background(), "test", "test", true, "test", "test", "test", fakePhaseFactory)
  1132  				h.AssertNil(t, err)
  1133  
  1134  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1135  				h.AssertNotEq(t, lastCallIndex, -1)
  1136  
  1137  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1138  				h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "CNB_USER_ID=2222")
  1139  				h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "CNB_GROUP_ID=3333")
  1140  			})
  1141  
  1142  			it("configures the phase with registry access", func() {
  1143  				lifecycle := newTestLifecycleExec(t, false)
  1144  				fakePhaseFactory := fakes.NewFakePhaseFactory()
  1145  				expectedRepos := []string{"some-repo-name", "some-run-image"}
  1146  
  1147  				err := lifecycle.Export(context.Background(), expectedRepos[0], expectedRepos[1], true, "test", "test", "test", fakePhaseFactory)
  1148  				h.AssertNil(t, err)
  1149  
  1150  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1151  				h.AssertNotEq(t, lastCallIndex, -1)
  1152  
  1153  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1154  				h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "CNB_REGISTRY_AUTH={}")
  1155  			})
  1156  
  1157  			it("configures the phase with root", func() {
  1158  				lifecycle := newTestLifecycleExec(t, false)
  1159  				fakePhaseFactory := fakes.NewFakePhaseFactory()
  1160  
  1161  				err := lifecycle.Export(context.Background(), "test", "test", true, "test", "test", "test", fakePhaseFactory)
  1162  				h.AssertNil(t, err)
  1163  
  1164  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1165  				h.AssertNotEq(t, lastCallIndex, -1)
  1166  
  1167  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1168  				h.AssertEq(t, configProvider.ContainerConfig().User, "root")
  1169  			})
  1170  
  1171  			it("configures the phase with the expected network mode", func() {
  1172  				lifecycle := newTestLifecycleExec(t, false)
  1173  				fakePhaseFactory := fakes.NewFakePhaseFactory()
  1174  				expectedNetworkMode := "some-network-mode"
  1175  
  1176  				err := lifecycle.Export(context.Background(), "test", "test", true, "test", "test", expectedNetworkMode, fakePhaseFactory)
  1177  				h.AssertNil(t, err)
  1178  
  1179  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1180  				h.AssertNotEq(t, lastCallIndex, -1)
  1181  
  1182  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1183  				h.AssertEq(t, configProvider.HostConfig().NetworkMode, container.NetworkMode(expectedNetworkMode))
  1184  			})
  1185  
  1186  			it("configures the phase with binds", func() {
  1187  				lifecycle := newTestLifecycleExec(t, false)
  1188  				fakePhaseFactory := fakes.NewFakePhaseFactory()
  1189  				expectedBind := "some-cache:/cache"
  1190  
  1191  				err := lifecycle.Export(context.Background(), "test", "test", true, "test", "some-cache", "test", fakePhaseFactory)
  1192  				h.AssertNil(t, err)
  1193  
  1194  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1195  				h.AssertNotEq(t, lastCallIndex, -1)
  1196  
  1197  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1198  				h.AssertSliceContains(t, configProvider.HostConfig().Binds, expectedBind)
  1199  			})
  1200  
  1201  			it("configures the phase to write stack toml", func() {
  1202  				lifecycle := newTestLifecycleExec(t, false)
  1203  				fakePhaseFactory := fakes.NewFakePhaseFactory()
  1204  				expectedBinds := []string{"some-cache:/cache", "some-launch-cache:/launch-cache"}
  1205  
  1206  				err := lifecycle.Export(context.Background(), "test", "test", false, "some-launch-cache", "some-cache", "test", fakePhaseFactory)
  1207  				h.AssertNil(t, err)
  1208  
  1209  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1210  				h.AssertNotEq(t, lastCallIndex, -1)
  1211  
  1212  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1213  				h.AssertSliceContains(t, configProvider.HostConfig().Binds, expectedBinds...)
  1214  
  1215  				h.AssertEq(t, len(configProvider.ContainerOps()), 1)
  1216  				h.AssertFunctionName(t, configProvider.ContainerOps()[0], "WriteStackToml")
  1217  			})
  1218  
  1219  			it("configures the phase with default process type", func() {
  1220  				lifecycle := newTestLifecycleExec(t, true, func(options *build.LifecycleOptions) {
  1221  					options.DefaultProcessType = "test-process"
  1222  				})
  1223  				fakePhaseFactory := fakes.NewFakePhaseFactory()
  1224  				expectedDefaultProc := []string{"-process-type", "test-process"}
  1225  
  1226  				err := lifecycle.Export(context.Background(), "test", "test", true, "test", "test", "test", fakePhaseFactory)
  1227  				h.AssertNil(t, err)
  1228  
  1229  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1230  				h.AssertNotEq(t, lastCallIndex, -1)
  1231  
  1232  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1233  				h.AssertIncludeAllExpectedPatterns(t, configProvider.ContainerConfig().Cmd, expectedDefaultProc)
  1234  			})
  1235  
  1236  			when("platform 0.3", func() {
  1237  				it("doesn't hint at default process type", func() {
  1238  					fakeBuilder, err := fakes.NewFakeBuilder(fakes.WithSupportedPlatformAPIs([]*api.Version{api.MustParse("0.3")}))
  1239  					h.AssertNil(t, err)
  1240  					lifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder))
  1241  					fakePhaseFactory := fakes.NewFakePhaseFactory()
  1242  
  1243  					err = lifecycle.Export(context.Background(), "test", "test", true, "test", "test", "test", fakePhaseFactory)
  1244  					h.AssertNil(t, err)
  1245  
  1246  					lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1247  					h.AssertNotEq(t, lastCallIndex, -1)
  1248  
  1249  					configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1250  					h.AssertSliceNotContains(t, configProvider.ContainerConfig().Cmd, "-process-type")
  1251  				})
  1252  			})
  1253  
  1254  			when("platform 0.4", func() {
  1255  				it("hints at default process type", func() {
  1256  					fakeBuilder, err := fakes.NewFakeBuilder(fakes.WithSupportedPlatformAPIs([]*api.Version{api.MustParse("0.4")}))
  1257  					h.AssertNil(t, err)
  1258  					lifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder))
  1259  					fakePhaseFactory := fakes.NewFakePhaseFactory()
  1260  
  1261  					err = lifecycle.Export(context.Background(), "test", "test", true, "test", "test", "test", fakePhaseFactory)
  1262  					h.AssertNil(t, err)
  1263  
  1264  					lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1265  					h.AssertNotEq(t, lastCallIndex, -1)
  1266  
  1267  					configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1268  					h.AssertIncludeAllExpectedPatterns(t, configProvider.ContainerConfig().Cmd, []string{"-process-type", "web"})
  1269  				})
  1270  			})
  1271  		})
  1272  
  1273  		when("publish is false", func() {
  1274  			it("runs the phase with the lifecycle image", func() {
  1275  				lifecycle := newTestLifecycleExec(t, true, func(options *build.LifecycleOptions) {
  1276  					options.LifecycleImage = "some-lifecycle-image"
  1277  				})
  1278  				fakePhaseFactory := fakes.NewFakePhaseFactory()
  1279  
  1280  				err := lifecycle.Export(context.Background(), "test", "test", false, "test", "test", "test", fakePhaseFactory)
  1281  				h.AssertNil(t, err)
  1282  
  1283  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1284  				h.AssertNotEq(t, lastCallIndex, -1)
  1285  
  1286  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1287  				h.AssertEq(t, configProvider.ContainerConfig().Image, "some-lifecycle-image")
  1288  			})
  1289  
  1290  			it("sets the CNB_USER_ID and CNB_GROUP_ID in the environment", func() {
  1291  				fakeBuilder, err := fakes.NewFakeBuilder(fakes.WithUID(2222), fakes.WithGID(3333))
  1292  				h.AssertNil(t, err)
  1293  				lifecycle := newTestLifecycleExec(t, false, fakes.WithBuilder(fakeBuilder))
  1294  				fakePhaseFactory := fakes.NewFakePhaseFactory()
  1295  
  1296  				err = lifecycle.Export(context.Background(), "test", "test", false, "test", "test", "test", fakePhaseFactory)
  1297  				h.AssertNil(t, err)
  1298  
  1299  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1300  				h.AssertNotEq(t, lastCallIndex, -1)
  1301  
  1302  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1303  				h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "CNB_USER_ID=2222")
  1304  				h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "CNB_GROUP_ID=3333")
  1305  			})
  1306  
  1307  			it("configures the phase with daemon access", func() {
  1308  				lifecycle := newTestLifecycleExec(t, false)
  1309  				fakePhaseFactory := fakes.NewFakePhaseFactory()
  1310  
  1311  				err := lifecycle.Export(context.Background(), "test", "test", false, "test", "test", "test", fakePhaseFactory)
  1312  				h.AssertNil(t, err)
  1313  
  1314  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1315  				h.AssertNotEq(t, lastCallIndex, -1)
  1316  
  1317  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1318  				h.AssertEq(t, configProvider.ContainerConfig().User, "root")
  1319  				h.AssertSliceContains(t, configProvider.HostConfig().Binds, "/var/run/docker.sock:/var/run/docker.sock")
  1320  			})
  1321  
  1322  			it("configures the phase with the expected arguments", func() {
  1323  				verboseLifecycle := newTestLifecycleExec(t, true)
  1324  				fakePhaseFactory := fakes.NewFakePhaseFactory()
  1325  
  1326  				err := verboseLifecycle.Export(context.Background(), "test", "test", false, "test", "test", "test", fakePhaseFactory)
  1327  				h.AssertNil(t, err)
  1328  
  1329  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1330  				h.AssertNotEq(t, lastCallIndex, -1)
  1331  
  1332  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1333  				h.AssertEq(t, configProvider.Name(), "exporter")
  1334  				h.AssertIncludeAllExpectedPatterns(t,
  1335  					configProvider.ContainerConfig().Cmd,
  1336  					[]string{"-daemon"},
  1337  					[]string{"-launch-cache", "/launch-cache"},
  1338  				)
  1339  			})
  1340  
  1341  			it("configures the phase with the expected network mode", func() {
  1342  				lifecycle := newTestLifecycleExec(t, false)
  1343  				fakePhaseFactory := fakes.NewFakePhaseFactory()
  1344  				expectedNetworkMode := "some-network-mode"
  1345  
  1346  				err := lifecycle.Export(context.Background(), "test", "test", false, "test", "test", expectedNetworkMode, fakePhaseFactory)
  1347  				h.AssertNil(t, err)
  1348  
  1349  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1350  				h.AssertNotEq(t, lastCallIndex, -1)
  1351  
  1352  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1353  				h.AssertEq(t, configProvider.HostConfig().NetworkMode, container.NetworkMode(expectedNetworkMode))
  1354  			})
  1355  
  1356  			it("configures the phase with binds", func() {
  1357  				lifecycle := newTestLifecycleExec(t, false)
  1358  				fakePhaseFactory := fakes.NewFakePhaseFactory()
  1359  				expectedBinds := []string{"some-cache:/cache", "some-launch-cache:/launch-cache"}
  1360  
  1361  				err := lifecycle.Export(context.Background(), "test", "test", false, "some-launch-cache", "some-cache", "test", fakePhaseFactory)
  1362  				h.AssertNil(t, err)
  1363  
  1364  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1365  				h.AssertNotEq(t, lastCallIndex, -1)
  1366  
  1367  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1368  				h.AssertSliceContains(t, configProvider.HostConfig().Binds, expectedBinds...)
  1369  			})
  1370  
  1371  			it("configures the phase to write stack toml", func() {
  1372  				lifecycle := newTestLifecycleExec(t, false)
  1373  				fakePhaseFactory := fakes.NewFakePhaseFactory()
  1374  				expectedBinds := []string{"some-cache:/cache", "some-launch-cache:/launch-cache"}
  1375  
  1376  				err := lifecycle.Export(context.Background(), "test", "test", false, "some-launch-cache", "some-cache", "test", fakePhaseFactory)
  1377  				h.AssertNil(t, err)
  1378  
  1379  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1380  				h.AssertNotEq(t, lastCallIndex, -1)
  1381  
  1382  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1383  				h.AssertSliceContains(t, configProvider.HostConfig().Binds, expectedBinds...)
  1384  
  1385  				h.AssertEq(t, len(configProvider.ContainerOps()), 1)
  1386  				h.AssertFunctionName(t, configProvider.ContainerOps()[0], "WriteStackToml")
  1387  			})
  1388  
  1389  			it("configures the phase with default process type", func() {
  1390  				lifecycle := newTestLifecycleExec(t, true, func(options *build.LifecycleOptions) {
  1391  					options.DefaultProcessType = "test-process"
  1392  				})
  1393  				fakePhaseFactory := fakes.NewFakePhaseFactory()
  1394  				expectedDefaultProc := []string{"-process-type", "test-process"}
  1395  
  1396  				err := lifecycle.Export(context.Background(), "test", "test", false, "test", "test", "test", fakePhaseFactory)
  1397  				h.AssertNil(t, err)
  1398  
  1399  				lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1400  				h.AssertNotEq(t, lastCallIndex, -1)
  1401  
  1402  				configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1403  				h.AssertIncludeAllExpectedPatterns(t, configProvider.ContainerConfig().Cmd, expectedDefaultProc)
  1404  			})
  1405  
  1406  			when("platform 0.3", func() {
  1407  				it("doesn't hint at default process type", func() {
  1408  					fakeBuilder, err := fakes.NewFakeBuilder(fakes.WithSupportedPlatformAPIs([]*api.Version{api.MustParse("0.3")}))
  1409  					h.AssertNil(t, err)
  1410  					lifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder))
  1411  					fakePhaseFactory := fakes.NewFakePhaseFactory()
  1412  
  1413  					err = lifecycle.Export(context.Background(), "test", "test", false, "test", "test", "test", fakePhaseFactory)
  1414  					h.AssertNil(t, err)
  1415  
  1416  					lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1417  					h.AssertNotEq(t, lastCallIndex, -1)
  1418  
  1419  					configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1420  					h.AssertSliceNotContains(t, configProvider.ContainerConfig().Cmd, "-process-type")
  1421  				})
  1422  			})
  1423  
  1424  			when("platform 0.4", func() {
  1425  				it("hints at default process type", func() {
  1426  					fakeBuilder, err := fakes.NewFakeBuilder(fakes.WithSupportedPlatformAPIs([]*api.Version{api.MustParse("0.4")}))
  1427  					h.AssertNil(t, err)
  1428  					lifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder))
  1429  					fakePhaseFactory := fakes.NewFakePhaseFactory()
  1430  
  1431  					err = lifecycle.Export(context.Background(), "test", "test", false, "test", "test", "test", fakePhaseFactory)
  1432  					h.AssertNil(t, err)
  1433  
  1434  					lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1
  1435  					h.AssertNotEq(t, lastCallIndex, -1)
  1436  
  1437  					configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex]
  1438  					h.AssertIncludeAllExpectedPatterns(t, configProvider.ContainerConfig().Cmd, []string{"-process-type", "web"})
  1439  				})
  1440  			})
  1441  		})
  1442  	})
  1443  }
  1444  
  1445  func newTestLifecycleExecErr(t *testing.T, logVerbose bool, ops ...func(*build.LifecycleOptions)) (*build.LifecycleExecution, error) {
  1446  	docker, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("1.38"))
  1447  	h.AssertNil(t, err)
  1448  
  1449  	var outBuf bytes.Buffer
  1450  	logger := ilogging.NewLogWithWriters(&outBuf, &outBuf)
  1451  	if logVerbose {
  1452  		logger.Level = log.DebugLevel
  1453  	}
  1454  
  1455  	defaultBuilder, err := fakes.NewFakeBuilder()
  1456  	h.AssertNil(t, err)
  1457  
  1458  	opts := build.LifecycleOptions{
  1459  		AppPath:    "some-app-path",
  1460  		Builder:    defaultBuilder,
  1461  		HTTPProxy:  "some-http-proxy",
  1462  		HTTPSProxy: "some-https-proxy",
  1463  		NoProxy:    "some-no-proxy",
  1464  	}
  1465  
  1466  	for _, op := range ops {
  1467  		op(&opts)
  1468  	}
  1469  
  1470  	return build.NewLifecycleExecution(logger, docker, opts)
  1471  }
  1472  
  1473  func newTestLifecycleExec(t *testing.T, logVerbose bool, ops ...func(*build.LifecycleOptions)) *build.LifecycleExecution {
  1474  	t.Helper()
  1475  
  1476  	lifecycleExec, err := newTestLifecycleExecErr(t, logVerbose, ops...)
  1477  	h.AssertNil(t, err)
  1478  	return lifecycleExec
  1479  }