command: use backend config from state when backend=false is used. (#23802)
* command: use backend config from state when backend=false is used. When a user runs `terraform init --backend=false`, terraform should inspect the state for a previously-configured backend, and use that backend, ignoring any backend config in the current configuration. If no backend is configured or there is no state, return a local backend. Fixes #16593
This commit is contained in:
parent
5d3ca8aaf1
commit
4d8fde3d6f
|
@ -295,6 +295,15 @@ func (c *InitCommand) Run(args []string) int {
|
|||
}
|
||||
back = be
|
||||
}
|
||||
} else {
|
||||
// load the previously-stored backend config
|
||||
be, backendDiags := c.Meta.backendFromState()
|
||||
diags = diags.Append(backendDiags)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(diags)
|
||||
return 1
|
||||
}
|
||||
back = be
|
||||
}
|
||||
|
||||
if back == nil {
|
||||
|
|
|
@ -562,6 +562,75 @@ func (m *Meta) backendFromConfig(opts *BackendOpts) (backend.Backend, tfdiags.Di
|
|||
}
|
||||
}
|
||||
|
||||
// backendFromState returns the initialized (not configured) backend directly
|
||||
// from the state. This should be used only when a user runs `terraform init
|
||||
// -backend=false`. This function returns a local backend if there is no state
|
||||
// or no backend configured.
|
||||
func (m *Meta) backendFromState() (backend.Backend, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
// 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
|
||||
// we haven't used a non-local backend before. That is okay.
|
||||
statePath := filepath.Join(m.DataDir(), DefaultStateFilename)
|
||||
sMgr := &state.LocalState{Path: statePath}
|
||||
if err := sMgr.RefreshState(); err != nil {
|
||||
diags = diags.Append(fmt.Errorf("Failed to load state: %s", err))
|
||||
return nil, diags
|
||||
}
|
||||
s := sMgr.State()
|
||||
if s == nil {
|
||||
// no state, so return a local backend
|
||||
log.Printf("[TRACE] Meta.Backend: backend has not previously been initialized in this working directory")
|
||||
return backendLocal.New(), diags
|
||||
}
|
||||
if s.Backend == nil {
|
||||
// s.Backend is nil, so return a local backend
|
||||
log.Printf("[TRACE] Meta.Backend: working directory was previously initialized but has no backend (is using legacy remote state?)")
|
||||
return backendLocal.New(), diags
|
||||
}
|
||||
log.Printf("[TRACE] Meta.Backend: working directory was previously initialized for %q backend", s.Backend.Type)
|
||||
|
||||
//backend init function
|
||||
if s.Backend.Type == "" {
|
||||
return backendLocal.New(), diags
|
||||
}
|
||||
f := backendInit.Backend(s.Backend.Type)
|
||||
if f == nil {
|
||||
diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), s.Backend.Type))
|
||||
return nil, diags
|
||||
}
|
||||
b := f()
|
||||
|
||||
// The configuration saved in the working directory state file is used
|
||||
// in this case, since it will contain any additional values that
|
||||
// were provided via -backend-config arguments on terraform init.
|
||||
schema := b.ConfigSchema()
|
||||
configVal, err := s.Backend.Config(schema)
|
||||
if err != nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Failed to decode current backend config",
|
||||
fmt.Sprintf("The backend configuration created by the most recent run of \"terraform init\" could not be decoded: %s. The configuration may have been initialized by an earlier version that used an incompatible configuration structure. Run \"terraform init -reconfigure\" to force re-initialization of the backend.", err),
|
||||
))
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
// Validate the config and then configure the backend
|
||||
newVal, validDiags := b.PrepareConfig(configVal)
|
||||
diags = diags.Append(validDiags)
|
||||
if validDiags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
configDiags := b.Configure(newVal)
|
||||
diags = diags.Append(configDiags)
|
||||
if configDiags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
return b, diags
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// Backend Config Scenarios
|
||||
//
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
|
||||
backendInit "github.com/hashicorp/terraform/backend/init"
|
||||
backendLocal "github.com/hashicorp/terraform/backend/local"
|
||||
backendInmem "github.com/hashicorp/terraform/backend/remote-state/inmem"
|
||||
)
|
||||
|
||||
// Test empty directory with no config/state creates a local state.
|
||||
|
@ -1771,7 +1772,7 @@ func TestMetaBackend_configureWithExtra(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// when confniguring a default local state, don't delete local state
|
||||
// when configuring a default local state, don't delete local state
|
||||
func TestMetaBackend_localDoesNotDeleteLocal(t *testing.T) {
|
||||
// Create a temporary working directory that is empty
|
||||
td := tempDir(t)
|
||||
|
@ -1860,6 +1861,30 @@ func TestMetaBackend_configToExtra(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// no config; return inmem backend stored in state
|
||||
func TestBackendFromState(t *testing.T) {
|
||||
td := tempDir(t)
|
||||
copy.CopyDir(testFixturePath("backend-from-state"), td)
|
||||
defer os.RemoveAll(td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
// Setup the meta
|
||||
m := testMetaBackend(t, nil)
|
||||
// terraform caches a small "state" file that stores the backend config.
|
||||
// This test must override m.dataDir so it loads the "terraform.tfstate" file in the
|
||||
// test directory as the backend config cache
|
||||
m.OverrideDataDir = td
|
||||
|
||||
stateBackend, diags := m.backendFromState()
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.Err())
|
||||
}
|
||||
|
||||
if _, ok := stateBackend.(*backendInmem.Backend); !ok {
|
||||
t.Fatal("did not get expected inmem backend")
|
||||
}
|
||||
}
|
||||
|
||||
func testMetaBackend(t *testing.T, args []string) *Meta {
|
||||
var m Meta
|
||||
m.Ui = new(cli.MockUi)
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"version": 3,
|
||||
"terraform_version": "0.12.0",
|
||||
"serial": 7,
|
||||
"lineage": "configured",
|
||||
"backend": {
|
||||
"type": "inmem",
|
||||
"config": {}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue