Replace backend individual attributes from Meta with arguments.Backend (#4073)
Some checks are pending
build / Build for freebsd_386 (push) Waiting to run
build / Build for linux_386 (push) Waiting to run
build / Build for openbsd_386 (push) Waiting to run
build / Build for windows_386 (push) Waiting to run
build / Build for freebsd_amd64 (push) Waiting to run
build / Build for linux_amd64 (push) Waiting to run
build / Build for openbsd_amd64 (push) Waiting to run
build / Build for solaris_amd64 (push) Waiting to run
build / Build for windows_amd64 (push) Waiting to run
build / Build for freebsd_arm (push) Waiting to run
build / Build for linux_arm (push) Waiting to run
build / Build for linux_arm64 (push) Waiting to run
build / Build for darwin_amd64 (push) Waiting to run
build / Build for darwin_arm64 (push) Waiting to run
build / End-to-end Tests for linux_386 (push) Waiting to run
build / End-to-end Tests for windows_386 (push) Waiting to run
build / End-to-end Tests for darwin_amd64 (push) Waiting to run
build / End-to-end Tests for linux_amd64 (push) Waiting to run
build / End-to-end Tests for windows_amd64 (push) Waiting to run
Quick Checks / List files changed for pull request (push) Waiting to run
Quick Checks / Unit tests for linux_386 (push) Blocked by required conditions
Quick Checks / Unit tests for linux_amd64 (push) Blocked by required conditions
Quick Checks / Unit tests for windows_amd64 (push) Blocked by required conditions
Quick Checks / Unit tests for linux_arm (push) Blocked by required conditions
Quick Checks / Unit tests for darwin_arm64 (push) Blocked by required conditions
Quick Checks / Unit tests for linux_arm64 (push) Blocked by required conditions
Quick Checks / Race Tests (push) Blocked by required conditions
Quick Checks / End-to-end Tests (push) Blocked by required conditions
Quick Checks / Code Consistency Checks (push) Blocked by required conditions
Quick Checks / License Checks (push) Waiting to run
Website checks / List files changed for pull request (push) Waiting to run
Website checks / Build (push) Blocked by required conditions
Website checks / Test Installation Instructions (push) Blocked by required conditions

Signed-off-by: Andrei Ciobanu <andrei.ciobanu@opentofu.org>
This commit is contained in:
Andrei Ciobanu 2026-05-19 16:05:47 +03:00 committed by GitHub
parent a7435277ed
commit df3b5de750
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 68 additions and 107 deletions

View file

@ -12,12 +12,14 @@ import (
)
type Backend struct {
// IgnoreRemoteVersion is used with commands which write state to allow users to write remote
// state even if the remote and local OpenTofu versions don't match.
IgnoreRemoteVersion bool
// ForceInitCopy controls if the prompts for state migration should be skipped or not.
// ForceInitCopy suppresses confirmation for copying state data during init.
ForceInitCopy bool
// Reconfigure controls if the reconfiguration of the backend should happen with discarding the old configurations.
// Reconfigure forces init to ignore any stored configuration.
Reconfigure bool
// MigrateState controls if during the reconfiguration of the backend a migration should be attempted.
// MigrateState confirms the user wishes to migrate from the prior backend configuration to a new configuration.
MigrateState bool
}

View file

@ -27,7 +27,7 @@ type StateMv struct {
// Vars, Backend and State are the common extended flags
Vars *Vars
Backend Backend
Backend *Backend
State *State
}
@ -38,8 +38,9 @@ func ParseStateMv(args []string) (*StateMv, func(), tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
ret := &StateMv{
Vars: &Vars{},
State: &State{},
Vars: &Vars{},
Backend: &Backend{},
State: &State{},
}
cmdFlags := extendedFlagSet("state mv", nil, ret.Vars)

View file

@ -164,7 +164,7 @@ func stateMvArgsWithDefaults(mutate func(stateMv *StateMv)) *StateMv {
ViewType: ViewHuman,
InputEnabled: false,
},
Backend: Backend{
Backend: &Backend{
IgnoreRemoteVersion: false,
},
State: &State{

View file

@ -21,7 +21,7 @@ type StatePush struct {
// Vars, Backend and State are the common extended flags
Vars *Vars
Backend Backend
Backend *Backend
State *State
}
@ -32,8 +32,9 @@ func ParseStatePush(args []string) (*StatePush, func(), tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
ret := &StatePush{
Vars: &Vars{},
State: &State{},
Vars: &Vars{},
Backend: &Backend{},
State: &State{},
}
cmdFlags := extendedFlagSet("state push", nil, ret.Vars)
ret.Backend.AddIgnoreRemoteVersionFlag(cmdFlags)

View file

@ -166,7 +166,7 @@ func statePushArgsWithDefaults(mutate func(v *StatePush)) *StatePush {
InputEnabled: false,
},
Vars: &Vars{},
Backend: Backend{
Backend: &Backend{
IgnoreRemoteVersion: false,
Reconfigure: false,
MigrateState: false,

View file

@ -25,7 +25,7 @@ type StateReplaceProvider struct {
// Vars, Backend and State are the common extended flags
Vars *Vars
Backend Backend
Backend *Backend
State *State
}
@ -36,8 +36,9 @@ func ParseReplaceProvider(args []string) (*StateReplaceProvider, func(), tfdiags
var diags tfdiags.Diagnostics
ret := &StateReplaceProvider{
Vars: &Vars{},
State: &State{},
Vars: &Vars{},
Backend: &Backend{},
State: &State{},
}
cmdFlags := extendedFlagSet("state replace-provider", nil, ret.Vars)

View file

@ -159,7 +159,7 @@ func stateReplaceProviderArgsWithDefaults(mutate func(srp *StateReplaceProvider)
ViewType: ViewHuman,
InputEnabled: false,
},
Backend: Backend{
Backend: &Backend{
IgnoreRemoteVersion: false,
},
Vars: &Vars{},

View file

@ -22,7 +22,7 @@ type StateRm struct {
// Vars, Backend and State are the common extended flags
Vars *Vars
Backend Backend
Backend *Backend
State *State
}
@ -33,8 +33,9 @@ func ParseStateRm(args []string) (*StateRm, func(), tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
ret := &StateRm{
Vars: &Vars{},
State: &State{},
Vars: &Vars{},
Backend: &Backend{},
State: &State{},
}
cmdFlags := extendedFlagSet("state rm", nil, ret.Vars)
ret.Backend.AddIgnoreRemoteVersionFlag(cmdFlags)

View file

@ -129,7 +129,7 @@ func stateRmArgsWithDefaults(mutate func(stateRm *StateRm)) *StateRm {
ViewType: ViewHuman,
InputEnabled: false,
},
Backend: Backend{
Backend: &Backend{
IgnoreRemoteVersion: false,
},
Vars: &Vars{},

View file

@ -56,7 +56,18 @@ func (c *ImportCommand) Run(rawArgs []string) int {
}
return cli.RunResultHelp
}
c.configureBackendFlags(args)
c.Meta.variableArgs = args.Vars.All()
c.stateArgs = *args.State
c.backendArgs = *args.Backend
// TODO meta-refactor: remove this only when there is clear path of passing these from the "arguments" package to
// the place where these needs to be used
c.Meta.parallelism = args.Parallelism
// FIXME: the -input flag value is needed to initialize the backend and the
// operation, but there is no clear path to pass this value down, so we
// continue to mutate the Meta object state for now.
c.Meta.input = args.ViewOptions.InputEnabled
// Parse the provided resource address.
traversalSrc := []byte(args.ResourceAddress)
@ -315,26 +326,6 @@ func (c *ImportCommand) Run(rawArgs []string) int {
return 0
}
// configureBackendFlags is a temporary shim until we move the flags for state management to a better place
//
// TODO meta-refactor: remove this when the Meta fields configured here will be removed and replaced
// with proper arguments for the backend.
func (c *ImportCommand) configureBackendFlags(args *arguments.Import) {
c.Meta.ignoreRemoteVersion = args.Backend.IgnoreRemoteVersion
// TODO meta-refactor: remove this only when there is clear path of passing these from the "arguments" package to
// the place where these needs to be used
c.Meta.parallelism = args.Parallelism
// FIXME: the -input flag value is needed to initialize the backend and the
// operation, but there is no clear path to pass this value down, so we
// continue to mutate the Meta object state for now.
c.Meta.input = args.ViewOptions.InputEnabled
c.Meta.variableArgs = args.Vars.All()
c.Meta.stateArgs = *args.State
}
func (c *ImportCommand) Help() string {
helpText := `
Usage: tofu [global options] import [options] ADDR ID

View file

@ -77,13 +77,13 @@ func (c *InitCommand) Run(rawArgs []string) int {
// operation, but there is no clear path to pass this value down, so we
// continue to mutate the Meta object state for now.
c.Meta.input = args.ViewOptions.InputEnabled
c.configureBackendFlags(args.Backend)
if len(args.FlagPluginPath) > 0 {
c.pluginPath = args.FlagPluginPath
}
c.Meta.variableArgs = args.Vars.All()
c.Meta.stateArgs = *args.State
c.Meta.backendArgs = *args.Backend
// This gets the current directory as full path.
path := c.WorkingDir.NormalizePath(c.WorkingDir.RootModuleDir())
@ -1152,17 +1152,6 @@ func (c *InitCommand) AutocompleteArgs() complete.Predictor {
return complete.PredictDirs("")
}
// configureBackendFlags is a temporary shim until we move the backend migration logic away from the Meta fields.
//
// TODO meta-refactor: remove this when the Meta fields configured here will be removed and replaced
// with proper arguments for the backend.
func (c *InitCommand) configureBackendFlags(args *arguments.Backend) {
c.forceInitCopy = args.ForceInitCopy
c.reconfigure = args.Reconfigure
c.migrateState = args.MigrateState
c.Meta.ignoreRemoteVersion = args.IgnoreRemoteVersion
}
func (c *InitCommand) AutocompleteFlags() complete.Flags {
return complete.Flags{
"-backend": completePredictBoolean,

View file

@ -180,23 +180,9 @@ type Meta struct {
//
// parallelism is used to control the number of concurrent operations
// allowed when walking the graph
//
// forceInitCopy suppresses confirmation for copying state data during
// init.
//
// reconfigure forces init to ignore any stored configuration.
//
// migrateState confirms the user wishes to migrate from the prior backend
// configuration to a new configuration.
stateArgs arguments.State
parallelism int
forceInitCopy bool
reconfigure bool
migrateState bool
// Used with commands which write state to allow users to write remote
// state even if the remote and local OpenTofu versions don't match.
ignoreRemoteVersion bool
stateArgs arguments.State
backendArgs arguments.Backend
parallelism int
// Used to cache the root module rootModuleCallCache and known variables.
// This helps prevent duplicate errors/warnings.

View file

@ -611,7 +611,7 @@ func (m *Meta) backendFromConfig(ctx context.Context, opts *BackendOpts, enc enc
// if we want to force reconfiguration of the backend, we set the backend
// state to nil on this copy. This will direct us through the correct
// configuration path in the switch statement below.
if m.reconfigure {
if m.backendArgs.Reconfigure {
s.Backend = nil
}
@ -646,7 +646,7 @@ func (m *Meta) backendFromConfig(ctx context.Context, opts *BackendOpts, enc enc
return nil, diags
}
if s.Backend.Type != "cloud" && !m.migrateState {
if s.Backend.Type != "cloud" && !m.backendArgs.MigrateState {
diags = diags.Append(migrateOrReconfigDiag)
return nil, diags
}
@ -735,7 +735,7 @@ func (m *Meta) backendFromConfig(ctx context.Context, opts *BackendOpts, enc enc
return nil, diags
}
if !cloudMode.InvolvesCloud() && !m.migrateState {
if !cloudMode.InvolvesCloud() && !m.backendArgs.MigrateState {
diags = diags.Append(migrateOrReconfigDiag)
return nil, diags
}
@ -1505,7 +1505,7 @@ func (m *Meta) remoteVersionCheck(b backend.Backend, workspace string) tfdiags.D
if back, ok := b.(BackendWithRemoteTerraformVersion); ok {
// Allow user override based on command-line flag
if m.ignoreRemoteVersion {
if m.backendArgs.IgnoreRemoteVersion {
back.IgnoreVersionConflict()
}
// If the override is set, this check will return a warning instead of
@ -1529,7 +1529,7 @@ func (m *Meta) assertSupportedCloudInitOptions(mode cloud.ConfigChangeMode) tfdi
var diags tfdiags.Diagnostics
if mode.InvolvesCloud() {
log.Printf("[TRACE] Meta.Backend: Cloud backend mode initialization type: %s", mode)
if m.reconfigure {
if m.backendArgs.Reconfigure {
if mode.IsCloudMigration() {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
@ -1544,11 +1544,11 @@ func (m *Meta) assertSupportedCloudInitOptions(mode cloud.ConfigChangeMode) tfdi
))
}
}
if m.migrateState {
if m.backendArgs.MigrateState {
name := "-migrate-state"
if m.forceInitCopy {
if m.backendArgs.ForceInitCopy {
// -force copy implies -migrate-state in "tofu init",
// so m.migrateState is forced to true in this case even if
// so m.backendArgs.migrateState is forced to true in this case even if
// the user didn't actually specify it. We'll use the other
// name here to avoid being confusing, then.
name = "-force-copy"

View file

@ -77,7 +77,7 @@ func (m *Meta) backendMigrateState(ctx context.Context, opts *backendMigrateOpts
// Set up defaults
opts.sourceWorkspace = backend.DefaultStateName
opts.destinationWorkspace = backend.DefaultStateName
opts.force = m.forceInitCopy
opts.force = m.backendArgs.ForceInitCopy
// Disregard remote OpenTofu version for the state source backend. If it's a
// Terraform Cloud remote backend, we don't care about the remote version,
@ -85,7 +85,7 @@ func (m *Meta) backendMigrateState(ctx context.Context, opts *backendMigrateOpts
m.ignoreRemoteVersionConflict(opts.Source)
// Disregard remote OpenTofu version if instructed to do so via CLI flag.
if m.ignoreRemoteVersion {
if m.backendArgs.IgnoreRemoteVersion {
m.ignoreRemoteVersionConflict(opts.Destination)
} else {
// Check the remote OpenTofu version for the state destination backend. If

View file

@ -326,7 +326,7 @@ func TestMetaBackend_configureNewWithState(t *testing.T) {
// This combination should not require the extra -migrate-state flag, since
// there is no existing backend config
m.migrateState = false
m.backendArgs.MigrateState = false
// Get the backend
b, diags := m.Backend(t.Context(), &BackendOpts{Init: true}, encryption.StateEncryptionDisabled())
@ -492,7 +492,7 @@ func TestMetaBackend_configureNewWithStateExisting(t *testing.T) {
// Setup the meta
m := testMetaBackend(t)
// suppress input
m.forceInitCopy = true
m.backendArgs.ForceInitCopy = true
// Get the backend
b, diags := m.Backend(t.Context(), &BackendOpts{Init: true}, encryption.StateEncryptionDisabled())
@ -746,7 +746,7 @@ func TestMetaBackend_configuredUnchangedWithStaticEvalVars(t *testing.T) {
// _want_ to perform migration, but for this one we're behaving as if the
// user hasn't set the -migrate-state option and thus it should be an error
// if state migration is required.
m.migrateState = false
m.backendArgs.MigrateState = false
// Get the backend
b, diags := m.Backend(
@ -911,7 +911,7 @@ func TestMetaBackend_reconfigureChange(t *testing.T) {
m.input = false
// cli flag -reconfigure
m.reconfigure = true
m.backendArgs.Reconfigure = true
// Get the backend
b, diags := m.Backend(t.Context(), &BackendOpts{Init: true}, encryption.StateEncryptionDisabled())
@ -2038,7 +2038,7 @@ func TestMetaBackend_localDoesNotDeleteLocal(t *testing.T) {
testStateFileDefault(t, orig)
m := testMetaBackend(t)
m.forceInitCopy = true
m.backendArgs.ForceInitCopy = true
// init the backend
_, diags := m.Backend(t.Context(), &BackendOpts{Init: true}, encryption.StateEncryptionDisabled())
if diags.HasErrors() {
@ -2081,7 +2081,7 @@ func TestMetaBackend_configToExtra(t *testing.T) {
// init the backend again with the options
extras := map[string]cty.Value{"path": cty.StringVal("hello")}
m = testMetaBackend(t)
m.forceInitCopy = true
m.backendArgs.ForceInitCopy = true
_, diags := m.Backend(t.Context(), &BackendOpts{
ConfigOverride: configs.SynthBody("synth", extras),
Init: true,
@ -2123,19 +2123,19 @@ func TestBackendFromState(t *testing.T) {
}
func testMetaBackend(t *testing.T) *Meta {
var m Meta
view, _ := testView(t)
m.View = view
m.stateArgs = arguments.State{Lock: true}
m := Meta{
WorkingDir: workdir.NewDir("."),
View: view,
stateArgs: arguments.State{Lock: true},
// metaBackend tests are verifying migrate actions
backendArgs: arguments.Backend{MigrateState: true},
}
// TODO meta-refactor: these assignments are needed because the extendedFlagSet was used here before,
// which had these with defaults as "true". In a future iteration, once these are not needed, we need to remove them.
m.input = true
// metaBackend tests are verifying migrate actions
m.migrateState = true
m.WorkingDir = workdir.NewDir(".")
t.Cleanup(func() {
// Trigger garbage collection to ensure that all open file handles are closed.
// This prevents TempDir RemoveAll cleanup errors on Windows.

View file

@ -49,9 +49,7 @@ func (c *StateMvCommand) Run(rawArgs []string) int {
}
return cli.RunResultHelp
}
// TODO meta-refactor: remove this assignment once there is a clear way to propagate this to the place
// where is used
c.ignoreRemoteVersion = args.Backend.IgnoreRemoteVersion
c.backendArgs = *args.Backend
c.Meta.variableArgs = args.Vars.All()
// NOTE: We intentionally configure the stateArgs here like this, ignoring the stateOutPath, because the c.stateArgs

View file

@ -53,12 +53,9 @@ func (c *StatePushCommand) Run(rawArgs []string) int {
}
return cli.RunResultHelp
}
// TODO meta-refactor: remove these assignments once we have a clear way to propagate these to the logic
// that uses them
c.ignoreRemoteVersion = args.Backend.IgnoreRemoteVersion
c.Meta.variableArgs = args.Vars.All()
c.Meta.stateArgs = *args.State
c.Meta.backendArgs = *args.Backend
if diags := c.Meta.checkRequiredVersion(ctx); diags != nil {
view.Diagnostics(diags)

View file

@ -51,12 +51,9 @@ func (c *StateReplaceProviderCommand) Run(rawArgs []string) int {
}
return cli.RunResultHelp
}
// TODO meta-refactor: remove these assignments once there is a clear way to propagate these to the place
// where are used
c.Meta.ignoreRemoteVersion = args.Backend.IgnoreRemoteVersion
c.Meta.stateArgs = *args.State
c.Meta.variableArgs = args.Vars.All()
c.Meta.backendArgs = *args.Backend
if diags := c.Meta.checkRequiredVersion(ctx); diags != nil {
view.Diagnostics(diags)

View file

@ -46,12 +46,9 @@ func (c *StateRmCommand) Run(rawArgs []string) int {
}
return cli.RunResultHelp
}
// TODO meta-refactor: remove these assignments once we have a clear way to propagate these to the logic
// that uses them
c.Meta.ignoreRemoteVersion = args.Backend.IgnoreRemoteVersion
c.Meta.variableArgs = args.Vars.All()
c.Meta.stateArgs = *args.State
c.Meta.backendArgs = *args.Backend
if diags := c.Meta.checkRequiredVersion(ctx); diags != nil {
view.Diagnostics(diags)