github.com/hashicorp/go-getter/v2@v2.2.2/get.go (about) 1 // getter is a package for downloading files or directories from a variety of 2 // protocols. 3 // 4 // getter is unique in its ability to download both directories and files. 5 // It also detects certain source strings to be protocol-specific URLs. For 6 // example, "github.com/hashicorp/go-getter/v2" would turn into a Git URL and 7 // use the Git protocol. 8 // 9 // Protocols and detectors are extensible. 10 // 11 // To get started, see Client. 12 package getter 13 14 import ( 15 "bytes" 16 "context" 17 "fmt" 18 "net/url" 19 "os/exec" 20 "regexp" 21 "syscall" 22 "time" 23 24 cleanhttp "github.com/hashicorp/go-cleanhttp" 25 ) 26 27 // Getter defines the interface that schemes must implement to download 28 // things. 29 type Getter interface { 30 // Get downloads the given URL into the given directory. This always 31 // assumes that we're updating and gets the latest version that it can. 32 // 33 // The directory may already exist (if we're updating). If it is in a 34 // format that isn't understood, an error should be returned. Get shouldn't 35 // simply nuke the directory. 36 Get(context.Context, *Request) error 37 38 // GetFile downloads the give URL into the given path. The URL must 39 // reference a single file. If possible, the Getter should check if 40 // the remote end contains the same file and no-op this operation. 41 GetFile(context.Context, *Request) error 42 43 // Mode returns the mode based on the given URL. This is used to 44 // allow clients to let the getters decide which mode to use. 45 Mode(context.Context, *url.URL) (Mode, error) 46 47 // Detect detects whether the Request.Src matches a known pattern to 48 // turn it into a proper URL, and also transforms and update Request.Src 49 // when necessary. 50 // The Getter must validate if the Request.Src is a valid URL 51 // with a valid scheme for the Getter, and also check if the 52 // current Getter is the forced one and return true if that's the case. 53 Detect(*Request) (bool, error) 54 } 55 56 // Getters is the mapping of scheme to the Getter implementation that will 57 // be used to get a dependency. 58 var Getters []Getter 59 60 // forcedRegexp is the regular expression that finds Forced getters. This 61 // syntax is schema::url, example: git::https://foo.com 62 var forcedRegexp = regexp.MustCompile(`^([A-Za-z0-9]+)::(.+)$`) 63 64 // httpClient is the default client to be used by HttpGetters. 65 var httpClient = cleanhttp.DefaultClient() 66 67 var DefaultClient = &Client{ 68 Getters: Getters, 69 Decompressors: Decompressors, 70 } 71 72 func init() { 73 httpGetter := &HttpGetter{ 74 Netrc: true, 75 XTerraformGetDisabled: true, 76 HeadFirstTimeout: 10 * time.Second, 77 ReadTimeout: 30 * time.Second, 78 } 79 80 // The order of the Getters in the list may affect the result 81 // depending if the Request.Src is detected as valid by multiple getters 82 Getters = []Getter{ 83 &GitGetter{ 84 Detectors: []Detector{ 85 new(GitHubDetector), 86 new(GitDetector), 87 new(BitBucketDetector), 88 new(GitLabDetector), 89 }, 90 }, 91 new(HgGetter), 92 new(SmbClientGetter), 93 new(SmbMountGetter), 94 httpGetter, 95 new(FileGetter), 96 } 97 } 98 99 // Get downloads the directory specified by src into the folder specified by 100 // dst. If dst already exists, Get will attempt to update it. 101 // 102 // src is a URL, whereas dst is always just a file path to a folder. This 103 // folder doesn't need to exist. It will be created if it doesn't exist. 104 func Get(ctx context.Context, dst, src string) (*GetResult, error) { 105 req := &Request{ 106 Src: src, 107 Dst: dst, 108 GetMode: ModeDir, 109 } 110 return DefaultClient.Get(ctx, req) 111 } 112 113 // GetAny downloads a URL into the given destination. Unlike Get or 114 // GetFile, both directories and files are supported. 115 // 116 // dst must be a directory. If src is a file, it will be downloaded 117 // into dst with the basename of the URL. If src is a directory or 118 // archive, it will be unpacked directly into dst. 119 func GetAny(ctx context.Context, dst, src string) (*GetResult, error) { 120 req := &Request{ 121 Src: src, 122 Dst: dst, 123 GetMode: ModeAny, 124 } 125 return DefaultClient.Get(ctx, req) 126 } 127 128 // GetFile downloads the file specified by src into the path specified by 129 // dst. 130 func GetFile(ctx context.Context, dst, src string) (*GetResult, error) { 131 req := &Request{ 132 Src: src, 133 Dst: dst, 134 GetMode: ModeFile, 135 } 136 return DefaultClient.Get(ctx, req) 137 } 138 139 // getRunCommand is a helper that will run a command and capture the output 140 // in the case an error happens. 141 func getRunCommand(cmd *exec.Cmd) error { 142 var buf bytes.Buffer 143 cmd.Stdout = &buf 144 cmd.Stderr = &buf 145 err := cmd.Run() 146 if err == nil { 147 return nil 148 } 149 if exiterr, ok := err.(*exec.ExitError); ok { 150 // The program has exited with an exit code != 0 151 if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { 152 return fmt.Errorf( 153 "%s exited with %d: %s", 154 cmd.Path, 155 status.ExitStatus(), 156 buf.String()) 157 } 158 } 159 160 return fmt.Errorf("error running %s: %s", cmd.Path, buf.String()) 161 } 162 163 // getForcedGetter takes a source and returns the tuple of the forced 164 // getter and the raw URL (without the force syntax). 165 // For example "git::https://...". returns "git" "https://". 166 func getForcedGetter(src string) (string, string) { 167 var forced string 168 if ms := forcedRegexp.FindStringSubmatch(src); ms != nil { 169 forced = ms[1] 170 src = ms[2] 171 } 172 173 return forced, src 174 }