github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/configs/configload/getter.go (about) 1 package configload 2 3 import ( 4 "fmt" 5 "log" 6 "os" 7 "path/filepath" 8 9 cleanhttp "github.com/hashicorp/go-cleanhttp" 10 getter "github.com/hashicorp/go-getter" 11 ) 12 13 // We configure our own go-getter detector and getter sets here, because 14 // the set of sources we support is part of Terraform's documentation and 15 // so we don't want any new sources introduced in go-getter to sneak in here 16 // and work even though they aren't documented. This also insulates us from 17 // any meddling that might be done by other go-getter callers linked into our 18 // executable. 19 20 var goGetterDetectors = []getter.Detector{ 21 new(getter.GitHubDetector), 22 new(getter.GitDetector), 23 new(getter.BitBucketDetector), 24 new(getter.GCSDetector), 25 new(getter.S3Detector), 26 new(getter.FileDetector), 27 } 28 29 var goGetterNoDetectors = []getter.Detector{} 30 31 var goGetterDecompressors = map[string]getter.Decompressor{ 32 "bz2": new(getter.Bzip2Decompressor), 33 "gz": new(getter.GzipDecompressor), 34 "xz": new(getter.XzDecompressor), 35 "zip": new(getter.ZipDecompressor), 36 37 "tar.bz2": new(getter.TarBzip2Decompressor), 38 "tar.tbz2": new(getter.TarBzip2Decompressor), 39 40 "tar.gz": new(getter.TarGzipDecompressor), 41 "tgz": new(getter.TarGzipDecompressor), 42 43 "tar.xz": new(getter.TarXzDecompressor), 44 "txz": new(getter.TarXzDecompressor), 45 } 46 47 var goGetterGetters = map[string]getter.Getter{ 48 "file": new(getter.FileGetter), 49 "gcs": new(getter.GCSGetter), 50 "git": new(getter.GitGetter), 51 "hg": new(getter.HgGetter), 52 "s3": new(getter.S3Getter), 53 "http": getterHTTPGetter, 54 "https": getterHTTPGetter, 55 } 56 57 var getterHTTPClient = cleanhttp.DefaultClient() 58 59 var getterHTTPGetter = &getter.HttpGetter{ 60 Client: getterHTTPClient, 61 Netrc: true, 62 } 63 64 // A reusingGetter is a helper for the module installer that remembers 65 // the final resolved addresses of all of the sources it has already been 66 // asked to install, and will copy from a prior installation directory if 67 // it has the same resolved source address. 68 // 69 // The keys in a reusingGetter are resolved and trimmed source addresses 70 // (with a scheme always present, and without any "subdir" component), 71 // and the values are the paths where each source was previously installed. 72 type reusingGetter map[string]string 73 74 // getWithGoGetter retrieves the package referenced in the given address 75 // into the installation path and then returns the full path to any subdir 76 // indicated in the address. 77 // 78 // The errors returned by this function are those surfaced by the underlying 79 // go-getter library, which have very inconsistent quality as 80 // end-user-actionable error messages. At this time we do not have any 81 // reasonable way to improve these error messages at this layer because 82 // the underlying errors are not separatelyr recognizable. 83 func (g reusingGetter) getWithGoGetter(instPath, addr string) (string, error) { 84 packageAddr, subDir := splitAddrSubdir(addr) 85 86 log.Printf("[DEBUG] will download %q to %s", packageAddr, instPath) 87 88 realAddr, err := getter.Detect(packageAddr, instPath, goGetterDetectors) 89 if err != nil { 90 return "", err 91 } 92 93 var realSubDir string 94 realAddr, realSubDir = splitAddrSubdir(realAddr) 95 if realSubDir != "" { 96 subDir = filepath.Join(realSubDir, subDir) 97 } 98 99 if realAddr != packageAddr { 100 log.Printf("[TRACE] go-getter detectors rewrote %q to %q", packageAddr, realAddr) 101 } 102 103 if prevDir, exists := g[realAddr]; exists { 104 log.Printf("[TRACE] copying previous install %s to %s", prevDir, instPath) 105 err := os.Mkdir(instPath, os.ModePerm) 106 if err != nil { 107 return "", fmt.Errorf("failed to create directory %s: %s", instPath, err) 108 } 109 err = copyDir(instPath, prevDir) 110 if err != nil { 111 return "", fmt.Errorf("failed to copy from %s to %s: %s", prevDir, instPath, err) 112 } 113 } else { 114 log.Printf("[TRACE] fetching %q to %q", realAddr, instPath) 115 client := getter.Client{ 116 Src: realAddr, 117 Dst: instPath, 118 Pwd: instPath, 119 120 Mode: getter.ClientModeDir, 121 122 Detectors: goGetterNoDetectors, // we already did detection above 123 Decompressors: goGetterDecompressors, 124 Getters: goGetterGetters, 125 } 126 err = client.Get() 127 if err != nil { 128 return "", err 129 } 130 // Remember where we installed this so we might reuse this directory 131 // on subsequent calls to avoid re-downloading. 132 g[realAddr] = instPath 133 } 134 135 // Our subDir string can contain wildcards until this point, so that 136 // e.g. a subDir of * can expand to one top-level directory in a .tar.gz 137 // archive. Now that we've expanded the archive successfully we must 138 // resolve that into a concrete path. 139 var finalDir string 140 if subDir != "" { 141 finalDir, err = getter.SubdirGlob(instPath, subDir) 142 log.Printf("[TRACE] expanded %q to %q", subDir, finalDir) 143 if err != nil { 144 return "", err 145 } 146 } else { 147 finalDir = instPath 148 } 149 150 // If we got this far then we have apparently succeeded in downloading 151 // the requested object! 152 return filepath.Clean(finalDir), nil 153 }