From 10d7ef490d53500704659b4d09a5053a550e797b Mon Sep 17 00:00:00 2001 From: Paddy Carver Date: Sat, 12 Sep 2020 04:03:31 -0700 Subject: [PATCH 1/2] Add ShowRawPlanFile The SDK used a non-JSON version of ShowPlanFile to return the human-friendly, opaque-to-machines plan output during testing. With the switch to tfexec, this is no longer possible, as all `terraform show` commands have the `-json` flag applied, meaning all output will be JSON output. This adds a new function, `ShowRawPlanFile`, which returns a string of the output of `terraform show` _without_ the `-json` flag, showing the raw contents of the plan file. A new function was chosen to avoid overloading the existing ShowPlanFile, and because the existing ShowPlanFile returns a `tfjson.Plan` type, which we can't shoehorn a non-JSON return type into easily. Rather than complicate the return type, it felt like a new function was a better solution. --- tfexec/show.go | 50 ++++++++++++++++++++++++++++++++++++++++----- tfexec/show_test.go | 6 +++--- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/tfexec/show.go b/tfexec/show.go index 340c7439..4a5ac5bf 100644 --- a/tfexec/show.go +++ b/tfexec/show.go @@ -1,6 +1,7 @@ package tfexec import ( + "bytes" "context" "fmt" "os/exec" @@ -45,7 +46,7 @@ func (tf *Terraform) Show(ctx context.Context, opts ...ShowOption) (*tfjson.Stat mergeEnv[reattachEnvVar] = reattachStr } - showCmd := tf.showCmd(ctx, mergeEnv) + showCmd := tf.showCmd(ctx, true, mergeEnv) var ret tfjson.State err = tf.runTerraformCmdJSON(showCmd, &ret) @@ -87,7 +88,7 @@ func (tf *Terraform) ShowStateFile(ctx context.Context, statePath string, opts . mergeEnv[reattachEnvVar] = reattachStr } - showCmd := tf.showCmd(ctx, mergeEnv, statePath) + showCmd := tf.showCmd(ctx, true, mergeEnv, statePath) var ret tfjson.State err = tf.runTerraformCmdJSON(showCmd, &ret) @@ -129,7 +130,7 @@ func (tf *Terraform) ShowPlanFile(ctx context.Context, planPath string, opts ... mergeEnv[reattachEnvVar] = reattachStr } - showCmd := tf.showCmd(ctx, mergeEnv, planPath) + showCmd := tf.showCmd(ctx, true, mergeEnv, planPath) var ret tfjson.Plan err = tf.runTerraformCmdJSON(showCmd, &ret) @@ -146,8 +147,47 @@ func (tf *Terraform) ShowPlanFile(ctx context.Context, planPath string, opts ... } -func (tf *Terraform) showCmd(ctx context.Context, mergeEnv map[string]string, args ...string) *exec.Cmd { - allArgs := []string{"show", "-json", "-no-color"} +// ShowRawPlanFile reads a given plan file and outputs the plan in a +// human-friendly, opaque format. +func (tf *Terraform) ShowRawPlanFile(ctx context.Context, planPath string, opts ...ShowOption) (string, error) { + if planPath == "" { + return "", fmt.Errorf("planPath cannot be blank: use Show() if not passing planPath") + } + + c := defaultShowOptions + + for _, o := range opts { + o.configureShow(&c) + } + + mergeEnv := map[string]string{} + if c.reattachInfo != nil { + reattachStr, err := c.reattachInfo.marshalString() + if err != nil { + return "", err + } + mergeEnv[reattachEnvVar] = reattachStr + } + + showCmd := tf.showCmd(ctx, false, mergeEnv, planPath) + + var ret bytes.Buffer + showCmd.Stdout = &ret + err := tf.runTerraformCmd(showCmd) + if err != nil { + return "", err + } + + return ret.String(), nil + +} + +func (tf *Terraform) showCmd(ctx context.Context, jsonOutput bool, mergeEnv map[string]string, args ...string) *exec.Cmd { + allArgs := []string{"show"} + if jsonOutput { + allArgs = append(allArgs, "-json") + } + allArgs = append(allArgs, "-no-color") allArgs = append(allArgs, args...) return tf.buildTerraformCmd(ctx, mergeEnv, allArgs...) diff --git a/tfexec/show_test.go b/tfexec/show_test.go index b5cdb061..f7193e47 100644 --- a/tfexec/show_test.go +++ b/tfexec/show_test.go @@ -21,7 +21,7 @@ func TestShowCmd(t *testing.T) { tf.SetEnv(map[string]string{}) // defaults - showCmd := tf.showCmd(context.Background(), nil) + showCmd := tf.showCmd(context.Background(), true, nil) assertCmd(t, []string{ "show", @@ -42,7 +42,7 @@ func TestShowStateFileCmd(t *testing.T) { // empty env, to avoid environ mismatch in testing tf.SetEnv(map[string]string{}) - showCmd := tf.showCmd(context.Background(), nil, "statefilepath") + showCmd := tf.showCmd(context.Background(), true, nil, "statefilepath") assertCmd(t, []string{ "show", @@ -64,7 +64,7 @@ func TestShowPlanFileCmd(t *testing.T) { // empty env, to avoid environ mismatch in testing tf.SetEnv(map[string]string{}) - showCmd := tf.showCmd(context.Background(), nil, "planfilepath") + showCmd := tf.showCmd(context.Background(), true, nil, "planfilepath") assertCmd(t, []string{ "show", From b8e9c7a92e0fba5cc523677313d1c3ddc08750f6 Mon Sep 17 00:00:00 2001 From: Paddy Carver Date: Sat, 12 Sep 2020 04:12:37 -0700 Subject: [PATCH 2/2] Rename to ShowPlanFileRaw. @paultyng suggested `ShowPlanFileRaw`, so we'll run with that, instead, because I'm ambivalent. --- tfexec/show.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tfexec/show.go b/tfexec/show.go index 4a5ac5bf..e2f52870 100644 --- a/tfexec/show.go +++ b/tfexec/show.go @@ -147,9 +147,9 @@ func (tf *Terraform) ShowPlanFile(ctx context.Context, planPath string, opts ... } -// ShowRawPlanFile reads a given plan file and outputs the plan in a +// ShowPlanFileRaw reads a given plan file and outputs the plan in a // human-friendly, opaque format. -func (tf *Terraform) ShowRawPlanFile(ctx context.Context, planPath string, opts ...ShowOption) (string, error) { +func (tf *Terraform) ShowPlanFileRaw(ctx context.Context, planPath string, opts ...ShowOption) (string, error) { if planPath == "" { return "", fmt.Errorf("planPath cannot be blank: use Show() if not passing planPath") }