diff --git a/cache/contenthash/checksum.go b/cache/contenthash/checksum.go index f30598f0..7dcb9499 100644 --- a/cache/contenthash/checksum.go +++ b/cache/contenthash/checksum.go @@ -10,6 +10,7 @@ import ( "path/filepath" "sync" + "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/locker" iradix "github.com/hashicorp/go-immutable-radix" "github.com/hashicorp/golang-lru/simplelru" @@ -51,8 +52,8 @@ func ChecksumWildcard(ctx context.Context, ref cache.ImmutableRef, path string, return getDefaultManager().ChecksumWildcard(ctx, ref, path, followLinks) } -func GetCacheContext(ctx context.Context, md *metadata.StorageItem) (CacheContext, error) { - return getDefaultManager().GetCacheContext(ctx, md) +func GetCacheContext(ctx context.Context, md *metadata.StorageItem, idmap *idtools.IdentityMapping) (CacheContext, error) { + return getDefaultManager().GetCacheContext(ctx, md, idmap) } func SetCacheContext(ctx context.Context, md *metadata.StorageItem, cc CacheContext) error { @@ -81,7 +82,7 @@ type cacheManager struct { } func (cm *cacheManager) Checksum(ctx context.Context, ref cache.ImmutableRef, p string, followLinks bool) (digest.Digest, error) { - cc, err := cm.GetCacheContext(ctx, ensureOriginMetadata(ref.Metadata())) + cc, err := cm.GetCacheContext(ctx, ensureOriginMetadata(ref.Metadata()), ref.IdentityMapping()) if err != nil { return "", nil } @@ -89,14 +90,14 @@ func (cm *cacheManager) Checksum(ctx context.Context, ref cache.ImmutableRef, p } func (cm *cacheManager) ChecksumWildcard(ctx context.Context, ref cache.ImmutableRef, p string, followLinks bool) (digest.Digest, error) { - cc, err := cm.GetCacheContext(ctx, ensureOriginMetadata(ref.Metadata())) + cc, err := cm.GetCacheContext(ctx, ensureOriginMetadata(ref.Metadata()), ref.IdentityMapping()) if err != nil { return "", nil } return cc.ChecksumWildcard(ctx, ref, p, followLinks) } -func (cm *cacheManager) GetCacheContext(ctx context.Context, md *metadata.StorageItem) (CacheContext, error) { +func (cm *cacheManager) GetCacheContext(ctx context.Context, md *metadata.StorageItem, idmap *idtools.IdentityMapping) (CacheContext, error) { cm.locker.Lock(md.ID()) cm.lruMu.Lock() v, ok := cm.lru.Get(md.ID()) @@ -106,7 +107,7 @@ func (cm *cacheManager) GetCacheContext(ctx context.Context, md *metadata.Storag v.(*cacheContext).linkMap = map[string][][]byte{} return v.(*cacheContext), nil } - cc, err := newCacheContext(md) + cc, err := newCacheContext(md, idmap) if err != nil { cm.locker.Unlock(md.ID()) return nil, err @@ -152,6 +153,7 @@ type cacheContext struct { node *iradix.Node dirtyMap map[string]struct{} linkMap map[string][][]byte + idmap *idtools.IdentityMapping } type mount struct { @@ -191,12 +193,13 @@ func (m *mount) clean() error { return nil } -func newCacheContext(md *metadata.StorageItem) (*cacheContext, error) { +func newCacheContext(md *metadata.StorageItem, idmap *idtools.IdentityMapping) (*cacheContext, error) { cc := &cacheContext{ md: md, tree: iradix.New(), dirtyMap: map[string]struct{}{}, linkMap: map[string][][]byte{}, + idmap: idmap, } if err := cc.load(); err != nil { return nil, err diff --git a/cache/contenthash/checksum_test.go b/cache/contenthash/checksum_test.go index d7d6cd62..dd5ec946 100644 --- a/cache/contenthash/checksum_test.go +++ b/cache/contenthash/checksum_test.go @@ -49,7 +49,7 @@ func TestChecksumHardlinks(t *testing.T) { ref := createRef(t, cm, ch) - cc, err := newCacheContext(ref.Metadata()) + cc, err := newCacheContext(ref.Metadata(), nil) require.NoError(t, err) dgst, err := cc.Checksum(context.TODO(), ref, "abc/foo", false) @@ -67,7 +67,7 @@ func TestChecksumHardlinks(t *testing.T) { // validate same results with handleChange ref2 := createRef(t, cm, nil) - cc2, err := newCacheContext(ref2.Metadata()) + cc2, err := newCacheContext(ref2.Metadata(), nil) require.NoError(t, err) err = emit(cc2.HandleChange, changeStream(ch)) @@ -138,7 +138,7 @@ func TestChecksumWildcard(t *testing.T) { ref := createRef(t, cm, ch) - cc, err := newCacheContext(ref.Metadata()) + cc, err := newCacheContext(ref.Metadata(), nil) require.NoError(t, err) dgst, err := cc.ChecksumWildcard(context.TODO(), ref, "f*o", false) @@ -189,7 +189,7 @@ func TestSymlinksNoFollow(t *testing.T) { ref := createRef(t, cm, ch) - cc, err := newCacheContext(ref.Metadata()) + cc, err := newCacheContext(ref.Metadata(), nil) require.NoError(t, err) expectedSym := digest.Digest("sha256:a2ba571981f48ec34eb79c9a3ab091b6491e825c2f7e9914ea86e8e958be7fae") @@ -251,7 +251,7 @@ func TestChecksumBasicFile(t *testing.T) { // for the digest values, the actual values are not important in development // phase but consistency is - cc, err := newCacheContext(ref.Metadata()) + cc, err := newCacheContext(ref.Metadata(), nil) require.NoError(t, err) _, err = cc.Checksum(context.TODO(), ref, "nosuch", true) @@ -312,7 +312,7 @@ func TestChecksumBasicFile(t *testing.T) { ref = createRef(t, cm, ch) - cc, err = newCacheContext(ref.Metadata()) + cc, err = newCacheContext(ref.Metadata(), nil) require.NoError(t, err) dgst, err = cc.Checksum(context.TODO(), ref, "/", true) @@ -331,7 +331,7 @@ func TestChecksumBasicFile(t *testing.T) { ref = createRef(t, cm, ch) - cc, err = newCacheContext(ref.Metadata()) + cc, err = newCacheContext(ref.Metadata(), nil) require.NoError(t, err) dgst, err = cc.Checksum(context.TODO(), ref, "/", true) @@ -357,7 +357,7 @@ func TestChecksumBasicFile(t *testing.T) { ref = createRef(t, cm, ch) - cc, err = newCacheContext(ref.Metadata()) + cc, err = newCacheContext(ref.Metadata(), nil) require.NoError(t, err) dgst, err = cc.Checksum(context.TODO(), ref, "abc/aa/foo", true) @@ -401,7 +401,7 @@ func TestHandleChange(t *testing.T) { // for the digest values, the actual values are not important in development // phase but consistency is - cc, err := newCacheContext(ref.Metadata()) + cc, err := newCacheContext(ref.Metadata(), nil) require.NoError(t, err) err = emit(cc.HandleChange, changeStream(ch)) @@ -477,7 +477,7 @@ func TestHandleRecursiveDir(t *testing.T) { ref := createRef(t, cm, nil) - cc, err := newCacheContext(ref.Metadata()) + cc, err := newCacheContext(ref.Metadata(), nil) require.NoError(t, err) err = emit(cc.HandleChange, changeStream(ch)) @@ -524,7 +524,7 @@ func TestChecksumUnorderedFiles(t *testing.T) { ref := createRef(t, cm, nil) - cc, err := newCacheContext(ref.Metadata()) + cc, err := newCacheContext(ref.Metadata(), nil) require.NoError(t, err) err = emit(cc.HandleChange, changeStream(ch)) @@ -544,7 +544,7 @@ func TestChecksumUnorderedFiles(t *testing.T) { ref = createRef(t, cm, nil) - cc, err = newCacheContext(ref.Metadata()) + cc, err = newCacheContext(ref.Metadata(), nil) require.NoError(t, err) err = emit(cc.HandleChange, changeStream(ch)) @@ -731,7 +731,7 @@ func TestSymlinkInPathHandleChange(t *testing.T) { ref := createRef(t, cm, nil) - cc, err := newCacheContext(ref.Metadata()) + cc, err := newCacheContext(ref.Metadata(), nil) require.NoError(t, err) err = emit(cc.HandleChange, changeStream(ch)) @@ -848,7 +848,7 @@ func setupCacheManager(t *testing.T, tmpdir string, snapshotter snapshots.Snapsh require.NoError(t, err) cm, err := cache.NewManager(cache.ManagerOpt{ - Snapshotter: snapshot.FromContainerdSnapshotter(snapshotter), + Snapshotter: snapshot.FromContainerdSnapshotter(snapshotter, nil), MetadataStore: md, }) require.NoError(t, err) diff --git a/cache/manager.go b/cache/manager.go index 948eb8df..be3bff2b 100644 --- a/cache/manager.go +++ b/cache/manager.go @@ -8,6 +8,7 @@ import ( "github.com/containerd/containerd/filters" "github.com/containerd/containerd/snapshots" + "github.com/docker/docker/pkg/idtools" "github.com/moby/buildkit/cache/metadata" "github.com/moby/buildkit/client" "github.com/moby/buildkit/identity" @@ -34,6 +35,7 @@ type Accessor interface { GetFromSnapshotter(ctx context.Context, id string, opts ...RefOption) (ImmutableRef, error) New(ctx context.Context, s ImmutableRef, opts ...RefOption) (MutableRef, error) GetMutable(ctx context.Context, id string) (MutableRef, error) // Rebase? + IdentityMapping() *idtools.IdentityMapping } type Controller interface { @@ -96,6 +98,11 @@ func (cm *cacheManager) init(ctx context.Context) error { return nil } +// IdentityMapping returns the userns remapping used for refs +func (cm *cacheManager) IdentityMapping() *idtools.IdentityMapping { + return cm.Snapshotter.IdentityMapping() +} + // Close closes the manager and releases the metadata database lock. No other // method should be called after Close. func (cm *cacheManager) Close() error { diff --git a/cache/manager_test.go b/cache/manager_test.go index 3468bce1..bd90ad30 100644 --- a/cache/manager_test.go +++ b/cache/manager_test.go @@ -374,7 +374,7 @@ func getCacheManager(t *testing.T, tmpdir string, snapshotter snapshots.Snapshot require.NoError(t, err) cm, err := NewManager(ManagerOpt{ - Snapshotter: snapshot.FromContainerdSnapshotter(snapshotter), + Snapshotter: snapshot.FromContainerdSnapshotter(snapshotter, nil), MetadataStore: md, }) require.NoError(t, err, fmt.Sprintf("error: %+v", err)) diff --git a/cache/refs.go b/cache/refs.go index 4c8fa27d..33531110 100644 --- a/cache/refs.go +++ b/cache/refs.go @@ -5,6 +5,7 @@ import ( "sync" "github.com/containerd/containerd/mount" + "github.com/docker/docker/pkg/idtools" "github.com/moby/buildkit/cache/metadata" "github.com/moby/buildkit/identity" "github.com/moby/buildkit/snapshot" @@ -20,6 +21,7 @@ type Ref interface { Release(context.Context) error Size(ctx context.Context) (int64, error) Metadata() *metadata.StorageItem + IdentityMapping() *idtools.IdentityMapping } type ImmutableRef interface { @@ -83,6 +85,10 @@ func (cr *cacheRecord) isDead() bool { return cr.dead || (cr.equalImmutable != nil && cr.equalImmutable.dead) || (cr.equalMutable != nil && cr.equalMutable.dead) } +func (cr *cacheRecord) IdentityMapping() *idtools.IdentityMapping { + return cr.cm.IdentityMapping() +} + func (cr *cacheRecord) Size(ctx context.Context) (int64, error) { // this expects that usage() is implemented lazily s, err := cr.sizeG.Do(ctx, cr.ID(), func(ctx context.Context) (interface{}, error) { diff --git a/cmd/buildkitd/config/config.go b/cmd/buildkitd/config/config.go index d6695be4..54df63d0 100644 --- a/cmd/buildkitd/config/config.go +++ b/cmd/buildkitd/config/config.go @@ -64,6 +64,9 @@ type OCIConfig struct { Rootless bool `toml:"rootless"` NoProcessSandbox bool `toml:"noProcessSandbox"` GCConfig + // UserRemapUnsupported is unsupported key for testing. The feature is + // incomplete and the intention is to make it default without config. + UserRemapUnsupported string `toml:"userRemapUnsupported"` } type ContainerdConfig struct { diff --git a/cmd/buildkitd/main_oci_worker.go b/cmd/buildkitd/main_oci_worker.go index 64487b05..7e71567c 100644 --- a/cmd/buildkitd/main_oci_worker.go +++ b/cmd/buildkitd/main_oci_worker.go @@ -168,6 +168,12 @@ func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker return nil, nil } + // TODO: this should never change the existing state dir + idmapping, err := parseIdentityMapping(cfg.UserRemapUnsupported) + if err != nil { + return nil, err + } + snFactory, err := snapshotterFactory(common.config.Root, cfg.Snapshotter) if err != nil { return nil, err @@ -186,7 +192,7 @@ func ociWorkerInitializer(c *cli.Context, common workerInitializerOpt) ([]worker processMode = oci.NoProcessSandbox } - opt, err := runc.NewWorkerOpt(common.config.Root, snFactory, cfg.Rootless, processMode, cfg.Labels) + opt, err := runc.NewWorkerOpt(common.config.Root, snFactory, cfg.Rootless, processMode, cfg.Labels, idmapping) if err != nil { return nil, err } diff --git a/cmd/buildkitd/util.go b/cmd/buildkitd/util.go index 5d462bd2..fe03109c 100644 --- a/cmd/buildkitd/util.go +++ b/cmd/buildkitd/util.go @@ -1,8 +1,13 @@ package main import ( + "runtime" "strconv" "strings" + + "github.com/docker/docker/pkg/idtools" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // parseBoolOrAuto returns (nil, nil) if s is "auto" @@ -13,3 +18,32 @@ func parseBoolOrAuto(s string) (*bool, error) { b, err := strconv.ParseBool(s) return &b, err } + +func parseIdentityMapping(str string) (*idtools.IdentityMapping, error) { + if str == "" { + return nil, nil + } + if runtime.GOOS != "linux" && str != "" { + return nil, errors.Errorf("user namespaces are only supported on linux") + } + + idparts := strings.Split(str, ":") + if len(idparts) > 2 { + return nil, errors.Errorf("invalid userns remap specification in %q", str) + } + + username := idparts[0] + groupname := username + if len(idparts) == 2 { + groupname = idparts[1] + } + + logrus.Debugf("user namespaces: ID ranges will be mapped to subuid/subgid ranges of: %s:%s", username, groupname) + + mappings, err := idtools.NewIdentityMapping(username, groupname) + if err != nil { + return nil, errors.Wrap(err, "failed to create ID mappings") + } + return mappings, nil + +} diff --git a/executor/containerdexecutor/executor.go b/executor/containerdexecutor/executor.go index aa3bb91a..95960a10 100644 --- a/executor/containerdexecutor/executor.go +++ b/executor/containerdexecutor/executor.go @@ -117,7 +117,7 @@ func (w containerdExecutor) Exec(ctx context.Context, meta executor.Meta, root c opts = append(opts, containerdoci.WithCgroup(cgroupsPath)) } processMode := oci.ProcessSandbox // FIXME(AkihiroSuda) - spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, processMode, opts...) + spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, processMode, nil, opts...) if err != nil { return err } diff --git a/executor/oci/spec_unix.go b/executor/oci/spec_unix.go index 613c08eb..08b0ee21 100644 --- a/executor/oci/spec_unix.go +++ b/executor/oci/spec_unix.go @@ -13,6 +13,7 @@ import ( "github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/oci" "github.com/containerd/continuity/fs" + "github.com/docker/docker/pkg/idtools" "github.com/mitchellh/hashstructure" "github.com/moby/buildkit/executor" "github.com/moby/buildkit/snapshot" @@ -40,7 +41,7 @@ const ( // GenerateSpec generates spec using containerd functionality. // opts are ignored for s.Process, s.Hostname, and s.Mounts . -func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, namespace network.Namespace, processMode ProcessMode, opts ...oci.SpecOpts) (*specs.Spec, func(), error) { +func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, namespace network.Namespace, processMode ProcessMode, idmap *idtools.IdentityMapping, opts ...oci.SpecOpts) (*specs.Spec, func(), error) { c := &containers.Container{ ID: id, } @@ -102,7 +103,14 @@ func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mou } } } - // TODO: User + + if idmap != nil { + s.Linux.Namespaces = append(s.Linux.Namespaces, specs.LinuxNamespace{ + Type: specs.UserNamespace, + }) + s.Linux.UIDMappings = specMapping(idmap.UIDs()) + s.Linux.GIDMappings = specMapping(idmap.GIDs()) + } sm := &submounts{} @@ -227,3 +235,15 @@ func sub(m mount.Mount, subPath string) (mount.Mount, error) { m.Source = src return m, nil } + +func specMapping(s []idtools.IDMap) []specs.LinuxIDMapping { + var ids []specs.LinuxIDMapping + for _, item := range s { + ids = append(ids, specs.LinuxIDMapping{ + HostID: uint32(item.HostID), + ContainerID: uint32(item.ContainerID), + Size: uint32(item.Size), + }) + } + return ids +} diff --git a/executor/runcexecutor/executor.go b/executor/runcexecutor/executor.go index 6acd9d4e..2fe0c514 100644 --- a/executor/runcexecutor/executor.go +++ b/executor/runcexecutor/executor.go @@ -17,6 +17,7 @@ import ( containerdoci "github.com/containerd/containerd/oci" "github.com/containerd/continuity/fs" runc "github.com/containerd/go-runc" + "github.com/docker/docker/pkg/idtools" "github.com/moby/buildkit/cache" "github.com/moby/buildkit/executor" "github.com/moby/buildkit/executor/oci" @@ -38,7 +39,8 @@ type Opt struct { // DefaultCgroupParent is the cgroup-parent name for executor DefaultCgroupParent string // ProcessMode - ProcessMode oci.ProcessMode + ProcessMode oci.ProcessMode + IdentityMapping *idtools.IdentityMapping } var defaultCommandCandidates = []string{"buildkit-runc", "runc"} @@ -51,6 +53,7 @@ type runcExecutor struct { rootless bool networkProviders map[pb.NetMode]network.Provider processMode oci.ProcessMode + idmap *idtools.IdentityMapping } func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Executor, error) { @@ -107,6 +110,7 @@ func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Ex rootless: opt.Rootless, networkProviders: networkProviders, processMode: opt.ProcessMode, + idmap: opt.IdentityMapping, } return w, nil } @@ -157,8 +161,14 @@ func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache. return err } defer os.RemoveAll(bundle) + + identity := idtools.Identity{} + if w.idmap != nil { + identity = w.idmap.RootPair() + } + rootFSPath := filepath.Join(bundle, "rootfs") - if err := os.Mkdir(rootFSPath, 0700); err != nil { + if err := idtools.MkdirAllAndChown(rootFSPath, 0700, identity); err != nil { return err } if err := mount.All(rootMount, rootFSPath); err != nil { @@ -193,7 +203,7 @@ func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache. } opts = append(opts, containerdoci.WithCgroup(cgroupsPath)) } - spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, w.processMode, opts...) + spec, cleanup, err := oci.GenerateSpec(ctx, meta, mounts, id, resolvConf, hostsFile, namespace, w.processMode, w.idmap, opts...) if err != nil { return err } @@ -208,7 +218,7 @@ func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache. if err != nil { return errors.Wrapf(err, "working dir %s points to invalid target", newp) } - if err := os.MkdirAll(newp, 0755); err != nil { + if err := idtools.MkdirAllAndChown(newp, 0755, identity); err != nil { return errors.Wrapf(err, "failed to create working directory %s", newp) } diff --git a/exporter/local/export.go b/exporter/local/export.go index 24f0fcd8..5a3f4bd2 100644 --- a/exporter/local/export.go +++ b/exporter/local/export.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/docker/docker/pkg/idtools" "github.com/moby/buildkit/cache" "github.com/moby/buildkit/exporter" "github.com/moby/buildkit/session" @@ -68,6 +69,7 @@ func (e *localExporterInstance) Export(ctx context.Context, inp exporter.Source) return func() error { var src string var err error + var idmap *idtools.IdentityMapping if ref == nil { src, err = ioutil.TempDir("", "buildkit") if err != nil { @@ -86,10 +88,30 @@ func (e *localExporterInstance) Export(ctx context.Context, inp exporter.Source) if err != nil { return err } + + idmap = mount.IdentityMapping() + defer lm.Unmount() } - fs := fsutil.NewFS(src, nil) + walkOpt := &fsutil.WalkOpt{} + + if idmap != nil { + walkOpt.Map = func(p string, st *fstypes.Stat) bool { + uid, gid, err := idmap.ToContainer(idtools.Identity{ + UID: int(st.Uid), + GID: int(st.Gid), + }) + if err != nil { + return false + } + st.Uid = uint32(uid) + st.Gid = uint32(gid) + return true + } + } + + fs := fsutil.NewFS(src, walkOpt) lbl := "copying files" if isMap { lbl += " " + k diff --git a/exporter/tar/export.go b/exporter/tar/export.go index 7ea496aa..12de8da9 100644 --- a/exporter/tar/export.go +++ b/exporter/tar/export.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/docker/docker/pkg/idtools" "github.com/moby/buildkit/cache" "github.com/moby/buildkit/exporter" "github.com/moby/buildkit/session" @@ -71,6 +72,7 @@ func (e *localExporterInstance) Export(ctx context.Context, inp exporter.Source) getDir := func(ctx context.Context, k string, ref cache.ImmutableRef) (*fsutil.Dir, error) { var src string var err error + var idmap *idtools.IdentityMapping if ref == nil { src, err = ioutil.TempDir("", "buildkit") if err != nil { @@ -89,11 +91,31 @@ func (e *localExporterInstance) Export(ctx context.Context, inp exporter.Source) if err != nil { return nil, err } + + idmap = mount.IdentityMapping() + defers = append(defers, func() { lm.Unmount() }) } + walkOpt := &fsutil.WalkOpt{} + + if idmap != nil { + walkOpt.Map = func(p string, st *fstypes.Stat) bool { + uid, gid, err := idmap.ToContainer(idtools.Identity{ + UID: int(st.Uid), + GID: int(st.Gid), + }) + if err != nil { + return false + } + st.Uid = uint32(uid) + st.Gid = uint32(gid) + return true + } + } + return &fsutil.Dir{ - FS: fsutil.NewFS(src, nil), + FS: fsutil.NewFS(src, walkOpt), Stat: fstypes.Stat{ Mode: uint32(os.ModeDir | 0755), Path: strings.Replace(k, "/", "_", -1), diff --git a/session/filesync/diffcopy.go b/session/filesync/diffcopy.go index 5148ade7..6934f946 100644 --- a/session/filesync/diffcopy.go +++ b/session/filesync/diffcopy.go @@ -57,7 +57,7 @@ func (wc *streamWriterCloser) Close() error { return nil } -func recvDiffCopy(ds grpc.Stream, dest string, cu CacheUpdater, progress progressCb) error { +func recvDiffCopy(ds grpc.Stream, dest string, cu CacheUpdater, progress progressCb, filter func(string, *fstypes.Stat) bool) error { st := time.Now() defer func() { logrus.Debugf("diffcopy took: %v", time.Since(st)) @@ -73,6 +73,7 @@ func recvDiffCopy(ds grpc.Stream, dest string, cu CacheUpdater, progress progres NotifyHashed: cf, ContentHasher: ch, ProgressCb: progress, + Filter: fsutil.FilterFunc(filter), }) } diff --git a/session/filesync/filesync.go b/session/filesync/filesync.go index d6adee82..de5237b1 100644 --- a/session/filesync/filesync.go +++ b/session/filesync/filesync.go @@ -129,7 +129,7 @@ type progressCb func(int, bool) type protocol struct { name string sendFn func(stream grpc.Stream, fs fsutil.FS, progress progressCb) error - recvFn func(stream grpc.Stream, destDir string, cu CacheUpdater, progress progressCb) error + recvFn func(stream grpc.Stream, destDir string, cu CacheUpdater, progress progressCb, mapFunc func(string, *fstypes.Stat) bool) error } func isProtoSupported(p string) bool { @@ -158,6 +158,7 @@ type FSSendRequestOpt struct { DestDir string CacheUpdater CacheUpdater ProgressCb func(int, bool) + Filter func(string, *fstypes.Stat) bool } // CacheUpdater is an object capable of sending notifications for the cache hash changes @@ -225,7 +226,7 @@ func FSSync(ctx context.Context, c session.Caller, opt FSSendRequestOpt) error { panic(fmt.Sprintf("invalid protocol: %q", pr.name)) } - return pr.recvFn(stream, opt.DestDir, opt.CacheUpdater, opt.ProgressCb) + return pr.recvFn(stream, opt.DestDir, opt.CacheUpdater, opt.ProgressCb, opt.Filter) } // NewFSSyncTargetDir allows writing into a directory diff --git a/snapshot/containerd/snapshotter.go b/snapshot/containerd/snapshotter.go index c4b6d128..cef856c7 100644 --- a/snapshot/containerd/snapshotter.go +++ b/snapshot/containerd/snapshotter.go @@ -8,15 +8,16 @@ import ( "github.com/containerd/containerd/mount" "github.com/containerd/containerd/namespaces" ctdsnapshot "github.com/containerd/containerd/snapshots" + "github.com/docker/docker/pkg/idtools" "github.com/moby/buildkit/cache/metadata" "github.com/moby/buildkit/snapshot" "github.com/moby/buildkit/snapshot/blobmapping" ) -func NewSnapshotter(snapshotter ctdsnapshot.Snapshotter, store content.Store, mdstore *metadata.Store, ns string, gc func(context.Context) error) snapshot.Snapshotter { +func NewSnapshotter(snapshotter ctdsnapshot.Snapshotter, store content.Store, mdstore *metadata.Store, ns string, gc func(context.Context) error, idmap *idtools.IdentityMapping) snapshot.Snapshotter { return blobmapping.NewSnapshotter(blobmapping.Opt{ Content: store, - Snapshotter: snapshot.FromContainerdSnapshotter(&nsSnapshotter{ns, snapshotter, gc}), + Snapshotter: snapshot.FromContainerdSnapshotter(&nsSnapshotter{ns, snapshotter, gc}, idmap), MetadataStore: mdstore, }) } diff --git a/snapshot/snapshotter.go b/snapshot/snapshotter.go index 920407f4..19155dc6 100644 --- a/snapshot/snapshotter.go +++ b/snapshot/snapshotter.go @@ -6,6 +6,7 @@ import ( "github.com/containerd/containerd/mount" "github.com/containerd/containerd/snapshots" + "github.com/docker/docker/pkg/idtools" digest "github.com/opencontainers/go-digest" ) @@ -13,6 +14,7 @@ type Mountable interface { // ID() string Mount() ([]mount.Mount, error) Release() error + IdentityMapping() *idtools.IdentityMapping } type SnapshotterBase interface { @@ -27,6 +29,7 @@ type SnapshotterBase interface { Remove(ctx context.Context, key string) error Walk(ctx context.Context, fn func(context.Context, snapshots.Info) error) error Close() error + IdentityMapping() *idtools.IdentityMapping } // Snapshotter defines interface that any snapshot implementation should satisfy @@ -40,12 +43,13 @@ type Blobmapper interface { SetBlob(ctx context.Context, key string, diffID, blob digest.Digest) error } -func FromContainerdSnapshotter(s snapshots.Snapshotter) SnapshotterBase { - return &fromContainerd{Snapshotter: s} +func FromContainerdSnapshotter(s snapshots.Snapshotter, idmap *idtools.IdentityMapping) SnapshotterBase { + return &fromContainerd{Snapshotter: s, idmap: idmap} } type fromContainerd struct { snapshots.Snapshotter + idmap *idtools.IdentityMapping } func (s *fromContainerd) Mounts(ctx context.Context, key string) (Mountable, error) { @@ -53,7 +57,7 @@ func (s *fromContainerd) Mounts(ctx context.Context, key string) (Mountable, err if err != nil { return nil, err } - return &staticMountable{mounts}, nil + return &staticMountable{mounts, s.idmap}, nil } func (s *fromContainerd) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) error { _, err := s.Snapshotter.Prepare(ctx, key, parent, opts...) @@ -64,11 +68,15 @@ func (s *fromContainerd) View(ctx context.Context, key, parent string, opts ...s if err != nil { return nil, err } - return &staticMountable{mounts}, nil + return &staticMountable{mounts, s.idmap}, nil +} +func (s *fromContainerd) IdentityMapping() *idtools.IdentityMapping { + return s.idmap } type staticMountable struct { mounts []mount.Mount + idmap *idtools.IdentityMapping } func (m *staticMountable) Mount() ([]mount.Mount, error) { @@ -79,6 +87,10 @@ func (cm *staticMountable) Release() error { return nil } +func (cm *staticMountable) IdentityMapping() *idtools.IdentityMapping { + return cm.idmap +} + // NewContainerdSnapshotter converts snapshotter to containerd snapshotter func NewContainerdSnapshotter(s Snapshotter) (snapshots.Snapshotter, func() error) { cs := &containerdSnapshotter{Snapshotter: s} diff --git a/solver/llbsolver/file/backend.go b/solver/llbsolver/file/backend.go index 05e201c4..45d66d71 100644 --- a/solver/llbsolver/file/backend.go +++ b/solver/llbsolver/file/backend.go @@ -10,6 +10,7 @@ import ( "time" "github.com/containerd/continuity/fs" + "github.com/docker/docker/pkg/idtools" "github.com/moby/buildkit/snapshot" "github.com/moby/buildkit/solver/llbsolver/ops/fileoptypes" "github.com/moby/buildkit/solver/pb" @@ -25,12 +26,35 @@ func timestampToTime(ts int64) *time.Time { return &tm } -func mkdir(ctx context.Context, d string, action pb.FileActionMkDir, user *copy.ChownOpt) error { +func mapUser(user *copy.ChownOpt, idmap *idtools.IdentityMapping) (*copy.ChownOpt, error) { + if idmap == nil { + return user, nil + } + if user == nil { + identity := idmap.RootPair() + return ©.ChownOpt{Uid: identity.UID, Gid: identity.GID}, nil + } + identity, err := idmap.ToHost(idtools.Identity{ + UID: user.Uid, + GID: user.Gid, + }) + if err != nil { + return nil, err + } + return ©.ChownOpt{Uid: identity.UID, Gid: identity.GID}, nil +} + +func mkdir(ctx context.Context, d string, action pb.FileActionMkDir, user *copy.ChownOpt, idmap *idtools.IdentityMapping) error { p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path))) if err != nil { return err } + user, err = mapUser(user, idmap) + if err != nil { + return err + } + if action.MakeParents { if err := copy.MkdirAll(p, os.FileMode(action.Mode)&0777, user, timestampToTime(action.Timestamp)); err != nil { return err @@ -53,12 +77,17 @@ func mkdir(ctx context.Context, d string, action pb.FileActionMkDir, user *copy. return nil } -func mkfile(ctx context.Context, d string, action pb.FileActionMkFile, user *copy.ChownOpt) error { +func mkfile(ctx context.Context, d string, action pb.FileActionMkFile, user *copy.ChownOpt, idmap *idtools.IdentityMapping) error { p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path))) if err != nil { return err } + user, err = mapUser(user, idmap) + if err != nil { + return err + } + if err := ioutil.WriteFile(p, action.Data, os.FileMode(action.Mode)&0777); err != nil { return err } @@ -90,7 +119,7 @@ func rm(ctx context.Context, d string, action pb.FileActionRm) error { return nil } -func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy, u *copy.ChownOpt) error { +func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy, u *copy.ChownOpt, idmap *idtools.IdentityMapping) error { srcPath := cleanPath(action.Src) destPath := cleanPath(action.Dest) @@ -109,6 +138,12 @@ func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy, u * return nil } + // TODO(tonistiigi): this is wrong. fsutil.Copy can't handle non-forced user + u, err := mapUser(u, idmap) + if err != nil { + return err + } + opt := []copy.Opt{ func(ci *copy.CopyInfo) { ci.Chown = u @@ -195,7 +230,7 @@ func (fb *Backend) Mkdir(ctx context.Context, m, user, group fileoptypes.Mount, return err } - return mkdir(ctx, dir, action, u) + return mkdir(ctx, dir, action, u, mnt.m.IdentityMapping()) } func (fb *Backend) Mkfile(ctx context.Context, m, user, group fileoptypes.Mount, action pb.FileActionMkFile) error { @@ -216,7 +251,7 @@ func (fb *Backend) Mkfile(ctx context.Context, m, user, group fileoptypes.Mount, return err } - return mkfile(ctx, dir, action, u) + return mkfile(ctx, dir, action, u, mnt.m.IdentityMapping()) } func (fb *Backend) Rm(ctx context.Context, m fileoptypes.Mount, action pb.FileActionRm) error { mnt, ok := m.(*Mount) @@ -262,5 +297,5 @@ func (fb *Backend) Copy(ctx context.Context, m1, m2, user, group fileoptypes.Mou return err } - return docopy(ctx, src, dest, action, u) + return docopy(ctx, src, dest, action, u, mnt2.m.IdentityMapping()) } diff --git a/solver/llbsolver/ops/exec.go b/solver/llbsolver/ops/exec.go index 065eb7c6..00f0f128 100644 --- a/solver/llbsolver/ops/exec.go +++ b/solver/llbsolver/ops/exec.go @@ -17,6 +17,7 @@ import ( "github.com/containerd/containerd/mount" "github.com/containerd/containerd/platforms" + "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/locker" "github.com/moby/buildkit/cache" "github.com/moby/buildkit/cache/metadata" @@ -329,30 +330,47 @@ func (e *execOp) getSSHMountable(ctx context.Context, m *pb.Mount) (cache.Mounta return nil, err } - return &sshMount{mount: m, caller: caller}, nil + return &sshMount{mount: m, caller: caller, idmap: e.cm.IdentityMapping()}, nil } type sshMount struct { mount *pb.Mount caller session.Caller + idmap *idtools.IdentityMapping } func (sm *sshMount) Mount(ctx context.Context, readonly bool) (snapshot.Mountable, error) { - return &sshMountInstance{sm: sm}, nil + return &sshMountInstance{sm: sm, idmap: sm.idmap}, nil } type sshMountInstance struct { sm *sshMount cleanup func() error + idmap *idtools.IdentityMapping } func (sm *sshMountInstance) Mount() ([]mount.Mount, error) { ctx, cancel := context.WithCancel(context.TODO()) + uid := int(sm.sm.mount.SSHOpt.Uid) + gid := int(sm.sm.mount.SSHOpt.Gid) + + if sm.idmap != nil { + identity, err := sm.idmap.ToHost(idtools.Identity{ + UID: uid, + GID: gid, + }) + if err != nil { + return nil, err + } + uid = identity.UID + gid = identity.GID + } + sock, cleanup, err := sshforward.MountSSHSocket(ctx, sm.sm.caller, sshforward.SocketOpt{ ID: sm.sm.mount.SSHOpt.ID, - UID: int(sm.sm.mount.SSHOpt.Uid), - GID: int(sm.sm.mount.SSHOpt.Gid), + UID: uid, + GID: gid, Mode: int(sm.sm.mount.SSHOpt.Mode & 0777), }) if err != nil { @@ -384,6 +402,10 @@ func (sm *sshMountInstance) Release() error { return nil } +func (sm *sshMountInstance) IdentityMapping() *idtools.IdentityMapping { + return sm.idmap +} + func (e *execOp) getSecretMountable(ctx context.Context, m *pb.Mount) (cache.Mountable, error) { if m.SecretOpt == nil { return nil, errors.Errorf("invalid sercet mount options") @@ -416,21 +438,23 @@ func (e *execOp) getSecretMountable(ctx context.Context, m *pb.Mount) (cache.Mou return nil, err } - return &secretMount{mount: m, data: dt}, nil + return &secretMount{mount: m, data: dt, idmap: e.cm.IdentityMapping()}, nil } type secretMount struct { mount *pb.Mount data []byte + idmap *idtools.IdentityMapping } func (sm *secretMount) Mount(ctx context.Context, readonly bool) (snapshot.Mountable, error) { - return &secretMountInstance{sm: sm}, nil + return &secretMountInstance{sm: sm, idmap: sm.idmap}, nil } type secretMountInstance struct { - sm *secretMount - root string + sm *secretMount + root string + idmap *idtools.IdentityMapping } func (sm *secretMountInstance) Mount() ([]mount.Mount, error) { @@ -465,7 +489,22 @@ func (sm *secretMountInstance) Mount() ([]mount.Mount, error) { return nil, err } - if err := os.Chown(fp, int(sm.sm.mount.SecretOpt.Uid), int(sm.sm.mount.SecretOpt.Gid)); err != nil { + uid := int(sm.sm.mount.SecretOpt.Uid) + gid := int(sm.sm.mount.SecretOpt.Gid) + + if sm.idmap != nil { + identity, err := sm.idmap.ToHost(idtools.Identity{ + UID: uid, + GID: gid, + }) + if err != nil { + return nil, err + } + uid = identity.UID + gid = identity.GID + } + + if err := os.Chown(fp, uid, gid); err != nil { return nil, err } @@ -490,6 +529,10 @@ func (sm *secretMountInstance) Release() error { return nil } +func (sm *secretMountInstance) IdentityMapping() *idtools.IdentityMapping { + return sm.idmap +} + func addDefaultEnvvar(env []string, k, v string) []string { for _, e := range env { if strings.HasPrefix(e, k+"=") { @@ -585,7 +628,7 @@ func (e *execOp) Exec(ctx context.Context, inputs []solver.Result) ([]solver.Res } case pb.MountType_TMPFS: - mountable = newTmpfs() + mountable = newTmpfs(e.cm.IdentityMapping()) case pb.MountType_SECRET: secretMount, err := e.getSecretMountable(ctx, m) @@ -702,19 +745,21 @@ func proxyEnvList(p *pb.ProxyEnv) []string { return out } -func newTmpfs() cache.Mountable { - return &tmpfs{} +func newTmpfs(idmap *idtools.IdentityMapping) cache.Mountable { + return &tmpfs{idmap: idmap} } type tmpfs struct { + idmap *idtools.IdentityMapping } func (f *tmpfs) Mount(ctx context.Context, readonly bool) (snapshot.Mountable, error) { - return &tmpfsMount{readonly: readonly}, nil + return &tmpfsMount{readonly: readonly, idmap: f.idmap}, nil } type tmpfsMount struct { readonly bool + idmap *idtools.IdentityMapping } func (m *tmpfsMount) Mount() ([]mount.Mount, error) { @@ -732,6 +777,10 @@ func (m *tmpfsMount) Release() error { return nil } +func (m *tmpfsMount) IdentityMapping() *idtools.IdentityMapping { + return m.idmap +} + var cacheRefsLocker = locker.New() var sharedCacheRefs = &cacheRefs{} diff --git a/source/git/gitsource.go b/source/git/gitsource.go index 9e878308..b2a3eacf 100644 --- a/source/git/gitsource.go +++ b/source/git/gitsource.go @@ -335,6 +335,16 @@ func (gs *gitSourceHandler) Snapshot(ctx context.Context) (out cache.ImmutableRe return nil, errors.Wrapf(err, "failed to update submodules for %s", gs.src.Remote) } + if idmap := mount.IdentityMapping(); idmap != nil { + u := idmap.RootPair() + err := filepath.Walk(gitDir, func(p string, f os.FileInfo, err error) error { + return os.Lchown(p, u.UID, u.GID) + }) + if err != nil { + return nil, errors.Wrap(err, "failed to remap git checkout") + } + } + lm.Unmount() lm = nil diff --git a/source/git/gitsource_test.go b/source/git/gitsource_test.go index 545c2f1e..1a1b8aa7 100644 --- a/source/git/gitsource_test.go +++ b/source/git/gitsource_test.go @@ -297,7 +297,7 @@ func setupGitSource(t *testing.T, tmpdir string) source.Source { assert.NoError(t, err) cm, err := cache.NewManager(cache.ManagerOpt{ - Snapshotter: snapshot.FromContainerdSnapshotter(snapshotter), + Snapshotter: snapshot.FromContainerdSnapshotter(snapshotter, nil), MetadataStore: md, }) assert.NoError(t, err) diff --git a/source/http/httpsource.go b/source/http/httpsource.go index 1682852c..c9fe8f5c 100644 --- a/source/http/httpsource.go +++ b/source/http/httpsource.go @@ -15,6 +15,7 @@ import ( "strings" "time" + "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/locker" "github.com/moby/buildkit/cache" "github.com/moby/buildkit/cache/metadata" @@ -278,8 +279,22 @@ func (hs *httpSourceHandler) save(ctx context.Context, resp *http.Response) (ref } f = nil - if hs.src.UID != 0 || hs.src.GID != 0 { - if err := os.Chown(fp, hs.src.UID, hs.src.GID); err != nil { + uid := hs.src.UID + gid := hs.src.GID + if idmap := mount.IdentityMapping(); idmap != nil { + identity, err := idmap.ToHost(idtools.Identity{ + UID: int(uid), + GID: int(gid), + }) + if err != nil { + return nil, "", err + } + uid = identity.UID + gid = identity.GID + } + + if gid != 0 || uid != 0 { + if err := os.Chown(fp, uid, gid); err != nil { return nil, "", err } } diff --git a/source/http/httpsource_test.go b/source/http/httpsource_test.go index 1265f582..f9fce971 100644 --- a/source/http/httpsource_test.go +++ b/source/http/httpsource_test.go @@ -315,7 +315,7 @@ func newHTTPSource(tmpdir string) (source.Source, error) { } cm, err := cache.NewManager(cache.ManagerOpt{ - Snapshotter: snapshot.FromContainerdSnapshotter(snapshotter), + Snapshotter: snapshot.FromContainerdSnapshotter(snapshotter, nil), MetadataStore: md, }) if err != nil { diff --git a/source/local/local.go b/source/local/local.go index b9420e66..b1748cb7 100644 --- a/source/local/local.go +++ b/source/local/local.go @@ -6,6 +6,7 @@ import ( "fmt" "time" + "github.com/docker/docker/pkg/idtools" "github.com/moby/buildkit/cache" "github.com/moby/buildkit/cache/contenthash" "github.com/moby/buildkit/cache/metadata" @@ -19,6 +20,7 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/tonistiigi/fsutil" + fstypes "github.com/tonistiigi/fsutil/types" bolt "go.etcd.io/bbolt" "golang.org/x/time/rate" "google.golang.org/grpc/codes" @@ -153,7 +155,7 @@ func (ls *localSourceHandler) Snapshot(ctx context.Context) (out cache.Immutable } }() - cc, err := contenthash.GetCacheContext(ctx, mutable.Metadata()) + cc, err := contenthash.GetCacheContext(ctx, mutable.Metadata(), mount.IdentityMapping()) if err != nil { return nil, err } @@ -165,10 +167,25 @@ func (ls *localSourceHandler) Snapshot(ctx context.Context) (out cache.Immutable FollowPaths: ls.src.FollowPaths, OverrideExcludes: false, DestDir: dest, - CacheUpdater: &cacheUpdater{cc}, + CacheUpdater: &cacheUpdater{cc, mount.IdentityMapping()}, ProgressCb: newProgressHandler(ctx, "transferring "+ls.src.Name+":"), } + if idmap := mount.IdentityMapping(); idmap != nil { + opt.Filter = func(p string, stat *fstypes.Stat) bool { + identity, err := idmap.ToHost(idtools.Identity{ + UID: int(stat.Uid), + GID: int(stat.Gid), + }) + if err != nil { + return false + } + stat.Uid = uint32(identity.UID) + stat.Gid = uint32(identity.GID) + return true + } + } + if err := filesync.FSSync(ctx, caller, opt); err != nil { if status.Code(err) == codes.NotFound { return nil, errors.Errorf("local source %s not enabled from the client", ls.src.Name) @@ -245,6 +262,7 @@ func newProgressHandler(ctx context.Context, id string) func(int, bool) { type cacheUpdater struct { contenthash.CacheContext + idmap *idtools.IdentityMapping } func (cu *cacheUpdater) MarkSupported(bool) { diff --git a/vendor/modules.txt b/vendor/modules.txt index 0f383f98..d772a417 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -94,9 +94,9 @@ github.com/containerd/continuity/fs github.com/containerd/continuity/syscallx github.com/containerd/continuity github.com/containerd/continuity/fs/fstest +github.com/containerd/continuity/pathdriver github.com/containerd/continuity/devices github.com/containerd/continuity/driver -github.com/containerd/continuity/pathdriver github.com/containerd/continuity/proto # github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260 github.com/containerd/fifo @@ -116,6 +116,7 @@ github.com/docker/cli/cli/config/types github.com/docker/distribution/reference github.com/docker/distribution/digestset # github.com/docker/docker v1.14.0-0.20190319215453-e7b5f7dbe98c +github.com/docker/docker/pkg/idtools github.com/docker/docker/pkg/locker github.com/docker/docker/pkg/reexec github.com/docker/docker/builder/dockerignore @@ -124,15 +125,14 @@ github.com/docker/docker/pkg/signal github.com/docker/docker/api/types/container github.com/docker/docker/pkg/archive github.com/docker/docker/pkg/chrootarchive +github.com/docker/docker/pkg/system github.com/docker/docker/pkg/fileutils github.com/docker/docker/pkg/ioutils github.com/docker/docker/api/types/blkiodev github.com/docker/docker/api/types/mount github.com/docker/docker/pkg/homedir -github.com/docker/docker/pkg/idtools github.com/docker/docker/pkg/longpath github.com/docker/docker/pkg/pools -github.com/docker/docker/pkg/system github.com/docker/docker/pkg/mount # github.com/docker/docker-credential-helpers v0.6.0 github.com/docker/docker-credential-helpers/client diff --git a/worker/base/worker.go b/worker/base/worker.go index 125d2681..bf682923 100644 --- a/worker/base/worker.go +++ b/worker/base/worker.go @@ -14,6 +14,7 @@ import ( "github.com/containerd/containerd/images" "github.com/containerd/containerd/rootfs" cdsnapshot "github.com/containerd/containerd/snapshots" + "github.com/docker/docker/pkg/idtools" "github.com/moby/buildkit/cache" "github.com/moby/buildkit/cache/blobs" "github.com/moby/buildkit/cache/metadata" @@ -70,6 +71,7 @@ type WorkerOpt struct { Differ diff.Comparer ImageStore images.Store // optional ResolveOptionsFunc resolver.ResolveOptionsFunc + IdentityMapping *idtools.IdentityMapping } // Worker is a local worker instance with dedicated snapshotter, cache, and so on. diff --git a/worker/containerd/containerd.go b/worker/containerd/containerd.go index 94321d96..5e04d0e0 100644 --- a/worker/containerd/containerd.go +++ b/worker/containerd/containerd.go @@ -106,7 +106,7 @@ func newContainerd(root string, client *containerd.Client, snapshotterName, ns s Labels: xlabels, MetadataStore: md, Executor: containerdexecutor.New(client, root, "", network.Default()), - Snapshotter: containerdsnapshot.NewSnapshotter(client.SnapshotService(snapshotterName), cs, md, ns, gc), + Snapshotter: containerdsnapshot.NewSnapshotter(client.SnapshotService(snapshotterName), cs, md, ns, gc, nil), ContentStore: cs, Applier: winlayers.NewFileSystemApplierWithWindows(cs, df), Differ: winlayers.NewWalkingDiffWithWindows(cs, df), diff --git a/worker/runc/runc.go b/worker/runc/runc.go index 743e7f9b..7d203384 100644 --- a/worker/runc/runc.go +++ b/worker/runc/runc.go @@ -12,6 +12,7 @@ import ( ctdmetadata "github.com/containerd/containerd/metadata" "github.com/containerd/containerd/platforms" ctdsnapshot "github.com/containerd/containerd/snapshots" + "github.com/docker/docker/pkg/idtools" "github.com/moby/buildkit/cache/metadata" "github.com/moby/buildkit/executor/oci" "github.com/moby/buildkit/executor/runcexecutor" @@ -32,7 +33,7 @@ type SnapshotterFactory struct { } // NewWorkerOpt creates a WorkerOpt. -func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, processMode oci.ProcessMode, labels map[string]string) (base.WorkerOpt, error) { +func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, processMode oci.ProcessMode, labels map[string]string, idmap *idtools.IdentityMapping) (base.WorkerOpt, error) { var opt base.WorkerOpt name := "runc-" + snFactory.Name root = filepath.Join(root, name) @@ -47,8 +48,9 @@ func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, proc // Root directory Root: filepath.Join(root, "executor"), // without root privileges - Rootless: rootless, - ProcessMode: processMode, + Rootless: rootless, + ProcessMode: processMode, + IdentityMapping: idmap, }, network.Default()) if err != nil { return opt, err @@ -97,16 +99,17 @@ func NewWorkerOpt(root string, snFactory SnapshotterFactory, rootless bool, proc xlabels[k] = v } opt = base.WorkerOpt{ - ID: id, - Labels: xlabels, - MetadataStore: md, - Executor: exe, - Snapshotter: containerdsnapshot.NewSnapshotter(mdb.Snapshotter(snFactory.Name), c, md, "buildkit", gc), - ContentStore: c, - Applier: winlayers.NewFileSystemApplierWithWindows(c, apply.NewFileSystemApplier(c)), - Differ: winlayers.NewWalkingDiffWithWindows(c, walking.NewWalkingDiff(c)), - ImageStore: nil, // explicitly - Platforms: []specs.Platform{platforms.Normalize(platforms.DefaultSpec())}, + ID: id, + Labels: xlabels, + MetadataStore: md, + Executor: exe, + Snapshotter: containerdsnapshot.NewSnapshotter(mdb.Snapshotter(snFactory.Name), c, md, "buildkit", gc, idmap), + ContentStore: c, + Applier: winlayers.NewFileSystemApplierWithWindows(c, apply.NewFileSystemApplier(c)), + Differ: winlayers.NewWalkingDiffWithWindows(c, walking.NewWalkingDiff(c)), + ImageStore: nil, // explicitly + Platforms: []specs.Platform{platforms.Normalize(platforms.DefaultSpec())}, + IdentityMapping: idmap, } return opt, nil } diff --git a/worker/runc/runc_test.go b/worker/runc/runc_test.go index 44935ff5..5c2082ed 100644 --- a/worker/runc/runc_test.go +++ b/worker/runc/runc_test.go @@ -39,7 +39,7 @@ func newWorkerOpt(t *testing.T, processMode oci.ProcessMode) (base.WorkerOpt, fu }, } rootless := false - workerOpt, err := NewWorkerOpt(tmpdir, snFactory, rootless, processMode, nil) + workerOpt, err := NewWorkerOpt(tmpdir, snFactory, rootless, processMode, nil, nil) require.NoError(t, err) return workerOpt, cleanup