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

Fix issue #20 "unexpected end of JSON input" error #64

Merged
merged 5 commits into from
Jan 27, 2024
Merged
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
39 changes: 19 additions & 20 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,33 @@ name: Build

on:
push:
branches: [ main ]
branches: [main]
pull_request:
branches: [ main ]
branches: [main]

jobs:

build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Run Gosec Security Scanner
uses: securego/gosec@master
with:
args: -exclude=G204 ./...
- name: Run Gosec Security Scanner
uses: securego/gosec@master
with:
args: -exclude=G204 ./...

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.21"

- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: latest
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: latest

- name: Test
run: go test -v ./...
- name: Test
run: go test -v ./...

- name: Build
run: go build
- name: Build
run: go build
97 changes: 51 additions & 46 deletions .github/workflows/demo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,49 +11,54 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_wrapper: false

- name: Install terraform-plan-summary
run: |
REPO="dineshba/tf-summarize"
curl -LO https://github.com/$REPO/releases/latest/download/tf-summarize_linux_amd64.zip
tmpDir=$(mktemp -d -t tmp.XXXXXXXXXX)
mv tf-summarize_linux_amd64.zip $tmpDir
cd $tmpDir
unzip tf-summarize_linux_amd64.zip
chmod +x tf-summarize
echo $PWD >> $GITHUB_PATH
- name: Print tf-summarize version and help
run: |
tf-summarize -v
tf-summarize -h
- name: Terraform Init
run: terraform init
working-directory: ./example

- name: Terraform Plan
run: terraform plan -out=tfplan -refresh=false # -refresh=false is only for demo workflow
working-directory: ./example

- name: summary in table format
run: terraform show -json tfplan | tf-summarize
working-directory: ./example

- name: summary in tree format
run: terraform show -json tfplan | tf-summarize -tree
working-directory: ./example

- name: summary in separate tree format
run: terraform show -json tfplan | tf-summarize -separate-tree
working-directory: ./example

- name: summary in draw visual tree format
run: terraform show -json tfplan | tf-summarize -tree -draw
working-directory: ./example
- uses: actions/checkout@v3

- name: Set Terraform version
id: set-terraform-version
run: echo "terraform-version=$(cat example/.terraform-version)" >> $GITHUB_OUTPUT

- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_wrapper: false
terraform_version: ${{ steps.set-terraform-version.outputs.terraform-version }}

- name: Install terraform-plan-summary
run: |
REPO="dineshba/tf-summarize"
curl -LO https://github.com/$REPO/releases/latest/download/tf-summarize_linux_amd64.zip
tmpDir=$(mktemp -d -t tmp.XXXXXXXXXX)
mv tf-summarize_linux_amd64.zip $tmpDir
cd $tmpDir
unzip tf-summarize_linux_amd64.zip
chmod +x tf-summarize
echo $PWD >> $GITHUB_PATH
- name: Print tf-summarize version and help
run: |
tf-summarize -v
tf-summarize -h
- name: Terraform Init
run: terraform init
working-directory: ./example

- name: Terraform Plan
run: terraform plan -out=tfplan -refresh=false # -refresh=false is only for demo workflow
working-directory: ./example

- name: summary in table format
run: terraform show -json tfplan | tf-summarize
working-directory: ./example

- name: summary in tree format
run: terraform show -json tfplan | tf-summarize -tree
working-directory: ./example

- name: summary in separate tree format
run: terraform show -json tfplan | tf-summarize -separate-tree
working-directory: ./example

- name: summary in draw visual tree format
run: terraform show -json tfplan | tf-summarize -tree -draw
working-directory: ./example
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ tf-summarize
**/.envrc
**/.terraform
*.swp

example/tfplan
22 changes: 21 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
TERRAFORM_VERSION:=$(shell cat example/.terraform-version)

.PHONY: help
help: ## prints help (only for tasks with comment)
@grep -E '^[a-zA-Z._-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
Expand All @@ -17,4 +19,22 @@ test: lint
i: install ## build and install to /usr/local/bin/

lint:
golangci-lint run --timeout 10m -v
golangci-lint run --timeout 10m -v

define generate-example
docker run \
--interactive \
--tty \
--volume $(shell pwd):/src \
--workdir /src/example \
--entrypoint /bin/sh \
hashicorp/terraform:$(1) \
-c \
"terraform init && \
terraform plan -out tfplan && \
terraform show -json tfplan > tfplan.json"
endef

