Commit d09510a8 authored by Martin Atkins's avatar Martin Atkins
Browse files

command: Early error message for missing cache entries of locked providers

In the original incarnation of Meta.providerFactories we were returning
into a Meta.contextOpts whose signature didn't allow it to return an
error directly, and so we had compromised by making the provider factory
functions themselves return errors once called.

Subsequent work made Meta.contextOpts need to return an error anyway, but
at the time we neglected to update our handling of the providerFactories
result, having it still defer the error handling until we finally
instantiate a provider.

Although that did ultimately get the expected result anyway, the error
ended up being reported from deep in the guts of a Terraform Core graph
walk, in whichever concurrently-visited graph node happened to try to
instantiate the plugin first. This meant that the exact phrasing of the
error message would vary between runs and the reporting codepath didn't
have enough context to given an actionable suggestion on how to proceed.

In this commit we make Meta.contextOpts pass through directly any error
that Meta.providerFactories produces, and then make Meta.providerFactories
produce a special error type so that Meta.Backend can ultimately return
a user-friendly diagnostic message containing a specific suggestion to
run "terraform init", along with a short explanation of what a provider
plugin is.

The reliance here on an implied contract between two functions that are
not directly connected in the callstack is non-ideal, and so hopefully
we'll revisit this further in future work on the overall architecture of
the CLI layer. To try to make this robust in the meantime though, I wrote
it to use the errors.As function to potentially unwrap a wrapped version
of our special error type, in case one of the intervening layers is
changed at some point to wrap the downstream error before returning it.
parent aece887a
main MatthewTestBranch add-cont-valid-callout add-internals-to-sidebar add-jsonstate-to-cloudbackendstate add-jsonstate-to-cloudbackendstate2 add-learn-callout-moved-blocks add-new-intro-docs add-note-about-spaces add-tutorial-custom-conditions add-version-notes-1.2 add-warnings-backends alisdair/disable-preconditions-postconditions alisdair/fix-configload-snapshot-panic alisdair/fix-remote-backend-migrate-version-check alisdair/metadata-functions-command alisdair/resource-instance-object-dependencies b-1.1-module-source-git b-check-output-multi-expand b-check-resource-multi-expand b-flatten-panic b-move-implied-cross-package b-type-conversion-funcs-null b-yamldecode-emptydoc-null backport/29156_do_not_log_sensitive_values/nearly-safe-bullfrog backport/4th-alternate-mirror-directory-fix/closely-key-civet backport/add-cont-valid-callout/luckily-golden-wildcat backport/add-format-function-guidance/ultimately-lucky-halibut backport/add-internals-to-sidebar/willingly-bright-sunbird backport/add-learn-callout-moved-blocks/multiply-awaited-cub backport/add-new-intro-docs/strangely-lucky-bluegill backport/add-powershell-warning/certainly-amazing-lemur backport/add-version-notes-1.2/amazingly-premium-woodcock backport/add-warnings-backends/physically-many-mantis backport/alisdair/fix-30641/firmly-musical-lemming backport/alisdair/fix-show-plan-against-non-default-state/blatantly-balanced-porpoise backport/alisdair/pre-convert-optional-defaults/virtually-able-mouse backport/alisdair/redact-sensitive-values-from-function-errors/definitely-assured-mantis backport/b-check-resource-multi-expand/extremely-key-sheep backport/backport/cstella84/patch-add-hyperlink-for-referenced-argument/manually-patient-locust backport/barrettclark/update-go-slug/severely-destined-crow backport/bugfix_typos/hardly-sharp-mosquito backport/clarify-backend-state-storage/ultimately-causal-ladybird backport/cstella84/patch-add-hyperlink-for-referenced-argument backport/doc-provisioner-scp/definitely-capital-bug backport/doc-refactoring-nav-link/honestly-sweet-sawfly backport/doc-s3-fix/utterly-close-rabbit backport/docs-fix-typo/usefully-blessed-monkey backport/docs-for-each-list-toset/basically-still-zebra backport/docs/unknwon-value/completely-musical-lionfish backport/f-build-go1.19.3/largely-peaceful-grouper backport/f-non-existing-module-instance-crash/neatly-perfect-kiwi backport/file-provisioner-powershell-warning/noticeably-adequate-mullet backport/fix-apt-page/abnormally-relative-quagga backport/fix-backends-link/strangely-emerging-crane backport/fix-backends-link/vertically-noble-hornet backport/fix-broken-link/certainly-measured-chipmunk backport/fix-broken-links-1-10/firmly-equal-rabbit backport/fix-cdktf-link/highly-pumped-snapper backport/fix-glossary-table-contents/uniquely-pro-garfish backport/fix-grammar/precisely-polite-tortoise backport/fix-internals-overview/globally-allowed-kid backport/fix-internals-overview/noticeably-up-rodent backport/fix-intro-page-images/constantly-capable-shiner backport/fix-last-intro-nits/infinitely-workable-redfish backport/fix-links-devdot/strictly-notable-sparrow backport/fix-links-release/seemingly-living-dinosaur backport/fix-remote-backend-references/primarily-tops-mite backport/fix-workspace-name-docs/ideally-uncommon-pheasant backport/jbardin/cancel-auto-approve/extremely-brave-bear backport/jbardin/k8s-mod-update/likely-probable-falcon backport/jbardin/static-validate-nested-types/possibly-crack-mouse backport/kevin/rewrite-internal-redirects/quietly-helped-pelican backport/main/cleanly-mature-scorpion backport/mg_no_code_prov_followup/marginally-relevant-eagle backport/mktg-tf-76ef54dc3c574e032725e0341be8e1d2/constantly-smart-kingfish backport/mktg-tf-76ef54dc3c574e032725e0341be8e1d2/distinctly-sharp-ferret backport/mktg-tf-76ef54dc3c574e032725e0341be8e1d2/friendly-evident-grouse backport/module-invocation-warning/fully-fitting-buzzard backport/nvanthao/update-docs-implicit-provider/locally-neutral-lemur backport/optional-type-attributes-note/inherently-dear-goat backport/patch-1/gladly-mature-oarfish backport/patch-1/manually-fine-mantis backport/patch-1/nationally-working-kite backport/patch-1/noticeably-comic-manatee backport/patch-1/rarely-informed-gopher backport/patch-1/sensibly-saving-swine backport/patch-1/usually-clear-shad backport/patch-1/vaguely-deciding-beagle backport/patch-1/virtually-more-rhino backport/patch-1/wholly-verified-racer backport/patch-1/willingly-usable-husky backport/patch-1/yearly-rich-skunk backport/patch-2/badly-game-spider backport/patch-2/finally-amazed-catfish backport/patch-2/openly-clean-tick backport/patch-2/weekly-selected-tiger backport/remove-future-statement-import/briefly-viable-glowworm backport/remove-provisioners/readily-correct-ferret backport/remove-provisioners/widely-singular-hound backport/replace-flag-clarifications/definitely-saved-elf backport/replace-flag-updates/nominally-assured-weevil backport/startsswith-to-startswith/highly-gorgeous-katydid backport/system-parameter/infinitely-open-bluebird backport/tweak-multi-to-multi-migration-tfc/suddenly-real-duckling backport/update-cloud-block-pages/verbally-key-kangaroo backport/update-console-docs/closely-genuine-javelin backport/update-for-each-example/early-crucial-piranha backport/update-plan-page/lightly-outgoing-halibut backport/update-run-task-result/factually-star-sunfish backport/update_docs_for_30072/gradually-trusting-wahoo backport/workspaces-confusion-fixes/secondly-huge-titmouse barrettclark/fix-state-outputs-read-permissions brandonc/changelog_nested_sensitive brandonc/changelog_sensitive_diff_fixes brandonc/cloud_upgrade_013 brandonc/nested_attr_sensitive brandonc/output_cloud_reads brandonc/providers-estimate brandonc/run_variables_types brandonc/scheme_override_cloud brandonc/variable_parsing_refactor build-pr-checks build-workflow-dev/cgo-enabled build-workflow-dev/liamcervante/equivalence-test-action bump-gcp-storage-dependency bump-gcp-storage-dependency-2 cloud-e2e-fix cloud-integration-changelog-entry dependabot/go_modules/github.com/bmatcuk/doublestar-1.3.4 dependabot/go_modules/github.com/mattn/go-shellwords-1.0.12 dev-portal-updates-docs dividers-devdot-fixes doc-provisioner-scp doc-refactoring-nav-link doc-unicode-hcl doc-yamlencode-stable docs-for-each-list-toset docs-readme-updates-versioned-docs ds.submodule-nav-main f-addrs-static-checkable f-build-go1.19.3 f-cli-hide-fast-refresh f-cmd-web f-diagnostics-cli-reorg f-dynamic-provider-assignment f-e2etest-deps-forbidden f-expand-root-outputs f-fileexists-errmsg f-functions-in-providers f-implied-move-module-call f-init-provider-source-feedback f-jsonstate-2 f-moduletest-2 f-new-build-pipeline f-ng-workflow f-output-value-types f-partial-plan-on-error f-partial-plan-on-error-ui f-persistent-checks-old f-rpcplugin-interface f-svcauth-environment f-testing-with-conditions f-validate-lint f/azurerm-backend-msal fix-apt-page fix-broken-link fix-broken-links-1-10 fix-cdktf-link fix-dividers-for-devdot fix-future-facing-language fix-future-lang-2 fix-internals-overview fix-intro-page-images fix-last-intro-nits fix-links-devdot fix-links-release fix-postconditions-example fix-preconditions fix-provisioners-content fix-readme-again gcs-backend-add-kms gcs-backend-add-private-connect-support gcs-refactor-credential-handling gs/add-pre-plan-run-tasks jbardin/1.3-destroy-perf jbardin/backport-31576 jbardin/call-plan-destroy jbardin/data-source-destroy-edges jbardin/lookup-objects jbardin/null-variable jbardin/output-perf jbardin/plan-orphan-deleted jbardin/remove-deprecated-backends jbardin/resolved-provided-by jbardin/terraform-data jbardin/terraform-null jbardin/trigger-replacement jbardin/variable-eval kevin/local-preview-post-split kevin/preview kevin/remove-guides-docs kevin/vercel-config kmoe/http-backend-debug-log kmoe/init-checksum-miss-error kmoe/misc-help-text kmoe/unused-resource-attributes lafentres/autolabel-dependabot-prs lafentres/refactor-show-command laura-update-docs-readme laura-update-pre-post-conditions liamcervante/cicd-go-vet liamcervante/structured-run-output link_workflow_tutorials megan_invalid_creds megan_update_msg migrate-go-tfe-1_0 nf/nov21-migrate-away-from-cloud preapply-runtasks-cli-output preapply-runtasks-clioutput release-notes-env-credentials replace-flag-updates rt-backport-changelog rt-changelog-entry-1.1 run-tasks-backport sebasslash/add-cloud-e2e-test-workflow sebasslash/add-tf-hostname-env-var sebasslash/add-tf-org-env-var sebasslash/env-cloud-e2e-tests sebasslash/err-approval-input-false sebasslash/resolve-flaky-env-var-test sebasslash/tf-workspace-cloud-config stable-website tags-reconfigure-msg tchupp/override-local-vars test-branch-protection-workflow tfc-integration-docs uk1288/backport-cloud-integration-panic-fix uk1288/fix-for-cloud-integration-panic uk1288/update-changelog-md uk1288/update-changelog-md-v1-1 update-TF-WORKSPACE-variable update-cidrnetmask-docs update-depends-on-docs update-for-each-example update-packaging-action-name update_gen_meta uturunku1-patch-1 uturunku1-patch-2 v1.1 v1.4.0-alpha20221109 v1.3.5 v1.3.4 v1.3.3 v1.3.2 v1.3.1 v1.3.0 v1.3.0-rc1 v1.3.0-dev v1.3.0-beta1 v1.3.0-alpha20220817 v1.3.0-alpha20220803 v1.3.0-alpha20220706 v1.3.0-alpha20220622 v1.3.0-alpha20220608 v1.2.9 v1.2.8 v1.2.7 v1.2.6 v1.2.5 v1.2.4 v1.2.3 v1.2.2 v1.2.1 v1.2.0 v1.2.0-rc2 v1.2.0-rc1 v1.2.0-beta1 v1.2.0-alpha20220413 v1.2.0-alpha-20220328 v1.1.9 v1.1.8 v1.1.7 v1.1.6 v1.1.5 v1.1.4 v1.1.3 v1.1.2 v1.1.1 v1.1.0 v1.1.0-rc1 v1.1.0-beta2 v1.1.0-beta1 v1.1.0-alpha20211029 v1.1.0-alpha20211020 v1.1.0-alpha20211006
No related merge requests found
Showing with 59 additions and 18 deletions
+59 -18
......@@ -461,20 +461,7 @@ func (m *Meta) contextOpts() (*terraform.ContextOpts, error) {
} else {
providerFactories, err := m.providerFactories()
if err != nil {
// providerFactories can fail if the plugin selections file is
// invalid in some way, but we don't have any way to report that
// from here so we'll just behave as if no providers are available
// in that case. However, we will produce a warning in case this
// shows up unexpectedly and prompts a bug report.
// This situation shouldn't arise commonly in practice because
// the selections file is generated programmatically.
log.Printf("[WARN] Failed to determine selected providers: %s", err)
// variable providerFactories may now be incomplete, which could
// lead to errors reported downstream from here. providerFactories
// tries to populate as many providers as possible even in an
// error case, so that operations not using problematic providers
// can still succeed.
return nil, err
}
opts.Providers = providerFactories
opts.Provisioners = m.provisionerFactories()
......
......@@ -4,8 +4,10 @@ package command
// exported and private.
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"log"
"path/filepath"
......@@ -105,7 +107,32 @@ func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, tfdiags.Diagnostics
// Set up the CLI opts we pass into backends that support it.
cliOpts, err := m.backendCLIOpts()
if err != nil {
diags = diags.Append(err)
if errs := providerPluginErrors(nil); errors.As(err, &errs) {
// This is a special type returned by m.providerFactories, which
// indicates one or more inconsistencies between the dependency
// lock file and the provider plugins actually available in the
// local cache directory.
var buf bytes.Buffer
for addr, err := range errs {
fmt.Fprintf(&buf, "\n - %s: %s", addr, err)
}
suggestion := "To download the plugins required for this configuration, run:\n terraform init"
if m.RunningInAutomation {
// Don't mention "terraform init" specifically if we're running in an automation wrapper
suggestion = "You must install the required plugins before running Terraform operations."
}
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Required plugins are not installed",
fmt.Sprintf(
"The installed provider plugins are not consistent with the packages selected in the dependency lock file:%s\n\nTerraform uses external plugins to integrate with a variety of different infrastructure services. %s",
buf.String(), suggestion,
),
))
} else {
// All other errors just get generic handling.
diags = diags.Append(err)
}
return nil, diags
}
cliOpts.Validation = true
......
package command
import (
"bytes"
"errors"
"fmt"
"log"
......@@ -8,7 +9,6 @@ import (
"os/exec"
"strings"
"github.com/hashicorp/go-multierror"
plugin "github.com/hashicorp/go-plugin"
"github.com/hashicorp/terraform/internal/addrs"
......@@ -236,7 +236,7 @@ func (m *Meta) providerFactories() (map[addrs.Provider]providers.Factory, error)
// where appropriate and so that callers can potentially make use of the
// partial result we return if e.g. they want to enumerate which providers
// are available, or call into one of the providers that didn't fail.
var err error
errs := make(map[addrs.Provider]error)
// For the providers from the lock file, we expect them to be already
// available in the provider cache because "terraform init" should already
......@@ -274,7 +274,7 @@ func (m *Meta) providerFactories() (map[addrs.Provider]providers.Factory, error)
}
for provider, lock := range providerLocks {
reportError := func(thisErr error) {
err = multierror.Append(err, thisErr)
errs[provider] = thisErr
// We'll populate a provider factory that just echoes our error
// again if called, which allows us to still report a helpful
// error even if it gets detected downstream somewhere from the
......@@ -324,6 +324,11 @@ func (m *Meta) providerFactories() (map[addrs.Provider]providers.Factory, error)
for provider, reattach := range unmanagedProviders {
factories[provider] = unmanagedProviderFactory(provider, reattach)
}
var err error
if len(errs) > 0 {
err = providerPluginErrors(errs)
}
return factories, err
}
......@@ -475,3 +480,25 @@ func providerFactoryError(err error) providers.Factory {
return nil, err
}
}
// providerPluginErrors is an error implementation we can return from
// Meta.providerFactories to capture potentially multiple errors about the
// locally-cached plugins (or lack thereof) for particular external providers.
//
// Some functions closer to the UI layer can sniff for this error type in order
// to return a more helpful error message.
type providerPluginErrors map[addrs.Provider]error
func (errs providerPluginErrors) Error() string {
if len(errs) == 1 {
for addr, err := range errs {
return fmt.Sprintf("%s: %s", addr, err)
}
}
var buf bytes.Buffer
fmt.Fprintf(&buf, "missing or corrupted provider plugins:")
for addr, err := range errs {
fmt.Fprintf(&buf, "\n - %s: %s", addr, err)
}
return buf.String()
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment