main: Honor explicit provider_installation CLI config when present

If the CLI configuration contains a provider_installation block then we'll
use the source configuration it describes instead of the implied one we'd
build otherwise.
This commit is contained in:
Martin Atkins 2020-04-21 16:28:59 -07:00
parent c5bd783eba
commit 5af1e6234a
2 changed files with 104 additions and 7 deletions

18
main.go
View File

@ -127,6 +127,7 @@ func wrappedMain() int {
log.Printf("[INFO] CLI args: %#v", os.Args)
config, diags := cliconfig.LoadConfig()
if len(diags) > 0 {
// Since we haven't instantiated a command.Meta yet, we need to do
// some things manually here and use some "safe" defaults for things
@ -168,7 +169,22 @@ func wrappedMain() int {
// direct from a registry. In future there should be a mechanism to
// configure providers sources from the CLI config, which will then
// change how we construct this object.
providerSrc := providerSource(services)
providerSrc, diags := providerSource(config.ProviderInstallation, services)
if len(diags) > 0 {
Ui.Error("There are some problems with the provider_installation configuration:")
for _, diag := range diags {
earlyColor := &colorstring.Colorize{
Colors: colorstring.DefaultColors,
Disable: true, // Disable color to be conservative until we know better
Reset: true,
}
Ui.Error(format.Diagnostic(diag, nil, earlyColor, 78))
}
if diags.HasErrors() {
Ui.Error("As a result of the above problems, Terraform's provider installer may not behave as intended.\n\n")
// We continue to run anyway, because most commands don't do provider installation.
}
}
// Initialize the backends.
backendInit.Init(services)

View File

@ -1,28 +1,76 @@
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"github.com/apparentlymart/go-userdirs/userdirs"
svchost "github.com/hashicorp/terraform-svchost"
"github.com/hashicorp/terraform-svchost/disco"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/command/cliconfig"
"github.com/hashicorp/terraform/internal/getproviders"
"github.com/hashicorp/terraform/tfdiags"
)
// 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 just always use the
// implicit default provider source.
return implicitProviderSource(services)
func providerSource(configs []*cliconfig.ProviderInstallation, services *disco.Disco) (getproviders.Source, tfdiags.Diagnostics) {
if len(configs) == 0 {
// If there's no explicit installation configuration then we'll build
// up an implicit one with direct registry installation along with
// some automatically-selected local filesystem mirrors.
return implicitProviderSource(services), nil
}
// There should only be zero or one configurations, which is checked by
// the validation logic in the cliconfig package. Therefore we'll just
// ignore any additional configurations in here.
config := configs[0]
return explicitProviderSource(config, services)
}
func explicitProviderSource(config *cliconfig.ProviderInstallation, services *disco.Disco) (getproviders.Source, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
var searchRules []getproviders.MultiSourceSelector
for _, sourceConfig := range config.Sources {
source, moreDiags := providerSourceForCLIConfigLocation(sourceConfig.Location, services)
diags = diags.Append(moreDiags)
if moreDiags.HasErrors() {
continue
}
include, err := getproviders.ParseMultiSourceMatchingPatterns(sourceConfig.Include)
if err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid provider source inclusion patterns",
fmt.Sprintf("CLI config specifies invalid provider inclusion patterns: %s.", err),
))
}
exclude, err := getproviders.ParseMultiSourceMatchingPatterns(sourceConfig.Include)
if err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid provider source exclusion patterns",
fmt.Sprintf("CLI config specifies invalid provider exclusion patterns: %s.", err),
))
}
searchRules = append(searchRules, getproviders.MultiSourceSelector{
Source: source,
Include: include,
Exclude: exclude,
})
}
return getproviders.MultiSource(searchRules), diags
}
// implicitProviderSource builds a default provider source to use if there's
@ -130,3 +178,36 @@ func implicitProviderSource(services *disco.Disco) getproviders.Source {
return getproviders.MultiSource(searchRules)
}
func providerSourceForCLIConfigLocation(loc cliconfig.ProviderInstallationSourceLocation, services *disco.Disco) (getproviders.Source, tfdiags.Diagnostics) {
if loc == cliconfig.ProviderInstallationDirect {
return getproviders.NewMemoizeSource(
getproviders.NewRegistrySource(services),
), nil
}
switch loc := loc.(type) {
case cliconfig.ProviderInstallationFilesystemMirror:
return getproviders.NewFilesystemMirrorSource(string(loc)), nil
case cliconfig.ProviderInstallationNetworkMirror:
host, err := svchost.ForComparison(string(loc))
if err != nil {
var diags tfdiags.Diagnostics
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid hostname for provider installation source",
fmt.Sprintf("Cannot parse %q as a hostname for a network provider mirror: %s.", string(loc), err),
))
return nil, diags
}
return getproviders.NewNetworkMirrorSource(host), nil
default:
// We should not get here because the set of cases above should
// be comprehensive for all of the
// cliconfig.ProviderInstallationLocation implementations.
panic(fmt.Sprintf("unexpected provider source location type %T", loc))
}
}