fileop: add chown support
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>docker-19.03
parent
171feaafeb
commit
7210bf6806
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
bccommon "github.com/moby/buildkit/cmd/buildctl/common"
|
||||
"github.com/moby/buildkit/util/apicaps"
|
||||
|
@ -16,7 +15,6 @@ import (
|
|||
|
||||
func init() {
|
||||
apicaps.ExportedProduct = "buildkit"
|
||||
syscall.Umask(0)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// +build !windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
syscall.Umask(0)
|
||||
}
|
|
@ -13,7 +13,6 @@ import (
|
|||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
|
@ -56,7 +55,6 @@ import (
|
|||
func init() {
|
||||
apicaps.ExportedProduct = "buildkit"
|
||||
seed.WithTimeAndRand()
|
||||
syscall.Umask(0)
|
||||
}
|
||||
|
||||
type workerInitializerOpt struct {
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// +build !windows
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
syscall.Umask(0)
|
||||
}
|
|
@ -3,6 +3,7 @@ package file
|
|||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
|
@ -11,24 +12,34 @@ import (
|
|||
"github.com/moby/buildkit/solver/llbsolver/ops/fileoptypes"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
copy "github.com/tonistiigi/fsutil/copy"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func mkdir(ctx context.Context, d string, action pb.FileActionMkDir) error {
|
||||
func mkdir(ctx context.Context, d string, action pb.FileActionMkDir, user *uidgid) error {
|
||||
p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if action.MakeParents {
|
||||
if err := os.MkdirAll(p, os.FileMode(action.Mode)&0777); err != nil {
|
||||
if err := mkdirAll(p, os.FileMode(action.Mode)&0777, user); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := os.Mkdir(p, os.FileMode(action.Mode)&0777); err != nil {
|
||||
if os.IsExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
if user != nil {
|
||||
if err := os.Chown(p, user.uid, user.gid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if action.Timestamp != -1 {
|
||||
|
@ -42,7 +53,7 @@ func mkdir(ctx context.Context, d string, action pb.FileActionMkDir) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func mkfile(ctx context.Context, d string, action pb.FileActionMkFile) error {
|
||||
func mkfile(ctx context.Context, d string, action pb.FileActionMkFile, user *uidgid) error {
|
||||
p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path)))
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -52,6 +63,12 @@ func mkfile(ctx context.Context, d string, action pb.FileActionMkFile) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if user != nil {
|
||||
if err := os.Chown(p, user.uid, user.gid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if action.Timestamp != -1 {
|
||||
st := unix.Timespec{Sec: action.Timestamp / 1e9, Nsec: action.Timestamp % 1e9}
|
||||
timespec := []unix.Timespec{st, st}
|
||||
|
@ -59,6 +76,7 @@ func mkfile(ctx context.Context, d string, action pb.FileActionMkFile) error {
|
|||
return errors.Wrapf(err, "failed to utime %s", p)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -78,7 +96,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) error {
|
||||
func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy, u *uidgid) error {
|
||||
// // src is the source path
|
||||
// Src string `protobuf:"bytes,1,opt,name=src,proto3" json:"src,omitempty"`
|
||||
// // dest path
|
||||
|
@ -118,6 +136,19 @@ func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy) err
|
|||
opt = append(opt, copy.AllowWildcards)
|
||||
}
|
||||
|
||||
if u != nil {
|
||||
opt = append(opt, func(ci *copy.CopyInfo) {
|
||||
ci.Chown = ©.ChownOpt{Uid: u.uid, Gid: u.gid}
|
||||
})
|
||||
}
|
||||
|
||||
xattrErrorHandler := func(dst, src, key string, err error) error {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
opt = append(opt, copy.WithXAttrErrorHandler(xattrErrorHandler))
|
||||
|
||||
if err := copy.Copy(ctx, srcp, destp, opt...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -128,7 +159,7 @@ func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy) err
|
|||
type Backend struct {
|
||||
}
|
||||
|
||||
func (fb *Backend) Mkdir(ctx context.Context, m fileoptypes.Mount, action pb.FileActionMkDir) error {
|
||||
func (fb *Backend) Mkdir(ctx context.Context, m, user, group fileoptypes.Mount, action pb.FileActionMkDir) error {
|
||||
mnt, ok := m.(*Mount)
|
||||
if !ok {
|
||||
return errors.Errorf("invalid mount type %T", m)
|
||||
|
@ -141,10 +172,15 @@ func (fb *Backend) Mkdir(ctx context.Context, m fileoptypes.Mount, action pb.Fil
|
|||
}
|
||||
defer lm.Unmount()
|
||||
|
||||
return mkdir(ctx, dir, action)
|
||||
u, err := readUser(action.Owner, user, group)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return mkdir(ctx, dir, action, u)
|
||||
}
|
||||
|
||||
func (fb *Backend) Mkfile(ctx context.Context, m fileoptypes.Mount, action pb.FileActionMkFile) error {
|
||||
func (fb *Backend) Mkfile(ctx context.Context, m, user, group fileoptypes.Mount, action pb.FileActionMkFile) error {
|
||||
mnt, ok := m.(*Mount)
|
||||
if !ok {
|
||||
return errors.Errorf("invalid mount type %T", m)
|
||||
|
@ -157,7 +193,12 @@ func (fb *Backend) Mkfile(ctx context.Context, m fileoptypes.Mount, action pb.Fi
|
|||
}
|
||||
defer lm.Unmount()
|
||||
|
||||
return mkfile(ctx, dir, action)
|
||||
u, err := readUser(action.Owner, user, group)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return mkfile(ctx, dir, action, u)
|
||||
}
|
||||
func (fb *Backend) Rm(ctx context.Context, m fileoptypes.Mount, action pb.FileActionRm) error {
|
||||
mnt, ok := m.(*Mount)
|
||||
|
@ -174,7 +215,7 @@ func (fb *Backend) Rm(ctx context.Context, m fileoptypes.Mount, action pb.FileAc
|
|||
|
||||
return rm(ctx, dir, action)
|
||||
}
|
||||
func (fb *Backend) Copy(ctx context.Context, m1 fileoptypes.Mount, m2 fileoptypes.Mount, action pb.FileActionCopy) error {
|
||||
func (fb *Backend) Copy(ctx context.Context, m1, m2, user, group fileoptypes.Mount, action pb.FileActionCopy) error {
|
||||
mnt1, ok := m1.(*Mount)
|
||||
if !ok {
|
||||
return errors.Errorf("invalid mount type %T", m1)
|
||||
|
@ -198,5 +239,12 @@ func (fb *Backend) Copy(ctx context.Context, m1 fileoptypes.Mount, m2 fileoptype
|
|||
}
|
||||
defer lm2.Unmount()
|
||||
|
||||
return docopy(ctx, src, dest, action)
|
||||
u, err := readUser(action.Owner, user, group)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logrus.Debugf("copy %+v %+v %+v", action.Owner, user, group)
|
||||
|
||||
return docopy(ctx, src, dest, action, u)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// mkdirAll is forked os.MkdirAll
|
||||
func mkdirAll(path string, perm os.FileMode, user *uidgid) error {
|
||||
// Fast path: if we can tell whether path is a directory or file, stop with success or error.
|
||||
dir, err := os.Stat(path)
|
||||
if err == nil {
|
||||
if dir.IsDir() {
|
||||
return nil
|
||||
}
|
||||
return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR}
|
||||
}
|
||||
|
||||
// Slow path: make sure parent exists and then call Mkdir for path.
|
||||
i := len(path)
|
||||
for i > 0 && os.IsPathSeparator(path[i-1]) { // Skip trailing path separator.
|
||||
i--
|
||||
}
|
||||
|
||||
j := i
|
||||
for j > 0 && !os.IsPathSeparator(path[j-1]) { // Scan backward over element.
|
||||
j--
|
||||
}
|
||||
|
||||
if j > 1 {
|
||||
// Create parent.
|
||||
err = mkdirAll(fixRootDirectory(path[:j-1]), perm, user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
dir, err1 := os.Lstat(path)
|
||||
if err1 == nil && dir.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parent now exists; invoke Mkdir and use its result.
|
||||
err = os.Mkdir(path, perm)
|
||||
if err != nil {
|
||||
// Handle arguments like "foo/." by
|
||||
// double-checking that directory doesn't exist.
|
||||
dir, err1 := os.Lstat(path)
|
||||
if err1 == nil && dir.IsDir() {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if user != nil {
|
||||
if err := os.Chown(path, user.uid, user.gid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
// +build !windows
|
||||
|
||||
package file
|
||||
|
||||
func fixRootDirectory(p string) string {
|
||||
return p
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// +build windows
|
||||
|
||||
package file
|
||||
|
||||
import "os"
|
||||
|
||||
func fixRootDirectory(p string) string {
|
||||
if len(p) == len(`\\?\c:`) {
|
||||
if os.IsPathSeparator(p[0]) && os.IsPathSeparator(p[1]) && p[2] == '?' && os.IsPathSeparator(p[3]) && p[5] == ':' {
|
||||
return p + `\`
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/containerd/continuity/fs"
|
||||
"github.com/moby/buildkit/snapshot"
|
||||
"github.com/moby/buildkit/solver/llbsolver/ops/fileoptypes"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
"github.com/opencontainers/runc/libcontainer/user"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type uidgid struct {
|
||||
uid, gid int
|
||||
}
|
||||
|
||||
func readUser(chopt *pb.ChownOpt, mu, mg fileoptypes.Mount) (*uidgid, error) {
|
||||
if chopt == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var us uidgid
|
||||
if chopt.User != nil {
|
||||
switch u := chopt.User.User.(type) {
|
||||
case *pb.UserOpt_ByName:
|
||||
if mu == nil {
|
||||
return nil, errors.Errorf("invalid missing user mount")
|
||||
}
|
||||
mmu, ok := mu.(*Mount)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("invalid mount type %T", mu)
|
||||
}
|
||||
lm := snapshot.LocalMounter(mmu.m)
|
||||
dir, err := lm.Mount()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer lm.Unmount()
|
||||
|
||||
passwdPath, err := user.GetPasswdPath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
passwdPath, err = fs.RootPath(dir, passwdPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ufile, err := os.Open(passwdPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ufile.Close()
|
||||
|
||||
users, err := user.ParsePasswdFilter(ufile, func(uu user.User) bool {
|
||||
return uu.Name == u.ByName.Name
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(users) > 0 {
|
||||
us.uid = users[0].Uid
|
||||
us.gid = users[0].Gid
|
||||
}
|
||||
case *pb.UserOpt_ByID:
|
||||
us.uid = int(u.ByID)
|
||||
us.gid = int(u.ByID)
|
||||
}
|
||||
}
|
||||
|
||||
if chopt.Group != nil {
|
||||
switch u := chopt.Group.User.(type) {
|
||||
case *pb.UserOpt_ByName:
|
||||
if mg == nil {
|
||||
return nil, errors.Errorf("invalid missing group mount")
|
||||
}
|
||||
mmg, ok := mg.(*Mount)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("invalid mount type %T", mg)
|
||||
}
|
||||
lm := snapshot.LocalMounter(mmg.m)
|
||||
dir, err := lm.Mount()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer lm.Unmount()
|
||||
|
||||
groupPath, err := user.GetGroupPath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
groupPath, err = fs.RootPath(dir, groupPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gfile, err := os.Open(groupPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer gfile.Close()
|
||||
|
||||
groups, err := user.ParseGroupFilter(gfile, func(g user.Group) bool {
|
||||
return g.Name == u.ByName.Name
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(groups) > 0 {
|
||||
us.gid = groups[0].Gid
|
||||
}
|
||||
case *pb.UserOpt_ByID:
|
||||
us.gid = int(u.ByID)
|
||||
}
|
||||
}
|
||||
|
||||
return &us, nil
|
||||
}
|
|
@ -55,7 +55,7 @@ func (f *fileOp) CacheMap(ctx context.Context, index int) (*solver.CacheMap, boo
|
|||
switch a := action.Action.(type) {
|
||||
case *pb.FileAction_Mkdir:
|
||||
p := *a.Mkdir
|
||||
p.Owner = nil
|
||||
processOwner(p.Owner, selectors)
|
||||
dt, err = json.Marshal(p)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
|
@ -63,6 +63,7 @@ func (f *fileOp) CacheMap(ctx context.Context, index int) (*solver.CacheMap, boo
|
|||
case *pb.FileAction_Mkfile:
|
||||
p := *a.Mkfile
|
||||
p.Owner = nil
|
||||
processOwner(p.Owner, selectors)
|
||||
dt, err = json.Marshal(p)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
|
@ -75,6 +76,7 @@ func (f *fileOp) CacheMap(ctx context.Context, index int) (*solver.CacheMap, boo
|
|||
}
|
||||
case *pb.FileAction_Copy:
|
||||
p := *a.Copy
|
||||
processOwner(p.Owner, selectors)
|
||||
p.Owner = nil
|
||||
if action.SecondaryInput != -1 && int(action.SecondaryInput) < f.numInputs {
|
||||
p.Src = path.Base(p.Src)
|
||||
|
@ -213,6 +215,29 @@ func dedupeSelectors(m map[llbsolver.Selector]struct{}) []llbsolver.Selector {
|
|||
return selectors
|
||||
}
|
||||
|
||||
func processOwner(chopt *pb.ChownOpt, selectors map[int]map[llbsolver.Selector]struct{}) error {
|
||||
if chopt == nil {
|
||||
return nil
|
||||
}
|
||||
if chopt.User != nil {
|
||||
if u, ok := chopt.User.User.(*pb.UserOpt_ByName); ok {
|
||||
if u.ByName.Input < 0 {
|
||||
return errors.Errorf("invalid user index %d", u.ByName.Input)
|
||||
}
|
||||
addSelector(selectors, int(u.ByName.Input), "/etc/passwd", false, true)
|
||||
}
|
||||
}
|
||||
if chopt.Group != nil {
|
||||
if u, ok := chopt.Group.User.(*pb.UserOpt_ByName); ok {
|
||||
if u.ByName.Input < 0 {
|
||||
return errors.Errorf("invalid user index %d", u.ByName.Input)
|
||||
}
|
||||
addSelector(selectors, int(u.ByName.Input), "/etc/group", false, true)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewFileOpSolver(b fileoptypes.Backend, r fileoptypes.RefManager) *FileOpSolver {
|
||||
return &FileOpSolver{
|
||||
b: b,
|
||||
|
@ -354,6 +379,13 @@ func (s *FileOpSolver) getInput(ctx context.Context, idx int, inputs []fileoptyp
|
|||
return inp, nil
|
||||
}
|
||||
|
||||
var toRelease []fileoptypes.Mount
|
||||
defer func() {
|
||||
for _, m := range toRelease {
|
||||
m.Release(context.TODO())
|
||||
}
|
||||
}()
|
||||
|
||||
var inpMount, inpMountSecondary fileoptypes.Mount
|
||||
action := actions[idx-len(inputs)]
|
||||
|
||||
|
@ -388,6 +420,7 @@ func (s *FileOpSolver) getInput(ctx context.Context, idx int, inputs []fileoptyp
|
|||
return err
|
||||
}
|
||||
inpMountSecondary = m
|
||||
toRelease = append(toRelease, m)
|
||||
return nil
|
||||
}
|
||||
inpMountSecondary = inp.mount
|
||||
|
@ -395,6 +428,51 @@ func (s *FileOpSolver) getInput(ctx context.Context, idx int, inputs []fileoptyp
|
|||
}
|
||||
}
|
||||
|
||||
loadUser := func(ctx context.Context, uopt *pb.UserOpt) (fileoptypes.Mount, error) {
|
||||
if uopt == nil {
|
||||
return nil, nil
|
||||
}
|
||||
switch u := uopt.User.(type) {
|
||||
case *pb.UserOpt_ByName:
|
||||
var m fileoptypes.Mount
|
||||
if u.ByName.Input < 0 {
|
||||
return nil, errors.Errorf("invalid user index: %d", u.ByName.Input)
|
||||
}
|
||||
inp, err := s.getInput(ctx, int(u.ByName.Input), inputs, actions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if inp.ref != nil {
|
||||
mm, err := s.r.Prepare(ctx, inp.ref, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toRelease = append(toRelease, mm)
|
||||
m = mm
|
||||
} else {
|
||||
m = inp.mount
|
||||
}
|
||||
return m, nil
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
loadOwner := func(ctx context.Context, chopt *pb.ChownOpt) (fileoptypes.Mount, fileoptypes.Mount, error) {
|
||||
if chopt == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
um, err := loadUser(ctx, chopt.User)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
gm, err := loadUser(ctx, chopt.Group)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return um, gm, nil
|
||||
}
|
||||
|
||||
if action.Input != -1 && action.SecondaryInput != -1 {
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
eg.Go(loadInput(ctx))
|
||||
|
@ -425,11 +503,19 @@ func (s *FileOpSolver) getInput(ctx context.Context, idx int, inputs []fileoptyp
|
|||
|
||||
switch a := action.Action.(type) {
|
||||
case *pb.FileAction_Mkdir:
|
||||
if err := s.b.Mkdir(ctx, inpMount, *a.Mkdir); err != nil {
|
||||
user, group, err := loadOwner(ctx, a.Mkdir.Owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.b.Mkdir(ctx, inpMount, user, group, *a.Mkdir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *pb.FileAction_Mkfile:
|
||||
if err := s.b.Mkfile(ctx, inpMount, *a.Mkfile); err != nil {
|
||||
user, group, err := loadOwner(ctx, a.Mkfile.Owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.b.Mkfile(ctx, inpMount, user, group, *a.Mkfile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *pb.FileAction_Rm:
|
||||
|
@ -444,7 +530,11 @@ func (s *FileOpSolver) getInput(ctx context.Context, idx int, inputs []fileoptyp
|
|||
}
|
||||
inpMountSecondary = m
|
||||
}
|
||||
if err := s.b.Copy(ctx, inpMountSecondary, inpMount, *a.Copy); err != nil {
|
||||
user, group, err := loadOwner(ctx, a.Copy.Owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.b.Copy(ctx, inpMountSecondary, inpMount, user, group, *a.Copy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -54,6 +54,135 @@ func TestMkdirMkfile(t *testing.T) {
|
|||
require.Equal(t, fo.Actions[1].Action.(*pb.FileAction_Mkfile).Mkfile, o.mount.chain[1].mkfile)
|
||||
}
|
||||
|
||||
func TestChownOpt(t *testing.T) {
|
||||
fo := &pb.FileOp{
|
||||
Actions: []*pb.FileAction{
|
||||
{
|
||||
Input: 0,
|
||||
SecondaryInput: -1,
|
||||
Output: -1,
|
||||
Action: &pb.FileAction_Mkdir{
|
||||
Mkdir: &pb.FileActionMkDir{
|
||||
Path: "/foo/bar",
|
||||
MakeParents: true,
|
||||
Mode: 0700,
|
||||
Owner: &pb.ChownOpt{
|
||||
User: &pb.UserOpt{
|
||||
User: &pb.UserOpt_ByName{
|
||||
ByName: &pb.NamedUserOpt{
|
||||
Input: 1,
|
||||
Name: "myuser",
|
||||
},
|
||||
},
|
||||
},
|
||||
Group: &pb.UserOpt{
|
||||
User: &pb.UserOpt_ByName{
|
||||
ByName: &pb.NamedUserOpt{
|
||||
Input: 1,
|
||||
Name: "myuser",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: 2,
|
||||
SecondaryInput: -1,
|
||||
Output: 0,
|
||||
Action: &pb.FileAction_Mkfile{
|
||||
Mkfile: &pb.FileActionMkFile{
|
||||
Path: "/foo/bar/baz",
|
||||
Mode: 0700,
|
||||
Owner: &pb.ChownOpt{
|
||||
User: &pb.UserOpt{
|
||||
User: &pb.UserOpt_ByID{
|
||||
ByID: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
s, rb := newTestFileSolver()
|
||||
inp := rb.NewRef("ref1")
|
||||
inp2 := rb.NewRef("usermount")
|
||||
outs, err := s.Solve(context.TODO(), []fileoptypes.Ref{inp, inp2}, fo.Actions)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(outs), 1)
|
||||
rb.checkReleased(t, append(outs, inp, inp2))
|
||||
|
||||
o := outs[0].(*testFileRef)
|
||||
require.Equal(t, "mount-ref1-mkdir#u(mount-usermount)#g(mount-usermount)-mkfile-commit", o.id)
|
||||
require.Equal(t, 2, len(o.mount.chain))
|
||||
require.Equal(t, fo.Actions[0].Action.(*pb.FileAction_Mkdir).Mkdir, o.mount.chain[0].mkdir)
|
||||
require.Equal(t, fo.Actions[1].Action.(*pb.FileAction_Mkfile).Mkfile, o.mount.chain[1].mkfile)
|
||||
}
|
||||
|
||||
func TestChownCopy(t *testing.T) {
|
||||
fo := &pb.FileOp{
|
||||
Actions: []*pb.FileAction{
|
||||
{
|
||||
Input: -1,
|
||||
SecondaryInput: -1,
|
||||
Output: -1,
|
||||
Action: &pb.FileAction_Mkfile{
|
||||
Mkfile: &pb.FileActionMkFile{
|
||||
Path: "/foo/bar/baz",
|
||||
Mode: 0700,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Input: 1,
|
||||
SecondaryInput: 0,
|
||||
Output: 0,
|
||||
Action: &pb.FileAction_Copy{
|
||||
Copy: &pb.FileActionCopy{
|
||||
Src: "/src",
|
||||
Dest: "/dest",
|
||||
Owner: &pb.ChownOpt{
|
||||
User: &pb.UserOpt{
|
||||
User: &pb.UserOpt_ByName{
|
||||
ByName: &pb.NamedUserOpt{
|
||||
Input: 1,
|
||||
Name: "myuser",
|
||||
},
|
||||
},
|
||||
},
|
||||
Group: &pb.UserOpt{
|
||||
User: &pb.UserOpt_ByName{
|
||||
ByName: &pb.NamedUserOpt{
|
||||
Input: 2,
|
||||
Name: "mygroup",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
s, rb := newTestFileSolver()
|
||||
inpSrc := rb.NewRef("src")
|
||||
inpDest := rb.NewRef("dest")
|
||||
outs, err := s.Solve(context.TODO(), []fileoptypes.Ref{inpSrc, inpDest}, fo.Actions)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(outs), 1)
|
||||
rb.checkReleased(t, append(outs, inpSrc, inpDest))
|
||||
|
||||
o := outs[0].(*testFileRef)
|
||||
require.Equal(t, "mount-dest-copy(mount-src)#u(mount-dest)#g(mount-scratch-mkfile)-commit", o.id)
|
||||
require.Equal(t, 1, len(o.mount.chain))
|
||||
require.Equal(t, fo.Actions[1].Action.(*pb.FileAction_Copy).Copy, o.mount.chain[0].copy)
|
||||
}
|
||||
|
||||
func TestInvalidNoOutput(t *testing.T) {
|
||||
fo := &pb.FileOp{
|
||||
Actions: []*pb.FileAction{
|
||||
|
@ -267,6 +396,37 @@ func TestFileFromScratch(t *testing.T) {
|
|||
require.Equal(t, fo.Actions[1].Action.(*pb.FileAction_Mkfile).Mkfile, o.mount.chain[1].mkfile)
|
||||
}
|
||||
|
||||
func TestFileCopyInputSrc(t *testing.T) {
|
||||
fo := &pb.FileOp{
|
||||
Actions: []*pb.FileAction{
|
||||
{
|
||||
Input: 1,
|
||||
SecondaryInput: 0,
|
||||
Output: 0,
|
||||
Action: &pb.FileAction_Copy{
|
||||
Copy: &pb.FileActionCopy{
|
||||
Src: "/src",
|
||||
Dest: "/dest",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
s, rb := newTestFileSolver()
|
||||
inp0 := rb.NewRef("srcref")
|
||||
inp1 := rb.NewRef("destref")
|
||||
outs, err := s.Solve(context.TODO(), []fileoptypes.Ref{inp0, inp1}, fo.Actions)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(outs), 1)
|
||||
rb.checkReleased(t, append(outs, inp0, inp1))
|
||||
|
||||
o := outs[0].(*testFileRef)
|
||||
require.Equal(t, "mount-destref-copy(mount-srcref)-commit", o.id)
|
||||
require.Equal(t, 1, len(o.mount.chain))
|
||||
require.Equal(t, fo.Actions[0].Action.(*pb.FileAction_Copy).Copy, o.mount.chain[0].copy)
|
||||
}
|
||||
|
||||
func TestFileCopyInputRm(t *testing.T) {
|
||||
fo := &pb.FileOp{
|
||||
Actions: []*pb.FileAction{
|
||||
|
@ -409,6 +569,17 @@ type testMount struct {
|
|||
active *testFileRef
|
||||
}
|
||||
|
||||
func (tm *testMount) addUser(user, group fileoptypes.Mount) {
|
||||
if user != nil {
|
||||
um := user.(*testMount)
|
||||
tm.id += "#u(" + um.id + ")"
|
||||
}
|
||||
if group != nil {
|
||||
gm := group.(*testMount)
|
||||
tm.id += "#g(" + gm.id + ")"
|
||||
}
|
||||
}
|
||||
|
||||
type mod struct {
|
||||
mkdir *pb.FileActionMkDir
|
||||
rm *pb.FileActionRm
|
||||
|
@ -419,7 +590,7 @@ type mod struct {
|
|||
|
||||
func (m *testMount) IsFileOpMount() {}
|
||||
func (m *testMount) Release(ctx context.Context) error {
|
||||
if m.initID != m.id {
|
||||
if m.b.mounts[m.initID] != m {
|
||||
return m.b.mounts[m.initID].Release(ctx)
|
||||
}
|
||||
if m.unmounted {
|
||||
|
@ -435,19 +606,21 @@ func (m *testMount) Release(ctx context.Context) error {
|
|||
type testFileBackend struct {
|
||||
}
|
||||
|
||||
func (b *testFileBackend) Mkdir(_ context.Context, m fileoptypes.Mount, a pb.FileActionMkDir) error {
|
||||
func (b *testFileBackend) Mkdir(_ context.Context, m, user, group fileoptypes.Mount, a pb.FileActionMkDir) error {
|
||||
mm := m.(*testMount)
|
||||
if mm.callback != nil {
|
||||
mm.callback()
|
||||
}
|
||||
mm.id += "-mkdir"
|
||||
mm.addUser(user, group)
|
||||
mm.chain = append(mm.chain, mod{mkdir: &a})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *testFileBackend) Mkfile(_ context.Context, m fileoptypes.Mount, a pb.FileActionMkFile) error {
|
||||
func (b *testFileBackend) Mkfile(_ context.Context, m, user, group fileoptypes.Mount, a pb.FileActionMkFile) error {
|
||||
mm := m.(*testMount)
|
||||
mm.id += "-mkfile"
|
||||
mm.addUser(user, group)
|
||||
mm.chain = append(mm.chain, mod{mkfile: &a})
|
||||
return nil
|
||||
}
|
||||
|
@ -457,10 +630,11 @@ func (b *testFileBackend) Rm(_ context.Context, m fileoptypes.Mount, a pb.FileAc
|
|||
mm.chain = append(mm.chain, mod{rm: &a})
|
||||
return nil
|
||||
}
|
||||
func (b *testFileBackend) Copy(_ context.Context, m1 fileoptypes.Mount, m fileoptypes.Mount, a pb.FileActionCopy) error {
|
||||
func (b *testFileBackend) Copy(_ context.Context, m1, m, user, group fileoptypes.Mount, a pb.FileActionCopy) error {
|
||||
mm := m.(*testMount)
|
||||
mm1 := m1.(*testMount)
|
||||
mm.id += "-copy(" + mm1.id + ")"
|
||||
mm.addUser(user, group)
|
||||
mm.chain = append(mm.chain, mod{copy: &a, copySrc: mm1.chain})
|
||||
return nil
|
||||
}
|
||||
|
@ -524,6 +698,6 @@ loop0:
|
|||
}
|
||||
|
||||
for _, m := range b.mounts {
|
||||
require.True(t, m.unmounted, "%s still mounted", m.id)
|
||||
require.True(t, m.unmounted, "%s %p still mounted", m.id, m)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,10 @@ type Mount interface {
|
|||
}
|
||||
|
||||
type Backend interface {
|
||||
Mkdir(context.Context, Mount, pb.FileActionMkDir) error
|
||||
Mkfile(context.Context, Mount, pb.FileActionMkFile) error
|
||||
Mkdir(context.Context, Mount, Mount, Mount, pb.FileActionMkDir) error
|
||||
Mkfile(context.Context, Mount, Mount, Mount, pb.FileActionMkFile) error
|
||||
Rm(context.Context, Mount, pb.FileActionRm) error
|
||||
Copy(context.Context, Mount, Mount, pb.FileActionCopy) error
|
||||
Copy(context.Context, Mount, Mount, Mount, Mount, pb.FileActionCopy) error
|
||||
}
|
||||
|
||||
type RefManager interface {
|
||||
|
|
Loading…
Reference in New Issue