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()
|
s.imageID = image.ImageID()
|
||||||
|
|
||||||
if image.RunConfig() != nil {
|
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 {
|
} else {
|
||||||
s.runConfig = &container.Config{}
|
s.runConfig = &container.Config{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
"github.com/docker/docker/pkg/symlink"
|
"github.com/docker/docker/pkg/symlink"
|
||||||
"github.com/docker/docker/pkg/system"
|
"github.com/docker/docker/pkg/system"
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
lcUser "github.com/opencontainers/runc/libcontainer/user"
|
lcUser "github.com/opencontainers/runc/libcontainer/user"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
@ -385,14 +386,6 @@ func hashStringSlice(prefix string, slice []string) string {
|
||||||
|
|
||||||
type runConfigModifier func(*container.Config)
|
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 {
|
func withCmd(cmd []string) runConfigModifier {
|
||||||
return func(runConfig *container.Config) {
|
return func(runConfig *container.Config) {
|
||||||
runConfig.Cmd = cmd
|
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
|
// getShell is a helper function which gets the right shell for prefixing the
|
||||||
// shell-form of RUN, ENTRYPOINT and CMD instructions
|
// shell-form of RUN, ENTRYPOINT and CMD instructions
|
||||||
func getShell(c *container.Config, os string) []string {
|
func getShell(c *container.Config, os string) []string {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/docker/docker/builder/remotecontext"
|
"github.com/docker/docker/builder/remotecontext"
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
"github.com/docker/docker/pkg/idtools"
|
"github.com/docker/docker/pkg/idtools"
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"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) {
|
func TestChownFlagParsing(t *testing.T) {
|
||||||
testFiles := map[string]string{
|
testFiles := map[string]string{
|
||||||
"passwd": `root:x:0:0::/bin:/bin/false
|
"passwd": `root:x:0:0::/bin:/bin/false
|
||||||
|
|
|
@ -6,13 +6,16 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"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/integration/util/request"
|
||||||
"github.com/docker/docker/pkg/jsonmessage"
|
"github.com/docker/docker/pkg/jsonmessage"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"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