add differ support for local source
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>v0.9
parent
59d2f76e5e
commit
baa4fcdb0f
|
@ -19,6 +19,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -126,6 +127,7 @@ func TestIntegration(t *testing.T) {
|
||||||
testStargzLazyPull,
|
testStargzLazyPull,
|
||||||
testFileOpInputSwap,
|
testFileOpInputSwap,
|
||||||
testRelativeMountpoint,
|
testRelativeMountpoint,
|
||||||
|
testLocalSourceDiffer,
|
||||||
}, mirrors)
|
}, mirrors)
|
||||||
|
|
||||||
integration.Run(t, []integration.Test{
|
integration.Run(t, []integration.Test{
|
||||||
|
@ -1269,6 +1271,86 @@ func testFileOpInputSwap(t *testing.T, sb integration.Sandbox) {
|
||||||
require.Contains(t, err.Error(), "bar: no such file")
|
require.Contains(t, err.Error(), "bar: no such file")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testLocalSourceDiffer(t *testing.T, sb integration.Sandbox) {
|
||||||
|
for _, d := range []llb.DiffType{llb.DiffNone, llb.DiffMetadata} {
|
||||||
|
t.Run(fmt.Sprintf("differ=%s", d), func(t *testing.T) {
|
||||||
|
testLocalSourceWithDiffer(t, sb, d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLocalSourceWithDiffer(t *testing.T, sb integration.Sandbox, d llb.DiffType) {
|
||||||
|
requiresLinux(t)
|
||||||
|
c, err := New(context.TODO(), sb.Address())
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
dir, err := tmpdir(
|
||||||
|
fstest.CreateFile("foo", []byte("foo"), 0600),
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
tv := syscall.NsecToTimespec(time.Now().UnixNano())
|
||||||
|
|
||||||
|
err = syscall.UtimesNano(filepath.Join(dir, "foo"), []syscall.Timespec{tv, tv})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
st := llb.Local("mylocal"+string(d), llb.Differ(d, false))
|
||||||
|
|
||||||
|
def, err := st.Marshal(context.TODO())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
destDir, err := ioutil.TempDir("", "buildkit")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer os.RemoveAll(destDir)
|
||||||
|
|
||||||
|
_, err = c.Solve(context.TODO(), def, SolveOpt{
|
||||||
|
Exports: []ExportEntry{
|
||||||
|
{
|
||||||
|
Type: ExporterLocal,
|
||||||
|
OutputDir: destDir,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
LocalDirs: map[string]string{
|
||||||
|
"mylocal" + string(d): dir,
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
dt, err := ioutil.ReadFile(filepath.Join(destDir, "foo"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []byte("foo"), dt)
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(filepath.Join(dir, "foo"), []byte("bar"), 0600)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = syscall.UtimesNano(filepath.Join(dir, "foo"), []syscall.Timespec{tv, tv})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = c.Solve(context.TODO(), def, SolveOpt{
|
||||||
|
Exports: []ExportEntry{
|
||||||
|
{
|
||||||
|
Type: ExporterLocal,
|
||||||
|
OutputDir: destDir,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
LocalDirs: map[string]string{
|
||||||
|
"mylocal" + string(d): dir,
|
||||||
|
},
|
||||||
|
}, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
dt, err = ioutil.ReadFile(filepath.Join(destDir, "foo"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
if d == llb.DiffMetadata {
|
||||||
|
require.Equal(t, []byte("foo"), dt)
|
||||||
|
}
|
||||||
|
if d == llb.DiffNone {
|
||||||
|
require.Equal(t, []byte("bar"), dt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testFileOpRmWildcard(t *testing.T, sb integration.Sandbox) {
|
func testFileOpRmWildcard(t *testing.T, sb integration.Sandbox) {
|
||||||
requiresLinux(t)
|
requiresLinux(t)
|
||||||
c, err := New(context.TODO(), sb.Address())
|
c, err := New(context.TODO(), sb.Address())
|
||||||
|
|
|
@ -361,6 +361,12 @@ func Local(name string, opts ...LocalOption) State {
|
||||||
attrs[pb.AttrSharedKeyHint] = gi.SharedKeyHint
|
attrs[pb.AttrSharedKeyHint] = gi.SharedKeyHint
|
||||||
addCap(&gi.Constraints, pb.CapSourceLocalSharedKeyHint)
|
addCap(&gi.Constraints, pb.CapSourceLocalSharedKeyHint)
|
||||||
}
|
}
|
||||||
|
if gi.Differ.Type != "" {
|
||||||
|
attrs[pb.AttrLocalDiffer] = string(gi.Differ.Type)
|
||||||
|
if gi.Differ.Required {
|
||||||
|
addCap(&gi.Constraints, pb.CapSourceLocalDiffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
addCap(&gi.Constraints, pb.CapSourceLocal)
|
addCap(&gi.Constraints, pb.CapSourceLocal)
|
||||||
|
|
||||||
|
@ -423,6 +429,26 @@ func SharedKeyHint(h string) LocalOption {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Differ(t DiffType, required bool) LocalOption {
|
||||||
|
return localOptionFunc(func(li *LocalInfo) {
|
||||||
|
li.Differ = DifferInfo{
|
||||||
|
Type: t,
|
||||||
|
Required: required,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiffType string
|
||||||
|
|
||||||
|
const DiffNone DiffType = pb.AttrLocalDifferNone
|
||||||
|
const DiffMetadata DiffType = pb.AttrLocalDifferMetadata
|
||||||
|
const DiffContent DiffType = pb.AttrLocalDifferContent
|
||||||
|
|
||||||
|
type DifferInfo struct {
|
||||||
|
Type DiffType
|
||||||
|
Required bool
|
||||||
|
}
|
||||||
|
|
||||||
type LocalInfo struct {
|
type LocalInfo struct {
|
||||||
constraintsWrapper
|
constraintsWrapper
|
||||||
SessionID string
|
SessionID string
|
||||||
|
@ -430,6 +456,7 @@ type LocalInfo struct {
|
||||||
ExcludePatterns string
|
ExcludePatterns string
|
||||||
FollowPaths string
|
FollowPaths string
|
||||||
SharedKeyHint string
|
SharedKeyHint string
|
||||||
|
Differ DifferInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func HTTP(url string, opts ...HTTPOption) State {
|
func HTTP(url string, opts ...HTTPOption) State {
|
||||||
|
|
|
@ -70,7 +70,7 @@ func (wc *streamWriterCloser) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func recvDiffCopy(ds grpc.ClientStream, dest string, cu CacheUpdater, progress progressCb, filter func(string, *fstypes.Stat) bool) (err error) {
|
func recvDiffCopy(ds grpc.ClientStream, dest string, cu CacheUpdater, progress progressCb, differ fsutil.DiffType, filter func(string, *fstypes.Stat) bool) (err error) {
|
||||||
st := time.Now()
|
st := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
logrus.Debugf("diffcopy took: %v", time.Since(st))
|
logrus.Debugf("diffcopy took: %v", time.Since(st))
|
||||||
|
@ -93,6 +93,7 @@ func recvDiffCopy(ds grpc.ClientStream, dest string, cu CacheUpdater, progress p
|
||||||
ContentHasher: ch,
|
ContentHasher: ch,
|
||||||
ProgressCb: progress,
|
ProgressCb: progress,
|
||||||
Filter: fsutil.FilterFunc(filter),
|
Filter: fsutil.FilterFunc(filter),
|
||||||
|
Differ: differ,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ const (
|
||||||
keyFollowPaths = "followpaths"
|
keyFollowPaths = "followpaths"
|
||||||
keyDirName = "dir-name"
|
keyDirName = "dir-name"
|
||||||
keyExporterMetaPrefix = "exporter-md-"
|
keyExporterMetaPrefix = "exporter-md-"
|
||||||
|
keyDiffer = "differ"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fsSyncProvider struct {
|
type fsSyncProvider struct {
|
||||||
|
@ -130,7 +131,7 @@ type progressCb func(int, bool)
|
||||||
type protocol struct {
|
type protocol struct {
|
||||||
name string
|
name string
|
||||||
sendFn func(stream Stream, fs fsutil.FS, progress progressCb) error
|
sendFn func(stream Stream, fs fsutil.FS, progress progressCb) error
|
||||||
recvFn func(stream grpc.ClientStream, destDir string, cu CacheUpdater, progress progressCb, mapFunc func(string, *fstypes.Stat) bool) error
|
recvFn func(stream grpc.ClientStream, destDir string, cu CacheUpdater, progress progressCb, differ fsutil.DiffType, mapFunc func(string, *fstypes.Stat) bool) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func isProtoSupported(p string) bool {
|
func isProtoSupported(p string) bool {
|
||||||
|
@ -160,6 +161,7 @@ type FSSendRequestOpt struct {
|
||||||
CacheUpdater CacheUpdater
|
CacheUpdater CacheUpdater
|
||||||
ProgressCb func(int, bool)
|
ProgressCb func(int, bool)
|
||||||
Filter func(string, *fstypes.Stat) bool
|
Filter func(string, *fstypes.Stat) bool
|
||||||
|
Differ fsutil.DiffType
|
||||||
}
|
}
|
||||||
|
|
||||||
// CacheUpdater is an object capable of sending notifications for the cache hash changes
|
// CacheUpdater is an object capable of sending notifications for the cache hash changes
|
||||||
|
@ -227,7 +229,7 @@ func FSSync(ctx context.Context, c session.Caller, opt FSSendRequestOpt) error {
|
||||||
panic(fmt.Sprintf("invalid protocol: %q", pr.name))
|
panic(fmt.Sprintf("invalid protocol: %q", pr.name))
|
||||||
}
|
}
|
||||||
|
|
||||||
return pr.recvFn(stream, opt.DestDir, opt.CacheUpdater, opt.ProgressCb, opt.Filter)
|
return pr.recvFn(stream, opt.DestDir, opt.CacheUpdater, opt.ProgressCb, opt.Differ, opt.Filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFSSyncTargetDir allows writing into a directory
|
// NewFSSyncTargetDir allows writing into a directory
|
||||||
|
|
|
@ -12,6 +12,7 @@ const AttrIncludePatterns = "local.includepattern"
|
||||||
const AttrFollowPaths = "local.followpaths"
|
const AttrFollowPaths = "local.followpaths"
|
||||||
const AttrExcludePatterns = "local.excludepatterns"
|
const AttrExcludePatterns = "local.excludepatterns"
|
||||||
const AttrSharedKeyHint = "local.sharedkeyhint"
|
const AttrSharedKeyHint = "local.sharedkeyhint"
|
||||||
|
|
||||||
const AttrLLBDefinitionFilename = "llbbuild.filename"
|
const AttrLLBDefinitionFilename = "llbbuild.filename"
|
||||||
|
|
||||||
const AttrHTTPChecksum = "http.checksum"
|
const AttrHTTPChecksum = "http.checksum"
|
||||||
|
@ -26,4 +27,9 @@ const AttrImageResolveModeForcePull = "pull"
|
||||||
const AttrImageResolveModePreferLocal = "local"
|
const AttrImageResolveModePreferLocal = "local"
|
||||||
const AttrImageRecordType = "image.recordtype"
|
const AttrImageRecordType = "image.recordtype"
|
||||||
|
|
||||||
|
const AttrLocalDiffer = "local.differ"
|
||||||
|
const AttrLocalDifferNone = "none"
|
||||||
|
const AttrLocalDifferMetadata = "metadata"
|
||||||
|
const AttrLocalDifferContent = "content"
|
||||||
|
|
||||||
type IsFileAction = isFileAction_Action
|
type IsFileAction = isFileAction_Action
|
||||||
|
|
|
@ -18,6 +18,7 @@ const (
|
||||||
CapSourceLocalFollowPaths apicaps.CapID = "source.local.followpaths"
|
CapSourceLocalFollowPaths apicaps.CapID = "source.local.followpaths"
|
||||||
CapSourceLocalExcludePatterns apicaps.CapID = "source.local.excludepatterns"
|
CapSourceLocalExcludePatterns apicaps.CapID = "source.local.excludepatterns"
|
||||||
CapSourceLocalSharedKeyHint apicaps.CapID = "source.local.sharedkeyhint"
|
CapSourceLocalSharedKeyHint apicaps.CapID = "source.local.sharedkeyhint"
|
||||||
|
CapSourceLocalDiffer apicaps.CapID = "source.local.differ"
|
||||||
|
|
||||||
CapSourceGit apicaps.CapID = "source.git"
|
CapSourceGit apicaps.CapID = "source.git"
|
||||||
CapSourceGitKeepDir apicaps.CapID = "source.git.keepgitdir"
|
CapSourceGitKeepDir apicaps.CapID = "source.git.keepgitdir"
|
||||||
|
@ -118,6 +119,13 @@ func init() {
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
Status: apicaps.CapStatusExperimental,
|
Status: apicaps.CapStatusExperimental,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Caps.Init(apicaps.Cap{
|
||||||
|
ID: CapSourceLocalDiffer,
|
||||||
|
Enabled: true,
|
||||||
|
Status: apicaps.CapStatusExperimental,
|
||||||
|
})
|
||||||
|
|
||||||
Caps.Init(apicaps.Cap{
|
Caps.Init(apicaps.Cap{
|
||||||
ID: CapSourceGit,
|
ID: CapSourceGit,
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/tonistiigi/fsutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -146,6 +147,15 @@ func FromLLB(op *pb.Op_Source, platform *pb.Platform) (Identifier, error) {
|
||||||
id.FollowPaths = paths
|
id.FollowPaths = paths
|
||||||
case pb.AttrSharedKeyHint:
|
case pb.AttrSharedKeyHint:
|
||||||
id.SharedKeyHint = v
|
id.SharedKeyHint = v
|
||||||
|
case pb.AttrLocalDiffer:
|
||||||
|
switch v {
|
||||||
|
case pb.AttrLocalDifferMetadata, "":
|
||||||
|
id.Differ = fsutil.DiffMetadata
|
||||||
|
case pb.AttrLocalDifferNone:
|
||||||
|
id.Differ = fsutil.DiffNone
|
||||||
|
case pb.AttrLocalDifferContent:
|
||||||
|
id.Differ = fsutil.DiffContent
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,6 +224,7 @@ type LocalIdentifier struct {
|
||||||
ExcludePatterns []string
|
ExcludePatterns []string
|
||||||
FollowPaths []string
|
FollowPaths []string
|
||||||
SharedKeyHint string
|
SharedKeyHint string
|
||||||
|
Differ fsutil.DiffType
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLocalIdentifier(str string) (*LocalIdentifier, error) {
|
func NewLocalIdentifier(str string) (*LocalIdentifier, error) {
|
||||||
|
|
|
@ -178,6 +178,7 @@ func (ls *localSourceHandler) snapshot(ctx context.Context, s session.Group, cal
|
||||||
DestDir: dest,
|
DestDir: dest,
|
||||||
CacheUpdater: &cacheUpdater{cc, mount.IdentityMapping()},
|
CacheUpdater: &cacheUpdater{cc, mount.IdentityMapping()},
|
||||||
ProgressCb: newProgressHandler(ctx, "transferring "+ls.src.Name+":"),
|
ProgressCb: newProgressHandler(ctx, "transferring "+ls.src.Name+":"),
|
||||||
|
Differ: ls.src.Differ,
|
||||||
}
|
}
|
||||||
|
|
||||||
if idmap := mount.IdentityMapping(); idmap != nil {
|
if idmap := mount.IdentityMapping(); idmap != nil {
|
||||||
|
|
Loading…
Reference in New Issue