diff --git a/internal/command/arguments/backend.go b/internal/command/arguments/backend.go index 68aeefc2c1..fa681ab265 100644 --- a/internal/command/arguments/backend.go +++ b/internal/command/arguments/backend.go @@ -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 } diff --git a/internal/command/arguments/state_mv.go b/internal/command/arguments/state_mv.go index 377e45a5d9..942668aa41 100644 --- a/internal/command/arguments/state_mv.go +++ b/internal/command/arguments/state_mv.go @@ -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) diff --git a/internal/command/arguments/state_mv_test.go b/internal/command/arguments/state_mv_test.go index 623b36046e..149746c09d 100644 --- a/internal/command/arguments/state_mv_test.go +++ b/internal/command/arguments/state_mv_test.go @@ -164,7 +164,7 @@ func stateMvArgsWithDefaults(mutate func(stateMv *StateMv)) *StateMv { ViewType: ViewHuman, InputEnabled: false, }, - Backend: Backend{ + Backend: &Backend{ IgnoreRemoteVersion: false, }, State: &State{ diff --git a/internal/command/arguments/state_push.go b/internal/command/arguments/state_push.go index 17b6768bea..aa5b1f10f2 100644 --- a/internal/command/arguments/state_push.go +++ b/internal/command/arguments/state_push.go @@ -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) diff --git a/internal/command/arguments/state_push_test.go b/internal/command/arguments/state_push_test.go index e992790111..37c9d1d85c 100644 --- a/internal/command/arguments/state_push_test.go +++ b/internal/command/arguments/state_push_test.go @@ -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, diff --git a/internal/command/arguments/state_replace_provider.go b/internal/command/arguments/state_replace_provider.go index f07e36042b..dc7196dafd 100644 --- a/internal/command/arguments/state_replace_provider.go +++ b/internal/command/arguments/state_replace_provider.go @@ -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) diff --git a/internal/command/arguments/state_replace_provider_test.go b/internal/command/arguments/state_replace_provider_test.go index 3f7ea427e8..e9ffbb6199 100644 --- a/internal/command/arguments/state_replace_provider_test.go +++ b/internal/command/arguments/state_replace_provider_test.go @@ -159,7 +159,7 @@ func stateReplaceProviderArgsWithDefaults(mutate func(srp *StateReplaceProvider) ViewType: ViewHuman, InputEnabled: false, }, - Backend: Backend{ + Backend: &Backend{ IgnoreRemoteVersion: false, }, Vars: &Vars{}, diff --git a/internal/command/arguments/state_rm.go b/internal/command/arguments/state_rm.go index 984cf3e371..4d4b702995 100644 --- a/internal/command/arguments/state_rm.go +++ b/internal/command/arguments/state_rm.go @@ -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) diff --git a/internal/command/arguments/state_rm_test.go b/internal/command/arguments/state_rm_test.go index a4d056e0e1..8b704a1956 100644 --- a/internal/command/arguments/state_rm_test.go +++ b/internal/command/arguments/state_rm_test.go @@ -129,7 +129,7 @@ func stateRmArgsWithDefaults(mutate func(stateRm *StateRm)) *StateRm { ViewType: ViewHuman, InputEnabled: false, }, - Backend: Backend{ + Backend: &Backend{ IgnoreRemoteVersion: false, }, Vars: &Vars{}, diff --git a/internal/command/import.go b/internal/command/import.go index c07aed660b..4c11c0bcac 100644 --- a/internal/command/import.go +++ b/internal/command/import.go @@ -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 diff --git a/internal/command/init.go b/internal/command/init.go index e3cb4430c5..cd44c899bd 100644 --- a/internal/command/init.go +++ b/internal/command/init.go @@ -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, diff --git a/internal/command/meta.go b/internal/command/meta.go index fd05982fb7..e7064e90f4 100644 --- a/internal/command/meta.go +++ b/internal/command/meta.go @@ -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. diff --git a/internal/command/meta_backend.go b/internal/command/meta_backend.go index 16813ebc56..09b59decea 100644 --- a/internal/command/meta_backend.go +++ b/internal/command/meta_backend.go @@ -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" diff --git a/internal/command/meta_backend_migrate.go b/internal/command/meta_backend_migrate.go index a8321ff5dd..40e3984fd4 100644 --- a/internal/command/meta_backend_migrate.go +++ b/internal/command/meta_backend_migrate.go @@ -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 diff --git a/internal/command/meta_backend_test.go b/internal/command/meta_backend_test.go index 0ed30e5c41..865f67a64c 100644 --- a/internal/command/meta_backend_test.go +++ b/internal/command/meta_backend_test.go @@ -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. diff --git a/internal/command/state_mv.go b/internal/command/state_mv.go index 3822a47ca8..93d9087863 100644 --- a/internal/command/state_mv.go +++ b/internal/command/state_mv.go @@ -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 diff --git a/internal/command/state_push.go b/internal/command/state_push.go index f1d6a59dce..58af11379d 100644 --- a/internal/command/state_push.go +++ b/internal/command/state_push.go @@ -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) diff --git a/internal/command/state_replace_provider.go b/internal/command/state_replace_provider.go index 2f22dcc429..7fcea59a58 100644 --- a/internal/command/state_replace_provider.go +++ b/internal/command/state_replace_provider.go @@ -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) diff --git a/internal/command/state_rm.go b/internal/command/state_rm.go index f08f1dbb7f..6294b329d3 100644 --- a/internal/command/state_rm.go +++ b/internal/command/state_rm.go @@ -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)