726 lines
17 KiB
Go
726 lines
17 KiB
Go
package ops
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"github.com/moby/buildkit/session"
|
|
"github.com/moby/buildkit/solver/llbsolver/ops/fileoptypes"
|
|
"github.com/moby/buildkit/solver/pb"
|
|
"github.com/pkg/errors"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestMkdirMkfile(t *testing.T) {
|
|
t.Parallel()
|
|
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,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Input: 1,
|
|
SecondaryInput: -1,
|
|
Output: 0,
|
|
Action: &pb.FileAction_Mkfile{
|
|
Mkfile: &pb.FileActionMkFile{
|
|
Path: "/foo/bar/baz",
|
|
Mode: 0700,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
s, rb := newTestFileSolver()
|
|
inp := rb.NewRef("ref1")
|
|
outs, err := s.Solve(context.TODO(), []fileoptypes.Ref{inp}, fo.Actions, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(outs), 1)
|
|
rb.checkReleased(t, append(outs, inp))
|
|
|
|
o := outs[0].(*testFileRef)
|
|
require.Equal(t, "mount-ref1-mkdir-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 TestChownOpt(t *testing.T) {
|
|
t.Parallel()
|
|
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, nil)
|
|
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) {
|
|
t.Parallel()
|
|
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, nil)
|
|
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) {
|
|
t.Parallel()
|
|
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,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
s, rb := newTestFileSolver()
|
|
outs, err := s.Solve(context.TODO(), []fileoptypes.Ref{}, fo.Actions, nil)
|
|
rb.checkReleased(t, outs)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "no outputs specified")
|
|
}
|
|
|
|
func TestInvalidDuplicateOutput(t *testing.T) {
|
|
t.Parallel()
|
|
fo := &pb.FileOp{
|
|
Actions: []*pb.FileAction{
|
|
{
|
|
Input: 0,
|
|
SecondaryInput: -1,
|
|
Output: 0,
|
|
Action: &pb.FileAction_Mkdir{
|
|
Mkdir: &pb.FileActionMkDir{
|
|
Path: "/foo/bar",
|
|
MakeParents: true,
|
|
Mode: 0700,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Input: 1,
|
|
SecondaryInput: -1,
|
|
Output: 0,
|
|
Action: &pb.FileAction_Mkfile{
|
|
Mkfile: &pb.FileActionMkFile{
|
|
Path: "/foo/bar/baz",
|
|
Mode: 0700,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
s, rb := newTestFileSolver()
|
|
_, err := s.Solve(context.TODO(), []fileoptypes.Ref{}, fo.Actions, nil)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "duplicate output")
|
|
rb.checkReleased(t, nil)
|
|
}
|
|
|
|
func TestActionInvalidIndex(t *testing.T) {
|
|
t.Parallel()
|
|
fo := &pb.FileOp{
|
|
Actions: []*pb.FileAction{
|
|
{
|
|
Input: 0,
|
|
SecondaryInput: -1,
|
|
Output: 0,
|
|
Action: &pb.FileAction_Mkdir{
|
|
Mkdir: &pb.FileActionMkDir{
|
|
Path: "/foo/bar",
|
|
MakeParents: true,
|
|
Mode: 0700,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
s, rb := newTestFileSolver()
|
|
_, err := s.Solve(context.TODO(), []fileoptypes.Ref{}, fo.Actions, nil)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "loop from index")
|
|
rb.checkReleased(t, nil)
|
|
}
|
|
|
|
func TestActionLoop(t *testing.T) {
|
|
t.Parallel()
|
|
fo := &pb.FileOp{
|
|
Actions: []*pb.FileAction{
|
|
{
|
|
Input: 1,
|
|
SecondaryInput: -1,
|
|
Output: -1,
|
|
Action: &pb.FileAction_Mkdir{
|
|
Mkdir: &pb.FileActionMkDir{
|
|
Path: "/foo/bar",
|
|
MakeParents: true,
|
|
Mode: 0700,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Input: 0,
|
|
SecondaryInput: -1,
|
|
Output: 0,
|
|
Action: &pb.FileAction_Mkfile{
|
|
Mkfile: &pb.FileActionMkFile{
|
|
Path: "/foo/bar/baz",
|
|
Mode: 0700,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
s, rb := newTestFileSolver()
|
|
_, err := s.Solve(context.TODO(), []fileoptypes.Ref{}, fo.Actions, nil)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "loop from index")
|
|
rb.checkReleased(t, nil)
|
|
}
|
|
|
|
func TestMultiOutput(t *testing.T) {
|
|
t.Parallel()
|
|
fo := &pb.FileOp{
|
|
Actions: []*pb.FileAction{
|
|
{
|
|
Input: 0,
|
|
SecondaryInput: -1,
|
|
Output: 0,
|
|
Action: &pb.FileAction_Mkdir{
|
|
Mkdir: &pb.FileActionMkDir{
|
|
Path: "/foo/bar",
|
|
MakeParents: true,
|
|
Mode: 0700,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Input: 1,
|
|
SecondaryInput: -1,
|
|
Output: 1,
|
|
Action: &pb.FileAction_Mkfile{
|
|
Mkfile: &pb.FileActionMkFile{
|
|
Path: "/foo/bar/baz",
|
|
Mode: 0700,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
s, rb := newTestFileSolver()
|
|
inp := rb.NewRef("ref1")
|
|
outs, err := s.Solve(context.TODO(), []fileoptypes.Ref{inp}, fo.Actions, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(outs), 2)
|
|
rb.checkReleased(t, append(outs, inp))
|
|
|
|
o := outs[0].(*testFileRef)
|
|
require.Equal(t, "mount-ref1-mkdir-commit", o.id)
|
|
require.Equal(t, 1, len(o.mount.chain))
|
|
require.Equal(t, fo.Actions[0].Action.(*pb.FileAction_Mkdir).Mkdir, o.mount.chain[0].mkdir)
|
|
|
|
o = outs[1].(*testFileRef)
|
|
require.Equal(t, "mount-ref1-mkdir-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 TestFileFromScratch(t *testing.T) {
|
|
t.Parallel()
|
|
fo := &pb.FileOp{
|
|
Actions: []*pb.FileAction{
|
|
{
|
|
Input: -1,
|
|
SecondaryInput: -1,
|
|
Output: -1,
|
|
Action: &pb.FileAction_Mkdir{
|
|
Mkdir: &pb.FileActionMkDir{
|
|
Path: "/foo/bar",
|
|
MakeParents: true,
|
|
Mode: 0700,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Input: 0,
|
|
SecondaryInput: -1,
|
|
Output: 0,
|
|
Action: &pb.FileAction_Mkfile{
|
|
Mkfile: &pb.FileActionMkFile{
|
|
Path: "/foo/bar/baz",
|
|
Mode: 0700,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
s, rb := newTestFileSolver()
|
|
outs, err := s.Solve(context.TODO(), []fileoptypes.Ref{}, fo.Actions, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(outs), 1)
|
|
rb.checkReleased(t, outs)
|
|
|
|
o := outs[0].(*testFileRef)
|
|
|
|
require.Equal(t, "mount-scratch-mkdir-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 TestFileCopyInputSrc(t *testing.T) {
|
|
t.Parallel()
|
|
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, nil)
|
|
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) {
|
|
t.Parallel()
|
|
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,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Input: 1,
|
|
SecondaryInput: 2,
|
|
Output: -1,
|
|
Action: &pb.FileAction_Copy{
|
|
Copy: &pb.FileActionCopy{
|
|
Src: "/src",
|
|
Dest: "/dest",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Input: 3,
|
|
SecondaryInput: -1,
|
|
Output: 0,
|
|
Action: &pb.FileAction_Rm{
|
|
Rm: &pb.FileActionRm{
|
|
Path: "/foo/bar/baz",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
s, rb := newTestFileSolver()
|
|
inp0 := rb.NewRef("srcref")
|
|
inp1 := rb.NewRef("destref")
|
|
outs, err := s.Solve(context.TODO(), []fileoptypes.Ref{inp0, inp1}, fo.Actions, nil)
|
|
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-mkdir)-rm-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].copySrc[0].mkdir)
|
|
require.Equal(t, fo.Actions[1].Action.(*pb.FileAction_Copy).Copy, o.mount.chain[0].copy)
|
|
require.Equal(t, fo.Actions[2].Action.(*pb.FileAction_Rm).Rm, o.mount.chain[1].rm)
|
|
}
|
|
|
|
func TestFileParallelActions(t *testing.T) {
|
|
t.Parallel()
|
|
// two mkdirs from scratch copied over each other. mkdirs should happen in parallel
|
|
fo := &pb.FileOp{
|
|
Actions: []*pb.FileAction{
|
|
{
|
|
Input: 0,
|
|
SecondaryInput: -1,
|
|
Output: -1,
|
|
Action: &pb.FileAction_Mkdir{
|
|
Mkdir: &pb.FileActionMkDir{
|
|
Path: "/foo",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Input: 0,
|
|
SecondaryInput: -1,
|
|
Output: -1,
|
|
Action: &pb.FileAction_Mkdir{
|
|
Mkdir: &pb.FileActionMkDir{
|
|
Path: "/bar",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Input: 2,
|
|
SecondaryInput: 1,
|
|
Output: 0,
|
|
Action: &pb.FileAction_Copy{
|
|
Copy: &pb.FileActionCopy{
|
|
Src: "/src",
|
|
Dest: "/dest",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
s, rb := newTestFileSolver()
|
|
inp := rb.NewRef("inpref")
|
|
|
|
ch := make(chan struct{})
|
|
var sem int64
|
|
inp.callback = func() {
|
|
if atomic.AddInt64(&sem, 1) == 2 {
|
|
close(ch)
|
|
}
|
|
<-ch
|
|
}
|
|
|
|
outs, err := s.Solve(context.TODO(), []fileoptypes.Ref{inp}, fo.Actions, nil)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(outs), 1)
|
|
|
|
require.Equal(t, int64(2), sem)
|
|
}
|
|
|
|
func newTestFileSolver() (*FileOpSolver, *testFileRefBackend) {
|
|
trb := &testFileRefBackend{refs: map[*testFileRef]struct{}{}, mounts: map[string]*testMount{}}
|
|
return NewFileOpSolver(nil, &testFileBackend{}, trb), trb
|
|
}
|
|
|
|
type testFileRef struct {
|
|
id string
|
|
mount *testMount
|
|
refcount int
|
|
callback func()
|
|
}
|
|
|
|
func (r *testFileRef) Release(context.Context) error {
|
|
if r.refcount == 0 {
|
|
return errors.Errorf("ref already released")
|
|
}
|
|
r.refcount--
|
|
return nil
|
|
}
|
|
|
|
type testMount struct {
|
|
b *testFileRefBackend
|
|
id string
|
|
initID string
|
|
chain []mod
|
|
callback func()
|
|
unmounted bool
|
|
active *testFileRef
|
|
readonly bool
|
|
}
|
|
|
|
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
|
|
mkfile *pb.FileActionMkFile
|
|
copy *pb.FileActionCopy
|
|
copySrc []mod
|
|
}
|
|
|
|
func (tm *testMount) IsFileOpMount() {}
|
|
func (tm *testMount) Release(ctx context.Context) error {
|
|
if tm.b.mounts[tm.initID] != tm {
|
|
return tm.b.mounts[tm.initID].Release(ctx)
|
|
}
|
|
if tm.unmounted {
|
|
return errors.Errorf("already unmounted")
|
|
}
|
|
tm.unmounted = true
|
|
if tm.active != nil {
|
|
return tm.active.Release(ctx)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (tm *testMount) Readonly() bool {
|
|
return tm.readonly
|
|
}
|
|
|
|
type testFileBackend struct {
|
|
}
|
|
|
|
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, 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
|
|
}
|
|
func (b *testFileBackend) Rm(_ context.Context, m fileoptypes.Mount, a pb.FileActionRm) error {
|
|
mm := m.(*testMount)
|
|
mm.id += "-rm"
|
|
mm.chain = append(mm.chain, mod{rm: &a})
|
|
return nil
|
|
}
|
|
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
|
|
}
|
|
|
|
type testFileRefBackend struct {
|
|
mu sync.Mutex
|
|
refs map[*testFileRef]struct{}
|
|
mounts map[string]*testMount
|
|
}
|
|
|
|
func (b *testFileRefBackend) NewRef(id string) *testFileRef {
|
|
r := &testFileRef{refcount: 1, id: id}
|
|
b.refs[r] = struct{}{}
|
|
return r
|
|
}
|
|
|
|
func (b *testFileRefBackend) Prepare(ctx context.Context, ref fileoptypes.Ref, readonly bool, _ session.Group) (fileoptypes.Mount, error) {
|
|
var active *testFileRef
|
|
if ref == nil {
|
|
active = b.NewRef("scratch")
|
|
ref = active
|
|
}
|
|
rr := ref.(*testFileRef)
|
|
m := rr.mount
|
|
if m == nil {
|
|
m = &testMount{b: b, id: "mount-" + rr.id, callback: rr.callback, readonly: readonly}
|
|
}
|
|
m.initID = m.id
|
|
m.active = active
|
|
b.mu.Lock()
|
|
b.mounts[m.initID] = m
|
|
b.mu.Unlock()
|
|
m2 := *m
|
|
m2.chain = append([]mod{}, m2.chain...)
|
|
return &m2, nil
|
|
}
|
|
func (b *testFileRefBackend) Commit(ctx context.Context, mount fileoptypes.Mount) (fileoptypes.Ref, error) {
|
|
m := mount.(*testMount)
|
|
if err := b.mounts[m.initID].Release(context.TODO()); err != nil {
|
|
return nil, err
|
|
}
|
|
m2 := *m
|
|
m2.unmounted = false
|
|
m2.callback = nil
|
|
r := b.NewRef(m2.id + "-commit")
|
|
r.mount = &m2
|
|
return r, nil
|
|
}
|
|
|
|
func (b *testFileRefBackend) checkReleased(t *testing.T, outs []fileoptypes.Ref) {
|
|
loop0:
|
|
for r := range b.refs {
|
|
for _, o := range outs {
|
|
if o.(*testFileRef) == r {
|
|
require.Equal(t, 1, r.refcount)
|
|
continue loop0
|
|
}
|
|
}
|
|
require.Equal(t, 0, r.refcount, "%s not released", r.id)
|
|
}
|
|
for _, o := range outs {
|
|
_, ok := b.refs[o.(*testFileRef)]
|
|
require.True(t, ok)
|
|
}
|
|
|
|
for _, m := range b.mounts {
|
|
require.True(t, m.unmounted, "%s %p still mounted", m.id, m)
|
|
}
|
|
}
|