Add deepCopyRunConfig for copying buidler runConfig
Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
parent
aea31ab242
commit
9bcd5d2574
4 changed files with 124 additions and 9 deletions
|
@ -214,7 +214,8 @@ func (s *dispatchState) beginStage(stageName string, image builder.Image) {
|
|||
s.imageID = image.ImageID()
|
||||
|
||||
if image.RunConfig() != nil {
|
||||
s.runConfig = copyRunConfig(image.RunConfig()) // copy avoids referencing the same instance when 2 stages have the same base
|
||||
// copy avoids referencing the same instance when 2 stages have the same base
|
||||
s.runConfig = copyRunConfig(image.RunConfig())
|
||||
} else {
|
||||
s.runConfig = &container.Config{}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/go-connections/nat"
|
||||
lcUser "github.com/opencontainers/runc/libcontainer/user"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -385,14 +386,6 @@ func hashStringSlice(prefix string, slice []string) string {
|
|||
|
||||
type runConfigModifier func(*container.Config)
|
||||
|
||||
func copyRunConfig(runConfig *container.Config, modifiers ...runConfigModifier) *container.Config {
|
||||
copy := *runConfig
|
||||
for _, modifier := range modifiers {
|
||||
modifier(©)
|
||||
}
|
||||
return ©
|
||||
}
|
||||
|
||||
func withCmd(cmd []string) runConfigModifier {
|
||||
return func(runConfig *container.Config) {
|
||||
runConfig.Cmd = cmd
|
||||
|
@ -438,6 +431,48 @@ func withEntrypointOverride(cmd []string, entrypoint []string) runConfigModifier
|
|||
}
|
||||
}
|
||||
|
||||
func copyRunConfig(runConfig *container.Config, modifiers ...runConfigModifier) *container.Config {
|
||||
copy := *runConfig
|
||||
copy.Cmd = copyStringSlice(runConfig.Cmd)
|
||||
copy.Env = copyStringSlice(runConfig.Env)
|
||||
copy.Entrypoint = copyStringSlice(runConfig.Entrypoint)
|
||||
copy.OnBuild = copyStringSlice(runConfig.OnBuild)
|
||||
copy.Shell = copyStringSlice(runConfig.Shell)
|
||||
|
||||
if copy.Volumes != nil {
|
||||
copy.Volumes = make(map[string]struct{}, len(runConfig.Volumes))
|
||||
for k, v := range runConfig.Volumes {
|
||||
copy.Volumes[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if copy.ExposedPorts != nil {
|
||||
copy.ExposedPorts = make(nat.PortSet, len(runConfig.ExposedPorts))
|
||||
for k, v := range runConfig.ExposedPorts {
|
||||
copy.ExposedPorts[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if copy.Labels != nil {
|
||||
copy.Labels = make(map[string]string, len(runConfig.Labels))
|
||||
for k, v := range runConfig.Labels {
|
||||
copy.Labels[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
for _, modifier := range modifiers {
|
||||
modifier(©)
|
||||
}
|
||||
return ©
|
||||
}
|
||||
|
||||
func copyStringSlice(orig []string) []string {
|
||||
if orig == nil {
|
||||
return nil
|
||||
}
|
||||
return append([]string{}, orig...)
|
||||
}
|
||||
|
||||
// getShell is a helper function which gets the right shell for prefixing the
|
||||
// shell-form of RUN, ENTRYPOINT and CMD instructions
|
||||
func getShell(c *container.Config, os string) []string {
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/docker/docker/builder/remotecontext"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -133,6 +134,44 @@ func TestCopyRunConfig(t *testing.T) {
|
|||
|
||||
}
|
||||
|
||||
func fullMutableRunConfig() *container.Config {
|
||||
return &container.Config{
|
||||
Cmd: []string{"command", "arg1"},
|
||||
Env: []string{"env1=foo", "env2=bar"},
|
||||
ExposedPorts: nat.PortSet{
|
||||
"1000/tcp": {},
|
||||
"1001/tcp": {},
|
||||
},
|
||||
Volumes: map[string]struct{}{
|
||||
"one": {},
|
||||
"two": {},
|
||||
},
|
||||
Entrypoint: []string{"entry", "arg1"},
|
||||
OnBuild: []string{"first", "next"},
|
||||
Labels: map[string]string{
|
||||
"label1": "value1",
|
||||
"label2": "value2",
|
||||
},
|
||||
Shell: []string{"shell", "-c"},
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeepCopyRunConfig(t *testing.T) {
|
||||
runConfig := fullMutableRunConfig()
|
||||
copy := copyRunConfig(runConfig)
|
||||
assert.Equal(t, fullMutableRunConfig(), copy)
|
||||
|
||||
copy.Cmd[1] = "arg2"
|
||||
copy.Env[1] = "env2=new"
|
||||
copy.ExposedPorts["10002"] = struct{}{}
|
||||
copy.Volumes["three"] = struct{}{}
|
||||
copy.Entrypoint[1] = "arg2"
|
||||
copy.OnBuild[0] = "start"
|
||||
copy.Labels["label3"] = "value3"
|
||||
copy.Shell[0] = "sh"
|
||||
assert.Equal(t, fullMutableRunConfig(), runConfig)
|
||||
}
|
||||
|
||||
func TestChownFlagParsing(t *testing.T) {
|
||||
testFiles := map[string]string{
|
||||
"passwd": `root:x:0:0::/bin:/bin/false
|
||||
|
|
|
@ -6,13 +6,16 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/integration-cli/cli/build/fakecontext"
|
||||
"github.com/docker/docker/integration/util/request"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -129,3 +132,40 @@ func buildContainerIdsFilter(buildOutput io.Reader) (filters.Args, error) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildMultiStageParentConfig(t *testing.T) {
|
||||
dockerfile := `
|
||||
FROM busybox AS stage0
|
||||
ENV WHO=parent
|
||||
WORKDIR /foo
|
||||
|
||||
FROM stage0
|
||||
ENV WHO=sibling1
|
||||
WORKDIR sub1
|
||||
|
||||
FROM stage0
|
||||
WORKDIR sub2
|
||||
`
|
||||
ctx := context.Background()
|
||||
source := fakecontext.New(t, "", fakecontext.WithDockerfile(dockerfile))
|
||||
defer source.Close()
|
||||
|
||||
apiclient := testEnv.APIClient()
|
||||
resp, err := apiclient.ImageBuild(ctx,
|
||||
source.AsTarReader(t),
|
||||
types.ImageBuildOptions{
|
||||
Remove: true,
|
||||
ForceRemove: true,
|
||||
Tags: []string{"build1"},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
_, err = io.Copy(ioutil.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
image, _, err := apiclient.ImageInspectWithRaw(ctx, "build1")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "/foo/sub2", image.Config.WorkingDir)
|
||||
assert.Contains(t, image.Config.Env, "WHO=parent")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue