github.com/taylorchu/nomad@v0.5.3-rc1.0.20170407200202-db11e7dd7b55/client/getter/getter.go (about) 1 package getter 2 3 import ( 4 "fmt" 5 "net/url" 6 "path/filepath" 7 "strings" 8 "sync" 9 10 gg "github.com/hashicorp/go-getter" 11 "github.com/hashicorp/nomad/client/driver/env" 12 "github.com/hashicorp/nomad/nomad/structs" 13 ) 14 15 var ( 16 // getters is the map of getters suitable for Nomad. It is initialized once 17 // and the lock is used to guard access to it. 18 getters map[string]gg.Getter 19 lock sync.Mutex 20 21 // supported is the set of download schemes supported by Nomad 22 supported = []string{"http", "https", "s3", "hg", "git"} 23 ) 24 25 const ( 26 // gitSSHPrefix is the prefix for dowwnloading via git using ssh 27 gitSSHPrefix = "git@github.com:" 28 ) 29 30 // getClient returns a client that is suitable for Nomad downloading artifacts. 31 func getClient(src, dst string) *gg.Client { 32 lock.Lock() 33 defer lock.Unlock() 34 35 // Return the pre-initialized client 36 if getters == nil { 37 getters = make(map[string]gg.Getter, len(supported)) 38 for _, getter := range supported { 39 if impl, ok := gg.Getters[getter]; ok { 40 getters[getter] = impl 41 } 42 } 43 } 44 45 return &gg.Client{ 46 Src: src, 47 Dst: dst, 48 Mode: gg.ClientModeAny, 49 Getters: getters, 50 } 51 } 52 53 // getGetterUrl returns the go-getter URL to download the artifact. 54 func getGetterUrl(taskEnv *env.TaskEnvironment, artifact *structs.TaskArtifact) (string, error) { 55 taskEnv.Build() 56 source := taskEnv.ReplaceEnv(artifact.GetterSource) 57 58 // Handle an invalid URL when given a go-getter url such as 59 // git@github.com:hashicorp/nomad.git 60 gitSSH := false 61 if strings.HasPrefix(source, gitSSHPrefix) { 62 gitSSH = true 63 source = source[len(gitSSHPrefix):] 64 } 65 66 u, err := url.Parse(source) 67 if err != nil { 68 return "", fmt.Errorf("failed to parse source URL %q: %v", artifact.GetterSource, err) 69 } 70 71 // Build the url 72 q := u.Query() 73 for k, v := range artifact.GetterOptions { 74 q.Add(k, taskEnv.ReplaceEnv(v)) 75 } 76 u.RawQuery = q.Encode() 77 78 // Add the prefix back 79 url := u.String() 80 if gitSSH { 81 url = fmt.Sprintf("%s%s", gitSSHPrefix, url) 82 } 83 84 return url, nil 85 } 86 87 // GetArtifact downloads an artifact into the specified task directory. 88 func GetArtifact(taskEnv *env.TaskEnvironment, artifact *structs.TaskArtifact, taskDir string) error { 89 url, err := getGetterUrl(taskEnv, artifact) 90 if err != nil { 91 return newGetError(artifact.GetterSource, err, false) 92 } 93 94 // Download the artifact 95 dest := filepath.Join(taskDir, artifact.RelativeDest) 96 if err := getClient(url, dest).Get(); err != nil { 97 return newGetError(url, err, true) 98 } 99 100 return nil 101 } 102 103 // GetError wraps the underlying artifact fetching error with the URL. It 104 // implements the RecoverableError interface. 105 type GetError struct { 106 URL string 107 Err error 108 recoverable bool 109 } 110 111 func newGetError(url string, err error, recoverable bool) *GetError { 112 return &GetError{ 113 URL: url, 114 Err: err, 115 recoverable: recoverable, 116 } 117 } 118 119 func (g *GetError) Error() string { 120 return g.Err.Error() 121 } 122 123 func (g *GetError) IsRecoverable() bool { 124 return g.recoverable 125 }