Export metrics to a file (#122)

This commit is contained in:
Alex Goodman 2018-12-08 12:44:10 -05:00 committed by GitHub
parent e63a886f04
commit d78b6cdc44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 99 additions and 19 deletions

View File

@ -1,6 +1,7 @@
package cmd
import (
"encoding/json"
"fmt"
"github.com/fatih/color"
"github.com/spf13/cobra"
@ -8,6 +9,7 @@ import (
"github.com/wagoodman/dive/image"
"github.com/wagoodman/dive/ui"
"github.com/wagoodman/dive/utils"
"io/ioutil"
)
// doAnalyzeCmd takes a docker image tag, digest, or id and displays the
@ -32,14 +34,84 @@ func doAnalyzeCmd(cmd *cobra.Command, args []string) {
cmd.Help()
utils.Exit(1)
}
color.New(color.Bold).Println("Analyzing Image")
result := fetchAndAnalyze(userImage)
fmt.Println(" Building cache...")
cache := filetree.NewFileTreeCache(result.RefTrees)
cache.Build()
run(userImage)
}
ui.Run(result, cache)
type export struct {
Layer []exportLayer `json:"layer"`
Image exportImage `json:"image"`
}
type exportLayer struct {
Index int `json:"index"`
DigestID string `json:"digestId"`
SizeBytes uint64 `json:"sizeBytes"`
Command string `json:"command"`
}
type exportImage struct {
SizeBytes uint64 `json:"sizeBytes"`
InefficientBytes uint64 `json:"inefficientBytes"`
EfficiencyScore float64 `json:"efficiencyScore"`
InefficientFiles []inefficientFiles `json:"inefficientFiles"`
}
type inefficientFiles struct {
Count int `json:"count"`
SizeBytes uint64 `json:"sizeBytes"`
File string `json:"file"`
}
func newExport(analysis *image.AnalysisResult) *export {
data := export{}
data.Layer = make([]exportLayer, len(analysis.Layers))
data.Image.InefficientFiles = make([]inefficientFiles, len(analysis.Inefficiencies))
// export layers in order
for revIdx := len(analysis.Layers) - 1; revIdx >= 0; revIdx-- {
layer := analysis.Layers[revIdx]
idx := (len(analysis.Layers) - 1) - revIdx
data.Layer[idx] = exportLayer{
Index: idx,
DigestID: layer.Id(),
SizeBytes: layer.Size(),
Command: layer.Command(),
}
}
// export image info
data.Image.SizeBytes = 0
for idx := 0; idx < len(analysis.Layers); idx++ {
data.Image.SizeBytes += analysis.Layers[idx].Size()
}
data.Image.EfficiencyScore = analysis.Efficiency
for idx := 0; idx < len(analysis.Inefficiencies); idx++ {
fileData := analysis.Inefficiencies[len(analysis.Inefficiencies)-1-idx]
data.Image.InefficientBytes += uint64(fileData.CumulativeSize)
data.Image.InefficientFiles[idx] = inefficientFiles{
Count: len(fileData.Nodes),
SizeBytes: uint64(fileData.CumulativeSize),
File: fileData.Path,
}
}
return &data
}
func exportStatistics(analysis *image.AnalysisResult) {
data := newExport(analysis)
payload, err := json.MarshalIndent(&data, "", " ")
if err != nil {
panic(err)
}
err = ioutil.WriteFile(exportFile, payload, 0644)
if err != nil {
panic(err)
}
}
func fetchAndAnalyze(imageID string) *image.AnalysisResult {
@ -60,3 +132,20 @@ func fetchAndAnalyze(imageID string) *image.AnalysisResult {
}
return result
}
func run(imageID string) {
color.New(color.Bold).Println("Analyzing Image")
result := fetchAndAnalyze(imageID)
if exportFile != "" {
exportStatistics(result)
color.New(color.Bold).Println(fmt.Sprintf("Exported to %s", exportFile))
utils.Exit(0)
}
fmt.Println(" Building cache...")
cache := filetree.NewFileTreeCache(result.RefTrees)
cache.Build()
ui.Run(result, cache)
}

View File

@ -1,12 +1,8 @@
package cmd
import (
"fmt"
"github.com/fatih/color"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/wagoodman/dive/filetree"
"github.com/wagoodman/dive/ui"
"github.com/wagoodman/dive/utils"
"io/ioutil"
"os"
@ -47,12 +43,5 @@ func doBuildCmd(cmd *cobra.Command, args []string) {
log.Fatal(err)
}
color.New(color.Bold).Println("Analyzing Image")
result := fetchAndAnalyze(string(imageId))
fmt.Println(" Building cache...")
cache := filetree.NewFileTreeCache(result.RefTrees)
cache.Build()
ui.Run(result, cache)
run(string(imageId))
}

View File

@ -15,6 +15,7 @@ import (
)
var cfgFile string
var exportFile string
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
@ -42,8 +43,9 @@ func init() {
cobra.OnInitialize(initLogging)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.dive.yaml, ~/.config/dive.yaml, or $XDG_CONFIG_HOME/dive.yaml)")
rootCmd.PersistentFlags().BoolP("version", "v", false, "display version number")
rootCmd.Flags().StringVarP(&exportFile, "json", "j", "", "Skip the interactive TUI and write the layer analysis statistics to a given file.")
}
// initConfig reads in config file and ENV variables if set.