Merge pull request #29792 from hashicorp/alisdair/init-missing-cache

cli: Fix init failure with deleted cache
This commit is contained in:
Alisdair McDiarmid 2021-10-22 09:23:19 -04:00 committed by GitHub
commit 92a9aa9b39
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 40 additions and 26 deletions

View File

@ -57,7 +57,7 @@ func TestProviderTampering(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
_, stderr, err := tf.Run("plan") stdout, stderr, err := tf.Run("plan")
if err == nil { if err == nil {
t.Fatalf("unexpected plan success\nstdout:\n%s", stdout) t.Fatalf("unexpected plan success\nstdout:\n%s", stdout)
} }
@ -67,6 +67,16 @@ func TestProviderTampering(t *testing.T) {
if want := `terraform init`; !strings.Contains(stderr, want) { if want := `terraform init`; !strings.Contains(stderr, want) {
t.Errorf("missing expected error message\nwant substring: %s\ngot:\n%s", want, stderr) t.Errorf("missing expected error message\nwant substring: %s\ngot:\n%s", want, stderr)
} }
// Running init as suggested resolves the problem
_, stderr, err = tf.Run("init")
if err != nil {
t.Fatalf("unexpected init error: %s\nstderr:\n%s", err, stderr)
}
_, stderr, err = tf.Run("plan")
if err != nil {
t.Fatalf("unexpected plan error: %s\nstderr:\n%s", err, stderr)
}
}) })
t.Run("null plugin package modified before plan", func(t *testing.T) { t.Run("null plugin package modified before plan", func(t *testing.T) {
tf := e2e.NewBinary(terraformBin, seedDir) tf := e2e.NewBinary(terraformBin, seedDir)

View File

@ -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 // by a previous run, so we must still expect that "back" may be nil
// in code that follows. // in code that follows.
var backDiags tfdiags.Diagnostics var backDiags tfdiags.Diagnostics
back, backDiags = c.Backend(nil) back, backDiags = c.Backend(&BackendOpts{Init: true})
if backDiags.HasErrors() { if backDiags.HasErrors() {
// This is fine. We'll proceed with no backend, then. // This is fine. We'll proceed with no backend, then.
back = nil back = nil

View File

@ -459,10 +459,8 @@ func (m *Meta) contextOpts() (*terraform.ContextOpts, error) {
opts.Providers = m.testingOverrides.Providers opts.Providers = m.testingOverrides.Providers
opts.Provisioners = m.testingOverrides.Provisioners opts.Provisioners = m.testingOverrides.Provisioners
} else { } else {
providerFactories, err := m.providerFactories() var providerFactories map[addrs.Provider]providers.Factory
if err != nil { providerFactories, err = m.providerFactories()
return nil, err
}
opts.Providers = providerFactories opts.Providers = providerFactories
opts.Provisioners = m.provisionerFactories() opts.Provisioners = m.provisionerFactories()
} }
@ -472,7 +470,7 @@ func (m *Meta) contextOpts() (*terraform.ContextOpts, error) {
OriginalWorkingDir: m.WorkingDir.OriginalWorkingDir(), OriginalWorkingDir: m.WorkingDir.OriginalWorkingDir(),
} }
return &opts, nil return &opts, err
} }
// defaultFlagSet creates a default flag set for commands. // defaultFlagSet creates a default flag set for commands.

View File

@ -112,28 +112,34 @@ func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, tfdiags.Diagnostics
// indicates one or more inconsistencies between the dependency // indicates one or more inconsistencies between the dependency
// lock file and the provider plugins actually available in the // lock file and the provider plugins actually available in the
// local cache directory. // local cache directory.
var buf bytes.Buffer //
for addr, err := range errs { // If initialization is allowed, we ignore this error, as it may
fmt.Fprintf(&buf, "\n - %s: %s", addr, err) // 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 { } else {
// All other errors just get generic handling. // All other errors just get generic handling.
diags = diags.Append(err) diags = diags.Append(err)
return nil, diags
} }
return nil, diags
} }
cliOpts.Validation = true cliOpts.Validation = true
@ -337,7 +343,7 @@ func (m *Meta) BackendForPlan(settings plans.Backend) (backend.Enhanced, tfdiags
// a backend that supports local CLI operations. // a backend that supports local CLI operations.
func (m *Meta) backendCLIOpts() (*backend.CLIOpts, error) { func (m *Meta) backendCLIOpts() (*backend.CLIOpts, error) {
contextOpts, err := m.contextOpts() contextOpts, err := m.contextOpts()
if err != nil { if contextOpts == nil && err != nil {
return nil, err return nil, err
} }
return &backend.CLIOpts{ return &backend.CLIOpts{
@ -350,7 +356,7 @@ func (m *Meta) backendCLIOpts() (*backend.CLIOpts, error) {
ContextOpts: contextOpts, ContextOpts: contextOpts,
Input: m.Input(), Input: m.Input(),
RunningInAutomation: m.RunningInAutomation, RunningInAutomation: m.RunningInAutomation,
}, nil }, err
} }
// Operation initializes a new backend.Operation struct. // Operation initializes a new backend.Operation struct.