Skip to content
Draft
12 changes: 5 additions & 7 deletions internal/command/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,16 +222,14 @@ func (c *ApplyCommand) PrepareBackend(planFile *planfile.WrappedPlanFile, args *
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() {
mod, mDiags := c.Meta.loadSingleModule(".")
if mDiags.HasErrors() {
diags = diags.Append(mDiags)
return nil, diags
}

be, beDiags = c.Backend(&BackendOpts{
BackendConfig: backendConfig,
ViewType: viewType,
})
// Load the backend
be, beDiags = c.Meta.prepareBackend(mod)
}

diags = diags.Append(beDiags)
Expand Down
7 changes: 2 additions & 5 deletions internal/command/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,14 @@ func (c *ConsoleCommand) Run(args []string) int {

var diags tfdiags.Diagnostics

backendConfig, backendDiags := c.loadBackendConfig(configPath)
diags = diags.Append(backendDiags)
mod, diags := c.Meta.loadSingleModule(configPath)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}

// Load the backend
b, backendDiags := c.Backend(&BackendOpts{
BackendConfig: backendConfig,
})
b, backendDiags := c.Meta.prepareBackend(mod)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)
Expand Down
7 changes: 2 additions & 5 deletions internal/command/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,14 @@ func (c *GraphCommand) Run(args []string) int {

var diags tfdiags.Diagnostics

backendConfig, backendDiags := c.loadBackendConfig(configPath)
diags = diags.Append(backendDiags)
mod, diags := c.Meta.loadSingleModule(configPath)
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}

// Load the backend
b, backendDiags := c.Backend(&BackendOpts{
BackendConfig: backendConfig,
})
b, backendDiags := c.Meta.prepareBackend(mod)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)
Expand Down
4 changes: 1 addition & 3 deletions internal/command/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.Meta.prepareBackend(config.Root.Module)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)
Expand Down
31 changes: 3 additions & 28 deletions internal/command/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,34 +187,9 @@ func (c *InitCommand) initBackend(ctx context.Context, root *configs.Module, ext
return nil, true, diags
case root.StateStore != nil:
// state_store config present
// Access provider factories
ctxOpts, err := c.contextOpts()
if err != nil {
diags = diags.Append(err)
return nil, true, diags
}

if root.StateStore.ProviderAddr.IsZero() {
// This should not happen; this data is populated when parsing config,
// even for builtin providers
panic(fmt.Sprintf("unknown provider while beginning to initialize state store %q from provider %q",
root.StateStore.Type,
root.StateStore.Provider.Name))
}

var exists bool
factory, exists := ctxOpts.Providers[root.StateStore.ProviderAddr]
if !exists {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Provider unavailable",
Detail: fmt.Sprintf("The provider %s (%q) is required to initialize the %q state store, but the matching provider factory is missing. This is a bug in Terraform and should be reported.",
root.StateStore.Provider.Name,
root.StateStore.ProviderAddr,
root.StateStore.Type,
),
Subject: &root.StateStore.TypeRange,
})
factory, fDiags := c.Meta.getStateStoreProviderFactory(root.StateStore)
diags = diags.Append(fDiags)
if fDiags.HasErrors() {
return nil, true, diags
}

Expand Down
91 changes: 91 additions & 0 deletions internal/command/meta_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -1525,6 +1525,62 @@ func (m *Meta) updateSavedBackendHash(cHash int, sMgr *clistate.LocalState) tfdi
return diags
}

// prepareBackend returns an operations backend that may use a backend, cloud, or state_store block for state storage.
// 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) prepareBackend(root *configs.Module) (backendrun.OperationsBackend, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics

var opts *BackendOpts
switch {
case root.Backend != nil:
opts = &BackendOpts{
BackendConfig: root.Backend,
}
case root.CloudConfig != nil:
backendConfig := root.CloudConfig.ToBackendConfig()
opts = &BackendOpts{
BackendConfig: &backendConfig,
}
case root.StateStore != nil:
// In addition to config, use of a state_store requires
// provider factory and provider locks data
factory, fDiags := m.getStateStoreProviderFactory(root.StateStore)
diags = diags.Append(fDiags)
if fDiags.HasErrors() {
return nil, diags
}

// TODO - Use locks from here in opts below
_, lDiags := m.lockedDependencies()
diags = diags.Append(lDiags)
if lDiags.HasErrors() {
return nil, diags
}

opts = &BackendOpts{
StateStoreConfig: root.StateStore,
ProviderFactory: factory,
// TODO - update once other work is merged into main
// Locks: locks,
}
default:
// there is no config; defaults to local state storage
opts = &BackendOpts{}
}
opts.Init = false // To be explicit- this method should not be used in init commands!

// Load the backend
be, beDiags := m.Backend(opts)
diags = diags.Append(beDiags)
if beDiags.HasErrors() {
return nil, diags
}

return be, diags
}

//-------------------------------------------------------------------
// Reusable helper functions for backend management
//-------------------------------------------------------------------
Expand Down Expand Up @@ -1746,6 +1802,41 @@ func (m *Meta) assertSupportedCloudInitOptions(mode cloud.ConfigChangeMode) tfdi
return diags
}

func (m *Meta) getStateStoreProviderFactory(config *configs.StateStore) (providers.Factory, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics

if config.ProviderAddr.IsZero() {
// This should not happen; this data is populated when parsing config,
// even for builtin providers
panic(fmt.Sprintf("unknown provider while beginning to initialize state store %q from provider %q",
config.Type,
config.Provider.Name))
}

ctxOpts, err := m.contextOpts()
if err != nil {
diags = diags.Append(err)
return nil, diags
}

factory, exists := ctxOpts.Providers[config.ProviderAddr]
if !exists {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Provider unavailable",
Detail: fmt.Sprintf("The provider %s (%q) is required to initialize the %q state store, but the matching provider factory is missing. This is a bug in Terraform and should be reported.",
config.Provider.Name,
config.ProviderAddr,
config.Type,
),
Subject: &config.TypeRange,
})
return nil, diags
}

