local: allow followpaths for local source

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
docker-18.09
Tonis Tiigi 2018-06-04 14:08:29 -07:00
parent 103cd072d6
commit 055dcb6c09
7 changed files with 107 additions and 4 deletions

View File

@ -21,6 +21,7 @@ import (
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/snapshots"
"github.com/containerd/continuity/fs/fstest"
"github.com/docker/distribution/manifest/schema2"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/identity"
@ -50,9 +51,64 @@ func TestClientIntegration(t *testing.T) {
testBasicCacheImportExport,
testCachedMounts,
testProxyEnv,
testLocalSymlinkEscape,
})
}
func testLocalSymlinkEscape(t *testing.T, sb integration.Sandbox) {
t.Parallel()
requiresLinux(t)
c, err := New(sb.Address())
require.NoError(t, err)
defer c.Close()
test := []byte(`set -x
[[ -L /mount/foo ]]
[[ -L /mount/sub/bar ]]
[[ -L /mount/bax ]]
[[ -f /mount/bay ]]
[[ ! -f /mount/baz ]]
[[ ! -f /mount/etc/passwd ]]
[[ ! -f /mount/etc/group ]]
[[ $(readlink /mount/foo) == "/etc/passwd" ]]
[[ $(readlink /mount/sub/bar) == "../../../etc/group" ]]
`)
dir, err := tmpdir(
// point to absolute path that is not part of dir
fstest.Symlink("/etc/passwd", "foo"),
fstest.CreateDir("sub", 0700),
// point outside of the dir
fstest.Symlink("../../../etc/group", "sub/bar"),
// regular valid symlink
fstest.Symlink("bay", "bax"),
// target for symlink (not requested)
fstest.CreateFile("bay", []byte{}, 0600),
// unused file that shouldn't be included
fstest.CreateFile("baz", []byte{}, 0600),
fstest.CreateFile("test.sh", test, 0700),
)
require.NoError(t, err)
defer os.RemoveAll(dir)
local := llb.Local("mylocal", llb.FollowPaths([]string{
"test.sh", "foo", "sub/bar", "bax",
}))
st := llb.Image("busybox:latest").
Run(llb.Shlex(`sh /mount/test.sh`), llb.AddMount("/mount", local, llb.Readonly))
def, err := st.Marshal()
require.NoError(t, err)
_, err = c.Solve(context.TODO(), def, SolveOpt{
LocalDirs: map[string]string{
"mylocal": dir,
},
}, nil)
require.NoError(t, err)
}
func testRelativeWorkDir(t *testing.T, sb integration.Sandbox) {
t.Parallel()
requiresLinux(t)
@ -1182,3 +1238,14 @@ func testInvalidExporter(t *testing.T, sb integration.Sandbox) {
checkAllReleasable(t, c, sb, true)
}
func tmpdir(appliers ...fstest.Applier) (string, error) {
tmpdir, err := ioutil.TempDir("", "buildkit-client")
if err != nil {
return "", err
}
if err := fstest.Apply(appliers...).Apply(tmpdir); err != nil {
return "", err
}
return tmpdir, nil
}

View File

@ -210,6 +210,9 @@ func Local(name string, opts ...LocalOption) State {
if gi.IncludePatterns != "" {
attrs[pb.AttrIncludePatterns] = gi.IncludePatterns
}
if gi.FollowPaths != "" {
attrs[pb.AttrFollowPaths] = gi.FollowPaths
}
if gi.ExcludePatterns != "" {
attrs[pb.AttrExcludePatterns] = gi.ExcludePatterns
}
@ -248,6 +251,17 @@ func IncludePatterns(p []string) LocalOption {
})
}
func FollowPaths(p []string) LocalOption {
return localOptionFunc(func(li *LocalInfo) {
if len(p) == 0 {
li.FollowPaths = ""
return
}
dt, _ := json.Marshal(p) // empty on error
li.FollowPaths = string(dt)
})
}
func ExcludePatterns(p []string) LocalOption {
return localOptionFunc(func(li *LocalInfo) {
if len(p) == 0 {
@ -270,6 +284,7 @@ type LocalInfo struct {
SessionID string
IncludePatterns string
ExcludePatterns string
FollowPaths string
SharedKeyHint string
}

View File

@ -12,10 +12,11 @@ import (
"google.golang.org/grpc"
)
func sendDiffCopy(stream grpc.Stream, dir string, includes, excludes []string, progress progressCb, _map func(*fsutil.Stat) bool) error {
func sendDiffCopy(stream grpc.Stream, dir string, includes, excludes, followPaths []string, progress progressCb, _map func(*fsutil.Stat) bool) error {
return fsutil.Send(stream.Context(), stream, dir, &fsutil.WalkOpt{
ExcludePatterns: excludes,
IncludePatterns: includes,
FollowPaths: followPaths,
Map: _map,
}, progress)
}

View File

@ -18,6 +18,7 @@ const (
keyOverrideExcludes = "override-excludes"
keyIncludePatterns = "include-patterns"
keyExcludePatterns = "exclude-patterns"
keyFollowPaths = "followpaths"
keyDirName = "dir-name"
)
@ -87,6 +88,8 @@ func (sp *fsSyncProvider) handle(method string, stream grpc.ServerStream) (retEr
}
includes := opts[keyIncludePatterns]
followPaths := opts[keyFollowPaths]
var progress progressCb
if sp.p != nil {
progress = sp.p
@ -98,7 +101,7 @@ func (sp *fsSyncProvider) handle(method string, stream grpc.ServerStream) (retEr
doneCh = sp.doneCh
sp.doneCh = nil
}
err := pr.sendFn(stream, dir.Dir, includes, excludes, progress, dir.Map)
err := pr.sendFn(stream, dir.Dir, includes, excludes, followPaths, progress, dir.Map)
if doneCh != nil {
if err != nil {
doneCh <- err
@ -117,7 +120,7 @@ type progressCb func(int, bool)
type protocol struct {
name string
sendFn func(stream grpc.Stream, srcDir string, includes, excludes []string, progress progressCb, _map func(*fsutil.Stat) bool) error
sendFn func(stream grpc.Stream, srcDir string, includes, excludes, followPaths []string, progress progressCb, _map func(*fsutil.Stat) bool) error
recvFn func(stream grpc.Stream, destDir string, cu CacheUpdater, progress progressCb) error
}
@ -142,6 +145,7 @@ type FSSendRequestOpt struct {
Name string
IncludePatterns []string
ExcludePatterns []string
FollowPaths []string
OverrideExcludes bool // deprecated: this is used by docker/cli for automatically loading .dockerignore from the directory
DestDir string
CacheUpdater CacheUpdater
@ -181,6 +185,10 @@ func FSSync(ctx context.Context, c session.Caller, opt FSSendRequestOpt) error {
opts[keyExcludePatterns] = opt.ExcludePatterns
}
if opt.FollowPaths != nil {
opts[keyFollowPaths] = opt.FollowPaths
}
opts[keyDirName] = []string{opt.Name}
ctx, cancel := context.WithCancel(ctx)
@ -261,7 +269,7 @@ func CopyToCaller(ctx context.Context, srcPath string, c session.Caller, progres
return err
}
return sendDiffCopy(cc, srcPath, nil, nil, progress, nil)
return sendDiffCopy(cc, srcPath, nil, nil, nil, progress, nil)
}
func CopyFileWriter(ctx context.Context, c session.Caller) (io.WriteCloser, error) {

View File

@ -4,6 +4,7 @@ const AttrKeepGitDir = "git.keepgitdir"
const AttrFullRemoteURL = "git.fullurl"
const AttrLocalSessionID = "local.session"
const AttrIncludePatterns = "local.includepattern"
const AttrFollowPaths = "local.followpaths"
const AttrExcludePatterns = "local.excludepatterns"
const AttrSharedKeyHint = "local.sharedkeyhint"
const AttrLLBDefinitionFilename = "llbbuild.filename"

View File

@ -2,6 +2,7 @@ package source
import (
"encoding/json"
"fmt"
"strconv"
"strings"
@ -69,6 +70,7 @@ func FromLLB(op *pb.Op_Source) (Identifier, error) {
}
if id, ok := id.(*LocalIdentifier); ok {
for k, v := range op.Source.Attrs {
fmt.Printf("kv %q %q\n", k, v)
switch k {
case pb.AttrLocalSessionID:
id.SessionID = v
@ -88,6 +90,13 @@ func FromLLB(op *pb.Op_Source) (Identifier, error) {
return nil, err
}
id.ExcludePatterns = patterns
case pb.AttrFollowPaths:
var paths []string
if err := json.Unmarshal([]byte(v), &paths); err != nil {
return nil, err
}
id.FollowPaths = paths
fmt.Printf("FollowPaths %#v\n", paths)
case pb.AttrSharedKeyHint:
id.SharedKeyHint = v
}
@ -153,6 +162,7 @@ type LocalIdentifier struct {
SessionID string
IncludePatterns []string
ExcludePatterns []string
FollowPaths []string
SharedKeyHint string
}

View File

@ -159,6 +159,7 @@ func (ls *localSourceHandler) Snapshot(ctx context.Context) (out cache.Immutable
Name: ls.src.Name,
IncludePatterns: ls.src.IncludePatterns,
ExcludePatterns: ls.src.ExcludePatterns,
FollowPaths: ls.src.FollowPaths,
OverrideExcludes: false,
DestDir: dest,
CacheUpdater: &cacheUpdater{cc},