Skip to content
This repository was archived by the owner on Mar 27, 2024. It is now read-only.

Commit ea8bccc

Browse files
authored
Merge pull request #279 from vsoch/enh/save-to-file
Enhancement - save to file
2 parents 9d720eb + f3bee1f commit ea8bccc

File tree

5 files changed

+87
-49
lines changed

5 files changed

+87
-49
lines changed

cmd/diff.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,15 @@ func diffFile(image1, image2 *pkgutil.Image) error {
163163
if err != nil {
164164
return err
165165
}
166-
util.TemplateOutput(diff, "FilenameDiff")
166+
writer, err := getWriter(outputFile)
167+
if err != nil {
168+
return err
169+
}
170+
util.TemplateOutput(writer, diff, "FilenameDiff")
171+
if err != nil {
172+
logrus.Error(err)
173+
return err
174+
}
167175
return nil
168176
}
169177

cmd/root.go

+32-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package cmd
1919
import (
2020
goflag "flag"
2121
"fmt"
22+
"io"
2223
"os"
2324
"path/filepath"
2425
"sort"
@@ -40,6 +41,8 @@ var save bool
4041
var types diffTypes
4142
var noCache bool
4243

44+
var outputFile string
45+
var forceWrite bool
4346
var cacheDir string
4447
var LogLevel string
4548
var format string
@@ -77,20 +80,26 @@ func outputResults(resultMap map[string]util.Result) {
7780
}
7881
sort.Strings(sortedTypes)
7982

83+
// Get the writer
84+
writer, err := getWriter(outputFile)
85+
if err != nil {
86+
errors.Wrap(err, "getting writer for output file")
87+
}
88+
8089
results := make([]interface{}, len(resultMap))
8190
for i, analyzerType := range sortedTypes {
8291
result := resultMap[analyzerType]
8392
if json {
8493
results[i] = result.OutputStruct()
8594
} else {
86-
err := result.OutputText(analyzerType, format)
95+
err := result.OutputText(writer, analyzerType, format)
8796
if err != nil {
8897
logrus.Error(err)
8998
}
9099
}
91100
}
92101
if json {
93-
err := util.JSONify(results)
102+
err := util.JSONify(writer, results)
94103
if err != nil {
95104
logrus.Error(err)
96105
}
@@ -162,6 +171,25 @@ func getCacheDir(imageName string) (string, error) {
162171
return filepath.Join(rootDir, filepath.Clean(imageName)), nil
163172
}
164173

174+
func getWriter(outputFile string) (io.Writer, error) {
175+
var err error
176+
var outWriter io.Writer
177+
// If the user specifies an output file, ensure exists
178+
if outputFile != "" {
179+
// Don't overwrite a file that exists, unless given --force
180+
if _, err := os.Stat(outputFile); !os.IsNotExist(err) && !forceWrite {
181+
errors.Wrap(err, "file exist, will not overwrite.")
182+
}
183+
// Otherwise, output file is an io.writer
184+
outWriter, err = os.Create(outputFile)
185+
}
186+
// If still doesn't exist, return stdout as the io.Writer
187+
if outputFile == "" {
188+
outWriter = os.Stdout
189+
}
190+
return outWriter, err
191+
}
192+
165193
func init() {
166194
RootCmd.PersistentFlags().StringVarP(&LogLevel, "verbosity", "v", "warning", "This flag controls the verbosity of container-diff.")
167195
RootCmd.PersistentFlags().StringVarP(&format, "format", "", "", "Format to output diff in.")
@@ -201,5 +229,6 @@ func addSharedFlags(cmd *cobra.Command) {
201229
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.")
202230
cmd.Flags().BoolVarP(&noCache, "no-cache", "n", false, "Set this to force retrieval of image filesystem on each run.")
203231
cmd.Flags().StringVarP(&cacheDir, "cache-dir", "c", "", "cache directory base to create .container-diff (default is $HOME).")
204-
232+
cmd.Flags().StringVarP(&outputFile, "output", "w", "", "output file to write to (default writes to the screen).")
233+
cmd.Flags().BoolVar(&forceWrite, "force", false, "force overwrite output file, if exists already.")
205234
}

util/analyze_output_utils.go

+18-17
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ package util
1919
import (
2020
"errors"
2121
"fmt"
22+
"io"
2223

2324
"github.com/GoogleContainerTools/container-diff/pkg/util"
2425
"github.com/sirupsen/logrus"
2526
)
2627

2728
type Result interface {
2829
OutputStruct() interface{}
29-
OutputText(resultType string, format string) error
30+
OutputText(writer io.Writer, resultType string, format string) error
3031
}
3132

3233
type AnalyzeResult struct {
@@ -41,14 +42,14 @@ func (r ListAnalyzeResult) OutputStruct() interface{} {
4142
return r
4243
}
4344

44-
func (r ListAnalyzeResult) OutputText(resultType string, format string) error {
45+
func (r ListAnalyzeResult) OutputText(writer io.Writer, resultType string, format string) error {
4546
analysis, valid := r.Analysis.([]string)
4647
if !valid {
4748
logrus.Error("Unexpected structure of Analysis. Should be of type []string")
4849
return fmt.Errorf("Could not output %s analysis result", r.AnalyzeType)
4950
}
5051
r.Analysis = analysis
51-
return TemplateOutputFromFormat(r, "ListAnalyze", format)
52+
return TemplateOutputFromFormat(writer, r, "ListAnalyze", format)
5253

5354
}
5455

@@ -73,7 +74,7 @@ func (r MultiVersionPackageAnalyzeResult) OutputStruct() interface{} {
7374
return output
7475
}
7576

76-
func (r MultiVersionPackageAnalyzeResult) OutputText(resultType string, format string) error {
77+
func (r MultiVersionPackageAnalyzeResult) OutputText(writer io.Writer, resultType string, format string) error {
7778
analysis, valid := r.Analysis.(map[string]map[string]PackageInfo)
7879
if !valid {
7980
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
9192
AnalyzeType: r.AnalyzeType,
9293
Analysis: strAnalysis,
9394
}
94-
return TemplateOutputFromFormat(strResult, "MultiVersionPackageAnalyze", format)
95+
return TemplateOutputFromFormat(writer, strResult, "MultiVersionPackageAnalyze", format)
9596
}
9697

9798
type SingleVersionPackageAnalyzeResult AnalyzeResult
@@ -115,7 +116,7 @@ func (r SingleVersionPackageAnalyzeResult) OutputStruct() interface{} {
115116
return output
116117
}
117118

118-
func (r SingleVersionPackageAnalyzeResult) OutputText(diffType string, format string) error {
119+
func (r SingleVersionPackageAnalyzeResult) OutputText(writer io.Writer, diffType string, format string) error {
119120
analysis, valid := r.Analysis.(map[string]PackageInfo)
120121
if !valid {
121122
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
133134
AnalyzeType: r.AnalyzeType,
134135
Analysis: strAnalysis,
135136
}
136-
return TemplateOutputFromFormat(strResult, "SingleVersionPackageAnalyze", format)
137+
return TemplateOutputFromFormat(writer, strResult, "SingleVersionPackageAnalyze", format)
137138
}
138139

139140
type SingleVersionPackageLayerAnalyzeResult AnalyzeResult
@@ -173,7 +174,7 @@ func (r SingleVersionPackageLayerAnalyzeResult) OutputStruct() interface{} {
173174
return output
174175
}
175176

176-
func (r SingleVersionPackageLayerAnalyzeResult) OutputText(diffType string, format string) error {
177+
func (r SingleVersionPackageLayerAnalyzeResult) OutputText(writer io.Writer, diffType string, format string) error {
177178
analysis, valid := r.Analysis.(PackageLayerDiff)
178179
if !valid {
179180
logrus.Error("Unexpected structure of Analysis. Should be of type PackageLayerDiff")
@@ -205,7 +206,7 @@ func (r SingleVersionPackageLayerAnalyzeResult) OutputText(diffType string, form
205206
AnalyzeType: r.AnalyzeType,
206207
Analysis: analysisOutput,
207208
}
208-
return TemplateOutputFromFormat(strResult, "SingleVersionPackageLayerAnalyze", format)
209+
return TemplateOutputFromFormat(writer, strResult, "SingleVersionPackageLayerAnalyze", format)
209210
}
210211

211212
type PackageOutput struct {
@@ -263,7 +264,7 @@ func (r FileAnalyzeResult) OutputStruct() interface{} {
263264
return r
264265
}
265266

266-
func (r FileAnalyzeResult) OutputText(analyzeType string, format string) error {
267+
func (r FileAnalyzeResult) OutputText(writer io.Writer, analyzeType string, format string) error {
267268
analysis, valid := r.Analysis.([]util.DirectoryEntry)
268269
if !valid {
269270
logrus.Error("Unexpected structure of Analysis. Should be of type []DirectoryEntry")
@@ -286,7 +287,7 @@ func (r FileAnalyzeResult) OutputText(analyzeType string, format string) error {
286287
AnalyzeType: r.AnalyzeType,
287288
Analysis: strAnalysis,
288289
}
289-
return TemplateOutputFromFormat(strResult, "FileAnalyze", format)
290+
return TemplateOutputFromFormat(writer, strResult, "FileAnalyze", format)
290291
}
291292

292293
type FileLayerAnalyzeResult AnalyzeResult
@@ -310,7 +311,7 @@ func (r FileLayerAnalyzeResult) OutputStruct() interface{} {
310311
return r
311312
}
312313

313-
func (r FileLayerAnalyzeResult) OutputText(analyzeType string, format string) error {
314+
func (r FileLayerAnalyzeResult) OutputText(writer io.Writer, analyzeType string, format string) error {
314315
analysis, valid := r.Analysis.([][]util.DirectoryEntry)
315316
if !valid {
316317
logrus.Error("Unexpected structure of Analysis. Should be of type []DirectoryEntry")
@@ -338,7 +339,7 @@ func (r FileLayerAnalyzeResult) OutputText(analyzeType string, format string) er
338339
AnalyzeType: r.AnalyzeType,
339340
Analysis: strDirectoryEntries,
340341
}
341-
return TemplateOutputFromFormat(strResult, "FileLayerAnalyze", format)
342+
return TemplateOutputFromFormat(writer, strResult, "FileLayerAnalyze", format)
342343
}
343344

344345
type SizeAnalyzeResult AnalyzeResult
@@ -353,7 +354,7 @@ func (r SizeAnalyzeResult) OutputStruct() interface{} {
353354
return r
354355
}
355356

356-
func (r SizeAnalyzeResult) OutputText(analyzeType string, format string) error {
357+
func (r SizeAnalyzeResult) OutputText(writer io.Writer, analyzeType string, format string) error {
357358
analysis, valid := r.Analysis.([]SizeEntry)
358359
if !valid {
359360
logrus.Error("Unexpected structure of Analysis. Should be of type []SizeEntry")
@@ -371,7 +372,7 @@ func (r SizeAnalyzeResult) OutputText(analyzeType string, format string) error {
371372
AnalyzeType: r.AnalyzeType,
372373
Analysis: strAnalysis,
373374
}
374-
return TemplateOutputFromFormat(strResult, "SizeAnalyze", format)
375+
return TemplateOutputFromFormat(writer, strResult, "SizeAnalyze", format)
375376
}
376377

377378
type SizeLayerAnalyzeResult AnalyzeResult
@@ -386,7 +387,7 @@ func (r SizeLayerAnalyzeResult) OutputStruct() interface{} {
386387
return r
387388
}
388389

389-
func (r SizeLayerAnalyzeResult) OutputText(analyzeType string, format string) error {
390+
func (r SizeLayerAnalyzeResult) OutputText(writer io.Writer, analyzeType string, format string) error {
390391
analysis, valid := r.Analysis.([]SizeEntry)
391392
if !valid {
392393
logrus.Error("Unexpected structure of Analysis. Should be of type []SizeEntry")
@@ -404,5 +405,5 @@ func (r SizeLayerAnalyzeResult) OutputText(analyzeType string, format string) er
404405
AnalyzeType: r.AnalyzeType,
405406
Analysis: strAnalysis,
406407
}
407-
return TemplateOutputFromFormat(strResult, "SizeLayerAnalyze", format)
408+
return TemplateOutputFromFormat(writer, strResult, "SizeLayerAnalyze", format)
408409
}

0 commit comments

Comments
 (0)