fileop: updates with new fsutil copy pkg

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
docker-19.03
Tonis Tiigi 2019-03-08 16:15:42 -08:00
parent 8a4674bab4
commit 0d17ac323e
13 changed files with 282 additions and 188 deletions

View File

@ -558,12 +558,13 @@ func (cc *cacheContext) lazyChecksum(ctx context.Context, m *mount, p string) (*
}
func (cc *cacheContext) checksum(ctx context.Context, root *iradix.Node, txn *iradix.Txn, m *mount, k []byte, follow bool) (*CacheRecord, bool, error) {
origk := k
k, cr, err := getFollowLinks(root, k, follow)
if err != nil {
return nil, false, err
}
if cr == nil {
return nil, false, errors.Wrapf(errNotFound, "%s not found", convertKeyToPath(k))
return nil, false, errors.Wrapf(errNotFound, "%q not found", convertKeyToPath(origk))
}
if cr.Digest != "" {
return cr, false, nil

View File

@ -672,8 +672,12 @@ func testFileOpMkdirMkfile(t *testing.T, sb integration.Sandbox) {
defer os.RemoveAll(destDir)
_, err = c.Solve(context.TODO(), def, SolveOpt{
Exporter: ExporterLocal,
ExporterOutputDir: destDir,
Exports: []ExportEntry{
{
Type: ExporterLocal,
OutputDir: destDir,
},
},
}, nil)
require.NoError(t, err)
@ -722,8 +726,12 @@ func testFileOpCopyRm(t *testing.T, sb integration.Sandbox) {
defer os.RemoveAll(destDir)
_, err = c.Solve(context.TODO(), def, SolveOpt{
Exporter: ExporterLocal,
ExporterOutputDir: destDir,
Exports: []ExportEntry{
{
Type: ExporterLocal,
OutputDir: destDir,
},
},
LocalDirs: map[string]string{
"mylocal": dir,
"mylocal2": dir2,

View File

@ -149,7 +149,7 @@ type fileActionMkdir struct {
func (a *fileActionMkdir) toProtoAction(parent string, base pb.InputIndex) pb.IsFileAction {
return &pb.FileAction_Mkdir{
Mkdir: &pb.FileActionMkDir{
Path: normalizePath(parent, a.file),
Path: normalizePath(parent, a.file, false),
Mode: int32(a.mode & 0777),
MakeParents: a.info.MakeParents,
Owner: a.info.ChownOpt.marshal(base),
@ -318,7 +318,7 @@ type fileActionMkfile struct {
func (a *fileActionMkfile) toProtoAction(parent string, base pb.InputIndex) pb.IsFileAction {
return &pb.FileAction_Mkfile{
Mkfile: &pb.FileActionMkFile{
Path: normalizePath(parent, a.file),
Path: normalizePath(parent, a.file, false),
Mode: int32(a.mode & 0777),
Data: a.dt,
Owner: a.info.ChownOpt.marshal(base),
@ -382,7 +382,7 @@ type fileActionRm struct {
func (a *fileActionRm) toProtoAction(parent string, base pb.InputIndex) pb.IsFileAction {
return &pb.FileAction_Rm{
Rm: &pb.FileActionRm{
Path: normalizePath(parent, a.file),
Path: normalizePath(parent, a.file, false),
AllowNotFound: a.info.AllowNotFound,
AllowWildcard: a.info.AllowWildcard,
},
@ -451,7 +451,7 @@ type fileActionCopy struct {
func (a *fileActionCopy) toProtoAction(parent string, base pb.InputIndex) pb.IsFileAction {
c := &pb.FileActionCopy{
Src: a.sourcePath(),
Dest: normalizePath(parent, a.dest),
Dest: normalizePath(parent, a.dest, true),
Owner: a.info.ChownOpt.marshal(base),
AllowWildcard: a.info.AllowWildcard,
AllowEmptyWildcard: a.info.AllowEmptyWildcard,
@ -683,11 +683,22 @@ func (f *FileOp) Marshal(c *Constraints) (digest.Digest, []byte, *pb.OpMetadata,
return f.Load()
}
func normalizePath(parent, p string) string {
func normalizePath(parent, p string, keepSlash bool) string {
origPath := p
p = path.Clean(p)
if !path.IsAbs(p) {
p = path.Join("/", parent, p)
}
if keepSlash {
if strings.HasSuffix(origPath, "/") && !strings.HasSuffix(p, "/") {
p += "/"
} else if strings.HasSuffix(origPath, "/.") {
if p != "/" {
p += "/"
}
p += "."
}
}
return p
}

View File

@ -19,6 +19,7 @@ import (
"github.com/containerd/containerd/pkg/seed"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/sys"
"github.com/docker/docker/pkg/reexec"
"github.com/docker/go-connections/sockets"
"github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc"
"github.com/moby/buildkit/cache/remotecache"
@ -55,6 +56,7 @@ import (
func init() {
apicaps.ExportedProduct = "buildkit"
seed.WithTimeAndRand()
reexec.Init()
}
type workerInitializerOpt struct {

View File

@ -474,7 +474,7 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
case *instructions.WorkdirCommand:
err = dispatchWorkdir(d, c, true, &opt)
case *instructions.AddCommand:
err = dispatchCopy(d, c.SourcesAndDest, opt.buildContext, true, c, "", opt)
err = dispatchCopy(d, c.SourcesAndDest, opt.buildContext, true, c, c.Chown, opt)
if err == nil {
for _, src := range c.Sources() {
if !strings.HasPrefix(src, "http://") && !strings.HasPrefix(src, "https://") {
@ -660,7 +660,8 @@ func dispatchWorkdir(d *dispatchState, c *instructions.WorkdirCommand, commit bo
}
d.image.Config.WorkingDir = wd
if commit {
if opt != nil && useFileOp(opt.buildArgValues, opt.llbCaps) {
withLayer := false
if wd != "/" && opt != nil && useFileOp(opt.buildArgValues, opt.llbCaps) {
mkdirOpt := []llb.MkdirOption{llb.WithParents(true)}
if user := d.image.Config.User; user != "" {
mkdirOpt = append(mkdirOpt, llb.WithUser(user))
@ -670,9 +671,9 @@ func dispatchWorkdir(d *dispatchState, c *instructions.WorkdirCommand, commit bo
platform = *d.platform
}
d.state = d.state.File(llb.Mkdir(wd, 0755, mkdirOpt...), llb.WithCustomName(prefixCommand(d, uppercaseCmd(processCmdEnv(opt.shlex, c.String(), d.state.Env())), d.prefixPlatform, &platform)))
withLayer = true
}
return commitToHistory(&d.image, "WORKDIR "+wd, false, nil)
return commitToHistory(&d.image, "WORKDIR "+wd, withLayer, nil)
}
return nil
}
@ -1285,7 +1286,7 @@ func prefixCommand(ds *dispatchState, str string, prefixPlatform bool, platform
func useFileOp(args map[string]string, caps *apicaps.CapSet) bool {
enabled := fileOpEnabled
if v, ok := args["BUILDKIT_USE_FILEOP"]; ok {
if b, err := strconv.ParseBool(v); err != nil {
if b, err := strconv.ParseBool(v); err == nil {
enabled = b
}
}

View File

@ -81,6 +81,7 @@ var allTests = []integration.Test{
testCopyChownCreateDest,
testEmptyDestDir,
testSymlinkedDockerfile,
testDockerfileAddArchiveWildcard,
}
var opts []integration.TestOpt
@ -1309,6 +1310,79 @@ ADD %s /newname.tar.gz
require.Equal(t, buf2.Bytes(), dt)
}
func testDockerfileAddArchiveWildcard(t *testing.T, sb integration.Sandbox) {
f := getFrontend(t, sb)
buf := bytes.NewBuffer(nil)
tw := tar.NewWriter(buf)
expectedContent := []byte("content0")
err := tw.WriteHeader(&tar.Header{
Name: "foo",
Typeflag: tar.TypeReg,
Size: int64(len(expectedContent)),
Mode: 0644,
})
require.NoError(t, err)
_, err = tw.Write(expectedContent)
require.NoError(t, err)
err = tw.Close()
require.NoError(t, err)
buf2 := bytes.NewBuffer(nil)
tw = tar.NewWriter(buf2)
expectedContent = []byte("content1")
err = tw.WriteHeader(&tar.Header{
Name: "bar",
Typeflag: tar.TypeReg,
Size: int64(len(expectedContent)),
Mode: 0644,
})
require.NoError(t, err)
_, err = tw.Write(expectedContent)
require.NoError(t, err)
err = tw.Close()
require.NoError(t, err)
dockerfile := []byte(`
FROM scratch
ADD *.tar /dest
`)
dir, err := tmpdir(
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("t.tar", buf.Bytes(), 0600),
fstest.CreateFile("b.tar", buf2.Bytes(), 0600),
)
require.NoError(t, err)
defer os.RemoveAll(dir)
destDir, err := ioutil.TempDir("", "buildkit")
require.NoError(t, err)
defer os.RemoveAll(destDir)
c, err := client.New(context.TODO(), sb.Address())
require.NoError(t, err)
defer c.Close()
_, err = f.Solve(context.TODO(), c, client.SolveOpt{
Exporter: client.ExporterLocal,
ExporterOutputDir: destDir,
LocalDirs: map[string]string{
builder.DefaultLocalNameDockerfile: dir,
builder.DefaultLocalNameContext: dir,
},
}, nil)
require.NoError(t, err)
dt, err := ioutil.ReadFile(filepath.Join(destDir, "dest/foo"))
require.NoError(t, err)
require.Equal(t, "content0", string(dt))
dt, err = ioutil.ReadFile(filepath.Join(destDir, "dest/bar"))
require.NoError(t, err)
require.Equal(t, "content1", string(dt))
}
func testSymlinkDestination(t *testing.T, sb integration.Sandbox) {
f := getFrontend(t, sb)
f.RequiresBuildctl(t)
@ -2061,7 +2135,7 @@ COPY sub/dir1 subdest6
require.NoError(t, err)
require.Equal(t, "foo-contents", string(dt))
dt, err = ioutil.ReadFile(filepath.Join(destDir, "subdest/dir1/dir2/foo"))
dt, err = ioutil.ReadFile(filepath.Join(destDir, "subdest/dir2/foo"))
require.NoError(t, err)
require.Equal(t, "foo-contents", string(dt))

View File

@ -6,25 +6,33 @@ import (
"log"
"os"
"path/filepath"
"strings"
"time"
"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/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, user *uidgid) error {
func timestampToTime(ts int64) *time.Time {
if ts == -1 {
return nil
}
tm := time.Unix(ts/1e9, ts%1e9)
return &tm
}
func mkdir(ctx context.Context, d string, action pb.FileActionMkDir, user *copy.ChownOpt) error {
p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path)))
if err != nil {
return err
}
if action.MakeParents {
if err := mkdirAll(p, os.FileMode(action.Mode)&0777, user); err != nil {
if err := copy.MkdirAll(p, os.FileMode(action.Mode)&0777, user, timestampToTime(action.Timestamp)); err != nil {
return err
}
} else {
@ -34,26 +42,18 @@ func mkdir(ctx context.Context, d string, action pb.FileActionMkDir, user *uidgi
}
return err
}
if user != nil {
if err := os.Chown(p, user.uid, user.gid); err != nil {
if err := copy.Chown(p, user); err != nil {
return err
}
}
}
if action.Timestamp != -1 {
st := unix.Timespec{Sec: action.Timestamp / 1e9, Nsec: action.Timestamp % 1e9}
timespec := []unix.Timespec{st, st}
if err := unix.UtimesNanoAt(unix.AT_FDCWD, p, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
return errors.Wrapf(err, "failed to utime %s", p)
if err := copy.Utimes(p, timestampToTime(action.Timestamp)); err != nil {
return err
}
}
return nil
}
func mkfile(ctx context.Context, d string, action pb.FileActionMkFile, user *uidgid) error {
func mkfile(ctx context.Context, d string, action pb.FileActionMkFile, user *copy.ChownOpt) error {
p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path)))
if err != nil {
return err
@ -63,18 +63,12 @@ func mkfile(ctx context.Context, d string, action pb.FileActionMkFile, user *uid
return err
}
if user != nil {
if err := os.Chown(p, user.uid, user.gid); err != nil {
if err := copy.Chown(p, user); err != nil {
return err
}
}
if action.Timestamp != -1 {
st := unix.Timespec{Sec: action.Timestamp / 1e9, Nsec: action.Timestamp % 1e9}
timespec := []unix.Timespec{st, st}
if err := unix.UtimesNanoAt(unix.AT_FDCWD, p, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
return errors.Wrapf(err, "failed to utime %s", p)
}
if err := copy.Utimes(p, timestampToTime(action.Timestamp)); err != nil {
return err
}
return nil
@ -96,50 +90,18 @@ 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 *uidgid) error {
// // src is the source path
// Src string `protobuf:"bytes,1,opt,name=src,proto3" json:"src,omitempty"`
// // dest path
// Dest string `protobuf:"bytes,2,opt,name=dest,proto3" json:"dest,omitempty"`
// // optional owner override
// Owner *ChownOpt `protobuf:"bytes,4,opt,name=owner" json:"owner,omitempty"`
// // optional permission bits override
// Mode int32 `protobuf:"varint,5,opt,name=mode,proto3" json:"mode,omitempty"`
// // followSymlink resolves symlinks in src
// FollowSymlink bool `protobuf:"varint,6,opt,name=followSymlink,proto3" json:"followSymlink,omitempty"`
// // dirCopyContents only copies contents if src is a directory
// DirCopyContents bool `protobuf:"varint,7,opt,name=dirCopyContents,proto3" json:"dirCopyContents,omitempty"`
// // attemptUnpackDockerCompatibility detects if src is an archive to unpack it instead
// AttemptUnpackDockerCompatibility bool `protobuf:"varint,8,opt,name=attemptUnpackDockerCompatibility,proto3" json:"attemptUnpackDockerCompatibility,omitempty"`
// // createDestPath creates dest path directories if needed
// CreateDestPath bool `protobuf:"varint,9,opt,name=createDestPath,proto3" json:"createDestPath,omitempty"`
// // allowWildcard allows filepath.Match wildcards in src path
// AllowWildcard bool `protobuf:"varint,10,opt,name=allowWildcard,proto3" json:"allowWildcard,omitempty"`
// // allowEmptyWildcard doesn't fail the whole copy if wildcard doesn't resolve to files
// AllowEmptyWildcard bool `protobuf:"varint,11,opt,name=allowEmptyWildcard,proto3" json:"allowEmptyWildcard,omitempty"`
// // optional created time override
// Timestamp int64 `protobuf:"varint,12,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy, u *copy.ChownOpt) error {
srcPath := cleanPath(action.Src)
destPath := cleanPath(action.Dest)
srcp, err := fs.RootPath(src, filepath.Join(filepath.Join("/", action.Src)))
if !action.CreateDestPath {
p, err := fs.RootPath(dest, filepath.Join(filepath.Join("/", action.Dest)))
if err != nil {
return err
}
destp, err := fs.RootPath(dest, filepath.Join(filepath.Join("/", action.Dest)))
if err != nil {
return err
if _, err := os.Lstat(filepath.Dir(p)); err != nil {
return errors.Wrapf(err, "failed to stat %s", action.Dest)
}
var opt []copy.Opt
if action.AllowWildcard {
opt = append(opt, copy.AllowWildcards)
}
if u != nil {
opt = append(opt, func(ci *copy.CopyInfo) {
ci.Chown = &copy.ChownOpt{Uid: u.uid, Gid: u.gid}
})
}
xattrErrorHandler := func(dst, src, key string, err error) error {
@ -147,13 +109,69 @@ func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy, u *
return nil
}
opt = append(opt, copy.WithXAttrErrorHandler(xattrErrorHandler))
opt := []copy.Opt{
func(ci *copy.CopyInfo) {
ci.Chown = u
ci.Utime = timestampToTime(action.Timestamp)
if m := int(action.Mode); m != -1 {
ci.Mode = &m
}
ci.CopyDirContents = action.DirCopyContents
ci.FollowLinks = action.FollowSymlink
},
copy.WithXAttrErrorHandler(xattrErrorHandler),
}
if err := copy.Copy(ctx, srcp, destp, opt...); err != nil {
if !action.AllowWildcard {
if action.AttemptUnpackDockerCompatibility {
if ok, err := unpack(ctx, src, srcPath, dest, destPath, u, timestampToTime(action.Timestamp)); err != nil {
return err
} else if ok {
return nil
}
}
return copy.Copy(ctx, src, srcPath, dest, destPath, opt...)
}
m, err := copy.ResolveWildcards(src, srcPath, action.FollowSymlink)
if err != nil {
return err
}
if len(m) == 0 {
if action.AllowEmptyWildcard {
return nil
}
return errors.Errorf("%s not found", srcPath)
}
for _, s := range m {
if action.AttemptUnpackDockerCompatibility {
if ok, err := unpack(ctx, src, s, dest, destPath, u, timestampToTime(action.Timestamp)); err != nil {
return err
} else if ok {
continue
}
}
if err := copy.Copy(ctx, src, s, dest, destPath, opt...); err != nil {
return err
}
}
return nil
}
func cleanPath(s string) string {
s2 := filepath.Join("/", s)
if strings.HasSuffix(s, "/.") {
if s2 != "/" {
s2 += "/"
}
s2 += "."
} else if strings.HasSuffix(s, "/") && s2 != "/" {
s2 += "/"
}
return s2
}
type Backend struct {
@ -244,7 +262,5 @@ func (fb *Backend) Copy(ctx context.Context, m1, m2, user, group fileoptypes.Mou
return err
}
logrus.Debugf("copy %+v %+v %+v", action.Owner, user, group)
return docopy(ctx, src, dest, action, u)
}

View File

@ -1,62 +0,0 @@
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
}

View File

@ -1,7 +0,0 @@
// +build !windows
package file
func fixRootDirectory(p string) string {
return p
}

View File

@ -1,14 +0,0 @@
// +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
}

View File

@ -0,0 +1,61 @@
package file
import (
"archive/tar"
"context"
"os"
"time"
"github.com/containerd/continuity/fs"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/chrootarchive"
copy "github.com/tonistiigi/fsutil/copy"
)
func unpack(ctx context.Context, srcRoot string, src string, destRoot string, dest string, user *copy.ChownOpt, tm *time.Time) (bool, error) {
src, err := fs.RootPath(srcRoot, src)
if err != nil {
return false, err
}
if !isArchivePath(src) {
return false, nil
}
dest, err = fs.RootPath(destRoot, dest)
if err != nil {
return false, err
}
if err := copy.MkdirAll(dest, 0755, user, tm); err != nil {
return false, err
}
file, err := os.Open(src)
if err != nil {
return false, err
}
defer file.Close()
return true, chrootarchive.Untar(file, dest, nil)
}
func isArchivePath(path string) bool {
fi, err := os.Lstat(path)
if err != nil {
return false
}
if fi.Mode()&os.ModeType != 0 {
return false
}
file, err := os.Open(path)
if err != nil {
return false
}
defer file.Close()
rdr, err := archive.DecompressStream(file)
if err != nil {
return false
}
r := tar.NewReader(rdr)
_, err = r.Next()
return err == nil
}

View File

@ -9,17 +9,14 @@ import (
"github.com/moby/buildkit/solver/pb"
"github.com/opencontainers/runc/libcontainer/user"
"github.com/pkg/errors"
copy "github.com/tonistiigi/fsutil/copy"
)
type uidgid struct {
uid, gid int
}
func readUser(chopt *pb.ChownOpt, mu, mg fileoptypes.Mount) (*uidgid, error) {
func readUser(chopt *pb.ChownOpt, mu, mg fileoptypes.Mount) (*copy.ChownOpt, error) {
if chopt == nil {
return nil, nil
}
var us uidgid
var us copy.ChownOpt
if chopt.User != nil {
switch u := chopt.User.User.(type) {
case *pb.UserOpt_ByName:
@ -61,12 +58,12 @@ func readUser(chopt *pb.ChownOpt, mu, mg fileoptypes.Mount) (*uidgid, error) {
}
if len(users) > 0 {
us.uid = users[0].Uid
us.gid = users[0].Gid
us.Uid = users[0].Uid
us.Gid = users[0].Gid
}
case *pb.UserOpt_ByID:
us.uid = int(u.ByID)
us.gid = int(u.ByID)
us.Uid = int(u.ByID)
us.Gid = int(u.ByID)
}
}
@ -111,10 +108,10 @@ func readUser(chopt *pb.ChownOpt, mu, mg fileoptypes.Mount) (*uidgid, error) {
}
if len(groups) > 0 {
us.gid = groups[0].Gid
us.Gid = groups[0].Gid
}
case *pb.UserOpt_ByID:
us.gid = int(u.ByID)
us.Gid = int(u.ByID)
}
}

View File

@ -88,8 +88,8 @@ func (f *fileOp) CacheMap(ctx context.Context, index int) (*solver.CacheMap, boo
markInvalid(action.Input)
processOwner(p.Owner, selectors)
if action.SecondaryInput != -1 && int(action.SecondaryInput) < f.numInputs {
p.Src = path.Base(p.Src)
addSelector(selectors, int(action.SecondaryInput), p.Src, p.AllowWildcard, p.FollowSymlink)
p.Src = path.Base(p.Src)
}
dt, err = json.Marshal(p)
if err != nil {
@ -375,7 +375,7 @@ func (s *FileOpSolver) validate(idx int, inputs []fileoptypes.Ref, actions []*pb
}
func (s *FileOpSolver) getInput(ctx context.Context, idx int, inputs []fileoptypes.Ref, actions []*pb.FileAction) (input, error) {
inp, err := s.g.Do(ctx, fmt.Sprintf("inp-%d", idx), func(ctx context.Context) (interface{}, error) {
inp, err := s.g.Do(ctx, fmt.Sprintf("inp-%d", idx), func(ctx context.Context) (_ interface{}, err error) {
s.mu.Lock()
inp := s.ins[idx]
s.mu.Unlock()
@ -391,14 +391,18 @@ func (s *FileOpSolver) getInput(ctx context.Context, idx int, inputs []fileoptyp
return inp, nil
}
var inpMount, inpMountSecondary fileoptypes.Mount
var toRelease []fileoptypes.Mount
var inpMountPrepared bool
defer func() {
for _, m := range toRelease {
m.Release(context.TODO())
}
if err != nil && inpMount != nil && inpMountPrepared {
inpMount.Release(context.TODO())
}
}()
var inpMount, inpMountSecondary fileoptypes.Mount
action := actions[idx-len(inputs)]
loadInput := func(ctx context.Context) func() error {
@ -413,6 +417,7 @@ func (s *FileOpSolver) getInput(ctx context.Context, idx int, inputs []fileoptyp
return err
}
inpMount = m
inpMountPrepared = true
return nil
}
inpMount = inp.mount
@ -511,6 +516,7 @@ func (s *FileOpSolver) getInput(ctx context.Context, idx int, inputs []fileoptyp
return nil, err
}
inpMount = m
inpMountPrepared = true
}
switch a := action.Action.(type) {