PSS: Update how commands access backends, so both `backend` and `state_store` configuration can be used (#37569)

* Add a generic method for loading an operations backend in non-init commands

* Refactor commands to use new prepareBackend method: group 1

* Refactor commands to use new prepareBackend method: group 2, where config parsing needs to be explicitly added

* Refactor commands to use new prepareBackend method: group 3, where we can use already parsed config

* Additional, more nested, places where logic for accessing backends needs to be refactored

* Remove duplicated comment

* Add test coverage of `(m *Meta) prepareBackend()`

* Add TODO related to using plans for backend/state_store config in apply commands

* Add `testStateStoreMockWithChunkNegotiation` test helper

* Add assertions to tests about the backend (remote-state, local, etc) in use within operations backend

* Stop prepareBackend taking locks as argument

* Code comment in prepareBackend

* Replace c.Meta.prepareBackend with c.prepareBackend

* Change `c.Meta.loadSingleModule` to `c.loadSingleModule`

* Rename (Meta).prepareBackend to  (Meta).backend, update godoc comment to make relationship to (Meta).Backend more obvious.

* Revert change from config.Module to config.Root.Module

* Update `(m *Meta) backend` method to parse config itself, and also to adhere to calling code's viewtype instructions

* Update all tests and calling code following previous commit

* Change how an operations backend is obtained by autocomplete code

* Update autocomplete to return nil if no workspace names are returned from the backend

* Add test coverage for autocompleting workspace names when using a pluggable state store

* Fix output command: pass view type data to new `backend` method

* Fix in plan command: pass correct view type to `backend` method

* Fix `providers schema` command to use correct viewtype when preparing a backend
pull/37864/head
Sarah French 6 months ago committed by GitHub
parent f0a2953bd0
commit f5a28cfa8b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -219,19 +219,15 @@ func (c *ApplyCommand) PrepareBackend(planFile *planfile.WrappedPlanFile, args *
))
return nil, diags
}
// TODO: Update BackendForLocalPlan to use state storage, and plan to be able to contain State Store config details
be, beDiags = c.BackendForLocalPlan(plan.Backend)
} else {
// Both new plans and saved cloud plans load their backend from config.
backendConfig, configDiags := c.loadBackendConfig(".")
diags = diags.Append(configDiags)
if configDiags.HasErrors() {
return nil, diags
}
be, beDiags = c.Backend(&BackendOpts{
BackendConfig: backendConfig,
ViewType: viewType,
})
// Load the backend
//
// Note: Both new plans and saved cloud plans load their backend from config,
// hence the config parsing in the method below.
be, beDiags = c.backend(".", viewType)
}
diags = diags.Append(beDiags)

@ -4,6 +4,7 @@
package command
import (
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/posener/complete"
)
@ -48,19 +49,18 @@ func (m *Meta) completePredictWorkspaceName() complete.Predictor {
return nil
}
backendConfig, diags := m.loadBackendConfig(configPath)
b, diags := m.backend(configPath, arguments.ViewHuman)
if diags.HasErrors() {
return nil
}
b, diags := m.Backend(&BackendOpts{
BackendConfig: backendConfig,
})
if diags.HasErrors() {
names, _ := b.Workspaces()
if len(names) == 0 {
// Presence of the "default" isn't always guaranteed
// Backends will report it as always existing, pluggable
// state stores will only do so if it _actually_ exists.
return nil
}
names, _ := b.Workspaces()
return names
})
}

