mirror of https://github.com/docker/cli.git
Reset idPair during build to avoid cache busting
Signed-off-by: Sébastien HOUZÉ <cto@verylastroom.com>
This commit is contained in:
parent
b60413264d
commit
7e407610d4
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"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())
|
||||
buildCtx, err = archive.TarWithOptions(contextDir, &archive.TarOptions{
|
||||
ExcludePatterns: excludes,
|
||||
ChownOpts: &idtools.IDPair{UID: 0, GID: 0},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -6,18 +6,57 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"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/require"
|
||||
"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) {
|
||||
dest, err := ioutil.TempDir("", "test-build-compress-dest")
|
||||
require.NoError(t, err)
|
||||
|
|
|
@ -20,7 +20,7 @@ github.com/gogo/protobuf v0.4
|
|||
github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4
|
||||
github.com/gorilla/context 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/mattn/go-shellwords v1.0.3
|
||||
github.com/Microsoft/go-winio v0.4.4
|
||||
|
|
|
@ -31,14 +31,17 @@ func AsUser(uid, gid int) PathOp {
|
|||
}
|
||||
|
||||
// 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 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 {
|
||||
fullpath := filepath.Join(dir, filepath.FromSlash(filename))
|
||||
func createFile(fullpath string, content string) error {
|
||||
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 {
|
||||
return func(path Path) error {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
entries, err := ioutil.ReadDir(source)
|
||||
if err != nil {
|
||||
|
|
|
@ -54,16 +54,17 @@ func IfCondition(t skipT, condition bool, msgAndArgs ...interface{}) {
|
|||
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) {
|
||||
const callstackIndex = 3
|
||||
lines, err := getSourceLine(callstackIndex)
|
||||
lines, err := getSourceLine()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for i := range lines {
|
||||
source := strings.Join(lines[len(lines)-i-1:], "\n")
|
||||
node, err := parser.ParseExpr(source)
|
||||
node, err := parser.ParseExpr(getSource(lines, i))
|
||||
if err == nil {
|
||||
return getConditionArgFromAST(node)
|
||||
}
|
||||
|
@ -79,7 +80,8 @@ const maxContextLines = 10
|
|||
// few preceding lines. To properly parse the AST a complete statement is
|
||||
// required, and that statement may be split across multiple lines, so include
|
||||
// up to maxContextLines.
|
||||
func getSourceLine(stackIndex int) ([]string, error) {
|
||||
func getSourceLine() ([]string, error) {
|
||||
const stackIndex = 3
|
||||
_, filename, line, ok := runtime.Caller(stackIndex)
|
||||
if !ok {
|
||||
return nil, errors.New("failed to get caller info")
|
||||
|
@ -94,11 +96,8 @@ func getSourceLine(stackIndex int) ([]string, error) {
|
|||
if len(lines) < line {
|
||||
return nil, errors.Errorf("file %s does not have line %d", filename, line)
|
||||
}
|
||||
firstLine := line - maxContextLines
|
||||
if firstLine < 0 {
|
||||
firstLine = 0
|
||||
}
|
||||
return lines[firstLine:line], nil
|
||||
firstLine, lastLine := getSourceLinesRange(line, len(lines))
|
||||
return lines[firstLine:lastLine], nil
|
||||
}
|
||||
|
||||
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