Skip to content

Commit f31a8a8

Browse files
author
Martin Guibert
committed
Use printer to not break output isolation
added pinter interface to print info json output use void printer is the path output it stdout
1 parent 8f584d6 commit f31a8a8

File tree

15 files changed

+303
-28
lines changed

15 files changed

+303
-28
lines changed

.editorconfig

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,43 @@ end_of_line = lf
55
insert_final_newline = true
66
charset = utf-8
77
trim_trailing_whitespace=true
8+
indent_style = space
9+
indent_size = 4
10+
11+
[.editorconfig]
12+
ij_editorconfig_align_group_field_declarations = false
13+
ij_editorconfig_space_after_colon = false
14+
ij_editorconfig_space_after_comma = true
15+
ij_editorconfig_space_before_colon = false
16+
ij_editorconfig_space_before_comma = false
17+
ij_editorconfig_spaces_around_assignment_operators = true
18+
19+
[{*.go,*.go2}]
20+
ij_continuation_indent_size = 4
21+
ij_go_add_leading_space_to_comments = true
22+
ij_go_add_parentheses_for_single_import = false
23+
ij_go_call_parameters_new_line_after_left_paren = true
24+
ij_go_call_parameters_right_paren_on_new_line = true
25+
ij_go_call_parameters_wrap = off
26+
ij_go_fill_paragraph_width = 80
27+
ij_go_group_current_project_imports = true
28+
ij_go_group_stdlib_imports = true
29+
ij_go_import_sorting = gofmt
30+
ij_go_keep_indents_on_empty_lines = false
31+
ij_go_move_all_imports_in_one_declaration = true
32+
ij_go_move_all_stdlib_imports_in_one_group = true
33+
ij_go_remove_redundant_import_aliases = true
34+
ij_go_use_back_quotes_for_imports = false
35+
ij_go_wrap_comp_lit = off
36+
ij_go_wrap_comp_lit_newline_after_lbrace = true
37+
ij_go_wrap_comp_lit_newline_before_rbrace = true
38+
ij_go_wrap_func_params = off
39+
ij_go_wrap_func_params_newline_after_lparen = true
40+
ij_go_wrap_func_params_newline_before_rparen = true
41+
ij_go_wrap_func_result = off
42+
ij_go_wrap_func_result_newline_after_lparen = true
43+
ij_go_wrap_func_result_newline_before_rparen = true
44+
845

946
[*.json]
1047
insert_final_newline = false

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ deps:
6565

6666
.PHONY: install-tools
6767
install-tools:
68-
$(GOGET) -u gotest.tools/gotestsum
68+
$(GOGET) gotest.tools/gotestsum
6969
$(GOGET) github.com/vektra/mockery/.../
7070

7171

goland_watchers.xml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<TaskOptions>
2+
<TaskOptions>
3+
<option name="arguments" value="fmt $FilePath$" />
4+
<option name="checkSyntaxErrors" value="true" />
5+
<option name="description" />
6+
<option name="exitCodeBehavior" value="ERROR" />
7+
<option name="fileExtension" value="go" />
8+
<option name="immediateSync" value="false" />
9+
<option name="name" value="go fmt" />
10+
<option name="output" value="$FilePath$" />
11+
<option name="outputFilters">
12+
<array />
13+
</option>
14+
<option name="outputFromStdout" value="false" />
15+
<option name="program" value="$GoExecPath$" />
16+
<option name="runOnExternalChanges" value="false" />
17+
<option name="scopeName" value="Project Files" />
18+
<option name="trackOnlyRoot" value="true" />
19+
<option name="workingDir" value="$ProjectFileDir$" />
20+
<envs>
21+
<env name="GOROOT" value="$GOROOT$" />
22+
<env name="GOPATH" value="$GOPATH$" />
23+
<env name="PATH" value="$GoBinDirs$" />
24+
</envs>
25+
</TaskOptions>
26+
<TaskOptions>
27+
<option name="arguments" value="run --disable=typecheck $FileDir$" />
28+
<option name="checkSyntaxErrors" value="true" />
29+
<option name="description" />
30+
<option name="exitCodeBehavior" value="ERROR" />
31+
<option name="fileExtension" value="go" />
32+
<option name="immediateSync" value="false" />
33+
<option name="name" value="golangci-lint" />
34+
<option name="output" value="" />
35+
<option name="outputFilters">
36+
<array />
37+
</option>
38+
<option name="outputFromStdout" value="false" />
39+
<option name="program" value="golangci-lint" />
40+
<option name="runOnExternalChanges" value="false" />
41+
<option name="scopeName" value="Project Files" />
42+
<option name="trackOnlyRoot" value="true" />
43+
<option name="workingDir" value="$ProjectFileDir$" />
44+
<envs>
45+
<env name="GOROOT" value="$GOROOT$" />
46+
<env name="GOPATH" value="$GOPATH$" />
47+
<env name="PATH" value="$GoBinDirs$" />
48+
</envs>
49+
</TaskOptions>
50+
</TaskOptions>

