From 71d688dd63b727a1a29fa0b0ad4e33cebf24f658 Mon Sep 17 00:00:00 2001 From: Maia McCormick Date: Thu, 11 Jul 2019 16:13:21 -0400 Subject: [PATCH] tiltfile: add 'entrypoint' opt arg to docker_build and custom_build, translate into image target [ch2886] (#1833) * tiltfile: add 'entrypoint' opt arg to docker_build and custom_build, translate into image target * tests * rename a fastBuild-related field to free up better names * fix comment * fix goimports asdfg why are my filewatchers broken? --- internal/tiltfile/docker.go | 32 ++++++++--- .../tiltfile/tiltfile_docker_compose_test.go | 14 +++++ internal/tiltfile/tiltfile_state.go | 12 +++- internal/tiltfile/tiltfile_test.go | 56 +++++++++++++++++++ 4 files changed, 106 insertions(+), 8 deletions(-) diff --git a/internal/tiltfile/docker.go b/internal/tiltfile/docker.go index ca2060cde3..9a1b80c63e 100644 --- a/internal/tiltfile/docker.go +++ b/internal/tiltfile/docker.go @@ -25,14 +25,17 @@ type dockerImage struct { baseDockerfile dockerfile.Dockerfile configurationRef container.RefSelector deploymentRef reference.Named - syncs []pathSync - runs []model.Run - entrypoint string cachePaths []string - hotReload bool matchInEnvVars bool ignores []string onlys []string + entrypoint model.Cmd // optional: if specified, we override the image entrypoint/k8s command with this + + // fast-build properties -- will be deprecated + syncs []pathSync + runs []model.Run + fbEntrypoint string + hotReload bool dbDockerfilePath localPath dbDockerfile dockerfile.Dockerfile @@ -81,7 +84,7 @@ func (d *dockerImage) Type() dockerImageBuildType { } func (s *tiltfileState) dockerBuild(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { - var dockerRef string + var dockerRef, entrypoint string var contextVal, dockerfilePathVal, buildArgs, dockerfileContentsVal, cacheVal, liveUpdateVal, ignoreVal, onlyVal starlark.Value var matchInEnvVars bool if err := starlark.UnpackArgs(fn.Name(), args, kwargs, @@ -95,6 +98,7 @@ func (s *tiltfileState) dockerBuild(thread *starlark.Thread, fn *starlark.Builti "match_in_env_vars?", &matchInEnvVars, "ignore?", &ignoreVal, "only?", &onlyVal, + "entrypoint?", &entrypoint, ); err != nil { return nil, err } @@ -178,6 +182,11 @@ func (s *tiltfileState) dockerBuild(thread *starlark.Thread, fn *starlark.Builti return nil, error2 } + var entrypointCmd model.Cmd + if entrypoint != "" { + entrypointCmd = model.ToShellCmd(entrypoint) + } + r := &dockerImage{ dbDockerfilePath: dockerfilePath, dbDockerfile: dockerfile.Dockerfile(dockerfileContents), @@ -189,6 +198,7 @@ func (s *tiltfileState) dockerBuild(thread *starlark.Thread, fn *starlark.Builti matchInEnvVars: matchInEnvVars, ignores: ignores, onlys: onlys, + entrypoint: entrypointCmd, } err = s.buildIndex.addImage(r) if err != nil { @@ -207,7 +217,7 @@ func (s *tiltfileState) fastBuildForImage(image *dockerImage) model.FastBuild { BaseDockerfile: image.baseDockerfile.String(), Syncs: s.syncsToDomain(image), Runs: image.runs, - Entrypoint: model.ToShellCmd(image.entrypoint), + Entrypoint: model.ToShellCmd(image.fbEntrypoint), HotReload: image.hotReload, } } @@ -219,6 +229,7 @@ func (s *tiltfileState) customBuild(thread *starlark.Thread, fn *starlark.Builti var disablePush bool var liveUpdateVal, ignoreVal starlark.Value var matchInEnvVars bool + var entrypoint string err := starlark.UnpackArgs(fn.Name(), args, kwargs, "ref", &dockerRef, @@ -229,6 +240,7 @@ func (s *tiltfileState) customBuild(thread *starlark.Thread, fn *starlark.Builti "live_update?", &liveUpdateVal, "match_in_env_vars?", &matchInEnvVars, "ignore?", &ignoreVal, + "entrypoint?", &entrypoint, ) if err != nil { return nil, err @@ -269,6 +281,11 @@ func (s *tiltfileState) customBuild(thread *starlark.Thread, fn *starlark.Builti return nil, error } + var entrypointCmd model.Cmd + if entrypoint != "" { + entrypointCmd = model.ToShellCmd(entrypoint) + } + img := &dockerImage{ configurationRef: container.NewRefSelector(ref), customCommand: command, @@ -278,6 +295,7 @@ func (s *tiltfileState) customBuild(thread *starlark.Thread, fn *starlark.Builti liveUpdate: liveUpdate, matchInEnvVars: matchInEnvVars, ignores: ignores, + entrypoint: entrypointCmd, } err = s.buildIndex.addImage(img) @@ -397,7 +415,7 @@ func (s *tiltfileState) fastBuild(thread *starlark.Thread, fn *starlark.Builtin, baseDockerfilePath: baseDockerfilePath, baseDockerfile: df, configurationRef: container.NewRefSelector(ref), - entrypoint: entrypoint, + fbEntrypoint: entrypoint, cachePaths: cachePaths, } err = s.buildIndex.addImage(r) diff --git a/internal/tiltfile/tiltfile_docker_compose_test.go b/internal/tiltfile/tiltfile_docker_compose_test.go index 9902a55d3c..13650b7988 100644 --- a/internal/tiltfile/tiltfile_docker_compose_test.go +++ b/internal/tiltfile/tiltfile_docker_compose_test.go @@ -490,6 +490,20 @@ dc_resource('no-svc-with-this-name-eek', 'gcr.io/foo') f.loadErrString("no Docker Compose service found with name") } +func TestDockerComposeDoesntSupportEntrypointOverride(t *testing.T) { + f := newFixture(t) + defer f.TearDown() + + f.dockerfile("foo/Dockerfile") + f.file("docker-compose.yml", simpleConfig) + f.file("Tiltfile", `docker_build('gcr.io/foo', './foo', entrypoint='./foo') +docker_compose('docker-compose.yml') +dc_resource('foo', 'gcr.io/foo') +`) + + f.loadErrString("docker_build/custom_build.entrypoint not supported for Docker Compose resources") +} + func (f *fixture) assertDcManifest(name string, opts ...interface{}) model.Manifest { m := f.assertNextManifest(name) diff --git a/internal/tiltfile/tiltfile_state.go b/internal/tiltfile/tiltfile_state.go index 19bdd0029b..ff3270b352 100644 --- a/internal/tiltfile/tiltfile_state.go +++ b/internal/tiltfile/tiltfile_state.go @@ -916,6 +916,10 @@ func (s *tiltfileState) imgTargetsForDependencyIDsHelper(ids []model.TargetID, c MatchInEnvVars: image.matchInEnvVars, }.WithCachePaths(image.cachePaths) + if !image.entrypoint.Empty() { + iTarget = iTarget.WithOverrideCommand(image.entrypoint) + } + lu := image.liveUpdate switch image.Type() { @@ -982,6 +986,13 @@ func (s *tiltfileState) translateDC(dc dcResourceSet) ([]model.Manifest, error) if err != nil { return nil, errors.Wrapf(err, "getting image build info for %s", svc.Name) } + + for _, iTarg := range iTargets { + if !iTarg.OverrideCmd.Empty() { + return nil, fmt.Errorf("docker_build/custom_build.entrypoint not supported for Docker Compose resources") + } + } + m = m.WithImageTargets(iTargets) err = s.checkForImpossibleLiveUpdates(m) @@ -996,7 +1007,6 @@ func (s *tiltfileState) translateDC(dc dcResourceSet) ([]model.Manifest, error) s.configFiles = sliceutils.DedupedAndSorted(append(s.configFiles, configFiles...)) } if len(dc.configPaths) != 0 { - s.configFiles = sliceutils.DedupedAndSorted(append(s.configFiles, dc.configPaths...)) } diff --git a/internal/tiltfile/tiltfile_test.go b/internal/tiltfile/tiltfile_test.go index 0973f2ef3d..e60b839cca 100644 --- a/internal/tiltfile/tiltfile_test.go +++ b/internal/tiltfile/tiltfile_test.go @@ -11,6 +11,8 @@ import ( appsv1 "k8s.io/api/apps/v1" + "github.com/windmilleng/tilt/internal/sliceutils" + "github.com/stretchr/testify/assert" "github.com/windmilleng/wmclient/pkg/analytics" v1 "k8s.io/api/core/v1" @@ -3592,6 +3594,42 @@ func TestDisableFeatureThatDoesntExist(t *testing.T) { f.loadErrString("Unknown feature flag: testflag") } +func TestDockerBuildEntrypoint(t *testing.T) { + f := newFixture(t) + defer f.TearDown() + + f.dockerfile("Dockerfile") + f.yaml("foo.yaml", deployment("foo", image("gcr.io/foo"))) + f.file("Tiltfile", ` +docker_build('gcr.io/foo', '.', entrypoint="/bin/the_app") +k8s_yaml('foo.yaml') +`) + + f.load() + f.assertNextManifest("foo", db(image("gcr.io/foo"), entrypoint("/bin/the_app"))) +} + +func TestCustomBuildEntrypoint(t *testing.T) { + f := newFixture(t) + defer f.TearDown() + + f.dockerfile("Dockerfile") + f.yaml("foo.yaml", deployment("foo", image("gcr.io/foo"))) + f.file("Tiltfile", ` +custom_build('gcr.io/foo', 'docker build -t $EXPECTED_REF foo', + ['foo'], entrypoint="/bin/the_app") +k8s_yaml('foo.yaml') +`) + + f.load() + f.assertNextManifest("foo", cb( + image("gcr.io/foo"), + deps(f.JoinPath("foo")), + cmd("docker build -t $EXPECTED_REF foo"), + entrypoint("/bin/the_app")), + ) +} + type fixture struct { ctx context.Context out *bytes.Buffer @@ -3874,6 +3912,11 @@ func (f *fixture) assertNextManifest(name string, opts ...interface{}) model.Man for _, matcher := range opt.matchers { switch matcher := matcher.(type) { + case entrypointHelper: + if !sliceutils.StringSliceEquals(matcher.cmd.Argv, image.OverrideCmd.Argv) { + f.t.Fatalf("expected OverrideCommand (aka entrypoint) %v, got %v", + matcher.cmd.Argv, image.OverrideCmd.Argv) + } case nestedFBHelper: dbInfo := image.DockerBuildInfo() if matcher.fb == nil { @@ -3936,6 +3979,11 @@ func (f *fixture) assertNextManifest(name string, opts ...interface{}) model.Man assert.Equal(f.t, matcher.tag, cbInfo.Tag) case disablePushHelper: assert.Equal(f.t, matcher.disabled, cbInfo.DisablePush) + case entrypointHelper: + if !sliceutils.StringSliceEquals(matcher.cmd.Argv, image.OverrideCmd.Argv) { + f.t.Fatalf("expected OverrideCommand (aka entrypoint) %v, got %v", + matcher.cmd.Argv, image.OverrideCmd.Argv) + } case fbHelper: if cbInfo.Fast.Empty() { f.t.Fatalf("Expected manifest %v to have fast build, but it didn't", m.Name) @@ -4375,6 +4423,14 @@ func cb(img imageHelper, opts ...interface{}) cbHelper { return cbHelper{img, opts} } +type entrypointHelper struct { + cmd model.Cmd +} + +func entrypoint(command string) entrypointHelper { + return entrypointHelper{model.ToShellCmd(command)} +} + type nestedFBHelper struct { fb *fbHelper }