return factory, diags
}

//-------------------------------------------------------------------
// Output constants and initialization code
//-------------------------------------------------------------------
Expand Down
9 changes: 7 additions & 2 deletions internal/command/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,15 @@ func (c *OutputCommand) Outputs(statePath string) (map[string]*states.OutputValu
c.Meta.statePath = statePath
}

mod, diags := c.Meta.loadSingleModule(".")
if diags.HasErrors() {
return nil, diags
}

// Load the backend
b, backendDiags := c.Backend(nil)
b, backendDiags := c.Meta.prepareBackend(mod)
diags = diags.Append(backendDiags)
if diags.HasErrors() {
if backendDiags.HasErrors() {
return nil, diags
}

Expand Down
7 changes: 2 additions & 5 deletions internal/command/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,16 +124,13 @@ 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(".")
mod, diags := c.Meta.loadSingleModule(".")
if diags.HasErrors() {
return nil, diags
}

// Load the backend
be, beDiags := c.Backend(&BackendOpts{
BackendConfig: backendConfig,
ViewType: viewType,
})
be, beDiags := c.Meta.prepareBackend(mod)
diags = diags.Append(beDiags)
if beDiags.HasErrors() {
return nil, diags
Expand Down
4 changes: 1 addition & 3 deletions internal/command/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,7 @@ func (c *ProvidersCommand) Run(args []string) int {
}

// Load the backend
b, backendDiags := c.Backend(&BackendOpts{
BackendConfig: config.Module.Backend,
})
b, backendDiags := c.Meta.prepareBackend(config.Root.Module)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)
Expand Down
8 changes: 7 additions & 1 deletion internal/command/providers_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,14 @@ func (c *ProvidersSchemaCommand) Run(args []string) int {

var diags tfdiags.Diagnostics

mod, diags := c.Meta.loadSingleModule(".")
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}

// Load the backend
b, backendDiags := c.Backend(nil)
b, backendDiags := c.Meta.prepareBackend(mod)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(diags)
Expand Down
11 changes: 6 additions & 5 deletions internal/command/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,16 +153,17 @@ 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(".")
mod, diags := c.Meta.loadSingleModule(".")
if diags.HasErrors() {
return nil, diags
}

// Load the backend
be, beDiags := c.Backend(&BackendOpts{
BackendConfig: backendConfig,
ViewType: viewType,
})
be, beDiags := c.Meta.prepareBackend(mod)
diags = diags.Append(beDiags)
if beDiags.HasErrors() {
return nil, diags
}
diags = diags.Append(beDiags)
if beDiags.HasErrors() {
return nil, diags
Expand Down
7 changes: 2 additions & 5 deletions internal/command/refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,16 +117,13 @@ 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(".")
mod, diags := c.Meta.loadSingleModule(".")
if diags.HasErrors() {
return nil, diags
}

// Load the backend
be, beDiags := c.Backend(&BackendOpts{
BackendConfig: backendConfig,
ViewType: viewType,
})
be, beDiags := c.Meta.prepareBackend(mod)
diags = diags.Append(beDiags)
if beDiags.HasErrors() {
return nil, diags
Expand Down
18 changes: 14 additions & 4 deletions internal/command/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,13 @@ func (c *ShowCommand) show(path string) (*plans.Plan, *cloudplan.RemotePlanJSON,
func (c *ShowCommand) showFromLatestStateSnapshot() (*statefile.File, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics

mod, diags := c.Meta.loadSingleModule(".")
if diags.HasErrors() {
return nil, diags
}

// Load the backend
b, backendDiags := c.Backend(nil)
b, backendDiags := c.Meta.prepareBackend(mod)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
return nil, diags
Expand Down Expand Up @@ -277,10 +282,15 @@ func (c *ShowCommand) getPlanFromPath(path string) (*plans.Plan, *cloudplan.Remo
}

func (c *ShowCommand) getDataFromCloudPlan(plan *cloudplan.SavedPlanBookmark, redacted bool) (*cloudplan.RemotePlanJSON, error) {
mod, diags := c.Meta.loadSingleModule(".")
if diags.HasErrors() {
return nil, diags.Err()
}

// Set up the backend
b, backendDiags := c.Backend(nil)
if backendDiags.HasErrors() {
return nil, errUnusable(backendDiags.Err(), "cloud plan")
b, diags := c.Meta.prepareBackend(mod)
if diags.HasErrors() {
return nil, errUnusable(diags.Err(), "cloud plan")
}
Comment on lines 284 to 294
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This currently doesn't have any test coverage

// Cloud plans only work if we're cloud.
cl, ok := b.(*cloud.Cloud)
Expand Down
13 changes: 9 additions & 4 deletions internal/command/state_identities.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/hashicorp/cli"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/tfdiags"
)

// StateIdentitiesCommand is a Command implementation that lists the resource identities
Expand Down Expand Up @@ -46,10 +45,17 @@ func (c *StateIdentitiesCommand) Run(args []string) int {
c.Meta.statePath = statePath
}

mod, diags := c.Meta.loadSingleModule(".")
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}

// Load the backend
b, backendDiags := c.Backend(nil)
b, backendDiags := c.Meta.prepareBackend(mod)
diags = diags.Append(backendDiags)
if backendDiags.HasErrors() {
c.showDiagnostics(backendDiags)
c.showDiagnostics(diags)
return 1
}

Expand Down Expand Up @@ -79,7 +85,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 {
Expand Down
Loading
Loading