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  }