12764: Writable file/directory support wip
authorPeter Amstutz <pamstutz@veritasgenetics.com>
Thu, 7 Dec 2017 14:51:25 +0000 (09:51 -0500)
committerPeter Amstutz <pamstutz@veritasgenetics.com>
Mon, 29 Jan 2018 19:49:56 +0000 (14:49 -0500)
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <pamstutz@veritasgenetics.com>

services/crunch-run/crunchrun.go
services/crunch-run/crunchrun_test.go

index 7eefb1aaaded35be0b4ab99a03f4d82e30076c06..60f2a6fa2cadf86321db4155649e951af9ff24fe 100644 (file)
@@ -330,6 +330,37 @@ func (runner *ContainerRunner) SetupArvMountPoint(prefix string) (err error) {
        return
 }
 
+func copyfile(src string, dst string) (err error) {
+       srcfile, err := os.Open(src)
+       if err != nil {
+               return
+       }
+
+       os.MkdirAll(path.Dir(dst), 0770)
+
+       dstfile, err := os.Create(dst)
+       if err != nil {
+               return
+       }
+       _, err = io.Copy(srcfile, dstfile)
+       if err != nil {
+               return
+       }
+
+       err = srcfile.Close()
+       err2 := dstfile.Close()
+
+       if err != nil {
+               return
+       }
+
+       if err2 != nil {
+               return err2
+       }
+
+       return nil
+}
+
 func (runner *ContainerRunner) SetupMounts() (err error) {
        err = runner.SetupArvMountPoint("keep")
        if err != nil {
@@ -357,6 +388,11 @@ func (runner *ContainerRunner) SetupMounts() (err error) {
        runner.Binds = nil
        runner.Volumes = make(map[string]struct{})
        needCertMount := true
+       type copyFile struct {
+               src  string
+               bind string
+       }
+       var copyFiles []copyFile
 
        var binds []string
        for bind := range runner.Container.Mounts {
@@ -412,7 +448,7 @@ func (runner *ContainerRunner) SetupMounts() (err error) {
                                pdhOnly = false
                                src = fmt.Sprintf("%s/by_id/%s", runner.ArvMountPoint, mnt.UUID)
                        } else if mnt.PortableDataHash != "" {
-                               if mnt.Writable {
+                               if mnt.Writable && !strings.HasPrefix(bind, runner.Container.OutputPath+"/") {
                                        return fmt.Errorf("Can never write to a collection specified by portable data hash")
                                }
                                idx := strings.Index(mnt.PortableDataHash, "/")
@@ -439,10 +475,12 @@ func (runner *ContainerRunner) SetupMounts() (err error) {
                        if mnt.Writable {
                                if bind == runner.Container.OutputPath {
                                        runner.HostOutputDir = src
+                                       runner.Binds = append(runner.Binds, fmt.Sprintf("%s:%s", src, bind))
                                } else if strings.HasPrefix(bind, runner.Container.OutputPath+"/") {
-                                       return fmt.Errorf("Writable mount points are not permitted underneath the output_path: %v", bind)
+                                       copyFiles = append(copyFiles, copyFile{src, runner.HostOutputDir + bind[len(runner.Container.OutputPath):]})
+                               } else {
+                                       runner.Binds = append(runner.Binds, fmt.Sprintf("%s:%s", src, bind))
                                }
-                               runner.Binds = append(runner.Binds, fmt.Sprintf("%s:%s", src, bind))
                        } else {
                                runner.Binds = append(runner.Binds, fmt.Sprintf("%s:%s:ro", src, bind))
                        }
@@ -537,6 +575,22 @@ func (runner *ContainerRunner) SetupMounts() (err error) {
                }
        }
 
+       for _, cp := range copyFiles {
+               dir, err := os.Stat(cp.src)
+               if err == nil {
+                       if dir.IsDir() {
+                               err = filepath.Walk(cp.src, func(walkpath string, walkinfo os.FileInfo, walkerr error) error {
+                                       return copyfile(walkpath, path.Join(cp.bind, walkpath))
+                               })
+                       } else {
+                               err = copyfile(cp.src, cp.bind)
+                       }
+               }
+               if err != nil {
+                       return fmt.Errorf("While staging writable files: %v", err)
+               }
+       }
+
        return nil
 }
 
index 22989bb2ece510e6f239151b267968fde25f1203..5965a7d02c90f9daca0b40760a4db396ffa57298 100644 (file)
@@ -1233,20 +1233,25 @@ func (s *TestSuite) TestSetupMounts(c *C) {
                checkEmpty()
        }
 
-       // Writable mount points are not allowed underneath output_dir mount point
+       // Writable mount points copied to output_dir mount point
        {
                i = 0
                cr.ArvMountPoint = ""
                cr.Container.Mounts = make(map[string]arvados.Mount)
                cr.Container.Mounts = map[string]arvados.Mount{
-                       "/tmp":     {Kind: "tmp"},
-                       "/tmp/foo": {Kind: "collection", Writable: true},
+                       "/tmp": {Kind: "tmp"},
+                       "/tmp/foo": {Kind: "collection",
+                               PortableDataHash: "59389a8f9ee9d399be35462a0f92541c+53",
+                               Writable:         true},
                }
                cr.OutputPath = "/tmp"
 
+               os.MkdirAll(realTemp+"/keep1/by_id/59389a8f9ee9d399be35462a0f92541c+53", os.ModePerm)
+
                err := cr.SetupMounts()
-               c.Check(err, NotNil)
-               c.Check(err, ErrorMatches, `Writable mount points are not permitted underneath the output_path.*`)
+               c.Check(err, IsNil)
+               _, err = os.Stat(cr.HostOutputDir + "/foo")
+               c.Check(err, IsNil)
                os.RemoveAll(cr.ArvMountPoint)
                cr.CleanupDirs()
                checkEmpty()