Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
104 changes: 104 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(SarahFrench/radeksimko): 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(SarahFrench/radeksimko): update once other work is merged into main
// Locks: locks,
}
Comment on lines +1555 to +1567
Copy link
Member Author

Choose a reason for hiding this comment

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

I have a (blocked) PR in draft that adds the ability to pass locks into the methods used for initialising an operations backend that uses state_store. While that work is in progress there are some parts of this code that aren't possible yet but I think it's reasonable to get this completed version into main, to help make other subsequent PRs smaller.

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,54 @@ 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 == nil {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing state_store configuration",
Detail: "While initializing the state store Terraform encountered a nil state_store configuration. This is an error in Terraform and should be reported.",
})
return nil, diags
}
if config.ProviderAddr.IsZero() {
// This should not happen; this data is populated when parsing config,
// even for builtin providers
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing state_store provider data",
Detail: fmt.Sprintf("While initializing the state store %q from provider %s (%q) Terraform encountered missing information about the linked provider. This is an error in Terraform and should be reported.",
config.Type,
config.Provider.Name,
config.ProviderAddr),
})
return nil, diags
}

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
Loading