backend/remote: fix panic when loading a 0.12 cached backend (#21199)

* backend/remote: fix panic when loading a 0.12 cached backend

The remote backend schema changed in terraform 0.12, causing a panic
when 0.11 tries to load it. While we do not support downgrading
configurations, we should prevent panics where possible.

Fixes #21190
pull/21226/head
Kristin Laemmert 7 years ago committed by GitHub
parent 20e17ec86f
commit 5f2d32e4d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -149,8 +149,16 @@ func (b *Remote) configure(ctx context.Context) error {
b.hostname = d.Get("hostname").(string)
b.organization = d.Get("organization").(string)
// The backend schema for the workspaces attribute changed in terraform
// 0.12, and causes a panic when encountered by 0.11. While we don't support
// downgrading, we do want to protect users from that panic.
wsConfig := d.Get("workspaces").(*schema.Set).List()
if len(wsConfig) == 0 {
return fmt.Errorf("the cached backend configuration is not valid for this version of terraform")
}
// Get and assert the workspaces configuration block.
workspace := d.Get("workspaces").(*schema.Set).List()[0].(map[string]interface{})
workspace := wsConfig[0].(map[string]interface{})
// Get the default workspace name and prefix.
b.workspace = workspace["name"].(string)

@ -126,6 +126,42 @@ func TestRemote_config(t *testing.T) {
}
}
// TestRemote_CachedConfig is similar to the test above, but we mimic loading a
// cached backend config by skipping the validate call
func TestRemote_CachedConfig(t *testing.T) {
cases := map[string]struct {
config map[string]interface{}
err error
}{
"0.12 config": {
config: map[string]interface{}{
"hostname": "nonexisting.local",
"organization": "hashicorp",
"workspaces": map[string]interface{}{
"name": "prod",
},
},
err: errors.New("the cached backend configuration is not valid for this version of terraform"),
},
}
for name, tc := range cases {
s := testServer(t)
b := New(testDisco(s))
// Get the proper config structure
rc, err := config.NewRawConfig(tc.config)
if err != nil {
t.Fatalf("%s: error creating raw config: %v", name, err)
}
conf := terraform.NewResourceConfig(rc)
// Configure
err = b.Configure(conf)
if err != tc.err && err != nil && tc.err != nil && !strings.Contains(err.Error(), tc.err.Error()) {
t.Fatalf("%s: expected error %q, got: %q", name, tc.err, err)
}
}
}
func TestRemote_versionConstraints(t *testing.T) {
cases := map[string]struct {
config map[string]interface{}

@ -12,7 +12,7 @@ import (
"path/filepath"
"strings"
"github.com/hashicorp/go-multierror"
multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/hcl"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/command/clistate"
@ -390,16 +390,14 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, error) {
case c != nil && s.Remote.Empty() && !s.Backend.Empty():
// If our configuration is the same, then we're just initializing
// a previously configured remote backend.
if !s.Backend.Empty() {
hash := s.Backend.Hash
// on init we need an updated hash containing any extra options
// that were added after merging.
if opts.Init {
hash = s.Backend.Rehash()
}
if hash == cHash {
return m.backend_C_r_S_unchanged(c, sMgr)
}
hash := s.Backend.Hash
// on init we need an updated hash containing any extra options
// that were added after merging.
if opts.Init {
hash = s.Backend.Rehash()
}
if hash == cHash {
return m.backend_C_r_S_unchanged(c, sMgr)
}
if !opts.Init {
@ -412,7 +410,7 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, error) {
log.Printf(
"[WARN] command: backend config change! saved: %d, new: %d",
s.Backend.Hash, cHash)
hash, cHash)
return m.backend_C_r_S_changed(c, sMgr, true)
// Configuring a backend for the first time while having legacy

Loading…
Cancel
Save