example:
$(call generate-example,$(TERRAFORM_VERSION))
.PHONY: example
1 change: 1 addition & 0 deletions example/.terraform-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.1.4
1 change: 1 addition & 0 deletions example/tfplan.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func main() {
err := validateFlags(*tree, *separateTree, *drawable, *md, args)
logIfErrorAndExit("invalid input flags: %s\n", err, flag.Usage)

newReader, err := reader.CreateReader(os.Stdin, args)
newReader, err := reader.CreateReader(args)
logIfErrorAndExit("error creating input reader: %s\n", err, flag.Usage)

input, err := newReader.Read()
Expand Down
95 changes: 95 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package main

import (
"fmt"
"log"
"os"
"os/exec"
"strings"
"testing"
)

const (
testVersion string = "test"
testExecutable string = "tf-summarize-test"
)

func TestMain(m *testing.M) {
// compile a 'tf-summarize' for use in running tests
exe := exec.Command("go", "build", "-ldflags", fmt.Sprintf("-X main.version=%s", testVersion), "-o", testExecutable)
err := exe.Run()
if err != nil {
os.Exit(1)
}

m.Run()

// delete the compiled tf-summarize
err = os.Remove(testExecutable)
if err != nil {
log.Fatal(err)
}
}

func TestVersionArg(t *testing.T) {
args := []string{
"-v",
}

for _, arg := range args {
t.Run(fmt.Sprintf("when tf-summarize is passed '%s'", arg), func(t *testing.T) {
output, err := exec.Command(fmt.Sprintf("./%s", testExecutable), arg).CombinedOutput()
if err != nil {
t.Errorf("expected '%s' not to cause error; got '%v'", arg, err)
}

if !strings.Contains(string(output), testVersion) {
t.Errorf("expected '%s' to output version '%s'; got '%s'", arg, testVersion, output)
}
})
}
}

func TestTFSummarize(t *testing.T) {
tests := []struct {
command string
expectedError error
expectedOutput string
}{{
command: fmt.Sprintf("./%s -md example/tfplan.json", testExecutable),
expectedOutput: "basic.txt",
}, {
command: fmt.Sprintf("cat example/tfplan.json | ./%s -md", testExecutable),
expectedOutput: "basic.txt",
}}

for _, test := range tests {
t.Run(fmt.Sprintf("when tf-summarize is passed '%q'", test.command), func(t *testing.T) {
output, err := exec.Command("/bin/sh", "-c", test.command).CombinedOutput()
if err != nil && test.expectedError == nil {
t.Errorf("expected '%s' not to error; got '%v'", test.command, err)
}

b, err := os.ReadFile(fmt.Sprintf("testdata/%s", test.expectedOutput))
if err != nil {
t.Errorf("error reading file '%s': '%v'", test.expectedOutput, err)
}

expected := string(b)

if test.expectedError != nil && err == nil {
t.Errorf("expected error '%s'; got '%v'", test.expectedError.Error(), err)
}

if test.expectedError != nil && err != nil && test.expectedError.Error() != err.Error() {
t.Errorf("expected error '%s'; got '%v'", test.expectedError.Error(), err.Error())
}

if string(output) != expected {
t.Logf("expected output: \n%s", expected)
t.Logf("got output: \n%s", output)
t.Errorf("received unexpected output from '%s'", test.command)
}
})
}
}
24 changes: 14 additions & 10 deletions reader/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package reader

import (
"bufio"
"errors"
"fmt"
"io"
"os"
"strings"
)

type Reader interface {
Expand All @@ -22,19 +23,22 @@ func readFile(f io.Reader) ([]byte, error) {
input = append(input, line...)
}
if err != io.EOF {
return nil, fmt.Errorf("error reading file: %s", err.Error())
return nil, fmt.Errorf("error reading input: %s", err.Error())
}
if len(input) == 0 {
return nil, errors.New("no input data; expected input via a non-empty file or via STDIN")
}
return input, nil
}

func CreateReader(stdin *os.File, args []string) (Reader, error) {
stat, _ := stdin.Stat()
if (stat.Mode() & os.ModeCharDevice) == 0 {
return NewStdinReader(), nil
dineshba marked this conversation as resolved.
Show resolved Hide resolved
func CreateReader(args []string) (Reader, error) {
if len(args) > 1 {
return nil, fmt.Errorf("expected input via a single filename argument or via STDIN; received multiple arguments: %s", strings.Join(args, ", "))
}
if len(args) < 1 {
return nil, fmt.Errorf("should have either stdin input through pipe or first argument should be file")

if len(args) == 1 {
return NewFileReader(args[0]), nil
}
fileName := args[0]
return NewFileReader(fileName), nil

return NewStdinReader(), nil
}
9 changes: 9 additions & 0 deletions testdata/basic.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
| CHANGE | RESOURCE |
|--------|------------------------------------------------------------------------|
| add | `github_repository.terraform_plan_summary` |
| | `module.github["demo-repository"].github_branch.development` |
| | `module.github["demo-repository"].github_branch.main` |
| | `module.github["demo-repository"].github_repository.repository` |
| | `module.github["terraform-plan-summary"].github_branch.development` |
| | `module.github["terraform-plan-summary"].github_branch.main` |
| | `module.github["terraform-plan-summary"].github_repository.repository` |
Loading