From 0ad399d7b98ddaf26c8abf06d7001607271678b2 Mon Sep 17 00:00:00 2001 From: Rambabu Duddukuri Date: Fri, 19 Nov 2021 18:35:40 -0800 Subject: [PATCH 1/5] Adding Taint/UnTaint command support Adding output name option support --- tfexec/internal/e2etest/taint_test.go | 29 ++++++++++++++++ tfexec/internal/e2etest/untain_test.go | 34 +++++++++++++++++++ tfexec/options.go | 8 +++++ tfexec/output.go | 42 +++++++++++++++++------ tfexec/output_test.go | 23 +++++++++++-- tfexec/taint.go | 46 +++++++++++++++++++++++++ tfexec/taint_test.go | 45 ++++++++++++++++++++++++ tfexec/untaint.go | 46 +++++++++++++++++++++++++ tfexec/untaint_test.go | 47 ++++++++++++++++++++++++++ 9 files changed, 307 insertions(+), 13 deletions(-) create mode 100644 tfexec/internal/e2etest/taint_test.go create mode 100644 tfexec/internal/e2etest/untain_test.go create mode 100644 tfexec/taint.go create mode 100644 tfexec/taint_test.go create mode 100644 tfexec/untaint.go create mode 100644 tfexec/untaint_test.go diff --git a/tfexec/internal/e2etest/taint_test.go b/tfexec/internal/e2etest/taint_test.go new file mode 100644 index 00000000..174f46fc --- /dev/null +++ b/tfexec/internal/e2etest/taint_test.go @@ -0,0 +1,29 @@ +package e2etest + +import ( + "context" + "testing" + + "github.com/hashicorp/go-version" + + "github.com/hashicorp/terraform-exec/tfexec" +) + +func TestTaint(t *testing.T) { + runTest(t, "basic", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) { + err := tf.Init(context.Background()) + if err != nil { + t.Fatalf("error running Init in test directory: %s", err) + } + + err = tf.Apply(context.Background()) + if err != nil { + t.Fatalf("error running Apply: %s", err) + } + + err = tf.Taint(context.Background(), "null_resource.foo") + if err != nil { + t.Fatalf("error running Taint: %s", err) + } + }) +} diff --git a/tfexec/internal/e2etest/untain_test.go b/tfexec/internal/e2etest/untain_test.go new file mode 100644 index 00000000..e934c1fd --- /dev/null +++ b/tfexec/internal/e2etest/untain_test.go @@ -0,0 +1,34 @@ +package e2etest + +import ( + "context" + "testing" + + "github.com/hashicorp/go-version" + + "github.com/hashicorp/terraform-exec/tfexec" +) + +func TestUnTaint(t *testing.T) { + runTest(t, "basic", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) { + err := tf.Init(context.Background()) + if err != nil { + t.Fatalf("error running Init in test directory: %s", err) + } + + err = tf.Apply(context.Background()) + if err != nil { + t.Fatalf("error running Apply: %s", err) + } + + err = tf.Taint(context.Background(), "null_resource.foo") + if err != nil { + t.Fatalf("error running Taint: %s", err) + } + + err = tf.UnTaint(context.Background(), "null_resource.foo") + if err != nil { + t.Fatalf("error running UnTaint: %s", err) + } + }) +} diff --git a/tfexec/options.go b/tfexec/options.go index 71638895..b6831f45 100644 --- a/tfexec/options.go +++ b/tfexec/options.go @@ -204,6 +204,14 @@ func Out(path string) *OutOption { return &OutOption{path} } +type OutputNameOption struct { + name string +} + +func OutputName(name string) *OutputNameOption { + return &OutputNameOption{name} +} + type ParallelismOption struct { parallelism int } diff --git a/tfexec/output.go b/tfexec/output.go index b16b8b72..d6060aae 100644 --- a/tfexec/output.go +++ b/tfexec/output.go @@ -8,6 +8,7 @@ import ( type outputConfig struct { state string + name string json bool } @@ -22,6 +23,10 @@ func (opt *StateOption) configureOutput(conf *outputConfig) { conf.state = opt.path } +func (opt *OutputNameOption) configureOutput(conf *outputConfig) { + conf.name = opt.name +} + // OutputMeta represents the JSON output of 'terraform output -json', // which resembles state format version 3 due to a historical accident. // Please see hashicorp/terraform/command/output.go. @@ -34,23 +39,35 @@ type OutputMeta struct { // Output represents the terraform output subcommand. func (tf *Terraform) Output(ctx context.Context, opts ...OutputOption) (map[string]OutputMeta, error) { - outputCmd := tf.outputCmd(ctx, opts...) + c := defaultOutputOptions + + for _, o := range opts { + o.configureOutput(&c) + } + outputCmd := tf.outputCmd(ctx, c) outputs := map[string]OutputMeta{} - err := tf.runTerraformCmdJSON(ctx, outputCmd, &outputs) - if err != nil { - return nil, err + + if c.name != "" { + var outputValue json.RawMessage + err := tf.runTerraformCmdJSON(ctx, outputCmd, &outputValue) + if err != nil { + return nil, err + } + output := OutputMeta{} + output.Value = outputValue + outputs[c.name] = output + } else { + err := tf.runTerraformCmdJSON(ctx, outputCmd, &outputs) + if err != nil { + return nil, err + } } return outputs, nil } -func (tf *Terraform) outputCmd(ctx context.Context, opts ...OutputOption) *exec.Cmd { - c := defaultOutputOptions - - for _, o := range opts { - o.configureOutput(&c) - } +func (tf *Terraform) outputCmd(ctx context.Context, c outputConfig) *exec.Cmd { args := []string{"output", "-no-color", "-json"} @@ -59,5 +76,10 @@ func (tf *Terraform) outputCmd(ctx context.Context, opts ...OutputOption) *exec. args = append(args, "-state="+c.state) } + // output name: only pass if set + if c.name != "" { + args = append(args, c.name) + } + return tf.buildTerraformCmd(ctx, nil, args...) } diff --git a/tfexec/output_test.go b/tfexec/output_test.go index 6a7d5305..de33639f 100644 --- a/tfexec/output_test.go +++ b/tfexec/output_test.go @@ -19,7 +19,8 @@ func TestOutputCmd(t *testing.T) { tf.SetEnv(map[string]string{}) t.Run("defaults", func(t *testing.T) { - outputCmd := tf.outputCmd(context.Background()) + var config = outputConfig{} + outputCmd := tf.outputCmd(context.Background(), config) assertCmd(t, []string{ "output", @@ -29,8 +30,9 @@ func TestOutputCmd(t *testing.T) { }) t.Run("override all defaults", func(t *testing.T) { - outputCmd := tf.outputCmd(context.Background(), - State("teststate")) + var config = outputConfig{} + config.state = "teststate" + outputCmd := tf.outputCmd(context.Background(), config) assertCmd(t, []string{ "output", @@ -39,4 +41,19 @@ func TestOutputCmd(t *testing.T) { "-state=teststate", }, nil, outputCmd) }) + + t.Run("defaults with single output", func(t *testing.T) { + var config = outputConfig{} + config.state = "teststate" + config.name = "testoutput" + outputCmd := tf.outputCmd(context.Background(), config) + + assertCmd(t, []string{ + "output", + "-no-color", + "-json", + "-state=teststate", + "testoutput", + }, nil, outputCmd) + }) } diff --git a/tfexec/taint.go b/tfexec/taint.go new file mode 100644 index 00000000..a47a8486 --- /dev/null +++ b/tfexec/taint.go @@ -0,0 +1,46 @@ +package tfexec + +import ( + "context" + "os/exec" +) + +type taintConfig struct { + state string +} + +var defaultTaintOptions = taintConfig{} + +// TaintOption represents options used in the Taint method. +type TaintOption interface { + configureTaint(*taintConfig) +} + +func (opt *StateOption) configureTaint(conf *taintConfig) { + conf.state = opt.path +} + +// Taint represents the terraform taint subcommand. +func (tf *Terraform) Taint(ctx context.Context, address string, opts ...TaintOption) error { + taintCmd := tf.taintCmd(ctx, address, opts...) + return tf.runTerraformCmd(ctx, taintCmd) +} + +func (tf *Terraform) taintCmd(ctx context.Context, address string, opts ...TaintOption) *exec.Cmd { + c := defaultTaintOptions + + for _, o := range opts { + o.configureTaint(&c) + } + + args := []string{"taint", "-allow-missing", "-lock-timeout=0s", "-lock=true"} + + // string opts: only pass if set + if c.state != "" { + args = append(args, "-state="+c.state) + } + + args = append(args, address) + + return tf.buildTerraformCmd(ctx, nil, args...) +} diff --git a/tfexec/taint_test.go b/tfexec/taint_test.go new file mode 100644 index 00000000..16e89694 --- /dev/null +++ b/tfexec/taint_test.go @@ -0,0 +1,45 @@ +package tfexec + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-exec/tfexec/internal/testutil" +) + +func TestTaintCmd(t *testing.T) { + td := t.TempDir() + + 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) { + taintCmd := tf.taintCmd(context.Background(), "aws_instance.foo") + + assertCmd(t, []string{ + "taint", + "-allow-missing", + "-lock-timeout=0s", + "-lock=true", + "aws_instance.foo", + }, nil, taintCmd) + }) + + t.Run("override all defaults", func(t *testing.T) { + taintCmd := tf.taintCmd(context.Background(), "aws_instance.foo", State("teststate")) + + assertCmd(t, []string{ + "taint", + "-allow-missing", + "-lock-timeout=0s", + "-lock=true", + "-state=teststate", + "aws_instance.foo", + }, nil, taintCmd) + }) +} diff --git a/tfexec/untaint.go b/tfexec/untaint.go new file mode 100644 index 00000000..2df66431 --- /dev/null +++ b/tfexec/untaint.go @@ -0,0 +1,46 @@ +package tfexec + +import ( + "context" + "os/exec" +) + +type untaintConfig struct { + state string +} + +var defaultUnTaintOptions = untaintConfig{} + +// OutputOption represents options used in the Output method. +type UnTaintOption interface { + configureUnTaint(*untaintConfig) +} + +func (opt *StateOption) configureUnTaint(conf *untaintConfig) { + conf.state = opt.path +} + +// Untaint represents the terraform untaint subcommand. +func (tf *Terraform) UnTaint(ctx context.Context, address string, opts ...UnTaintOption) error { + unTaintCmd := tf.unTaintCmd(ctx, address, opts...) + return tf.runTerraformCmd(ctx, unTaintCmd) +} + +func (tf *Terraform) unTaintCmd(ctx context.Context, address string, opts ...UnTaintOption) *exec.Cmd { + c := defaultUnTaintOptions + + for _, o := range opts { + o.configureUnTaint(&c) + } + + args := []string{"untaint", "-allow-missing", "-lock-timeout=0s", "-lock=true", "-no-color"} + + // string opts: only pass if set + if c.state != "" { + args = append(args, "-state="+c.state) + } + + args = append(args, address) + + return tf.buildTerraformCmd(ctx, nil, args...) +} diff --git a/tfexec/untaint_test.go b/tfexec/untaint_test.go new file mode 100644 index 00000000..a75f99b1 --- /dev/null +++ b/tfexec/untaint_test.go @@ -0,0 +1,47 @@ +package tfexec + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-exec/tfexec/internal/testutil" +) + +func TestUnTaintCmd(t *testing.T) { + td := t.TempDir() + + 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) { + unTaintCmd := tf.unTaintCmd(context.Background(), "aws_instance.foo") + + assertCmd(t, []string{ + "untaint", + "-allow-missing", + "-lock-timeout=0s", + "-lock=true", + "-no-color", + "aws_instance.foo", + }, nil, unTaintCmd) + }) + + t.Run("override all defaults", func(t *testing.T) { + unTaintCmd := tf.unTaintCmd(context.Background(), "aws_instance.foo", State("teststate")) + + assertCmd(t, []string{ + "untaint", + "-allow-missing", + "-lock-timeout=0s", + "-lock=true", + "-no-color", + "-state=teststate", + "aws_instance.foo", + }, nil, unTaintCmd) + }) +} From e4697647ee995543e91603419e3add1298cfbe56 Mon Sep 17 00:00:00 2001 From: Rambabu Duddukuri Date: Tue, 30 Nov 2021 10:07:07 -0800 Subject: [PATCH 2/5] Add support for taint/untaint sub command --- CHANGELOG.md | 1 + .../{untain_test.go => untaint_test.go} | 4 +- tfexec/options.go | 8 ---- tfexec/output.go | 40 +++++-------------- tfexec/output_test.go | 23 ++--------- tfexec/taint.go | 7 +++- tfexec/taint_test.go | 8 +--- tfexec/untaint.go | 9 ++++- tfexec/untaint_test.go | 6 --- tfexec/version.go | 2 + 10 files changed, 32 insertions(+), 76 deletions(-) rename tfexec/internal/e2etest/{untain_test.go => untaint_test.go} (87%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d4e6dba..2472ee07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ FEATURES: - Add support for `workspace delete` command [GH-212] - Add support for `workspace show` command [GH-245] - Add support for `force-unlock` command [GH-223] + - Add support for `taint`/`untaint` command [GH-251] # 0.15.0 (October 05, 2021) diff --git a/tfexec/internal/e2etest/untain_test.go b/tfexec/internal/e2etest/untaint_test.go similarity index 87% rename from tfexec/internal/e2etest/untain_test.go rename to tfexec/internal/e2etest/untaint_test.go index e934c1fd..507c93fb 100644 --- a/tfexec/internal/e2etest/untain_test.go +++ b/tfexec/internal/e2etest/untaint_test.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform-exec/tfexec" ) -func TestUnTaint(t *testing.T) { +func TestUntaint(t *testing.T) { runTest(t, "basic", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) { err := tf.Init(context.Background()) if err != nil { @@ -26,7 +26,7 @@ func TestUnTaint(t *testing.T) { t.Fatalf("error running Taint: %s", err) } - err = tf.UnTaint(context.Background(), "null_resource.foo") + err = tf.Untaint(context.Background(), "null_resource.foo") if err != nil { t.Fatalf("error running UnTaint: %s", err) } diff --git a/tfexec/options.go b/tfexec/options.go index b6831f45..71638895 100644 --- a/tfexec/options.go +++ b/tfexec/options.go @@ -204,14 +204,6 @@ func Out(path string) *OutOption { return &OutOption{path} } -type OutputNameOption struct { - name string -} - -func OutputName(name string) *OutputNameOption { - return &OutputNameOption{name} -} - type ParallelismOption struct { parallelism int } diff --git a/tfexec/output.go b/tfexec/output.go index d6060aae..c8f984b3 100644 --- a/tfexec/output.go +++ b/tfexec/output.go @@ -8,7 +8,6 @@ import ( type outputConfig struct { state string - name string json bool } @@ -23,10 +22,6 @@ func (opt *StateOption) configureOutput(conf *outputConfig) { conf.state = opt.path } -func (opt *OutputNameOption) configureOutput(conf *outputConfig) { - conf.name = opt.name -} - // OutputMeta represents the JSON output of 'terraform output -json', // which resembles state format version 3 due to a historical accident. // Please see hashicorp/terraform/command/output.go. @@ -39,36 +34,24 @@ type OutputMeta struct { // Output represents the terraform output subcommand. func (tf *Terraform) Output(ctx context.Context, opts ...OutputOption) (map[string]OutputMeta, error) { - c := defaultOutputOptions - for _, o := range opts { - o.configureOutput(&c) - } - outputCmd := tf.outputCmd(ctx, c) + outputCmd := tf.outputCmd(ctx, opts...) outputs := map[string]OutputMeta{} - - if c.name != "" { - var outputValue json.RawMessage - err := tf.runTerraformCmdJSON(ctx, outputCmd, &outputValue) - if err != nil { - return nil, err - } - output := OutputMeta{} - output.Value = outputValue - outputs[c.name] = output - } else { - err := tf.runTerraformCmdJSON(ctx, outputCmd, &outputs) - if err != nil { - return nil, err - } + err := tf.runTerraformCmdJSON(ctx, outputCmd, &outputs) + if err != nil { + return nil, err } return outputs, nil } -func (tf *Terraform) outputCmd(ctx context.Context, c outputConfig) *exec.Cmd { +func (tf *Terraform) outputCmd(ctx context.Context, opts ...OutputOption) *exec.Cmd { + c := defaultOutputOptions + for _, o := range opts { + o.configureOutput(&c) + } args := []string{"output", "-no-color", "-json"} // string opts: only pass if set @@ -76,10 +59,5 @@ func (tf *Terraform) outputCmd(ctx context.Context, c outputConfig) *exec.Cmd { args = append(args, "-state="+c.state) } - // output name: only pass if set - if c.name != "" { - args = append(args, c.name) - } - return tf.buildTerraformCmd(ctx, nil, args...) } diff --git a/tfexec/output_test.go b/tfexec/output_test.go index de33639f..6a7d5305 100644 --- a/tfexec/output_test.go +++ b/tfexec/output_test.go @@ -19,8 +19,7 @@ func TestOutputCmd(t *testing.T) { tf.SetEnv(map[string]string{}) t.Run("defaults", func(t *testing.T) { - var config = outputConfig{} - outputCmd := tf.outputCmd(context.Background(), config) + outputCmd := tf.outputCmd(context.Background()) assertCmd(t, []string{ "output", @@ -30,9 +29,8 @@ func TestOutputCmd(t *testing.T) { }) t.Run("override all defaults", func(t *testing.T) { - var config = outputConfig{} - config.state = "teststate" - outputCmd := tf.outputCmd(context.Background(), config) + outputCmd := tf.outputCmd(context.Background(), + State("teststate")) assertCmd(t, []string{ "output", @@ -41,19 +39,4 @@ func TestOutputCmd(t *testing.T) { "-state=teststate", }, nil, outputCmd) }) - - t.Run("defaults with single output", func(t *testing.T) { - var config = outputConfig{} - config.state = "teststate" - config.name = "testoutput" - outputCmd := tf.outputCmd(context.Background(), config) - - assertCmd(t, []string{ - "output", - "-no-color", - "-json", - "-state=teststate", - "testoutput", - }, nil, outputCmd) - }) } diff --git a/tfexec/taint.go b/tfexec/taint.go index a47a8486..232590bd 100644 --- a/tfexec/taint.go +++ b/tfexec/taint.go @@ -2,6 +2,7 @@ package tfexec import ( "context" + "fmt" "os/exec" ) @@ -22,6 +23,10 @@ func (opt *StateOption) configureTaint(conf *taintConfig) { // Taint represents the terraform taint subcommand. func (tf *Terraform) Taint(ctx context.Context, address string, opts ...TaintOption) error { + err := tf.compatible(ctx, tf0_4_1, nil) + if err != nil { + return fmt.Errorf("taint was first introduced in Terraform 0.4.1: %w", err) + } taintCmd := tf.taintCmd(ctx, address, opts...) return tf.runTerraformCmd(ctx, taintCmd) } @@ -33,7 +38,7 @@ func (tf *Terraform) taintCmd(ctx context.Context, address string, opts ...Taint o.configureTaint(&c) } - args := []string{"taint", "-allow-missing", "-lock-timeout=0s", "-lock=true"} + args := []string{"taint", "-no-color"} // string opts: only pass if set if c.state != "" { diff --git a/tfexec/taint_test.go b/tfexec/taint_test.go index 16e89694..19dae1bc 100644 --- a/tfexec/taint_test.go +++ b/tfexec/taint_test.go @@ -23,9 +23,7 @@ func TestTaintCmd(t *testing.T) { assertCmd(t, []string{ "taint", - "-allow-missing", - "-lock-timeout=0s", - "-lock=true", + "-no-color", "aws_instance.foo", }, nil, taintCmd) }) @@ -35,9 +33,7 @@ func TestTaintCmd(t *testing.T) { assertCmd(t, []string{ "taint", - "-allow-missing", - "-lock-timeout=0s", - "-lock=true", + "-no-color", "-state=teststate", "aws_instance.foo", }, nil, taintCmd) diff --git a/tfexec/untaint.go b/tfexec/untaint.go index 2df66431..9da0bd6b 100644 --- a/tfexec/untaint.go +++ b/tfexec/untaint.go @@ -2,6 +2,7 @@ package tfexec import ( "context" + "fmt" "os/exec" ) @@ -21,7 +22,11 @@ func (opt *StateOption) configureUnTaint(conf *untaintConfig) { } // Untaint represents the terraform untaint subcommand. -func (tf *Terraform) UnTaint(ctx context.Context, address string, opts ...UnTaintOption) error { +func (tf *Terraform) Untaint(ctx context.Context, address string, opts ...UnTaintOption) error { + err := tf.compatible(ctx, tf0_6_13, nil) + if err != nil { + return fmt.Errorf("untaint was first introduced in Terraform 0.6.13: %w", err) + } unTaintCmd := tf.unTaintCmd(ctx, address, opts...) return tf.runTerraformCmd(ctx, unTaintCmd) } @@ -33,7 +38,7 @@ func (tf *Terraform) unTaintCmd(ctx context.Context, address string, opts ...UnT o.configureUnTaint(&c) } - args := []string{"untaint", "-allow-missing", "-lock-timeout=0s", "-lock=true", "-no-color"} + args := []string{"untaint", "-no-color"} // string opts: only pass if set if c.state != "" { diff --git a/tfexec/untaint_test.go b/tfexec/untaint_test.go index a75f99b1..e738db90 100644 --- a/tfexec/untaint_test.go +++ b/tfexec/untaint_test.go @@ -23,9 +23,6 @@ func TestUnTaintCmd(t *testing.T) { assertCmd(t, []string{ "untaint", - "-allow-missing", - "-lock-timeout=0s", - "-lock=true", "-no-color", "aws_instance.foo", }, nil, unTaintCmd) @@ -36,9 +33,6 @@ func TestUnTaintCmd(t *testing.T) { assertCmd(t, []string{ "untaint", - "-allow-missing", - "-lock-timeout=0s", - "-lock=true", "-no-color", "-state=teststate", "aws_instance.foo", diff --git a/tfexec/version.go b/tfexec/version.go index f42fbfc3..2c60ea43 100644 --- a/tfexec/version.go +++ b/tfexec/version.go @@ -14,6 +14,8 @@ import ( ) var ( + tf0_4_1 = version.Must(version.NewVersion("0.4.1")) + tf0_6_13 = version.Must(version.NewVersion("0.6.13")) tf0_7_7 = version.Must(version.NewVersion("0.7.7")) tf0_10_0 = version.Must(version.NewVersion("0.10.0")) tf0_12_0 = version.Must(version.NewVersion("0.12.0")) From d9477de687d7000ef4ffb2b16dc2cda57ed8c01b Mon Sep 17 00:00:00 2001 From: Rambabu Duddukuri Date: Tue, 30 Nov 2021 11:12:47 -0800 Subject: [PATCH 3/5] Add support for taint/untaint sub command --- CHANGELOG.md | 1 - tfexec/output.go | 2 +- tfexec/untaint.go | 20 ++++++++++---------- tfexec/untaint_test.go | 10 +++++----- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2472ee07..9d4e6dba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,6 @@ FEATURES: - Add support for `workspace delete` command [GH-212] - Add support for `workspace show` command [GH-245] - Add support for `force-unlock` command [GH-223] - - Add support for `taint`/`untaint` command [GH-251] # 0.15.0 (October 05, 2021) diff --git a/tfexec/output.go b/tfexec/output.go index c8f984b3..b16b8b72 100644 --- a/tfexec/output.go +++ b/tfexec/output.go @@ -34,7 +34,6 @@ type OutputMeta struct { // Output represents the terraform output subcommand. func (tf *Terraform) Output(ctx context.Context, opts ...OutputOption) (map[string]OutputMeta, error) { - outputCmd := tf.outputCmd(ctx, opts...) outputs := map[string]OutputMeta{} @@ -52,6 +51,7 @@ func (tf *Terraform) outputCmd(ctx context.Context, opts ...OutputOption) *exec. for _, o := range opts { o.configureOutput(&c) } + args := []string{"output", "-no-color", "-json"} // string opts: only pass if set diff --git a/tfexec/untaint.go b/tfexec/untaint.go index 9da0bd6b..5ad56d5b 100644 --- a/tfexec/untaint.go +++ b/tfexec/untaint.go @@ -10,32 +10,32 @@ type untaintConfig struct { state string } -var defaultUnTaintOptions = untaintConfig{} +var defaultUntaintOptions = untaintConfig{} // OutputOption represents options used in the Output method. -type UnTaintOption interface { - configureUnTaint(*untaintConfig) +type UntaintOption interface { + configureUntaint(*untaintConfig) } -func (opt *StateOption) configureUnTaint(conf *untaintConfig) { +func (opt *StateOption) configureUntaint(conf *untaintConfig) { conf.state = opt.path } // Untaint represents the terraform untaint subcommand. -func (tf *Terraform) Untaint(ctx context.Context, address string, opts ...UnTaintOption) error { +func (tf *Terraform) Untaint(ctx context.Context, address string, opts ...UntaintOption) error { err := tf.compatible(ctx, tf0_6_13, nil) if err != nil { return fmt.Errorf("untaint was first introduced in Terraform 0.6.13: %w", err) } - unTaintCmd := tf.unTaintCmd(ctx, address, opts...) - return tf.runTerraformCmd(ctx, unTaintCmd) + untaintCmd := tf.untaintCmd(ctx, address, opts...) + return tf.runTerraformCmd(ctx, untaintCmd) } -func (tf *Terraform) unTaintCmd(ctx context.Context, address string, opts ...UnTaintOption) *exec.Cmd { - c := defaultUnTaintOptions +func (tf *Terraform) untaintCmd(ctx context.Context, address string, opts ...UntaintOption) *exec.Cmd { + c := defaultUntaintOptions for _, o := range opts { - o.configureUnTaint(&c) + o.configureUntaint(&c) } args := []string{"untaint", "-no-color"} diff --git a/tfexec/untaint_test.go b/tfexec/untaint_test.go index e738db90..75da8110 100644 --- a/tfexec/untaint_test.go +++ b/tfexec/untaint_test.go @@ -7,7 +7,7 @@ import ( "github.com/hashicorp/terraform-exec/tfexec/internal/testutil" ) -func TestUnTaintCmd(t *testing.T) { +func TestUntaintCmd(t *testing.T) { td := t.TempDir() tf, err := NewTerraform(td, tfVersion(t, testutil.Latest013)) @@ -19,23 +19,23 @@ func TestUnTaintCmd(t *testing.T) { tf.SetEnv(map[string]string{}) t.Run("defaults", func(t *testing.T) { - unTaintCmd := tf.unTaintCmd(context.Background(), "aws_instance.foo") + untaintCmd := tf.untaintCmd(context.Background(), "aws_instance.foo") assertCmd(t, []string{ "untaint", "-no-color", "aws_instance.foo", - }, nil, unTaintCmd) + }, nil, untaintCmd) }) t.Run("override all defaults", func(t *testing.T) { - unTaintCmd := tf.unTaintCmd(context.Background(), "aws_instance.foo", State("teststate")) + untaintCmd := tf.untaintCmd(context.Background(), "aws_instance.foo", State("teststate")) assertCmd(t, []string{ "untaint", "-no-color", "-state=teststate", "aws_instance.foo", - }, nil, unTaintCmd) + }, nil, untaintCmd) }) } From 135e6dec20b895ba2f342995e91d05409087fe47 Mon Sep 17 00:00:00 2001 From: Rambabu Duddukuri Date: Tue, 30 Nov 2021 12:15:37 -0800 Subject: [PATCH 4/5] Add support for taint/untaint sub command --- tfexec/options.go | 10 ++++++++++ tfexec/taint.go | 31 +++++++++++++++++++++++++++++-- tfexec/taint_test.go | 10 +++++++++- tfexec/untaint.go | 31 +++++++++++++++++++++++++++++-- tfexec/untaint_test.go | 10 +++++++++- 5 files changed, 86 insertions(+), 6 deletions(-) diff --git a/tfexec/options.go b/tfexec/options.go index 71638895..4c3e3567 100644 --- a/tfexec/options.go +++ b/tfexec/options.go @@ -14,6 +14,16 @@ func AllowMissingConfig(allowMissingConfig bool) *AllowMissingConfigOption { return &AllowMissingConfigOption{allowMissingConfig} } +// AllowMissingOption represents the -allow-missing flag. +type AllowMissingOption struct { + allowMissing bool +} + +// AllowMissing represents the -allow-missing flag. +func AllowMissing(allowMissing bool) *AllowMissingOption { + return &AllowMissingOption{allowMissing} +} + // BackendOption represents the -backend flag. type BackendOption struct { backend bool diff --git a/tfexec/taint.go b/tfexec/taint.go index 232590bd..cd69df30 100644 --- a/tfexec/taint.go +++ b/tfexec/taint.go @@ -4,13 +4,20 @@ import ( "context" "fmt" "os/exec" + "strconv" ) type taintConfig struct { - state string + state string + allowMissing bool + lock bool + lockTimeout string } -var defaultTaintOptions = taintConfig{} +var defaultTaintOptions = taintConfig{ + allowMissing: false, + lock: true, +} // TaintOption represents options used in the Taint method. type TaintOption interface { @@ -21,6 +28,18 @@ func (opt *StateOption) configureTaint(conf *taintConfig) { conf.state = opt.path } +func (opt *AllowMissingOption) configureTaint(conf *taintConfig) { + conf.allowMissing = opt.allowMissing +} + +func (opt *LockOption) configureTaint(conf *taintConfig) { + conf.lock = opt.lock +} + +func (opt *LockTimeoutOption) configureTaint(conf *taintConfig) { + conf.lockTimeout = opt.timeout +} + // Taint represents the terraform taint subcommand. func (tf *Terraform) Taint(ctx context.Context, address string, opts ...TaintOption) error { err := tf.compatible(ctx, tf0_4_1, nil) @@ -40,11 +59,19 @@ func (tf *Terraform) taintCmd(ctx context.Context, address string, opts ...Taint args := []string{"taint", "-no-color"} + if c.lockTimeout != "" { + args = append(args, "-lock-timeout="+c.lockTimeout) + } + // string opts: only pass if set if c.state != "" { args = append(args, "-state="+c.state) } + args = append(args, "-lock="+strconv.FormatBool(c.lock)) + if c.allowMissing { + args = append(args, "-allow-missing") + } args = append(args, address) return tf.buildTerraformCmd(ctx, nil, args...) diff --git a/tfexec/taint_test.go b/tfexec/taint_test.go index 19dae1bc..67f1d5ef 100644 --- a/tfexec/taint_test.go +++ b/tfexec/taint_test.go @@ -24,17 +24,25 @@ func TestTaintCmd(t *testing.T) { assertCmd(t, []string{ "taint", "-no-color", + "-lock=true", "aws_instance.foo", }, nil, taintCmd) }) t.Run("override all defaults", func(t *testing.T) { - taintCmd := tf.taintCmd(context.Background(), "aws_instance.foo", State("teststate")) + taintCmd := tf.taintCmd(context.Background(), "aws_instance.foo", + State("teststate"), + AllowMissing(true), + LockTimeout("200s"), + Lock(false)) assertCmd(t, []string{ "taint", "-no-color", + "-lock-timeout=200s", "-state=teststate", + "-lock=false", + "-allow-missing", "aws_instance.foo", }, nil, taintCmd) }) diff --git a/tfexec/untaint.go b/tfexec/untaint.go index 5ad56d5b..bda12727 100644 --- a/tfexec/untaint.go +++ b/tfexec/untaint.go @@ -4,13 +4,20 @@ import ( "context" "fmt" "os/exec" + "strconv" ) type untaintConfig struct { - state string + state string + allowMissing bool + lock bool + lockTimeout string } -var defaultUntaintOptions = untaintConfig{} +var defaultUntaintOptions = untaintConfig{ + allowMissing: false, + lock: true, +} // OutputOption represents options used in the Output method. type UntaintOption interface { @@ -21,6 +28,18 @@ func (opt *StateOption) configureUntaint(conf *untaintConfig) { conf.state = opt.path } +func (opt *AllowMissingOption) configureUntaint(conf *untaintConfig) { + conf.allowMissing = opt.allowMissing +} + +func (opt *LockOption) configureUntaint(conf *untaintConfig) { + conf.lock = opt.lock +} + +func (opt *LockTimeoutOption) configureUntaint(conf *untaintConfig) { + conf.lockTimeout = opt.timeout +} + // Untaint represents the terraform untaint subcommand. func (tf *Terraform) Untaint(ctx context.Context, address string, opts ...UntaintOption) error { err := tf.compatible(ctx, tf0_6_13, nil) @@ -40,11 +59,19 @@ func (tf *Terraform) untaintCmd(ctx context.Context, address string, opts ...Unt args := []string{"untaint", "-no-color"} + if c.lockTimeout != "" { + args = append(args, "-lock-timeout="+c.lockTimeout) + } + // string opts: only pass if set if c.state != "" { args = append(args, "-state="+c.state) } + args = append(args, "-lock="+strconv.FormatBool(c.lock)) + if c.allowMissing { + args = append(args, "-allow-missing") + } args = append(args, address) return tf.buildTerraformCmd(ctx, nil, args...) diff --git a/tfexec/untaint_test.go b/tfexec/untaint_test.go index 75da8110..99075f45 100644 --- a/tfexec/untaint_test.go +++ b/tfexec/untaint_test.go @@ -24,17 +24,25 @@ func TestUntaintCmd(t *testing.T) { assertCmd(t, []string{ "untaint", "-no-color", + "-lock=true", "aws_instance.foo", }, nil, untaintCmd) }) t.Run("override all defaults", func(t *testing.T) { - untaintCmd := tf.untaintCmd(context.Background(), "aws_instance.foo", State("teststate")) + untaintCmd := tf.untaintCmd(context.Background(), "aws_instance.foo", + State("teststate"), + AllowMissing(true), + LockTimeout("200s"), + Lock(false)) assertCmd(t, []string{ "untaint", "-no-color", + "-lock-timeout=200s", "-state=teststate", + "-lock=false", + "-allow-missing", "aws_instance.foo", }, nil, untaintCmd) }) From 48909eb2229fd151848ab2c85a7c3be6b41df632 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Wed, 1 Dec 2021 09:40:28 +0100 Subject: [PATCH 5/5] Update tfexec/internal/e2etest/untaint_test.go --- tfexec/internal/e2etest/untaint_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tfexec/internal/e2etest/untaint_test.go b/tfexec/internal/e2etest/untaint_test.go index 507c93fb..d5f57210 100644 --- a/tfexec/internal/e2etest/untaint_test.go +++ b/tfexec/internal/e2etest/untaint_test.go @@ -28,7 +28,7 @@ func TestUntaint(t *testing.T) { err = tf.Untaint(context.Background(), "null_resource.foo") if err != nil { - t.Fatalf("error running UnTaint: %s", err) + t.Fatalf("error running Untaint: %s", err) } }) }