Skip to content

Commit

Permalink
Adding Taint/UnTaint command support (#251)
Browse files Browse the repository at this point in the history
* Adding Taint/UnTaint command support

Adding output name option support

* Add support for taint/untaint sub command

* Add support for taint/untaint sub command

* Add support for taint/untaint sub command

* Update tfexec/internal/e2etest/untaint_test.go

Co-authored-by: Radek Simko <[email protected]>
  • Loading branch information
rambabuiitk and radeksimko authored Dec 1, 2021
1 parent 6d83f4f commit ea6fa6c
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 0 deletions.
29 changes: 29 additions & 0 deletions tfexec/internal/e2etest/taint_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
34 changes: 34 additions & 0 deletions tfexec/internal/e2etest/untaint_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
10 changes: 10 additions & 0 deletions tfexec/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
78 changes: 78 additions & 0 deletions tfexec/taint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package tfexec

import (
"context"
"fmt"
"os/exec"
"strconv"
)

type taintConfig struct {
state string
allowMissing bool
lock bool
lockTimeout string
}

var defaultTaintOptions = taintConfig{
allowMissing: false,
lock: true,
}

// TaintOption represents options used in the Taint method.
type TaintOption interface {
configureTaint(*taintConfig)
}

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)
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)
}

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", "-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...)
}
49 changes: 49 additions & 0 deletions tfexec/taint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
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",
"-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"),
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)
})
}
78 changes: 78 additions & 0 deletions tfexec/untaint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package tfexec

import (
"context"
"fmt"
"os/exec"
"strconv"
)

type untaintConfig struct {
state string
allowMissing bool
lock bool
lockTimeout string
}

var defaultUntaintOptions = untaintConfig{
allowMissing: false,
lock: true,
}

// OutputOption represents options used in the Output method.
type UntaintOption interface {
configureUntaint(*untaintConfig)
}

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)
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)
}

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", "-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...)
}
49 changes: 49 additions & 0 deletions tfexec/untaint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
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",
"-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"),
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)
})
}
2 changes: 2 additions & 0 deletions tfexec/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down

0 comments on commit ea6fa6c

Please sign in to comment.