Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Taint/Untaint command support #251

Merged
merged 5 commits into from
Dec 1, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/untain_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)
}
})
}
8 changes: 8 additions & 0 deletions tfexec/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
42 changes: 32 additions & 10 deletions tfexec/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

type outputConfig struct {
state string
name string
json bool
}

Expand All @@ -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.
Expand All @@ -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...)
rambabuiitk marked this conversation as resolved.
Show resolved Hide resolved
c := defaultOutputOptions

rambabuiitk marked this conversation as resolved.
Show resolved Hide resolved
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 {

rambabuiitk marked this conversation as resolved.
Show resolved Hide resolved
args := []string{"output", "-no-color", "-json"}

Expand All @@ -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...)
}
23 changes: 20 additions & 3 deletions tfexec/output_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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)
})
}
46 changes: 46 additions & 0 deletions tfexec/taint.go
Original file line number Diff line number Diff line change
@@ -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"}
rambabuiitk marked this conversation as resolved.
Show resolved Hide resolved

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

import (
"context"
"os/exec"
)

type untaintConfig struct {
state string
}

var defaultUnTaintOptions = untaintConfig{}
rambabuiitk marked this conversation as resolved.
Show resolved Hide resolved

// OutputOption represents options used in the Output method.
type UnTaintOption interface {
rambabuiitk marked this conversation as resolved.
Show resolved Hide resolved
configureUnTaint(*untaintConfig)
}

func (opt *StateOption) configureUnTaint(conf *untaintConfig) {
rambabuiitk marked this conversation as resolved.
Show resolved Hide resolved
conf.state = opt.path
}

// Untaint represents the terraform untaint subcommand.
func (tf *Terraform) UnTaint(ctx context.Context, address string, opts ...UnTaintOption) error {
rambabuiitk marked this conversation as resolved.
Show resolved Hide resolved
unTaintCmd := tf.unTaintCmd(ctx, address, opts...)
return tf.runTerraformCmd(ctx, unTaintCmd)
}

func (tf *Terraform) unTaintCmd(ctx context.Context, address string, opts ...UnTaintOption) *exec.Cmd {
rambabuiitk marked this conversation as resolved.
Show resolved Hide resolved
c := defaultUnTaintOptions

for _, o := range opts {
o.configureUnTaint(&c)
}

args := []string{"untaint", "-allow-missing", "-lock-timeout=0s", "-lock=true", "-no-color"}
rambabuiitk marked this conversation as resolved.
Show resolved Hide resolved

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

import (
"context"
"testing"

"github.com/hashicorp/terraform-exec/tfexec/internal/testutil"
)

func TestUnTaintCmd(t *testing.T) {
rambabuiitk marked this conversation as resolved.
Show resolved Hide resolved
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")
rambabuiitk marked this conversation as resolved.
Show resolved Hide resolved

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"))
rambabuiitk marked this conversation as resolved.
Show resolved Hide resolved

assertCmd(t, []string{
"untaint",
"-allow-missing",
"-lock-timeout=0s",
"-lock=true",
"-no-color",
"-state=teststate",
"aws_instance.foo",
}, nil, unTaintCmd)
})
}