8311: Pass in container token to extractTree
[arvados.git] / services / crunch-run / git_mount.go
1 package main
2
3 import (
4         "fmt"
5         "net/url"
6         "regexp"
7
8         "git.curoverse.com/arvados.git/sdk/go/arvados"
9         "gopkg.in/src-d/go-billy.v3/osfs"
10         git "gopkg.in/src-d/go-git.v4"
11         git_config "gopkg.in/src-d/go-git.v4/config"
12         git_plumbing "gopkg.in/src-d/go-git.v4/plumbing"
13         git_http "gopkg.in/src-d/go-git.v4/plumbing/transport/http"
14         "gopkg.in/src-d/go-git.v4/storage/memory"
15 )
16
17 type gitMount arvados.Mount
18
19 var (
20         sha1re     = regexp.MustCompile(`^[0-9a-f]{40}$`)
21         repoUUIDre = regexp.MustCompile(`^[0-9a-z]{5}-s0uqq-[0-9a-z]{15}$`)
22 )
23
24 func (gm gitMount) validate() error {
25         if gm.Path != "" && gm.Path != "/" {
26                 return fmt.Errorf("cannot mount git_tree with path %q -- only \"/\" is supported", gm.Path)
27         }
28         if !sha1re.MatchString(gm.Commit) {
29                 return fmt.Errorf("cannot mount git_tree with commit %q -- must be a 40-char SHA1", gm.Commit)
30         }
31         if gm.RepositoryName != "" || gm.GitURL != "" {
32                 return fmt.Errorf("cannot mount git_tree -- repository_name and git_url must be empty")
33         }
34         if !repoUUIDre.MatchString(gm.UUID) {
35                 return fmt.Errorf("cannot mount git_tree with uuid %q -- must be a repository UUID", gm.UUID)
36         }
37         return nil
38 }
39
40 // ExtractTree extracts the specified tree into dir, which is an
41 // existing empty local directory.
42 func (gm gitMount) extractTree(ac IArvadosClient, dir string, token string) error {
43         err := gm.validate()
44         if err != nil {
45                 return err
46         }
47         baseURL, err := ac.Discovery("gitUrl")
48         if err != nil {
49                 return fmt.Errorf("discover gitUrl from API: %s", err)
50         } else if _, ok := baseURL.(string); !ok {
51                 return fmt.Errorf("discover gitUrl from API: expected string, found %T", baseURL)
52         }
53
54         u, err := url.Parse(baseURL.(string))
55         if err != nil {
56                 return fmt.Errorf("parse gitUrl %q: %s", baseURL, err)
57         }
58         u, err = u.Parse("/" + gm.UUID + ".git")
59         if err != nil {
60                 return fmt.Errorf("build git url from %q, %q: %s", baseURL, gm.UUID, err)
61         }
62         store := memory.NewStorage()
63         repo, err := git.Init(store, osfs.New(dir))
64         if err != nil {
65                 return fmt.Errorf("init repo: %s", err)
66         }
67         _, err = repo.CreateRemote(&git_config.RemoteConfig{
68                 Name: "origin",
69                 URLs: []string{u.String()},
70         })
71         if err != nil {
72                 return fmt.Errorf("create remote %q: %s", u.String(), err)
73         }
74         err = repo.Fetch(&git.FetchOptions{
75                 RemoteName: "origin",
76                 Auth:       git_http.NewBasicAuth("none", token),
77         })
78         if err != nil {
79                 return fmt.Errorf("git fetch %q: %s", u.String(), err)
80         }
81         wt, err := repo.Worktree()
82         if err != nil {
83                 return fmt.Errorf("worktree failed: %s", err)
84         }
85         err = wt.Checkout(&git.CheckoutOptions{
86                 Hash: git_plumbing.NewHash(gm.Commit),
87         })
88         if err != nil {
89                 return fmt.Errorf("checkout failed: %s", err)
90         }
91         return nil
92 }