Use printer to not break output isolation
added pinter interface to print info json output use void printer is the path output it stdoutmain
parent
8f584d67af
commit
f31a8a8395
|
@ -5,6 +5,43 @@ end_of_line = lf
|
|||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace=true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[.editorconfig]
|
||||
ij_editorconfig_align_group_field_declarations = false
|
||||
ij_editorconfig_space_after_colon = false
|
||||
ij_editorconfig_space_after_comma = true
|
||||
ij_editorconfig_space_before_colon = false
|
||||
ij_editorconfig_space_before_comma = false
|
||||
ij_editorconfig_spaces_around_assignment_operators = true
|
||||
|
||||
[{*.go,*.go2}]
|
||||
ij_continuation_indent_size = 4
|
||||
ij_go_add_leading_space_to_comments = true
|
||||
ij_go_add_parentheses_for_single_import = false
|
||||
ij_go_call_parameters_new_line_after_left_paren = true
|
||||
ij_go_call_parameters_right_paren_on_new_line = true
|
||||
ij_go_call_parameters_wrap = off
|
||||
ij_go_fill_paragraph_width = 80
|
||||
ij_go_group_current_project_imports = true
|
||||
ij_go_group_stdlib_imports = true
|
||||
ij_go_import_sorting = gofmt
|
||||
ij_go_keep_indents_on_empty_lines = false
|
||||
ij_go_move_all_imports_in_one_declaration = true
|
||||
ij_go_move_all_stdlib_imports_in_one_group = true
|
||||
ij_go_remove_redundant_import_aliases = true
|
||||
ij_go_use_back_quotes_for_imports = false
|
||||
ij_go_wrap_comp_lit = off
|
||||
ij_go_wrap_comp_lit_newline_after_lbrace = true
|
||||
ij_go_wrap_comp_lit_newline_before_rbrace = true
|
||||
ij_go_wrap_func_params = off
|
||||
ij_go_wrap_func_params_newline_after_lparen = true
|
||||
ij_go_wrap_func_params_newline_before_rparen = true
|
||||
ij_go_wrap_func_result = off
|
||||
ij_go_wrap_func_result_newline_after_lparen = true
|
||||
ij_go_wrap_func_result_newline_before_rparen = true
|
||||
|
||||
|
||||
[*.json]
|
||||
insert_final_newline = false
|
||||
|
|
2
Makefile
2
Makefile
|
@ -65,7 +65,7 @@ deps:
|
|||
|
||||
.PHONY: install-tools
|
||||
install-tools:
|
||||
$(GOGET) -u gotest.tools/gotestsum
|
||||
$(GOGET) gotest.tools/gotestsum
|
||||
$(GOGET) github.com/vektra/mockery/.../
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<TaskOptions>
|
||||
<TaskOptions>
|
||||
<option name="arguments" value="fmt $FilePath$" />
|
||||
<option name="checkSyntaxErrors" value="true" />
|
||||
<option name="description" />
|
||||
<option name="exitCodeBehavior" value="ERROR" />
|
||||
<option name="fileExtension" value="go" />
|
||||
<option name="immediateSync" value="false" />
|
||||
<option name="name" value="go fmt" />
|
||||
<option name="output" value="$FilePath$" />
|
||||
<option name="outputFilters">
|
||||
<array />
|
||||
</option>
|
||||
<option name="outputFromStdout" value="false" />
|
||||
<option name="program" value="$GoExecPath$" />
|
||||
<option name="runOnExternalChanges" value="false" />
|
||||
<option name="scopeName" value="Project Files" />
|
||||
<option name="trackOnlyRoot" value="true" />
|
||||
<option name="workingDir" value="$ProjectFileDir$" />
|
||||
<envs>
|
||||
<env name="GOROOT" value="$GOROOT$" />
|
||||
<env name="GOPATH" value="$GOPATH$" />
|
||||
<env name="PATH" value="$GoBinDirs$" />
|
||||
</envs>
|
||||
</TaskOptions>
|
||||
<TaskOptions>
|
||||
<option name="arguments" value="run --disable=typecheck $FileDir$" />
|
||||
<option name="checkSyntaxErrors" value="true" />
|
||||
<option name="description" />
|
||||
<option name="exitCodeBehavior" value="ERROR" />
|
||||
<option name="fileExtension" value="go" />
|
||||
<option name="immediateSync" value="false" />
|
||||
<option name="name" value="golangci-lint" />
|
||||
<option name="output" value="" />
|
||||
<option name="outputFilters">
|
||||
<array />
|
||||
</option>
|
||||
<option name="outputFromStdout" value="false" />
|
||||
<option name="program" value="golangci-lint" />
|
||||
<option name="runOnExternalChanges" value="false" />
|
||||
<option name="scopeName" value="Project Files" />
|
||||
<option name="trackOnlyRoot" value="true" />
|
||||
<option name="workingDir" value="$ProjectFileDir$" />
|
||||
<envs>
|
||||
<env name="GOROOT" value="$GOROOT$" />
|
||||
<env name="GOPATH" value="$GOPATH$" />
|
||||
<env name="PATH" value="$GoBinDirs$" />
|
||||
</envs>
|
||||
</TaskOptions>
|
||||
</TaskOptions>
|
6
main.go
6
main.go
|
@ -64,15 +64,15 @@ func run() int {
|
|||
if cmd.IsReportingEnabled(&driftctlCmd.Command) {
|
||||
sentry.CaptureException(err)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, color.RedString("%s", err))
|
||||
_, _ = fmt.Fprintln(os.Stderr, color.RedString("%s", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
if checkVersion {
|
||||
newVersion := <-latestVersionChan
|
||||
if newVersion != "" {
|
||||
fmt.Println("\n\nYour version of driftctl is outdated, please upgrade !")
|
||||
fmt.Printf("Current: %s; Latest: %s\n", version.Current(), newVersion)
|
||||
_, _ = fmt.Fprintln(os.Stderr, "\n\nYour version of driftctl is outdated, please upgrade !")
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Current: %s; Latest: %s\n", version.Current(), newVersion)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,11 @@ import (
|
|||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/jmespath/go-jmespath"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cloudskiff/driftctl/pkg"
|
||||
"github.com/cloudskiff/driftctl/pkg/alerter"
|
||||
cmderrors "github.com/cloudskiff/driftctl/pkg/cmd/errors"
|
||||
|
@ -18,10 +23,6 @@ import (
|
|||
"github.com/cloudskiff/driftctl/pkg/remote"
|
||||
"github.com/cloudskiff/driftctl/pkg/resource"
|
||||
"github.com/cloudskiff/driftctl/pkg/terraform"
|
||||
"github.com/jmespath/go-jmespath"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type ScanOptions struct {
|
||||
|
@ -122,6 +123,8 @@ func NewScanCmd() *cobra.Command {
|
|||
}
|
||||
|
||||
func scanRun(opts *ScanOptions) error {
|
||||
selectedOutput := output.GetOutput(opts.Output)
|
||||
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
|
@ -161,7 +164,7 @@ func scanRun(opts *ScanOptions) error {
|
|||
return err
|
||||
}
|
||||
|
||||
err = output.GetOutput(opts.Output).Write(analysis)
|
||||
err = selectedOutput.Write(analysis)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -2,18 +2,18 @@ package output
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudskiff/driftctl/pkg/remote"
|
||||
|
||||
"github.com/cloudskiff/driftctl/pkg/analyser"
|
||||
"github.com/cloudskiff/driftctl/pkg/resource"
|
||||
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||
"github.com/fatih/color"
|
||||
"github.com/nsf/jsondiff"
|
||||
"github.com/r3labs/diff/v2"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||
"github.com/cloudskiff/driftctl/pkg/analyser"
|
||||
"github.com/cloudskiff/driftctl/pkg/remote"
|
||||
"github.com/cloudskiff/driftctl/pkg/resource"
|
||||
)
|
||||
|
||||
const ConsoleOutputType = "console"
|
||||
|
@ -106,7 +106,7 @@ func (c *Console) Write(analysis *analyser.Analysis) error {
|
|||
}
|
||||
|
||||
if enumerationErrorMessage != "" {
|
||||
fmt.Printf("\n%s\n", color.YellowString(enumerationErrorMessage))
|
||||
_, _ = fmt.Fprintf(os.Stderr, "\n%s\n", color.YellowString(enumerationErrorMessage))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -72,9 +72,11 @@ func TestConsole_Write(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := NewConsole()
|
||||
|
||||
old := os.Stdout // keep backup of the real stdout
|
||||
stdout := os.Stdout // keep backup of the real stdout
|
||||
stderr := os.Stderr // keep backup of the real stderr
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
os.Stderr = w
|
||||
|
||||
if err := c.Write(tt.args.analysis); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Write() error = %v, wantErr %v", err, tt.wantErr)
|
||||
|
@ -90,7 +92,8 @@ func TestConsole_Write(t *testing.T) {
|
|||
|
||||
// back to normal state
|
||||
w.Close()
|
||||
os.Stdout = old // restoring the real stdout
|
||||
os.Stdout = stdout // restoring the real stdout
|
||||
os.Stderr = stderr
|
||||
out := <-outC
|
||||
|
||||
expectedFilePath := path.Join("./testdata", tt.goldenfile)
|
||||
|
|
|
@ -19,11 +19,15 @@ func NewJSON(path string) *JSON {
|
|||
}
|
||||
|
||||
func (c *JSON) Write(analysis *analyser.Analysis) error {
|
||||
file, err := os.OpenFile(c.path, os.O_CREATE|os.O_RDWR, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
file := os.Stdout
|
||||
if !isStdOut(c.path) {
|
||||
f, err := os.OpenFile(c.path, os.O_CREATE|os.O_RDWR, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
file = f
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
json, err := json.MarshalIndent(analysis, "", "\t")
|
||||
if err != nil {
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
package output
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/cloudskiff/driftctl/test/goldenfile"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/cloudskiff/driftctl/pkg/analyser"
|
||||
"github.com/cloudskiff/driftctl/test/goldenfile"
|
||||
)
|
||||
|
||||
func TestJSON_Write(t *testing.T) {
|
||||
|
@ -84,3 +86,74 @@ func TestJSON_Write(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSON_Write_stdout(t *testing.T) {
|
||||
type args struct {
|
||||
analysis *analyser.Analysis
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
path string
|
||||
goldenfile string
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "test json output stdout",
|
||||
goldenfile: "output.json",
|
||||
path: "stdout",
|
||||
args: args{
|
||||
analysis: fakeAnalysis(),
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "test json output /dev/stdout",
|
||||
goldenfile: "output.json",
|
||||
path: "/dev/stdout",
|
||||
args: args{
|
||||
analysis: fakeAnalysis(),
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
stdout := os.Stdout // keep backup of the real stdout
|
||||
r, w, _ := os.Pipe()
|
||||
os.Stdout = w
|
||||
|
||||
c := NewJSON(tt.path)
|
||||
if err := c.Write(tt.args.analysis); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Write() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
outC := make(chan []byte)
|
||||
// copy the output in a separate goroutine so printing can't block indefinitely
|
||||
go func() {
|
||||
var buf bytes.Buffer
|
||||
_, _ = io.Copy(&buf, r)
|
||||
outC <- buf.Bytes()
|
||||
}()
|
||||
|
||||
// back to normal state
|
||||
w.Close()
|
||||
os.Stdout = stdout // restoring the real stdout
|
||||
result := <-outC
|
||||
|
||||
expectedFilePath := path.Join("./testdata/", tt.goldenfile)
|
||||
if *goldenfile.Update == tt.goldenfile {
|
||||
if err := ioutil.WriteFile(expectedFilePath, result, 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
expected, err := ioutil.ReadFile(expectedFilePath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, string(expected), string(result))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"sort"
|
||||
|
||||
"github.com/cloudskiff/driftctl/pkg/analyser"
|
||||
"github.com/cloudskiff/driftctl/pkg/output"
|
||||
)
|
||||
|
||||
type Output interface {
|
||||
|
@ -47,6 +48,8 @@ func IsSupported(key string) bool {
|
|||
}
|
||||
|
||||
func GetOutput(config OutputConfig) Output {
|
||||
output.ChangePrinter(GetPrinter(config))
|
||||
|
||||
switch config.Key {
|
||||
case JSONOutputType:
|
||||
return NewJSON(config.Options["path"])
|
||||
|
@ -56,3 +59,21 @@ func GetOutput(config OutputConfig) Output {
|
|||
return NewConsole()
|
||||
}
|
||||
}
|
||||
|
||||
func GetPrinter(config OutputConfig) output.Printer {
|
||||
switch config.Key {
|
||||
case JSONOutputType:
|
||||
if isStdOut(config.Options["path"]) {
|
||||
return &output.VoidPrinter{}
|
||||
}
|
||||
fallthrough
|
||||
case ConsoleOutputType:
|
||||
fallthrough
|
||||
default:
|
||||
return output.NewConsolePrinter()
|
||||
}
|
||||
}
|
||||
|
||||
func isStdOut(path string) bool {
|
||||
return path == "/dev/stdout" || path == "stdout"
|
||||
}
|
||||
|
|
|
@ -2,9 +2,12 @@ package output
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/cloudskiff/driftctl/pkg/alerter"
|
||||
"github.com/cloudskiff/driftctl/pkg/analyser"
|
||||
"github.com/cloudskiff/driftctl/pkg/output"
|
||||
"github.com/cloudskiff/driftctl/pkg/remote"
|
||||
"github.com/cloudskiff/driftctl/pkg/remote/aws"
|
||||
"github.com/cloudskiff/driftctl/pkg/remote/github"
|
||||
|
@ -261,3 +264,49 @@ func fakeAnalysisWithGithubEnumerationError() *analyser.Analysis {
|
|||
})
|
||||
return &a
|
||||
}
|
||||
|
||||
func TestGetPrinter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
path string
|
||||
key string
|
||||
want output.Printer
|
||||
}{
|
||||
{
|
||||
name: "json file output",
|
||||
path: "/path/to/file",
|
||||
key: JSONOutputType,
|
||||
want: output.NewConsolePrinter(),
|
||||
},
|
||||
{
|
||||
name: "json stdout output",
|
||||
path: "stdout",
|
||||
key: JSONOutputType,
|
||||
want: &output.VoidPrinter{},
|
||||
},
|
||||
{
|
||||
name: "json /dev/stdout output",
|
||||
path: "/dev/stdout",
|
||||
key: JSONOutputType,
|
||||
want: &output.VoidPrinter{},
|
||||
},
|
||||
{
|
||||
name: "console stdout output",
|
||||
path: "stdout",
|
||||
key: ConsoleOutputType,
|
||||
want: output.NewConsolePrinter(),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := GetPrinter(OutputConfig{
|
||||
Key: tt.key,
|
||||
Options: map[string]string{
|
||||
"path": tt.path,
|
||||
},
|
||||
}); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("GetPrinter() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package output
|
||||
|
||||
import "fmt"
|
||||
|
||||
var globalPrinter Printer = &VoidPrinter{}
|
||||
|
||||
func ChangePrinter(printer Printer) {
|
||||
globalPrinter = printer
|
||||
}
|
||||
|
||||
func Printf(format string, args ...interface{}) {
|
||||
globalPrinter.Printf(format, args...)
|
||||
}
|
||||
|
||||
type Printer interface {
|
||||
Printf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
type ConsolePrinter struct{}
|
||||
|
||||
func NewConsolePrinter() *ConsolePrinter {
|
||||
return &ConsolePrinter{}
|
||||
}
|
||||
|
||||
func (c *ConsolePrinter) Printf(format string, args ...interface{}) {
|
||||
fmt.Printf(format, args...)
|
||||
}
|
||||
|
||||
type VoidPrinter struct{}
|
||||
|
||||
func (v *VoidPrinter) Printf(format string, args ...interface{}) {}
|
|
@ -2,6 +2,7 @@ package aws
|
|||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
|
||||
"github.com/cloudskiff/driftctl/pkg/remote/terraform"
|
||||
tf "github.com/cloudskiff/driftctl/pkg/terraform"
|
||||
)
|
||||
|
|
|
@ -2,13 +2,14 @@ package terraform
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/cloudskiff/driftctl/pkg/output"
|
||||
|
||||
"github.com/cloudskiff/driftctl/pkg/parallel"
|
||||
tf "github.com/cloudskiff/driftctl/pkg/terraform"
|
||||
"github.com/eapache/go-resiliency/retrier"
|
||||
|
@ -125,11 +126,11 @@ func (p *TerraformProvider) configure(alias string) error {
|
|||
"alias": alias,
|
||||
}).Debug("New gRPC client started")
|
||||
|
||||
fmt.Printf("Terraform provider initialized (name=%s", p.Config.Name)
|
||||
output.Printf("Terraform provider initialized (name=%s", p.Config.Name)
|
||||
if alias != "" {
|
||||
fmt.Printf(", alias=%s", alias)
|
||||
output.Printf(", alias=%s", alias)
|
||||
}
|
||||
fmt.Print(")\n")
|
||||
output.Printf(")\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ import (
|
|||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/cloudskiff/driftctl/pkg/output"
|
||||
)
|
||||
|
||||
type HomeDirInterface interface {
|
||||
|
@ -45,7 +47,7 @@ func (p *ProviderInstaller) Install() (string, error) {
|
|||
logrus.WithFields(logrus.Fields{
|
||||
"path": providerPath,
|
||||
}).Debug("provider not found, downloading ...")
|
||||
fmt.Printf("Downloading terraform provider: %s\n", p.config.Key)
|
||||
output.Printf("Downloading terraform provider: %s\n", p.config.Key)
|
||||
err := p.downloader.Download(
|
||||
p.config.GetDownloadUrl(),
|
||||
providerDir,
|
||||
|
|
Loading…
Reference in New Issue