@ -4,37 +4,124 @@
package command
import (
"io/ioutil"
"os"
"reflect"
"testing"
"github.com/hashicorp/cli"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/command/workdir"
"github.com/hashicorp/terraform/internal/providers"
"github.com/posener/complete"
)
func TestMetaCompletePredictWorkspaceName(t *testing.T) {
// Create a temporary working directory that is empty
td := t.TempDir()
os.MkdirAll(td, 0755)
t.Chdir(td)
// make sure a vars file doesn't interfere
err := ioutil.WriteFile(DefaultVarsFilename, nil, 0644)
if err != nil {
t.Fatal(err)
}
t.Run("test autocompletion using the local backend", func(t *testing.T) {
// Create a temporary working directory that is empty
td := t.TempDir()
t.Chdir(td)
ui := new(cli.MockUi)
meta := &Meta{Ui: ui}
ui := new(cli.MockUi)
meta := &Meta{Ui: ui}
predictor := meta.completePredictWorkspaceName()
predictor := meta.completePredictWorkspaceName()
got := predictor.Predict(complete.Args{
Last: "",
got := predictor.Predict(complete.Args{
Last: "",
})
want := []string{"default"}
if !reflect.DeepEqual(got, want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want)
}
})
t.Run("test autocompletion using a state store", func(t *testing.T) {
// Create a temporary working directory with state_store config
td := t.TempDir()
testCopyDir(t, testFixturePath("state-store-unchanged"), td)
t.Chdir(td)
// Set up pluggable state store provider mock
mockProvider := mockPluggableStateStorageProvider()
// Mock the existence of workspaces
mockProvider.MockStates = map[string]interface{}{
"default": true,
"foobar": true,
}
mockProviderAddress := addrs.NewDefaultProvider("test")
providerSource, close := newMockProviderSource(t, map[string][]string{
"hashicorp/test": {"1.0.0"},
})
defer close()
ui := new(cli.MockUi)
view, _ := testView(t)
wd := workdir.NewDir(".")
wd.OverrideOriginalWorkingDir(td)
meta := Meta{
WorkingDir: wd, // Use the test's temp dir
Ui: ui,
View: view,
AllowExperimentalFeatures: true,
testingOverrides: &testingOverrides{
Providers: map[addrs.Provider]providers.Factory{
mockProviderAddress: providers.FactoryFixed(mockProvider),
},
},
ProviderSource: providerSource,
}
predictor := meta.completePredictWorkspaceName()
got := predictor.Predict(complete.Args{
Last: "",
})
want := []string{"default", "foobar"}
if !reflect.DeepEqual(got, want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want)
}
})
t.Run("test autocompletion using a state store containing no workspaces", func(t *testing.T) {
// Create a temporary working directory with state_store config
td := t.TempDir()
testCopyDir(t, testFixturePath("state-store-unchanged"), td)
t.Chdir(td)
// Set up pluggable state store provider mock
mockProvider := mockPluggableStateStorageProvider()
// No workspaces exist in the mock
mockProvider.MockStates = map[string]interface{}{}
mockProviderAddress := addrs.NewDefaultProvider("test")
providerSource, close := newMockProviderSource(t, map[string][]string{
"hashicorp/test": {"1.0.0"},
})
defer close()
ui := new(cli.MockUi)
view, _ := testView(t)
wd := workdir.NewDir(".")
wd.OverrideOriginalWorkingDir(td)
meta := Meta{
WorkingDir: wd, // Use the test's temp dir
Ui: ui,
View: view,
AllowExperimentalFeatures: true,
testingOverrides: &testingOverrides{
Providers: map[addrs.Provider]providers.Factory{
mockProviderAddress: providers.FactoryFixed(mockProvider),
},
},
ProviderSource: providerSource,
}
predictor := meta.completePredictWorkspaceName()
got := predictor.Predict(complete.Args{
Last: "",
})
if got != nil {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, nil)
}
})
want := []string{"default"}
if !reflect.DeepEqual(got, want) {
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want)
}
}

@ -53,17 +53,8 @@ func (c *ConsoleCommand) Run(args []string) int {
var diags tfdiags.Diagnostics
backendConfig, backendDiags := c.loadBackendConfig(configPath)
diags = diags.Append(backendDiags)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
// Load the backend
b, backendDiags := c.Backend(&BackendOpts{
BackendConfig: backendConfig,
})
b, backendDiags := c.backend(configPath, arguments.ViewHuman)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)

@ -68,17 +68,8 @@ func (c *GraphCommand) Run(args []string) int {
var diags tfdiags.Diagnostics
backendConfig, backendDiags := c.loadBackendConfig(configPath)
diags = diags.Append(backendDiags)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
// Load the backend
b, backendDiags := c.Backend(&BackendOpts{
BackendConfig: backendConfig,
})
b, backendDiags := c.backend(".", arguments.ViewHuman)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)

