Merge pull request #110 from tonistiigi/improve-copy

dockerfile: improve copy implementation
docker-18.09
Akihiro Suda 2017-09-02 14:21:00 +09:00 committed by GitHub
commit 659e899c3e
2 changed files with 41 additions and 16 deletions

View File

@ -3,8 +3,8 @@ package dockerfile2llb
import (
"bytes"
"context"
"fmt"
"path"
"path/filepath"
"strconv"
"strings"
@ -184,6 +184,8 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand) error {
var args []string = c.CmdLine
if c.PrependShell {
args = append(defaultShell(), strings.Join(args, " "))
} else if d.image.Config.Entrypoint != nil {
args = append(d.image.Config.Entrypoint, args...)
}
d.state = d.state.Run(llb.Args(args)).Root()
return nil
@ -201,20 +203,23 @@ func dispatchWorkdir(d *dispatchState, c *instructions.WorkdirCommand) error {
func dispatchCopy(d *dispatchState, c instructions.SourcesAndDest, sourceState llb.State) error {
// TODO: this should use CopyOp instead. Current implementation is inefficient and doesn't match Dockerfile path suffixes rules
img := llb.Image("docker.io/library/alpine@sha256:1072e499f3f655a032e88542330cf75b02e7bdf673278f701d7ba61629ee3ebe")
img := llb.Image("tonistiigi/copy@sha256:260a4355be76e0609518ebd7c0e026831c80b8908d4afd3f8e8c942645b1e5cf")
destDir := filepath.Join("/dest", toWorkingDir(d.state, c.Dest())) // TODO: detect file source + no dest path case
srcs := make([]string, 0, len(c.Sources()))
for _, src := range c.Sources() {
src = path.Join("/src", toWorkingDir(sourceState, src))
if src == "/src" {
src += "/."
dest := path.Join("/dest", toWorkingDir(d.state, c.Dest()))
args := []string{"copy"}
mounts := make([]llb.RunOption, 0, len(c.Sources()))
for i, src := range c.Sources() {
d, f := splitWildcards(src)
if f == "" {
f = path.Base(src)
}
srcs = append(srcs, src)
target := path.Join(fmt.Sprintf("/src-%d", i), f)
args = append(args, target)
mounts = append(mounts, llb.AddMount(target, sourceState, llb.SourcePath(d), llb.Readonly))
}
run := img.Run(llb.Shlexf("sh -c \"mkdir -p %s && cp -a %s %s\"", destDir, strings.Join(srcs, " "), destDir))
run.AddMount("/src", sourceState, llb.Readonly)
args = append(args, dest)
run := img.Run(append([]llb.RunOption{llb.Args(args)}, mounts...)...)
d.state = run.AddMount("/dest", d.state)
return nil
}
@ -255,3 +260,19 @@ func dispatchCmd(d *dispatchState, c *instructions.CmdCommand) error {
// d.image.Config.ArgsEscaped = true
return nil
}
func splitWildcards(name string) (string, string) {
i := 0
for ; i < len(name); i++ {
ch := name[i]
if ch == '\\' {
i++
} else if ch == '*' || ch == '?' || ch == '[' {
break
}
}
if i == len(name) {
return name, ""
}
return path.Dir(name[:i]), path.Base(name[:i]) + name[i:]
}

View File

@ -137,16 +137,16 @@ func (e *execOp) ContentKeys(ctx context.Context, inputs [][]digest.Digest, refs
// contentKey for exec uses content based checksum for mounts and definition
// based checksum for root
rootIndex := -1
skipped := make([]int, 0)
skip := true
srcs := make([]string, len(refs))
for _, m := range e.op.Mounts {
if m.Input != pb.Empty {
if m.Dest != pb.RootMount {
if m.Dest != pb.RootMount && m.Readonly { // could also include rw if they don't have a selector, but not sure if helps performance
srcs[int(m.Input)] = path.Join("/", m.Selector)
skip = false
} else {
rootIndex = int(m.Input)
skipped = append(skipped, int(m.Input))
}
}
}
@ -180,16 +180,20 @@ func (e *execOp) ContentKeys(ctx context.Context, inputs [][]digest.Digest, refs
}
var out []digest.Digest
inputKeys := make([]digest.Digest, len(skipped))
for _, cacheKeys := range inputs {
for i := range inputKeys {
inputKeys[i] = cacheKeys[skipped[i]]
}
dt, err := json.Marshal(struct {
Type string
Sources []digest.Digest
Root digest.Digest
Inputs []digest.Digest
Exec *pb.ExecOp
}{
Type: execCacheType,
Sources: dgsts,
Root: cacheKeys[rootIndex],
Inputs: inputKeys,
Exec: e.op,
})
if err != nil {