Skip to content

Commit

Permalink
Refactoring stdout related functions (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
bendbennett committed Jan 13, 2023
1 parent 9a77e42 commit f496bfe
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 105 deletions.
97 changes: 97 additions & 0 deletions helper/resource/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,109 @@
package resource

import (
"bufio"
"bytes"
"encoding/json"
"io"
"log"
"os"
"path/filepath"
"regexp"
"strings"

tfjson "github.com/hashicorp/terraform-json"

"github.com/hashicorp/terraform-plugin-testing/internal/plugintest"
)

const stdoutFile = "stdout.txt"

func unmarshalJSON(data []byte, v interface{}) error {
dec := json.NewDecoder(bytes.NewReader(data))
dec.UseNumber()
return dec.Decode(v)
}

func stdoutWriter(wd *plugintest.WorkingDir) (io.WriteCloser, error) {
return os.Create(filepath.Join(wd.GetBaseDir(), stdoutFile))
}

func stdoutReader(wd *plugintest.WorkingDir) (io.ReadCloser, error) {
return os.Open(filepath.Join(wd.GetBaseDir(), stdoutFile))
}

func getJSONOutput(wd *plugintest.WorkingDir) []string {
reader, err := stdoutReader(wd)
if err != nil {
log.Fatal(err)
}

defer reader.Close()

scanner := bufio.NewScanner(reader)
var jsonOutput []string

for scanner.Scan() {
var outer struct{}

txt := scanner.Text()

if json.Unmarshal([]byte(txt), &outer) == nil {
jsonOutput = append(jsonOutput, txt)
}
}

if err := scanner.Err(); err != nil {
log.Fatal(err)
}

return jsonOutput
}

func getJSONOutputStr(wd *plugintest.WorkingDir) string {
return strings.Join(getJSONOutput(wd), "\n")
}

func diagnosticFound(wd *plugintest.WorkingDir, r *regexp.Regexp, severity tfjson.DiagnosticSeverity) (bool, []string) {
reader, err := stdoutReader(wd)
if err != nil {
log.Fatal(err)
}

defer reader.Close()

scanner := bufio.NewScanner(reader)
var jsonOutput []string

for scanner.Scan() {
var outer struct {
Diagnostic tfjson.Diagnostic
}

txt := scanner.Text()

if json.Unmarshal([]byte(txt), &outer) == nil {
jsonOutput = append(jsonOutput, txt)

if outer.Diagnostic.Severity == "" {
continue
}

if !r.MatchString(outer.Diagnostic.Summary) && !r.MatchString(outer.Diagnostic.Detail) {
continue
}

if outer.Diagnostic.Severity != severity {
continue
}

return true, jsonOutput
}
}

if err := scanner.Err(); err != nil {
log.Fatal(err)
}

return false, jsonOutput
}
61 changes: 6 additions & 55 deletions helper/resource/testing_new.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,9 @@
package resource

import (
"bufio"
"context"
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"reflect"
"regexp"
"strings"

"github.com/google/go-cmp/cmp"
Expand Down Expand Up @@ -126,15 +120,14 @@ func runNewTest(ctx context.Context, t testing.T, c TestCase, helper *plugintest
// acts as default for import tests
var appliedCfg string

// stdoutFile is used as the handle to the file that contains
// json output from running Terraform commands.
stdoutFile, err := os.Create(filepath.Join(wd.GetBaseDir(), "stdout.txt"))
// w (io.WriteCloser) is supplied to all terraform commands that are using streaming json output.
w, err := stdoutWriter(wd)
if err != nil {
t.Fatalf("unable to create file for writing stdout: %w", err)
}
defer func() {
if stdoutFile != nil {
err := stdoutFile.Close()
if w != nil {
err := w.Close()
if err != nil {
logging.HelperResourceError(ctx,
"Error closing file containing json output from Terraform commands",
Expand Down Expand Up @@ -272,7 +265,7 @@ func runNewTest(ctx context.Context, t testing.T, c TestCase, helper *plugintest
if step.RefreshState {
logging.HelperResourceTrace(ctx, "TestStep is RefreshState mode")

err := testStepNewRefreshState(ctx, t, wd, step, providers, stdoutFile)
err := testStepNewRefreshState(ctx, t, wd, step, providers, w)

if step.ExpectError != nil {
logging.HelperResourceDebug(ctx, "Checking TestStep ExpectError")
Expand Down Expand Up @@ -337,7 +330,7 @@ func runNewTest(ctx context.Context, t testing.T, c TestCase, helper *plugintest
if step.Config != "" {
logging.HelperResourceTrace(ctx, "TestStep is Config mode")

err := testStepNewConfig(ctx, t, c, wd, step, providers, stdoutFile)
err := testStepNewConfig(ctx, t, c, wd, step, providers, w)

if step.ExpectError != nil {
logging.HelperResourceDebug(ctx, "Checking TestStep ExpectError")
Expand Down Expand Up @@ -407,48 +400,6 @@ func runNewTest(ctx context.Context, t testing.T, c TestCase, helper *plugintest
}
}

func diagnosticFound(wd *plugintest.WorkingDir, r *regexp.Regexp, severity tfjson.DiagnosticSeverity) (bool, []string) {
stdoutFile, err := os.Open(filepath.Join(wd.GetBaseDir(), "stdout.txt"))
if err != nil {
log.Fatal(err)
}

scanner := bufio.NewScanner(stdoutFile)
var jsonOutput []string

for scanner.Scan() {
var outer struct {
Diagnostic tfjson.Diagnostic
}

txt := scanner.Text()

if json.Unmarshal([]byte(txt), &outer) == nil {
jsonOutput = append(jsonOutput, txt)

if outer.Diagnostic.Severity == "" {
continue
}

if !r.MatchString(outer.Diagnostic.Summary) && !r.MatchString(outer.Diagnostic.Detail) {
continue
}

if outer.Diagnostic.Severity != severity {
continue
}

return true, jsonOutput
}
}

if err := scanner.Err(); err != nil {
log.Fatal(err)
}

return false, jsonOutput
}

func getState(ctx context.Context, t testing.T, wd *plugintest.WorkingDir) (*terraform.State, error) {
t.Helper()

Expand Down
58 changes: 11 additions & 47 deletions helper/resource/testing_new_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,10 @@
package resource

import (
"bufio"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"

tfjson "github.com/hashicorp/terraform-json"
"github.com/mitchellh/go-testing-interface"
Expand All @@ -24,34 +18,6 @@ import (
"github.com/hashicorp/terraform-plugin-testing/internal/plugintest"
)

func getJSONOutput(wd *plugintest.WorkingDir) []string {
stdoutFile, err := os.Open(filepath.Join(wd.GetBaseDir(), "stdout.txt"))
if err != nil {
log.Fatal(err)
}

scanner := bufio.NewScanner(stdoutFile)
var jsonOutput []string

for scanner.Scan() {
var outer struct {
Diagnostic tfjson.Diagnostic
}

txt := scanner.Text()

if json.Unmarshal([]byte(txt), &outer) == nil {
jsonOutput = append(jsonOutput, txt)
}
}

if err := scanner.Err(); err != nil {
log.Fatal(err)
}

return jsonOutput
}

func testStepNewConfig(ctx context.Context, t testing.T, c TestCase, wd *plugintest.WorkingDir, step TestStep, providers *providerFactories, w io.Writer) error {
t.Helper()

Expand All @@ -63,10 +29,10 @@ func testStepNewConfig(ctx context.Context, t testing.T, c TestCase, wd *plugint
// require a refresh before applying
// failing to do this will result in data sources not being updated
err = runProviderCommand(ctx, t, func() error {
return wd.Refresh(ctx)
return wd.RefreshJSON(ctx, w)
}, wd, providers)
if err != nil {
return fmt.Errorf("Error running pre-apply refresh: %w", err)
return fmt.Errorf("Error running pre-apply refresh: %s", getJSONOutputStr(wd))
}

// If this step is a PlanOnly step, skip over this first Plan and
Expand All @@ -78,12 +44,12 @@ func testStepNewConfig(ctx context.Context, t testing.T, c TestCase, wd *plugint
// Plan!
err := runProviderCommand(ctx, t, func() error {
if step.Destroy {
return wd.CreateDestroyPlan(ctx)
return wd.CreateDestroyPlanJSON(ctx, w)
}
return wd.CreatePlanJSON(ctx, w)
}, wd, providers)
if err != nil {
return fmt.Errorf("Error running pre-apply plan: %w", err)
return fmt.Errorf("Error running pre-apply plan: %s", getJSONOutputStr(wd))
}

// We need to keep a copy of the state prior to destroying such
Expand All @@ -102,16 +68,14 @@ func testStepNewConfig(ctx context.Context, t testing.T, c TestCase, wd *plugint
}

// Apply the diff, creating real resources
// TODO: Update all Errorf outputs that should use contents of streaming json
// after calling terraform-exec with `-json`.
err = runProviderCommand(ctx, t, func() error {
return wd.ApplyJSON(ctx, w)
}, wd, providers)
if err != nil {
if step.Destroy {
return fmt.Errorf("Error running destroy: %w", err)
}
return fmt.Errorf("Error running apply: %s", strings.Join(getJSONOutput(wd), "\n"))
return fmt.Errorf("Error running apply: %s", getJSONOutputStr(wd))
}

// Get the new state
Expand Down Expand Up @@ -150,12 +114,12 @@ func testStepNewConfig(ctx context.Context, t testing.T, c TestCase, wd *plugint
// do a plan
err = runProviderCommand(ctx, t, func() error {
if step.Destroy {
return wd.CreateDestroyPlan(ctx)
return wd.CreateDestroyPlanJSON(ctx, w)
}
return wd.CreatePlanJSON(ctx, w)
}, wd, providers)
if err != nil {
return fmt.Errorf("Error running post-apply plan: %w", err)
return fmt.Errorf("Error running post-apply plan: %s", getJSONOutputStr(wd))
}

var plan *tfjson.Plan
Expand Down Expand Up @@ -184,22 +148,22 @@ func testStepNewConfig(ctx context.Context, t testing.T, c TestCase, wd *plugint
// do a refresh
if !step.Destroy || (step.Destroy && !step.PreventPostDestroyRefresh) {
err := runProviderCommand(ctx, t, func() error {
return wd.Refresh(ctx)
return wd.RefreshJSON(ctx, w)
}, wd, providers)
if err != nil {
return fmt.Errorf("Error running post-apply refresh: %w", err)
return fmt.Errorf("Error running post-apply refresh: %s", getJSONOutputStr(wd))
}
}

// do another plan
err = runProviderCommand(ctx, t, func() error {
if step.Destroy {
return wd.CreateDestroyPlan(ctx)
return wd.CreateDestroyPlanJSON(ctx, w)
}
return wd.CreatePlanJSON(ctx, w)
}, wd, providers)
if err != nil {
return fmt.Errorf("Error running second post-apply plan: %w", err)
return fmt.Errorf("Error running second post-apply plan: %s", getJSONOutputStr(wd))
}

err = runProviderCommand(ctx, t, func() error {
Expand Down
6 changes: 3 additions & 3 deletions helper/resource/testing_new_refresh_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func testStepNewRefreshState(ctx context.Context, t testing.T, wd *plugintest.Wo
return wd.RefreshJSON(ctx, w)
}, wd, providers)
if err != nil {
return err
return fmt.Errorf("%s", getJSONOutputStr(wd))
}

var refreshState *terraform.State
Expand Down Expand Up @@ -65,10 +65,10 @@ func testStepNewRefreshState(ctx context.Context, t testing.T, wd *plugintest.Wo

// do a plan
err = runProviderCommand(ctx, t, func() error {
return wd.CreatePlan(ctx)
return wd.CreatePlanJSON(ctx, w)
}, wd, providers)
if err != nil {
return fmt.Errorf("Error running post-apply plan: %w", err)
return fmt.Errorf("Error running post-apply plan: %s", getJSONOutputStr(wd))
}

var plan *tfjson.Plan
Expand Down
Loading

0 comments on commit f496bfe

Please sign in to comment.