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  }