From 39de3ebec71e51d5eb44923129b67be732f5d6d8 Mon Sep 17 00:00:00 2001 From: Alisdair McDiarmid Date: Thu, 21 Oct 2021 08:44:26 -0400 Subject: [PATCH] cli: Fix init failure with deleted cache The init command needs to initialize a backend, in order to access state, in turn to derive provider requirements from state. The backend initialization step requires building provider factories, which previously would fail if a lockfile was present without a corresponding local provider cache. This commit ensures that in this situation only, errors with the provider factories are temporarily ignored. This allows us to continue to initialize the backend, fetch providers, and then report any errors as necessary. --- internal/command/init.go | 2 +- internal/command/meta.go | 8 +++--- internal/command/meta_backend.go | 44 ++++++++++++++++++-------------- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/internal/command/init.go b/internal/command/init.go index 8083b7513..8f8c5b829 100644 --- a/internal/command/init.go +++ b/internal/command/init.go @@ -238,7 +238,7 @@ func (c *InitCommand) Run(args []string) int { // by a previous run, so we must still expect that "back" may be nil // in code that follows. var backDiags tfdiags.Diagnostics - back, backDiags = c.Backend(nil) + back, backDiags = c.Backend(&BackendOpts{Init: true}) if backDiags.HasErrors() { // This is fine. We'll proceed with no backend, then. back = nil diff --git a/internal/command/meta.go b/internal/command/meta.go index cd9387173..6bfc2d8c7 100644 --- a/internal/command/meta.go +++ b/internal/command/meta.go @@ -459,10 +459,8 @@ func (m *Meta) contextOpts() (*terraform.ContextOpts, error) { opts.Providers = m.testingOverrides.Providers opts.Provisioners = m.testingOverrides.Provisioners } else { - providerFactories, err := m.providerFactories() - if err != nil { - return nil, err - } + var providerFactories map[addrs.Provider]providers.Factory + providerFactories, err = m.providerFactories() opts.Providers = providerFactories opts.Provisioners = m.provisionerFactories() } @@ -472,7 +470,7 @@ func (m *Meta) contextOpts() (*terraform.ContextOpts, error) { OriginalWorkingDir: m.WorkingDir.OriginalWorkingDir(), } - return &opts, nil + return &opts, err } // defaultFlagSet creates a default flag set for commands. diff --git a/internal/command/meta_backend.go b/internal/command/meta_backend.go index 4d9b115d4..2524ccb28 100644 --- a/internal/command/meta_backend.go +++ b/internal/command/meta_backend.go @@ -112,28 +112,34 @@ func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, tfdiags.Diagnostics // 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) + // + // If initialization is allowed, we ignore this error, as it may + // be resolved by the later step where providers are fetched. + if !opts.Init { + 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, + ), + )) + return nil, diags } - 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 } - return nil, diags } cliOpts.Validation = true @@ -337,7 +343,7 @@ func (m *Meta) BackendForPlan(settings plans.Backend) (backend.Enhanced, tfdiags // a backend that supports local CLI operations. func (m *Meta) backendCLIOpts() (*backend.CLIOpts, error) { contextOpts, err := m.contextOpts() - if err != nil { + if contextOpts == nil && err != nil { return nil, err } return &backend.CLIOpts{ @@ -350,7 +356,7 @@ func (m *Meta) backendCLIOpts() (*backend.CLIOpts, error) { ContextOpts: contextOpts, Input: m.Input(), RunningInAutomation: m.RunningInAutomation, - }, nil + }, err } // Operation initializes a new backend.Operation struct.