From 7e07ab4e85be8b1bc48036e949f55f152967e776 Mon Sep 17 00:00:00 2001 From: Aleksander Aleksic Date: Wed, 20 Jan 2021 13:20:28 +0100 Subject: [PATCH 1/3] Implemented state remove command --- tfexec/state_rm.go | 104 ++++++++++++++++++++++++++++++++++++++++ tfexec/state_rm_test.go | 56 ++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 tfexec/state_rm.go create mode 100644 tfexec/state_rm_test.go diff --git a/tfexec/state_rm.go b/tfexec/state_rm.go new file mode 100644 index 00000000..8bcc3ee6 --- /dev/null +++ b/tfexec/state_rm.go @@ -0,0 +1,104 @@ +package tfexec + +import ( + "context" + "os/exec" + "strconv" +) + +type stateRmConfig struct { + backup string + backupOut string + dryRun bool + lock bool + lockTimeout string + state string + stateOut string +} + +var defaultStateRmOptions = stateRmConfig{ + lock: true, + lockTimeout: "0s", +} + +// StateRmCmdOption represents options used in the Refresh method. +type StateRmCmdOption interface { + configureStateRm(*stateRmConfig) +} + +func (opt *BackupOption) configureStateRm(conf *stateRmConfig) { + conf.backup = opt.path +} + +func (opt *BackupOutOption) configureStateRm(conf *stateRmConfig) { + conf.backupOut = opt.path +} + +func (opt *DryRunOption) configureStateRm(conf *stateRmConfig) { + conf.dryRun = opt.dryRun +} + +func (opt *LockOption) configureStateRm(conf *stateRmConfig) { + conf.lock = opt.lock +} + +func (opt *LockTimeoutOption) configureStateRm(conf *stateRmConfig) { + conf.lockTimeout = opt.timeout +} + +func (opt *StateOption) configureStateRm(conf *stateRmConfig) { + conf.state = opt.path +} + +func (opt *StateOutOption) configureStateRm(conf *stateRmConfig) { + conf.stateOut = opt.path +} + +// StateRm represents the terraform state rm subcommand. +func (tf *Terraform) StateRm(ctx context.Context, source string, opts ...StateRmCmdOption) error { + cmd, err := tf.stateRmCmd(ctx, source, opts...) + if err != nil { + return err + } + return tf.runTerraformCmd(ctx, cmd) +} + +func (tf *Terraform) stateRmCmd(ctx context.Context, source string, opts ...StateRmCmdOption) (*exec.Cmd, error) { + c := defaultStateRmOptions + + for _, o := range opts { + o.configureStateRm(&c) + } + + args := []string{"state", "rm", "-no-color"} + + // string opts: only pass if set + if c.backup != "" { + args = append(args, "-backup="+c.backup) + } + if c.backupOut != "" { + args = append(args, "-backup-out="+c.backupOut) + } + if c.lockTimeout != "" { + args = append(args, "-lock-timeout="+c.lockTimeout) + } + if c.state != "" { + args = append(args, "-state="+c.state) + } + if c.stateOut != "" { + args = append(args, "-state-out="+c.stateOut) + } + + // boolean and numerical opts: always pass + args = append(args, "-lock="+strconv.FormatBool(c.lock)) + + // unary flags: pass if true + if c.dryRun { + args = append(args, "-dry-run") + } + + // positional arguments + args = append(args, source) + + return tf.buildTerraformCmd(ctx, nil, args...), nil +} diff --git a/tfexec/state_rm_test.go b/tfexec/state_rm_test.go new file mode 100644 index 00000000..3fee8cc5 --- /dev/null +++ b/tfexec/state_rm_test.go @@ -0,0 +1,56 @@ +package tfexec + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-exec/tfexec/internal/testutil" +) + +func TestStateRmCmd(t *testing.T) { + td := testTempDir(t) + + tf, err := NewTerraform(td, tfVersion(t, testutil.Latest013)) + if err != nil { + t.Fatal(err) + } + + // empty env, to avoid environ mismatch in testing + tf.SetEnv(map[string]string{}) + + t.Run("defaults", func(t *testing.T) { + stateRmCmd, err := tf.stateRmCmd(context.Background(), "testsource") + if err != nil { + t.Fatal(err) + } + + assertCmd(t, []string{ + "state", + "rm", + "-no-color", + "-lock-timeout=0s", + "-lock=true", + "testsource", + }, nil, stateRmCmd) + }) + + t.Run("override all defaults", func(t *testing.T) { + stateRmCmd, err := tf.stateRmCmd(context.Background(), "testsrc", Backup("testbackup"), BackupOut("testbackupout"), LockTimeout("200s"), State("teststate"), StateOut("teststateout"), Lock(false)) + if err != nil { + t.Fatal(err) + } + + assertCmd(t, []string{ + "state", + "rm", + "-no-color", + "-backup=testbackup", + "-backup-out=testbackupout", + "-lock-timeout=200s", + "-state=teststate", + "-state-out=teststateout", + "-lock=false", + "testsrc", + }, nil, stateRmCmd) + }) +} From 56f8ce6ed99d9e2e086340647dd552ef28665d2d Mon Sep 17 00:00:00 2001 From: Aleksander Aleksic Date: Fri, 22 Jan 2021 08:01:41 +0100 Subject: [PATCH 2/3] Added e2e test for state remove command --- tfexec/internal/e2etest/state_rm_test.go | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tfexec/internal/e2etest/state_rm_test.go diff --git a/tfexec/internal/e2etest/state_rm_test.go b/tfexec/internal/e2etest/state_rm_test.go new file mode 100644 index 00000000..5e59bb8f --- /dev/null +++ b/tfexec/internal/e2etest/state_rm_test.go @@ -0,0 +1,45 @@ +package e2etest + +import ( + "context" + "testing" + + "github.com/hashicorp/go-version" + tfjson "github.com/hashicorp/terraform-json" + + "github.com/hashicorp/terraform-exec/tfexec" +) + +func TestStateRm(t *testing.T) { + runTest(t, "basic_with_state", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) { + if tfv.LessThan(providerAddressMinVersion) { + t.Skip("state file provider FQNs not compatible with this Terraform version") + } + + err := tf.Init(context.Background()) + if err != nil { + t.Fatalf("error running Init in test directory: %s", err) + } + + err = tf.StateRm(context.Background(), "null_resource.foo") + if err != nil { + t.Fatalf("error running StateRm: %s", err) + } + + // test that the new state is as expected + expected := &tfjson.State{ + FormatVersion: "0.1", + // TerraformVersion is ignored to facilitate latest version testing + Values: nil, + } + + actual, err := tf.Show(context.Background()) + if err != nil { + t.Fatal(err) + } + + if diff := diffState(expected, actual); diff != "" { + t.Fatalf("mismatch (-want +got):\n%s", diff) + } + }) +} From ce30461d843c03a4b39811086122216b533a8fd7 Mon Sep 17 00:00:00 2001 From: Aleksander Aleksic Date: Mon, 25 Jan 2021 19:11:51 +0100 Subject: [PATCH 3/3] Rename State Rm source to address --- tfexec/state_rm.go | 8 ++++---- tfexec/state_rm_test.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tfexec/state_rm.go b/tfexec/state_rm.go index 8bcc3ee6..0c5dd666 100644 --- a/tfexec/state_rm.go +++ b/tfexec/state_rm.go @@ -55,15 +55,15 @@ func (opt *StateOutOption) configureStateRm(conf *stateRmConfig) { } // StateRm represents the terraform state rm subcommand. -func (tf *Terraform) StateRm(ctx context.Context, source string, opts ...StateRmCmdOption) error { - cmd, err := tf.stateRmCmd(ctx, source, opts...) +func (tf *Terraform) StateRm(ctx context.Context, address string, opts ...StateRmCmdOption) error { + cmd, err := tf.stateRmCmd(ctx, address, opts...) if err != nil { return err } return tf.runTerraformCmd(ctx, cmd) } -func (tf *Terraform) stateRmCmd(ctx context.Context, source string, opts ...StateRmCmdOption) (*exec.Cmd, error) { +func (tf *Terraform) stateRmCmd(ctx context.Context, address string, opts ...StateRmCmdOption) (*exec.Cmd, error) { c := defaultStateRmOptions for _, o := range opts { @@ -98,7 +98,7 @@ func (tf *Terraform) stateRmCmd(ctx context.Context, source string, opts ...Stat } // positional arguments - args = append(args, source) + args = append(args, address) return tf.buildTerraformCmd(ctx, nil, args...), nil } diff --git a/tfexec/state_rm_test.go b/tfexec/state_rm_test.go index 3fee8cc5..e2794e4e 100644 --- a/tfexec/state_rm_test.go +++ b/tfexec/state_rm_test.go @@ -19,7 +19,7 @@ func TestStateRmCmd(t *testing.T) { tf.SetEnv(map[string]string{}) t.Run("defaults", func(t *testing.T) { - stateRmCmd, err := tf.stateRmCmd(context.Background(), "testsource") + stateRmCmd, err := tf.stateRmCmd(context.Background(), "testAddress") if err != nil { t.Fatal(err) } @@ -30,12 +30,12 @@ func TestStateRmCmd(t *testing.T) { "-no-color", "-lock-timeout=0s", "-lock=true", - "testsource", + "testAddress", }, nil, stateRmCmd) }) t.Run("override all defaults", func(t *testing.T) { - stateRmCmd, err := tf.stateRmCmd(context.Background(), "testsrc", Backup("testbackup"), BackupOut("testbackupout"), LockTimeout("200s"), State("teststate"), StateOut("teststateout"), Lock(false)) + stateRmCmd, err := tf.stateRmCmd(context.Background(), "testAddress", Backup("testbackup"), BackupOut("testbackupout"), LockTimeout("200s"), State("teststate"), StateOut("teststateout"), Lock(false)) if err != nil { t.Fatal(err) } @@ -50,7 +50,7 @@ func TestStateRmCmd(t *testing.T) { "-state=teststate", "-state-out=teststateout", "-lock=false", - "testsrc", + "testAddress", }, nil, stateRmCmd) }) }