From c587384dfffd98eaadbdba9f406a0ed59aed6faf Mon Sep 17 00:00:00 2001 From: Alisdair McDiarmid Date: Mon, 18 Oct 2021 14:41:04 -0400 Subject: [PATCH] cli: Restore -lock and -lock-timeout init flags The -lock and -lock-timeout flags were removed prior to the release of 1.0 as they were thought to have no effect. This is not true in the case of state migrations when changing backends. This commit restores these flags, and adds test coverage for locking during backend state migration. Also update the help output describing other boolean flags, showing the argument as the user would type it rather than the default behavior. --- internal/command/init.go | 27 +++++++--- internal/command/init_test.go | 54 +++++++++++++++++++ .../.terraform/terraform.tfstate | 22 ++++++++ .../input.config | 1 + .../init-backend-migrate-while-locked/main.tf | 5 ++ 5 files changed, 102 insertions(+), 7 deletions(-) create mode 100644 internal/command/testdata/init-backend-migrate-while-locked/.terraform/terraform.tfstate create mode 100644 internal/command/testdata/init-backend-migrate-while-locked/input.config create mode 100644 internal/command/testdata/init-backend-migrate-while-locked/main.tf diff --git a/internal/command/init.go b/internal/command/init.go index 551a4d0b8..8083b7513 100644 --- a/internal/command/init.go +++ b/internal/command/init.go @@ -44,6 +44,8 @@ func (c *InitCommand) Run(args []string) int { cmdFlags.StringVar(&flagFromModule, "from-module", "", "copy the source of the given module into the directory before init") cmdFlags.BoolVar(&flagGet, "get", true, "") cmdFlags.BoolVar(&c.forceInitCopy, "force-copy", false, "suppress prompts about copying state data") + cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state") + cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout") cmdFlags.BoolVar(&c.reconfigure, "reconfigure", false, "reconfigure") cmdFlags.BoolVar(&c.migrateState, "migrate-state", false, "migrate state") cmdFlags.BoolVar(&flagUpgrade, "upgrade", false, "") @@ -932,6 +934,8 @@ func (c *InitCommand) AutocompleteFlags() complete.Flags { "-from-module": completePredictModuleSource, "-get": completePredictBoolean, "-input": completePredictBoolean, + "-lock": completePredictBoolean, + "-lock-timeout": complete.PredictAnything, "-no-color": complete.PredictNothing, "-plugin-dir": complete.PredictDirs(""), "-reconfigure": complete.PredictNothing, @@ -959,7 +963,8 @@ Usage: terraform [global options] init [options] Options: - -backend=true Configure the backend for this configuration. + -backend=false Disable backend initialization for this configuration + and use the previously initialized backend instead. -backend-config=path This can be either a path to an HCL file with key/value assignments (same format as terraform.tfvars) or a @@ -975,10 +980,17 @@ Options: -from-module=SOURCE Copy the contents of the given module into the target directory before initialization. - -get=true Download any modules for this configuration. + -get=false Disable downloading modules for this configuration. - -input=true Ask for input if necessary. If false, will error if - input was required. + -input=false Disable prompting for missing backend configuration + values. This will result in an error if the backend + configuration is not fully specified. + + -lock=false Don't hold a state lock during backend migration. + This is dangerous if others might concurrently run + commands against the same workspace. + + -lock-timeout=0s Duration to retry a state lock. -no-color If specified, output won't contain any color. @@ -993,9 +1005,10 @@ Options: -migrate-state Reconfigure the backend, and attempt to migrate any existing state. - -upgrade=false If installing modules (-get) or plugins, ignore - previously-downloaded objects and install the - latest version allowed within configured constraints. + -upgrade Install the latest module and provider versions + allowed within configured constraints, overriding the + default behavior of selecting exactly the version + recorded in the dependency lockfile. -lockfile=MODE Set a dependency lockfile mode. Currently only "readonly" is valid. diff --git a/internal/command/init_test.go b/internal/command/init_test.go index 2d96b27b4..c50e97ac9 100644 --- a/internal/command/init_test.go +++ b/internal/command/init_test.go @@ -562,6 +562,60 @@ func TestInit_backendConfigFileChange(t *testing.T) { } } +func TestInit_backendMigrateWhileLocked(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + testCopyDir(t, testFixturePath("init-backend-migrate-while-locked"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + providerSource, close := newMockProviderSource(t, map[string][]string{ + "hashicorp/test": {"1.2.3"}, + }) + defer close() + + ui := new(cli.MockUi) + view, _ := testView(t) + c := &InitCommand{ + Meta: Meta{ + testingOverrides: metaOverridesForProvider(testProvider()), + ProviderSource: providerSource, + Ui: ui, + View: view, + }, + } + + // Create some state, so the backend has something to migrate from + f, err := os.Create("local-state.tfstate") + if err != nil { + t.Fatalf("err: %s", err) + } + err = writeStateForTesting(testState(), f) + f.Close() + if err != nil { + t.Fatalf("err: %s", err) + } + + // Lock the source state + unlock, err := testLockState(testDataDir, "local-state.tfstate") + if err != nil { + t.Fatal(err) + } + defer unlock() + + // Attempt to migrate + args := []string{"-backend-config", "input.config", "-migrate-state", "-force-copy"} + if code := c.Run(args); code == 0 { + t.Fatalf("expected nonzero exit code: %s", ui.OutputWriter.String()) + } + + // Disabling locking should work + args = []string{"-backend-config", "input.config", "-migrate-state", "-force-copy", "-lock=false"} + if code := c.Run(args); code != 0 { + t.Fatalf("expected zero exit code, got %d: %s", code, ui.ErrorWriter.String()) + } +} + func TestInit_backendConfigKV(t *testing.T) { // Create a temporary working directory that is empty td := tempDir(t) diff --git a/internal/command/testdata/init-backend-migrate-while-locked/.terraform/terraform.tfstate b/internal/command/testdata/init-backend-migrate-while-locked/.terraform/terraform.tfstate new file mode 100644 index 000000000..073bd7a82 --- /dev/null +++ b/internal/command/testdata/init-backend-migrate-while-locked/.terraform/terraform.tfstate @@ -0,0 +1,22 @@ +{ + "version": 3, + "serial": 0, + "lineage": "666f9301-7e65-4b19-ae23-71184bb19b03", + "backend": { + "type": "local", + "config": { + "path": "local-state.tfstate" + }, + "hash": 9073424445967744180 + }, + "modules": [ + { + "path": [ + "root" + ], + "outputs": {}, + "resources": {}, + "depends_on": [] + } + ] +} diff --git a/internal/command/testdata/init-backend-migrate-while-locked/input.config b/internal/command/testdata/init-backend-migrate-while-locked/input.config new file mode 100644 index 000000000..6cd14f4a3 --- /dev/null +++ b/internal/command/testdata/init-backend-migrate-while-locked/input.config @@ -0,0 +1 @@ +path = "hello" diff --git a/internal/command/testdata/init-backend-migrate-while-locked/main.tf b/internal/command/testdata/init-backend-migrate-while-locked/main.tf new file mode 100644 index 000000000..bea8e789f --- /dev/null +++ b/internal/command/testdata/init-backend-migrate-while-locked/main.tf @@ -0,0 +1,5 @@ +terraform { + backend "local" { + path = "local-state.tfstate" + } +}