main.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,15 @@ func run() int {
6464
if cmd.IsReportingEnabled(&driftctlCmd.Command) {
6565
sentry.CaptureException(err)
6666
}
67-
fmt.Fprintln(os.Stderr, color.RedString("%s", err))
67+
_, _ = fmt.Fprintln(os.Stderr, color.RedString("%s", err))
6868
return 1
6969
}
7070

7171
if checkVersion {
7272
newVersion := <-latestVersionChan
7373
if newVersion != "" {
74-
fmt.Println("\n\nYour version of driftctl is outdated, please upgrade !")
75-
fmt.Printf("Current: %s; Latest: %s\n", version.Current(), newVersion)
74+
_, _ = fmt.Fprintln(os.Stderr, "\n\nYour version of driftctl is outdated, please upgrade !")
75+
_, _ = fmt.Fprintf(os.Stderr, "Current: %s; Latest: %s\n", version.Current(), newVersion)
7676
}
7777
}
7878

pkg/cmd/scan.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ import (
77
"strings"
88
"syscall"
99

10+
"github.com/jmespath/go-jmespath"
11+
"github.com/pkg/errors"
12+
"github.com/sirupsen/logrus"
13+
"github.com/spf13/cobra"
14+
1015
"github.com/cloudskiff/driftctl/pkg"
1116
"github.com/cloudskiff/driftctl/pkg/alerter"
1217
cmderrors "github.com/cloudskiff/driftctl/pkg/cmd/errors"
@@ -18,10 +23,6 @@ import (
1823
"github.com/cloudskiff/driftctl/pkg/remote"
1924
"github.com/cloudskiff/driftctl/pkg/resource"
2025
"github.com/cloudskiff/driftctl/pkg/terraform"
21-
"github.com/jmespath/go-jmespath"
22-
"github.com/pkg/errors"
23-
"github.com/sirupsen/logrus"
24-
"github.com/spf13/cobra"
2526
)
2627

2728
type ScanOptions struct {
@@ -122,6 +123,8 @@ func NewScanCmd() *cobra.Command {
122123
}
123124

124125
func scanRun(opts *ScanOptions) error {
126+
selectedOutput := output.GetOutput(opts.Output)
127+
125128
c := make(chan os.Signal)
126129
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
127130

@@ -161,7 +164,7 @@ func scanRun(opts *ScanOptions) error {
161164
return err
162165
}
163166

164-
err = output.GetOutput(opts.Output).Write(analysis)
167+
err = selectedOutput.Write(analysis)
165168
if err != nil {
166169
return err
167170
}

pkg/cmd/scan/output/console.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ package output
22

33
import (
44
"fmt"
5+
"os"
56
"reflect"
67
"strings"
78

8-
"github.com/cloudskiff/driftctl/pkg/remote"
9-
10-
"github.com/cloudskiff/driftctl/pkg/analyser"
11-
"github.com/cloudskiff/driftctl/pkg/resource"
9+
"github.com/aws/aws-sdk-go/aws/awsutil"
1210
"github.com/fatih/color"
1311
"github.com/nsf/jsondiff"
1412
"github.com/r3labs/diff/v2"
1513

16-
"github.com/aws/aws-sdk-go/aws/awsutil"
14+
"github.com/cloudskiff/driftctl/pkg/analyser"
15+
"github.com/cloudskiff/driftctl/pkg/remote"
16+
"github.com/cloudskiff/driftctl/pkg/resource"
1717
)
1818

1919
const ConsoleOutputType = "console"
@@ -106,7 +106,7 @@ func (c *Console) Write(analysis *analyser.Analysis) error {
106106
}
107107

108108
if enumerationErrorMessage != "" {
109-
fmt.Printf("\n%s\n", color.YellowString(enumerationErrorMessage))
109+
_, _ = fmt.Fprintf(os.Stderr, "\n%s\n", color.YellowString(enumerationErrorMessage))
110110
}
111111

112112
return nil

pkg/cmd/scan/output/console_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,11 @@ func TestConsole_Write(t *testing.T) {
7272
t.Run(tt.name, func(t *testing.T) {
7373
c := NewConsole()
7474

75-
old := os.Stdout // keep backup of the real stdout
75+
stdout := os.Stdout // keep backup of the real stdout
76+
stderr := os.Stderr // keep backup of the real stderr
7677
r, w, _ := os.Pipe()
7778
os.Stdout = w
79+
os.Stderr = w
7880

7981
if err := c.Write(tt.args.analysis); (err != nil) != tt.wantErr {
8082
t.Errorf("Write() error = %v, wantErr %v", err, tt.wantErr)
@@ -90,7 +92,8 @@ func TestConsole_Write(t *testing.T) {
9092

9193
// back to normal state
9294
w.Close()
93-
os.Stdout = old // restoring the real stdout
95+
os.Stdout = stdout // restoring the real stdout
96+
os.Stderr = stderr
9497
out := <-outC
9598

9699
expectedFilePath := path.Join("./testdata", tt.goldenfile)

pkg/cmd/scan/output/json.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ func NewJSON(path string) *JSON {
1919
}
2020

2121
func (c *JSON) Write(analysis *analyser.Analysis) error {
22-
file, err := os.OpenFile(c.path, os.O_CREATE|os.O_RDWR, 0600)
23-
if err != nil {
24-
return err
22+
file := os.Stdout
23+
if !isStdOut(c.path) {
24+
f, err := os.OpenFile(c.path, os.O_CREATE|os.O_RDWR, 0600)
25+
if err != nil {
26+
return err
27+
}
28+
defer f.Close()
29+
file = f
2530
}
26-
defer file.Close()
2731

2832
json, err := json.MarshalIndent(analysis, "", "\t")
2933
if err != nil {

pkg/cmd/scan/output/json_test.go

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
package output
22

33
import (
4+
"bytes"
5+
"io"
46
"io/ioutil"
7+
"os"
58
"path"
69
"testing"
710

8-
"github.com/cloudskiff/driftctl/test/goldenfile"
9-
1011
"github.com/stretchr/testify/assert"
1112

1213
"github.com/cloudskiff/driftctl/pkg/analyser"
14+
"github.com/cloudskiff/driftctl/test/goldenfile"
1315
)
1416

1517
func TestJSON_Write(t *testing.T) {
@@ -84,3 +86,74 @@ func TestJSON_Write(t *testing.T) {
8486
})
8587
}
8688
}
89+
90+
func TestJSON_Write_stdout(t *testing.T) {
91+
type args struct {
92+
analysis *analyser.Analysis
93+
}
94+
tests := []struct {
95+
name string
96+
path string
97+
goldenfile string
98+
args args
99+
wantErr bool
100+
}{
101+
{
102+
name: "test json output stdout",
103+
goldenfile: "output.json",
104+
path: "stdout",
105+
args: args{
106+
analysis: fakeAnalysis(),
107+
},
108+
wantErr: false,
109+
},
110+
111+
{
112+
name: "test json output /dev/stdout",
113+
goldenfile: "output.json",
114+
path: "/dev/stdout",
115+
args: args{
116+
analysis: fakeAnalysis(),
117+
},
118+
wantErr: false,
119+
},
120+
}
121+
for _, tt := range tests {
122+
t.Run(tt.name, func(t *testing.T) {
123+
124+
stdout := os.Stdout // keep backup of the real stdout
125+
r, w, _ := os.Pipe()
126+
os.Stdout = w
127+
128+
c := NewJSON(tt.path)
129+
if err := c.Write(tt.args.analysis); (err != nil) != tt.wantErr {
130+
t.Errorf("Write() error = %v, wantErr %v", err, tt.wantErr)
131+
}
132+
133+
outC := make(chan []byte)
134+
// copy the output in a separate goroutine so printing can't block indefinitely
135+
go func() {
136+
var buf bytes.Buffer
137+
_, _ = io.Copy(&buf, r)
138+
outC <- buf.Bytes()
139+
}()
140+
141+
// back to normal state
142+
w.Close()
143+
os.Stdout = stdout // restoring the real stdout
144+
result := <-outC
145+
146+
expectedFilePath := path.Join("./testdata/", tt.goldenfile)
147+
if *goldenfile.Update == tt.goldenfile {
148+
if err := ioutil.WriteFile(expectedFilePath, result, 0600); err != nil {
149+
t.Fatal(err)
150+
}
151+
}
152+
expected, err := ioutil.ReadFile(expectedFilePath)
153+
if err != nil {
154+
t.Fatal(err)
155+
}
156+
assert.Equal(t, string(expected), string(result))
157+
})
158+
}
159+
}

pkg/cmd/scan/output/output.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"sort"
55

66
"github.com/cloudskiff/driftctl/pkg/analyser"
7+
"github.com/cloudskiff/driftctl/pkg/output"
78
)
89

910
type Output interface {
@@ -47,6 +48,8 @@ func IsSupported(key string) bool {
4748
}
4849

4950
func GetOutput(config OutputConfig) Output {
51+
output.ChangePrinter(GetPrinter(config))
52+
5053
switch config.Key {
5154
case JSONOutputType:
5255
return NewJSON(config.Options["path"])
@@ -56,3 +59,21 @@ func GetOutput(config OutputConfig) Output {
5659
return NewConsole()
5760
}
5861
}
62+
63+
func GetPrinter(config OutputConfig) output.Printer {
64+
switch config.Key {
65+
case JSONOutputType:
66+
if isStdOut(config.Options["path"]) {
67+
return &output.VoidPrinter{}
68+
}
69+
fallthrough
70+
case ConsoleOutputType:
71+
fallthrough
72+
default:
73+
return output.NewConsolePrinter()
74+
}
75+
}
76+
77+
func isStdOut(path string) bool {
78+
return path == "/dev/stdout" || path == "stdout"
79+
}

0 commit comments

Comments
 (0)