@ -164,9 +164,7 @@ func (c *ImportCommand) Run(args []string) int {
}
// Load the backend
b, backendDiags := c.Backend(&BackendOpts{
BackendConfig: config.Module.Backend,
})
b, backendDiags := c.backend(".", arguments.ViewHuman)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)

@ -1580,6 +1580,82 @@ func (m *Meta) updateSavedBackendHash(cHash int, sMgr *clistate.LocalState) tfdi
return diags
}
// backend returns an operations backend that may use a backend, cloud, or state_store block for state storage.
// Based on the supplied config, it prepares arguments to pass into (Meta).Backend, which returns the operations backend.
//
// This method should be used in NON-init operations only; it's incapable of processing new init command CLI flags used
// for partial configuration, however it will use the backend state file to use partial configuration from a previous
// init command.
func (m *Meta) backend(configPath string, viewType arguments.ViewType) (backendrun.OperationsBackend, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
if configPath == "" {
configPath = "."
}
// Only return error diagnostics at this point. Any warnings will be caught
// again later and duplicated in the output.
root, mDiags := m.loadSingleModule(configPath)
if mDiags.HasErrors() {
diags = diags.Append(mDiags)
return nil, diags
}
var opts *BackendOpts
switch {
case root.Backend != nil:
opts = &BackendOpts{
BackendConfig: root.Backend,
ViewType: viewType,
}
case root.CloudConfig != nil:
backendConfig := root.CloudConfig.ToBackendConfig()
opts = &BackendOpts{
BackendConfig: &backendConfig,
ViewType: viewType,
}
case root.StateStore != nil:
// In addition to config, use of a state_store requires
// provider factory and provider locks data
locks, lDiags := m.lockedDependencies()
diags = diags.Append(lDiags)
if lDiags.HasErrors() {
return nil, diags
}
factory, fDiags := m.GetStateStoreProviderFactory(root.StateStore, locks)
diags = diags.Append(fDiags)
if fDiags.HasErrors() {
return nil, diags
}
opts = &BackendOpts{
StateStoreConfig: root.StateStore,
ProviderFactory: factory,
Locks: locks,
ViewType: viewType,
}
default:
// there is no config; defaults to local state storage
opts = &BackendOpts{
ViewType: viewType,
}
}
// This method should not be used for init commands,
// so we always set this value as false.
opts.Init = false
// Load the backend
be, beDiags := m.Backend(opts)
diags = diags.Append(beDiags)
if beDiags.HasErrors() {
return nil, diags
}
return be, diags
}
//-------------------------------------------------------------------
// State Store Config Scenarios
// The functions below cover handling all the various scenarios that

