From 0d419b5faa041380462486f0c12492a8712a4fab Mon Sep 17 00:00:00 2001 From: Yuichiro Kaneko Date: Sun, 8 Jul 2018 09:36:50 +0900 Subject: [PATCH] Ensure RUN instruction to run without Healthcheck Before this commit Healthcheck run if HEALTHCHECK instruction appears before RUN instruction. By passing `withoutHealthcheck` to `copyRunConfig`, always RUN instruction run without Healthcheck. Fix: https://github.com/moby/moby/issues/37362 Signed-off-by: Yuichiro Kaneko (cherry picked from commit 44e08d8a7d1249a1956018c6c3d3655642a4f273) Signed-off-by: Sebastiaan van Stijn Upstream-commit: 160be68bbd6974eef7a4aebcfce2af5971baafe7 Component: engine --- .../engine/builder/dockerfile/dispatchers.go | 3 +- .../builder/dockerfile/dispatchers_test.go | 61 +++++++++++++++++++ .../engine/builder/dockerfile/internals.go | 12 ++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/components/engine/builder/dockerfile/dispatchers.go b/components/engine/builder/dockerfile/dispatchers.go index 5d72237cdb4..c110ab40b85 100644 --- a/components/engine/builder/dockerfile/dispatchers.go +++ b/components/engine/builder/dockerfile/dispatchers.go @@ -365,7 +365,8 @@ func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error { runConfig := copyRunConfig(stateRunConfig, withCmd(cmdFromArgs), withEnv(append(stateRunConfig.Env, buildArgs...)), - withEntrypointOverride(saveCmd, strslice.StrSlice{""})) + withEntrypointOverride(saveCmd, strslice.StrSlice{""}), + withoutHealthcheck()) // set config as already being escaped, this prevents double escaping on windows runConfig.ArgsEscaped = true diff --git a/components/engine/builder/dockerfile/dispatchers_test.go b/components/engine/builder/dockerfile/dispatchers_test.go index c61a45b03a1..efa3a1f47eb 100644 --- a/components/engine/builder/dockerfile/dispatchers_test.go +++ b/components/engine/builder/dockerfile/dispatchers_test.go @@ -473,3 +473,64 @@ func TestRunWithBuildArgs(t *testing.T) { // Check that runConfig.Cmd has not been modified by run assert.Check(t, is.DeepEqual(origCmd, sb.state.runConfig.Cmd)) } + +func TestRunIgnoresHealthcheck(t *testing.T) { + b := newBuilderWithMockBackend() + args := NewBuildArgs(make(map[string]*string)) + sb := newDispatchRequest(b, '`', nil, args, newStagesBuildResults()) + b.disableCommit = false + + origCmd := strslice.StrSlice([]string{"cmd", "in", "from", "image"}) + + imageCache := &mockImageCache{ + getCacheFunc: func(parentID string, cfg *container.Config) (string, error) { + return "", nil + }, + } + + mockBackend := b.docker.(*MockBackend) + mockBackend.makeImageCacheFunc = func(_ []string) builder.ImageCache { + return imageCache + } + b.imageProber = newImageProber(mockBackend, nil, false) + mockBackend.getImageFunc = func(_ string) (builder.Image, builder.ROLayer, error) { + return &mockImage{ + id: "abcdef", + config: &container.Config{Cmd: origCmd}, + }, nil, nil + } + mockBackend.containerCreateFunc = func(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) { + return container.ContainerCreateCreatedBody{ID: "12345"}, nil + } + mockBackend.commitFunc = func(cfg backend.CommitConfig) (image.ID, error) { + return "", nil + } + from := &instructions.Stage{BaseName: "abcdef"} + err := initializeStage(sb, from) + assert.NilError(t, err) + + expectedTest := []string{"CMD-SHELL", "curl -f http://localhost/ || exit 1"} + cmd := &instructions.HealthCheckCommand{ + Health: &container.HealthConfig{ + Test: expectedTest, + }, + } + assert.NilError(t, dispatch(sb, cmd)) + assert.Assert(t, sb.state.runConfig.Healthcheck != nil) + + mockBackend.containerCreateFunc = func(config types.ContainerCreateConfig) (container.ContainerCreateCreatedBody, error) { + // Check the Healthcheck is disabled. + assert.Check(t, is.DeepEqual([]string{"NONE"}, config.Config.Healthcheck.Test)) + return container.ContainerCreateCreatedBody{ID: "123456"}, nil + } + + sb.state.buildArgs.AddArg("one", strPtr("two")) + run := &instructions.RunCommand{ + ShellDependantCmdLine: instructions.ShellDependantCmdLine{ + CmdLine: strslice.StrSlice{"echo foo"}, + PrependShell: true, + }, + } + assert.NilError(t, dispatch(sb, run)) + assert.Check(t, is.DeepEqual(expectedTest, sb.state.runConfig.Healthcheck.Test)) +} diff --git a/components/engine/builder/dockerfile/internals.go b/components/engine/builder/dockerfile/internals.go index 1b3a5b0f03a..d892285a1c8 100644 --- a/components/engine/builder/dockerfile/internals.go +++ b/components/engine/builder/dockerfile/internals.go @@ -343,6 +343,18 @@ func withEntrypointOverride(cmd []string, entrypoint []string) runConfigModifier } } +// withoutHealthcheck disables healthcheck. +// +// The dockerfile RUN instruction expect to run without healthcheck +// so the runConfig Healthcheck needs to be disabled. +func withoutHealthcheck() runConfigModifier { + return func(runConfig *container.Config) { + runConfig.Healthcheck = &container.HealthConfig{ + Test: []string{"NONE"}, + } + } +} + func copyRunConfig(runConfig *container.Config, modifiers ...runConfigModifier) *container.Config { copy := *runConfig copy.Cmd = copyStringSlice(runConfig.Cmd)