90 lines
4.0 KiB
Go
90 lines
4.0 KiB
Go
package main
|
|
|
|
import (
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/apparentlymart/go-userdirs/userdirs"
|
|
|
|
"github.com/hashicorp/terraform-svchost/disco"
|
|
"github.com/hashicorp/terraform/command/cliconfig"
|
|
"github.com/hashicorp/terraform/internal/getproviders"
|
|
)
|
|
|
|
// providerSource constructs a provider source based on a combination of the
|
|
// CLI configuration and some default search locations. This will be the
|
|
// provider source used for provider installation in the "terraform init"
|
|
// command, unless overridden by the special -plugin-dir option.
|
|
func providerSource(services *disco.Disco) getproviders.Source {
|
|
// We're not yet using the CLI config here because we've not implemented
|
|
// yet the new configuration constructs to customize provider search
|
|
// locations. That'll come later.
|
|
// For now, we have a fixed set of search directories:
|
|
// - The "terraform.d/plugins" directory in the current working directory,
|
|
// which we've historically documented as a place to put plugins as a
|
|
// way to include them in bundles uploaded to Terraform Cloud, where
|
|
// there has historically otherwise been no way to use custom providers.
|
|
// - The "plugins" subdirectory of the CLI config search directory.
|
|
// (thats ~/.terraform.d/plugins on Unix systems, equivalents elsewhere)
|
|
// - The "plugins" subdirectory of any platform-specific search paths,
|
|
// following e.g. the XDG base directory specification on Unix systems,
|
|
// Apple's guidelines on OS X, and "known folders" on Windows.
|
|
//
|
|
// Those directories are checked in addition to the direct upstream
|
|
// registry specified in the provider's address.
|
|
var searchRules []getproviders.MultiSourceSelector
|
|
|
|
addLocalDir := func(dir string) {
|
|
// We'll make sure the directory actually exists before we add it,
|
|
// because otherwise installation would always fail trying to look
|
|
// in non-existent directories. (This is done here rather than in
|
|
// the source itself because explicitly-selected directories via the
|
|
// CLI config, once we have them, _should_ produce an error if they
|
|
// don't exist to help users get their configurations right.)
|
|
if info, err := os.Stat(dir); err == nil && info.IsDir() {
|
|
log.Printf("[DEBUG] will search for provider plugins in %s", dir)
|
|
searchRules = append(searchRules, getproviders.MultiSourceSelector{
|
|
Source: getproviders.NewFilesystemMirrorSource(dir),
|
|
})
|
|
} else {
|
|
log.Printf("[DEBUG] ignoring non-existing provider search directory %s", dir)
|
|
}
|
|
}
|
|
|
|
addLocalDir("terraform.d/plugins") // our "vendor" directory
|
|
cliConfigDir, err := cliconfig.ConfigDir()
|
|
if err != nil {
|
|
addLocalDir(filepath.Join(cliConfigDir, "plugins"))
|
|
}
|
|
|
|
// This "userdirs" library implements an appropriate user-specific and
|
|
// app-specific directory layout for the current platform, such as XDG Base
|
|
// Directory on Unix, using the following name strings to construct a
|
|
// suitable application-specific subdirectory name following the
|
|
// conventions for each platform:
|
|
//
|
|
// XDG (Unix): lowercase of the first string, "terraform"
|
|
// Windows: two-level heirarchy of first two strings, "HashiCorp\Terraform"
|
|
// OS X: reverse-DNS unique identifier, "io.terraform".
|
|
sysSpecificDirs := userdirs.ForApp("Terraform", "HashiCorp", "io.terraform")
|
|
for _, dir := range sysSpecificDirs.DataSearchPaths("plugins") {
|
|
addLocalDir(dir)
|
|
}
|
|
|
|
// Last but not least, the main registry source! We'll wrap a caching
|
|
// layer around this one to help optimize the several network requests
|
|
// we'll end up making to it while treating it as one of several sources
|
|
// in a MultiSource (as recommended in the MultiSource docs).
|
|
// This one is listed last so that if a particular version is available
|
|
// both in one of the above directories _and_ in a remote registry, the
|
|
// local copy will take precedence.
|
|
searchRules = append(searchRules, getproviders.MultiSourceSelector{
|
|
Source: getproviders.NewMemoizeSource(
|
|
getproviders.NewRegistrySource(services),
|
|
),
|
|
})
|
|
|
|
return getproviders.MultiSource(searchRules)
|
|
}
|