diff --git a/client/client_test.go b/client/client_test.go index a186ee0d..82e00dc8 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -64,6 +64,7 @@ func TestIntegration(t *testing.T) { testRelativeWorkDir, testFileOpMkdirMkfile, testFileOpCopyRm, + testFileOpRmWildcard, testCallDiskUsage, testBuildMultiMount, testBuildHTTPSource, @@ -1046,6 +1047,63 @@ func testFileOpCopyRm(t *testing.T, sb integration.Sandbox) { } +func testFileOpRmWildcard(t *testing.T, sb integration.Sandbox) { + requiresLinux(t) + c, err := New(context.TODO(), sb.Address()) + require.NoError(t, err) + defer c.Close() + + dir, err := tmpdir( + fstest.CreateDir("foo", 0700), + fstest.CreateDir("bar", 0700), + fstest.CreateFile("foo/target", []byte("foo0"), 0600), + fstest.CreateFile("bar/target", []byte("bar0"), 0600), + fstest.CreateFile("bar/remaining", []byte("bar1"), 0600), + ) + require.NoError(t, err) + defer os.RemoveAll(dir) + + st := llb.Scratch().File( + llb.Copy(llb.Local("mylocal"), "foo", "foo"). + Copy(llb.Local("mylocal"), "bar", "bar"), + ).File( + llb.Rm("*/target", llb.WithAllowWildcard(true)), + ) + def, err := st.Marshal() + require.NoError(t, err) + + destDir, err := ioutil.TempDir("", "buildkit") + require.NoError(t, err) + defer os.RemoveAll(destDir) + + _, err = c.Solve(context.TODO(), def, SolveOpt{ + Exports: []ExportEntry{ + { + Type: ExporterLocal, + OutputDir: destDir, + }, + }, + LocalDirs: map[string]string{ + "mylocal": dir, + }, + }, nil) + require.NoError(t, err) + + dt, err := ioutil.ReadFile(filepath.Join(destDir, "bar/remaining")) + require.NoError(t, err) + require.Equal(t, []byte("bar1"), dt) + + fi, err := os.Stat(filepath.Join(destDir, "foo")) + require.NoError(t, err) + require.Equal(t, true, fi.IsDir()) + + _, err = os.Stat(filepath.Join(destDir, "foo/target")) + require.Equal(t, true, os.IsNotExist(err)) + + _, err = os.Stat(filepath.Join(destDir, "bar/target")) + require.Equal(t, true, os.IsNotExist(err)) +} + func testCallDiskUsage(t *testing.T, sb integration.Sandbox) { c, err := New(context.TODO(), sb.Address()) require.NoError(t, err) diff --git a/solver/llbsolver/file/backend.go b/solver/llbsolver/file/backend.go index 07044691..b0f738a8 100644 --- a/solver/llbsolver/file/backend.go +++ b/solver/llbsolver/file/backend.go @@ -100,13 +100,33 @@ func mkfile(ctx context.Context, d string, action pb.FileActionMkFile, user *cop } func rm(ctx context.Context, d string, action pb.FileActionRm) error { - p, err := fs.RootPath(d, filepath.Join(filepath.Join("/", action.Path))) + if action.AllowWildcard { + src := cleanPath(action.Path) + m, err := copy.ResolveWildcards(d, src, false) + if err != nil { + return err + } + + for _, s := range m { + if err := rmPath(d, s, action.AllowNotFound); err != nil { + return err + } + } + + return nil + } + + return rmPath(d, action.Path, action.AllowNotFound) +} + +func rmPath(root, src string, allowNotFound bool) error { + p, err := fs.RootPath(root, filepath.Join(filepath.Join("/", src))) if err != nil { return err } if err := os.RemoveAll(p); err != nil { - if os.IsNotExist(errors.Cause(err)) && action.AllowNotFound { + if os.IsNotExist(errors.Cause(err)) && allowNotFound { return nil } return err diff --git a/solver/pb/caps.go b/solver/pb/caps.go index 7649ce7d..279a43dc 100644 --- a/solver/pb/caps.go +++ b/solver/pb/caps.go @@ -45,7 +45,8 @@ const ( CapExecMountSSH apicaps.CapID = "exec.mount.ssh" CapExecCgroupsMounted apicaps.CapID = "exec.cgroup" - CapFileBase apicaps.CapID = "file.base" + CapFileBase apicaps.CapID = "file.base" + CapFileRmWildcard apicaps.CapID = "file.rm.wildcard" CapConstraints apicaps.CapID = "constraints" CapPlatform apicaps.CapID = "platform" @@ -252,6 +253,12 @@ func init() { }, }) + Caps.Init(apicaps.Cap{ + ID: CapFileRmWildcard, + Enabled: true, + Status: apicaps.CapStatusExperimental, + }) + Caps.Init(apicaps.Cap{ ID: CapConstraints, Enabled: true,