@ -23,6 +23,7 @@ import (
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/backend"
"github.com/hashicorp/terraform/internal/cloud"
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/hashicorp/terraform/internal/command/clistate"
"github.com/hashicorp/terraform/internal/command/workdir"
"github.com/hashicorp/terraform/internal/configs"
@ -2962,6 +2963,126 @@ func Test_getStateStorageProviderVersion(t *testing.T) {
})
}
func TestMetaBackend_prepareBackend(t *testing.T) {
t.Run("it returns a cloud backend from cloud backend config", func(t *testing.T) {
// Create a temporary working directory with cloud configuration in
td := t.TempDir()
testCopyDir(t, testFixturePath("cloud-config"), td)
t.Chdir(td)
m := testMetaBackend(t, nil)
// We cannot initialize a cloud backend so we instead check
// the init error is referencing HCP Terraform
_, bDiags := m.backend(td, arguments.ViewHuman)
if !bDiags.HasErrors() {
t.Fatal("expected error but got none")
}
wantErr := "HCP Terraform or Terraform Enterprise initialization required: please run \"terraform init\""
if !strings.Contains(bDiags.Err().Error(), wantErr) {
t.Fatalf("expected error to contain %q, but got: %q",
wantErr,
bDiags.Err())
}
})
t.Run("it returns a backend from backend config", func(t *testing.T) {
// Create a temporary working directory with backend configuration in
td := t.TempDir()
testCopyDir(t, testFixturePath("backend-unchanged"), td)
t.Chdir(td)
m := testMetaBackend(t, nil)
b, bDiags := m.backend(td, arguments.ViewHuman)
if bDiags.HasErrors() {
t.Fatal("unexpected error: ", bDiags.Err())
}
if _, ok := b.(*local.Local); !ok {
t.Fatal("expected returned operations backend to be a Local backend")
}
// Check the type of backend inside the Local via schema
// In this case a `local` backend should have been returned by default.
//
// Look for the path attribute.
schema := b.ConfigSchema()
if _, ok := schema.Attributes["path"]; !ok {
t.Fatalf("expected the operations backend to report the schema of a local backend, but got something unexpected: %#v", schema)
}
})
t.Run("it returns a local backend when there is empty configuration", func(t *testing.T) {
td := t.TempDir()
testCopyDir(t, testFixturePath("empty"), td)
t.Chdir(td)
m := testMetaBackend(t, nil)
b, bDiags := m.backend(td, arguments.ViewHuman)
if bDiags.HasErrors() {
t.Fatal("unexpected error: ", bDiags.Err())
}
if _, ok := b.(*local.Local); !ok {
t.Fatal("expected returned operations backend to be a Local backend")
}
// Check the type of backend inside the Local via schema
// In this case a `local` backend should have been returned by default.
//
// Look for the path attribute.
schema := b.ConfigSchema()
if _, ok := schema.Attributes["path"]; !ok {
t.Fatalf("expected the operations backend to report the schema of a local backend, but got something unexpected: %#v", schema)
}
})
t.Run("it returns a state_store from state_store config", func(t *testing.T) {
// Create a temporary working directory with backend configuration in
td := t.TempDir()
testCopyDir(t, testFixturePath("state-store-unchanged"), td)
t.Chdir(td)
m := testMetaBackend(t, nil)
m.AllowExperimentalFeatures = true
mock := testStateStoreMockWithChunkNegotiation(t, 12345) // chunk size needs to be set, value is arbitrary
m.testingOverrides = &testingOverrides{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): providers.FactoryFixed(mock),
},
}
// Prepare appropriate locks; config uses a hashicorp/test provider @ v1.2.3
locks := depsfile.NewLocks()
providerAddr := addrs.MustParseProviderSourceString("registry.terraform.io/hashicorp/test")
constraint, err := providerreqs.ParseVersionConstraints(">1.0.0")
if err != nil {
t.Fatalf("test setup failed when making constraint: %s", err)
}
locks.SetProvider(
providerAddr,
versions.MustParseVersion("1.2.3"),
constraint,
[]providerreqs.Hash{""},
)
b, bDiags := m.backend(td, arguments.ViewHuman)
if bDiags.HasErrors() {
t.Fatalf("unexpected error: %s", bDiags.Err())
}
if _, ok := b.(*local.Local); !ok {
t.Fatal("expected returned operations backend to be a Local backend")
}
// Check the state_store inside the Local via schema
// Look for the mock state_store's attribute called `value`.
schema := b.ConfigSchema()
if _, ok := schema.Attributes["value"]; !ok {
t.Fatalf("expected the operations backend to report the schema of the state_store, but got something unexpected: %#v", schema)
}
})
}
func testMetaBackend(t *testing.T, args []string) *Meta {
var m Meta
m.Ui = new(cli.MockUi)
@ -3011,6 +3132,21 @@ func testStateStoreMock(t *testing.T) *testing_provider.MockProvider {
}
}
// testStateStoreMockWithChunkNegotiation is just like testStateStoreMock but the returned mock is set up so it'll be configured
// without this error: `Failed to negotiate acceptable chunk size`
//
// This is meant to be a convenience method when a test is definitely not testing anything related to state store configuration.
func testStateStoreMockWithChunkNegotiation(t *testing.T, chunkSize int64) *testing_provider.MockProvider {
t.Helper()
mock := testStateStoreMock(t)
mock.ConfigureStateStoreResponse = &providers.ConfigureStateStoreResponse{
Capabilities: providers.StateStoreServerCapabilities{
ChunkSize: chunkSize,
},
}
return mock
}
func configBodyForTest(t *testing.T, config string) hcl.Body {
t.Helper()
f, diags := hclsyntax.ParseConfig([]byte(config), "", hcl.Pos{Line: 1, Column: 1})

@ -35,7 +35,7 @@ func (c *OutputCommand) Run(rawArgs []string) int {
view := views.NewOutput(args.ViewType, c.View)
// Fetch data from state
outputs, diags := c.Outputs(args.StatePath)
outputs, diags := c.Outputs(args.StatePath, args.ViewType)
if diags.HasErrors() {
view.Diagnostics(diags)
return 1
@ -54,7 +54,7 @@ func (c *OutputCommand) Run(rawArgs []string) int {
return 0
}
func (c *OutputCommand) Outputs(statePath string) (map[string]*states.OutputValue, tfdiags.Diagnostics) {
func (c *OutputCommand) Outputs(statePath string, view arguments.ViewType) (map[string]*states.OutputValue, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
// Allow state path override
@ -63,9 +63,9 @@ func (c *OutputCommand) Outputs(statePath string) (map[string]*states.OutputValu
}
// Load the backend
b, backendDiags := c.Backend(nil)
b, backendDiags := c.backend(".", view)
diags = diags.Append(backendDiags)
if diags.HasErrors() {
if backendDiags.HasErrors() {
return nil, diags
}

@ -124,18 +124,9 @@ func (c *PlanCommand) PrepareBackend(args *arguments.State, viewType arguments.V
// difficult but would make their use easier to understand.
c.Meta.applyStateArguments(args)
backendConfig, diags := c.loadBackendConfig(".")
if diags.HasErrors() {
return nil, diags
}
// Load the backend
be, beDiags := c.Backend(&BackendOpts{
BackendConfig: backendConfig,
ViewType: viewType,
})
diags = diags.Append(beDiags)
if beDiags.HasErrors() {
be, diags := c.backend(".", viewType)
if diags.HasErrors() {
return nil, diags
}

@ -10,6 +10,7 @@ import (
"github.com/xlab/treeprint"
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/getproviders"
"github.com/hashicorp/terraform/internal/tfdiags"
@ -81,9 +82,7 @@ func (c *ProvidersCommand) Run(args []string) int {
}
// Load the backend
b, backendDiags := c.Backend(&BackendOpts{
BackendConfig: config.Module.Backend,
})
b, backendDiags := c.backend(".", arguments.ViewHuman)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)

@ -45,6 +45,7 @@ func (c *ProvidersSchemaCommand) Run(args []string) int {
cmdFlags.Usage()
return 1
}
viewType := arguments.ViewJSON // See above; enforced use of JSON output
// Check for user-supplied plugin path
var err error
@ -56,7 +57,7 @@ func (c *ProvidersSchemaCommand) Run(args []string) int {
var diags tfdiags.Diagnostics
// Load the backend
b, backendDiags := c.Backend(nil)
b, backendDiags := c.backend(".", viewType)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)

@ -154,21 +154,8 @@ func (c *QueryCommand) Run(rawArgs []string) int {
}
func (c *QueryCommand) PrepareBackend(args *arguments.State, viewType arguments.ViewType) (backendrun.OperationsBackend, tfdiags.Diagnostics) {
backendConfig, diags := c.loadBackendConfig(".")
if diags.HasErrors() {
return nil, diags
}
// Load the backend
be, beDiags := c.Backend(&BackendOpts{
BackendConfig: backendConfig,
ViewType: viewType,
})
diags = diags.Append(beDiags)
if beDiags.HasErrors() {
return nil, diags
}
be, diags := c.backend(".", viewType)
return be, diags
}

@ -117,18 +117,9 @@ func (c *RefreshCommand) PrepareBackend(args *arguments.State, viewType argument
// difficult but would make their use easier to understand.
c.Meta.applyStateArguments(args)
backendConfig, diags := c.loadBackendConfig(".")
if diags.HasErrors() {
return nil, diags
}
// Load the backend
be, beDiags := c.Backend(&BackendOpts{
BackendConfig: backendConfig,
ViewType: viewType,
})
diags = diags.Append(beDiags)
if beDiags.HasErrors() {
be, diags := c.backend(".", viewType)
if diags.HasErrors() {
return nil, diags
}

@ -150,7 +150,7 @@ func (c *ShowCommand) showFromLatestStateSnapshot() (*statefile.File, tfdiags.Di
var diags tfdiags.Diagnostics
// Load the backend
b, backendDiags := c.Backend(nil)
b, backendDiags := c.backend(".", c.viewType)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
return nil, diags
@ -278,9 +278,9 @@ func (c *ShowCommand) getPlanFromPath(path string) (*plans.Plan, *cloudplan.Remo
func (c *ShowCommand) getDataFromCloudPlan(plan *cloudplan.SavedPlanBookmark, redacted bool) (*cloudplan.RemotePlanJSON, error) {
// Set up the backend
b, backendDiags := c.Backend(nil)
if backendDiags.HasErrors() {
return nil, errUnusable(backendDiags.Err(), "cloud plan")
b, diags := c.backend(".", c.viewType)
if diags.HasErrors() {
return nil, errUnusable(diags.Err(), "cloud plan")
}
// Cloud plans only work if we're cloud.
cl, ok := b.(*cloud.Cloud)

@ -10,8 +10,8 @@ import (
"github.com/hashicorp/cli"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/tfdiags"
)
// StateIdentitiesCommand is a Command implementation that lists the resource identities
@ -41,15 +41,16 @@ func (c *StateIdentitiesCommand) Run(args []string) int {
cmdFlags.Usage()
return 1
}
view := arguments.ViewJSON // See above
if statePath != "" {
c.Meta.statePath = statePath
}
// Load the backend
b, backendDiags := c.Backend(nil)
if backendDiags.HasErrors() {
c.showDiagnostics(backendDiags)
b, diags := c.backend(".", view)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
@ -79,7 +80,6 @@ func (c *StateIdentitiesCommand) Run(args []string) int {
}
var addrs []addrs.AbsResourceInstance
var diags tfdiags.Diagnostics
if len(args) == 0 {
addrs, diags = c.lookupAllResourceInstanceAddrs(state)
} else {

@ -9,8 +9,8 @@ import (
"github.com/hashicorp/cli"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/tfdiags"
)
// StateListCommand is a Command implementation that lists the resources
@ -37,9 +37,9 @@ func (c *StateListCommand) Run(args []string) int {
}
// Load the backend
b, backendDiags := c.Backend(nil)
if backendDiags.HasErrors() {
c.showDiagnostics(backendDiags)
b, diags := c.backend(".", arguments.ViewHuman)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
@ -69,7 +69,6 @@ func (c *StateListCommand) Run(args []string) int {
}
var addrs []addrs.AbsResourceInstance
var diags tfdiags.Diagnostics
if len(args) == 0 {
addrs, diags = c.lookupAllResourceInstanceAddrs(state)
} else {

@ -9,6 +9,7 @@ import (
"time"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/states/statemgr"
"github.com/hashicorp/terraform/internal/tfdiags"
@ -25,7 +26,7 @@ type StateMeta struct {
// the backend, but changes the way that backups are done. This configures
// backups to be timestamped rather than just the original state path plus a
// backup path.
func (c *StateMeta) State() (statemgr.Full, error) {
func (c *StateMeta) State(view arguments.ViewType) (statemgr.Full, error) {
var realState statemgr.Full
backupPath := c.backupPath
stateOutPath := c.statePath
@ -34,10 +35,11 @@ func (c *StateMeta) State() (statemgr.Full, error) {
if c.statePath != "" {
realState = statemgr.NewFilesystem(c.statePath)
} else {
// Load the backend
b, backendDiags := c.Backend(nil)
if backendDiags.HasErrors() {
return nil, backendDiags.Err()
b, diags := c.backend(".", view)
if diags.HasErrors() {
return nil, diags.Err()
}
workspace, err := c.Workspace()

@ -92,7 +92,8 @@ func (c *StateMvCommand) Run(args []string) int {
}
// Read the from state
stateFromMgr, err := c.State()
view := arguments.ViewHuman
stateFromMgr, err := c.State(view)
if err != nil {
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
return 1
@ -130,7 +131,7 @@ func (c *StateMvCommand) Run(args []string) int {
c.statePath = statePathOut
c.backupPath = backupPathOut
stateToMgr, err = c.State()
stateToMgr, err = c.State(view)
if err != nil {
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
return 1
@ -391,7 +392,8 @@ func (c *StateMvCommand) Run(args []string) int {
return 0 // This is as far as we go in dry-run mode
}
b, backendDiags := c.Backend(nil)
// Load the backend
b, backendDiags := c.backend(".", view)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)

@ -8,6 +8,7 @@ import (
"fmt"
"strings"
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/hashicorp/terraform/internal/states/statefile"
"github.com/hashicorp/terraform/internal/states/statemgr"
)
@ -33,9 +34,10 @@ func (c *StatePullCommand) Run(args []string) int {
}
// Load the backend
b, backendDiags := c.Backend(nil)
if backendDiags.HasErrors() {
c.showDiagnostics(backendDiags)
view := arguments.ViewHuman
b, diags := c.backend(".", view)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}

@ -16,7 +16,6 @@ import (
"github.com/hashicorp/terraform/internal/states/statefile"
"github.com/hashicorp/terraform/internal/states/statemgr"
"github.com/hashicorp/terraform/internal/terraform"
"github.com/hashicorp/terraform/internal/tfdiags"
)
// StatePushCommand is a Command implementation that allows
@ -77,9 +76,10 @@ func (c *StatePushCommand) Run(args []string) int {
}
// Load the backend
b, backendDiags := c.Backend(nil)
if backendDiags.HasErrors() {
c.showDiagnostics(backendDiags)
view := arguments.ViewHuman
b, diags := c.backend(".", view)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
@ -135,7 +135,6 @@ func (c *StatePushCommand) Run(args []string) int {
// Get schemas, if possible, before writing state
var schemas *terraform.Schemas
var diags tfdiags.Diagnostics
if isCloudMode(b) {
schemas, diags = c.MaybeGetSchemas(srcStateFile.State, nil)
}

@ -76,7 +76,8 @@ func (c *StateReplaceProviderCommand) Run(args []string) int {
}
// Initialize the state manager as configured
stateMgr, err := c.State()
view := arguments.ViewHuman
stateMgr, err := c.State(view)
if err != nil {
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
return 1
@ -164,7 +165,8 @@ func (c *StateReplaceProviderCommand) Run(args []string) int {
resource.ProviderConfig.Provider = to
}
b, backendDiags := c.Backend(nil)
// Load the backend
b, backendDiags := c.backend(".", view)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)

@ -48,7 +48,8 @@ func (c *StateRmCommand) Run(args []string) int {
}
// Get the state
stateMgr, err := c.State()
view := arguments.ViewHuman
stateMgr, err := c.State(view)
if err != nil {
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
return 1
@ -115,7 +116,8 @@ func (c *StateRmCommand) Run(args []string) int {
return 0 // This is as far as we go in dry-run mode
}
b, backendDiags := c.Backend(nil)
// Load the backend
b, backendDiags := c.backend(".", view)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)

@ -48,9 +48,10 @@ func (c *StateShowCommand) Run(args []string) int {
}
// Load the backend
b, backendDiags := c.Backend(nil)
if backendDiags.HasErrors() {
c.showDiagnostics(backendDiags)
view := arguments.ViewHuman
b, diags := c.backend(".", view)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}

@ -9,6 +9,7 @@ import (
"sort"
"testing"
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/hashicorp/terraform/internal/states/statemgr"
)
@ -31,7 +32,8 @@ func TestStateDefaultBackupExtension(t *testing.T) {
tmp := t.TempDir()
t.Chdir(tmp)
s, err := (&StateMeta{}).State()
view := arguments.ViewHuman
s, err := (&StateMeta{}).State(view)
if err != nil {
t.Fatal(err)
}

@ -66,7 +66,8 @@ func (c *TaintCommand) Run(args []string) int {
}
// Load the backend
b, backendDiags := c.Backend(nil)
view := arguments.ViewHuman
b, backendDiags := c.backend(".", view)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)

@ -1,7 +1,8 @@
terraform {
required_providers {
test = {
source = "hashicorp/test"
source = "hashicorp/test"
version = "1.2.3"
}
}
state_store "test_store" {

@ -8,6 +8,7 @@ import (
"fmt"
"strings"
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/hashicorp/terraform/internal/states/statemgr"
"github.com/hashicorp/cli"
@ -51,17 +52,9 @@ func (c *UnlockCommand) Run(args []string) int {
var diags tfdiags.Diagnostics
backendConfig, backendDiags := c.loadBackendConfig(configPath)
diags = diags.Append(backendDiags)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
// Load the backend
b, backendDiags := c.Backend(&BackendOpts{
BackendConfig: backendConfig,
})
view := arguments.ViewHuman
b, backendDiags := c.backend(configPath, view)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)

@ -56,7 +56,8 @@ func (c *UntaintCommand) Run(args []string) int {
}
// Load the backend
b, backendDiags := c.Backend(nil)
view := arguments.ViewHuman
b, backendDiags := c.backend(".", view)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)

@ -59,17 +59,9 @@ func (c *WorkspaceDeleteCommand) Run(args []string) int {
var diags tfdiags.Diagnostics
backendConfig, backendDiags := c.loadBackendConfig(configPath)
diags = diags.Append(backendDiags)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
// Load the backend
b, backendDiags := c.Backend(&BackendOpts{
BackendConfig: backendConfig,
})
view := arguments.ViewHuman
b, backendDiags := c.backend(configPath, view)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)

@ -8,7 +8,7 @@ import (
"fmt"
"strings"
"github.com/hashicorp/terraform/internal/tfdiags"
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/posener/complete"
)
@ -35,21 +35,10 @@ func (c *WorkspaceListCommand) Run(args []string) int {
return 1
}
var diags tfdiags.Diagnostics
backendConfig, backendDiags := c.loadBackendConfig(configPath)
diags = diags.Append(backendDiags)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
// Load the backend
b, backendDiags := c.Backend(&BackendOpts{
BackendConfig: backendConfig,
})
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
view := arguments.ViewHuman
b, diags := c.backend(configPath, view)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
@ -58,6 +47,7 @@ func (c *WorkspaceListCommand) Run(args []string) int {
c.ignoreRemoteVersionConflict(b)
states, wDiags := b.Workspaces()
diags = diags.Append(wDiags)
if wDiags.HasErrors() {
c.Ui.Error(wDiags.Err().Error())
return 1

@ -68,19 +68,10 @@ func (c *WorkspaceNewCommand) Run(args []string) int {
var diags tfdiags.Diagnostics
backendConfig, backendDiags := c.loadBackendConfig(configPath)
diags = diags.Append(backendDiags)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
// Load the backend
b, backendDiags := c.Backend(&BackendOpts{
BackendConfig: backendConfig,
})
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
view := arguments.ViewHuman
b, diags := c.backend(configPath, view)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}

@ -8,7 +8,7 @@ import (
"strings"
"github.com/hashicorp/cli"
"github.com/hashicorp/terraform/internal/tfdiags"
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/posener/complete"
)
@ -42,15 +42,6 @@ func (c *WorkspaceSelectCommand) Run(args []string) int {
return 1
}
var diags tfdiags.Diagnostics
backendConfig, backendDiags := c.loadBackendConfig(configPath)
diags = diags.Append(backendDiags)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}
current, isOverridden := c.WorkspaceOverridden()
if isOverridden {
c.Ui.Error(envIsOverriddenSelectError)
@ -58,11 +49,9 @@ func (c *WorkspaceSelectCommand) Run(args []string) int {
}
// Load the backend
b, backendDiags := c.Backend(&BackendOpts{
BackendConfig: backendConfig,
})
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
view := arguments.ViewHuman
b, diags := c.backend(configPath, view)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}

Loading…
Cancel
Save