From bd97f7c36c40d22b16bbaaf7bd7bb4c2042d01c3 Mon Sep 17 00:00:00 2001 From: Vanessa Sochat Date: Fri, 7 Dec 2018 15:58:08 -0500 Subject: [PATCH 1/3] first test compiles, will need testing and additional tests --- cmd/diff.go | 6 +++++- cmd/root.go | 39 +++++++++++++++++++++++++++++++++--- util/analyze_output_utils.go | 35 ++++++++++++++++---------------- util/diff_output_utils.go | 37 +++++++++++++++++----------------- util/format_utils.go | 19 +++++++++--------- 5 files changed, 87 insertions(+), 49 deletions(-) diff --git a/cmd/diff.go b/cmd/diff.go index 80020c65..e3f0e0d2 100644 --- a/cmd/diff.go +++ b/cmd/diff.go @@ -158,7 +158,11 @@ func diffFile(image1, image2 *pkgutil.Image) error { if err != nil { return err } - util.TemplateOutput(diff, "FilenameDiff") + writer, err := getWriter(outputFile) + if err != nil { + return err + } + util.TemplateOutput(writer, diff, "FilenameDiff") return nil } diff --git a/cmd/root.go b/cmd/root.go index 8bbf8d0b..a228614c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -19,6 +19,7 @@ package cmd import ( goflag "flag" "fmt" + "io" "os" "path/filepath" "sort" @@ -40,6 +41,7 @@ var save bool var types diffTypes var noCache bool +var outputFile string var cacheDir string var LogLevel string var format string @@ -70,6 +72,7 @@ Tarballs can also be specified by simply providing the path to the .tar, .tar.gz } func outputResults(resultMap map[string]util.Result) { + // Outputs diff/analysis results in alphabetical order by analyzer name sortedTypes := []string{} for analyzerType := range resultMap { @@ -77,20 +80,27 @@ func outputResults(resultMap map[string]util.Result) { } sort.Strings(sortedTypes) + // Get the writer + writer, err := getWriter(outputFile) + if err != nil { + logrus.Error(err) + } + results := make([]interface{}, len(resultMap)) for i, analyzerType := range sortedTypes { result := resultMap[analyzerType] if json { results[i] = result.OutputStruct() } else { - err := result.OutputText(analyzerType, format) + err := result.OutputText(writer, analyzerType, format) if err != nil { logrus.Error(err) } } } if json { - err := util.JSONify(results) + + err := util.JSONify(writer, results) if err != nil { logrus.Error(err) } @@ -162,6 +172,29 @@ func getCacheDir(imageName string) (string, error) { return filepath.Join(rootDir, filepath.Clean(imageName)), nil } +func getWriter(outputFile string) (io.Writer, error) { + var err error + var outWriter io.Writer + + // If the user specifies an output file, ensure exists + if outputFile != "" { + + // The file can't exist, we would overwrite it + if _, err := os.Stat(outputFile); !os.IsNotExist(err) { + return os.Stdout, errors.Wrap(err, "file exists, will not overwrite") + } + + // Otherwise, output file is an io.writer + outWriter, err = os.Create(outputFile) + + } + // If still doesn't exist, return stdout as the io.Writer + if outputFile == "" { + outWriter = os.Stdout + } + return outWriter, err +} + func init() { RootCmd.PersistentFlags().StringVarP(&LogLevel, "verbosity", "v", "warning", "This flag controls the verbosity of container-diff.") RootCmd.PersistentFlags().StringVarP(&format, "format", "", "", "Format to output diff in.") @@ -201,5 +234,5 @@ func addSharedFlags(cmd *cobra.Command) { cmd.Flags().BoolVarP(&util.SortSize, "order", "o", false, "Set this flag to sort any file/package results by descending size. Otherwise, they will be sorted by name.") cmd.Flags().BoolVarP(&noCache, "no-cache", "n", false, "Set this to force retrieval of image filesystem on each run.") cmd.Flags().StringVarP(&cacheDir, "cache-dir", "c", "", "cache directory base to create .container-diff (default is $HOME).") - + cmd.Flags().StringVarP(&outputFile, "output", "w", "", "output file to write to (default writes to STDOUT).") } diff --git a/util/analyze_output_utils.go b/util/analyze_output_utils.go index ddb4bdf1..3e15c7dc 100644 --- a/util/analyze_output_utils.go +++ b/util/analyze_output_utils.go @@ -19,6 +19,7 @@ package util import ( "errors" "fmt" + "io" "github.com/GoogleContainerTools/container-diff/pkg/util" "github.com/sirupsen/logrus" @@ -26,7 +27,7 @@ import ( type Result interface { OutputStruct() interface{} - OutputText(resultType string, format string) error + OutputText(writer io.Writer, resultType string, format string) error } type AnalyzeResult struct { @@ -41,14 +42,14 @@ func (r ListAnalyzeResult) OutputStruct() interface{} { return r } -func (r ListAnalyzeResult) OutputText(resultType string, format string) error { +func (r ListAnalyzeResult) OutputText(writer io.Writer, resultType string, format string) error { analysis, valid := r.Analysis.([]string) if !valid { logrus.Error("Unexpected structure of Analysis. Should be of type []string") return fmt.Errorf("Could not output %s analysis result", r.AnalyzeType) } r.Analysis = analysis - return TemplateOutputFromFormat(r, "ListAnalyze", format) + return TemplateOutputFromFormat(writer, r, "ListAnalyze", format) } @@ -73,7 +74,7 @@ func (r MultiVersionPackageAnalyzeResult) OutputStruct() interface{} { return output } -func (r MultiVersionPackageAnalyzeResult) OutputText(resultType string, format string) error { +func (r MultiVersionPackageAnalyzeResult) OutputText(writer io.Writer, resultType string, format string) error { analysis, valid := r.Analysis.(map[string]map[string]PackageInfo) if !valid { logrus.Error("Unexpected structure of Analysis. Should be of type map[string]map[string]PackageInfo") @@ -91,7 +92,7 @@ func (r MultiVersionPackageAnalyzeResult) OutputText(resultType string, format s AnalyzeType: r.AnalyzeType, Analysis: strAnalysis, } - return TemplateOutputFromFormat(strResult, "MultiVersionPackageAnalyze", format) + return TemplateOutputFromFormat(writer, strResult, "MultiVersionPackageAnalyze", format) } type SingleVersionPackageAnalyzeResult AnalyzeResult @@ -115,7 +116,7 @@ func (r SingleVersionPackageAnalyzeResult) OutputStruct() interface{} { return output } -func (r SingleVersionPackageAnalyzeResult) OutputText(diffType string, format string) error { +func (r SingleVersionPackageAnalyzeResult) OutputText(writer io.Writer, diffType string, format string) error { analysis, valid := r.Analysis.(map[string]PackageInfo) if !valid { logrus.Error("Unexpected structure of Analysis. Should be of type map[string]PackageInfo") @@ -133,7 +134,7 @@ func (r SingleVersionPackageAnalyzeResult) OutputText(diffType string, format st AnalyzeType: r.AnalyzeType, Analysis: strAnalysis, } - return TemplateOutputFromFormat(strResult, "SingleVersionPackageAnalyze", format) + return TemplateOutputFromFormat(writer, strResult, "SingleVersionPackageAnalyze", format) } type SingleVersionPackageLayerAnalyzeResult AnalyzeResult @@ -173,7 +174,7 @@ func (r SingleVersionPackageLayerAnalyzeResult) OutputStruct() interface{} { return output } -func (r SingleVersionPackageLayerAnalyzeResult) OutputText(diffType string, format string) error { +func (r SingleVersionPackageLayerAnalyzeResult) OutputText(writer io.Writer, diffType string, format string) error { analysis, valid := r.Analysis.(PackageLayerDiff) if !valid { logrus.Error("Unexpected structure of Analysis. Should be of type PackageLayerDiff") @@ -205,7 +206,7 @@ func (r SingleVersionPackageLayerAnalyzeResult) OutputText(diffType string, form AnalyzeType: r.AnalyzeType, Analysis: analysisOutput, } - return TemplateOutputFromFormat(strResult, "SingleVersionPackageLayerAnalyze", format) + return TemplateOutputFromFormat(writer, strResult, "SingleVersionPackageLayerAnalyze", format) } type PackageOutput struct { @@ -263,7 +264,7 @@ func (r FileAnalyzeResult) OutputStruct() interface{} { return r } -func (r FileAnalyzeResult) OutputText(analyzeType string, format string) error { +func (r FileAnalyzeResult) OutputText(writer io.Writer, analyzeType string, format string) error { analysis, valid := r.Analysis.([]util.DirectoryEntry) if !valid { logrus.Error("Unexpected structure of Analysis. Should be of type []DirectoryEntry") @@ -286,7 +287,7 @@ func (r FileAnalyzeResult) OutputText(analyzeType string, format string) error { AnalyzeType: r.AnalyzeType, Analysis: strAnalysis, } - return TemplateOutputFromFormat(strResult, "FileAnalyze", format) + return TemplateOutputFromFormat(writer, strResult, "FileAnalyze", format) } type FileLayerAnalyzeResult AnalyzeResult @@ -310,7 +311,7 @@ func (r FileLayerAnalyzeResult) OutputStruct() interface{} { return r } -func (r FileLayerAnalyzeResult) OutputText(analyzeType string, format string) error { +func (r FileLayerAnalyzeResult) OutputText(writer io.Writer, analyzeType string, format string) error { analysis, valid := r.Analysis.([][]util.DirectoryEntry) if !valid { logrus.Error("Unexpected structure of Analysis. Should be of type []DirectoryEntry") @@ -338,7 +339,7 @@ func (r FileLayerAnalyzeResult) OutputText(analyzeType string, format string) er AnalyzeType: r.AnalyzeType, Analysis: strDirectoryEntries, } - return TemplateOutputFromFormat(strResult, "FileLayerAnalyze", format) + return TemplateOutputFromFormat(writer, strResult, "FileLayerAnalyze", format) } type SizeAnalyzeResult AnalyzeResult @@ -353,7 +354,7 @@ func (r SizeAnalyzeResult) OutputStruct() interface{} { return r } -func (r SizeAnalyzeResult) OutputText(analyzeType string, format string) error { +func (r SizeAnalyzeResult) OutputText(writer io.Writer, analyzeType string, format string) error { analysis, valid := r.Analysis.([]SizeEntry) if !valid { logrus.Error("Unexpected structure of Analysis. Should be of type []SizeEntry") @@ -371,7 +372,7 @@ func (r SizeAnalyzeResult) OutputText(analyzeType string, format string) error { AnalyzeType: r.AnalyzeType, Analysis: strAnalysis, } - return TemplateOutputFromFormat(strResult, "SizeAnalyze", format) + return TemplateOutputFromFormat(writer, strResult, "SizeAnalyze", format) } type SizeLayerAnalyzeResult AnalyzeResult @@ -386,7 +387,7 @@ func (r SizeLayerAnalyzeResult) OutputStruct() interface{} { return r } -func (r SizeLayerAnalyzeResult) OutputText(analyzeType string, format string) error { +func (r SizeLayerAnalyzeResult) OutputText(writer io.Writer, analyzeType string, format string) error { analysis, valid := r.Analysis.([]SizeEntry) if !valid { logrus.Error("Unexpected structure of Analysis. Should be of type []SizeEntry") @@ -404,5 +405,5 @@ func (r SizeLayerAnalyzeResult) OutputText(analyzeType string, format string) er AnalyzeType: r.AnalyzeType, Analysis: strAnalysis, } - return TemplateOutputFromFormat(strResult, "SizeLayerAnalyze", format) + return TemplateOutputFromFormat(writer, strResult, "SizeLayerAnalyze", format) } diff --git a/util/diff_output_utils.go b/util/diff_output_utils.go index ac65f832..c437f1aa 100644 --- a/util/diff_output_utils.go +++ b/util/diff_output_utils.go @@ -19,6 +19,7 @@ package util import ( "errors" "fmt" + "io" "github.com/sirupsen/logrus" ) @@ -52,7 +53,7 @@ func (r MultiVersionPackageDiffResult) OutputStruct() interface{} { return r } -func (r MultiVersionPackageDiffResult) OutputText(diffType string, format string) error { +func (r MultiVersionPackageDiffResult) OutputText(writer io.Writer, diffType string, format string) error { diff, valid := r.Diff.(MultiVersionPackageDiff) if !valid { logrus.Error("Unexpected structure of Diff. Should follow the MultiVersionPackageDiff struct") @@ -84,7 +85,7 @@ func (r MultiVersionPackageDiffResult) OutputText(diffType string, format string InfoDiff: strInfoDiff, }, } - return TemplateOutputFromFormat(strResult, "MultiVersionPackageDiff", format) + return TemplateOutputFromFormat(writer, strResult, "MultiVersionPackageDiff", format) } func getMultiVersionInfoDiffOutput(infoDiff []MultiVersionInfo) []MultiVersionInfo { @@ -118,7 +119,7 @@ func (r SingleVersionPackageDiffResult) OutputStruct() interface{} { return r } -func (r SingleVersionPackageDiffResult) OutputText(diffType string, format string) error { +func (r SingleVersionPackageDiffResult) OutputText(writer io.Writer, diffType string, format string) error { diff, valid := r.Diff.(PackageDiff) if !valid { logrus.Error("Unexpected structure of Diff. Should follow the PackageDiff struct") @@ -150,7 +151,7 @@ func (r SingleVersionPackageDiffResult) OutputText(diffType string, format strin InfoDiff: strInfoDiff, }, } - return TemplateOutputFromFormat(strResult, "SingleVersionPackageDiff", format) + return TemplateOutputFromFormat(writer, strResult, "SingleVersionPackageDiff", format) } func getSingleVersionInfoDiffOutput(infoDiff []Info) []Info { @@ -191,7 +192,7 @@ func (r SingleVersionPackageLayerDiffResult) OutputStruct() interface{} { return r } -func (r SingleVersionPackageLayerDiffResult) OutputText(diffType string, format string) error { +func (r SingleVersionPackageLayerDiffResult) OutputText(writer io.Writer, diffType string, format string) error { diff, valid := r.Diff.(PackageLayerDiff) if !valid { logrus.Error("Unexpected structure of Diff. Should follow the PackageLayerDiff struct") @@ -225,7 +226,7 @@ func (r SingleVersionPackageLayerDiffResult) OutputText(diffType string, format DiffType: r.DiffType, Diff: diffOutputs, } - return TemplateOutputFromFormat(strResult, "SingleVersionPackageLayerDiff", format) + return TemplateOutputFromFormat(writer, strResult, "SingleVersionPackageLayerDiff", format) } type HistDiffResult DiffResult @@ -234,8 +235,8 @@ func (r HistDiffResult) OutputStruct() interface{} { return r } -func (r HistDiffResult) OutputText(diffType string, format string) error { - return TemplateOutputFromFormat(r, "HistDiff", format) +func (r HistDiffResult) OutputText(writer io.Writer, diffType string, format string) error { + return TemplateOutputFromFormat(writer, r, "HistDiff", format) } type MetadataDiffResult DiffResult @@ -244,8 +245,8 @@ func (r MetadataDiffResult) OutputStruct() interface{} { return r } -func (r MetadataDiffResult) OutputText(diffType string, format string) error { - return TemplateOutputFromFormat(r, "MetadataDiff", format) +func (r MetadataDiffResult) OutputText(writer io.Writer, diffType string, format string) error { + return TemplateOutputFromFormat(writer, r, "MetadataDiff", format) } type DirDiffResult DiffResult @@ -261,7 +262,7 @@ func (r DirDiffResult) OutputStruct() interface{} { return r } -func (r DirDiffResult) OutputText(diffType string, format string) error { +func (r DirDiffResult) OutputText(writer io.Writer, diffType string, format string) error { diff, valid := r.Diff.(DirDiff) if !valid { logrus.Error("Unexpected structure of Diff. Should follow the DirDiff struct") @@ -294,7 +295,7 @@ func (r DirDiffResult) OutputText(diffType string, format string) error { Mods: strMods, }, } - return TemplateOutputFromFormat(strResult, "DirDiff", format) + return TemplateOutputFromFormat(writer, strResult, "DirDiff", format) } type SizeDiffResult DiffResult @@ -310,7 +311,7 @@ func (r SizeDiffResult) OutputStruct() interface{} { return r } -func (r SizeDiffResult) OutputText(diffType string, format string) error { +func (r SizeDiffResult) OutputText(writer io.Writer, diffType string, format string) error { diff, valid := r.Diff.([]SizeDiff) if !valid { logrus.Error("Unexpected structure of Diff. Should be of type []SizeDiff") @@ -330,7 +331,7 @@ func (r SizeDiffResult) OutputText(diffType string, format string) error { DiffType: r.DiffType, Diff: strDiff, } - return TemplateOutputFromFormat(strResult, "SizeDiff", format) + return TemplateOutputFromFormat(writer, strResult, "SizeDiff", format) } type SizeLayerDiffResult DiffResult @@ -346,7 +347,7 @@ func (r SizeLayerDiffResult) OutputStruct() interface{} { return r } -func (r SizeLayerDiffResult) OutputText(diffType string, format string) error { +func (r SizeLayerDiffResult) OutputText(writer io.Writer, diffType string, format string) error { diff, valid := r.Diff.([]SizeDiff) if !valid { logrus.Error("Unexpected structure of Diff. Should be of type []SizeDiff") @@ -366,7 +367,7 @@ func (r SizeLayerDiffResult) OutputText(diffType string, format string) error { DiffType: r.DiffType, Diff: strDiff, } - return TemplateOutputFromFormat(strResult, "SizeLayerDiff", format) + return TemplateOutputFromFormat(writer, strResult, "SizeLayerDiff", format) } type MultipleDirDiffResult DiffResult @@ -384,7 +385,7 @@ func (r MultipleDirDiffResult) OutputStruct() interface{} { return r } -func (r MultipleDirDiffResult) OutputText(diffType string, format string) error { +func (r MultipleDirDiffResult) OutputText(writer io.Writer, diffType string, format string) error { diff, valid := r.Diff.(MultipleDirDiff) if !valid { logrus.Error("Unexpected structure of Diff. Should follow the MultipleDirDiff struct") @@ -428,5 +429,5 @@ func (r MultipleDirDiffResult) OutputText(diffType string, format string) error DiffType: r.DiffType, Diff: strDiffs, } - return TemplateOutputFromFormat(strResult, "MultipleDirDiff", format) + return TemplateOutputFromFormat(writer, strResult, "MultipleDirDiff", format) } diff --git a/util/format_utils.go b/util/format_utils.go index 83717feb..483ff347 100644 --- a/util/format_utils.go +++ b/util/format_utils.go @@ -20,7 +20,7 @@ import ( "bufio" "encoding/json" "errors" - "os" + "io" "strings" "text/tabwriter" "text/template" @@ -48,12 +48,12 @@ var templates = map[string]string{ "SingleVersionPackageLayerAnalyze": SingleVersionPackageLayerOutput, } -func JSONify(diff interface{}) error { +func JSONify(writer io.Writer, diff interface{}) error { diffBytes, err := json.MarshalIndent(diff, "", " ") if err != nil { return err } - f := bufio.NewWriter(os.Stdout) + f := bufio.NewWriter(writer) defer f.Flush() f.Write(diffBytes) return nil @@ -66,11 +66,10 @@ func getTemplate(templateType string) (string, error) { return "", errors.New("No available template") } -func TemplateOutput(diff interface{}, templateType string) error { +func TemplateOutput(writer io.Writer, diff interface{}, templateType string) error { outputTmpl, err := getTemplate(templateType) if err != nil { logrus.Error(err) - } funcs := template.FuncMap{"join": strings.Join} tmpl, err := template.New("tmpl").Funcs(funcs).Parse(outputTmpl) @@ -78,7 +77,7 @@ func TemplateOutput(diff interface{}, templateType string) error { logrus.Error(err) return err } - w := tabwriter.NewWriter(os.Stdout, 8, 8, 8, ' ', 0) + w := tabwriter.NewWriter(writer, 8, 8, 8, ' ', 0) err = tmpl.Execute(w, diff) if err != nil { logrus.Error(err) @@ -88,18 +87,18 @@ func TemplateOutput(diff interface{}, templateType string) error { return nil } -func TemplateOutputFromFormat(diff interface{}, templateType string, format string) error { +func TemplateOutputFromFormat(writer io.Writer, diff interface{}, templateType string, format string) error { if format == "" { - return TemplateOutput(diff, templateType) + return TemplateOutput(writer, diff, templateType) } funcs := template.FuncMap{"join": strings.Join} tmpl, err := template.New("tmpl").Funcs(funcs).Parse(format) if err != nil { logrus.Warningf("User specified format resulted in error, printing default output.") logrus.Error(err) - return TemplateOutput(diff, templateType) + return TemplateOutput(writer, diff, templateType) } - w := tabwriter.NewWriter(os.Stdout, 8, 8, 8, ' ', 0) + w := tabwriter.NewWriter(writer, 8, 8, 8, ' ', 0) err = tmpl.Execute(w, diff) if err != nil { return err From bea5a17c110e76d9505a015902c90678d0996eb7 Mon Sep 17 00:00:00 2001 From: Vanessa Sochat Date: Fri, 7 Dec 2018 16:46:37 -0500 Subject: [PATCH 2/3] final tweaks --- cmd/root.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index a228614c..ee06401c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -42,6 +42,7 @@ var types diffTypes var noCache bool var outputFile string +var forceWrite bool var cacheDir string var LogLevel string var format string @@ -179,9 +180,11 @@ func getWriter(outputFile string) (io.Writer, error) { // If the user specifies an output file, ensure exists if outputFile != "" { - // The file can't exist, we would overwrite it + // Don't overwrite a file that exists, unless given --force if _, err := os.Stat(outputFile); !os.IsNotExist(err) { - return os.Stdout, errors.Wrap(err, "file exists, will not overwrite") + if !forceWrite { + logrus.Error("file exist, will not overwrite.") + } } // Otherwise, output file is an io.writer @@ -234,5 +237,6 @@ func addSharedFlags(cmd *cobra.Command) { cmd.Flags().BoolVarP(&util.SortSize, "order", "o", false, "Set this flag to sort any file/package results by descending size. Otherwise, they will be sorted by name.") cmd.Flags().BoolVarP(&noCache, "no-cache", "n", false, "Set this to force retrieval of image filesystem on each run.") cmd.Flags().StringVarP(&cacheDir, "cache-dir", "c", "", "cache directory base to create .container-diff (default is $HOME).") - cmd.Flags().StringVarP(&outputFile, "output", "w", "", "output file to write to (default writes to STDOUT).") + cmd.Flags().StringVarP(&outputFile, "output", "w", "", "output file to write to (default writes to the screen).") + cmd.Flags().BoolVar(&forceWrite, "force", false, "force overwrite output file, if exists already.") } From f3bee1fc8a18ccfe49cb97e15eed21e5d8df16d8 Mon Sep 17 00:00:00 2001 From: Vanessa Sochat Date: Mon, 10 Dec 2018 17:54:30 -0500 Subject: [PATCH 3/3] fixing spacing, error messages --- cmd/diff.go | 4 ++++ cmd/root.go | 14 +++----------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/cmd/diff.go b/cmd/diff.go index e3f0e0d2..b147aac8 100644 --- a/cmd/diff.go +++ b/cmd/diff.go @@ -163,6 +163,10 @@ func diffFile(image1, image2 *pkgutil.Image) error { return err } util.TemplateOutput(writer, diff, "FilenameDiff") + if err != nil { + logrus.Error(err) + return err + } return nil } diff --git a/cmd/root.go b/cmd/root.go index ee06401c..93696809 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -73,7 +73,6 @@ Tarballs can also be specified by simply providing the path to the .tar, .tar.gz } func outputResults(resultMap map[string]util.Result) { - // Outputs diff/analysis results in alphabetical order by analyzer name sortedTypes := []string{} for analyzerType := range resultMap { @@ -84,7 +83,7 @@ func outputResults(resultMap map[string]util.Result) { // Get the writer writer, err := getWriter(outputFile) if err != nil { - logrus.Error(err) + errors.Wrap(err, "getting writer for output file") } results := make([]interface{}, len(resultMap)) @@ -100,7 +99,6 @@ func outputResults(resultMap map[string]util.Result) { } } if json { - err := util.JSONify(writer, results) if err != nil { logrus.Error(err) @@ -176,20 +174,14 @@ func getCacheDir(imageName string) (string, error) { func getWriter(outputFile string) (io.Writer, error) { var err error var outWriter io.Writer - // If the user specifies an output file, ensure exists if outputFile != "" { - // Don't overwrite a file that exists, unless given --force - if _, err := os.Stat(outputFile); !os.IsNotExist(err) { - if !forceWrite { - logrus.Error("file exist, will not overwrite.") - } + if _, err := os.Stat(outputFile); !os.IsNotExist(err) && !forceWrite { + errors.Wrap(err, "file exist, will not overwrite.") } - // Otherwise, output file is an io.writer outWriter, err = os.Create(outputFile) - } // If still doesn't exist, return stdout as the io.Writer if outputFile == "" {