Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ go 1.21

require (
github.com/fatih/color v1.15.0
github.com/golang/mock v1.6.0
github.com/hashicorp/terraform-json v0.20.0
github.com/m1gwings/treedrawer v0.3.3-beta
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249
github.com/olekukonko/tablewriter v0.0.5
github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.9.0
)

require (
Expand All @@ -22,5 +23,5 @@ require (
github.com/zclconf/go-cty v1.14.1 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.11.0 // indirect
gopkg.in/yaml.v3 v3.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
37 changes: 30 additions & 7 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/terraform-json v0.20.0 h1:cJcvn4gIOTi0SD7pIy+xiofV1zFA3hza+6K+fo52IX8=
Expand All @@ -25,18 +27,39 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA=
github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
14 changes: 11 additions & 3 deletions parser/binary-parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,30 @@ import (
tfjson "github.com/hashicorp/terraform-json"
)

type DefaultCommandExecutor struct{}

func (e DefaultCommandExecutor) CombinedOutput(name string, args ...string) ([]byte, error) {
cmd := exec.Command(name, args...)
return cmd.CombinedOutput()
}

type BinaryParser struct {
fileName string
executor CommandExecutor
}

func (j BinaryParser) Parse() (tfjson.Plan, error) {
tfbinary := "terraform"
if tfoverride, ok := os.LookupEnv("TF_BINARY"); ok {
tfbinary = tfoverride
}
cmd := exec.Command(tfbinary, "show", "-json", j.fileName)
output, err := cmd.CombinedOutput()
output, err := j.executor.CombinedOutput(tfbinary, "show", "-json", j.fileName)
if err != nil {
return tfjson.Plan{}, fmt.Errorf(
"error when running 'terraform show -json %s': \n%s\n\n%s",
j.fileName, output, "Make sure you are running in terraform directory and terraform init is done")
}
plan := tfjson.Plan{}
var plan tfjson.Plan
err = json.Unmarshal(output, &plan)
if err != nil {
return tfjson.Plan{}, fmt.Errorf("error when parsing input: %s", err.Error())
Expand All @@ -36,5 +43,6 @@ func (j BinaryParser) Parse() (tfjson.Plan, error) {
func NewBinaryParser(fileName string) Parser {
return BinaryParser{
fileName: fileName,
executor: DefaultCommandExecutor{},
}
}
160 changes: 160 additions & 0 deletions parser/binary-parser_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// binary_parser_test.go
package parser

import (
"encoding/json"
"testing"

"github.com/dineshba/tf-summarize/parser/mocks"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"

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

// MockCommandExecutor mocks the command execution.
type MockCommandExecutor struct {
Output []byte
Err error
ActualName string
ActualArgs []string
}

func (e *MockCommandExecutor) CombinedOutput(name string, args ...string) ([]byte, error) {
e.ActualName = name
e.ActualArgs = args
return e.Output, e.Err
}

func TestBinaryParser_Parse_Success(t *testing.T) {
// Initialize GoMock controller
ctrl := gomock.NewController(t)
defer ctrl.Finish()

// Prepare mock output
mockPlan := tfjson.Plan{
FormatVersion: "0.1",
TerraformVersion: "1.0.0",
}
output, err := json.Marshal(mockPlan)
assert.NoError(t, err, "Failed to marshal mock plan")

// Create mock executor
mockExecutor := mocks.NewMockCommandExecutor(ctrl)
tfbinary := "terraform"

// Set up expected call
mockExecutor.
EXPECT().
CombinedOutput(tfbinary, "show", "-json", "mock-file").
Return(output, nil)

// Create parser with mock executor
parser := BinaryParser{
fileName: "mock-file",
executor: mockExecutor,
}

// Call Parse
parsedPlan, err := parser.Parse()

// assertions
assert.NoError(t, err, "Expected no error")
assert.Equal(t, mockPlan, parsedPlan)
}
func TestBinaryParser_Parse_EmptyOutput(t *testing.T) {
// Initialize GoMock controller
ctrl := gomock.NewController(t)
defer ctrl.Finish()

// Create mock executor
mockExecutor := mocks.NewMockCommandExecutor(ctrl)
tfbinary := "terraform"

// Set up expectation CombinedOutput will return empty output
mockExecutor.
EXPECT().
CombinedOutput(tfbinary, "show", "-json", "mock-file").
Return([]byte(""), nil)

// Create parser with mock executor
parser := BinaryParser{
fileName: "mock-file",
executor: mockExecutor,
}

// Call Parse
_, err := parser.Parse()

// assertions
assert.Error(t, err, "Expected error due to empty output")
assert.Contains(t, err.Error(), "error when parsing input")
}

func TestBinaryParser_Parse_MissingRequiredFields(t *testing.T) {
// Initialize GoMock controller
ctrl := gomock.NewController(t)
defer ctrl.Finish()

// Prepare mock output with missing required fields
incompleteMockOutput := map[string]interface{}{
// format_version and terraform_version are missing
"variables": map[string]interface{}{},
}
output, err := json.Marshal(incompleteMockOutput)
assert.NoError(t, err, "Failed to marshal incomplete plan")

// Create mock executor
mockExecutor := mocks.NewMockCommandExecutor(ctrl)
tfbinary := "terraform"

// Set up expectation: CombinedOutput should return incomplete output
mockExecutor.
EXPECT().
CombinedOutput(tfbinary, "show", "-json", "mock-file").
Return(output, nil)

// Create parser with mock executor
parser := BinaryParser{
fileName: "mock-file",
executor: mockExecutor,
}

// Call Parse
parsedPlan, err := parser.Parse()

// assertions
assert.Error(t, err, "Expected an error due to missing required fields")
assert.Contains(t, err.Error(), "format version is missing")
assert.Equal(t, "", parsedPlan.TerraformVersion)
}

func TestBinaryParser_Parse_InvalidJSONOutput(t *testing.T) {
// Initialize GoMock controller
ctrl := gomock.NewController(t)
defer ctrl.Finish()

// Prepare mock output with invalid json output
invalidJSON := []byte(`{"invalid-json"}`)
mockExecutor := mocks.NewMockCommandExecutor(ctrl)
tfbinary := "terraform"

// Set up expectation
mockExecutor.
EXPECT().
CombinedOutput(tfbinary, "show", "-json", "mock-file").
Return(invalidJSON, nil)

// Create parser with mock executor
parser := BinaryParser{
fileName: "mock-file",
executor: mockExecutor,
}

// Call Parse
_, err := parser.Parse()

// Assertions
assert.Error(t, err, "Expected error due to invalid JSON output")
assert.Contains(t, err.Error(), "error when parsing input")
}
7 changes: 7 additions & 0 deletions parser/command_executor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// command_executor.go
package parser

// CommandExecutor defines an interface for executing commands.
type CommandExecutor interface {
CombinedOutput(name string, args ...string) ([]byte, error)
}
54 changes: 54 additions & 0 deletions parser/mocks/mock_command_interface.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.