diff --git a/build.go b/build.go index 657126171..1b8493f1b 100644 --- a/build.go +++ b/build.go @@ -90,6 +90,10 @@ type BuildOptions struct { // built atop. RunImage string + // Address of docker daemon exposed to build container + // e.g. tcp://example.com:1234, unix:///run/user/1000/podman/podman.sock + DockerHost string + // Used to determine a run-image mirror if Run Image is empty. // Used in combination with Builder metadata to determine to the the 'best' mirror. // 'best' is defined as: @@ -284,6 +288,7 @@ func (c *Client) Build(ctx context.Context, opts BuildOptions) error { RunImage: runImageName, ClearCache: opts.ClearCache, Publish: opts.Publish, + DockerHost: opts.DockerHost, UseCreator: false, TrustBuilder: opts.TrustBuilder, LifecycleImage: ephemeralBuilder.Name(), diff --git a/internal/build/lifecycle_execution.go b/internal/build/lifecycle_execution.go index b3b0fe542..bddfea5dc 100644 --- a/internal/build/lifecycle_execution.go +++ b/internal/build/lifecycle_execution.go @@ -132,7 +132,7 @@ func (l *LifecycleExecution) Run(ctx context.Context, phaseFactoryCreator PhaseF } l.logger.Info(style.Step("ANALYZING")) - if err := l.Analyze(ctx, l.opts.Image.String(), l.opts.Network, l.opts.Publish, l.opts.ClearCache, buildCache, phaseFactory); err != nil { + if err := l.Analyze(ctx, l.opts.Image.String(), l.opts.Network, l.opts.Publish, l.opts.DockerHost, l.opts.ClearCache, buildCache, phaseFactory); err != nil { return err } @@ -150,22 +150,10 @@ func (l *LifecycleExecution) Run(ctx context.Context, phaseFactoryCreator PhaseF } l.logger.Info(style.Step("EXPORTING")) - return l.Export(ctx, l.opts.Image.String(), l.opts.RunImage, l.opts.Publish, l.opts.Network, buildCache, launchCache, l.opts.AdditionalTags, phaseFactory) + return l.Export(ctx, l.opts.Image.String(), l.opts.RunImage, l.opts.Publish, l.opts.DockerHost, l.opts.Network, buildCache, launchCache, l.opts.AdditionalTags, phaseFactory) } - return l.Create( - ctx, - l.opts.Publish, - l.opts.ClearCache, - l.opts.RunImage, - l.opts.Image.String(), - l.opts.Network, - buildCache, - launchCache, - l.opts.AdditionalTags, - l.opts.Volumes, - phaseFactory, - ) + return l.Create(ctx, l.opts.Publish, l.opts.DockerHost, l.opts.ClearCache, l.opts.RunImage, l.opts.Image.String(), l.opts.Network, buildCache, launchCache, l.opts.AdditionalTags, l.opts.Volumes, phaseFactory) } func (l *LifecycleExecution) Cleanup() error { @@ -179,15 +167,7 @@ func (l *LifecycleExecution) Cleanup() error { return reterr } -func (l *LifecycleExecution) Create( - ctx context.Context, - publish, clearCache bool, - runImage, repoName, networkMode string, - buildCache, launchCache Cache, - additionalTags []string, - volumes []string, - phaseFactory PhaseFactory, -) error { +func (l *LifecycleExecution) Create(ctx context.Context, publish bool, dockerHost string, clearCache bool, runImage, repoName, networkMode string, buildCache, launchCache Cache, additionalTags, volumes []string, phaseFactory PhaseFactory) error { flags := addTags([]string{ "-cache-dir", l.mountPaths.cacheDir(), "-run-image", runImage, @@ -228,7 +208,7 @@ func (l *LifecycleExecution) Create( opts = append(opts, WithRoot(), WithRegistryAccess(authConfig)) } else { opts = append(opts, - WithDaemonAccess(), + WithDaemonAccess(dockerHost), WithFlags("-daemon", "-launch-cache", l.mountPaths.launchCacheDir()), WithBinds(fmt.Sprintf("%s:%s", launchCache.Name(), l.mountPaths.launchCacheDir())), ) @@ -292,8 +272,8 @@ func (l *LifecycleExecution) Restore(ctx context.Context, networkMode string, bu return restore.Run(ctx) } -func (l *LifecycleExecution) Analyze(ctx context.Context, repoName, networkMode string, publish, clearCache bool, cache Cache, phaseFactory PhaseFactory) error { - analyze, err := l.newAnalyze(repoName, networkMode, publish, clearCache, cache, phaseFactory) +func (l *LifecycleExecution) Analyze(ctx context.Context, repoName, networkMode string, publish bool, dockerHost string, clearCache bool, cache Cache, phaseFactory PhaseFactory) error { + analyze, err := l.newAnalyze(repoName, networkMode, publish, dockerHost, clearCache, cache, phaseFactory) if err != nil { return err } @@ -301,7 +281,7 @@ func (l *LifecycleExecution) Analyze(ctx context.Context, repoName, networkMode return analyze.Run(ctx) } -func (l *LifecycleExecution) newAnalyze(repoName, networkMode string, publish, clearCache bool, buildCache Cache, phaseFactory PhaseFactory) (RunnerCleaner, error) { +func (l *LifecycleExecution) newAnalyze(repoName, networkMode string, publish bool, dockerHost string, clearCache bool, buildCache Cache, phaseFactory PhaseFactory) (RunnerCleaner, error) { args := []string{ repoName, } @@ -355,7 +335,7 @@ func (l *LifecycleExecution) newAnalyze(repoName, networkMode string, publish, c fmt.Sprintf("%s=%d", builder.EnvUID, l.opts.Builder.UID()), fmt.Sprintf("%s=%d", builder.EnvGID, l.opts.Builder.GID()), ), - WithDaemonAccess(), + WithDaemonAccess(dockerHost), WithArgs( l.withLogLevel( prependArg( @@ -396,7 +376,7 @@ func determineDefaultProcessType(platformAPI *api.Version, providedValue string) return providedValue } -func (l *LifecycleExecution) newExport(repoName, runImage string, publish bool, networkMode string, buildCache, launchCache Cache, additionalTags []string, phaseFactory PhaseFactory) (RunnerCleaner, error) { +func (l *LifecycleExecution) newExport(repoName, runImage string, publish bool, dockerHost, networkMode string, buildCache, launchCache Cache, additionalTags []string, phaseFactory PhaseFactory) (RunnerCleaner, error) { flags := []string{ "-cache-dir", l.mountPaths.cacheDir(), "-stack", l.mountPaths.stackPath(), @@ -447,7 +427,7 @@ func (l *LifecycleExecution) newExport(repoName, runImage string, publish bool, } else { opts = append( opts, - WithDaemonAccess(), + WithDaemonAccess(dockerHost), WithFlags("-daemon", "-launch-cache", l.mountPaths.launchCacheDir()), WithBinds(fmt.Sprintf("%s:%s", launchCache.Name(), l.mountPaths.launchCacheDir())), ) @@ -456,8 +436,8 @@ func (l *LifecycleExecution) newExport(repoName, runImage string, publish bool, return phaseFactory.New(NewPhaseConfigProvider("exporter", l, opts...)), nil } -func (l *LifecycleExecution) Export(ctx context.Context, repoName string, runImage string, publish bool, networkMode string, buildCache, launchCache Cache, additionalTags []string, phaseFactory PhaseFactory) error { - export, err := l.newExport(repoName, runImage, publish, networkMode, buildCache, launchCache, additionalTags, phaseFactory) +func (l *LifecycleExecution) Export(ctx context.Context, repoName, runImage string, publish bool, dockerHost, networkMode string, buildCache, launchCache Cache, additionalTags []string, phaseFactory PhaseFactory) error { + export, err := l.newExport(repoName, runImage, publish, dockerHost, networkMode, buildCache, launchCache, additionalTags, phaseFactory) if err != nil { return err } diff --git a/internal/build/lifecycle_execution_test.go b/internal/build/lifecycle_execution_test.go index 8fd03f87e..ed27fba97 100644 --- a/internal/build/lifecycle_execution_test.go +++ b/internal/build/lifecycle_execution_test.go @@ -230,19 +230,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhase := &fakes.FakePhase{} fakePhaseFactory := fakes.NewFakePhaseFactory(fakes.WhichReturnsForNew(fakePhase)) - err := lifecycle.Create( - context.Background(), - false, - false, - "test", - "test", - "test", - fakeBuildCache, - fakeLaunchCache, - []string{}, - []string{}, - fakePhaseFactory, - ) + err := lifecycle.Create(context.Background(), false, "", false, "test", "test", "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory) h.AssertNil(t, err) h.AssertEq(t, fakePhase.CleanupCallCount, 1) @@ -255,19 +243,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { expectedRepoName := "some-repo-name" expectedRunImage := "some-run-image" - err := verboseLifecycle.Create( - context.Background(), - false, - false, - expectedRunImage, - expectedRepoName, - "test", - fakeBuildCache, - fakeLaunchCache, - []string{}, - []string{}, - fakePhaseFactory, - ) + err := verboseLifecycle.Create(context.Background(), false, "", false, expectedRunImage, expectedRepoName, "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -288,19 +264,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedNetworkMode := "some-network-mode" - err := lifecycle.Create( - context.Background(), - false, - false, - "test", - "test", - expectedNetworkMode, - fakeBuildCache, - fakeLaunchCache, - []string{}, - []string{}, - fakePhaseFactory, - ) + err := lifecycle.Create(context.Background(), false, "", false, "test", "test", expectedNetworkMode, fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -315,19 +279,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { verboseLifecycle := newTestLifecycleExec(t, true) fakePhaseFactory := fakes.NewFakePhaseFactory() - err := verboseLifecycle.Create( - context.Background(), - false, - true, - "test", - "test", - "test", - fakeBuildCache, - fakeLaunchCache, - []string{}, - []string{}, - fakePhaseFactory, - ) + err := verboseLifecycle.Create(context.Background(), false, "", true, "test", "test", "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -347,19 +299,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { verboseLifecycle := newTestLifecycleExec(t, true) fakePhaseFactory := fakes.NewFakePhaseFactory() - err := verboseLifecycle.Create( - context.Background(), - false, - false, - "test", - "test", - "test", - fakeBuildCache, - fakeLaunchCache, - []string{}, - []string{}, - fakePhaseFactory, - ) + err := verboseLifecycle.Create(context.Background(), false, "", false, "test", "test", "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -383,19 +323,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { verboseLifecycle := newTestLifecycleExec(t, true) fakePhaseFactory := fakes.NewFakePhaseFactory() - err := verboseLifecycle.Create( - context.Background(), - false, - true, - "test", - "test", - "test", - fakeBuildCache, - fakeLaunchCache, - []string{}, - []string{}, - fakePhaseFactory, - ) + err := verboseLifecycle.Create(context.Background(), false, "", true, "test", "test", "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -419,19 +347,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() additionalTags := []string{"additional-tag-1", "additional-tag-2"} - err := lifecycle.Create( - context.Background(), - false, - false, - "test", - "test", - "test", - fakes.NewFakeCache(), - fakes.NewFakeCache(), - additionalTags, - []string{}, - fakePhaseFactory, - ) + err := lifecycle.Create(context.Background(), false, "", false, "test", "test", "test", fakes.NewFakeCache(), fakes.NewFakeCache(), additionalTags, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -466,19 +382,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { volumeMount := "custom-mount-source:/custom-mount-target" expectedBinds := []string{volumeMount, "some-cache:/cache"} - err := lifecycle.Create( - context.Background(), - true, - false, - "test", - "test", - "test", - fakeBuildCache, - fakeLaunchCache, - []string{}, - []string{volumeMount}, - fakePhaseFactory, - ) + err := lifecycle.Create(context.Background(), true, "", false, "test", "test", "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{volumeMount}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -492,19 +396,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, false) fakePhaseFactory := fakes.NewFakePhaseFactory() - err := lifecycle.Create( - context.Background(), - true, - false, - "test", - "test", - "test", - fakeBuildCache, - fakeLaunchCache, - []string{}, - []string{}, - fakePhaseFactory, - ) + err := lifecycle.Create(context.Background(), true, "", false, "test", "test", "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -519,19 +411,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedRepos := "some-repo-name" - err := lifecycle.Create( - context.Background(), - true, - false, - "test", - expectedRepos, - "test", - fakeBuildCache, - fakeLaunchCache, - []string{}, - []string{}, - fakePhaseFactory, - ) + err := lifecycle.Create(context.Background(), true, "", false, "test", expectedRepos, "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -551,19 +431,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { verboseLifecycle := newTestLifecycleExec(t, true) fakePhaseFactory := fakes.NewFakePhaseFactory() - err := verboseLifecycle.Create( - context.Background(), - true, - true, - "test", - "test", - "test", - fakeBuildCache, - fakeLaunchCache, - []string{}, - []string{}, - fakePhaseFactory, - ) + err := verboseLifecycle.Create(context.Background(), true, "", true, "test", "test", "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -602,7 +470,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder)) fakePhaseFactory := fakes.NewFakePhaseFactory() - err = lifecycle.Export(context.Background(), "test", "test", true, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err = lifecycle.Export(context.Background(), "test", "test", true, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -620,7 +488,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder)) fakePhaseFactory := fakes.NewFakePhaseFactory() - err = lifecycle.Export(context.Background(), "test", "test", true, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err = lifecycle.Export(context.Background(), "test", "test", true, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -650,19 +518,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { verboseLifecycle := newTestLifecycleExec(t, true) fakePhaseFactory := fakes.NewFakePhaseFactory() - err := verboseLifecycle.Create( - context.Background(), - false, - false, - "test", - "test", - "test", - fakeBuildCache, - fakeLaunchCache, - []string{}, - []string{}, - fakePhaseFactory, - ) + err := verboseLifecycle.Create(context.Background(), false, "", false, "test", "test", "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -681,19 +537,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, false) fakePhaseFactory := fakes.NewFakePhaseFactory() - err := lifecycle.Create( - context.Background(), - false, - false, - "test", - "test", - "test", - fakeBuildCache, - fakeLaunchCache, - []string{}, - []string{}, - fakePhaseFactory, - ) + err := lifecycle.Create(context.Background(), false, "", false, "test", "test", "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -704,25 +548,101 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { h.AssertSliceContains(t, configProvider.HostConfig().Binds, "/var/run/docker.sock:/var/run/docker.sock") }) + it("configures the phase with daemon access with tcp docker-host", func() { + lifecycle := newTestLifecycleExec(t, false) + fakePhaseFactory := fakes.NewFakePhaseFactory() + + err := lifecycle.Create(context.Background(), false, "tcp://localhost:1234", false, "test", "test", "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory) + h.AssertNil(t, err) + + lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 + h.AssertNotEq(t, lastCallIndex, -1) + + configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex] + h.AssertSliceNotContains(t, configProvider.HostConfig().Binds, "/var/run/docker.sock:/var/run/docker.sock") + h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "DOCKER_HOST=tcp://localhost:1234") + }) + + it("configures the phase with daemon access with alternative unix socket docker-host", func() { + lifecycle := newTestLifecycleExec(t, false) + fakePhaseFactory := fakes.NewFakePhaseFactory() + + err := lifecycle.Create(context.Background(), false, "unix:///home/user/docker.sock", false, "test", "test", "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory) + h.AssertNil(t, err) + + lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 + h.AssertNotEq(t, lastCallIndex, -1) + + configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex] + h.AssertSliceContains(t, configProvider.HostConfig().Binds, "/home/user/docker.sock:/var/run/docker.sock") + }) + + it("configures the phase with daemon access with alternative windows pipe docker-host", func() { + lifecycle := newTestLifecycleExec(t, false) + fakePhaseFactory := fakes.NewFakePhaseFactory() + + err := lifecycle.Create(context.Background(), false, `npipe:\\\\.\pipe\docker_engine_alt`, false, "test", "test", "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory) + h.AssertNil(t, err) + + lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 + h.AssertNotEq(t, lastCallIndex, -1) + + configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex] + h.AssertSliceNotContains(t, configProvider.HostConfig().Binds, "/home/user/docker.sock:/var/run/docker.sock") + h.AssertSliceContains(t, configProvider.HostConfig().Binds, `\\.\pipe\docker_engine_alt:\\.\pipe\docker_engine`) + }) + + when("environment variable DOCKER_HOST is set", func() { + var ( + oldDH string + oldDHExists bool + ) + it.Before(func() { + oldDH, oldDHExists = os.LookupEnv("DOCKER_HOST") + os.Setenv("DOCKER_HOST", "tcp://example.com:1234") + }) + it.After(func() { + if oldDHExists { + os.Setenv("DOCKER_HOST", oldDH) + } else { + os.Unsetenv("DOCKER_HOST") + } + }) + it("configures the phase with daemon access with inherited docker-host", func() { + lifecycle := newTestLifecycleExec(t, false) + fakePhaseFactory := fakes.NewFakePhaseFactory() + + err := lifecycle.Create(context.Background(), false, `inherit`, false, "test", "test", "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory) + h.AssertNil(t, err) + + lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 + h.AssertNotEq(t, lastCallIndex, -1) + + configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex] + h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "DOCKER_HOST=tcp://example.com:1234") + }) + }) + + it("configures the phase with daemon access with docker-host with unknown protocol", func() { + lifecycle := newTestLifecycleExec(t, false) + fakePhaseFactory := fakes.NewFakePhaseFactory() + err := lifecycle.Create(context.Background(), false, `withoutprotocol`, false, "test", "test", "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{}, fakePhaseFactory) + h.AssertNil(t, err) + + lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 + h.AssertNotEq(t, lastCallIndex, -1) + + configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex] + h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "DOCKER_HOST=withoutprotocol") + }) + it("configures the phase with binds", func() { lifecycle := newTestLifecycleExec(t, false) fakePhaseFactory := fakes.NewFakePhaseFactory() volumeMount := "custom-mount-source:/custom-mount-target" expectedBinds := []string{volumeMount, "some-cache:/cache", "some-launch-cache:/launch-cache"} - err := lifecycle.Create( - context.Background(), - false, - false, - "test", - "test", - "test", - fakeBuildCache, - fakeLaunchCache, - []string{}, - []string{volumeMount}, - fakePhaseFactory, - ) + err := lifecycle.Create(context.Background(), false, "", false, "test", "test", "test", fakeBuildCache, fakeLaunchCache, []string{}, []string{volumeMount}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -739,7 +659,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder)) fakePhaseFactory := fakes.NewFakePhaseFactory() - err = lifecycle.Export(context.Background(), "test", "test", false, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err = lifecycle.Export(context.Background(), "test", "test", false, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -757,7 +677,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder)) fakePhaseFactory := fakes.NewFakePhaseFactory() - err = lifecycle.Export(context.Background(), "test", "test", false, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err = lifecycle.Export(context.Background(), "test", "test", false, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -847,7 +767,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhase := &fakes.FakePhase{} fakePhaseFactory := fakes.NewFakePhaseFactory(fakes.WhichReturnsForNew(fakePhase)) - err := lifecycle.Analyze(context.Background(), "test", "test", false, false, fakeCache, fakePhaseFactory) + err := lifecycle.Analyze(context.Background(), "test", "test", false, "", false, fakeCache, fakePhaseFactory) h.AssertNil(t, err) h.AssertEq(t, fakePhase.CleanupCallCount, 1) @@ -860,7 +780,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedRepoName := "some-repo-name" - err := lifecycle.Analyze(context.Background(), expectedRepoName, "test", false, true, fakeCache, fakePhaseFactory) + err := lifecycle.Analyze(context.Background(), expectedRepoName, "test", false, "", true, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -878,7 +798,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedRepoName := "some-repo-name" - err := lifecycle.Analyze(context.Background(), expectedRepoName, "test", false, false, fakeCache, fakePhaseFactory) + err := lifecycle.Analyze(context.Background(), expectedRepoName, "test", false, "", false, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -907,7 +827,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory = fakes.NewFakePhaseFactory() }) it("configures the phase with a build cache images", func() { - err := lifecycle.Analyze(context.Background(), expectedRepoName, "", false, false, fakeCache, fakePhaseFactory) + err := lifecycle.Analyze(context.Background(), expectedRepoName, "", false, "", false, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -927,7 +847,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { }) when("clear-cache", func() { it("cache is omitted from Analyze", func() { - err := lifecycle.Analyze(context.Background(), expectedRepoName, "", false, true, fakeCache, fakePhaseFactory) + err := lifecycle.Analyze(context.Background(), expectedRepoName, "", false, "", true, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -947,7 +867,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { }) fakePhaseFactory := fakes.NewFakePhaseFactory() - err := lifecycle.Analyze(context.Background(), "test", "test", true, false, fakeCache, fakePhaseFactory) + err := lifecycle.Analyze(context.Background(), "test", "test", true, "", false, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -963,7 +883,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, false, fakes.WithBuilder(fakeBuilder)) fakePhaseFactory := fakes.NewFakePhaseFactory() - err = lifecycle.Analyze(context.Background(), "test", "test", true, false, fakeCache, fakePhaseFactory) + err = lifecycle.Analyze(context.Background(), "test", "test", true, "", false, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -980,7 +900,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { expectedRepos := "some-repo-name" expectedNetworkMode := "some-network-mode" - err := lifecycle.Analyze(context.Background(), expectedRepos, expectedNetworkMode, true, false, fakeCache, fakePhaseFactory) + err := lifecycle.Analyze(context.Background(), expectedRepos, expectedNetworkMode, true, "", false, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -995,7 +915,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, false) fakePhaseFactory := fakes.NewFakePhaseFactory() - err := lifecycle.Analyze(context.Background(), "test", "test", true, false, fakeCache, fakePhaseFactory) + err := lifecycle.Analyze(context.Background(), "test", "test", true, "", false, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1010,7 +930,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedRepoName := "some-repo-name" - err := verboseLifecycle.Analyze(context.Background(), expectedRepoName, "test", true, false, fakeCache, fakePhaseFactory) + err := verboseLifecycle.Analyze(context.Background(), expectedRepoName, "test", true, "", false, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1031,7 +951,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedBind := "some-cache:/cache" - err := lifecycle.Analyze(context.Background(), "test", "test", true, false, fakeCache, fakePhaseFactory) + err := lifecycle.Analyze(context.Background(), "test", "test", true, "", false, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1052,7 +972,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedRepoName := "some-repo-name" - err := lifecycle.Analyze(context.Background(), expectedRepoName, "test", true, false, fakeCache, fakePhaseFactory) + err := lifecycle.Analyze(context.Background(), expectedRepoName, "test", true, "", false, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1079,7 +999,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { }) fakePhaseFactory := fakes.NewFakePhaseFactory() - err := lifecycle.Analyze(context.Background(), "test", "test", false, false, fakeCache, fakePhaseFactory) + err := lifecycle.Analyze(context.Background(), "test", "test", false, "", false, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1095,7 +1015,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, false, fakes.WithBuilder(fakeBuilder)) fakePhaseFactory := fakes.NewFakePhaseFactory() - err = lifecycle.Analyze(context.Background(), "test", "test", false, false, fakeCache, fakePhaseFactory) + err = lifecycle.Analyze(context.Background(), "test", "test", false, "", false, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1110,7 +1030,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, false) fakePhaseFactory := fakes.NewFakePhaseFactory() - err := lifecycle.Analyze(context.Background(), "test", "test", false, false, fakeCache, fakePhaseFactory) + err := lifecycle.Analyze(context.Background(), "test", "test", false, "", false, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1121,12 +1041,27 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { h.AssertSliceContains(t, configProvider.HostConfig().Binds, "/var/run/docker.sock:/var/run/docker.sock") }) + it("configures the phase with daemon access with TCP docker-host", func() { + lifecycle := newTestLifecycleExec(t, false) + fakePhaseFactory := fakes.NewFakePhaseFactory() + + err := lifecycle.Analyze(context.Background(), "test", "test", false, "tcp://localhost:1234", false, fakeCache, fakePhaseFactory) + h.AssertNil(t, err) + + lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 + h.AssertNotEq(t, lastCallIndex, -1) + + configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex] + h.AssertSliceNotContains(t, configProvider.HostConfig().Binds, "/var/run/docker.sock:/var/run/docker.sock") + h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "DOCKER_HOST=tcp://localhost:1234") + }) + it("configures the phase with the expected arguments", func() { verboseLifecycle := newTestLifecycleExec(t, true) fakePhaseFactory := fakes.NewFakePhaseFactory() expectedRepoName := "some-repo-name" - err := verboseLifecycle.Analyze(context.Background(), expectedRepoName, "test", false, true, fakeCache, fakePhaseFactory) + err := verboseLifecycle.Analyze(context.Background(), expectedRepoName, "test", false, "", true, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1147,7 +1082,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedNetworkMode := "some-network-mode" - err := lifecycle.Analyze(context.Background(), "test", expectedNetworkMode, false, false, fakeCache, fakePhaseFactory) + err := lifecycle.Analyze(context.Background(), "test", expectedNetworkMode, false, "", false, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1163,7 +1098,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedBind := "some-cache:/cache" - err := lifecycle.Analyze(context.Background(), "test", "test", false, true, fakeCache, fakePhaseFactory) + err := lifecycle.Analyze(context.Background(), "test", "test", false, "", true, fakeCache, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1404,7 +1339,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhase := &fakes.FakePhase{} fakePhaseFactory := fakes.NewFakePhaseFactory(fakes.WhichReturnsForNew(fakePhase)) - err := lifecycle.Export(context.Background(), "test", "test", false, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := lifecycle.Export(context.Background(), "test", "test", false, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) h.AssertEq(t, fakePhase.CleanupCallCount, 1) @@ -1417,7 +1352,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { expectedRepoName := "some-repo-name" expectedRunImage := "some-run-image" - err := verboseLifecycle.Export(context.Background(), expectedRepoName, expectedRunImage, false, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := verboseLifecycle.Export(context.Background(), expectedRepoName, expectedRunImage, false, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1442,7 +1377,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { expectedRunImage := "some-run-image" additionalTags := []string{"additional-tag-1", "additional-tag-2"} - err := verboseLifecycle.Export(context.Background(), expectedRepoName, expectedRunImage, false, "test", fakes.NewFakeCache(), fakes.NewFakeCache(), additionalTags, fakePhaseFactory) + err := verboseLifecycle.Export(context.Background(), expectedRepoName, expectedRunImage, false, "", "test", fakes.NewFakeCache(), fakes.NewFakeCache(), additionalTags, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1472,7 +1407,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { expectedRepoName := "some-repo-name" expectedRunImage := "some-run-image" - err := verboseLifecycle.Export(context.Background(), expectedRepoName, expectedRunImage, false, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := verboseLifecycle.Export(context.Background(), expectedRepoName, expectedRunImage, false, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1496,7 +1431,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { }) fakePhaseFactory := fakes.NewFakePhaseFactory() - err := lifecycle.Export(context.Background(), "test", "test", true, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := lifecycle.Export(context.Background(), "test", "test", true, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1512,7 +1447,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, false, fakes.WithBuilder(fakeBuilder)) fakePhaseFactory := fakes.NewFakePhaseFactory() - err = lifecycle.Export(context.Background(), "test", "test", true, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err = lifecycle.Export(context.Background(), "test", "test", true, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1528,7 +1463,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedRepos := []string{"some-repo-name", "some-run-image"} - err := lifecycle.Export(context.Background(), expectedRepos[0], expectedRepos[1], true, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := lifecycle.Export(context.Background(), expectedRepos[0], expectedRepos[1], true, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1542,7 +1477,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, false) fakePhaseFactory := fakes.NewFakePhaseFactory() - err := lifecycle.Export(context.Background(), "test", "test", true, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := lifecycle.Export(context.Background(), "test", "test", true, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1557,7 +1492,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedNetworkMode := "some-network-mode" - err := lifecycle.Export(context.Background(), "test", "test", true, expectedNetworkMode, fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := lifecycle.Export(context.Background(), "test", "test", true, "", expectedNetworkMode, fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1572,7 +1507,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedBind := "some-cache:/cache" - err := lifecycle.Export(context.Background(), "test", "test", true, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := lifecycle.Export(context.Background(), "test", "test", true, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1587,7 +1522,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedBinds := []string{"some-cache:/cache", "some-launch-cache:/launch-cache"} - err := lifecycle.Export(context.Background(), "test", "test", false, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := lifecycle.Export(context.Background(), "test", "test", false, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1607,7 +1542,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedDefaultProc := []string{"-process-type", "test-process"} - err := lifecycle.Export(context.Background(), "test", "test", true, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := lifecycle.Export(context.Background(), "test", "test", true, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1629,7 +1564,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { expectedRepoName := "some-repo-name" expectedRunImage := "some-run-image" - err := verboseLifecycle.Export(context.Background(), expectedRepoName, expectedRunImage, true, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := verboseLifecycle.Export(context.Background(), expectedRepoName, expectedRunImage, true, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1653,7 +1588,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder)) fakePhaseFactory := fakes.NewFakePhaseFactory() - err = lifecycle.Export(context.Background(), "test", "test", true, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err = lifecycle.Export(context.Background(), "test", "test", true, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1671,7 +1606,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder)) fakePhaseFactory := fakes.NewFakePhaseFactory() - err = lifecycle.Export(context.Background(), "test", "test", true, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err = lifecycle.Export(context.Background(), "test", "test", true, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1690,7 +1625,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { }) fakePhaseFactory := fakes.NewFakePhaseFactory() - err := lifecycle.Export(context.Background(), "test", "test", false, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := lifecycle.Export(context.Background(), "test", "test", false, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1706,7 +1641,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, false, fakes.WithBuilder(fakeBuilder)) fakePhaseFactory := fakes.NewFakePhaseFactory() - err = lifecycle.Export(context.Background(), "test", "test", false, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err = lifecycle.Export(context.Background(), "test", "test", false, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1721,7 +1656,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, false) fakePhaseFactory := fakes.NewFakePhaseFactory() - err := lifecycle.Export(context.Background(), "test", "test", false, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := lifecycle.Export(context.Background(), "test", "test", false, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1732,11 +1667,26 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { h.AssertSliceContains(t, configProvider.HostConfig().Binds, "/var/run/docker.sock:/var/run/docker.sock") }) + it("configures the phase with daemon access with tcp docker-host", func() { + lifecycle := newTestLifecycleExec(t, false) + fakePhaseFactory := fakes.NewFakePhaseFactory() + + err := lifecycle.Export(context.Background(), "test", "test", false, "tcp://localhost:1234", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + h.AssertNil(t, err) + + lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 + h.AssertNotEq(t, lastCallIndex, -1) + + configProvider := fakePhaseFactory.NewCalledWithProvider[lastCallIndex] + h.AssertSliceNotContains(t, configProvider.HostConfig().Binds, "/var/run/docker.sock:/var/run/docker.sock") + h.AssertSliceContains(t, configProvider.ContainerConfig().Env, "DOCKER_HOST=tcp://localhost:1234") + }) + it("configures the phase with the expected arguments", func() { verboseLifecycle := newTestLifecycleExec(t, true) fakePhaseFactory := fakes.NewFakePhaseFactory() - err := verboseLifecycle.Export(context.Background(), "test", "test", false, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := verboseLifecycle.Export(context.Background(), "test", "test", false, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1756,7 +1706,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedNetworkMode := "some-network-mode" - err := lifecycle.Export(context.Background(), "test", "test", false, expectedNetworkMode, fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := lifecycle.Export(context.Background(), "test", "test", false, "", expectedNetworkMode, fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1771,7 +1721,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedBinds := []string{"some-cache:/cache", "some-launch-cache:/launch-cache"} - err := lifecycle.Export(context.Background(), "test", "test", false, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := lifecycle.Export(context.Background(), "test", "test", false, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1786,7 +1736,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedBinds := []string{"some-cache:/cache", "some-launch-cache:/launch-cache"} - err := lifecycle.Export(context.Background(), "test", "test", false, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := lifecycle.Export(context.Background(), "test", "test", false, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1806,7 +1756,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { fakePhaseFactory := fakes.NewFakePhaseFactory() expectedDefaultProc := []string{"-process-type", "test-process"} - err := lifecycle.Export(context.Background(), "test", "test", false, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err := lifecycle.Export(context.Background(), "test", "test", false, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1823,7 +1773,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder)) fakePhaseFactory := fakes.NewFakePhaseFactory() - err = lifecycle.Export(context.Background(), "test", "test", false, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err = lifecycle.Export(context.Background(), "test", "test", false, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 @@ -1841,7 +1791,7 @@ func testLifecycleExecution(t *testing.T, when spec.G, it spec.S) { lifecycle := newTestLifecycleExec(t, true, fakes.WithBuilder(fakeBuilder)) fakePhaseFactory := fakes.NewFakePhaseFactory() - err = lifecycle.Export(context.Background(), "test", "test", false, "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) + err = lifecycle.Export(context.Background(), "test", "test", false, "", "test", fakeBuildCache, fakeLaunchCache, []string{}, fakePhaseFactory) h.AssertNil(t, err) lastCallIndex := len(fakePhaseFactory.NewCalledWithProvider) - 1 diff --git a/internal/build/lifecycle_executor.go b/internal/build/lifecycle_executor.go index f73a48445..bbd5cb765 100644 --- a/internal/build/lifecycle_executor.go +++ b/internal/build/lifecycle_executor.go @@ -58,6 +58,7 @@ type LifecycleOptions struct { Publish bool TrustBuilder bool UseCreator bool + DockerHost string CacheImage string HTTPProxy string HTTPSProxy string diff --git a/internal/build/phase_config_provider.go b/internal/build/phase_config_provider.go index b08921982..1fec97100 100644 --- a/internal/build/phase_config_provider.go +++ b/internal/build/phase_config_provider.go @@ -3,6 +3,7 @@ package build import ( "fmt" "io" + "os" "strings" "github.com/docker/docker/api/types/container" @@ -122,14 +123,32 @@ func WithBinds(binds ...string) PhaseConfigProviderOperation { } } -func WithDaemonAccess() PhaseConfigProviderOperation { +func WithDaemonAccess(dockerHost string) PhaseConfigProviderOperation { return func(provider *PhaseConfigProvider) { WithRoot()(provider) - bind := "/var/run/docker.sock:/var/run/docker.sock" - if provider.os == "windows" { - bind = `\\.\pipe\docker_engine:\\.\pipe\docker_engine` + if dockerHost == "inherit" { + dockerHost = os.Getenv("DOCKER_HOST") + } + var bind string + if dockerHost == "" { + bind = "/var/run/docker.sock:/var/run/docker.sock" + if provider.os == "windows" { + bind = `\\.\pipe\docker_engine:\\.\pipe\docker_engine` + } + } else { + switch { + case strings.HasPrefix(dockerHost, "unix://"): + bind = fmt.Sprintf("%s:/var/run/docker.sock", strings.TrimPrefix(dockerHost, "unix://")) + case strings.HasPrefix(dockerHost, "npipe://") || strings.HasPrefix(dockerHost, `npipe:\\`): + sub := ([]rune(dockerHost))[8:] + bind = fmt.Sprintf(`%s:\\.\pipe\docker_engine`, string(sub)) + default: + provider.ctrConf.Env = append(provider.ctrConf.Env, fmt.Sprintf(`DOCKER_HOST=%s`, dockerHost)) + } + } + if bind != "" { + provider.hostConf.Binds = append(provider.hostConf.Binds, bind) } - provider.hostConf.Binds = append(provider.hostConf.Binds, bind) } } diff --git a/internal/build/phase_config_provider_test.go b/internal/build/phase_config_provider_test.go index 59b17e33b..2dbfec0a2 100644 --- a/internal/build/phase_config_provider_test.go +++ b/internal/build/phase_config_provider_test.go @@ -133,7 +133,7 @@ func testPhaseConfigProvider(t *testing.T, when spec.G, it spec.S) { phaseConfigProvider := build.NewPhaseConfigProvider( "some-name", lifecycle, - build.WithDaemonAccess(), + build.WithDaemonAccess(""), ) h.AssertEq(t, phaseConfigProvider.ContainerConfig().User, "root") @@ -152,7 +152,7 @@ func testPhaseConfigProvider(t *testing.T, when spec.G, it spec.S) { phaseConfigProvider := build.NewPhaseConfigProvider( "some-name", lifecycle, - build.WithDaemonAccess(), + build.WithDaemonAccess(""), ) h.AssertEq(t, phaseConfigProvider.ContainerConfig().User, "ContainerAdministrator") diff --git a/internal/build/phase_test.go b/internal/build/phase_test.go index d69663b18..44d53a545 100644 --- a/internal/build/phase_test.go +++ b/internal/build/phase_test.go @@ -4,13 +4,16 @@ import ( "bytes" "context" "fmt" + "io" "io/ioutil" "math/rand" + "net" "os" "path/filepath" "regexp" "runtime" "strconv" + "sync" "testing" "time" @@ -260,11 +263,64 @@ func testPhase(t *testing.T, when spec.G, it spec.S) { }) when("#WithDaemonAccess", func() { - it("allows daemon access inside the container", func() { - configProvider := build.NewPhaseConfigProvider(phaseName, lifecycleExec, build.WithArgs("daemon"), build.WithDaemonAccess()) - phase := phaseFactory.New(configProvider) - assertRunSucceeds(t, phase, &outBuf, &errBuf) - h.AssertContains(t, outBuf.String(), "daemon test") + when("with standard docker socket", func() { + it("allows daemon access inside the container", func() { + configProvider := build.NewPhaseConfigProvider(phaseName, lifecycleExec, build.WithArgs("daemon"), build.WithDaemonAccess("")) + phase := phaseFactory.New(configProvider) + assertRunSucceeds(t, phase, &outBuf, &errBuf) + h.AssertContains(t, outBuf.String(), "daemon test") + }) + }) + + when("with non standard docker socket", func() { + it.Before(func() { + h.SkipIf(t, runtime.GOOS != "linux", "Skipped on non-linux") + }) + it("allows daemon access inside the container", func() { + tmp, err := ioutil.TempDir("", "testSocketDir") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmp) + + newName := filepath.Join(tmp, "docker.sock") + err = os.Symlink("/var/run/docker.sock", newName) + if err != nil { + t.Fatal(err) + } + + configProvider := build.NewPhaseConfigProvider(phaseName, lifecycleExec, build.WithArgs("daemon"), build.WithDaemonAccess("unix://"+newName)) + phase := phaseFactory.New(configProvider) + assertRunSucceeds(t, phase, &outBuf, &errBuf) + h.AssertContains(t, outBuf.String(), "daemon test") + }) + }) + + when("with TCP docker-host", func() { + it.Before(func() { + h.SkipIf(t, runtime.GOOS != "linux", "Skipped on non-linux") + }) + it("allows daemon access inside the container", func() { + forwardCtx, cancelForward := context.WithCancel(context.Background()) + defer cancelForward() + + portChan := make(chan int, 1) + forwardExited := make(chan struct{}, 1) + go func() { + forwardUnix2TCP(forwardCtx, t, portChan) + forwardExited <- struct{}{} + }() + dockerHost := fmt.Sprintf("tcp://127.0.0.1:%d", <-portChan) + configProvider := build.NewPhaseConfigProvider(phaseName, lifecycleExec, + build.WithArgs("daemon"), + build.WithDaemonAccess(dockerHost), + build.WithNetwork("host")) + phase := phaseFactory.New(configProvider) + assertRunSucceeds(t, phase, &outBuf, &errBuf) + h.AssertContains(t, outBuf.String(), "daemon test") + cancelForward() + <-forwardExited + }) }) }) @@ -422,3 +478,62 @@ func CreateFakeLifecycleExecution(logger logging.Logger, docker client.CommonAPI NoProxy: "some-no-proxy", }) } + +// helper function to expose standard UNIX socket `/var/run/docker.sock` via TCP localhost:PORT +// where PORT is picked automatically and returned via outPort parameter +func forwardUnix2TCP(ctx context.Context, t *testing.T, outPort chan<- int) { + wg := sync.WaitGroup{} + + forwardCon := func(tcpCon net.Conn) { + defer wg.Done() + + defer tcpCon.Close() + go func() { + <-ctx.Done() + tcpCon.Close() + }() + + unixCon, err := net.Dial("unix", "/var/run/docker.sock") + if err != nil { + t.Fatal(err) + } + defer unixCon.Close() + go func() { + <-ctx.Done() + unixCon.Close() + }() + + go io.Copy(tcpCon, unixCon) + io.Copy(unixCon, tcpCon) + } + + listener, err := net.Listen("tcp4", ":") + if err != nil { + t.Fatal(err) + } + outPort <- listener.Addr().(*net.TCPAddr).Port + defer listener.Close() + go func() { + <-ctx.Done() + listener.Close() + }() + for { + select { + case <-ctx.Done(): + goto out + default: + c, err := listener.Accept() + if err != nil { + if ctx.Err() == context.Canceled { + goto out + } else { + t.Fatal(err) + } + } + wg.Add(1) + go forwardCon(c) + } + } +out: + wg.Wait() +} diff --git a/internal/commands/build.go b/internal/commands/build.go index 06c322902..93fa285de 100644 --- a/internal/commands/build.go +++ b/internal/commands/build.go @@ -22,6 +22,7 @@ type BuildFlags struct { Publish bool ClearCache bool TrustBuilder bool + DockerHost string CacheImage string AppPath string Builder string @@ -107,6 +108,7 @@ func Build(logger logging.Logger, cfg config.Config, packClient PackClient) *cob Env: env, Image: imageName, Publish: flags.Publish, + DockerHost: flags.DockerHost, PullPolicy: pullPolicy, ClearCache: flags.ClearCache, TrustBuilder: trustBuilder, @@ -143,6 +145,12 @@ func buildCommandFlags(cmd *cobra.Command, buildFlags *BuildFlags, cfg config.Co cmd.Flags().StringArrayVar(&buildFlags.EnvFiles, "env-file", []string{}, "Build-time environment variables file\nOne variable per line, of the form 'VAR=VALUE' or 'VAR'\nWhen using latter value-less form, value will be taken from current\n environment at the time this command is executed\nNOTE: These are NOT available at image runtime.\"") cmd.Flags().StringVar(&buildFlags.Network, "network", "", "Connect detect and build containers to network") cmd.Flags().BoolVar(&buildFlags.Publish, "publish", false, "Publish to registry") + cmd.Flags().StringVar(&buildFlags.DockerHost, "docker-host", "", + `Address to docker daemon that will be exposed to the build container. +If not set (or set to empty string) the standard socket location will be used. +Special value 'inherit' may be used in which case DOCKER_HOST environment variable will be used. +This option may set DOCKER_HOST environment variable for the build container if needed. +`) cmd.Flags().StringVar(&buildFlags.Policy, "pull-policy", "", `Pull policy to use. Accepted values are always, never, and if-not-present. (default "always")`) cmd.Flags().StringVarP(&buildFlags.Registry, "buildpack-registry", "r", cfg.DefaultRegistryName, "Buildpack Registry by name") cmd.Flags().StringVar(&buildFlags.RunImage, "run-image", "", "Run image (defaults to default stack's run image)")