Merge pull request #20674 from hashicorp/registry-warnings
plugin/discovery: Parse warnings from TF Registry
This commit is contained in:
commit
e696e063dc
|
@ -494,7 +494,8 @@ func (c *InitCommand) getProviders(earlyConfig *earlyconfig.Config, state *state
|
||||||
}
|
}
|
||||||
|
|
||||||
for provider, reqd := range missing {
|
for provider, reqd := range missing {
|
||||||
_, err := c.providerInstaller.Get(provider, reqd.Versions)
|
_, providerDiags, err := c.providerInstaller.Get(provider, reqd.Versions)
|
||||||
|
diags = diags.Append(providerDiags)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
constraint := reqd.Versions.String()
|
constraint := reqd.Versions.String()
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/hashicorp/terraform/states"
|
"github.com/hashicorp/terraform/states"
|
||||||
"github.com/hashicorp/terraform/states/statemgr"
|
"github.com/hashicorp/terraform/states/statemgr"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
"github.com/hashicorp/terraform/tfdiags"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInit_empty(t *testing.T) {
|
func TestInit_empty(t *testing.T) {
|
||||||
|
@ -964,8 +965,8 @@ func TestInit_getProviderHaveLegacyVersion(t *testing.T) {
|
||||||
testingOverrides: metaOverridesForProvider(testProvider()),
|
testingOverrides: metaOverridesForProvider(testProvider()),
|
||||||
Ui: ui,
|
Ui: ui,
|
||||||
},
|
},
|
||||||
providerInstaller: callbackPluginInstaller(func(provider string, req discovery.Constraints) (discovery.PluginMeta, error) {
|
providerInstaller: callbackPluginInstaller(func(provider string, req discovery.Constraints) (discovery.PluginMeta, tfdiags.Diagnostics, error) {
|
||||||
return discovery.PluginMeta{}, fmt.Errorf("EXPECTED PROVIDER ERROR %s", provider)
|
return discovery.PluginMeta{}, tfdiags.Diagnostics{}, fmt.Errorf("EXPECTED PROVIDER ERROR %s", provider)
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1174,9 +1175,9 @@ func TestInit_pluginDirProvidersDoesNotGet(t *testing.T) {
|
||||||
|
|
||||||
c := &InitCommand{
|
c := &InitCommand{
|
||||||
Meta: m,
|
Meta: m,
|
||||||
providerInstaller: callbackPluginInstaller(func(provider string, req discovery.Constraints) (discovery.PluginMeta, error) {
|
providerInstaller: callbackPluginInstaller(func(provider string, req discovery.Constraints) (discovery.PluginMeta, tfdiags.Diagnostics, error) {
|
||||||
t.Fatalf("plugin installer should not have been called for %q", provider)
|
t.Fatalf("plugin installer should not have been called for %q", provider)
|
||||||
return discovery.PluginMeta{}, nil
|
return discovery.PluginMeta{}, tfdiags.Diagnostics{}, nil
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/hashicorp/terraform/plugin/discovery"
|
"github.com/hashicorp/terraform/plugin/discovery"
|
||||||
"github.com/hashicorp/terraform/providers"
|
"github.com/hashicorp/terraform/providers"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
"github.com/hashicorp/terraform/tfdiags"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMultiVersionProviderResolver(t *testing.T) {
|
func TestMultiVersionProviderResolver(t *testing.T) {
|
||||||
|
@ -146,16 +147,17 @@ func (i *mockProviderInstaller) FileName(provider, version string) string {
|
||||||
return fmt.Sprintf("terraform-provider-%s_v%s_x4", provider, version)
|
return fmt.Sprintf("terraform-provider-%s_v%s_x4", provider, version)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *mockProviderInstaller) Get(provider string, req discovery.Constraints) (discovery.PluginMeta, error) {
|
func (i *mockProviderInstaller) Get(provider string, req discovery.Constraints) (discovery.PluginMeta, tfdiags.Diagnostics, error) {
|
||||||
|
var diags tfdiags.Diagnostics
|
||||||
noMeta := discovery.PluginMeta{}
|
noMeta := discovery.PluginMeta{}
|
||||||
versions := i.Providers[provider]
|
versions := i.Providers[provider]
|
||||||
if len(versions) == 0 {
|
if len(versions) == 0 {
|
||||||
return noMeta, fmt.Errorf("provider %q not found", provider)
|
return noMeta, diags, fmt.Errorf("provider %q not found", provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := os.MkdirAll(i.Dir, 0755)
|
err := os.MkdirAll(i.Dir, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return noMeta, fmt.Errorf("error creating plugins directory: %s", err)
|
return noMeta, diags, fmt.Errorf("error creating plugins directory: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range versions {
|
for _, v := range versions {
|
||||||
|
@ -170,18 +172,18 @@ func (i *mockProviderInstaller) Get(provider string, req discovery.Constraints)
|
||||||
path := filepath.Join(i.Dir, name)
|
path := filepath.Join(i.Dir, name)
|
||||||
f, err := os.Create(path)
|
f, err := os.Create(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return noMeta, fmt.Errorf("error fetching provider: %s", err)
|
return noMeta, diags, fmt.Errorf("error fetching provider: %s", err)
|
||||||
}
|
}
|
||||||
f.Close()
|
f.Close()
|
||||||
return discovery.PluginMeta{
|
return discovery.PluginMeta{
|
||||||
Name: provider,
|
Name: provider,
|
||||||
Version: discovery.VersionStr(v),
|
Version: discovery.VersionStr(v),
|
||||||
Path: path,
|
Path: path,
|
||||||
}, nil
|
}, diags, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return noMeta, fmt.Errorf("no suitable version for provider %q found with constraints %s", provider, req)
|
return noMeta, diags, fmt.Errorf("no suitable version for provider %q found with constraints %s", provider, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *mockProviderInstaller) PurgeUnused(map[string]discovery.PluginMeta) (discovery.PluginMetaSet, error) {
|
func (i *mockProviderInstaller) PurgeUnused(map[string]discovery.PluginMeta) (discovery.PluginMetaSet, error) {
|
||||||
|
@ -195,9 +197,9 @@ func (i *mockProviderInstaller) PurgeUnused(map[string]discovery.PluginMeta) (di
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type callbackPluginInstaller func(provider string, req discovery.Constraints) (discovery.PluginMeta, error)
|
type callbackPluginInstaller func(provider string, req discovery.Constraints) (discovery.PluginMeta, tfdiags.Diagnostics, error)
|
||||||
|
|
||||||
func (cb callbackPluginInstaller) Get(provider string, req discovery.Constraints) (discovery.PluginMeta, error) {
|
func (cb callbackPluginInstaller) Get(provider string, req discovery.Constraints) (discovery.PluginMeta, tfdiags.Diagnostics, error) {
|
||||||
return cb(provider, req)
|
return cb(provider, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/hashicorp/terraform/registry/regsrc"
|
"github.com/hashicorp/terraform/registry/regsrc"
|
||||||
"github.com/hashicorp/terraform/registry/response"
|
"github.com/hashicorp/terraform/registry/response"
|
||||||
"github.com/hashicorp/terraform/svchost/disco"
|
"github.com/hashicorp/terraform/svchost/disco"
|
||||||
|
"github.com/hashicorp/terraform/tfdiags"
|
||||||
tfversion "github.com/hashicorp/terraform/version"
|
tfversion "github.com/hashicorp/terraform/version"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
)
|
)
|
||||||
|
@ -54,7 +55,7 @@ func init() {
|
||||||
// An Installer maintains a local cache of plugins by downloading plugins
|
// An Installer maintains a local cache of plugins by downloading plugins
|
||||||
// from an online repository.
|
// from an online repository.
|
||||||
type Installer interface {
|
type Installer interface {
|
||||||
Get(name string, req Constraints) (PluginMeta, error)
|
Get(name string, req Constraints) (PluginMeta, tfdiags.Diagnostics, error)
|
||||||
PurgeUnused(used map[string]PluginMeta) (removed PluginMetaSet, err error)
|
PurgeUnused(used map[string]PluginMeta) (removed PluginMetaSet, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +112,9 @@ type ProviderInstaller struct {
|
||||||
// are produced under the assumption that if presented to the user they will
|
// are produced under the assumption that if presented to the user they will
|
||||||
// be presented alongside context about what is being installed, and thus the
|
// be presented alongside context about what is being installed, and thus the
|
||||||
// error messages do not redundantly include such information.
|
// error messages do not redundantly include such information.
|
||||||
func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, error) {
|
func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, tfdiags.Diagnostics, error) {
|
||||||
|
var diags tfdiags.Diagnostics
|
||||||
|
|
||||||
// a little bit of initialization.
|
// a little bit of initialization.
|
||||||
if i.OS == "" {
|
if i.OS == "" {
|
||||||
i.OS = runtime.GOOS
|
i.OS = runtime.GOOS
|
||||||
|
@ -129,19 +132,30 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
|
||||||
// TODO: return multiple errors
|
// TODO: return multiple errors
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if registry.IsServiceNotProvided(err) {
|
if registry.IsServiceNotProvided(err) {
|
||||||
return PluginMeta{}, err
|
return PluginMeta{}, diags, err
|
||||||
}
|
}
|
||||||
return PluginMeta{}, ErrorNoSuchProvider
|
return PluginMeta{}, diags, ErrorNoSuchProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add any warnings from the response to diags
|
||||||
|
for _, warning := range allVersions.Warnings {
|
||||||
|
hostname, err := i.hostname()
|
||||||
|
if err != nil {
|
||||||
|
return PluginMeta{}, diags, err
|
||||||
|
}
|
||||||
|
diag := tfdiags.SimpleWarning(fmt.Sprintf("%s: %s", hostname, warning))
|
||||||
|
diags = diags.Append(diag)
|
||||||
|
}
|
||||||
|
|
||||||
if len(allVersions.Versions) == 0 {
|
if len(allVersions.Versions) == 0 {
|
||||||
return PluginMeta{}, ErrorNoSuitableVersion
|
return PluginMeta{}, diags, ErrorNoSuitableVersion
|
||||||
}
|
}
|
||||||
providerSource := allVersions.ID
|
providerSource := allVersions.ID
|
||||||
|
|
||||||
// Filter the list of plugin versions to those which meet the version constraints
|
// Filter the list of plugin versions to those which meet the version constraints
|
||||||
versions := allowedVersions(allVersions, req)
|
versions := allowedVersions(allVersions, req)
|
||||||
if len(versions) == 0 {
|
if len(versions) == 0 {
|
||||||
return PluginMeta{}, ErrorNoSuitableVersion
|
return PluginMeta{}, diags, ErrorNoSuitableVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort them newest to oldest. The newest version wins!
|
// sort them newest to oldest. The newest version wins!
|
||||||
|
@ -152,7 +166,7 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
|
||||||
if err := i.checkPlatformCompatibility(versions[0]); err != nil {
|
if err := i.checkPlatformCompatibility(versions[0]); err != nil {
|
||||||
versions = i.platformCompatibleVersions(versions)
|
versions = i.platformCompatibleVersions(versions)
|
||||||
if len(versions) == 0 {
|
if len(versions) == 0 {
|
||||||
return PluginMeta{}, ErrorNoVersionCompatibleWithPlatform
|
return PluginMeta{}, diags, ErrorNoVersionCompatibleWithPlatform
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +179,7 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
|
||||||
closestMatch, err := i.findClosestProtocolCompatibleVersion(allVersions.Versions)
|
closestMatch, err := i.findClosestProtocolCompatibleVersion(allVersions.Versions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// No operation here if we can't find a version with compatible protocol
|
// No operation here if we can't find a version with compatible protocol
|
||||||
return PluginMeta{}, err
|
return PluginMeta{}, diags, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prompt version suggestion to UI based on closest protocol match
|
// Prompt version suggestion to UI based on closest protocol match
|
||||||
|
@ -182,7 +196,7 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
|
||||||
constraintStr = "(any version)"
|
constraintStr = "(any version)"
|
||||||
}
|
}
|
||||||
|
|
||||||
return PluginMeta{}, errwrap.Wrap(ErrorVersionIncompatible, fmt.Errorf(fmt.Sprintf(
|
return PluginMeta{}, diags, errwrap.Wrap(ErrorVersionIncompatible, fmt.Errorf(fmt.Sprintf(
|
||||||
errMsg, provider, v.String(), tfversion.String(),
|
errMsg, provider, v.String(), tfversion.String(),
|
||||||
closestVersion.String(), closestVersion.MinorUpgradeConstraintStr(), constraintStr)))
|
closestVersion.String(), closestVersion.MinorUpgradeConstraintStr(), constraintStr)))
|
||||||
}
|
}
|
||||||
|
@ -193,7 +207,7 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
|
||||||
if !i.SkipVerify {
|
if !i.SkipVerify {
|
||||||
sha256, err := i.getProviderChecksum(downloadURLs)
|
sha256, err := i.getProviderChecksum(downloadURLs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return PluginMeta{}, err
|
return PluginMeta{}, diags, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the checksum parameter for go-getter to verify the download for us.
|
// add the checksum parameter for go-getter to verify the download for us.
|
||||||
|
@ -207,7 +221,7 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
|
||||||
log.Printf("[DEBUG] getting provider %q version %q", printedProviderName, versionMeta.Version)
|
log.Printf("[DEBUG] getting provider %q version %q", printedProviderName, versionMeta.Version)
|
||||||
err = i.install(provider, v, providerURL)
|
err = i.install(provider, v, providerURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return PluginMeta{}, err
|
return PluginMeta{}, diags, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find what we just installed
|
// Find what we just installed
|
||||||
|
@ -224,7 +238,7 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
|
||||||
// This should never happen. Suggests that the release archive
|
// This should never happen. Suggests that the release archive
|
||||||
// contains an executable file whose name doesn't match the
|
// contains an executable file whose name doesn't match the
|
||||||
// expected convention.
|
// expected convention.
|
||||||
return PluginMeta{}, fmt.Errorf(
|
return PluginMeta{}, diags, fmt.Errorf(
|
||||||
"failed to find installed plugin version %s; this is a bug in Terraform and should be reported",
|
"failed to find installed plugin version %s; this is a bug in Terraform and should be reported",
|
||||||
versionMeta.Version,
|
versionMeta.Version,
|
||||||
)
|
)
|
||||||
|
@ -235,7 +249,7 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
|
||||||
// particular version was re-released with a different
|
// particular version was re-released with a different
|
||||||
// executable filename. We consider releases as immutable, so
|
// executable filename. We consider releases as immutable, so
|
||||||
// this is an error.
|
// this is an error.
|
||||||
return PluginMeta{}, fmt.Errorf(
|
return PluginMeta{}, diags, fmt.Errorf(
|
||||||
"multiple plugins installed for version %s; this is a bug in Terraform and should be reported",
|
"multiple plugins installed for version %s; this is a bug in Terraform and should be reported",
|
||||||
versionMeta.Version,
|
versionMeta.Version,
|
||||||
)
|
)
|
||||||
|
@ -243,8 +257,7 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e
|
||||||
|
|
||||||
// By now we know we have exactly one meta, and so "Newest" will
|
// By now we know we have exactly one meta, and so "Newest" will
|
||||||
// return that one.
|
// return that one.
|
||||||
return metas.Newest(), nil
|
return metas.Newest(), diags, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *ProviderInstaller) install(provider string, version Version, url string) error {
|
func (i *ProviderInstaller) install(provider string, version Version, url string) error {
|
||||||
|
@ -421,6 +434,16 @@ func (i *ProviderInstaller) getProviderChecksum(urls *response.TerraformProvider
|
||||||
return checksumForFile(shasums, urls.Filename), nil
|
return checksumForFile(shasums, urls.Filename), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *ProviderInstaller) hostname() (string, error) {
|
||||||
|
provider := regsrc.NewTerraformProvider("", i.OS, i.Arch)
|
||||||
|
svchost, err := provider.SvcHost()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return svchost.ForDisplay(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// list all versions available for the named provider
|
// list all versions available for the named provider
|
||||||
func (i *ProviderInstaller) listProviderVersions(name string) (*response.TerraformProviderVersions, error) {
|
func (i *ProviderInstaller) listProviderVersions(name string) (*response.TerraformProviderVersions, error) {
|
||||||
provider := regsrc.NewTerraformProvider(name, i.OS, i.Arch)
|
provider := regsrc.NewTerraformProvider(name, i.OS, i.Arch)
|
||||||
|
|
|
@ -415,7 +415,7 @@ func TestProviderInstallerGet(t *testing.T) {
|
||||||
Ui: cli.NewMockUi(),
|
Ui: cli.NewMockUi(),
|
||||||
registry: registry.NewClient(Disco(server), nil),
|
registry: registry.NewClient(Disco(server), nil),
|
||||||
}
|
}
|
||||||
_, err = i.Get("test", AllVersions)
|
_, _, err = i.Get("test", AllVersions)
|
||||||
|
|
||||||
if err != ErrorNoVersionCompatibleWithPlatform {
|
if err != ErrorNoVersionCompatibleWithPlatform {
|
||||||
t.Fatal("want error for incompatible version")
|
t.Fatal("want error for incompatible version")
|
||||||
|
@ -432,20 +432,20 @@ func TestProviderInstallerGet(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
_, err := i.Get("test", ConstraintStr(">9.0.0").MustParse())
|
_, _, err := i.Get("test", ConstraintStr(">9.0.0").MustParse())
|
||||||
if err != ErrorNoSuitableVersion {
|
if err != ErrorNoSuitableVersion {
|
||||||
t.Fatal("want error for mismatching constraints")
|
t.Fatal("want error for mismatching constraints")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
_, err := i.Get("nonexist", AllVersions)
|
_, _, err := i.Get("nonexist", AllVersions)
|
||||||
if err != ErrorNoSuchProvider {
|
if err != ErrorNoSuchProvider {
|
||||||
t.Fatal("want error for no such provider")
|
t.Fatal("want error for no such provider")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gotMeta, err := i.Get("test", AllVersions)
|
gotMeta, _, err := i.Get("test", AllVersions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -503,7 +503,7 @@ func TestProviderInstallerGet_cache(t *testing.T) {
|
||||||
Arch: "mockarch",
|
Arch: "mockarch",
|
||||||
}
|
}
|
||||||
|
|
||||||
gotMeta, err := i.Get("test", AllVersions)
|
gotMeta, _, err := i.Get("test", AllVersions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ type TerraformProviderVersion struct {
|
||||||
type TerraformProviderVersions struct {
|
type TerraformProviderVersions struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Versions []*TerraformProviderVersion `json:"versions"`
|
Versions []*TerraformProviderVersion `json:"versions"`
|
||||||
|
Warnings []string `json:"warnings"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TerraformProviderPlatform is the Terraform-specific response structure for a
|
// TerraformProviderPlatform is the Terraform-specific response structure for a
|
||||||
|
|
|
@ -181,7 +181,7 @@ func (c *PackageCommand) Run(args []string) int {
|
||||||
} else { //attempt to get from the public registry if not found locally
|
} else { //attempt to get from the public registry if not found locally
|
||||||
c.ui.Output(fmt.Sprintf("- Checking for provider plugin on %s...",
|
c.ui.Output(fmt.Sprintf("- Checking for provider plugin on %s...",
|
||||||
releaseHost))
|
releaseHost))
|
||||||
_, err := installer.Get(name, constraint)
|
_, _, err := installer.Get(name, constraint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ui.Error(fmt.Sprintf("- Failed to resolve %s provider %s: %s", name, constraint, err))
|
c.ui.Error(fmt.Sprintf("- Failed to resolve %s provider %s: %s", name, constraint, err))
|
||||||
return 1
|
return 1
|
||||||
|
|
Loading…
Reference in New Issue