13497: Merge branch 'master' into 13497-controller
[arvados.git] / services / crunch-run / git_mount.go
index a792741ae43b64591a125660a8e774e15af421a8..c312a532e44f43d63fa65b1d6ff6e7af9028a924 100644 (file)
@@ -1,12 +1,18 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
 package main
 
 import (
        "fmt"
        "net/url"
+       "os"
+       "path/filepath"
+       "regexp"
 
        "git.curoverse.com/arvados.git/sdk/go/arvados"
-       "git.curoverse.com/arvados.git/sdk/go/arvadostest"
-       "gopkg.in/src-d/go-billy.v3/osfs"
+       "gopkg.in/src-d/go-billy.v4/osfs"
        git "gopkg.in/src-d/go-git.v4"
        git_config "gopkg.in/src-d/go-git.v4/config"
        git_plumbing "gopkg.in/src-d/go-git.v4/plumbing"
@@ -16,16 +22,33 @@ import (
 
 type gitMount arvados.Mount
 
+var (
+       sha1re     = regexp.MustCompile(`^[0-9a-f]{40}$`)
+       repoUUIDre = regexp.MustCompile(`^[0-9a-z]{5}-s0uqq-[0-9a-z]{15}$`)
+)
+
 func (gm gitMount) validate() error {
-       if gm.Path != "/" {
-               return fmt.Errorf("cannot mount git_tree path %q -- only \"/\" is supported", gm.Path)
+       if gm.Path != "" && gm.Path != "/" {
+               return fmt.Errorf("cannot mount git_tree with path %q -- only \"/\" is supported", gm.Path)
+       }
+       if !sha1re.MatchString(gm.Commit) {
+               return fmt.Errorf("cannot mount git_tree with commit %q -- must be a 40-char SHA1", gm.Commit)
+       }
+       if gm.RepositoryName != "" || gm.GitURL != "" {
+               return fmt.Errorf("cannot mount git_tree -- repository_name and git_url must be empty")
+       }
+       if !repoUUIDre.MatchString(gm.UUID) {
+               return fmt.Errorf("cannot mount git_tree with uuid %q -- must be a repository UUID", gm.UUID)
+       }
+       if gm.Writable {
+               return fmt.Errorf("writable git_tree mount is not supported")
        }
        return nil
 }
 
 // ExtractTree extracts the specified tree into dir, which is an
 // existing empty local directory.
-func (gm gitMount) extractTree(ac IArvadosClient, dir string) error {
+func (gm gitMount) extractTree(ac IArvadosClient, dir string, token string) error {
        err := gm.validate()
        if err != nil {
                return err
@@ -33,7 +56,10 @@ func (gm gitMount) extractTree(ac IArvadosClient, dir string) error {
        baseURL, err := ac.Discovery("gitUrl")
        if err != nil {
                return fmt.Errorf("discover gitUrl from API: %s", err)
+       } else if _, ok := baseURL.(string); !ok {
+               return fmt.Errorf("discover gitUrl from API: expected string, found %T", baseURL)
        }
+
        u, err := url.Parse(baseURL.(string))
        if err != nil {
                return fmt.Errorf("parse gitUrl %q: %s", baseURL, err)
@@ -56,7 +82,10 @@ func (gm gitMount) extractTree(ac IArvadosClient, dir string) error {
        }
        err = repo.Fetch(&git.FetchOptions{
                RemoteName: "origin",
-               Auth:       git_http.NewBasicAuth("none", arvadostest.ActiveToken),
+               Auth: &git_http.BasicAuth{
+                       Username: "none",
+                       Password: token,
+               },
        })
        if err != nil {
                return fmt.Errorf("git fetch %q: %s", u.String(), err)
@@ -71,5 +100,18 @@ func (gm gitMount) extractTree(ac IArvadosClient, dir string) error {
        if err != nil {
                return fmt.Errorf("checkout failed: %s", err)
        }
+       err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+               if err != nil {
+                       return err
+               }
+               // copy user rx bits to group and other, in case
+               // prevailing umask is more restrictive than 022
+               mode := info.Mode()
+               mode = mode | ((mode >> 3) & 050) | ((mode >> 6) & 5)
+               return os.Chmod(path, mode)
+       })
+       if err != nil {
+               return fmt.Errorf("chmod -R %q: %s", dir, err)
+       }
        return nil
 }