diff --git a/integration/dockerfiles/Dockerfile_test_run b/integration/dockerfiles/Dockerfile_test_run index f687c2ea8..61db07c02 100644 --- a/integration/dockerfiles/Dockerfile_test_run +++ b/integration/dockerfiles/Dockerfile_test_run @@ -21,3 +21,6 @@ RUN rm /etc/baz # Test with ARG ARG file RUN echo "run" > $file + +RUN echo "test home" > $HOME/file +COPY context/foo $HOME/foo diff --git a/integration/dockerfiles/Dockerfile_test_user_run b/integration/dockerfiles/Dockerfile_test_user_run index b9a3c2be4..d7f0ae3b8 100644 --- a/integration/dockerfiles/Dockerfile_test_user_run +++ b/integration/dockerfiles/Dockerfile_test_user_run @@ -18,4 +18,10 @@ RUN groupadd testgroup USER testuser:testgroup RUN echo "hey" > /tmp/foo USER testuser:1001 -RUN echo "hey2" >> /tmp/foo \ No newline at end of file +RUN echo "hey2" >> /tmp/foo + +RUN useradd -ms /bin/bash newuser +USER newuser +RUN echo "hi" > $HOME/file +COPY context/foo $HOME/foo + diff --git a/pkg/commands/run.go b/pkg/commands/run.go index 191e3d638..b0de6cbc9 100644 --- a/pkg/commands/run.go +++ b/pkg/commands/run.go @@ -17,12 +17,14 @@ limitations under the License. package commands import ( + "fmt" "os" "os/exec" "strconv" "strings" "syscall" + "github.com/GoogleContainerTools/kaniko/pkg/constants" "github.com/GoogleContainerTools/kaniko/pkg/dockerfile" "github.com/GoogleContainerTools/kaniko/pkg/util" "github.com/google/go-containerregistry/pkg/v1" @@ -59,7 +61,7 @@ func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr replacementEnvs := buildArgs.ReplacementEnvs(config.Env) - cmd.Env = replacementEnvs + cmd.Env = addDefaultHOME(config.User, replacementEnvs) cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} // If specified, run the command as a specific user @@ -113,6 +115,22 @@ func (r *RunCommand) ExecuteCommand(config *v1.Config, buildArgs *dockerfile.Bui return nil } +// addDefaultHOME adds the default value for HOME if it isn't already set +func addDefaultHOME(user string, envs []string) []string { + for _, env := range envs { + split := strings.SplitN(env, "=", 2) + if split[0] == constants.HOME { + return envs + } + } + // If user isn't set, set default value of HOME + if user == "" { + return append(envs, fmt.Sprintf("%s=%s", constants.HOME, constants.DefaultHOMEValue)) + } + // If user is set, set value of HOME to /home/${user} + return append(envs, fmt.Sprintf("%s=/home/%s", constants.HOME, user)) +} + // FilesToSnapshot returns nil for this command because we don't know which files // have changed, so we snapshot the entire system. func (r *RunCommand) FilesToSnapshot() []string { diff --git a/pkg/commands/run_test.go b/pkg/commands/run_test.go new file mode 100644 index 000000000..5a3971c68 --- /dev/null +++ b/pkg/commands/run_test.go @@ -0,0 +1,72 @@ +/* +Copyright 2018 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package commands + +import ( + "testing" + + "github.com/GoogleContainerTools/kaniko/testutil" +) + +func Test_addDefaultHOME(t *testing.T) { + tests := []struct { + name string + user string + initial []string + expected []string + }{ + { + name: "HOME already set", + user: "", + initial: []string{ + "HOME=/something", + "PATH=/something/else", + }, + expected: []string{ + "HOME=/something", + "PATH=/something/else", + }, + }, + { + name: "HOME isn't set, user isn't set", + user: "", + initial: []string{ + "PATH=/something/else", + }, + expected: []string{ + "PATH=/something/else", + "HOME=/root", + }, + }, + { + name: "HOME isn't set, user is set", + user: "newuser", + initial: []string{ + "PATH=/something/else", + }, + expected: []string{ + "PATH=/something/else", + "HOME=/home/newuser", + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actual := addDefaultHOME(test.user, test.initial) + testutil.CheckErrorAndDeepEqual(t, false, nil, test.expected, actual) + }) + } +} diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 6660ee65c..bcc658fca 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -54,6 +54,10 @@ const ( GCSBuildContextPrefix = "gs://" S3BuildContextPrefix = "s3://" LocalDirBuildContextPrefix = "dir://" + + // DefaultHOMEValue is the default value Docker sets for $HOME + HOME = "HOME" + DefaultHOMEValue = "/root" ) // KanikoBuildFiles is the list of files required to build kaniko diff --git a/pkg/executor/executor.go b/pkg/executor/executor.go index 7b82890ff..0ab0d98bb 100644 --- a/pkg/executor/executor.go +++ b/pkg/executor/executor.go @@ -30,7 +30,6 @@ import ( "github.com/google/go-containerregistry/pkg/authn/k8schain" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1" - "github.com/google/go-containerregistry/pkg/v1/empty" "github.com/google/go-containerregistry/pkg/v1/mutate" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/tarball" @@ -83,10 +82,7 @@ func DoBuild(k KanikoBuildArgs) (v1.Image, error) { if err := snapshotter.Init(); err != nil { return nil, err } - imageConfig, err := sourceImage.ConfigFile() - if sourceImage == empty.Image { - imageConfig.Config.Env = constants.ScratchEnvVars - } + imageConfig, err := util.RetrieveConfigFile(sourceImage) if err != nil { return nil, err } diff --git a/pkg/util/image_util.go b/pkg/util/image_util.go index 754f42443..0dd066d55 100644 --- a/pkg/util/image_util.go +++ b/pkg/util/image_util.go @@ -65,6 +65,18 @@ func RetrieveSourceImage(index int, buildArgs []string, stages []instructions.St return retrieveRemoteImage(currentBaseName) } +// RetrieveConfigFile returns the config file for an image +func RetrieveConfigFile(sourceImage v1.Image) (*v1.ConfigFile, error) { + imageConfig, err := sourceImage.ConfigFile() + if err != nil { + return nil, err + } + if sourceImage == empty.Image { + imageConfig.Config.Env = constants.ScratchEnvVars + } + return imageConfig, nil +} + func tarballImage(index int) (v1.Image, error) { tarPath := filepath.Join(constants.KanikoIntermediateStagesDir, strconv.Itoa(index)) logrus.Infof("Base image from previous stage %d found, using saved tar at path %s", index, tarPath) diff --git a/pkg/util/image_util_test.go b/pkg/util/image_util_test.go index 41cb387eb..419576d92 100644 --- a/pkg/util/image_util_test.go +++ b/pkg/util/image_util_test.go @@ -18,12 +18,13 @@ package util import ( "bytes" + "testing" + "github.com/GoogleContainerTools/kaniko/testutil" "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/empty" "github.com/moby/buildkit/frontend/dockerfile/instructions" "github.com/moby/buildkit/frontend/dockerfile/parser" - "testing" ) var (