command: recompute config hash with ConfigFile set

Fixes #12749

If we merge in an extra partial config we need to recompute the hash to
compare with the old value to detect that change.

This hash needs to NOT be stored and just used as a temporary. We want
to keep the original hash in the state so that we don't detect a change
from the config (since the config will always be partial).
This commit is contained in:
Mitchell Hashimoto 2017-03-16 11:47:59 -07:00
parent ead7a3758b
commit 81639480fb
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
5 changed files with 73 additions and 3 deletions

View File

@ -321,6 +321,38 @@ func TestInit_backendConfigFile(t *testing.T) {
} }
} }
func TestInit_backendConfigFileChange(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
copy.CopyDir(testFixturePath("init-backend-config-file-change"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()
// Ask input
defer testInputMap(t, map[string]string{
"backend-migrate-to-new": "no",
})()
ui := new(cli.MockUi)
c := &InitCommand{
Meta: Meta{
ContextOpts: testCtxConfig(testProvider()),
Ui: ui,
},
}
args := []string{"-backend-config", "input.config"}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
}
// Read our saved backend config and verify we have our settings
state := testStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
if v := state.Backend.Config["path"]; v != "hello" {
t.Fatalf("bad: %#v", v)
}
}
func TestInit_copyBackendDst(t *testing.T) { func TestInit_copyBackendDst(t *testing.T) {
// Create a temporary working directory that is empty // Create a temporary working directory that is empty
td := tempDir(t) td := tempDir(t)

View File

@ -302,6 +302,16 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, error) {
return nil, fmt.Errorf("Error loading backend config: %s", err) return nil, fmt.Errorf("Error loading backend config: %s", err)
} }
// cHash defaults to zero unless c is set
var cHash uint64
if c != nil {
// We need to rehash to get the value since we may have merged the
// config with an extra ConfigFile. We don't do this when merging
// because we do want the ORIGINAL value on c so that we store
// that to not detect drift. This is covered in tests.
cHash = c.Rehash()
}
// Get the path to where we store a local cache of backend configuration // Get the path to where we store a local cache of backend configuration
// if we're using a remote backend. This may not yet exist which means // if we're using a remote backend. This may not yet exist which means
// we haven't used a non-local backend before. That is okay. // we haven't used a non-local backend before. That is okay.
@ -384,7 +394,7 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, error) {
case c != nil && s.Remote.Empty() && !s.Backend.Empty(): case c != nil && s.Remote.Empty() && !s.Backend.Empty():
// If our configuration is the same, then we're just initializing // If our configuration is the same, then we're just initializing
// a previously configured remote backend. // a previously configured remote backend.
if !s.Backend.Empty() && s.Backend.Hash == c.Hash { if !s.Backend.Empty() && s.Backend.Hash == cHash {
return m.backend_C_r_S_unchanged(c, sMgr) return m.backend_C_r_S_unchanged(c, sMgr)
} }
@ -398,7 +408,7 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, error) {
log.Printf( log.Printf(
"[WARN] command: backend config change! saved: %d, new: %d", "[WARN] command: backend config change! saved: %d, new: %d",
s.Backend.Hash, c.Hash) s.Backend.Hash, cHash)
return m.backend_C_r_S_changed(c, sMgr, true) return m.backend_C_r_S_changed(c, sMgr, true)
// Configuring a backend for the first time while having legacy // Configuring a backend for the first time while having legacy
@ -420,7 +430,7 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, error) {
case c != nil && !s.Remote.Empty() && !s.Backend.Empty(): case c != nil && !s.Remote.Empty() && !s.Backend.Empty():
// If the hashes are the same, we have a legacy remote state with // If the hashes are the same, we have a legacy remote state with
// an unchanged stored backend state. // an unchanged stored backend state.
if s.Backend.Hash == c.Hash { if s.Backend.Hash == cHash {
if !opts.Init { if !opts.Init {
initReason := fmt.Sprintf( initReason := fmt.Sprintf(
"Legacy remote state found with configured backend %q", "Legacy remote state found with configured backend %q",

View File

@ -0,0 +1,22 @@
{
"version": 3,
"serial": 0,
"lineage": "666f9301-7e65-4b19-ae23-71184bb19b03",
"backend": {
"type": "local",
"config": {
"path": "local-state.tfstate"
},
"hash": 9073424445967744180
},
"modules": [
{
"path": [
"root"
],
"outputs": {},
"resources": {},
"depends_on": []
}
]
}

View File

@ -0,0 +1 @@
path = "hello"

View File

@ -0,0 +1,5 @@
terraform {
backend "local" {
path = "local-state.tfstate"
}
}