mirror of https://github.com/docker/cli.git
Merge pull request #513 from shouze/reset-id-pair-during-build-to-avoid-cache-busting
Reset uid/gid to 0 in build context to fix cache busting issues on ADD/COPY
This commit is contained in:
commit
7b77ab5c60
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
|
"github.com/docker/docker/pkg/idtools"
|
||||||
"github.com/docker/docker/pkg/jsonmessage"
|
"github.com/docker/docker/pkg/jsonmessage"
|
||||||
"github.com/docker/docker/pkg/progress"
|
"github.com/docker/docker/pkg/progress"
|
||||||
"github.com/docker/docker/pkg/streamformatter"
|
"github.com/docker/docker/pkg/streamformatter"
|
||||||
|
@ -243,6 +244,7 @@ func runBuild(dockerCli command.Cli, options buildOptions) error {
|
||||||
excludes = build.TrimBuildFilesFromExcludes(excludes, relDockerfile, options.dockerfileFromStdin())
|
excludes = build.TrimBuildFilesFromExcludes(excludes, relDockerfile, options.dockerfileFromStdin())
|
||||||
buildCtx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
|
buildCtx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
|
||||||
ExcludePatterns: excludes,
|
ExcludePatterns: excludes,
|
||||||
|
ChownOpts: &idtools.IDPair{UID: 0, GID: 0},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -6,18 +6,57 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/internal/test"
|
"github.com/docker/cli/internal/test"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
|
"github.com/gotestyourself/gotestyourself/fs"
|
||||||
|
"github.com/gotestyourself/gotestyourself/skip"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestRunBuildResetsUidAndGidInContext(t *testing.T) {
|
||||||
|
skip.IfCondition(t, runtime.GOOS == "windows", "uid and gid not relevant on windows")
|
||||||
|
dest := fs.NewDir(t, "test-build-context-dest")
|
||||||
|
defer dest.Remove()
|
||||||
|
|
||||||
|
fakeImageBuild := func(_ context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) {
|
||||||
|
assert.NoError(t, archive.Untar(context, dest.Path(), nil))
|
||||||
|
|
||||||
|
body := new(bytes.Buffer)
|
||||||
|
return types.ImageBuildResponse{Body: ioutil.NopCloser(body)}, nil
|
||||||
|
}
|
||||||
|
cli := test.NewFakeCli(&fakeClient{imageBuildFunc: fakeImageBuild})
|
||||||
|
|
||||||
|
dir := fs.NewDir(t, "test-build-context",
|
||||||
|
fs.WithFile("foo", "some content", fs.AsUser(65534, 65534)),
|
||||||
|
fs.WithFile("Dockerfile", `
|
||||||
|
FROM alpine:3.6
|
||||||
|
COPY foo bar /
|
||||||
|
`),
|
||||||
|
)
|
||||||
|
defer dir.Remove()
|
||||||
|
|
||||||
|
options := newBuildOptions()
|
||||||
|
options.context = dir.Path()
|
||||||
|
|
||||||
|
err := runBuild(cli, options)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
files, err := ioutil.ReadDir(dest.Path())
|
||||||
|
require.NoError(t, err)
|
||||||
|
for _, fileInfo := range files {
|
||||||
|
assert.Equal(t, uint32(0), fileInfo.Sys().(*syscall.Stat_t).Uid)
|
||||||
|
assert.Equal(t, uint32(0), fileInfo.Sys().(*syscall.Stat_t).Gid)
|
||||||
|
}
|
||||||
|
}
|
||||||
func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) {
|
func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) {
|
||||||
dest, err := ioutil.TempDir("", "test-build-compress-dest")
|
dest, err := ioutil.TempDir("", "test-build-compress-dest")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -20,7 +20,7 @@ github.com/gogo/protobuf v0.4
|
||||||
github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4
|
github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4
|
||||||
github.com/gorilla/context v1.1
|
github.com/gorilla/context v1.1
|
||||||
github.com/gorilla/mux v1.1
|
github.com/gorilla/mux v1.1
|
||||||
github.com/gotestyourself/gotestyourself v1.1.0
|
github.com/gotestyourself/gotestyourself v1.2.0
|
||||||
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||||
github.com/mattn/go-shellwords v1.0.3
|
github.com/mattn/go-shellwords v1.0.3
|
||||||
github.com/Microsoft/go-winio v0.4.4
|
github.com/Microsoft/go-winio v0.4.4
|
||||||
|
@ -28,7 +28,7 @@ github.com/miekg/pkcs11 df8ae6ca730422dba20c768ff38ef7d79077a59f
|
||||||
github.com/mitchellh/mapstructure f3009df150dadf309fdee4a54ed65c124afad715
|
github.com/mitchellh/mapstructure f3009df150dadf309fdee4a54ed65c124afad715
|
||||||
github.com/moby/buildkit da2b9dc7dab99e824b2b1067ad7d0523e32dd2d9 https://github.com/dmcgowan/buildkit.git
|
github.com/moby/buildkit da2b9dc7dab99e824b2b1067ad7d0523e32dd2d9 https://github.com/dmcgowan/buildkit.git
|
||||||
github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c https://github.com/ijc25/Gotty
|
github.com/Nvveen/Gotty a8b993ba6abdb0e0c12b0125c603323a71c7790c https://github.com/ijc25/Gotty
|
||||||
github.com/opencontainers/go-digest 21dfd564fd89c944783d00d069f33e3e7123c448
|
github.com/opencontainers/go-digest 21dfd564fd89c944783d00d069f33e3e7123c448
|
||||||
github.com/opencontainers/image-spec v1.0.0
|
github.com/opencontainers/image-spec v1.0.0
|
||||||
github.com/opencontainers/runc d40db12e72a40109dfcf28539f5ee0930d2f0277
|
github.com/opencontainers/runc d40db12e72a40109dfcf28539f5ee0930d2f0277
|
||||||
github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
|
github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
|
||||||
|
|
|
@ -31,14 +31,17 @@ func AsUser(uid, gid int) PathOp {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithFile creates a file in the directory at path with content
|
// WithFile creates a file in the directory at path with content
|
||||||
func WithFile(filename, content string) PathOp {
|
func WithFile(filename, content string, ops ...PathOp) PathOp {
|
||||||
return func(path Path) error {
|
return func(path Path) error {
|
||||||
return createFile(path.Path(), filename, content)
|
fullpath := filepath.Join(path.Path(), filepath.FromSlash(filename))
|
||||||
|
if err := createFile(fullpath, content); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return applyPathOps(&File{path: fullpath}, ops)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFile(dir, filename, content string) error {
|
func createFile(fullpath string, content string) error {
|
||||||
fullpath := filepath.Join(dir, filepath.FromSlash(filename))
|
|
||||||
return ioutil.WriteFile(fullpath, []byte(content), 0644)
|
return ioutil.WriteFile(fullpath, []byte(content), 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +49,8 @@ func createFile(dir, filename, content string) error {
|
||||||
func WithFiles(files map[string]string) PathOp {
|
func WithFiles(files map[string]string) PathOp {
|
||||||
return func(path Path) error {
|
return func(path Path) error {
|
||||||
for filename, content := range files {
|
for filename, content := range files {
|
||||||
if err := createFile(path.Path(), filename, content); err != nil {
|
fullpath := filepath.Join(path.Path(), filepath.FromSlash(filename))
|
||||||
|
if err := createFile(fullpath, content); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,6 +65,35 @@ func FromDir(source string) PathOp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithDir creates a subdirectory in the directory at path. Additional PathOp
|
||||||
|
// can be used to modify the subdirectory
|
||||||
|
func WithDir(name string, ops ...PathOp) PathOp {
|
||||||
|
return func(path Path) error {
|
||||||
|
fullpath := filepath.Join(path.Path(), filepath.FromSlash(name))
|
||||||
|
err := os.MkdirAll(fullpath, 0755)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return applyPathOps(&Dir{path: fullpath}, ops)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyPathOps(path Path, ops []PathOp) error {
|
||||||
|
for _, op := range ops {
|
||||||
|
if err := op(path); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMode sets the file mode on the directory or file at path
|
||||||
|
func WithMode(mode os.FileMode) PathOp {
|
||||||
|
return func(path Path) error {
|
||||||
|
return os.Chmod(path.Path(), mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func copyDirectory(source, dest string) error {
|
func copyDirectory(source, dest string) error {
|
||||||
entries, err := ioutil.ReadDir(source)
|
entries, err := ioutil.ReadDir(source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -54,16 +54,17 @@ func IfCondition(t skipT, condition bool, msgAndArgs ...interface{}) {
|
||||||
t.Skip(formatWithCustomMessage(source, formatMessage(msgAndArgs...)))
|
t.Skip(formatWithCustomMessage(source, formatMessage(msgAndArgs...)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getConditionSource returns the condition string by reading it from the file
|
||||||
|
// identified in the callstack. In golang 1.9 the line number changed from
|
||||||
|
// being the line where the statement ended to the line where the statement began.
|
||||||
func getConditionSource() (string, error) {
|
func getConditionSource() (string, error) {
|
||||||
const callstackIndex = 3
|
lines, err := getSourceLine()
|
||||||
lines, err := getSourceLine(callstackIndex)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range lines {
|
for i := range lines {
|
||||||
source := strings.Join(lines[len(lines)-i-1:], "\n")
|
node, err := parser.ParseExpr(getSource(lines, i))
|
||||||
node, err := parser.ParseExpr(source)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return getConditionArgFromAST(node)
|
return getConditionArgFromAST(node)
|
||||||
}
|
}
|
||||||
|
@ -79,7 +80,8 @@ const maxContextLines = 10
|
||||||
// few preceding lines. To properly parse the AST a complete statement is
|
// few preceding lines. To properly parse the AST a complete statement is
|
||||||
// required, and that statement may be split across multiple lines, so include
|
// required, and that statement may be split across multiple lines, so include
|
||||||
// up to maxContextLines.
|
// up to maxContextLines.
|
||||||
func getSourceLine(stackIndex int) ([]string, error) {
|
func getSourceLine() ([]string, error) {
|
||||||
|
const stackIndex = 3
|
||||||
_, filename, line, ok := runtime.Caller(stackIndex)
|
_, filename, line, ok := runtime.Caller(stackIndex)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("failed to get caller info")
|
return nil, errors.New("failed to get caller info")
|
||||||
|
@ -94,11 +96,8 @@ func getSourceLine(stackIndex int) ([]string, error) {
|
||||||
if len(lines) < line {
|
if len(lines) < line {
|
||||||
return nil, errors.Errorf("file %s does not have line %d", filename, line)
|
return nil, errors.Errorf("file %s does not have line %d", filename, line)
|
||||||
}
|
}
|
||||||
firstLine := line - maxContextLines
|
firstLine, lastLine := getSourceLinesRange(line, len(lines))
|
||||||
if firstLine < 0 {
|
return lines[firstLine:lastLine], nil
|
||||||
firstLine = 0
|
|
||||||
}
|
|
||||||
return lines[firstLine:line], nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getConditionArgFromAST(node ast.Expr) (string, error) {
|
func getConditionArgFromAST(node ast.Expr) (string, error) {
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
// +build !go1.9,!go.10,!go.11,!go1.12
|
||||||
|
|
||||||
|
package skip
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func getSourceLinesRange(line int, _ int) (int, int) {
|
||||||
|
firstLine := line - maxContextLines
|
||||||
|
if firstLine < 0 {
|
||||||
|
firstLine = 0
|
||||||
|
}
|
||||||
|
return firstLine, line
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSource(lines []string, i int) string {
|
||||||
|
return strings.Join(lines[len(lines)-i-1:], "\n")
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
// +build go1.9
|
||||||
|
|
||||||
|
package skip
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func getSourceLinesRange(line int, lines int) (int, int) {
|
||||||
|
lastLine := line + maxContextLines
|
||||||
|
if lastLine > lines {
|
||||||
|
lastLine = lines
|
||||||
|
}
|
||||||
|
return line - 1, lastLine
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSource(lines []string, i int) string {
|
||||||
|
return strings.Join(lines[:i], "\n")
|
||||||
|
}
|
Loading…
Reference in New Issue