From 485f5fc50078e05a62f3f915ecd9f2994017d7ae Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 2 Oct 2019 14:25:19 -0400 Subject: [PATCH 01/23] import podman runtime approach --- .../{get_analyzer.go => get_image_handler.go} | 8 ++-- dive/image/docker/{analyzer.go => image.go} | 23 +++++------- utils/docker.go | 37 ------------------- 3 files changed, 14 insertions(+), 54 deletions(-) rename dive/{get_analyzer.go => get_image_handler.go} (56%) rename dive/image/docker/{analyzer.go => image.go} (90%) delete mode 100644 utils/docker.go diff --git a/dive/get_analyzer.go b/dive/get_image_handler.go similarity index 56% rename from dive/get_analyzer.go rename to dive/get_image_handler.go index 1b3d465..69c1f67 100644 --- a/dive/get_analyzer.go +++ b/dive/get_image_handler.go @@ -1,12 +1,14 @@ package dive import ( + "fmt" "github.com/wagoodman/dive/dive/image" "github.com/wagoodman/dive/dive/image/docker" + "net/url" ) func GetAnalyzer(imageID string) image.Analyzer { - // u, _ := url.Parse(imageID) - // fmt.Printf("\n\nurl: %+v\n", u.Scheme) - return docker.NewImageAnalyzer(imageID) + u, _ := url.Parse(imageID) + fmt.Printf("\n\nurl: %+v\n", u.Scheme) + return docker.NewDockerImage(imageID) } diff --git a/dive/image/docker/analyzer.go b/dive/image/docker/image.go similarity index 90% rename from dive/image/docker/analyzer.go rename to dive/image/docker/image.go index 75fb0bf..adbd076 100644 --- a/dive/image/docker/analyzer.go +++ b/dive/image/docker/image.go @@ -19,7 +19,7 @@ import ( var dockerVersion string -type imageAnalyzer struct { +type dockerImage struct { id string client *client.Client jsonFiles map[string][]byte @@ -28,8 +28,8 @@ type imageAnalyzer struct { layers []*dockerLayer } -func NewImageAnalyzer(imageId string) *imageAnalyzer { - return &imageAnalyzer{ +func NewDockerImage(imageId string) *dockerImage { + return &dockerImage{ // store discovered json files in a map so we can read the image in one pass jsonFiles: make(map[string][]byte), layerMap: make(map[string]*filetree.FileTree), @@ -37,7 +37,7 @@ func NewImageAnalyzer(imageId string) *imageAnalyzer { } } -func (img *imageAnalyzer) Fetch() (io.ReadCloser, error) { +func (img *dockerImage) Fetch() (io.ReadCloser, error) { var err error // pull the img if it does not exist @@ -79,14 +79,9 @@ func (img *imageAnalyzer) Fetch() (io.ReadCloser, error) { } _, _, err = img.client.ImageInspectWithRaw(ctx, img.id) if err != nil { - - if !utils.IsDockerClientAvailable() { - return nil, fmt.Errorf("cannot find docker client executable") - } - // don't use the API, the CLI has more informative output fmt.Println("Image not available locally. Trying to pull '" + img.id + "'...") - err = utils.RunDockerCmd("pull", img.id) + err = runDockerCmd("pull", img.id) if err != nil { return nil, err } @@ -100,7 +95,7 @@ func (img *imageAnalyzer) Fetch() (io.ReadCloser, error) { return readCloser, nil } -func (img *imageAnalyzer) Parse(tarFile io.ReadCloser) error { +func (img *dockerImage) Parse(tarFile io.ReadCloser) error { tarReader := tar.NewReader(tarFile) var currentLayer uint @@ -144,7 +139,7 @@ func (img *imageAnalyzer) Parse(tarFile io.ReadCloser) error { return nil } -func (img *imageAnalyzer) Analyze() (*image.AnalysisResult, error) { +func (img *dockerImage) Analyze() (*image.AnalysisResult, error) { img.trees = make([]*filetree.FileTree, 0) manifest := newDockerImageManifest(img.jsonFiles["manifest.json"]) @@ -223,7 +218,7 @@ func (img *imageAnalyzer) Analyze() (*image.AnalysisResult, error) { }, nil } -func (img *imageAnalyzer) processLayerTar(name string, layerIdx uint, reader *tar.Reader) error { +func (img *dockerImage) processLayerTar(name string, layerIdx uint, reader *tar.Reader) error { tree := filetree.NewFileTree() tree.Name = name @@ -245,7 +240,7 @@ func (img *imageAnalyzer) processLayerTar(name string, layerIdx uint, reader *ta return nil } -func (img *imageAnalyzer) getFileList(tarReader *tar.Reader) ([]filetree.FileInfo, error) { +func (img *dockerImage) getFileList(tarReader *tar.Reader) ([]filetree.FileInfo, error) { var files []filetree.FileInfo for { diff --git a/utils/docker.go b/utils/docker.go deleted file mode 100644 index b5b9c1c..0000000 --- a/utils/docker.go +++ /dev/null @@ -1,37 +0,0 @@ -package utils - -import ( - "os" - "os/exec" - "strings" -) - -func IsDockerClientAvailable() bool { - _, err := exec.LookPath("docker") - return err == nil -} - -// RunDockerCmd runs a given Docker command in the current tty -func RunDockerCmd(cmdStr string, args ...string) error { - allArgs := cleanArgs(append([]string{cmdStr}, args...)) - - cmd := exec.Command("docker", allArgs...) - cmd.Env = os.Environ() - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Stdin = os.Stdin - - return cmd.Run() -} - -// cleanArgs trims the whitespace from the given set of strings. -func cleanArgs(s []string) []string { - var r []string - for _, str := range s { - if str != "" { - r = append(r, strings.Trim(str, " ")) - } - } - return r -} From acfdd70854314c65584a04d9d5f7e92cd7da33d0 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 2 Oct 2019 15:48:10 -0400 Subject: [PATCH 02/23] playing with podman store data --- cmd/analyze.go | 8 + cmd/build.go | 9 + cmd/root.go | 11 +- dive/get_image_handler.go | 40 ++- dive/image/analyzer.go | 3 - dive/image/docker/build.go | 27 ++ dive/image/docker/cli.go | 31 +++ dive/image/docker/image.go | 27 +- dive/image/docker/testing.go | 12 +- dive/image/image.go | 6 + dive/image/podman/build.go | 27 ++ dive/image/podman/cli.go | 31 +++ dive/image/podman/image.go | 269 +++++++++++++++++++ dive/image/resolver.go | 6 + go.mod | 24 +- go.sum | 505 +++++++++++++++++++++++++++++------ runtime/options.go | 2 + runtime/run.go | 63 ++--- utils/format.go | 12 + 19 files changed, 952 insertions(+), 161 deletions(-) create mode 100644 dive/image/docker/build.go create mode 100644 dive/image/docker/cli.go create mode 100644 dive/image/image.go create mode 100644 dive/image/podman/build.go create mode 100644 dive/image/podman/cli.go create mode 100644 dive/image/podman/image.go create mode 100644 dive/image/resolver.go diff --git a/cmd/analyze.go b/cmd/analyze.go index 8061de9..e521fc0 100644 --- a/cmd/analyze.go +++ b/cmd/analyze.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "github.com/wagoodman/dive/dive" "github.com/spf13/cobra" "github.com/wagoodman/dive/runtime" @@ -39,8 +40,15 @@ func doAnalyzeCmd(cmd *cobra.Command, args []string) { utils.Exit(1) } + engine, err := cmd.PersistentFlags().GetString("engine") + if err != nil { + fmt.Printf("unable to determine eingine: %v\n", err) + utils.Exit(1) + } + runtime.Run(runtime.Options{ Ci: isCi, + Engine: dive.GetEngine(engine), ImageId: userImage, ExportFile: exportFile, CiConfig: ciConfig, diff --git a/cmd/build.go b/cmd/build.go index 1ff2220..dbd25d2 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -1,7 +1,9 @@ package cmd import ( + "fmt" "github.com/spf13/cobra" + "github.com/wagoodman/dive/dive" "github.com/wagoodman/dive/runtime" "github.com/wagoodman/dive/utils" ) @@ -24,8 +26,15 @@ func doBuildCmd(cmd *cobra.Command, args []string) { initLogging() + engine, err := cmd.PersistentFlags().GetString("engine") + if err != nil { + fmt.Printf("unable to determine eingine: %v\n", err) + utils.Exit(1) + } + runtime.Run(runtime.Options{ Ci: isCi, + Engine: dive.GetEngine(engine), BuildArgs: args, ExportFile: exportFile, CiConfig: ciConfig, diff --git a/cmd/root.go b/cmd/root.go index 9dadad2..6bfb23a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "github.com/wagoodman/dive/dive" "github.com/wagoodman/dive/dive/filetree" "io/ioutil" "os" @@ -58,9 +59,15 @@ func initCli() { for _, key := range []string{"lowestEfficiency", "highestWastedBytes", "highestUserWastedPercent"} { if err := ciConfig.BindPFlag(fmt.Sprintf("rules.%s", key), rootCmd.Flags().Lookup(key)); err != nil { - log.Fatal("Unable to bind flag:", err) + log.Fatal("Unable to bind '%s' flag:", key, err) } } + + rootCmd.PersistentFlags().String("engine", "docker", "The container engine to fetch the image from. Allowed values: "+strings.Join(dive.AllowedEngines, ", ")) + + if err := ciConfig.BindPFlag("container-engine.default", rootCmd.PersistentFlags().Lookup("engine")); err != nil { + log.Fatal("Unable to bind 'engine' flag:", err) + } } // initConfig reads in config file and ENV variables if set. @@ -97,6 +104,8 @@ func initConfig() { viper.SetDefault("filetree.pane-width", 0.5) viper.SetDefault("filetree.show-attributes", true) + viper.SetDefault("container-engine.default", "docker") + viper.AutomaticEnv() // read in environment variables that match // If a config file is found, read it in. diff --git a/dive/get_image_handler.go b/dive/get_image_handler.go index 69c1f67..834ed11 100644 --- a/dive/get_image_handler.go +++ b/dive/get_image_handler.go @@ -4,11 +4,41 @@ import ( "fmt" "github.com/wagoodman/dive/dive/image" "github.com/wagoodman/dive/dive/image/docker" - "net/url" + "github.com/wagoodman/dive/dive/image/podman" ) -func GetAnalyzer(imageID string) image.Analyzer { - u, _ := url.Parse(imageID) - fmt.Printf("\n\nurl: %+v\n", u.Scheme) - return docker.NewDockerImage(imageID) +type Engine int + +const ( + EngineUnknown Engine = iota + EngineDocker + EnginePodman +) + +func (engine Engine) String() string { + return [...]string{"unknown", "docker", "podman"}[engine] +} + +var AllowedEngines = []string{EngineDocker.String(), EnginePodman.String()} + +func GetEngine(engine string) Engine { + switch engine { + case "docker": + return EngineDocker + case "podman": + return EnginePodman + default: + return EngineUnknown + } +} + +func GetImageHandler(engine Engine) (image.Image, error) { + switch engine { + case EngineDocker: + return docker.NewDockerImage(), nil + case EnginePodman: + return podman.NewPodmanImage(), nil + } + + return nil, fmt.Errorf("unable to determine image provider") } diff --git a/dive/image/analyzer.go b/dive/image/analyzer.go index 851774b..90a8689 100644 --- a/dive/image/analyzer.go +++ b/dive/image/analyzer.go @@ -2,12 +2,9 @@ package image import ( "github.com/wagoodman/dive/dive/filetree" - "io" ) type Analyzer interface { - Fetch() (io.ReadCloser, error) - Parse(io.ReadCloser) error Analyze() (*AnalysisResult, error) } diff --git a/dive/image/docker/build.go b/dive/image/docker/build.go new file mode 100644 index 0000000..490b72d --- /dev/null +++ b/dive/image/docker/build.go @@ -0,0 +1,27 @@ +package docker + +import ( + "io/ioutil" + "os" +) + +func buildImageFromCli(buildArgs []string) (string, error) { + iidfile, err := ioutil.TempFile("/tmp", "dive.*.iid") + if err != nil { + return "", err + } + defer os.Remove(iidfile.Name()) + + allArgs := append([]string{"--iidfile", iidfile.Name()}, buildArgs...) + err = runDockerCmd("build", allArgs...) + if err != nil { + return "", err + } + + imageId, err := ioutil.ReadFile(iidfile.Name()) + if err != nil { + return "", err + } + + return string(imageId), nil +} diff --git a/dive/image/docker/cli.go b/dive/image/docker/cli.go new file mode 100644 index 0000000..6c13696 --- /dev/null +++ b/dive/image/docker/cli.go @@ -0,0 +1,31 @@ +package docker + +import ( + "fmt" + "github.com/wagoodman/dive/utils" + "os" + "os/exec" +) + +// runDockerCmd runs a given Docker command in the current tty +func runDockerCmd(cmdStr string, args ...string) error { + if !isDockerClientBinaryAvailable() { + return fmt.Errorf("cannot find docker client executable") + } + + allArgs := utils.CleanArgs(append([]string{cmdStr}, args...)) + + cmd := exec.Command("docker", allArgs...) + cmd.Env = os.Environ() + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + + return cmd.Run() +} + +func isDockerClientBinaryAvailable() bool { + _, err := exec.LookPath("docker") + return err == nil +} diff --git a/dive/image/docker/image.go b/dive/image/docker/image.go index adbd076..b346bd8 100644 --- a/dive/image/docker/image.go +++ b/dive/image/docker/image.go @@ -28,23 +28,40 @@ type dockerImage struct { layers []*dockerLayer } -func NewDockerImage(imageId string) *dockerImage { +func NewDockerImage() *dockerImage { return &dockerImage{ // store discovered json files in a map so we can read the image in one pass jsonFiles: make(map[string][]byte), layerMap: make(map[string]*filetree.FileTree), - id: imageId, } } -func (img *dockerImage) Fetch() (io.ReadCloser, error) { +func (img *dockerImage) Get(id string) error { + img.id = id + + reader, err := img.fetch() + if err != nil { + return err + } + defer reader.Close() + + return img.parse(reader) +} + +func (img *dockerImage) Build(args []string) (string, error) { + var err error + img.id, err = buildImageFromCli(args) + return img.id, err +} + +func (img *dockerImage) fetch() (io.ReadCloser, error) { var err error // pull the img if it does not exist ctx := context.Background() host := os.Getenv("DOCKER_HOST") - var clientOpts []func(*client.Client) error + var clientOpts []client.Opt switch strings.Split(host, ":")[0] { case "ssh": @@ -95,7 +112,7 @@ func (img *dockerImage) Fetch() (io.ReadCloser, error) { return readCloser, nil } -func (img *dockerImage) Parse(tarFile io.ReadCloser) error { +func (img *dockerImage) parse(tarFile io.ReadCloser) error { tarReader := tar.NewReader(tarFile) var currentLayer uint diff --git a/dive/image/docker/testing.go b/dive/image/docker/testing.go index 8998a29..ed7c6b2 100644 --- a/dive/image/docker/testing.go +++ b/dive/image/docker/testing.go @@ -11,10 +11,16 @@ func TestLoadDockerImageTar(tarPath string) (*image.AnalysisResult, error) { return nil, err } defer f.Close() - analyzer := NewImageAnalyzer("dive-test:latest") - err = analyzer.Parse(f) + + img := NewDockerImage() + err = img.Get("dive-test:latest") if err != nil { return nil, err } - return analyzer.Analyze() + + err = img.parse(f) + if err != nil { + return nil, err + } + return img.Analyze() } diff --git a/dive/image/image.go b/dive/image/image.go new file mode 100644 index 0000000..73d802e --- /dev/null +++ b/dive/image/image.go @@ -0,0 +1,6 @@ +package image + +type Image interface { + Resolver + Analyzer +} diff --git a/dive/image/podman/build.go b/dive/image/podman/build.go new file mode 100644 index 0000000..f8813c6 --- /dev/null +++ b/dive/image/podman/build.go @@ -0,0 +1,27 @@ +package podman + +import ( + "io/ioutil" + "os" +) + +func buildImageFromCli(buildArgs []string) (string, error) { + iidfile, err := ioutil.TempFile("/tmp", "dive.*.iid") + if err != nil { + return "", err + } + defer os.Remove(iidfile.Name()) + + allArgs := append([]string{"--iidfile", iidfile.Name()}, buildArgs...) + err = runPodmanCmd("build", allArgs...) + if err != nil { + return "", err + } + + imageId, err := ioutil.ReadFile(iidfile.Name()) + if err != nil { + return "", err + } + + return string(imageId), nil +} diff --git a/dive/image/podman/cli.go b/dive/image/podman/cli.go new file mode 100644 index 0000000..90f26f3 --- /dev/null +++ b/dive/image/podman/cli.go @@ -0,0 +1,31 @@ +package podman + +import ( + "fmt" + "github.com/wagoodman/dive/utils" + "os" + "os/exec" +) + +// runPodmanCmd runs a given Podman command in the current tty +func runPodmanCmd(cmdStr string, args ...string) error { + if !isPodmanClientBinaryAvailable() { + return fmt.Errorf("cannot find podman client executable") + } + + allArgs := utils.CleanArgs(append([]string{cmdStr}, args...)) + + cmd := exec.Command("podman", allArgs...) + cmd.Env = os.Environ() + + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + + return cmd.Run() +} + +func isPodmanClientBinaryAvailable() bool { + _, err := exec.LookPath("podman") + return err == nil +} diff --git a/dive/image/podman/image.go b/dive/image/podman/image.go new file mode 100644 index 0000000..4769dd0 --- /dev/null +++ b/dive/image/podman/image.go @@ -0,0 +1,269 @@ +package podman + +import ( + "context" + "fmt" + "github.com/containers/libpod/libpod" + "os" + + // "github.com/containers/libpod/libpod" + // libpodImage "github.com/containers/libpod/libpod/image" + // "github.com/containers/storage" + "github.com/wagoodman/dive/dive/filetree" + "github.com/wagoodman/dive/dive/image" + "io" +) + +type podmanImage struct { + id string + jsonFiles map[string][]byte + trees []*filetree.FileTree + layerMap map[string]*filetree.FileTree + // layers []*podmanLayer +} + +func NewPodmanImage() *podmanImage { + return &podmanImage{ + // store discovered json files in a map so we can read the image in one pass + jsonFiles: make(map[string][]byte), + layerMap: make(map[string]*filetree.FileTree), + } +} + +func (img *podmanImage) Get(id string) error { + img.id = id + + reader, err := img.fetch() + if err != nil { + return err + } + defer reader.Close() + + return img.parse(reader) +} + +func (img *podmanImage) Build(args []string) (string, error) { + var err error + img.id, err = buildImageFromCli(args) + return img.id, err +} + +func (img *podmanImage) fetch() (io.ReadCloser, error) { + var err error + + runtime, err := libpod.NewRuntime(context.TODO()) + if err != nil { + return nil, err + } + + images, err := runtime.ImageRuntime().GetImages() + if err != nil { + return nil, err + } + + // cfg, _ := runtime.GetConfig() + // cfg.StorageConfig.GraphRoot + + for _, item:= range images { + for _, name := range item.Names() { + if name == img.id { + fmt.Println("Found it!") + + curImg := item + for { + h, _ := curImg.History(context.TODO()) + fmt.Printf("%+v %+v %+v\n", curImg.ID(), h[0].Size, h[0].CreatedBy) + x, _ := curImg.DriverData() + fmt.Printf(" %+v\n", x) + // for _, i := range x { + // fmt.Printf(" %+v\n", i) + // } + + curImg, err = curImg.GetParent(context.TODO()) + if err != nil || curImg == nil { + break + } + } + + } + } + } + + os.Exit(0) + + return nil, err +} + +func (img *podmanImage) parse(tarFile io.ReadCloser) error { + // tarReader := tar.NewReader(tarFile) + // + // var currentLayer uint + // for { + // header, err := tarReader.Next() + // + // if err == io.EOF { + // break + // } + // + // if err != nil { + // fmt.Println(err) + // utils.Exit(1) + // } + // + // name := header.Name + // + // // some layer tars can be relative layer symlinks to other layer tars + // if header.Typeflag == tar.TypeSymlink || header.Typeflag == tar.TypeReg { + // + // if strings.HasSuffix(name, "layer.tar") { + // currentLayer++ + // if err != nil { + // return err + // } + // layerReader := tar.NewReader(tarReader) + // err := img.processLayerTar(name, currentLayer, layerReader) + // if err != nil { + // return err + // } + // } else if strings.HasSuffix(name, ".json") { + // fileBuffer, err := ioutil.ReadAll(tarReader) + // if err != nil { + // return err + // } + // img.jsonFiles[name] = fileBuffer + // } + // } + // } + // + // return nil + return nil +} + +func (img *podmanImage) Analyze() (*image.AnalysisResult, error) { + // img.trees = make([]*filetree.FileTree, 0) + // + // manifest := newDockerImageManifest(img.jsonFiles["manifest.json"]) + // config := newDockerImageConfig(img.jsonFiles[manifest.ConfigPath]) + // + // // build the content tree + // for _, treeName := range manifest.LayerTarPaths { + // img.trees = append(img.trees, img.layerMap[treeName]) + // } + // + // // build the layers array + // img.layers = make([]*dockerLayer, len(img.trees)) + // + // // note that the img config stores images in reverse chronological order, so iterate backwards through layers + // // as you iterate chronologically through history (ignoring history items that have no layer contents) + // // Note: history is not required metadata in a docker img! + // tarPathIdx := 0 + // histIdx := 0 + // for layerIdx := len(img.trees) - 1; layerIdx >= 0; layerIdx-- { + // + // tree := img.trees[(len(img.trees)-1)-layerIdx] + // + // // ignore empty layers, we are only observing layers with content + // historyObj := imageHistoryEntry{ + // CreatedBy: "(missing)", + // } + // for nextHistIdx := histIdx; nextHistIdx < len(config.History); nextHistIdx++ { + // if !config.History[nextHistIdx].EmptyLayer { + // histIdx = nextHistIdx + // break + // } + // } + // if histIdx < len(config.History) && !config.History[histIdx].EmptyLayer { + // historyObj = config.History[histIdx] + // histIdx++ + // } + // + // img.layers[layerIdx] = &dockerLayer{ + // history: historyObj, + // index: tarPathIdx, + // tree: img.trees[layerIdx], + // tarPath: manifest.LayerTarPaths[tarPathIdx], + // } + // img.layers[layerIdx].history.Size = tree.FileSize + // + // tarPathIdx++ + // } + // + // efficiency, inefficiencies := filetree.Efficiency(img.trees) + // + // var sizeBytes, userSizeBytes uint64 + // layers := make([]image.Layer, len(img.layers)) + // for i, v := range img.layers { + // layers[i] = v + // sizeBytes += v.Size() + // if i != 0 { + // userSizeBytes += v.Size() + // } + // } + // + // var wastedBytes uint64 + // for idx := 0; idx < len(inefficiencies); idx++ { + // fileData := inefficiencies[len(inefficiencies)-1-idx] + // wastedBytes += uint64(fileData.CumulativeSize) + // } + // + // return &image.AnalysisResult{ + // Layers: layers, + // RefTrees: img.trees, + // Efficiency: efficiency, + // UserSizeByes: userSizeBytes, + // SizeBytes: sizeBytes, + // WastedBytes: wastedBytes, + // WastedUserPercent: float64(wastedBytes) / float64(userSizeBytes), + // Inefficiencies: inefficiencies, + // }, nil + + return nil, nil +} + +// func (img *podmanImage) processLayerTar(name string, layerIdx uint, reader *tar.Reader) error { +// tree := filetree.NewFileTree() +// tree.Name = name +// +// fileInfos, err := img.getFileList(reader) +// if err != nil { +// return err +// } +// +// for _, element := range fileInfos { +// tree.FileSize += uint64(element.Size) +// +// _, _, err := tree.AddPath(element.Path, element) +// if err != nil { +// return err +// } +// } +// +// img.layerMap[tree.Name] = tree +// return nil +// } +// +// func (img *podmanImage) getFileList(tarReader *tar.Reader) ([]filetree.FileInfo, error) { +// var files []filetree.FileInfo +// +// for { +// header, err := tarReader.Next() +// if err == io.EOF { +// break +// } else if err != nil { +// fmt.Println(err) +// utils.Exit(1) +// } +// +// name := header.Name +// +// switch header.Typeflag { +// case tar.TypeXGlobalHeader: +// return nil, fmt.Errorf("unexptected tar file: (XGlobalHeader): type=%v name=%s", header.Typeflag, name) +// case tar.TypeXHeader: +// return nil, fmt.Errorf("unexptected tar file (XHeader): type=%v name=%s", header.Typeflag, name) +// default: +// files = append(files, filetree.NewFileInfo(tarReader, header, name)) +// } +// } +// return files, nil +// } diff --git a/dive/image/resolver.go b/dive/image/resolver.go new file mode 100644 index 0000000..9485e13 --- /dev/null +++ b/dive/image/resolver.go @@ -0,0 +1,6 @@ +package image + +type Resolver interface { + Get(id string) error + Build(options []string) (string, error) +} diff --git a/go.mod b/go.mod index 0baa9cc..2848555 100644 --- a/go.mod +++ b/go.mod @@ -3,58 +3,44 @@ module github.com/wagoodman/dive go 1.13 require ( - github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect github.com/Microsoft/go-winio v0.4.14 // indirect - github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/cespare/xxhash v1.1.0 + github.com/containers/libpod v1.6.0 github.com/docker/cli v0.0.0-20190906153656-016a3232168d - github.com/docker/distribution v2.7.1+incompatible // indirect - github.com/docker/docker v0.0.0-20181126153310-0b7cb16dde4a20d024c7be59801d63bcfd18611b - github.com/docker/go-connections v0.4.0 // indirect - github.com/docker/go-units v0.4.0 // indirect + github.com/docker/docker v0.7.3-0.20190309235953-33c3200e0d16 + github.com/docker/engine v1.4.2-0.20190822205725-ed20165a37b4 // indirect github.com/dustin/go-humanize v1.0.0 github.com/fatih/color v1.7.0 github.com/gogo/protobuf v1.3.0 // indirect github.com/golang/lint v0.0.0-20180702182130-06c8688daad7 // indirect github.com/golangci/golangci-lint v1.17.1 // indirect github.com/google/uuid v1.1.1 - github.com/gorilla/context v1.1.1 // indirect - github.com/gorilla/mux v1.6.2 // indirect github.com/jroimartin/gocui v0.4.0 github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/logrusorgru/aurora v0.0.0-20190803045625-94edacc10f9b github.com/lunixbochs/vtclean v1.0.0 - github.com/magiconair/properties v1.8.1 // indirect github.com/mattn/go-colorable v0.1.2 // indirect github.com/mattn/go-isatty v0.0.9 // indirect github.com/mattn/go-runewidth v0.0.4 // indirect github.com/mitchellh/go-homedir v1.1.0 github.com/nsf/termbox-go v0.0.0-20190817171036-93860e161317 // indirect - github.com/opencontainers/go-digest v1.0.0-rc1 // indirect - github.com/opencontainers/image-spec v1.0.1 // indirect - github.com/pelletier/go-toml v1.4.0 // indirect github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee github.com/sergi/go-diff v1.0.0 github.com/sirupsen/logrus v1.4.2 - github.com/spf13/afero v1.2.2 // indirect github.com/spf13/cobra v0.0.5 - github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/viper v1.4.0 github.com/wagoodman/keybinding v0.0.0-20181213133715-6a824da6df05 golang.org/x/crypto v0.0.0-20190907121410-71b5226ff739 // indirect golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03 // indirect - golang.org/x/text v0.3.2 // indirect - google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 - gotest.tools v2.2.0+incompatible // indirect + google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601 ) // related to an invalid pseudo version in github.com/docker/distribution@v0.0.0-20181126153310-93e082742a009850ac46962150b2f652a822c5ff replace github.com/docker/distribution => github.com/docker/distribution v2.7.0-rc.0.0.20181024170156-93e082742a00+incompatible // related to an invalid pseudo version in github.com/docker/distribution@v0.0.0-20181126153310-93e082742a009850ac46962150b2f652a822c5ff -replace github.com/docker/docker => github.com/docker/docker v1.4.2-0.20181124105010-0b7cb16dde4a +replace github.com/docker/docker => github.com/docker/engine v0.0.0-20190822205725-ed20165a37b4 // relates to https://github.com/golangci/golangci-lint/issues/581 replace github.com/go-critic/go-critic => github.com/go-critic/go-critic v0.3.5-0.20190526074819-1df300866540 diff --git a/go.sum b/go.sum index 1bb942f..2b6849a 100644 --- a/go.sum +++ b/go.sum @@ -1,171 +1,304 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.40.0/go.mod h1:Tk58MuI9rbLMKlAjeO/bDnteAx7tX2gJIXw4T5Jwlro= +github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774 h1:SCbEWT58NSt7d2mcFdvxC9uyrdcTfvBbPLThhkDmXzg= +github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774/go.mod h1:6/0dYRLLXyJjbkIPeeGyoJ/eKOSI0eU6eTlCBYibgd0= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v12.1.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/zstd v1.4.0/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OpenPeeDeeP/depguard v0.0.0-20180806142446-a69c782687b2 h1:HTOmFEEYrWi4MW5ZKUx6xfeyM10Sx3kQF65xiQJMPYA= github.com/OpenPeeDeeP/depguard v0.0.0-20180806142446-a69c782687b2/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o= +github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= +github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/buger/goterm v0.0.0-20181115115552-c206103e1f37/go.mod h1:u9UyCz2eTrSGy6fbupqJ54eY5c4IC8gREQ1053dK12U= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/checkpoint-restore/go-criu v0.0.0-20190109184317-bdb7599cd87b h1:T4nWG1TXIxeor8mAu5bFguPJgSIGhZqv/f0z55KCrJM= +github.com/checkpoint-restore/go-criu v0.0.0-20190109184317-bdb7599cd87b/go.mod h1:TrMrLQfeENAPYPRsJuq3jsqdlRh3lvi6trTZJG8+tho= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/continuity v0.0.0-20180814194400-c7c5070e6f6e/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containernetworking/cni v0.7.1 h1:fE3r16wpSEyaqY4Z4oFrLMmIGfBYIKpPrHK31EJ9FzE= +github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/plugins v0.8.1 h1:dJbykiiSIS3Xvo8d+A6rSXcUEFGfvCjUA+bUED4qegQ= +github.com/containernetworking/plugins v0.8.1/go.mod h1:dagHaAhNjXjT9QYOklkKJDGaQPTg4pf//FrUcJeb7FU= +github.com/containers/buildah v1.11.2 h1:U6Abrp1J7H19vHvhqIran4Xvw+Z3WIqMM86fIt9L7Qk= +github.com/containers/buildah v1.11.2/go.mod h1:CtnP3vsLiU3xgKvkhdb4b0IzYwXNzHRv3ezl4z+RPC0= +github.com/containers/conmon v0.3.0/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= +github.com/containers/image v3.0.2+incompatible h1:B1lqAE8MUPCrsBLE86J0gnXleeRq8zJnQryhiiGQNyE= +github.com/containers/image v3.0.2+incompatible/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M= +github.com/containers/libpod v1.6.0 h1:OhoW3ssFTDly9KxXwwMAQUgSLK7Q+vkIPtBTzqLyxh0= +github.com/containers/libpod v1.6.0/go.mod h1:VLZ5FnOkW94RsOqYbIKEqFbSb0fg9WVJLO6Vey+k02Y= +github.com/containers/psgo v1.3.1 h1:1kE+jJ9Ou5f9zQT/M2IdeSclsKWsXrSFlOcnqc+F2TA= +github.com/containers/psgo v1.3.1/go.mod h1:LLiRMmxZ6FWP4bB/fOUu6kDT+4okk/ZCeeykqh0O5Ns= +github.com/containers/storage v1.13.2/go.mod h1:6D8nK2sU9V7nEmAraINRs88ZEscM5C5DK+8Npp27GeA= +github.com/containers/storage v1.13.4 h1:j0bBaJDKbUHtAW1MXPFnwXJtqcH+foWeuXK1YaBV5GA= +github.com/containers/storage v1.13.4/go.mod h1:6D8nK2sU9V7nEmAraINRs88ZEscM5C5DK+8Npp27GeA= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-iptables v0.4.2/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cri-o/ocicni v0.1.1-0.20190702175919-7762645d18ca h1:CJstDqYy9ClWuPcDHMTCAiUS+ckekluYetGR2iYYWuo= +github.com/cri-o/ocicni v0.1.1-0.20190702175919-7762645d18ca/go.mod h1:BO0al9TKber3XUTucLzKgoG5sq8qiOB41H7zSdfw6r8= +github.com/cyphar/filepath-securejoin v0.2.1/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= +github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/docker/cli v0.0.0-20190303104010-8ddde26af67f h1:avQULAD2yl7pQnDbjIxI/H5cwpRn8H/NeMNIbhnDtfI= -github.com/docker/cli v0.0.0-20190303104010-8ddde26af67f/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v0.0.0-20190906153656-016a3232168d h1:gwX/88xJZfxZV1yjhhuQpWTmEgJis7/XGCVu3iDIZYU= github.com/docker/cli v0.0.0-20190906153656-016a3232168d/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v0.0.0-20181126153310-93e082742a009850ac46962150b2f652a822c5ff h1:dtgxyWsA/HV5EbB2FO4YKoCKEjLwr8SXZQCGjA9mRx4= -github.com/docker/distribution v0.0.0-20181126153310-93e082742a009850ac46962150b2f652a822c5ff/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.0-rc.0.0.20181024170156-93e082742a00+incompatible h1:YOfVNTgst//UrD5ZhDfbY0+GTSWjXfXOYLYHhw0kMpo= github.com/docker/distribution v2.7.0-rc.0.0.20181024170156-93e082742a00+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v0.0.0-20181126153310-0b7cb16dde4a20d024c7be59801d63bcfd18611b h1:T6w2S9UvmzJQBKIUsOs3+lNWfZea5yUPLh2eMihRrKY= -github.com/docker/docker v0.0.0-20181126153310-0b7cb16dde4a20d024c7be59801d63bcfd18611b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v1.4.2-0.20181124105010-0b7cb16dde4a h1:8DkTRrioG4IEbvyukSm2UouCOPrcPYXlleW3QeNWfw0= github.com/docker/docker v1.4.2-0.20181124105010-0b7cb16dde4a/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v1.4.2-0.20181124105010-f5ec1e2936dcbe7b5001c2b817188b095c700c27/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.1/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= +github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/engine v0.0.0-20190822180741-9552f2b2fdde h1:6JcsC3tTf9ISGL2xesRNf0vQ/RgpVXXNYz/IFXQoqX0= +github.com/docker/engine v0.0.0-20190822180741-9552f2b2fdde/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= +github.com/docker/engine v0.0.0-20190822205725-ed20165a37b4 h1:hB02X3RT4IZUr/dm+zIXpAlpvQPtRyKBEA/CbzshNZ8= +github.com/docker/engine v0.0.0-20190822205725-ed20165a37b4/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= +github.com/docker/engine v1.4.2-0.20100822105010-9552f2b2fddeb0c2537b350f4b159ffe525d7a42/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= +github.com/docker/engine v1.4.2-0.20100824105010-9552f2b2fddeb0c2537b350f4b159ffe525d7a42/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= +github.com/docker/engine v1.4.2-0.20181124105010-0b7cb16dde4a h1:36nEEPrc6RgAy3HziqtYNtiOpbk3a9+2nPC0iRwOs+k= +github.com/docker/engine v1.4.2-0.20181124105010-0b7cb16dde4a/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= +github.com/docker/engine v1.4.2-0.20181124105010-9552f2b2fddeb0c2537b350f4b159ffe525d7a42/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= +github.com/docker/engine v1.4.2-0.20181124105010-f5ec1e2936dcbe7b5001c2b817188b095c700c27/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= +github.com/docker/engine v1.4.2-0.20190822205725-ed20165a37b4 h1:+VAGRKyn9Ca+ckzV/PJsaRO7UXO9KQjFmSffcSDrWdE= +github.com/docker/engine v1.4.2-0.20190822205725-ed20165a37b4/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= +github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 h1:X0fj836zx99zFu83v/M79DuBn84IL/Syx1SY6Y5ZEMA= +github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libnetwork v0.8.0-dev.2.0.20180608203834-19279f049241/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8= +github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316 h1:moehPjPiGUaWdwgOl92xRyFHJyaqXDHcCyW9M6nmCK4= +github.com/docker/libnetwork v0.8.0-dev.2.0.20190625141545-5a177b73e316/go.mod h1:93m0aTqz6z+g32wla4l4WxTrdtvBRmVzYRkYvasA5Z8= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c h1:ZfSZ3P3BedhKGUhzj7BQlPSU4OvT6tfOKe3DVHzOA7s= +github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f h1:8GDPb0tCY8LQ+OJ3dbHb5sA6YZWXFORQYZx5sdsTlMs= +github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1 h1:yY9rWGoXv1U5pl4gxqlULARMQD7x0QG85lqEXTWysik= +github.com/elazarl/goproxy/ext v0.0.0-20190911111923-ecfe977594f1 h1:8B7WF1rIoM8H1smfpXFvOawSAzlRDMVzoGu9zE3+OCk= +github.com/elazarl/goproxy/ext v0.0.0-20190911111923-ecfe977594f1/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.6+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM= +github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsouza/go-dockerclient v1.3.0/go.mod h1:IN9UPc4/w7cXiARH2Yg99XxUHbAM+6rAi9hzBVbkWRU= +github.com/fsouza/go-dockerclient v1.4.1 h1:W7wuJ3IB48WYZv/UBk9dCTIb9oX805+L9KIm65HcUYs= +github.com/fsouza/go-dockerclient v1.4.1/go.mod h1:PUNHxbowDqRXfRgZqMz1OeGtbWC6VKyZvJ99hDjB0qs= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-critic/go-critic v0.0.0-20181204210945-1df300866540 h1:7CU1IXBpPvxpQ/NqJrpuMXMHAw+FB2vfqtRF8tgW9fw= -github.com/go-critic/go-critic v0.0.0-20181204210945-1df300866540/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA= github.com/go-critic/go-critic v0.3.5-0.20190526074819-1df300866540/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-lintpack/lintpack v0.5.2 h1:DI5mA3+eKdWeJ40nU4d6Wc26qmdG8RCi/btYq0TuRN0= github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= +github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g= github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= -github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8= github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ= github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg= -github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k= github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk= -github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg= github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks= github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= -github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4= github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= -github.com/go-toolsmith/typep v1.0.0 h1:zKymWyA1TRYvqYrYDrfEMZULyrhcnGY3x7LDKU2XQaA= github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= -github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/godbus/dbus v0.0.0-20181101234600-2ff6f7ffd60f h1:zlOR3rOlPAVvtfuxGKoghCmop5B0TRyu/ZieziZuGiM= +github.com/godbus/dbus v0.0.0-20181101234600-2ff6f7ffd60f/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.0.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= -github.com/golangci/errcheck v0.0.0-20181003203344-ef45e06d44b6 h1:i2jIkQFb8RG45DuQs+ElyROY848cSJIoIkBM+7XXypA= -github.com/golangci/errcheck v0.0.0-20181003203344-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0= github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0= -github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw= github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= -github.com/golangci/go-tools v0.0.0-20180109140146-af6baa5dc196 h1:9rtVlONXLF1rJZzvLt4tfOXtnAFUEhxCJ64Ibzj6ECo= -github.com/golangci/go-tools v0.0.0-20180109140146-af6baa5dc196/go.mod h1:unzUULGw35sjyOYjUt0jMTXqHlZPpPc6e+xfO4cd6mM= github.com/golangci/go-tools v0.0.0-20190318060251-af6baa5dc196/go.mod h1:unzUULGw35sjyOYjUt0jMTXqHlZPpPc6e+xfO4cd6mM= -github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3 h1:pe9JHs3cHHDQgOFXJJdYkK6fLz2PWyYtP4hthoCMvs8= github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o= -github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee h1:J2XAy40+7yz70uaOiMbNnluTg7gyQhtGqLQncQh+4J8= github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU= -github.com/golangci/gofmt v0.0.0-20181105071733-0b8337e80d98 h1:ir6/L2ZOJfFrJlOTsuf/hlzdPuUwXV/VzkSlgS6f1vs= -github.com/golangci/gofmt v0.0.0-20181105071733-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= -github.com/golangci/golangci-lint v1.17.1 h1:lc8Hf9GPCjIr0hg3S/xhvFT1+Hydass8F1xchr8jkME= github.com/golangci/golangci-lint v1.17.1/go.mod h1:+5sJSl2h3aly+fpmL2meSP8CaSKua2E4Twi9LPy7b1g= -github.com/golangci/gosec v0.0.0-20180901114220-66fb7fc33547 h1:qMomh8bv+kDazm1dSLZ9S3zZ2PJZMHL4ilfBjxFOlmI= -github.com/golangci/gosec v0.0.0-20180901114220-66fb7fc33547/go.mod h1:0qUabqiIQgfmlAmulqxyiGkkyF6/tOGSnY2cnPVwrzU= github.com/golangci/gosec v0.0.0-20190211064107-66fb7fc33547/go.mod h1:0qUabqiIQgfmlAmulqxyiGkkyF6/tOGSnY2cnPVwrzU= -github.com/golangci/ineffassign v0.0.0-20180808204949-42439a7714cc h1:XRFao922N8F3EcIXBSNX8Iywk+GI0dxD/8FicMX2D/c= -github.com/golangci/ineffassign v0.0.0-20180808204949-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU= github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU= -github.com/golangci/lint-1 v0.0.0-20180610141402-ee948d087217 h1:r7vyX+SN24x6+5AnpnrRn/bdwBb7U+McZqCHOVtXDuk= -github.com/golangci/lint-1 v0.0.0-20180610141402-ee948d087217/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= github.com/golangci/lint-1 v0.0.0-20190420132249-ee948d087217/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= -github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770 h1:EL/O5HGrF7Jaq0yNhBLucz9hTuRzj2LdwGBOaENgxIk= github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= -github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21 h1:leSNB7iYzLYSSx3J/s5sVf4Drkc68W2wm4Ixh/mr0us= github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI= -github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0 h1:HVfrLniijszjS1aiNg8JbBMO2+E1WIQ+j/gL4SQqGPg= github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys= github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= +github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/uuid v1.1.0 h1:Jf4mxPC/ziBnoPIdpQdPJ9OeiomAUHLvxmPRSPH9m4s= -github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= +github.com/gophercloud/gophercloud v0.2.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I= +github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 h1:JVnpOZS+qxli+rgVl98ILOXVNbW+kb5wcxeGx8ShUIw= github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= +github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.2/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd h1:anPrsicrIi2ColgWTVPk+TrN42hJIWlfPHSBP9S0ZkM= +github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:3LVOLeyx9XVvwPgrt2be44XgSqndprz1G18rSk8KD84= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= +github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/ishidawataru/sctp v0.0.0-20180918013207-6e2cb1366111 h1:NAAiV9ass6VReWFjuxqrMIq12WKlSULI6Gs3PxQghLA= +github.com/ishidawataru/sctp v0.0.0-20180918013207-6e2cb1366111/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jroimartin/gocui v0.4.0 h1:52jnalstgmc25FmtGcWqa0tcbMEWS6RpFLsOIO+I+E8= github.com/jroimartin/gocui v0.4.0/go.mod h1:7i7bbj99OgFHzo7kB2zPb8pXLqMBSQegY7azfqXMkyY= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -174,22 +307,28 @@ github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.7.2 h1:liMOoeIvFpr9kEvalrZ7VVBA4wGf7zfOgwBjzz/5g2Y= +github.com/klauspost/compress v1.7.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/pgzip v1.2.1 h1:oIPZROsWuPHpOdMVWLuJZXwgjhrW8r1yEX8UqMyeNHM= +github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e h1:9MlwzLdW7QSDrhDjFlsEYmxpFyIoXmYRon3dt0io31k= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/logrusorgru/aurora v0.0.0-20190803045625-94edacc10f9b h1:PMbSa9CgaiQR9NLlUTwKi+7aeLl3GG5JX5ERJxfQ3IE= github.com/logrusorgru/aurora v0.0.0-20190803045625-94edacc10f9b/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw= -github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -197,12 +336,13 @@ github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDe github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= +github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= @@ -211,22 +351,37 @@ github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8Bz github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-shellwords v1.0.5 h1:JhhFTIOslh5ZsPrpa3Wdg8bF0WI3b44EMblmU9wIsXc= +github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= +github.com/mistifyio/go-zfs v2.1.1+incompatible h1:gAMO1HM9xBRONLHHYnu5iFsOJUiJdNZo6oqSENd4eW8= +github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.0.0 h1:vVpGvMXJPqSDh2VYHF7gsfQj8Ncx+Xw5Y1KHeTRY+7I= -github.com/mitchellh/mapstructure v1.0.0/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/moby/moby v0.0.0-20171005181806-f8806b18b4b9/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= +github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= +github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c h1:xa+eQWKuJ9MbB9FBL/eoNvDFvveAkz2LQoz8PzX7Q/4= +github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c/go.mod h1:GhAqVMEWnTcW2dxoD/SO3n2enrgWl3y6Dnx4m59GvcA= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20190414153302-2ae31c8b6b30/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= -github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663 h1:Ri1EhipkbhWsffPJ3IPlrb4SkTOPa2PfRXp3jchBczw= github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/nsf/termbox-go v0.0.0-20181027232701-60ab7e3d12ed h1:bAVGG6B+R5qpSylrrA+BAMrzYkdAoiTaKPVxRB+4cyM= github.com/nsf/termbox-go v0.0.0-20181027232701-60ab7e3d12ed/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= @@ -235,67 +390,117 @@ github.com/nsf/termbox-go v0.0.0-20190817171036-93860e161317/go.mod h1:IuKpRQcYE github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190425234816-dae70e8efea4/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc8/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc8.0.20190827142921-dd075602f158 h1:/A6bAdnSZoTQmKml3MdHAnSEPnBAQeigNBl4sxnfaaQ= +github.com/opencontainers/runc v1.0.0-rc8.0.20190827142921-dd075602f158/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7 h1:Dliu5QO+4JYWu/yMshaMU7G3JN2POGpwjJN7gjy10Go= +github.com/opencontainers/runtime-spec v0.1.2-0.20190618234442-a950415649c7/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.9.0 h1:FYgwVsKRI/H9hU32MJ/4MLOzXWodKK5zsQavY8NPMkU= +github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opencontainers/selinux v1.2.2/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs= +github.com/opencontainers/selinux v1.3.0 h1:xsI95WzPZu5exzA6JzkLSfdr/DilzOhCJOqGe5TgR0g= +github.com/opencontainers/selinux v1.3.0/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs= +github.com/openshift/api v3.9.1-0.20190810003144-27fb16909b15+incompatible h1:s55wx8JIG/CKnewev892HifTBrtKzMdvgB3rm4rxC2s= +github.com/openshift/api v3.9.1-0.20190810003144-27fb16909b15+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY= +github.com/openshift/imagebuilder v1.1.0 h1:oT704SkwMEzmIMU/+Uv1Wmvt+p10q3v2WuYMeFI18c4= +github.com/openshift/imagebuilder v1.1.0/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo= +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw= +github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc= github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= -github.com/phayes/permbits v0.0.0-20180830030258-59f2482cd460 h1:B9xJsGjeteSbA5LYAmW9KF9/jQcmrJkmpgVWsqRxc0k= -github.com/phayes/permbits v0.0.0-20180830030258-59f2482cd460/go.mod h1:3uODdxMgOaPYeWU7RzZLxVtJHZ/x1f/iHkBZuKJDzuY= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee h1:P6U24L02WMfj9ymZTxl7CxS73JC99x3ukk+DBkgQGQs= github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee/go.mod h1:3uODdxMgOaPYeWU7RzZLxVtJHZ/x1f/iHkBZuKJDzuY= +github.com/pkg/errors v0.0.0-20190227000051-27936f6d90f9/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.3.0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7 h1:gGBSHPOU7g8YjTbhwn+lvFm2VDEhhA+PwDIlstkgSxE= +github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/seccomp/containers-golang v0.0.0-20180629143253-cdfdaa7543f4/go.mod h1:f/98/SnvAzhAEFQJ3u836FePXvcbE8BS0YGMQNn4mhA= +github.com/seccomp/containers-golang v0.0.0-20190312124753-8ca8945ccf5f h1:OtU/w6sBKmXYaw2KEODxjcYi3oPSyyslhgGFgIJVGAI= +github.com/seccomp/containers-golang v0.0.0-20190312124753-8ca8945ccf5f/go.mod h1:f/98/SnvAzhAEFQJ3u836FePXvcbE8BS0YGMQNn4mhA= +github.com/seccomp/libseccomp-golang v0.9.0/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo= +github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/sirupsen/logrus v0.0.0-20190403091019-9b3cdde74fbe/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sourcegraph/go-diff v0.5.1 h1:gO6i5zugwzo1RVTvgvfwCOSVegNuvnNi6bAD1QCmkHs= github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg= github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= @@ -304,136 +509,268 @@ github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc= -github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= -github.com/spf13/viper v1.2.1 h1:bIcUwXqLseLF3BDAZduuNfekWG87ibtFxi59Bq+oI9M= -github.com/spf13/viper v1.2.1/go.mod h1:P4AexN0a+C9tGAnUFNwDMYYZv3pjFuvmeiMyKRaNVlI= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/timakin/bodyclose v0.0.0-20190407043127-4a873e97b2bb h1:lI9ufgFfvuqRctP9Ny8lDDLbSWCMxBPletcSqrnyFYM= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tchap/go-patricia v2.3.0+incompatible h1:GkY4dP3cEfEASBPPkWd+AmjYxhmDkqO9/zg7R0lSQRs= +github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= github.com/timakin/bodyclose v0.0.0-20190407043127-4a873e97b2bb/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/uber/jaeger-client-go v2.19.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v0.0.0-20190122222657-d036253de8f5/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.5-pre/go.mod h1:FwP/aQVg39TXzItUBMwnWp9T9gPQnXw4Poh4/oBQZ/0= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.5-pre/go.mod h1:tULtS6Gy1AE1yCENaw4Vb//HLH5njI2tfCQDUqRd8fI= +github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8= +github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b/go.mod h1:YHaw8N660ESgMgLOZfLQqT1htFItynAUxMesFBho52s= +github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02dE= +github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g= +github.com/vbauerster/mpb v3.4.0+incompatible h1:mfiiYw87ARaeRW6x5gWwYRUawxaW1tLAD8IceomUCNw= +github.com/vbauerster/mpb v3.4.0+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU= +github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM= +github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f h1:nBX3nTcmxEtHSERBJaIo1Qa26VwRaopnZmfDQUXsF4I= +github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/wagoodman/keybinding v0.0.0-20181213133715-6a824da6df05 h1:YMcRwVDe8DLDZ/vrhuImCfqjjG/+gZs6SF61DDQkL/8= github.com/wagoodman/keybinding v0.0.0-20181213133715-6a824da6df05/go.mod h1:gXFkc2sM2o06uzn5Lgo6Ql76uweGdxNfeAlFyKiHAdk= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.1.0 h1:ngVtJC9TY/lg0AA/1k48FYhBrhRoFlEmWzsehpNAaZg= +github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869 h1:kkXA53yGe04D0adEYJwEVQjeBppL01Exg+fnMjfUraU= -golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a h1:YX8ljsm6wXlHZO+aRz9Exqr0evNhKRNe5K/gi+zKh4U= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190907121410-71b5226ff739 h1:Gc7JIyxvWgD6m+QmVryY0MstDORNYididDGxgZ6Tnpk= golang.org/x/crypto v0.0.0-20190907121410-71b5226ff739/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190622003408-7e034cad6442/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977 h1:actzWV6iWn3GLqN8dZjzsB+CLt+gaV2+wsxroxiQI8I= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180824143301-4910a1d54f87/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116161606-93218def8b18 h1:Wh+XCfg3kNpjhdq2LXrsiOProjtQZKme5XUx7VcxwAw= -golang.org/x/sys v0.0.0-20181116161606-93218def8b18/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc= +golang.org/x/sys v0.0.0-20190310054646-10058d7d4faa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190425145619-16072639606e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03 h1:b3JiLYVaG9kHjTcOQIoUh978YMCO7oVTQQBLudU47zY= golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180810170437-e96c4e24768d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190121143147-24cd39ecf745/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd h1:7E3PabyysDSEjnaANKBgums/hyvMI/HoHQ50qZEzTrg= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601 h1:9VBRTdmgQxbs6HE0sUnMrSWNePppAJU07NYvX5dIB04= +google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v0.0.0-20190624233834-05ebafbffc79/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90= +gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A= +k8s.io/api v0.0.0-20190813020757-36bff7324fb7 h1:4uJOjRn9kWq4AqJRE8+qzmAy+lJd9rh8TY455dNef4U= +k8s.io/api v0.0.0-20190813020757-36bff7324fb7/go.mod h1:3Iy+myeAORNCLgjd/Xu9ebwN7Vh59Bw0vh9jhoX+V58= +k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA= +k8s.io/apimachinery v0.0.0-20190809020650-423f5d784010 h1:pyoq062NftC1y/OcnbSvgolyZDJ8y4fmUPWMkdA6gfU= +k8s.io/apimachinery v0.0.0-20190809020650-423f5d784010/go.mod h1:Waf/xTS2FGRrgXCkO5FP3XxTOWh0qLf2QhL1qFZZ/R8= +k8s.io/client-go v0.0.0-20181219152756-3dd551c0f083/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= +k8s.io/client-go v0.0.0-20190620085101-78d2af792bab h1:E8Fecph0qbNsAbijJJQryKu4Oi9QTp5cVpjTE+nqg6g= +k8s.io/client-go v0.0.0-20190620085101-78d2af792bab/go.mod h1:E95RaSlHr79aHaX0aGSwcPNfygDiPKOVXdmivCIZT0k= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20190327210449-e17681d19d3a/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.3 h1:niceAagH1tzskmaie/icWd7ci1wbG7Bf2c6YGcQv+3c= +k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc= +k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4= +k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= +k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a h1:2jUDc9gJja832Ftp+QbDV0tVhQHMISFn01els+2ZAcw= +k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unparam v0.0.0-20190124213536-fbb59629db34 h1:B1LAOfRqg2QUyCdzfjf46quTSYUTAK5OCwbh6pljHbM= -mvdan.cc/unparam v0.0.0-20190124213536-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY= mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4 h1:JPJh2pk3+X4lXAkZIk2RuE/7/FoK9maXw+TNPJhVS/c= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/runtime/options.go b/runtime/options.go index 37620c9..a2b124e 100644 --- a/runtime/options.go +++ b/runtime/options.go @@ -2,11 +2,13 @@ package runtime import ( "github.com/spf13/viper" + "github.com/wagoodman/dive/dive" ) type Options struct { Ci bool ImageId string + Engine dive.Engine ExportFile string CiConfig *viper.Viper BuildArgs []string diff --git a/runtime/run.go b/runtime/run.go index 5eca880..c42ee96 100644 --- a/runtime/run.go +++ b/runtime/run.go @@ -5,9 +5,6 @@ import ( "github.com/wagoodman/dive/dive" "github.com/wagoodman/dive/runtime/ci" "github.com/wagoodman/dive/runtime/export" - "io/ioutil" - "log" - "os" "time" "github.com/dustin/go-humanize" @@ -33,55 +30,39 @@ func runCi(analysis *image.AnalysisResult, options Options) { utils.Exit(1) } -func runBuild(buildArgs []string) string { - iidfile, err := ioutil.TempFile("/tmp", "dive.*.iid") - if err != nil { - utils.Cleanup() - log.Fatal(err) - } - defer os.Remove(iidfile.Name()) - - allArgs := append([]string{"--iidfile", iidfile.Name()}, buildArgs...) - err = utils.RunDockerCmd("build", allArgs...) - if err != nil { - utils.Cleanup() - log.Fatal(err) - } - - imageId, err := ioutil.ReadFile(iidfile.Name()) - if err != nil { - utils.Cleanup() - log.Fatal(err) - } - - return string(imageId) -} - func Run(options Options) { + var err error doExport := options.ExportFile != "" doBuild := len(options.BuildArgs) > 0 - if doBuild { - fmt.Println(utils.TitleFormat("Building image...")) - options.ImageId = runBuild(options.BuildArgs) + // if an image option was provided, parse it and determine the container image... + // otherwise, use the configs default value. + + // if build is given, get the handler based off of either the explicit runtime + + img, err := dive.GetImageHandler(options.Engine) + if err != nil { + fmt.Printf("cannot determine image provider: %v\n", err) + utils.Exit(1) } - analyzer := dive.GetAnalyzer(options.ImageId) + if doBuild { + fmt.Println(utils.TitleFormat("Building image...")) + options.ImageId, err = img.Build(options.BuildArgs) + if err != nil { + fmt.Printf("cannot build image: %v\n", err) + utils.Exit(1) + } + } - fmt.Println(utils.TitleFormat("Fetching image...") + " (this can take a while with large images)") - reader, err := analyzer.Fetch() + err = img.Get(options.ImageId) if err != nil { fmt.Printf("cannot fetch image: %v\n", err) utils.Exit(1) } - defer reader.Close() - fmt.Println(utils.TitleFormat("Parsing image...")) - err = analyzer.Parse(reader) - if err != nil { - fmt.Printf("cannot parse image: %v\n", err) - utils.Exit(1) - } + // todo, cleanup on error + // todo: image get shold return error for cleanup? if doExport { fmt.Println(utils.TitleFormat(fmt.Sprintf("Analyzing image... (export to '%s')", options.ExportFile))) @@ -89,7 +70,7 @@ func Run(options Options) { fmt.Println(utils.TitleFormat("Analyzing image...")) } - result, err := analyzer.Analyze() + result, err := img.Analyze() if err != nil { fmt.Printf("cannot analyze image: %v\n", err) utils.Exit(1) diff --git a/utils/format.go b/utils/format.go index c05b4c4..77a9d59 100644 --- a/utils/format.go +++ b/utils/format.go @@ -2,8 +2,20 @@ package utils import ( "github.com/logrusorgru/aurora" + "strings" ) func TitleFormat(s string) string { return aurora.Bold(s).String() } + +// CleanArgs trims the whitespace from the given set of strings. +func CleanArgs(s []string) []string { + var r []string + for _, str := range s { + if str != "" { + r = append(r, strings.Trim(str, " ")) + } + } + return r +} From d2c661eaf7e321aa992396552f54dc8c3db358a8 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 3 Oct 2019 09:42:27 -0400 Subject: [PATCH 03/23] leveraging docker export for podman --- dive/get_image_handler.go | 6 +- .../docker/{image_config.go => config.go} | 6 +- dive/image/docker/handler.go | 113 ++++++++ dive/image/docker/image.go | 232 ++++++--------- dive/image/docker/layer.go | 13 - .../docker/{image_manifest.go => manifest.go} | 6 +- dive/image/docker/testing.go | 10 +- dive/image/{image.go => handler.go} | 2 +- dive/image/podman/handler.go | 89 ++++++ dive/image/podman/image.go | 269 ------------------ 10 files changed, 295 insertions(+), 451 deletions(-) rename dive/image/docker/{image_config.go => config.go} (84%) create mode 100644 dive/image/docker/handler.go rename dive/image/docker/{image_manifest.go => manifest.go} (71%) rename dive/image/{image.go => handler.go} (59%) create mode 100644 dive/image/podman/handler.go delete mode 100644 dive/image/podman/image.go diff --git a/dive/get_image_handler.go b/dive/get_image_handler.go index 834ed11..a3eb5a1 100644 --- a/dive/get_image_handler.go +++ b/dive/get_image_handler.go @@ -32,12 +32,12 @@ func GetEngine(engine string) Engine { } } -func GetImageHandler(engine Engine) (image.Image, error) { +func GetImageHandler(engine Engine) (image.Handler, error) { switch engine { case EngineDocker: - return docker.NewDockerImage(), nil + return docker.NewHandler(), nil case EnginePodman: - return podman.NewPodmanImage(), nil + return podman.NewHandler(), nil } return nil, fmt.Errorf("unable to determine image provider") diff --git a/dive/image/docker/image_config.go b/dive/image/docker/config.go similarity index 84% rename from dive/image/docker/image_config.go rename to dive/image/docker/config.go index 704299a..9d2acb9 100644 --- a/dive/image/docker/image_config.go +++ b/dive/image/docker/config.go @@ -5,7 +5,7 @@ import ( "github.com/sirupsen/logrus" ) -type imageConfig struct { +type config struct { History []imageHistoryEntry `json:"history"` RootFs rootFs `json:"rootfs"` } @@ -15,8 +15,8 @@ type rootFs struct { DiffIds []string `json:"diff_ids"` } -func newDockerImageConfig(configBytes []byte) imageConfig { - var imageConfig imageConfig +func newConfig(configBytes []byte) config { + var imageConfig config err := json.Unmarshal(configBytes, &imageConfig) if err != nil { logrus.Panic(err) diff --git a/dive/image/docker/handler.go b/dive/image/docker/handler.go new file mode 100644 index 0000000..d8b1457 --- /dev/null +++ b/dive/image/docker/handler.go @@ -0,0 +1,113 @@ +package docker + +import ( + "fmt" + "github.com/wagoodman/dive/dive/image" + "io" + "net/http" + "os" + "strings" + + "github.com/docker/cli/cli/connhelper" + "github.com/docker/docker/client" + "golang.org/x/net/context" +) + +var dockerVersion string + +type handler struct { + id string + client *client.Client + image Image +} + +func NewHandler() *handler { + return &handler{} +} + +func (handler *handler) Get(id string) error { + handler.id = id + + reader, err := handler.fetchArchive() + if err != nil { + return err + } + defer reader.Close() + + img, err := NewImageFromArchive(reader) + if err != nil { + return err + } + handler.image = img + + return nil +} + +func (handler *handler) Build(args []string) (string, error) { + var err error + handler.id, err = buildImageFromCli(args) + return handler.id, err +} + +func (handler *handler) fetchArchive() (io.ReadCloser, error) { + var err error + + // pull the handler if it does not exist + ctx := context.Background() + + host := os.Getenv("DOCKER_HOST") + var clientOpts []client.Opt + + switch strings.Split(host, ":")[0] { + case "ssh": + helper, err := connhelper.GetConnectionHelper(host) + if err != nil { + fmt.Println("docker host", err) + } + clientOpts = append(clientOpts, func(c *client.Client) error { + httpClient := &http.Client{ + Transport: &http.Transport{ + DialContext: helper.Dialer, + }, + } + return client.WithHTTPClient(httpClient)(c) + }) + clientOpts = append(clientOpts, client.WithHost(helper.Host)) + clientOpts = append(clientOpts, client.WithDialContext(helper.Dialer)) + + default: + + if os.Getenv("DOCKER_TLS_VERIFY") != "" && os.Getenv("DOCKER_CERT_PATH") == "" { + os.Setenv("DOCKER_CERT_PATH", "~/.docker") + } + + clientOpts = append(clientOpts, client.FromEnv) + } + + clientOpts = append(clientOpts, client.WithVersion(dockerVersion)) + handler.client, err = client.NewClientWithOpts(clientOpts...) + if err != nil { + return nil, err + } + _, _, err = handler.client.ImageInspectWithRaw(ctx, handler.id) + if err != nil { + // don't use the API, the CLI has more informative output + fmt.Println("Handler not available locally. Trying to pull '" + handler.id + "'...") + err = runDockerCmd("pull", handler.id) + if err != nil { + return nil, err + } + } + + readCloser, err := handler.client.ImageSave(ctx, []string{handler.id}) + if err != nil { + return nil, err + } + + return readCloser, nil +} + + +func (handler *handler) Analyze() (*image.AnalysisResult, error) { + return handler.image.Analyze() +} diff --git a/dive/image/docker/image.go b/dive/image/docker/image.go index b346bd8..7523c04 100644 --- a/dive/image/docker/image.go +++ b/dive/image/docker/image.go @@ -3,116 +3,28 @@ package docker import ( "archive/tar" "fmt" + "github.com/wagoodman/dive/dive/filetree" "github.com/wagoodman/dive/dive/image" + "github.com/wagoodman/dive/utils" "io" "io/ioutil" - "net/http" - "os" "strings" - - "github.com/docker/cli/cli/connhelper" - "github.com/docker/docker/client" - "github.com/wagoodman/dive/dive/filetree" - "github.com/wagoodman/dive/utils" - "golang.org/x/net/context" ) -var dockerVersion string - -type dockerImage struct { - id string - client *client.Client +type Image struct { jsonFiles map[string][]byte trees []*filetree.FileTree layerMap map[string]*filetree.FileTree layers []*dockerLayer } -func NewDockerImage() *dockerImage { - return &dockerImage{ +func NewImageFromArchive(tarFile io.ReadCloser) (Image, error) { + img := Image{ // store discovered json files in a map so we can read the image in one pass jsonFiles: make(map[string][]byte), layerMap: make(map[string]*filetree.FileTree), } -} -func (img *dockerImage) Get(id string) error { - img.id = id - - reader, err := img.fetch() - if err != nil { - return err - } - defer reader.Close() - - return img.parse(reader) -} - -func (img *dockerImage) Build(args []string) (string, error) { - var err error - img.id, err = buildImageFromCli(args) - return img.id, err -} - -func (img *dockerImage) fetch() (io.ReadCloser, error) { - var err error - - // pull the img if it does not exist - ctx := context.Background() - - host := os.Getenv("DOCKER_HOST") - var clientOpts []client.Opt - - switch strings.Split(host, ":")[0] { - case "ssh": - helper, err := connhelper.GetConnectionHelper(host) - if err != nil { - fmt.Println("docker host", err) - } - clientOpts = append(clientOpts, func(c *client.Client) error { - httpClient := &http.Client{ - Transport: &http.Transport{ - DialContext: helper.Dialer, - }, - } - return client.WithHTTPClient(httpClient)(c) - }) - clientOpts = append(clientOpts, client.WithHost(helper.Host)) - clientOpts = append(clientOpts, client.WithDialContext(helper.Dialer)) - - default: - - if os.Getenv("DOCKER_TLS_VERIFY") != "" && os.Getenv("DOCKER_CERT_PATH") == "" { - os.Setenv("DOCKER_CERT_PATH", "~/.docker") - } - - clientOpts = append(clientOpts, client.FromEnv) - } - - clientOpts = append(clientOpts, client.WithVersion(dockerVersion)) - img.client, err = client.NewClientWithOpts(clientOpts...) - if err != nil { - return nil, err - } - _, _, err = img.client.ImageInspectWithRaw(ctx, img.id) - if err != nil { - // don't use the API, the CLI has more informative output - fmt.Println("Image not available locally. Trying to pull '" + img.id + "'...") - err = runDockerCmd("pull", img.id) - if err != nil { - return nil, err - } - } - - readCloser, err := img.client.ImageSave(ctx, []string{img.id}) - if err != nil { - return nil, err - } - - return readCloser, nil -} - -func (img *dockerImage) parse(tarFile io.ReadCloser) error { tarReader := tar.NewReader(tarFile) var currentLayer uint @@ -136,43 +48,107 @@ func (img *dockerImage) parse(tarFile io.ReadCloser) error { if strings.HasSuffix(name, "layer.tar") { currentLayer++ if err != nil { - return err + return img, err } layerReader := tar.NewReader(tarReader) - err := img.processLayerTar(name, currentLayer, layerReader) + tree, err := processLayerTar(name, layerReader) + if err != nil { - return err + return img, err } + + // add the layer to the image + img.layerMap[tree.Name] = tree + } else if strings.HasSuffix(name, ".json") { fileBuffer, err := ioutil.ReadAll(tarReader) if err != nil { - return err + return img, err } img.jsonFiles[name] = fileBuffer } } } - return nil + return img, nil } -func (img *dockerImage) Analyze() (*image.AnalysisResult, error) { +func processLayerTar(name string, reader *tar.Reader) (*filetree.FileTree, error) { + tree := filetree.NewFileTree() + tree.Name = pathToLayerId(name) + + fileInfos, err := getFileList(reader) + if err != nil { + return nil, err + } + + for _, element := range fileInfos { + tree.FileSize += uint64(element.Size) + + _, _, err := tree.AddPath(element.Path, element) + if err != nil { + return nil, err + } + } + + + return tree, nil +} + +func pathToLayerId(name string) string { + return strings.TrimSuffix(strings.TrimSuffix(name, ".tar"), "/layer") +} + +func getFileList(tarReader *tar.Reader) ([]filetree.FileInfo, error) { + var files []filetree.FileInfo + + for { + header, err := tarReader.Next() + if err == io.EOF { + break + } else if err != nil { + fmt.Println(err) + utils.Exit(1) + } + + name := header.Name + + switch header.Typeflag { + case tar.TypeXGlobalHeader: + return nil, fmt.Errorf("unexptected tar file: (XGlobalHeader): type=%v name=%s", header.Typeflag, name) + case tar.TypeXHeader: + return nil, fmt.Errorf("unexptected tar file (XHeader): type=%v name=%s", header.Typeflag, name) + default: + files = append(files, filetree.NewFileInfo(tarReader, header, name)) + } + } + return files, nil +} + +func (img *Image) Analyze() (*image.AnalysisResult, error) { + img.trees = make([]*filetree.FileTree, 0) - manifest := newDockerImageManifest(img.jsonFiles["manifest.json"]) - config := newDockerImageConfig(img.jsonFiles[manifest.ConfigPath]) + manifest := newManifest(img.jsonFiles["manifest.json"]) + config := newConfig(img.jsonFiles[manifest.ConfigPath]) // build the content tree for _, treeName := range manifest.LayerTarPaths { - img.trees = append(img.trees, img.layerMap[treeName]) + key := pathToLayerId(treeName) + tr, exists := img.layerMap[key] + if exists { + img.trees = append(img.trees, tr) + continue + } + return nil, fmt.Errorf("could not find '%s' in parsed layers", treeName) } // build the layers array img.layers = make([]*dockerLayer, len(img.trees)) - // note that the img config stores images in reverse chronological order, so iterate backwards through layers + // note that the handler config stores images in reverse chronological order, so iterate backwards through layers // as you iterate chronologically through history (ignoring history items that have no layer contents) - // Note: history is not required metadata in a docker img! + // Note: history is not required metadata in a docker image! tarPathIdx := 0 histIdx := 0 for layerIdx := len(img.trees) - 1; layerIdx >= 0; layerIdx-- { @@ -234,51 +210,3 @@ func (img *dockerImage) Analyze() (*image.AnalysisResult, error) { Inefficiencies: inefficiencies, }, nil } - -func (img *dockerImage) processLayerTar(name string, layerIdx uint, reader *tar.Reader) error { - tree := filetree.NewFileTree() - tree.Name = name - - fileInfos, err := img.getFileList(reader) - if err != nil { - return err - } - - for _, element := range fileInfos { - tree.FileSize += uint64(element.Size) - - _, _, err := tree.AddPath(element.Path, element) - if err != nil { - return err - } - } - - img.layerMap[tree.Name] = tree - return nil -} - -func (img *dockerImage) getFileList(tarReader *tar.Reader) ([]filetree.FileInfo, error) { - var files []filetree.FileInfo - - for { - header, err := tarReader.Next() - if err == io.EOF { - break - } else if err != nil { - fmt.Println(err) - utils.Exit(1) - } - - name := header.Name - - switch header.Typeflag { - case tar.TypeXGlobalHeader: - return nil, fmt.Errorf("unexptected tar file: (XGlobalHeader): type=%v name=%s", header.Typeflag, name) - case tar.TypeXHeader: - return nil, fmt.Errorf("unexptected tar file (XHeader): type=%v name=%s", header.Typeflag, name) - default: - files = append(files, filetree.NewFileInfo(tarReader, header, name)) - } - } - return files, nil -} diff --git a/dive/image/docker/layer.go b/dive/image/docker/layer.go index 08ff582..9997d3c 100644 --- a/dive/image/docker/layer.go +++ b/dive/image/docker/layer.go @@ -65,31 +65,18 @@ func (layer *dockerLayer) ShortId() string { } id = id[0:rangeBound] - // show the tagged image as the last layer - // if len(layer.History.Tags) > 0 { - // id = "[" + strings.Join(layer.History.Tags, ",") + "]" - // } - return id } -func (layer *dockerLayer) StringFormat() string { - return image.LayerFormat -} - // String represents a layer in a columnar format. func (layer *dockerLayer) String() string { if layer.index == 0 { return fmt.Sprintf(image.LayerFormat, - // layer.ShortId(), - // fmt.Sprintf("%d",layer.Index()), humanize.Bytes(layer.Size()), "FROM "+layer.ShortId()) } return fmt.Sprintf(image.LayerFormat, - // layer.ShortId(), - // fmt.Sprintf("%d",layer.Index()), humanize.Bytes(layer.Size()), layer.Command()) } diff --git a/dive/image/docker/image_manifest.go b/dive/image/docker/manifest.go similarity index 71% rename from dive/image/docker/image_manifest.go rename to dive/image/docker/manifest.go index 084dc72..b08ee05 100644 --- a/dive/image/docker/image_manifest.go +++ b/dive/image/docker/manifest.go @@ -5,14 +5,14 @@ import ( "github.com/sirupsen/logrus" ) -type imageManifest struct { +type manifest struct { ConfigPath string `json:"Config"` RepoTags []string `json:"RepoTags"` LayerTarPaths []string `json:"Layers"` } -func newDockerImageManifest(manifestBytes []byte) imageManifest { - var manifest []imageManifest +func newManifest(manifestBytes []byte) manifest { + var manifest []manifest err := json.Unmarshal(manifestBytes, &manifest) if err != nil { logrus.Panic(err) diff --git a/dive/image/docker/testing.go b/dive/image/docker/testing.go index ed7c6b2..48e465a 100644 --- a/dive/image/docker/testing.go +++ b/dive/image/docker/testing.go @@ -12,15 +12,11 @@ func TestLoadDockerImageTar(tarPath string) (*image.AnalysisResult, error) { } defer f.Close() - img := NewDockerImage() - err = img.Get("dive-test:latest") + handler := NewHandler() + err = handler.Get("dive-test:latest") if err != nil { return nil, err } - err = img.parse(f) - if err != nil { - return nil, err - } - return img.Analyze() + return handler.Analyze() } diff --git a/dive/image/image.go b/dive/image/handler.go similarity index 59% rename from dive/image/image.go rename to dive/image/handler.go index 73d802e..924f5f3 100644 --- a/dive/image/image.go +++ b/dive/image/handler.go @@ -1,6 +1,6 @@ package image -type Image interface { +type Handler interface { Resolver Analyzer } diff --git a/dive/image/podman/handler.go b/dive/image/podman/handler.go new file mode 100644 index 0000000..719f0ba --- /dev/null +++ b/dive/image/podman/handler.go @@ -0,0 +1,89 @@ +package podman + +import ( + "bufio" + "context" + "fmt" + "github.com/containers/libpod/libpod" + "github.com/wagoodman/dive/dive/image" + "github.com/wagoodman/dive/dive/image/docker" + "io/ioutil" + "os" +) + +type handler struct { + id string + // note: podman supports saving docker formatted archives, we're leveraging this here + // todo: add oci parser and image/layer objects + image docker.Image +} + +func NewHandler() *handler { + return &handler{} +} + +func (handler *handler) Get(id string) error { + handler.id = id + + path, err := handler.fetchArchive() + if err != nil { + return err + } + defer os.Remove(path) + + file, err := os.Open(path) + + // we use podman to extract a docker-formatted image + img, err := docker.NewImageFromArchive(ioutil.NopCloser(bufio.NewReader(file))) + if err != nil { + return err + } + + handler.image = img + return nil +} + +func (handler *handler) Build(args []string) (string, error) { + var err error + handler.id, err = buildImageFromCli(args) + return handler.id, err +} + +func (handler *handler) fetchArchive() (string, error) { + var err error + var ctx = context.Background() + + runtime, err := libpod.NewRuntime(ctx) + if err != nil { + return "", err + } + + images, err := runtime.ImageRuntime().GetImages() + if err != nil { + return "", err + } + + for _, item:= range images { + for _, name := range item.Names() { + if name == handler.id { + file, err := ioutil.TempFile(os.TempDir(), "dive-handler-tar") + if err != nil { + return "", err + } + + err = item.Save(ctx, "dive-export", "docker-archive", file.Name(), []string{}, false, false) + if err != nil { + return "", err + } + + return file.Name(), nil + } + } + } + + return "", fmt.Errorf("image could not be found") +} + +func (handler *handler) Analyze() (*image.AnalysisResult, error) { + return handler.image.Analyze() +} diff --git a/dive/image/podman/image.go b/dive/image/podman/image.go deleted file mode 100644 index 4769dd0..0000000 --- a/dive/image/podman/image.go +++ /dev/null @@ -1,269 +0,0 @@ -package podman - -import ( - "context" - "fmt" - "github.com/containers/libpod/libpod" - "os" - - // "github.com/containers/libpod/libpod" - // libpodImage "github.com/containers/libpod/libpod/image" - // "github.com/containers/storage" - "github.com/wagoodman/dive/dive/filetree" - "github.com/wagoodman/dive/dive/image" - "io" -) - -type podmanImage struct { - id string - jsonFiles map[string][]byte - trees []*filetree.FileTree - layerMap map[string]*filetree.FileTree - // layers []*podmanLayer -} - -func NewPodmanImage() *podmanImage { - return &podmanImage{ - // store discovered json files in a map so we can read the image in one pass - jsonFiles: make(map[string][]byte), - layerMap: make(map[string]*filetree.FileTree), - } -} - -func (img *podmanImage) Get(id string) error { - img.id = id - - reader, err := img.fetch() - if err != nil { - return err - } - defer reader.Close() - - return img.parse(reader) -} - -func (img *podmanImage) Build(args []string) (string, error) { - var err error - img.id, err = buildImageFromCli(args) - return img.id, err -} - -func (img *podmanImage) fetch() (io.ReadCloser, error) { - var err error - - runtime, err := libpod.NewRuntime(context.TODO()) - if err != nil { - return nil, err - } - - images, err := runtime.ImageRuntime().GetImages() - if err != nil { - return nil, err - } - - // cfg, _ := runtime.GetConfig() - // cfg.StorageConfig.GraphRoot - - for _, item:= range images { - for _, name := range item.Names() { - if name == img.id { - fmt.Println("Found it!") - - curImg := item - for { - h, _ := curImg.History(context.TODO()) - fmt.Printf("%+v %+v %+v\n", curImg.ID(), h[0].Size, h[0].CreatedBy) - x, _ := curImg.DriverData() - fmt.Printf(" %+v\n", x) - // for _, i := range x { - // fmt.Printf(" %+v\n", i) - // } - - curImg, err = curImg.GetParent(context.TODO()) - if err != nil || curImg == nil { - break - } - } - - } - } - } - - os.Exit(0) - - return nil, err -} - -func (img *podmanImage) parse(tarFile io.ReadCloser) error { - // tarReader := tar.NewReader(tarFile) - // - // var currentLayer uint - // for { - // header, err := tarReader.Next() - // - // if err == io.EOF { - // break - // } - // - // if err != nil { - // fmt.Println(err) - // utils.Exit(1) - // } - // - // name := header.Name - // - // // some layer tars can be relative layer symlinks to other layer tars - // if header.Typeflag == tar.TypeSymlink || header.Typeflag == tar.TypeReg { - // - // if strings.HasSuffix(name, "layer.tar") { - // currentLayer++ - // if err != nil { - // return err - // } - // layerReader := tar.NewReader(tarReader) - // err := img.processLayerTar(name, currentLayer, layerReader) - // if err != nil { - // return err - // } - // } else if strings.HasSuffix(name, ".json") { - // fileBuffer, err := ioutil.ReadAll(tarReader) - // if err != nil { - // return err - // } - // img.jsonFiles[name] = fileBuffer - // } - // } - // } - // - // return nil - return nil -} - -func (img *podmanImage) Analyze() (*image.AnalysisResult, error) { - // img.trees = make([]*filetree.FileTree, 0) - // - // manifest := newDockerImageManifest(img.jsonFiles["manifest.json"]) - // config := newDockerImageConfig(img.jsonFiles[manifest.ConfigPath]) - // - // // build the content tree - // for _, treeName := range manifest.LayerTarPaths { - // img.trees = append(img.trees, img.layerMap[treeName]) - // } - // - // // build the layers array - // img.layers = make([]*dockerLayer, len(img.trees)) - // - // // note that the img config stores images in reverse chronological order, so iterate backwards through layers - // // as you iterate chronologically through history (ignoring history items that have no layer contents) - // // Note: history is not required metadata in a docker img! - // tarPathIdx := 0 - // histIdx := 0 - // for layerIdx := len(img.trees) - 1; layerIdx >= 0; layerIdx-- { - // - // tree := img.trees[(len(img.trees)-1)-layerIdx] - // - // // ignore empty layers, we are only observing layers with content - // historyObj := imageHistoryEntry{ - // CreatedBy: "(missing)", - // } - // for nextHistIdx := histIdx; nextHistIdx < len(config.History); nextHistIdx++ { - // if !config.History[nextHistIdx].EmptyLayer { - // histIdx = nextHistIdx - // break - // } - // } - // if histIdx < len(config.History) && !config.History[histIdx].EmptyLayer { - // historyObj = config.History[histIdx] - // histIdx++ - // } - // - // img.layers[layerIdx] = &dockerLayer{ - // history: historyObj, - // index: tarPathIdx, - // tree: img.trees[layerIdx], - // tarPath: manifest.LayerTarPaths[tarPathIdx], - // } - // img.layers[layerIdx].history.Size = tree.FileSize - // - // tarPathIdx++ - // } - // - // efficiency, inefficiencies := filetree.Efficiency(img.trees) - // - // var sizeBytes, userSizeBytes uint64 - // layers := make([]image.Layer, len(img.layers)) - // for i, v := range img.layers { - // layers[i] = v - // sizeBytes += v.Size() - // if i != 0 { - // userSizeBytes += v.Size() - // } - // } - // - // var wastedBytes uint64 - // for idx := 0; idx < len(inefficiencies); idx++ { - // fileData := inefficiencies[len(inefficiencies)-1-idx] - // wastedBytes += uint64(fileData.CumulativeSize) - // } - // - // return &image.AnalysisResult{ - // Layers: layers, - // RefTrees: img.trees, - // Efficiency: efficiency, - // UserSizeByes: userSizeBytes, - // SizeBytes: sizeBytes, - // WastedBytes: wastedBytes, - // WastedUserPercent: float64(wastedBytes) / float64(userSizeBytes), - // Inefficiencies: inefficiencies, - // }, nil - - return nil, nil -} - -// func (img *podmanImage) processLayerTar(name string, layerIdx uint, reader *tar.Reader) error { -// tree := filetree.NewFileTree() -// tree.Name = name -// -// fileInfos, err := img.getFileList(reader) -// if err != nil { -// return err -// } -// -// for _, element := range fileInfos { -// tree.FileSize += uint64(element.Size) -// -// _, _, err := tree.AddPath(element.Path, element) -// if err != nil { -// return err -// } -// } -// -// img.layerMap[tree.Name] = tree -// return nil -// } -// -// func (img *podmanImage) getFileList(tarReader *tar.Reader) ([]filetree.FileInfo, error) { -// var files []filetree.FileInfo -// -// for { -// header, err := tarReader.Next() -// if err == io.EOF { -// break -// } else if err != nil { -// fmt.Println(err) -// utils.Exit(1) -// } -// -// name := header.Name -// -// switch header.Typeflag { -// case tar.TypeXGlobalHeader: -// return nil, fmt.Errorf("unexptected tar file: (XGlobalHeader): type=%v name=%s", header.Typeflag, name) -// case tar.TypeXHeader: -// return nil, fmt.Errorf("unexptected tar file (XHeader): type=%v name=%s", header.Typeflag, name) -// default: -// files = append(files, filetree.NewFileInfo(tarReader, header, name)) -// } -// } -// return files, nil -// } From 50d776e84592b01d65732c9f17db4d9f30a115e7 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 3 Oct 2019 10:47:38 -0400 Subject: [PATCH 04/23] rework resolver interface --- dive/get_image_handler.go | 6 +- dive/image/docker/image.go | 56 +++++++++++-------- dive/image/docker/{handler.go => resolver.go} | 45 +++++++-------- dive/image/docker/testing.go | 6 +- dive/image/handler.go | 6 -- dive/image/podman/{handler.go => resolver.go} | 28 ++++------ dive/image/resolver.go | 2 +- runtime/run.go | 8 +-- 8 files changed, 74 insertions(+), 83 deletions(-) rename dive/image/docker/{handler.go => resolver.go} (65%) delete mode 100644 dive/image/handler.go rename dive/image/podman/{handler.go => resolver.go} (71%) diff --git a/dive/get_image_handler.go b/dive/get_image_handler.go index a3eb5a1..d8e5cec 100644 --- a/dive/get_image_handler.go +++ b/dive/get_image_handler.go @@ -32,12 +32,12 @@ func GetEngine(engine string) Engine { } } -func GetImageHandler(engine Engine) (image.Handler, error) { +func GetImageHandler(engine Engine) (image.Resolver, error) { switch engine { case EngineDocker: - return docker.NewHandler(), nil + return docker.NewResolver(), nil case EnginePodman: - return podman.NewHandler(), nil + return podman.NewResolver(), nil } return nil, fmt.Errorf("unable to determine image provider") diff --git a/dive/image/docker/image.go b/dive/image/docker/image.go index 7523c04..f2b8cbd 100644 --- a/dive/image/docker/image.go +++ b/dive/image/docker/image.go @@ -12,21 +12,23 @@ import ( ) type Image struct { - jsonFiles map[string][]byte + manifest manifest + config config trees []*filetree.FileTree layerMap map[string]*filetree.FileTree layers []*dockerLayer } -func NewImageFromArchive(tarFile io.ReadCloser) (Image, error) { - img := Image{ - // store discovered json files in a map so we can read the image in one pass - jsonFiles: make(map[string][]byte), +func NewImageFromArchive(tarFile io.ReadCloser) (*Image, error) { + img := &Image{ layerMap: make(map[string]*filetree.FileTree), } tarReader := tar.NewReader(tarFile) + // store discovered json files in a map so we can read the image in one pass + jsonFiles := make(map[string][]byte) + var currentLayer uint for { header, err := tarReader.Next() @@ -45,7 +47,7 @@ func NewImageFromArchive(tarFile io.ReadCloser) (Image, error) { // some layer tars can be relative layer symlinks to other layer tars if header.Typeflag == tar.TypeSymlink || header.Typeflag == tar.TypeReg { - if strings.HasSuffix(name, "layer.tar") { + if strings.HasSuffix(name, ".tar") { currentLayer++ if err != nil { return img, err @@ -65,17 +67,31 @@ func NewImageFromArchive(tarFile io.ReadCloser) (Image, error) { if err != nil { return img, err } - img.jsonFiles[name] = fileBuffer + jsonFiles[name] = fileBuffer } } } + manifestContent, exists := jsonFiles["manifest.json"] + if !exists { + return img, fmt.Errorf("could not find image manifest") + } + + img.manifest = newManifest(manifestContent) + + configContent, exists := jsonFiles[img.manifest.ConfigPath] + if !exists { + return img, fmt.Errorf("could not find image config") + } + + img.config = newConfig(configContent) + return img, nil } func processLayerTar(name string, reader *tar.Reader) (*filetree.FileTree, error) { tree := filetree.NewFileTree() - tree.Name = pathToLayerId(name) + tree.Name = name fileInfos, err := getFileList(reader) if err != nil { @@ -91,13 +107,9 @@ func processLayerTar(name string, reader *tar.Reader) (*filetree.FileTree, error } } - return tree, nil } -func pathToLayerId(name string) string { - return strings.TrimSuffix(strings.TrimSuffix(name, ".tar"), "/layer") -} func getFileList(tarReader *tar.Reader) ([]filetree.FileInfo, error) { var files []filetree.FileInfo @@ -129,13 +141,9 @@ func (img *Image) Analyze() (*image.AnalysisResult, error) { img.trees = make([]*filetree.FileTree, 0) - manifest := newManifest(img.jsonFiles["manifest.json"]) - config := newConfig(img.jsonFiles[manifest.ConfigPath]) - // build the content tree - for _, treeName := range manifest.LayerTarPaths { - key := pathToLayerId(treeName) - tr, exists := img.layerMap[key] + for _, treeName := range img.manifest.LayerTarPaths { + tr, exists := img.layerMap[treeName] if exists { img.trees = append(img.trees, tr) continue @@ -146,7 +154,7 @@ func (img *Image) Analyze() (*image.AnalysisResult, error) { // build the layers array img.layers = make([]*dockerLayer, len(img.trees)) - // note that the handler config stores images in reverse chronological order, so iterate backwards through layers + // note that the resolver config stores images in reverse chronological order, so iterate backwards through layers // as you iterate chronologically through history (ignoring history items that have no layer contents) // Note: history is not required metadata in a docker image! tarPathIdx := 0 @@ -159,14 +167,14 @@ func (img *Image) Analyze() (*image.AnalysisResult, error) { historyObj := imageHistoryEntry{ CreatedBy: "(missing)", } - for nextHistIdx := histIdx; nextHistIdx < len(config.History); nextHistIdx++ { - if !config.History[nextHistIdx].EmptyLayer { + for nextHistIdx := histIdx; nextHistIdx < len(img.config.History); nextHistIdx++ { + if !img.config.History[nextHistIdx].EmptyLayer { histIdx = nextHistIdx break } } - if histIdx < len(config.History) && !config.History[histIdx].EmptyLayer { - historyObj = config.History[histIdx] + if histIdx < len(img.config.History) && !img.config.History[histIdx].EmptyLayer { + historyObj = img.config.History[histIdx] histIdx++ } @@ -174,7 +182,7 @@ func (img *Image) Analyze() (*image.AnalysisResult, error) { history: historyObj, index: tarPathIdx, tree: img.trees[layerIdx], - tarPath: manifest.LayerTarPaths[tarPathIdx], + tarPath: img.manifest.LayerTarPaths[tarPathIdx], } img.layers[layerIdx].history.Size = tree.FileSize diff --git a/dive/image/docker/handler.go b/dive/image/docker/resolver.go similarity index 65% rename from dive/image/docker/handler.go rename to dive/image/docker/resolver.go index d8b1457..5d0d3d9 100644 --- a/dive/image/docker/handler.go +++ b/dive/image/docker/resolver.go @@ -15,44 +15,41 @@ import ( var dockerVersion string -type handler struct { +type resolver struct { id string client *client.Client - image Image } -func NewHandler() *handler { - return &handler{} +func NewResolver() *resolver { + return &resolver{} } -func (handler *handler) Get(id string) error { - handler.id = id +func (r *resolver) Resolve(id string) (image.Analyzer, error) { + r.id = id - reader, err := handler.fetchArchive() + reader, err := r.fetchArchive() if err != nil { - return err + return nil, err } defer reader.Close() img, err := NewImageFromArchive(reader) if err != nil { - return err + return nil, err } - handler.image = img - - return nil + return img, nil } -func (handler *handler) Build(args []string) (string, error) { +func (r *resolver) Build(args []string) (string, error) { var err error - handler.id, err = buildImageFromCli(args) - return handler.id, err + r.id, err = buildImageFromCli(args) + return r.id, err } -func (handler *handler) fetchArchive() (io.ReadCloser, error) { +func (r *resolver) fetchArchive() (io.ReadCloser, error) { var err error - // pull the handler if it does not exist + // pull the resolver if it does not exist ctx := context.Background() host := os.Getenv("DOCKER_HOST") @@ -85,21 +82,21 @@ func (handler *handler) fetchArchive() (io.ReadCloser, error) { } clientOpts = append(clientOpts, client.WithVersion(dockerVersion)) - handler.client, err = client.NewClientWithOpts(clientOpts...) + r.client, err = client.NewClientWithOpts(clientOpts...) if err != nil { return nil, err } - _, _, err = handler.client.ImageInspectWithRaw(ctx, handler.id) + _, _, err = r.client.ImageInspectWithRaw(ctx, r.id) if err != nil { // don't use the API, the CLI has more informative output - fmt.Println("Handler not available locally. Trying to pull '" + handler.id + "'...") - err = runDockerCmd("pull", handler.id) + fmt.Println("Handler not available locally. Trying to pull '" + r.id + "'...") + err = runDockerCmd("pull", r.id) if err != nil { return nil, err } } - readCloser, err := handler.client.ImageSave(ctx, []string{handler.id}) + readCloser, err := r.client.ImageSave(ctx, []string{r.id}) if err != nil { return nil, err } @@ -107,7 +104,3 @@ func (handler *handler) fetchArchive() (io.ReadCloser, error) { return readCloser, nil } - -func (handler *handler) Analyze() (*image.AnalysisResult, error) { - return handler.image.Analyze() -} diff --git a/dive/image/docker/testing.go b/dive/image/docker/testing.go index 48e465a..0354a29 100644 --- a/dive/image/docker/testing.go +++ b/dive/image/docker/testing.go @@ -12,11 +12,11 @@ func TestLoadDockerImageTar(tarPath string) (*image.AnalysisResult, error) { } defer f.Close() - handler := NewHandler() - err = handler.Get("dive-test:latest") + handler := NewResolver() + img, err := handler.Resolve("dive-test:latest") if err != nil { return nil, err } - return handler.Analyze() + return img.Analyze() } diff --git a/dive/image/handler.go b/dive/image/handler.go deleted file mode 100644 index 924f5f3..0000000 --- a/dive/image/handler.go +++ /dev/null @@ -1,6 +0,0 @@ -package image - -type Handler interface { - Resolver - Analyzer -} diff --git a/dive/image/podman/handler.go b/dive/image/podman/resolver.go similarity index 71% rename from dive/image/podman/handler.go rename to dive/image/podman/resolver.go index 719f0ba..87da3a8 100644 --- a/dive/image/podman/handler.go +++ b/dive/image/podman/resolver.go @@ -11,45 +11,43 @@ import ( "os" ) -type handler struct { +type resolver struct { id string // note: podman supports saving docker formatted archives, we're leveraging this here // todo: add oci parser and image/layer objects image docker.Image } -func NewHandler() *handler { - return &handler{} +func NewResolver() *resolver { + return &resolver{} } -func (handler *handler) Get(id string) error { +func (handler *resolver) Resolve(id string) (image.Analyzer, error) { handler.id = id path, err := handler.fetchArchive() if err != nil { - return err + return nil, err } defer os.Remove(path) file, err := os.Open(path) - // we use podman to extract a docker-formatted image img, err := docker.NewImageFromArchive(ioutil.NopCloser(bufio.NewReader(file))) if err != nil { - return err + return nil, err } - handler.image = img - return nil + return img, nil } -func (handler *handler) Build(args []string) (string, error) { +func (handler *resolver) Build(args []string) (string, error) { var err error handler.id, err = buildImageFromCli(args) return handler.id, err } -func (handler *handler) fetchArchive() (string, error) { +func (handler *resolver) fetchArchive() (string, error) { var err error var ctx = context.Background() @@ -66,7 +64,7 @@ func (handler *handler) fetchArchive() (string, error) { for _, item:= range images { for _, name := range item.Names() { if name == handler.id { - file, err := ioutil.TempFile(os.TempDir(), "dive-handler-tar") + file, err := ioutil.TempFile(os.TempDir(), "dive-resolver-tar") if err != nil { return "", err } @@ -76,6 +74,8 @@ func (handler *handler) fetchArchive() (string, error) { return "", err } + fmt.Println(file.Name()) + return file.Name(), nil } } @@ -83,7 +83,3 @@ func (handler *handler) fetchArchive() (string, error) { return "", fmt.Errorf("image could not be found") } - -func (handler *handler) Analyze() (*image.AnalysisResult, error) { - return handler.image.Analyze() -} diff --git a/dive/image/resolver.go b/dive/image/resolver.go index 9485e13..825f557 100644 --- a/dive/image/resolver.go +++ b/dive/image/resolver.go @@ -1,6 +1,6 @@ package image type Resolver interface { - Get(id string) error + Resolve(id string) (Analyzer, error) Build(options []string) (string, error) } diff --git a/runtime/run.go b/runtime/run.go index c42ee96..66c04e6 100644 --- a/runtime/run.go +++ b/runtime/run.go @@ -40,7 +40,7 @@ func Run(options Options) { // if build is given, get the handler based off of either the explicit runtime - img, err := dive.GetImageHandler(options.Engine) + imageHandler, err := dive.GetImageHandler(options.Engine) if err != nil { fmt.Printf("cannot determine image provider: %v\n", err) utils.Exit(1) @@ -48,14 +48,14 @@ func Run(options Options) { if doBuild { fmt.Println(utils.TitleFormat("Building image...")) - options.ImageId, err = img.Build(options.BuildArgs) + options.ImageId, err = imageHandler.Build(options.BuildArgs) if err != nil { fmt.Printf("cannot build image: %v\n", err) utils.Exit(1) } } - err = img.Get(options.ImageId) + imgAnalyzer, err := imageHandler.Resolve(options.ImageId) if err != nil { fmt.Printf("cannot fetch image: %v\n", err) utils.Exit(1) @@ -70,7 +70,7 @@ func Run(options Options) { fmt.Println(utils.TitleFormat("Analyzing image...")) } - result, err := img.Analyze() + result, err := imgAnalyzer.Analyze() if err != nil { fmt.Printf("cannot analyze image: %v\n", err) utils.Exit(1) From 8053a8d1aae7e93611e48d9c77b3f99cd7ee0555 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 3 Oct 2019 16:46:29 -0400 Subject: [PATCH 05/23] introduced common image format and analyzer --- cmd/analyze.go | 2 +- cmd/build.go | 10 ++-- cmd/root.go | 9 ++-- dive/image/docker/config.go | 13 ++++- dive/image/docker/image.go | 64 +++++++--------------- dive/image/docker/layer.go | 55 +++++++------------ dive/image/docker/resolver.go | 31 ++++++----- dive/image/docker/testing.go | 4 +- dive/image/image.go | 40 ++++++++++++++ dive/image/podman/resolver.go | 93 ++++++++++++++++++++++++-------- dive/image/resolver.go | 4 +- runtime/run.go | 22 ++++---- runtime/ui/details_controller.go | 2 - 13 files changed, 203 insertions(+), 146 deletions(-) create mode 100644 dive/image/image.go diff --git a/cmd/analyze.go b/cmd/analyze.go index e521fc0..1b181cf 100644 --- a/cmd/analyze.go +++ b/cmd/analyze.go @@ -42,7 +42,7 @@ func doAnalyzeCmd(cmd *cobra.Command, args []string) { engine, err := cmd.PersistentFlags().GetString("engine") if err != nil { - fmt.Printf("unable to determine eingine: %v\n", err) + fmt.Printf("unable to determine engine: %v\n", err) utils.Exit(1) } diff --git a/cmd/build.go b/cmd/build.go index dbd25d2..cc6e09a 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -1,8 +1,8 @@ package cmd import ( - "fmt" "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/wagoodman/dive/dive" "github.com/wagoodman/dive/runtime" "github.com/wagoodman/dive/utils" @@ -26,11 +26,9 @@ func doBuildCmd(cmd *cobra.Command, args []string) { initLogging() - engine, err := cmd.PersistentFlags().GetString("engine") - if err != nil { - fmt.Printf("unable to determine eingine: %v\n", err) - utils.Exit(1) - } + // there is no cli options allowed, only config can be supplied + // todo: allow for an engine flag to be passed to dive but not the container engine + engine := viper.GetString("container-engine") runtime.Run(runtime.Options{ Ci: isCi, diff --git a/cmd/root.go b/cmd/root.go index 6bfb23a..b09b5aa 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -65,7 +65,7 @@ func initCli() { rootCmd.PersistentFlags().String("engine", "docker", "The container engine to fetch the image from. Allowed values: "+strings.Join(dive.AllowedEngines, ", ")) - if err := ciConfig.BindPFlag("container-engine.default", rootCmd.PersistentFlags().Lookup("engine")); err != nil { + if err := viper.BindPFlag("container-engine", rootCmd.PersistentFlags().Lookup("engine")); err != nil { log.Fatal("Unable to bind 'engine' flag:", err) } } @@ -104,9 +104,12 @@ func initConfig() { viper.SetDefault("filetree.pane-width", 0.5) viper.SetDefault("filetree.show-attributes", true) - viper.SetDefault("container-engine.default", "docker") + viper.SetDefault("container-engine", "docker") - viper.AutomaticEnv() // read in environment variables that match + viper.SetEnvPrefix("DIVE") + // replace all - with _ when looking for matching environment variables + viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) + viper.AutomaticEnv() // If a config file is found, read it in. if err := viper.ReadInConfig(); err == nil { diff --git a/dive/image/docker/config.go b/dive/image/docker/config.go index 9d2acb9..0efcb57 100644 --- a/dive/image/docker/config.go +++ b/dive/image/docker/config.go @@ -6,8 +6,8 @@ import ( ) type config struct { - History []imageHistoryEntry `json:"history"` - RootFs rootFs `json:"rootfs"` + History []historyEntry `json:"history"` + RootFs rootFs `json:"rootfs"` } type rootFs struct { @@ -15,6 +15,15 @@ type rootFs struct { DiffIds []string `json:"diff_ids"` } +type historyEntry struct { + ID string + Size uint64 + Created string `json:"created"` + Author string `json:"author"` + CreatedBy string `json:"created_by"` + EmptyLayer bool `json:"empty_layer"` +} + func newConfig(configBytes []byte) config { var imageConfig config err := json.Unmarshal(configBytes, &imageConfig) diff --git a/dive/image/docker/image.go b/dive/image/docker/image.go index f2b8cbd..a74fbb6 100644 --- a/dive/image/docker/image.go +++ b/dive/image/docker/image.go @@ -11,16 +11,14 @@ import ( "strings" ) -type Image struct { +type ImageArchive struct { manifest manifest config config - trees []*filetree.FileTree layerMap map[string]*filetree.FileTree - layers []*dockerLayer } -func NewImageFromArchive(tarFile io.ReadCloser) (*Image, error) { - img := &Image{ +func NewImageFromArchive(tarFile io.ReadCloser) (*ImageArchive, error) { + img := &ImageArchive{ layerMap: make(map[string]*filetree.FileTree), } @@ -110,7 +108,6 @@ func processLayerTar(name string, reader *tar.Reader) (*filetree.FileTree, error return tree, nil } - func getFileList(tarReader *tar.Reader) ([]filetree.FileInfo, error) { var files []filetree.FileInfo @@ -137,34 +134,32 @@ func getFileList(tarReader *tar.Reader) ([]filetree.FileInfo, error) { return files, nil } -func (img *Image) Analyze() (*image.AnalysisResult, error) { - - img.trees = make([]*filetree.FileTree, 0) +func (img *ImageArchive) ToImage() (*image.Image, error) { + trees := make([]*filetree.FileTree, 0) // build the content tree for _, treeName := range img.manifest.LayerTarPaths { tr, exists := img.layerMap[treeName] if exists { - img.trees = append(img.trees, tr) + trees = append(trees, tr) continue } return nil, fmt.Errorf("could not find '%s' in parsed layers", treeName) } // build the layers array - img.layers = make([]*dockerLayer, len(img.trees)) + layers := make([]image.Layer, len(trees)) // note that the resolver config stores images in reverse chronological order, so iterate backwards through layers // as you iterate chronologically through history (ignoring history items that have no layer contents) // Note: history is not required metadata in a docker image! tarPathIdx := 0 histIdx := 0 - for layerIdx := len(img.trees) - 1; layerIdx >= 0; layerIdx-- { - - tree := img.trees[(len(img.trees)-1)-layerIdx] + for layerIdx := len(trees) - 1; layerIdx >= 0; layerIdx-- { + tree := trees[(len(trees)-1)-layerIdx] // ignore empty layers, we are only observing layers with content - historyObj := imageHistoryEntry{ + historyObj := historyEntry{ CreatedBy: "(missing)", } for nextHistIdx := histIdx; nextHistIdx < len(img.config.History); nextHistIdx++ { @@ -178,43 +173,20 @@ func (img *Image) Analyze() (*image.AnalysisResult, error) { histIdx++ } - img.layers[layerIdx] = &dockerLayer{ + historyObj.Size = tree.FileSize + + layers[layerIdx] = &layer{ history: historyObj, index: tarPathIdx, - tree: img.trees[layerIdx], - tarPath: img.manifest.LayerTarPaths[tarPathIdx], + tree: trees[layerIdx], } - img.layers[layerIdx].history.Size = tree.FileSize tarPathIdx++ } - efficiency, inefficiencies := filetree.Efficiency(img.trees) - - var sizeBytes, userSizeBytes uint64 - layers := make([]image.Layer, len(img.layers)) - for i, v := range img.layers { - layers[i] = v - sizeBytes += v.Size() - if i != 0 { - userSizeBytes += v.Size() - } - } - - var wastedBytes uint64 - for idx := 0; idx < len(inefficiencies); idx++ { - fileData := inefficiencies[len(inefficiencies)-1-idx] - wastedBytes += uint64(fileData.CumulativeSize) - } - - return &image.AnalysisResult{ - Layers: layers, - RefTrees: img.trees, - Efficiency: efficiency, - UserSizeByes: userSizeBytes, - SizeBytes: sizeBytes, - WastedBytes: wastedBytes, - WastedUserPercent: float64(wastedBytes) / float64(userSizeBytes), - Inefficiencies: inefficiencies, + return &image.Image{ + Trees: trees, + Layers: layers, }, nil + } diff --git a/dive/image/docker/layer.go b/dive/image/docker/layer.go index 9997d3c..9c423b5 100644 --- a/dive/image/docker/layer.go +++ b/dive/image/docker/layer.go @@ -10,56 +10,41 @@ import ( ) // Layer represents a Docker image layer and metadata -type dockerLayer struct { - tarPath string - history imageHistoryEntry +type layer struct { + history historyEntry index int tree *filetree.FileTree } -type imageHistoryEntry struct { - ID string - Size uint64 - Created string `json:"created"` - Author string `json:"author"` - CreatedBy string `json:"created_by"` - EmptyLayer bool `json:"empty_layer"` -} - // ShortId returns the truncated id of the current layer. -func (layer *dockerLayer) TarId() string { - return strings.TrimSuffix(layer.tarPath, "/layer.tar") -} - -// ShortId returns the truncated id of the current layer. -func (layer *dockerLayer) Id() string { - return layer.history.ID +func (l *layer) Id() string { + return l.history.ID } // index returns the relative position of the layer within the image. -func (layer *dockerLayer) Index() int { - return layer.index +func (l *layer) Index() int { + return l.index } // Size returns the number of bytes that this image is. -func (layer *dockerLayer) Size() uint64 { - return layer.history.Size +func (l *layer) Size() uint64 { + return l.history.Size } // Tree returns the file tree representing the current layer. -func (layer *dockerLayer) Tree() *filetree.FileTree { - return layer.tree +func (l *layer) Tree() *filetree.FileTree { + return l.tree } // ShortId returns the truncated id of the current layer. -func (layer *dockerLayer) Command() string { - return strings.TrimPrefix(layer.history.CreatedBy, "/bin/sh -c ") +func (l *layer) Command() string { + return strings.TrimPrefix(l.history.CreatedBy, "/bin/sh -c ") } // ShortId returns the truncated id of the current layer. -func (layer *dockerLayer) ShortId() string { +func (l *layer) ShortId() string { rangeBound := 15 - id := layer.Id() + id := l.Id() if length := len(id); length < 15 { rangeBound = length } @@ -69,14 +54,14 @@ func (layer *dockerLayer) ShortId() string { } // String represents a layer in a columnar format. -func (layer *dockerLayer) String() string { +func (l *layer) String() string { - if layer.index == 0 { + if l.index == 0 { return fmt.Sprintf(image.LayerFormat, - humanize.Bytes(layer.Size()), - "FROM "+layer.ShortId()) + humanize.Bytes(l.Size()), + "FROM "+l.ShortId()) } return fmt.Sprintf(image.LayerFormat, - humanize.Bytes(layer.Size()), - layer.Command()) + humanize.Bytes(l.Size()), + l.Command()) } diff --git a/dive/image/docker/resolver.go b/dive/image/docker/resolver.go index 5d0d3d9..7ce366c 100644 --- a/dive/image/docker/resolver.go +++ b/dive/image/docker/resolver.go @@ -13,8 +13,6 @@ import ( "golang.org/x/net/context" ) -var dockerVersion string - type resolver struct { id string client *client.Client @@ -24,10 +22,9 @@ func NewResolver() *resolver { return &resolver{} } -func (r *resolver) Resolve(id string) (image.Analyzer, error) { - r.id = id +func (r *resolver) Fetch(id string) (*image.Image, error) { - reader, err := r.fetchArchive() + reader, err := r.fetchArchive(id) if err != nil { return nil, err } @@ -37,16 +34,18 @@ func (r *resolver) Resolve(id string) (image.Analyzer, error) { if err != nil { return nil, err } - return img, nil + return img.ToImage() } -func (r *resolver) Build(args []string) (string, error) { - var err error - r.id, err = buildImageFromCli(args) - return r.id, err +func (r *resolver) Build(args []string) (*image.Image, error) { + id, err := buildImageFromCli(args) + if err != nil { + return nil, err + } + return r.Fetch(id) } -func (r *resolver) fetchArchive() (io.ReadCloser, error) { +func (r *resolver) fetchArchive(id string) (io.ReadCloser, error) { var err error // pull the resolver if it does not exist @@ -81,22 +80,22 @@ func (r *resolver) fetchArchive() (io.ReadCloser, error) { clientOpts = append(clientOpts, client.FromEnv) } - clientOpts = append(clientOpts, client.WithVersion(dockerVersion)) + clientOpts = append(clientOpts, client.WithAPIVersionNegotiation()) r.client, err = client.NewClientWithOpts(clientOpts...) if err != nil { return nil, err } - _, _, err = r.client.ImageInspectWithRaw(ctx, r.id) + _, _, err = r.client.ImageInspectWithRaw(ctx, id) if err != nil { // don't use the API, the CLI has more informative output - fmt.Println("Handler not available locally. Trying to pull '" + r.id + "'...") - err = runDockerCmd("pull", r.id) + fmt.Println("Handler not available locally. Trying to pull '" + id + "'...") + err = runDockerCmd("pull", id) if err != nil { return nil, err } } - readCloser, err := r.client.ImageSave(ctx, []string{r.id}) + readCloser, err := r.client.ImageSave(ctx, []string{id}) if err != nil { return nil, err } diff --git a/dive/image/docker/testing.go b/dive/image/docker/testing.go index 0354a29..423d8ad 100644 --- a/dive/image/docker/testing.go +++ b/dive/image/docker/testing.go @@ -12,8 +12,8 @@ func TestLoadDockerImageTar(tarPath string) (*image.AnalysisResult, error) { } defer f.Close() - handler := NewResolver() - img, err := handler.Resolve("dive-test:latest") + resolver := NewResolver() + img, err := resolver.Fetch("dive-test:latest") if err != nil { return nil, err } diff --git a/dive/image/image.go b/dive/image/image.go new file mode 100644 index 0000000..66ed49a --- /dev/null +++ b/dive/image/image.go @@ -0,0 +1,40 @@ +package image + +import ( + "github.com/wagoodman/dive/dive/filetree" +) + +type Image struct { + Trees []*filetree.FileTree + Layers []Layer +} + +func (img *Image) Analyze() (*AnalysisResult, error) { + + efficiency, inefficiencies := filetree.Efficiency(img.Trees) + var sizeBytes, userSizeBytes uint64 + + for i, v := range img.Layers { + sizeBytes += v.Size() + if i != 0 { + userSizeBytes += v.Size() + } + } + + var wastedBytes uint64 + for idx := 0; idx < len(inefficiencies); idx++ { + fileData := inefficiencies[len(inefficiencies)-1-idx] + wastedBytes += uint64(fileData.CumulativeSize) + } + + return &AnalysisResult{ + Layers: img.Layers, + RefTrees: img.Trees, + Efficiency: efficiency, + UserSizeByes: userSizeBytes, + SizeBytes: sizeBytes, + WastedBytes: wastedBytes, + WastedUserPercent: float64(wastedBytes) / float64(userSizeBytes), + Inefficiencies: inefficiencies, + }, nil +} diff --git a/dive/image/podman/resolver.go b/dive/image/podman/resolver.go index 87da3a8..0589399 100644 --- a/dive/image/podman/resolver.go +++ b/dive/image/podman/resolver.go @@ -11,43 +11,96 @@ import ( "os" ) -type resolver struct { - id string - // note: podman supports saving docker formatted archives, we're leveraging this here - // todo: add oci parser and image/layer objects - image docker.Image -} +type resolver struct {} func NewResolver() *resolver { return &resolver{} } -func (handler *resolver) Resolve(id string) (image.Analyzer, error) { - handler.id = id +func (r *resolver) Build(args []string) (*image.Image, error) { + id, err := buildImageFromCli(args) + if err != nil { + return nil, err + } + return r.Fetch(id) +} - path, err := handler.fetchArchive() + +func (r *resolver) Fetch(id string) (*image.Image, error) { + img, err := r.resolveFromDisk(id) + if err == nil { + return img, err + } + img, err = r.resolveFromArchive(id) + if err == nil { + return img, err + } + + return nil, fmt.Errorf("unable to resolve image '%s'", id) +} + +func (r *resolver) resolveFromDisk(id string) (*image.Image, error) { + // var err error + return nil, fmt.Errorf("not implemented") + // + // runtime, err := libpod.NewRuntime(context.TODO()) + // if err != nil { + // return nil, err + // } + // + // images, err := runtime.ImageRuntime().GetImages() + // if err != nil { + // return nil, err + // } + // + // // cfg, _ := runtime.GetConfig() + // // cfg.StorageConfig.GraphRoot + // + // for _, item:= range images { + // for _, name := range item.Names() { + // if name == id { + // fmt.Println("Found it!") + // + // curImg := item + // for { + // h, _ := curImg.History(context.TODO()) + // fmt.Printf("%+v %+v %+v\n", curImg.ID(), h[0].Size, h[0].CreatedBy) + // x, _ := curImg.DriverData() + // fmt.Printf(" %+v\n", x.Data["UpperDir"]) + // + // + // curImg, err = curImg.GetParent(context.TODO()) + // if err != nil || curImg == nil { + // break + // } + // } + // + // } + // } + // } + // + // // os.Exit(0) + // return nil, nil +} + +func (r *resolver) resolveFromArchive(id string) (*image.Image, error) { + path, err := r.fetchArchive(id) if err != nil { return nil, err } defer os.Remove(path) file, err := os.Open(path) + defer file.Close() img, err := docker.NewImageFromArchive(ioutil.NopCloser(bufio.NewReader(file))) if err != nil { return nil, err } - - return img, nil + return img.ToImage() } -func (handler *resolver) Build(args []string) (string, error) { - var err error - handler.id, err = buildImageFromCli(args) - return handler.id, err -} - -func (handler *resolver) fetchArchive() (string, error) { +func (r *resolver) fetchArchive(id string) (string, error) { var err error var ctx = context.Background() @@ -63,7 +116,7 @@ func (handler *resolver) fetchArchive() (string, error) { for _, item:= range images { for _, name := range item.Names() { - if name == handler.id { + if name == id { file, err := ioutil.TempFile(os.TempDir(), "dive-resolver-tar") if err != nil { return "", err @@ -74,8 +127,6 @@ func (handler *resolver) fetchArchive() (string, error) { return "", err } - fmt.Println(file.Name()) - return file.Name(), nil } } diff --git a/dive/image/resolver.go b/dive/image/resolver.go index 825f557..aaa2407 100644 --- a/dive/image/resolver.go +++ b/dive/image/resolver.go @@ -1,6 +1,6 @@ package image type Resolver interface { - Resolve(id string) (Analyzer, error) - Build(options []string) (string, error) + Fetch(id string) (*Image, error) + Build(options []string) (*Image, error) } diff --git a/runtime/run.go b/runtime/run.go index 66c04e6..a6fca8d 100644 --- a/runtime/run.go +++ b/runtime/run.go @@ -40,29 +40,31 @@ func Run(options Options) { // if build is given, get the handler based off of either the explicit runtime - imageHandler, err := dive.GetImageHandler(options.Engine) + imageResolver, err := dive.GetImageHandler(options.Engine) if err != nil { fmt.Printf("cannot determine image provider: %v\n", err) utils.Exit(1) } + var img *image.Image + if doBuild { fmt.Println(utils.TitleFormat("Building image...")) - options.ImageId, err = imageHandler.Build(options.BuildArgs) + img, err = imageResolver.Build(options.BuildArgs) if err != nil { fmt.Printf("cannot build image: %v\n", err) utils.Exit(1) } - } - - imgAnalyzer, err := imageHandler.Resolve(options.ImageId) - if err != nil { - fmt.Printf("cannot fetch image: %v\n", err) - utils.Exit(1) + } else { + img, err = imageResolver.Fetch(options.ImageId) + if err != nil { + fmt.Printf("cannot fetch image: %v\n", err) + utils.Exit(1) + } } // todo, cleanup on error - // todo: image get shold return error for cleanup? + // todo: image get should return error for cleanup? if doExport { fmt.Println(utils.TitleFormat(fmt.Sprintf("Analyzing image... (export to '%s')", options.ExportFile))) @@ -70,7 +72,7 @@ func Run(options Options) { fmt.Println(utils.TitleFormat("Analyzing image...")) } - result, err := imgAnalyzer.Analyze() + result, err := img.Analyze() if err != nil { fmt.Printf("cannot analyze image: %v\n", err) utils.Exit(1) diff --git a/runtime/ui/details_controller.go b/runtime/ui/details_controller.go index ab9d7ec..92d7a6a 100644 --- a/runtime/ui/details_controller.go +++ b/runtime/ui/details_controller.go @@ -126,8 +126,6 @@ func (controller *DetailsController) Render() error { // update contents controller.view.Clear() _, _ = fmt.Fprintln(controller.view, Formatting.Header("Digest: ")+currentLayer.Id()) - // TODO: add back in with controller model - // fmt.Fprintln(view.view, Formatting.Header("Tar ID: ")+currentLayer.TarId()) _, _ = fmt.Fprintln(controller.view, Formatting.Header("Command:")) _, _ = fmt.Fprintln(controller.view, currentLayer.Command()) From 49c0002a0ea0b9c68163181df11b5defd0edb7e7 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 3 Oct 2019 23:36:57 -0400 Subject: [PATCH 06/23] image derived from podman dir (not working) --- dive/filetree/file_info.go | 66 ++++++--- .../docker/{image.go => image_archive.go} | 4 +- dive/image/docker/resolver.go | 14 +- dive/image/podman/image_directory.go | 132 ++++++++++++++++++ dive/image/podman/layer.go | 68 +++++++++ dive/image/podman/resolver.go | 86 ++++++------ 6 files changed, 298 insertions(+), 72 deletions(-) rename dive/image/docker/{image.go => image_archive.go} (96%) create mode 100644 dive/image/podman/image_directory.go create mode 100644 dive/image/podman/layer.go diff --git a/dive/filetree/file_info.go b/dive/filetree/file_info.go index 4d24925..603deaf 100644 --- a/dive/filetree/file_info.go +++ b/dive/filetree/file_info.go @@ -21,24 +21,13 @@ type FileInfo struct { IsDir bool } -// NewFileInfo extracts the metadata from a tar header and file contents and generates a new FileInfo object. -func NewFileInfo(reader *tar.Reader, header *tar.Header, path string) FileInfo { - if header.Typeflag == tar.TypeDir { - return FileInfo{ - Path: path, - TypeFlag: header.Typeflag, - Linkname: header.Linkname, - hash: 0, - Size: header.FileInfo().Size(), - Mode: header.FileInfo().Mode(), - Uid: header.Uid, - Gid: header.Gid, - IsDir: header.FileInfo().IsDir(), - } +// NewFileInfoFromTarHeader extracts the metadata from a tar header and file contents and generates a new FileInfo object. +func NewFileInfoFromTarHeader(reader *tar.Reader, header *tar.Header, path string) FileInfo { + var hash uint64 + if header.Typeflag != tar.TypeDir { + hash = getHashFromReader(reader) } - hash := getHashFromReader(reader) - return FileInfo{ Path: path, TypeFlag: header.Typeflag, @@ -52,6 +41,51 @@ func NewFileInfo(reader *tar.Reader, header *tar.Header, path string) FileInfo { } } +func NewFileInfo(realPath, path string, info os.FileInfo) FileInfo { + var err error + + // todo: don't use tar types here, create our own... + var fileType byte + if info.Mode() & os.ModeSymlink != 0 { + fileType = tar.TypeSymlink + } else if info.IsDir() { + fileType = tar.TypeDir + } else { + fileType = tar.TypeReg + } + + var hash uint64 + if fileType != tar.TypeDir { + file, err := os.Open(realPath) + if err != nil { + logrus.Panic("unable to read file:", realPath) + } + defer file.Close() + hash = getHashFromReader(file) + } + + var linkName string + if fileType == tar.TypeSymlink { + linkName, err = os.Readlink(realPath) + if err != nil { + logrus.Panic("unable to read link:", realPath, err) + } + } + + return FileInfo{ + Path: path, + TypeFlag: fileType, + Linkname: linkName, + hash: hash, + Size: info.Size(), + Mode: info.Mode(), + // todo: support UID/GID + Uid: -1, + Gid: -1, + IsDir: info.IsDir(), + } +} + // Copy duplicates a FileInfo func (data *FileInfo) Copy() *FileInfo { if data == nil { diff --git a/dive/image/docker/image.go b/dive/image/docker/image_archive.go similarity index 96% rename from dive/image/docker/image.go rename to dive/image/docker/image_archive.go index a74fbb6..69b1772 100644 --- a/dive/image/docker/image.go +++ b/dive/image/docker/image_archive.go @@ -17,7 +17,7 @@ type ImageArchive struct { layerMap map[string]*filetree.FileTree } -func NewImageFromArchive(tarFile io.ReadCloser) (*ImageArchive, error) { +func NewImageArchive(tarFile io.ReadCloser) (*ImageArchive, error) { img := &ImageArchive{ layerMap: make(map[string]*filetree.FileTree), } @@ -128,7 +128,7 @@ func getFileList(tarReader *tar.Reader) ([]filetree.FileInfo, error) { case tar.TypeXHeader: return nil, fmt.Errorf("unexptected tar file (XHeader): type=%v name=%s", header.Typeflag, name) default: - files = append(files, filetree.NewFileInfo(tarReader, header, name)) + files = append(files, filetree.NewFileInfoFromTarHeader(tarReader, header, name)) } } return files, nil diff --git a/dive/image/docker/resolver.go b/dive/image/docker/resolver.go index 7ce366c..38cfa45 100644 --- a/dive/image/docker/resolver.go +++ b/dive/image/docker/resolver.go @@ -13,10 +13,7 @@ import ( "golang.org/x/net/context" ) -type resolver struct { - id string - client *client.Client -} +type resolver struct {} func NewResolver() *resolver { return &resolver{} @@ -30,7 +27,7 @@ func (r *resolver) Fetch(id string) (*image.Image, error) { } defer reader.Close() - img, err := NewImageFromArchive(reader) + img, err := NewImageArchive(reader) if err != nil { return nil, err } @@ -47,6 +44,7 @@ func (r *resolver) Build(args []string) (*image.Image, error) { func (r *resolver) fetchArchive(id string) (io.ReadCloser, error) { var err error + var dockerClient *client.Client // pull the resolver if it does not exist ctx := context.Background() @@ -81,11 +79,11 @@ func (r *resolver) fetchArchive(id string) (io.ReadCloser, error) { } clientOpts = append(clientOpts, client.WithAPIVersionNegotiation()) - r.client, err = client.NewClientWithOpts(clientOpts...) + dockerClient, err = client.NewClientWithOpts(clientOpts...) if err != nil { return nil, err } - _, _, err = r.client.ImageInspectWithRaw(ctx, id) + _, _, err = dockerClient.ImageInspectWithRaw(ctx, id) if err != nil { // don't use the API, the CLI has more informative output fmt.Println("Handler not available locally. Trying to pull '" + id + "'...") @@ -95,7 +93,7 @@ func (r *resolver) fetchArchive(id string) (io.ReadCloser, error) { } } - readCloser, err := r.client.ImageSave(ctx, []string{id}) + readCloser, err := dockerClient.ImageSave(ctx, []string{id}) if err != nil { return nil, err } diff --git a/dive/image/podman/image_directory.go b/dive/image/podman/image_directory.go new file mode 100644 index 0000000..c66dbd3 --- /dev/null +++ b/dive/image/podman/image_directory.go @@ -0,0 +1,132 @@ +package podman + +import ( + "context" + "fmt" + podmanImage "github.com/containers/libpod/libpod/image" + "github.com/wagoodman/dive/dive/filetree" + "github.com/wagoodman/dive/dive/image" + "os" + "path/filepath" + "strings" +) + +type ImageDirectoryRef struct { + layerOrder []string + treeMap map[string]*filetree.FileTree + layerMap map[string]*podmanImage.Image +} + +func NewImageDirectoryRef(img *podmanImage.Image) (*ImageDirectoryRef, error) { + imgDirRef := &ImageDirectoryRef{ + layerOrder: make([]string, 0), + treeMap: make(map[string]*filetree.FileTree), + layerMap: make(map[string]*podmanImage.Image), + } + + ctx := context.TODO() + + curImg := img + for { + // h, _ := img.History(ctx) + // fmt.Printf("%+v %+v %+v\n", img.ID(), h[0].Size, h[0].CreatedBy) + + driver, err := curImg.DriverData() + if err != nil { + return nil, fmt.Errorf("graph driver error: %+v", err) + } + + if driver.Name != "overlay" { + return nil, fmt.Errorf("unsupported graph driver: %s", driver.Name) + } + + rootDir, exists := driver.Data["UpperDir"] + if !exists { + return nil, fmt.Errorf("graph has no upper dir") + } + + if _, err := os.Stat(rootDir); os.IsNotExist(err) { + return nil, fmt.Errorf("graph root dir does not exist: %s", rootDir) + } + + // build tree from directory... + tree, err := processLayer(curImg.ID(), rootDir) + if err != nil { + return nil, err + } + + // record the tree and layer info + imgDirRef.treeMap[curImg.ID()] = tree + imgDirRef.layerMap[curImg.ID()] = curImg + imgDirRef.layerOrder = append(imgDirRef.layerOrder, curImg.ID()) + + // continue to the next image + curImg, err = curImg.GetParent(ctx) + if err != nil || curImg == nil { + break + } + } + + return imgDirRef, nil +} + +func processLayer(name, rootDir string) (*filetree.FileTree, error) { + tree := filetree.NewFileTree() + tree.Name = name + + err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + // add this file to the tree... + fileInfo := filetree.NewFileInfo(path, "/"+strings.TrimPrefix(path, rootDir), info) + + tree.FileSize += uint64(fileInfo.Size) + + _, _, err = tree.AddPath(fileInfo.Path, fileInfo) + if err != nil { + return err + } + + return nil + }) + if err != nil { + return nil, fmt.Errorf("unable to walk upper directory tree") + } + + return tree, nil +} + +func (img *ImageDirectoryRef) ToImage() (*image.Image, error) { + trees := make([]*filetree.FileTree, 0) + // build the content tree + // todo: this isn't needed! + for _, id := range img.layerOrder { + tr, exists := img.treeMap[id] + if exists { + trees = append(trees, tr) + continue + } + return nil, fmt.Errorf("could not find '%s' in parsed trees", id) + } + + layers := make([]image.Layer, len(trees)) + + // note that the resolver config stores images in reverse chronological order, so iterate backwards through layers + // as you iterate chronologically through history (ignoring history items that have no layer contents) + // Note: history is not required metadata in a docker image! + tarPathIdx := 0 + for layerIdx := len(trees) - 1; layerIdx >= 0; layerIdx-- { + id := img.layerOrder[layerIdx] + layers[layerIdx] = &layer{ + obj: img.layerMap[id], + index: tarPathIdx, + tree: trees[layerIdx], + } + } + + return &image.Image{ + Trees: trees, + Layers: layers, + }, nil +} diff --git a/dive/image/podman/layer.go b/dive/image/podman/layer.go new file mode 100644 index 0000000..1323fcc --- /dev/null +++ b/dive/image/podman/layer.go @@ -0,0 +1,68 @@ +package podman + +import ( + "fmt" + podmanImage "github.com/containers/libpod/libpod/image" + "github.com/dustin/go-humanize" + "github.com/wagoodman/dive/dive/filetree" + "github.com/wagoodman/dive/dive/image" + "strings" +) + +// Layer represents a Docker image layer and metadata +type layer struct { + obj *podmanImage.Image + index int + tree *filetree.FileTree +} + +// ShortId returns the truncated id of the current layer. +func (l *layer) Id() string { + return l.obj.ID() +} + +// index returns the relative position of the layer within the image. +func (l *layer) Index() int { + return l.index +} + +// Size returns the number of bytes that this image is. +func (l *layer) Size() uint64 { + return uint64(l.obj.ImageData.Size) +} + +// Tree returns the file tree representing the current layer. +func (l *layer) Tree() *filetree.FileTree { + return l.tree +} + +// ShortId returns the truncated id of the current layer. +func (l *layer) Command() string { + // todo: is 0 right? + return strings.TrimPrefix(l.obj.ImageData.History[0].CreatedBy, "/bin/sh -c ") +} + +// ShortId returns the truncated id of the current layer. +func (l *layer) ShortId() string { + rangeBound := 15 + id := l.Id() + if length := len(id); length < 15 { + rangeBound = length + } + id = id[0:rangeBound] + + return id +} + +// String represents a layer in a columnar format. +func (l *layer) String() string { + + if l.index == 0 { + return fmt.Sprintf(image.LayerFormat, + humanize.Bytes(l.Size()), + "FROM "+l.ShortId()) + } + return fmt.Sprintf(image.LayerFormat, + humanize.Bytes(l.Size()), + l.Command()) +} diff --git a/dive/image/podman/resolver.go b/dive/image/podman/resolver.go index 0589399..2a9126d 100644 --- a/dive/image/podman/resolver.go +++ b/dive/image/podman/resolver.go @@ -31,7 +31,11 @@ func (r *resolver) Fetch(id string) (*image.Image, error) { if err == nil { return img, err } - img, err = r.resolveFromArchive(id) + + // todo: remove print of error + fmt.Println(err) + + img, err = r.resolveFromDockerArchive(id) if err == nil { return img, err } @@ -40,51 +44,41 @@ func (r *resolver) Fetch(id string) (*image.Image, error) { } func (r *resolver) resolveFromDisk(id string) (*image.Image, error) { - // var err error - return nil, fmt.Errorf("not implemented") - // - // runtime, err := libpod.NewRuntime(context.TODO()) - // if err != nil { - // return nil, err - // } - // - // images, err := runtime.ImageRuntime().GetImages() - // if err != nil { - // return nil, err - // } - // - // // cfg, _ := runtime.GetConfig() - // // cfg.StorageConfig.GraphRoot - // - // for _, item:= range images { - // for _, name := range item.Names() { - // if name == id { - // fmt.Println("Found it!") - // - // curImg := item - // for { - // h, _ := curImg.History(context.TODO()) - // fmt.Printf("%+v %+v %+v\n", curImg.ID(), h[0].Size, h[0].CreatedBy) - // x, _ := curImg.DriverData() - // fmt.Printf(" %+v\n", x.Data["UpperDir"]) - // - // - // curImg, err = curImg.GetParent(context.TODO()) - // if err != nil || curImg == nil { - // break - // } - // } - // - // } - // } - // } - // - // // os.Exit(0) - // return nil, nil + var img *ImageDirectoryRef + var err error + + runtime, err := libpod.NewRuntime(context.TODO()) + if err != nil { + return nil, err + } + + images, err := runtime.ImageRuntime().GetImages() + if err != nil { + return nil, err + } + + ImageLoop: + for _, candidateImage := range images { + for _, name := range candidateImage.Names() { + if name == id { + img, err = NewImageDirectoryRef(candidateImage) + if err != nil { + return nil, err + } + break ImageLoop + } + } + } + + if img == nil { + return nil, fmt.Errorf("could not find image by name: '%s'", id) + } + + return img.ToImage() } -func (r *resolver) resolveFromArchive(id string) (*image.Image, error) { - path, err := r.fetchArchive(id) +func (r *resolver) resolveFromDockerArchive(id string) (*image.Image, error) { + path, err := r.fetchDockerArchive(id) if err != nil { return nil, err } @@ -93,14 +87,14 @@ func (r *resolver) resolveFromArchive(id string) (*image.Image, error) { file, err := os.Open(path) defer file.Close() - img, err := docker.NewImageFromArchive(ioutil.NopCloser(bufio.NewReader(file))) + img, err := docker.NewImageArchive(ioutil.NopCloser(bufio.NewReader(file))) if err != nil { return nil, err } return img.ToImage() } -func (r *resolver) fetchArchive(id string) (string, error) { +func (r *resolver) fetchDockerArchive(id string) (string, error) { var err error var ctx = context.Background() From 56e8530dea39dbf48a3eb1fae7216b45c055e9da Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Fri, 4 Oct 2019 10:05:25 -0400 Subject: [PATCH 07/23] ui bug on bad analysis result --- dive/image/podman/image_directory.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dive/image/podman/image_directory.go b/dive/image/podman/image_directory.go index c66dbd3..4b03dae 100644 --- a/dive/image/podman/image_directory.go +++ b/dive/image/podman/image_directory.go @@ -115,12 +115,10 @@ func (img *ImageDirectoryRef) ToImage() (*image.Image, error) { // note that the resolver config stores images in reverse chronological order, so iterate backwards through layers // as you iterate chronologically through history (ignoring history items that have no layer contents) // Note: history is not required metadata in a docker image! - tarPathIdx := 0 - for layerIdx := len(trees) - 1; layerIdx >= 0; layerIdx-- { - id := img.layerOrder[layerIdx] + for layerIdx, id := range img.layerOrder { layers[layerIdx] = &layer{ obj: img.layerMap[id], - index: tarPathIdx, + index: layerIdx, tree: trees[layerIdx], } } @@ -129,4 +127,5 @@ func (img *ImageDirectoryRef) ToImage() (*image.Image, error) { Trees: trees, Layers: layers, }, nil + } From 26d37baabd841556facaf346a73883bd2c52ad8c Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Sun, 6 Oct 2019 12:15:58 -0400 Subject: [PATCH 08/23] better error propagation on startup --- dive/filetree/cache.go | 34 +++++++--- dive/filetree/efficiency.go | 15 +++-- dive/filetree/file_tree.go | 5 +- dive/filetree/file_tree_test.go | 5 +- runtime/run.go | 15 ++++- runtime/ui/filetree_viewmodel.go | 8 ++- runtime/ui/filetree_viewmodel_test.go | 11 +++- runtime/ui/ui.go | 90 ++++++++++++++++++++------- 8 files changed, 139 insertions(+), 44 deletions(-) diff --git a/dive/filetree/cache.go b/dive/filetree/cache.go index 82c1795..a7f0fc4 100644 --- a/dive/filetree/cache.go +++ b/dive/filetree/cache.go @@ -13,29 +13,36 @@ type TreeCache struct { cache map[TreeCacheKey]*FileTree } -func (cache *TreeCache) Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int) *FileTree { +func (cache *TreeCache) Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int) (*FileTree, error) { key := TreeCacheKey{bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop} if value, exists := cache.cache[key]; exists { - return value + return value, nil } - value := cache.buildTree(key) + value, err := cache.buildTree(key) + if err != nil { + return nil, err + } cache.cache[key] = value - return value + return value, nil } -func (cache *TreeCache) buildTree(key TreeCacheKey) *FileTree { - newTree := StackTreeRange(cache.refTrees, key.bottomTreeStart, key.bottomTreeStop) +func (cache *TreeCache) buildTree(key TreeCacheKey) (*FileTree, error) { + newTree, err := StackTreeRange(cache.refTrees, key.bottomTreeStart, key.bottomTreeStop) + if err != nil { + return nil, err + } for idx := key.topTreeStart; idx <= key.topTreeStop; idx++ { err := newTree.CompareAndMark(cache.refTrees[idx]) if err != nil { logrus.Errorf("unable to build tree: %+v", err) + return nil, err } } - return newTree + return newTree, nil } -func (cache *TreeCache) Build() { +func (cache *TreeCache) Build() error { var bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop int // case 1: layer compare (top tree SIZE is fixed (BUT floats forward), Bottom tree SIZE changes) @@ -51,7 +58,10 @@ func (cache *TreeCache) Build() { topTreeStart = selectIdx } - cache.Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop) + _, err := cache.Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop) + if err != nil { + return err + } } // case 2: aggregated compare (bottom tree is ENTIRELY fixed, top tree SIZE changes) @@ -66,8 +76,12 @@ func (cache *TreeCache) Build() { topTreeStart = 1 } - cache.Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop) + _, err := cache.Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop) + if err != nil { + return err + } } + return nil } func NewFileTreeCache(refTrees []*FileTree) TreeCache { diff --git a/dive/filetree/efficiency.go b/dive/filetree/efficiency.go index 7be116b..24ad3b5 100644 --- a/dive/filetree/efficiency.go +++ b/dive/filetree/efficiency.go @@ -1,7 +1,6 @@ package filetree import ( - "fmt" "sort" "github.com/sirupsen/logrus" @@ -63,14 +62,22 @@ func Efficiency(trees []*FileTree) (float64, EfficiencySlice) { sizeBytes += curNode.Data.FileInfo.Size return nil } - stackedTree := StackTreeRange(trees, 0, currentTree-1) + stackedTree, err := StackTreeRange(trees, 0, currentTree-1) + if err != nil { + logrus.Errorf("unable to stack tree range: %+v", err) + return err + } + previousTreeNode, err := stackedTree.GetNode(node.Path()) if err != nil { - logrus.Debug(fmt.Sprintf("CurrentTree: %d : %s", currentTree, err)) - } else if previousTreeNode.Data.FileInfo.IsDir { + return err + } + + if previousTreeNode.Data.FileInfo.IsDir { err = previousTreeNode.VisitDepthChildFirst(sizer, nil) if err != nil { logrus.Errorf("unable to propagate whiteout dir: %+v", err) + return err } } diff --git a/dive/filetree/file_tree.go b/dive/filetree/file_tree.go index c7c3dfc..11311fc 100644 --- a/dive/filetree/file_tree.go +++ b/dive/filetree/file_tree.go @@ -367,14 +367,15 @@ func (tree *FileTree) markRemoved(path string) error { } // StackTreeRange combines an array of trees into a single tree -func StackTreeRange(trees []*FileTree, start, stop int) *FileTree { +func StackTreeRange(trees []*FileTree, start, stop int) (*FileTree, error) { tree := trees[0].Copy() for idx := start; idx <= stop; idx++ { err := tree.Stack(trees[idx]) if err != nil { logrus.Errorf("could not stack tree range: %v", err) + return nil, err } } - return tree + return tree, nil } diff --git a/dive/filetree/file_tree_test.go b/dive/filetree/file_tree_test.go index fc57036..d9f9677 100644 --- a/dive/filetree/file_tree_test.go +++ b/dive/filetree/file_tree_test.go @@ -745,7 +745,10 @@ func TestStackRange(t *testing.T) { } } trees := []*FileTree{lowerTree, upperTree, tree} - StackTreeRange(trees, 0, 2) + _, err = StackTreeRange(trees, 0, 2) + if err != nil { + t.Fatal(err) + } } func TestRemoveOnIterate(t *testing.T) { diff --git a/runtime/run.go b/runtime/run.go index a6fca8d..b8185e8 100644 --- a/runtime/run.go +++ b/runtime/run.go @@ -2,6 +2,7 @@ package runtime import ( "fmt" + "github.com/sirupsen/logrus" "github.com/wagoodman/dive/dive" "github.com/wagoodman/dive/runtime/ci" "github.com/wagoodman/dive/runtime/export" @@ -95,7 +96,11 @@ func Run(options Options) { fmt.Println(utils.TitleFormat("Building cache...")) cache := filetree.NewFileTreeCache(result.RefTrees) - cache.Build() + err := cache.Build() + if err != nil { + logrus.Error(err) + utils.Exit(1) + } // it appears there is a race condition where termbox.Init() will // block nearly indefinitely when running as the first process in @@ -104,6 +109,12 @@ func Run(options Options) { // enough sleep will prevent this behavior (todo: remove this hack) time.Sleep(100 * time.Millisecond) - ui.Run(result, cache) + err = ui.Run(result, cache) + + if err != nil { + logrus.Error(err) + utils.Exit(1) + } + utils.Exit(0) } } diff --git a/runtime/ui/filetree_viewmodel.go b/runtime/ui/filetree_viewmodel.go index 46e9af5..30009fd 100644 --- a/runtime/ui/filetree_viewmodel.go +++ b/runtime/ui/filetree_viewmodel.go @@ -102,7 +102,11 @@ func (vm *FileTreeViewModel) setTreeByLayer(bottomTreeStart, bottomTreeStop, top if topTreeStop > len(vm.RefTrees)-1 { return fmt.Errorf("invalid layer index given: %d of %d", topTreeStop, len(vm.RefTrees)-1) } - newTree := vm.cache.Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop) + newTree, err := vm.cache.Get(bottomTreeStart, bottomTreeStop, topTreeStart, topTreeStop) + if err != nil { + logrus.Errorf("unable to fetch layer tree from cache: %+v", err) + return err + } // preserve vm state on copy visitor := func(node *filetree.FileNode) error { @@ -112,7 +116,7 @@ func (vm *FileTreeViewModel) setTreeByLayer(bottomTreeStart, bottomTreeStop, top } return nil } - err := vm.ModelTree.VisitDepthChildFirst(visitor, nil) + err = vm.ModelTree.VisitDepthChildFirst(visitor, nil) if err != nil { logrus.Errorf("unable to propagate layer tree: %+v", err) return err diff --git a/runtime/ui/filetree_viewmodel_test.go b/runtime/ui/filetree_viewmodel_test.go index a0ae5cd..ba1d86c 100644 --- a/runtime/ui/filetree_viewmodel_test.go +++ b/runtime/ui/filetree_viewmodel_test.go @@ -78,11 +78,18 @@ func initializeTestViewModel(t *testing.T) *FileTreeViewModel { t.Fatalf("%s: unable to fetch analysis: %v", t.Name(), err) } cache := filetree.NewFileTreeCache(result.RefTrees) - cache.Build() + err = cache.Build() + if err != nil { + t.Fatalf("%s: unable to build cache: %+v", t.Name(), err) + } Formatting.Selected = color.New(color.ReverseVideo, color.Bold).SprintFunc() - return NewFileTreeViewModel(filetree.StackTreeRange(result.RefTrees, 0, 0), result.RefTrees, cache) + treeStack, err := filetree.StackTreeRange(result.RefTrees, 0, 0) + if err != nil { + t.Fatalf("%s: unable to create tree ViewModel: %v", t.Name(), err) + } + return NewFileTreeViewModel(treeStack, result.RefTrees, cache) } func runTestCase(t *testing.T, vm *FileTreeViewModel, width, height int, filterRegex *regexp.Regexp) { diff --git a/runtime/ui/ui.go b/runtime/ui/ui.go index 0182607..6f4e5ed 100644 --- a/runtime/ui/ui.go +++ b/runtime/ui/ui.go @@ -251,20 +251,32 @@ func layout(g *gocui.Gui) error { view, viewErr = g.SetView(Controllers.Layer.Name, -1, -1+headerRows, splitCols, layersHeight) header, headerErr = g.SetView(Controllers.Layer.Name+"header", -1, -1, splitCols, headerRows) if isNewView(viewErr, headerErr) { - _ = Controllers.Layer.Setup(view, header) + err = Controllers.Layer.Setup(view, header) + if err != nil { + logrus.Error("unable to setup layer controller", err) + return err + } if _, err = g.SetCurrentView(Controllers.Layer.Name); err != nil { + logrus.Error("unable to set view to layer", err) return err } // since we are selecting the view, we should rerender to indicate it is selected - _ = Controllers.Layer.Render() + err = Controllers.Layer.Render() + if err != nil { + logrus.Error("unable to render layer view", err) + return err + } } // Details view, viewErr = g.SetView(Controllers.Details.Name, -1, -1+layersHeight+headerRows, splitCols, maxY-bottomRows) header, headerErr = g.SetView(Controllers.Details.Name+"header", -1, -1+layersHeight, splitCols, layersHeight+headerRows) if isNewView(viewErr, headerErr) { - _ = Controllers.Details.Setup(view, header) + err = Controllers.Details.Setup(view, header) + if err != nil { + return err + } } // Filetree @@ -275,40 +287,64 @@ func layout(g *gocui.Gui) error { view, viewErr = g.SetView(Controllers.Tree.Name, splitCols, -1+headerRows-offset, debugCols, maxY-bottomRows) header, headerErr = g.SetView(Controllers.Tree.Name+"header", splitCols, -1, debugCols, headerRows-offset) if isNewView(viewErr, headerErr) { - _ = Controllers.Tree.Setup(view, header) + err = Controllers.Tree.Setup(view, header) + if err != nil { + logrus.Error("unable to setup tree controller", err) + return err + } + } + err = Controllers.Tree.onLayoutChange(resized) + if err != nil { + logrus.Error("unable to setup layer controller onLayoutChange", err) + return err } - _ = Controllers.Tree.onLayoutChange(resized) // Status Bar view, viewErr = g.SetView(Controllers.Status.Name, -1, maxY-statusBarHeight-statusBarIndex, maxX, maxY-(statusBarIndex-1)) if isNewView(viewErr, headerErr) { - _ = Controllers.Status.Setup(view, nil) + err = Controllers.Status.Setup(view, nil) + if err != nil { + logrus.Error("unable to setup status controller", err) + return err + } } // Filter Bar view, viewErr = g.SetView(Controllers.Filter.Name, len(Controllers.Filter.headerStr)-1, maxY-filterBarHeight-filterBarIndex, maxX, maxY-(filterBarIndex-1)) header, headerErr = g.SetView(Controllers.Filter.Name+"header", -1, maxY-filterBarHeight-filterBarIndex, len(Controllers.Filter.headerStr), maxY-(filterBarIndex-1)) if isNewView(viewErr, headerErr) { - _ = Controllers.Filter.Setup(view, header) + err = Controllers.Filter.Setup(view, header) + if err != nil { + logrus.Error("unable to setup filter controller", err) + return err + } } return nil } // Update refreshes the state objects for future rendering. -func Update() { +func Update() error { for _, view := range Controllers.lookup { - _ = view.Update() + err := view.Update() + if err != nil { + return err + } } + return nil } // Render flushes the state objects to the screen. -func Render() { +func Render() error { for _, view := range Controllers.lookup { if view.IsVisible() { - _ = view.Render() + err := view.Render() + if err != nil { + return err + } } } + return nil } // renderStatusOption formats key help bindings-to-title pairs. @@ -321,7 +357,7 @@ func renderStatusOption(control, title string, selected bool) string { } // Run is the UI entrypoint. -func Run(analysis *image.AnalysisResult, cache filetree.TreeCache) { +func Run(analysis *image.AnalysisResult, cache filetree.TreeCache) error { Formatting.Selected = color.New(color.ReverseVideo, color.Bold).SprintFunc() Formatting.Header = color.New(color.Bold).SprintFunc() @@ -335,20 +371,20 @@ func Run(analysis *image.AnalysisResult, cache filetree.TreeCache) { var err error GlobalKeybindings.quit, err = keybinding.ParseAll(viper.GetString("keybinding.quit")) if err != nil { - logrus.Error(err) + return err } GlobalKeybindings.toggleView, err = keybinding.ParseAll(viper.GetString("keybinding.toggle-view")) if err != nil { - logrus.Error(err) + return err } GlobalKeybindings.filterView, err = keybinding.ParseAll(viper.GetString("keybinding.filter-files")) if err != nil { - logrus.Error(err) + return err } g, err := gocui.NewGui(gocui.OutputNormal) if err != nil { - logrus.Error(err) + return err } utils.SetUi(g) defer g.Close() @@ -358,7 +394,11 @@ func Run(analysis *image.AnalysisResult, cache filetree.TreeCache) { Controllers.Layer = NewLayerController("side", g, analysis.Layers) Controllers.lookup[Controllers.Layer.Name] = Controllers.Layer - Controllers.Tree = NewFileTreeController("main", g, filetree.StackTreeRange(analysis.RefTrees, 0, 0), analysis.RefTrees, cache) + treeStack, err := filetree.StackTreeRange(analysis.RefTrees, 0, 0) + if err != nil { + return err + } + Controllers.Tree = NewFileTreeController("main", g, treeStack, analysis.RefTrees, cache) Controllers.lookup[Controllers.Tree.Name] = Controllers.Tree Controllers.Status = NewStatusController("status", g) @@ -381,15 +421,23 @@ func Run(analysis *image.AnalysisResult, cache filetree.TreeCache) { // } // perform the first update and render now that all resources have been loaded - Update() - Render() + err = Update() + if err != nil { + return err + } + + err = Render() + if err != nil { + return err + } if err := keyBindings(g); err != nil { - logrus.Error("keybinding error: ", err) + return err } if err := g.MainLoop(); err != nil && err != gocui.ErrQuit { logrus.Error("main loop error: ", err) + return err } - utils.Exit(0) + return nil } From d53d8926bca6c831de5d6599547913b8a580d4f2 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 7 Oct 2019 11:44:41 -0400 Subject: [PATCH 09/23] stop swallowing errors & cleanup exit logic --- cmd/analyze.go | 11 ++-- cmd/build.go | 3 - cmd/root.go | 6 +- dive/image/docker/image_archive.go | 7 +-- dive/image/podman/image_directory.go | 2 +- runtime/run.go | 24 ++++---- runtime/ui/details_controller.go | 30 ++++++---- runtime/ui/filetree_controller.go | 31 ++++++----- runtime/ui/filetree_viewmodel.go | 22 +++++--- runtime/ui/filter_controller.go | 6 +- runtime/ui/layer_controller.go | 29 ++++++---- runtime/ui/status_controller.go | 9 ++- runtime/ui/ui.go | 82 ++++++++++++++++++---------- utils/exit.go | 34 ------------ 14 files changed, 153 insertions(+), 143 deletions(-) delete mode 100644 utils/exit.go diff --git a/cmd/analyze.go b/cmd/analyze.go index 1b181cf..4e6c27e 100644 --- a/cmd/analyze.go +++ b/cmd/analyze.go @@ -3,16 +3,15 @@ package cmd import ( "fmt" "github.com/wagoodman/dive/dive" + "os" "github.com/spf13/cobra" "github.com/wagoodman/dive/runtime" - "github.com/wagoodman/dive/utils" ) // doAnalyzeCmd takes a docker image tag, digest, or id and displays the // image analysis to the screen func doAnalyzeCmd(cmd *cobra.Command, args []string) { - defer utils.Cleanup() if len(args) == 0 { printVersionFlag, err := cmd.PersistentFlags().GetBool("version") @@ -22,13 +21,13 @@ func doAnalyzeCmd(cmd *cobra.Command, args []string) { } fmt.Println("No image argument given") - utils.Exit(1) + os.Exit(1) } userImage := args[0] if userImage == "" { fmt.Println("No image argument given") - utils.Exit(1) + os.Exit(1) } initLogging() @@ -37,13 +36,13 @@ func doAnalyzeCmd(cmd *cobra.Command, args []string) { if err != nil { fmt.Printf("ci configuration error: %v\n", err) - utils.Exit(1) + os.Exit(1) } engine, err := cmd.PersistentFlags().GetString("engine") if err != nil { fmt.Printf("unable to determine engine: %v\n", err) - utils.Exit(1) + os.Exit(1) } runtime.Run(runtime.Options{ diff --git a/cmd/build.go b/cmd/build.go index cc6e09a..0cff973 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -5,7 +5,6 @@ import ( "github.com/spf13/viper" "github.com/wagoodman/dive/dive" "github.com/wagoodman/dive/runtime" - "github.com/wagoodman/dive/utils" ) // buildCmd represents the build command @@ -22,8 +21,6 @@ func init() { // doBuildCmd implements the steps taken for the build command func doBuildCmd(cmd *cobra.Command, args []string) { - defer utils.Cleanup() - initLogging() // there is no cli options allowed, only config can be supplied diff --git a/cmd/root.go b/cmd/root.go index b09b5aa..41abe52 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -13,7 +13,6 @@ import ( log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/wagoodman/dive/utils" ) var cfgFile string @@ -36,9 +35,8 @@ the amount of wasted space and identifies the offending files from the image.`, func Execute() { if err := rootCmd.Execute(); err != nil { fmt.Println(err) - utils.Exit(1) + os.Exit(1) } - utils.Cleanup() } func init() { @@ -160,7 +158,7 @@ func getCfgFile(fromFlag string) string { home, err := homedir.Dir() if err != nil { fmt.Println(err) - utils.Exit(0) + os.Exit(0) } xdgHome := os.Getenv("XDG_CONFIG_HOME") diff --git a/dive/image/docker/image_archive.go b/dive/image/docker/image_archive.go index 69b1772..5c4b001 100644 --- a/dive/image/docker/image_archive.go +++ b/dive/image/docker/image_archive.go @@ -5,9 +5,9 @@ import ( "fmt" "github.com/wagoodman/dive/dive/filetree" "github.com/wagoodman/dive/dive/image" - "github.com/wagoodman/dive/utils" "io" "io/ioutil" + "os" "strings" ) @@ -37,7 +37,7 @@ func NewImageArchive(tarFile io.ReadCloser) (*ImageArchive, error) { if err != nil { fmt.Println(err) - utils.Exit(1) + os.Exit(1) } name := header.Name @@ -116,8 +116,7 @@ func getFileList(tarReader *tar.Reader) ([]filetree.FileInfo, error) { if err == io.EOF { break } else if err != nil { - fmt.Println(err) - utils.Exit(1) + return nil, err } name := header.Name diff --git a/dive/image/podman/image_directory.go b/dive/image/podman/image_directory.go index 4b03dae..8e551a8 100644 --- a/dive/image/podman/image_directory.go +++ b/dive/image/podman/image_directory.go @@ -58,7 +58,7 @@ func NewImageDirectoryRef(img *podmanImage.Image) (*ImageDirectoryRef, error) { // record the tree and layer info imgDirRef.treeMap[curImg.ID()] = tree imgDirRef.layerMap[curImg.ID()] = curImg - imgDirRef.layerOrder = append(imgDirRef.layerOrder, curImg.ID()) + imgDirRef.layerOrder = append([]string{curImg.ID()}, imgDirRef.layerOrder...) // continue to the next image curImg, err = curImg.GetParent(ctx) diff --git a/runtime/run.go b/runtime/run.go index b8185e8..65b3670 100644 --- a/runtime/run.go +++ b/runtime/run.go @@ -6,6 +6,7 @@ import ( "github.com/wagoodman/dive/dive" "github.com/wagoodman/dive/runtime/ci" "github.com/wagoodman/dive/runtime/export" + "os" "time" "github.com/dustin/go-humanize" @@ -26,9 +27,9 @@ func runCi(analysis *image.AnalysisResult, options Options) { evaluator.Report() if pass { - utils.Exit(0) + os.Exit(0) } - utils.Exit(1) + os.Exit(1) } func Run(options Options) { @@ -44,7 +45,7 @@ func Run(options Options) { imageResolver, err := dive.GetImageHandler(options.Engine) if err != nil { fmt.Printf("cannot determine image provider: %v\n", err) - utils.Exit(1) + os.Exit(1) } var img *image.Image @@ -54,13 +55,13 @@ func Run(options Options) { img, err = imageResolver.Build(options.BuildArgs) if err != nil { fmt.Printf("cannot build image: %v\n", err) - utils.Exit(1) + os.Exit(1) } } else { img, err = imageResolver.Fetch(options.ImageId) if err != nil { fmt.Printf("cannot fetch image: %v\n", err) - utils.Exit(1) + os.Exit(1) } } @@ -76,14 +77,14 @@ func Run(options Options) { result, err := img.Analyze() if err != nil { fmt.Printf("cannot analyze image: %v\n", err) - utils.Exit(1) + os.Exit(1) } if doExport { err = export.NewExport(result).ToFile(options.ExportFile) if err != nil { fmt.Printf("cannot write export file: %v\n", err) - utils.Exit(1) + os.Exit(1) } } @@ -91,7 +92,7 @@ func Run(options Options) { runCi(result, options) } else { if doExport { - utils.Exit(0) + os.Exit(0) } fmt.Println(utils.TitleFormat("Building cache...")) @@ -99,7 +100,7 @@ func Run(options Options) { err := cache.Build() if err != nil { logrus.Error(err) - utils.Exit(1) + os.Exit(1) } // it appears there is a race condition where termbox.Init() will @@ -110,11 +111,10 @@ func Run(options Options) { time.Sleep(100 * time.Millisecond) err = ui.Run(result, cache) - if err != nil { logrus.Error(err) - utils.Exit(1) + os.Exit(1) } - utils.Exit(0) + os.Exit(0) } } diff --git a/runtime/ui/details_controller.go b/runtime/ui/details_controller.go index 92d7a6a..a9ec51c 100644 --- a/runtime/ui/details_controller.go +++ b/runtime/ui/details_controller.go @@ -2,6 +2,7 @@ package ui import ( "fmt" + "github.com/sirupsen/logrus" "github.com/wagoodman/dive/dive/filetree" "strconv" "strings" @@ -121,22 +122,29 @@ func (controller *DetailsController) Render() error { layerHeaderStr := fmt.Sprintf("[Layer Details]%s", strings.Repeat("─", width-15)) imageHeaderStr := fmt.Sprintf("[Image Details]%s", strings.Repeat("─", width-15)) - _, _ = fmt.Fprintln(controller.header, Formatting.Header(vtclean.Clean(layerHeaderStr, false))) + _, err := fmt.Fprintln(controller.header, Formatting.Header(vtclean.Clean(layerHeaderStr, false))) + if err != nil { + return err + } // update contents controller.view.Clear() - _, _ = fmt.Fprintln(controller.view, Formatting.Header("Digest: ")+currentLayer.Id()) - _, _ = fmt.Fprintln(controller.view, Formatting.Header("Command:")) - _, _ = fmt.Fprintln(controller.view, currentLayer.Command()) - _, _ = fmt.Fprintln(controller.view, "\n"+Formatting.Header(vtclean.Clean(imageHeaderStr, false))) + var lines = make([]string, 0) + lines = append(lines, Formatting.Header("Digest: ")+currentLayer.Id()) + lines = append(lines, Formatting.Header("Command:")) + lines = append(lines, currentLayer.Command()) + lines = append(lines, "\n"+Formatting.Header(vtclean.Clean(imageHeaderStr, false))) + lines = append(lines, imageSizeStr) + lines = append(lines, wastedSpaceStr) + lines = append(lines, effStr+"\n") + lines = append(lines, inefficiencyReport) - _, _ = fmt.Fprintln(controller.view, imageSizeStr) - _, _ = fmt.Fprintln(controller.view, wastedSpaceStr) - _, _ = fmt.Fprintln(controller.view, effStr+"\n") - - _, _ = fmt.Fprintln(controller.view, inefficiencyReport) - return nil + _, err = fmt.Fprintln(controller.view, strings.Join(lines, "\n")) + if err != nil { + logrus.Debug("unable to write to buffer: ", err) + } + return err }) return nil } diff --git a/runtime/ui/filetree_controller.go b/runtime/ui/filetree_controller.go index 90bcba6..16a72b2 100644 --- a/runtime/ui/filetree_controller.go +++ b/runtime/ui/filetree_controller.go @@ -42,15 +42,17 @@ type FileTreeController struct { } // NewFileTreeController creates a new view object attached the the global [gocui] screen object. -func NewFileTreeController(name string, gui *gocui.Gui, tree *filetree.FileTree, refTrees []*filetree.FileTree, cache filetree.TreeCache) (controller *FileTreeController) { +func NewFileTreeController(name string, gui *gocui.Gui, tree *filetree.FileTree, refTrees []*filetree.FileTree, cache filetree.TreeCache) (controller *FileTreeController, err error) { controller = new(FileTreeController) // populate main fields controller.Name = name controller.gui = gui - controller.vm = NewFileTreeViewModel(tree, refTrees, cache) + controller.vm, err = NewFileTreeViewModel(tree, refTrees, cache) + if err != nil { + return nil, err + } - var err error controller.keybindingToggleCollapse, err = keybinding.ParseAll(viper.GetString("keybinding.toggle-collapse-dir")) if err != nil { logrus.Error(err) @@ -100,7 +102,7 @@ func NewFileTreeController(name string, gui *gocui.Gui, tree *filetree.FileTree, logrus.Error(err) } - return controller + return controller, err } // Setup initializes the UI concerns within the context of a global [gocui] view object. @@ -302,19 +304,15 @@ func (controller *FileTreeController) toggleAttributes() error { if err != nil { return err } - // we need to render the changes to the status pane as well - Update() - Render() - return nil + // we need to render the changes to the status pane as well (not just this contoller/view) + return UpdateAndRender() } // toggleShowDiffType will show/hide the selected DiffType in the filetree pane. func (controller *FileTreeController) toggleShowDiffType(diffType filetree.DiffType) error { controller.vm.toggleShowDiffType(diffType) - // we need to render the changes to the status pane as well - Update() - Render() - return nil + // we need to render the changes to the status pane as well (not just this contoller/view) + return UpdateAndRender() } // filterRegex will return a regular expression object to match the user's filter input. @@ -383,10 +381,13 @@ func (controller *FileTreeController) Render() error { // update the contents controller.view.Clear() - _ = controller.vm.Render() - _, _ = fmt.Fprint(controller.view, controller.vm.mainBuf.String()) + err := controller.vm.Render() + if err != nil { + return err + } + _, err = fmt.Fprint(controller.view, controller.vm.mainBuf.String()) - return nil + return err }) return nil } diff --git a/runtime/ui/filetree_viewmodel.go b/runtime/ui/filetree_viewmodel.go index 30009fd..18ebfdd 100644 --- a/runtime/ui/filetree_viewmodel.go +++ b/runtime/ui/filetree_viewmodel.go @@ -6,11 +6,9 @@ import ( "regexp" "strings" + "github.com/lunixbochs/vtclean" "github.com/sirupsen/logrus" "github.com/spf13/viper" - "github.com/wagoodman/dive/utils" - - "github.com/lunixbochs/vtclean" "github.com/wagoodman/dive/dive/filetree" ) @@ -36,7 +34,7 @@ type FileTreeViewModel struct { } // NewFileTreeController creates a new view object attached the the global [gocui] screen object. -func NewFileTreeViewModel(tree *filetree.FileTree, refTrees []*filetree.FileTree, cache filetree.TreeCache) (treeViewModel *FileTreeViewModel) { +func NewFileTreeViewModel(tree *filetree.FileTree, refTrees []*filetree.FileTree, cache filetree.TreeCache) (treeViewModel *FileTreeViewModel, err error) { treeViewModel = new(FileTreeViewModel) // populate main fields @@ -59,11 +57,11 @@ func NewFileTreeViewModel(tree *filetree.FileTree, refTrees []*filetree.FileTree case "unmodified": treeViewModel.HiddenDiffTypes[filetree.Unmodified] = true default: - utils.PrintAndExit(fmt.Sprintf("unknown diff.hide value: %s", t)) + return nil, fmt.Errorf("unknown diff.hide value: %s", t) } } - return treeViewModel + return treeViewModel, nil } // Setup initializes the UI concerns within the context of a global [gocui] view object. @@ -420,9 +418,17 @@ func (vm *FileTreeViewModel) Render() error { vm.mainBuf.Reset() for idx, line := range lines { if idx == vm.bufferIndex { - fmt.Fprintln(&vm.mainBuf, Formatting.Selected(vtclean.Clean(line, false))) + _, err := fmt.Fprintln(&vm.mainBuf, Formatting.Selected(vtclean.Clean(line, false))) + if err != nil { + logrus.Debug("unable to write to buffer: ", err) + return err + } } else { - fmt.Fprintln(&vm.mainBuf, line) + _, err := fmt.Fprintln(&vm.mainBuf, line) + if err != nil { + logrus.Debug("unable to write to buffer: ", err) + return err + } } } return nil diff --git a/runtime/ui/filter_controller.go b/runtime/ui/filter_controller.go index 7902052..bd740b3 100644 --- a/runtime/ui/filter_controller.go +++ b/runtime/ui/filter_controller.go @@ -3,6 +3,7 @@ package ui import ( "fmt" "github.com/jroimartin/gocui" + "github.com/sirupsen/logrus" ) // FilterController holds the UI objects and data models for populating the bottom row. Specifically the pane that @@ -99,9 +100,10 @@ func (controller *FilterController) Update() error { // Render flushes the state objects to the screen. Currently this is the users path filter input. func (controller *FilterController) Render() error { controller.gui.Update(func(g *gocui.Gui) error { - // render the header _, err := fmt.Fprintln(controller.header, Formatting.Header(controller.headerStr)) - + if err != nil { + logrus.Error("unable to write to buffer: ", err) + } return err }) return nil diff --git a/runtime/ui/layer_controller.go b/runtime/ui/layer_controller.go index ed7f259..b7c3740 100644 --- a/runtime/ui/layer_controller.go +++ b/runtime/ui/layer_controller.go @@ -9,7 +9,6 @@ import ( "github.com/lunixbochs/vtclean" "github.com/sirupsen/logrus" "github.com/spf13/viper" - "github.com/wagoodman/dive/utils" "github.com/wagoodman/keybinding" ) @@ -33,7 +32,7 @@ type LayerController struct { } // NewLayerController creates a new view object attached the the global [gocui] screen object. -func NewLayerController(name string, gui *gocui.Gui, layers []image.Layer) (controller *LayerController) { +func NewLayerController(name string, gui *gocui.Gui, layers []image.Layer) (controller *LayerController, err error) { controller = new(LayerController) // populate main fields @@ -47,10 +46,9 @@ func NewLayerController(name string, gui *gocui.Gui, layers []image.Layer) (cont case false: controller.CompareMode = CompareLayer default: - utils.PrintAndExit(fmt.Sprintf("unknown layer.show-aggregated-changes value: %v", mode)) + return nil, fmt.Errorf("unknown layer.show-aggregated-changes value: %v", mode) } - var err error controller.keybindingCompareAll, err = keybinding.ParseAll(viper.GetString("keybinding.compare-all")) if err != nil { logrus.Error(err) @@ -71,7 +69,7 @@ func NewLayerController(name string, gui *gocui.Gui, layers []image.Layer) (cont logrus.Error(err) } - return controller + return controller, err } // Setup initializes the UI concerns within the context of a global [gocui] view object. @@ -218,8 +216,11 @@ func (controller *LayerController) currentLayer() image.Layer { // setCompareMode switches the layer comparison between a single-layer comparison to an aggregated comparison. func (controller *LayerController) setCompareMode(compareMode CompareType) error { controller.CompareMode = compareMode - Update() - Render() + err := UpdateAndRender() + if err != nil { + logrus.Errorf("unable to set compare mode: %+v", err) + return err + } return Controllers.Tree.setTreeByLayer(controller.getCompareIndexes()) } @@ -283,7 +284,10 @@ func (controller *LayerController) Render() error { width, _ := g.Size() headerStr := fmt.Sprintf("[%s]%s\n", title, strings.Repeat("─", width*2)) headerStr += fmt.Sprintf("Cmp"+image.LayerFormat, "Size", "Command") - _, _ = fmt.Fprintln(controller.header, Formatting.Header(vtclean.Clean(headerStr, false))) + _, err := fmt.Fprintln(controller.header, Formatting.Header(vtclean.Clean(headerStr, false))) + if err != nil { + return err + } // update contents controller.view.Clear() @@ -295,9 +299,14 @@ func (controller *LayerController) Render() error { compareBar := controller.renderCompareBar(idx) if idx == controller.LayerIndex { - _, _ = fmt.Fprintln(controller.view, compareBar+" "+Formatting.Selected(layerStr)) + _, err = fmt.Fprintln(controller.view, compareBar+" "+Formatting.Selected(layerStr)) } else { - _, _ = fmt.Fprintln(controller.view, compareBar+" "+layerStr) + _, err = fmt.Fprintln(controller.view, compareBar+" "+layerStr) + } + + if err != nil { + logrus.Debug("unable to write to buffer: ", err) + return err } } diff --git a/runtime/ui/status_controller.go b/runtime/ui/status_controller.go index 2b5e933..8e590df 100644 --- a/runtime/ui/status_controller.go +++ b/runtime/ui/status_controller.go @@ -2,6 +2,7 @@ package ui import ( "fmt" + "github.com/sirupsen/logrus" "strings" @@ -61,11 +62,13 @@ func (controller *StatusController) Update() error { func (controller *StatusController) Render() error { controller.gui.Update(func(g *gocui.Gui) error { controller.view.Clear() - _, _ = fmt.Fprintln(controller.view, controller.KeyHelp()+Controllers.lookup[controller.gui.CurrentView().Name()].KeyHelp()+Formatting.StatusNormal("▏"+strings.Repeat(" ", 1000))) + _, err := fmt.Fprintln(controller.view, controller.KeyHelp()+Controllers.lookup[controller.gui.CurrentView().Name()].KeyHelp()+Formatting.StatusNormal("▏"+strings.Repeat(" ", 1000))) + if err != nil { + logrus.Debug("unable to write to buffer: ", err) + } - return nil + return err }) - // todo: blerg return nil } diff --git a/runtime/ui/ui.go b/runtime/ui/ui.go index 6f4e5ed..0e6c3d2 100644 --- a/runtime/ui/ui.go +++ b/runtime/ui/ui.go @@ -9,7 +9,6 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/viper" "github.com/wagoodman/dive/dive/filetree" - "github.com/wagoodman/dive/utils" "github.com/wagoodman/keybinding" ) @@ -72,6 +71,22 @@ type View interface { IsVisible() bool } +func UpdateAndRender() error { + err := Update() + if err != nil { + logrus.Debug("failed update: ", err) + return err + } + + err = Render() + if err != nil { + logrus.Debug("failed render: ", err) + return err + } + + return nil +} + // toggleView switches between the file view and the layer view and re-renders the screen. func toggleView(g *gocui.Gui, v *gocui.View) (err error) { if v == nil || v.Name() == Controllers.Layer.Name { @@ -79,9 +94,13 @@ func toggleView(g *gocui.Gui, v *gocui.View) (err error) { } else { _, err = g.SetCurrentView(Controllers.Layer.Name) } - Update() - Render() - return err + + if err != nil { + logrus.Error("unable to toggle view: ", err) + return err + } + + return UpdateAndRender() } // toggleFilterView shows/hides the file tree filter pane. @@ -95,22 +114,24 @@ func toggleFilterView(g *gocui.Gui, v *gocui.View) error { if !Controllers.Filter.hidden { _, err := g.SetCurrentView(Controllers.Filter.Name) if err != nil { + logrus.Error("unable to toggle filter view: ", err) return err } - Update() - Render() - } else { - err := toggleView(g, v) - if err != nil { - return err - } - - err = Controllers.Filter.view.SetCursor(0, 0) - if err != nil { - return err - } + return UpdateAndRender() } + err := toggleView(g, v) + if err != nil { + logrus.Error("unable to toggle filter view (back): ", err) + return err + } + + err = Controllers.Filter.view.SetCursor(0, 0) + if err != nil { + return err + } + + return nil } @@ -325,9 +346,10 @@ func layout(g *gocui.Gui) error { // Update refreshes the state objects for future rendering. func Update() error { - for _, view := range Controllers.lookup { - err := view.Update() + for _, controller := range Controllers.lookup { + err := controller.Update() if err != nil { + logrus.Debug("unable to update controller: ") return err } } @@ -336,9 +358,9 @@ func Update() error { // Render flushes the state objects to the screen. func Render() error { - for _, view := range Controllers.lookup { - if view.IsVisible() { - err := view.Render() + for _, controller := range Controllers.lookup { + if controller.IsVisible() { + err := controller.Render() if err != nil { return err } @@ -386,19 +408,24 @@ func Run(analysis *image.AnalysisResult, cache filetree.TreeCache) error { if err != nil { return err } - utils.SetUi(g) defer g.Close() Controllers.lookup = make(map[string]View) - Controllers.Layer = NewLayerController("side", g, analysis.Layers) + Controllers.Layer, err = NewLayerController("side", g, analysis.Layers) + if err != nil { + return err + } Controllers.lookup[Controllers.Layer.Name] = Controllers.Layer treeStack, err := filetree.StackTreeRange(analysis.RefTrees, 0, 0) if err != nil { return err } - Controllers.Tree = NewFileTreeController("main", g, treeStack, analysis.RefTrees, cache) + Controllers.Tree, err = NewFileTreeController("main", g, treeStack, analysis.RefTrees, cache) + if err != nil { + return err + } Controllers.lookup[Controllers.Tree.Name] = Controllers.Tree Controllers.Status = NewStatusController("status", g) @@ -421,12 +448,7 @@ func Run(analysis *image.AnalysisResult, cache filetree.TreeCache) error { // } // perform the first update and render now that all resources have been loaded - err = Update() - if err != nil { - return err - } - - err = Render() + err = UpdateAndRender() if err != nil { return err } diff --git a/utils/exit.go b/utils/exit.go deleted file mode 100644 index 512d37c..0000000 --- a/utils/exit.go +++ /dev/null @@ -1,34 +0,0 @@ -package utils - -import ( - "fmt" - "os" - - "github.com/jroimartin/gocui" - "github.com/sirupsen/logrus" -) - -var ui *gocui.Gui - -func SetUi(g *gocui.Gui) { - ui = g -} - -func PrintAndExit(args ...interface{}) { - logrus.Println(args...) - Cleanup() - fmt.Println(args...) - os.Exit(1) -} - -// Note: this should only be used when exiting from non-gocui code -func Exit(rc int) { - Cleanup() - os.Exit(rc) -} - -func Cleanup() { - if ui != nil { - ui.Close() - } -} From a31073206021a424b13ef1398f75c8ff88157fc9 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 7 Oct 2019 12:09:31 -0400 Subject: [PATCH 10/23] add shared layer type (instead of interface) --- dive/image/analyzer.go | 2 +- dive/image/docker/image_archive.go | 5 +-- dive/image/docker/layer.go | 54 ++++------------------------ dive/image/image.go | 6 ++-- dive/image/layer.go | 38 +++++++++++++++----- dive/image/podman/image_directory.go | 7 ++-- dive/image/podman/layer.go | 47 +++++++----------------- runtime/export/export.go | 46 +++++++----------------- runtime/export/export_test.go | 2 +- runtime/export/file_reference.go | 7 ++++ runtime/export/image.go | 8 +++++ runtime/export/layer.go | 8 +++++ runtime/ui/details_controller.go | 4 +-- runtime/ui/layer_controller.go | 8 ++--- 14 files changed, 104 insertions(+), 138 deletions(-) create mode 100644 runtime/export/file_reference.go create mode 100644 runtime/export/image.go create mode 100644 runtime/export/layer.go diff --git a/dive/image/analyzer.go b/dive/image/analyzer.go index 90a8689..58b8da8 100644 --- a/dive/image/analyzer.go +++ b/dive/image/analyzer.go @@ -9,7 +9,7 @@ type Analyzer interface { } type AnalysisResult struct { - Layers []Layer + Layers []*Layer RefTrees []*filetree.FileTree Efficiency float64 SizeBytes uint64 diff --git a/dive/image/docker/image_archive.go b/dive/image/docker/image_archive.go index 5c4b001..106bd4a 100644 --- a/dive/image/docker/image_archive.go +++ b/dive/image/docker/image_archive.go @@ -147,7 +147,7 @@ func (img *ImageArchive) ToImage() (*image.Image, error) { } // build the layers array - layers := make([]image.Layer, len(trees)) + layers := make([]*image.Layer, len(trees)) // note that the resolver config stores images in reverse chronological order, so iterate backwards through layers // as you iterate chronologically through history (ignoring history items that have no layer contents) @@ -174,11 +174,12 @@ func (img *ImageArchive) ToImage() (*image.Image, error) { historyObj.Size = tree.FileSize - layers[layerIdx] = &layer{ + dockerLayer := layer{ history: historyObj, index: tarPathIdx, tree: trees[layerIdx], } + layers[layerIdx] = dockerLayer.ToLayer() tarPathIdx++ } diff --git a/dive/image/docker/layer.go b/dive/image/docker/layer.go index 9c423b5..c7581d9 100644 --- a/dive/image/docker/layer.go +++ b/dive/image/docker/layer.go @@ -1,11 +1,9 @@ package docker import ( - "fmt" "github.com/wagoodman/dive/dive/image" "strings" - "github.com/dustin/go-humanize" "github.com/wagoodman/dive/dive/filetree" ) @@ -16,52 +14,14 @@ type layer struct { tree *filetree.FileTree } -// ShortId returns the truncated id of the current layer. -func (l *layer) Id() string { - return l.history.ID -} - -// index returns the relative position of the layer within the image. -func (l *layer) Index() int { - return l.index -} - -// Size returns the number of bytes that this image is. -func (l *layer) Size() uint64 { - return l.history.Size -} - -// Tree returns the file tree representing the current layer. -func (l *layer) Tree() *filetree.FileTree { - return l.tree -} - -// ShortId returns the truncated id of the current layer. -func (l *layer) Command() string { - return strings.TrimPrefix(l.history.CreatedBy, "/bin/sh -c ") -} - -// ShortId returns the truncated id of the current layer. -func (l *layer) ShortId() string { - rangeBound := 15 - id := l.Id() - if length := len(id); length < 15 { - rangeBound = length - } - id = id[0:rangeBound] - - return id -} // String represents a layer in a columnar format. -func (l *layer) String() string { - - if l.index == 0 { - return fmt.Sprintf(image.LayerFormat, - humanize.Bytes(l.Size()), - "FROM "+l.ShortId()) +func (l *layer) ToLayer() *image.Layer { + return &image.Layer{ + Id: l.history.ID, + Index: l.index, + Command: strings.TrimPrefix(l.history.CreatedBy, "/bin/sh -c "), + Size: l.history.Size, + Tree: l.tree, } - return fmt.Sprintf(image.LayerFormat, - humanize.Bytes(l.Size()), - l.Command()) } diff --git a/dive/image/image.go b/dive/image/image.go index 66ed49a..e875efa 100644 --- a/dive/image/image.go +++ b/dive/image/image.go @@ -6,7 +6,7 @@ import ( type Image struct { Trees []*filetree.FileTree - Layers []Layer + Layers []*Layer } func (img *Image) Analyze() (*AnalysisResult, error) { @@ -15,9 +15,9 @@ func (img *Image) Analyze() (*AnalysisResult, error) { var sizeBytes, userSizeBytes uint64 for i, v := range img.Layers { - sizeBytes += v.Size() + sizeBytes += v.Size if i != 0 { - userSizeBytes += v.Size() + userSizeBytes += v.Size } } diff --git a/dive/image/layer.go b/dive/image/layer.go index 71e48e0..b92eabe 100644 --- a/dive/image/layer.go +++ b/dive/image/layer.go @@ -1,6 +1,8 @@ package image import ( + "fmt" + "github.com/dustin/go-humanize" "github.com/wagoodman/dive/dive/filetree" ) @@ -8,12 +10,32 @@ const ( LayerFormat = "%7s %s" ) -type Layer interface { - Id() string - ShortId() string - Index() int - Command() string - Size() uint64 - Tree() *filetree.FileTree - String() string +type Layer struct { + Id string + Index int + Command string + Size uint64 + Tree *filetree.FileTree +} + +func (l *Layer) ShortId() string { + rangeBound := 15 + id := l.Id + if length := len(id); length < 15 { + rangeBound = length + } + id = id[0:rangeBound] + + return id +} + +func (l *Layer) String() string { + if l.Index == 0 { + return fmt.Sprintf(LayerFormat, + humanize.Bytes(l.Size), + "FROM "+l.ShortId()) + } + return fmt.Sprintf(LayerFormat, + humanize.Bytes(l.Size), + l.Command) } diff --git a/dive/image/podman/image_directory.go b/dive/image/podman/image_directory.go index 8e551a8..4e2d1d5 100644 --- a/dive/image/podman/image_directory.go +++ b/dive/image/podman/image_directory.go @@ -110,17 +110,18 @@ func (img *ImageDirectoryRef) ToImage() (*image.Image, error) { return nil, fmt.Errorf("could not find '%s' in parsed trees", id) } - layers := make([]image.Layer, len(trees)) + layers := make([]*image.Layer, len(trees)) // note that the resolver config stores images in reverse chronological order, so iterate backwards through layers // as you iterate chronologically through history (ignoring history items that have no layer contents) - // Note: history is not required metadata in a docker image! + // Note: history is not required metadata in an OCI image! for layerIdx, id := range img.layerOrder { - layers[layerIdx] = &layer{ + podmanLayer := layer{ obj: img.layerMap[id], index: layerIdx, tree: trees[layerIdx], } + layers[layerIdx] = podmanLayer.ToLayer() } return &image.Image{ diff --git a/dive/image/podman/layer.go b/dive/image/podman/layer.go index 1323fcc..de173a5 100644 --- a/dive/image/podman/layer.go +++ b/dive/image/podman/layer.go @@ -1,9 +1,7 @@ package podman import ( - "fmt" podmanImage "github.com/containers/libpod/libpod/image" - "github.com/dustin/go-humanize" "github.com/wagoodman/dive/dive/filetree" "github.com/wagoodman/dive/dive/image" "strings" @@ -16,36 +14,19 @@ type layer struct { tree *filetree.FileTree } -// ShortId returns the truncated id of the current layer. -func (l *layer) Id() string { - return l.obj.ID() -} - -// index returns the relative position of the layer within the image. -func (l *layer) Index() int { - return l.index -} - -// Size returns the number of bytes that this image is. -func (l *layer) Size() uint64 { - return uint64(l.obj.ImageData.Size) -} - -// Tree returns the file tree representing the current layer. -func (l *layer) Tree() *filetree.FileTree { - return l.tree -} - // ShortId returns the truncated id of the current layer. func (l *layer) Command() string { - // todo: is 0 right? - return strings.TrimPrefix(l.obj.ImageData.History[0].CreatedBy, "/bin/sh -c ") + if len(l.obj.ImageData.History) > 0 { + hist := l.obj.ImageData.History + return strings.TrimPrefix(hist[len(hist)-1].CreatedBy, "/bin/sh -c ") + } + return "unknown" } // ShortId returns the truncated id of the current layer. func (l *layer) ShortId() string { rangeBound := 15 - id := l.Id() + id := l.obj.ID() if length := len(id); length < 15 { rangeBound = length } @@ -55,14 +36,12 @@ func (l *layer) ShortId() string { } // String represents a layer in a columnar format. -func (l *layer) String() string { - - if l.index == 0 { - return fmt.Sprintf(image.LayerFormat, - humanize.Bytes(l.Size()), - "FROM "+l.ShortId()) +func (l *layer) ToLayer() *image.Layer { + return &image.Layer{ + Id: l.obj.ID(), + Index: l.index, + Command: l.Command(), + Size: uint64(l.obj.ImageData.Size), + Tree: l.tree, } - return fmt.Sprintf(image.LayerFormat, - humanize.Bytes(l.Size()), - l.Command()) } diff --git a/runtime/export/export.go b/runtime/export/export.go index 58aa095..f94ac8b 100644 --- a/runtime/export/export.go +++ b/runtime/export/export.go @@ -2,50 +2,30 @@ package export import ( "encoding/json" - "github.com/wagoodman/dive/dive/image" + diveImage "github.com/wagoodman/dive/dive/image" "io/ioutil" ) type export struct { - Layer []exportLayer `json:"layer"` - Image exportImage `json:"image"` + Layer []layer `json:"layer"` + Image image `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 []exportReferenceFile `json:"exportReferenceFile"` -} - -type exportReferenceFile struct { - References int `json:"count"` - SizeBytes uint64 `json:"sizeBytes"` - Path string `json:"file"` -} - -func NewExport(analysis *image.AnalysisResult) *export { +func NewExport(analysis *diveImage.AnalysisResult) *export { data := export{} - data.Layer = make([]exportLayer, len(analysis.Layers)) - data.Image.InefficientFiles = make([]exportReferenceFile, len(analysis.Inefficiencies)) + data.Layer = make([]layer, len(analysis.Layers)) + data.Image.InefficientFiles = make([]fileReference, len(analysis.Inefficiencies)) // export layers in order for revIdx := len(analysis.Layers) - 1; revIdx >= 0; revIdx-- { - layer := analysis.Layers[revIdx] + curLayer := analysis.Layers[revIdx] idx := (len(analysis.Layers) - 1) - revIdx - data.Layer[idx] = exportLayer{ - Index: layer.Index(), - DigestID: layer.Id(), - SizeBytes: layer.Size(), - Command: layer.Command(), + data.Layer[idx] = layer{ + Index: curLayer.Index, + DigestID: curLayer.Id, + SizeBytes: curLayer.Size, + Command: curLayer.Command, } } @@ -56,7 +36,7 @@ func NewExport(analysis *image.AnalysisResult) *export { for idx := 0; idx < len(analysis.Inefficiencies); idx++ { fileData := analysis.Inefficiencies[len(analysis.Inefficiencies)-1-idx] - data.Image.InefficientFiles[idx] = exportReferenceFile{ + data.Image.InefficientFiles[idx] = fileReference{ References: len(fileData.Nodes), SizeBytes: uint64(fileData.CumulativeSize), Path: fileData.Path, diff --git a/runtime/export/export_test.go b/runtime/export/export_test.go index e3750ed..c7cf97d 100644 --- a/runtime/export/export_test.go +++ b/runtime/export/export_test.go @@ -108,7 +108,7 @@ func Test_Export(t *testing.T) { "sizeBytes": 1220598, "inefficientBytes": 32025, "efficiencyScore": 0.9844212134184309, - "exportReferenceFile": [ + "fileReference": [ { "count": 2, "sizeBytes": 12810, diff --git a/runtime/export/file_reference.go b/runtime/export/file_reference.go new file mode 100644 index 0000000..14aac0e --- /dev/null +++ b/runtime/export/file_reference.go @@ -0,0 +1,7 @@ +package export + +type fileReference struct { + References int `json:"count"` + SizeBytes uint64 `json:"sizeBytes"` + Path string `json:"file"` +} diff --git a/runtime/export/image.go b/runtime/export/image.go new file mode 100644 index 0000000..2052371 --- /dev/null +++ b/runtime/export/image.go @@ -0,0 +1,8 @@ +package export + +type image struct { + SizeBytes uint64 `json:"sizeBytes"` + InefficientBytes uint64 `json:"inefficientBytes"` + EfficiencyScore float64 `json:"efficiencyScore"` + InefficientFiles []fileReference `json:"fileReference"` +} diff --git a/runtime/export/layer.go b/runtime/export/layer.go new file mode 100644 index 0000000..b360f48 --- /dev/null +++ b/runtime/export/layer.go @@ -0,0 +1,8 @@ +package export + +type layer struct { + Index int `json:"index"` + DigestID string `json:"digestId"` + SizeBytes uint64 `json:"sizeBytes"` + Command string `json:"command"` +} diff --git a/runtime/ui/details_controller.go b/runtime/ui/details_controller.go index a9ec51c..f1d9c5b 100644 --- a/runtime/ui/details_controller.go +++ b/runtime/ui/details_controller.go @@ -131,9 +131,9 @@ func (controller *DetailsController) Render() error { controller.view.Clear() var lines = make([]string, 0) - lines = append(lines, Formatting.Header("Digest: ")+currentLayer.Id()) + lines = append(lines, Formatting.Header("Digest: ")+currentLayer.Id) lines = append(lines, Formatting.Header("Command:")) - lines = append(lines, currentLayer.Command()) + lines = append(lines, currentLayer.Command) lines = append(lines, "\n"+Formatting.Header(vtclean.Clean(imageHeaderStr, false))) lines = append(lines, imageSizeStr) lines = append(lines, wastedSpaceStr) diff --git a/runtime/ui/layer_controller.go b/runtime/ui/layer_controller.go index b7c3740..b1868c9 100644 --- a/runtime/ui/layer_controller.go +++ b/runtime/ui/layer_controller.go @@ -20,7 +20,7 @@ type LayerController struct { view *gocui.View header *gocui.View LayerIndex int - Layers []image.Layer + Layers []*image.Layer CompareMode CompareType CompareStartIndex int ImageSize uint64 @@ -32,7 +32,7 @@ type LayerController struct { } // NewLayerController creates a new view object attached the the global [gocui] screen object. -func NewLayerController(name string, gui *gocui.Gui, layers []image.Layer) (controller *LayerController, err error) { +func NewLayerController(name string, gui *gocui.Gui, layers []*image.Layer) (controller *LayerController, err error) { controller = new(LayerController) // populate main fields @@ -209,7 +209,7 @@ func (controller *LayerController) SetCursor(layer int) error { } // currentLayer returns the Layer object currently selected. -func (controller *LayerController) currentLayer() image.Layer { +func (controller *LayerController) currentLayer() *image.Layer { return controller.Layers[(len(controller.Layers)-1)-controller.LayerIndex] } @@ -262,7 +262,7 @@ func (controller *LayerController) renderCompareBar(layerIdx int) string { func (controller *LayerController) Update() error { controller.ImageSize = 0 for idx := 0; idx < len(controller.Layers); idx++ { - controller.ImageSize += controller.Layers[idx].Size() + controller.ImageSize += controller.Layers[idx].Size } return nil } From 2712c1017e0a6d82ca2e1b48044bb1189faf7634 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 7 Oct 2019 12:41:57 -0400 Subject: [PATCH 11/23] remove reverse iteration for layer display and processing --- dive/image/docker/image_archive.go | 16 ++++++---------- dive/image/podman/image_directory.go | 28 ++++++++++++---------------- dive/image/podman/layer.go | 10 +++++++--- runtime/ui/layer_controller.go | 4 +--- 4 files changed, 26 insertions(+), 32 deletions(-) diff --git a/dive/image/docker/image_archive.go b/dive/image/docker/image_archive.go index 106bd4a..8d93301 100644 --- a/dive/image/docker/image_archive.go +++ b/dive/image/docker/image_archive.go @@ -147,16 +147,13 @@ func (img *ImageArchive) ToImage() (*image.Image, error) { } // build the layers array - layers := make([]*image.Layer, len(trees)) + layers := make([]*image.Layer, 0) // note that the resolver config stores images in reverse chronological order, so iterate backwards through layers // as you iterate chronologically through history (ignoring history items that have no layer contents) // Note: history is not required metadata in a docker image! - tarPathIdx := 0 histIdx := 0 - for layerIdx := len(trees) - 1; layerIdx >= 0; layerIdx-- { - tree := trees[(len(trees)-1)-layerIdx] - + for idx, tree := range trees { // ignore empty layers, we are only observing layers with content historyObj := historyEntry{ CreatedBy: "(missing)", @@ -176,14 +173,13 @@ func (img *ImageArchive) ToImage() (*image.Image, error) { dockerLayer := layer{ history: historyObj, - index: tarPathIdx, - tree: trees[layerIdx], + index: idx, + tree: tree, } - layers[layerIdx] = dockerLayer.ToLayer() - - tarPathIdx++ + layers = append(layers, dockerLayer.ToLayer()) } + return &image.Image{ Trees: trees, Layers: layers, diff --git a/dive/image/podman/image_directory.go b/dive/image/podman/image_directory.go index 4e2d1d5..333e967 100644 --- a/dive/image/podman/image_directory.go +++ b/dive/image/podman/image_directory.go @@ -99,29 +99,25 @@ func processLayer(name, rootDir string) (*filetree.FileTree, error) { func (img *ImageDirectoryRef) ToImage() (*image.Image, error) { trees := make([]*filetree.FileTree, 0) + layers := make([]*image.Layer, 0) + // build the content tree - // todo: this isn't needed! - for _, id := range img.layerOrder { - tr, exists := img.treeMap[id] - if exists { - trees = append(trees, tr) - continue - } - return nil, fmt.Errorf("could not find '%s' in parsed trees", id) - } - - layers := make([]*image.Layer, len(trees)) - - // note that the resolver config stores images in reverse chronological order, so iterate backwards through layers - // as you iterate chronologically through history (ignoring history items that have no layer contents) - // Note: history is not required metadata in an OCI image! for layerIdx, id := range img.layerOrder { + tr, exists := img.treeMap[id] + if !exists { + return nil, fmt.Errorf("could not find '%s' in parsed trees", id) + } + trees = append(trees, tr) + + // note that the resolver config stores images in reverse chronological order, so iterate backwards through layers + // as you iterate chronologically through history (ignoring history items that have no layer contents) + // Note: history is not required metadata in an OCI image! podmanLayer := layer{ obj: img.layerMap[id], index: layerIdx, tree: trees[layerIdx], } - layers[layerIdx] = podmanLayer.ToLayer() + layers = append(layers, podmanLayer.ToLayer()) } return &image.Image{ diff --git a/dive/image/podman/layer.go b/dive/image/podman/layer.go index de173a5..daad3d3 100644 --- a/dive/image/podman/layer.go +++ b/dive/image/podman/layer.go @@ -1,6 +1,7 @@ package podman import ( + "context" podmanImage "github.com/containers/libpod/libpod/image" "github.com/wagoodman/dive/dive/filetree" "github.com/wagoodman/dive/dive/image" @@ -16,9 +17,12 @@ type layer struct { // ShortId returns the truncated id of the current layer. func (l *layer) Command() string { - if len(l.obj.ImageData.History) > 0 { - hist := l.obj.ImageData.History - return strings.TrimPrefix(hist[len(hist)-1].CreatedBy, "/bin/sh -c ") + history, err := l.obj.History(context.TODO()) + if err != nil { + return "error: " + err.Error() + } + if len(history) > 0 { + return strings.TrimPrefix(history[0].CreatedBy, "/bin/sh -c ") } return "unknown" } diff --git a/runtime/ui/layer_controller.go b/runtime/ui/layer_controller.go index b1868c9..56cce36 100644 --- a/runtime/ui/layer_controller.go +++ b/runtime/ui/layer_controller.go @@ -291,9 +291,7 @@ func (controller *LayerController) Render() error { // update contents controller.view.Clear() - for revIdx := len(controller.Layers) - 1; revIdx >= 0; revIdx-- { - layer := controller.Layers[revIdx] - idx := (len(controller.Layers) - 1) - revIdx + for idx, layer := range controller.Layers { layerStr := layer.String() compareBar := controller.renderCompareBar(idx) From 7f786eb725c3d8e774d888fe7a45ce8ce5e76439 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 7 Oct 2019 13:57:59 -0400 Subject: [PATCH 12/23] cleanup layer id/digest & add tags support --- dive/filetree/file_info.go | 21 +++++++++------- dive/image/docker/layer.go | 6 ++++- dive/image/layer.go | 2 ++ dive/image/podman/image_directory.go | 6 ++--- dive/image/podman/layer.go | 37 +++++++++++++++++++++++----- runtime/ui/details_controller.go | 8 +++++- runtime/ui/layer_controller.go | 2 +- 7 files changed, 60 insertions(+), 22 deletions(-) diff --git a/dive/filetree/file_info.go b/dive/filetree/file_info.go index 603deaf..4ea3887 100644 --- a/dive/filetree/file_info.go +++ b/dive/filetree/file_info.go @@ -46,12 +46,23 @@ func NewFileInfo(realPath, path string, info os.FileInfo) FileInfo { // todo: don't use tar types here, create our own... var fileType byte + var linkName string + var size int64 + if info.Mode() & os.ModeSymlink != 0 { fileType = tar.TypeSymlink + + linkName, err = os.Readlink(realPath) + if err != nil { + logrus.Panic("unable to read link:", realPath, err) + } + } else if info.IsDir() { fileType = tar.TypeDir } else { fileType = tar.TypeReg + + size = info.Size() } var hash uint64 @@ -64,20 +75,12 @@ func NewFileInfo(realPath, path string, info os.FileInfo) FileInfo { hash = getHashFromReader(file) } - var linkName string - if fileType == tar.TypeSymlink { - linkName, err = os.Readlink(realPath) - if err != nil { - logrus.Panic("unable to read link:", realPath, err) - } - } - return FileInfo{ Path: path, TypeFlag: fileType, Linkname: linkName, hash: hash, - Size: info.Size(), + Size: size, Mode: info.Mode(), // todo: support UID/GID Uid: -1, diff --git a/dive/image/docker/layer.go b/dive/image/docker/layer.go index c7581d9..4e46933 100644 --- a/dive/image/docker/layer.go +++ b/dive/image/docker/layer.go @@ -17,11 +17,15 @@ type layer struct { // String represents a layer in a columnar format. func (l *layer) ToLayer() *image.Layer { + id := strings.Split(l.tree.Name, "/")[0] return &image.Layer{ - Id: l.history.ID, + Id: id, Index: l.index, Command: strings.TrimPrefix(l.history.CreatedBy, "/bin/sh -c "), Size: l.history.Size, Tree: l.tree, + // todo: query docker api for tags + Names: []string{"(unavailable)"}, + Digest: l.history.ID, } } diff --git a/dive/image/layer.go b/dive/image/layer.go index b92eabe..70354ad 100644 --- a/dive/image/layer.go +++ b/dive/image/layer.go @@ -16,6 +16,8 @@ type Layer struct { Command string Size uint64 Tree *filetree.FileTree + Names []string + Digest string } func (l *Layer) ShortId() string { diff --git a/dive/image/podman/image_directory.go b/dive/image/podman/image_directory.go index 333e967..9cad70c 100644 --- a/dive/image/podman/image_directory.go +++ b/dive/image/podman/image_directory.go @@ -28,9 +28,6 @@ func NewImageDirectoryRef(img *podmanImage.Image) (*ImageDirectoryRef, error) { curImg := img for { - // h, _ := img.History(ctx) - // fmt.Printf("%+v %+v %+v\n", img.ID(), h[0].Size, h[0].CreatedBy) - driver, err := curImg.DriverData() if err != nil { return nil, fmt.Errorf("graph driver error: %+v", err) @@ -79,7 +76,8 @@ func processLayer(name, rootDir string) (*filetree.FileTree, error) { return err } // add this file to the tree... - fileInfo := filetree.NewFileInfo(path, "/"+strings.TrimPrefix(path, rootDir), info) + relativeImagePath := "/"+strings.TrimPrefix(strings.TrimPrefix(path, rootDir), "/") + fileInfo := filetree.NewFileInfo(path, relativeImagePath, info) tree.FileSize += uint64(fileInfo.Size) diff --git a/dive/image/podman/layer.go b/dive/image/podman/layer.go index daad3d3..b59eab5 100644 --- a/dive/image/podman/layer.go +++ b/dive/image/podman/layer.go @@ -2,6 +2,7 @@ package podman import ( "context" + "fmt" podmanImage "github.com/containers/libpod/libpod/image" "github.com/wagoodman/dive/dive/filetree" "github.com/wagoodman/dive/dive/image" @@ -11,20 +12,42 @@ import ( // Layer represents a Docker image layer and metadata type layer struct { obj *podmanImage.Image + history *podmanImage.History index int tree *filetree.FileTree } +func (l *layer) getHistory() (*podmanImage.History, error) { + if l.history != nil { + return l.history, nil + } + history, err := l.obj.History(context.TODO()) + if err != nil { + return nil, err + } + if len(history) > 0 { + l.history = history[0] + return history[0], nil + } + return nil, fmt.Errorf("could not find history") +} + +func (l *layer) Size() uint64 { + history, err := l.getHistory() + if err != nil { + // todo: what should be done here??? + panic(err) + } + return uint64(history.Size) +} + // ShortId returns the truncated id of the current layer. func (l *layer) Command() string { - history, err := l.obj.History(context.TODO()) + history, err := l.getHistory() if err != nil { return "error: " + err.Error() } - if len(history) > 0 { - return strings.TrimPrefix(history[0].CreatedBy, "/bin/sh -c ") - } - return "unknown" + return strings.TrimPrefix(history.CreatedBy, "/bin/sh -c ") } // ShortId returns the truncated id of the current layer. @@ -45,7 +68,9 @@ func (l *layer) ToLayer() *image.Layer { Id: l.obj.ID(), Index: l.index, Command: l.Command(), - Size: uint64(l.obj.ImageData.Size), + Size: l.Size(), Tree: l.tree, + Names: l.obj.Names(), + Digest: l.obj.Digest().String(), } } diff --git a/runtime/ui/details_controller.go b/runtime/ui/details_controller.go index f1d9c5b..61ca251 100644 --- a/runtime/ui/details_controller.go +++ b/runtime/ui/details_controller.go @@ -131,7 +131,13 @@ func (controller *DetailsController) Render() error { controller.view.Clear() var lines = make([]string, 0) - lines = append(lines, Formatting.Header("Digest: ")+currentLayer.Id) + if currentLayer.Names != nil && len(currentLayer.Names) > 0 { + lines = append(lines, Formatting.Header("Tags: ")+strings.Join(currentLayer.Names, ", ")) + } else { + lines = append(lines, Formatting.Header("Tags: ")+ "(none)") + } + lines = append(lines, Formatting.Header("Id: ")+currentLayer.Id) + lines = append(lines, Formatting.Header("Digest: ")+currentLayer.Digest) lines = append(lines, Formatting.Header("Command:")) lines = append(lines, currentLayer.Command) lines = append(lines, "\n"+Formatting.Header(vtclean.Clean(imageHeaderStr, false))) diff --git a/runtime/ui/layer_controller.go b/runtime/ui/layer_controller.go index 56cce36..5695bb5 100644 --- a/runtime/ui/layer_controller.go +++ b/runtime/ui/layer_controller.go @@ -210,7 +210,7 @@ func (controller *LayerController) SetCursor(layer int) error { // currentLayer returns the Layer object currently selected. func (controller *LayerController) currentLayer() *image.Layer { - return controller.Layers[(len(controller.Layers)-1)-controller.LayerIndex] + return controller.Layers[controller.LayerIndex] } // setCompareMode switches the layer comparison between a single-layer comparison to an aggregated comparison. From 4714aca12302324254969fc70c3f9410767a9051 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 7 Oct 2019 17:44:52 -0400 Subject: [PATCH 13/23] fix tests --- dive/image/docker/testing.go | 8 ++++++-- dive/image/image.go | 5 ++--- runtime/export/export.go | 25 +++++++++++++------------ runtime/export/export_test.go | 20 +++++++++++++++++++- runtime/export/layer.go | 1 + runtime/ui/filetree_viewmodel_test.go | 8 ++++++-- 6 files changed, 47 insertions(+), 20 deletions(-) diff --git a/dive/image/docker/testing.go b/dive/image/docker/testing.go index 423d8ad..f6d1777 100644 --- a/dive/image/docker/testing.go +++ b/dive/image/docker/testing.go @@ -12,8 +12,12 @@ func TestLoadDockerImageTar(tarPath string) (*image.AnalysisResult, error) { } defer f.Close() - resolver := NewResolver() - img, err := resolver.Fetch("dive-test:latest") + archive, err := NewImageArchive(f) + if err != nil { + return nil, err + } + + img, err := archive.ToImage() if err != nil { return nil, err } diff --git a/dive/image/image.go b/dive/image/image.go index e875efa..de72c7b 100644 --- a/dive/image/image.go +++ b/dive/image/image.go @@ -22,9 +22,8 @@ func (img *Image) Analyze() (*AnalysisResult, error) { } var wastedBytes uint64 - for idx := 0; idx < len(inefficiencies); idx++ { - fileData := inefficiencies[len(inefficiencies)-1-idx] - wastedBytes += uint64(fileData.CumulativeSize) + for _, file := range inefficiencies { + wastedBytes += uint64(file.CumulativeSize) } return &AnalysisResult{ diff --git a/runtime/export/export.go b/runtime/export/export.go index f94ac8b..44e09d7 100644 --- a/runtime/export/export.go +++ b/runtime/export/export.go @@ -12,27 +12,28 @@ type export struct { } func NewExport(analysis *diveImage.AnalysisResult) *export { - data := export{} - data.Layer = make([]layer, len(analysis.Layers)) - data.Image.InefficientFiles = make([]fileReference, len(analysis.Inefficiencies)) + data := export{ + Layer: make([]layer, len(analysis.Layers)), + Image: image { + InefficientFiles: make([]fileReference, len(analysis.Inefficiencies)), + SizeBytes: analysis.SizeBytes, + EfficiencyScore: analysis.Efficiency, + InefficientBytes: analysis.WastedBytes, + }, + } // export layers in order - for revIdx := len(analysis.Layers) - 1; revIdx >= 0; revIdx-- { - curLayer := analysis.Layers[revIdx] - idx := (len(analysis.Layers) - 1) - revIdx - + for idx, curLayer := range analysis.Layers { data.Layer[idx] = layer{ Index: curLayer.Index, - DigestID: curLayer.Id, + ID: curLayer.Id, + DigestID: curLayer.Digest, SizeBytes: curLayer.Size, Command: curLayer.Command, } } - data.Image.SizeBytes = analysis.SizeBytes - data.Image.EfficiencyScore = analysis.Efficiency - data.Image.InefficientBytes = analysis.WastedBytes - + // add file references for idx := 0; idx < len(analysis.Inefficiencies); idx++ { fileData := analysis.Inefficiencies[len(analysis.Inefficiencies)-1-idx] diff --git a/runtime/export/export_test.go b/runtime/export/export_test.go index c7cf97d..78cd2bc 100644 --- a/runtime/export/export_test.go +++ b/runtime/export/export_test.go @@ -1,6 +1,7 @@ package export import ( + "github.com/sergi/go-diff/diffmatchpatch" "github.com/wagoodman/dive/dive/image/docker" "testing" ) @@ -21,84 +22,98 @@ func Test_Export(t *testing.T) { "layer": [ { "index": 0, + "id": "28cfe03618aa2e914e81fdd90345245c15f4478e35252c06ca52d238fd3cc694", "digestId": "sha256:23bc2b70b2014dec0ac22f27bb93e9babd08cdd6f1115d0c955b9ff22b382f5a", "sizeBytes": 1154361, "command": "#(nop) ADD file:ce026b62356eec3ad1214f92be2c9dc063fe205bd5e600be3492c4dfb17148bd in / " }, { "index": 1, + "id": "1871059774abe6914075e4a919b778fa1561f577d620ae52438a9635e6241936", "digestId": "sha256:a65b7d7ac139a0e4337bc3c73ce511f937d6140ef61a0108f7d4b8aab8d67274", "sizeBytes": 6405, "command": "#(nop) ADD file:139c3708fb6261126453e34483abd8bf7b26ed16d952fd976994d68e72d93be2 in /somefile.txt " }, { "index": 2, + "id": "49fe2a475548bfa4d493fc796fce41f30704e3d4cbff3e45dd3e06f463236d1d", "digestId": "sha256:93e208d471756ffbac88cf9c25feb442007f221d3bd73231e27b747a0a68927c", "sizeBytes": 0, "command": "mkdir -p /root/example/really/nested" }, { "index": 3, + "id": "80cd2ca1ffc89962b9349c80280c2bc551acbd11e09b16badb0669f8e2369020", "digestId": "sha256:4abad3abe3cb99ad7a492a9d9f6b3d66287c1646843c74128bbbec4f7be5aa9e", "sizeBytes": 6405, "command": "cp /somefile.txt /root/example/somefile1.txt" }, { "index": 4, + "id": "c99e2f8d3f6282668f0d30dc1db5e67a51d7a1dcd7ff6ddfa0f90760836778ec", "digestId": "sha256:14c9a6ffcb6a0f32d1035f97373b19608e2d307961d8be156321c3f1c1504cbf", "sizeBytes": 6405, "command": "chmod 444 /root/example/somefile1.txt" }, { "index": 5, + "id": "5eca617bdc3bc06134fe957a30da4c57adb7c340a6d749c8edc4c15861c928d7", "digestId": "sha256:778fb5770ef466f314e79cc9dc418eba76bfc0a64491ce7b167b76aa52c736c4", "sizeBytes": 6405, "command": "cp /somefile.txt /root/example/somefile2.txt" }, { "index": 6, + "id": "f07c3eb887572395408f8e11a07af945e4da5f02b3188bb06b93fad713ca0b99", "digestId": "sha256:f275b8a31a71deb521cc048e6021e2ff6fa52bedb25c9b7bbe129a0195ddca5f", "sizeBytes": 6405, "command": "cp /somefile.txt /root/example/somefile3.txt" }, { "index": 7, + "id": "461885fc22589158dee3c5b9f01cc41c87805439f58b4399d733b51aa305cbf9", "digestId": "sha256:dd1effc5eb19894c3e9b57411c98dd1cf30fa1de4253c7fae53c9cea67267d83", "sizeBytes": 6405, "command": "mv /root/example/somefile3.txt /root/saved.txt" }, { "index": 8, + "id": "a10327f68ffed4afcba78919052809a8f774978a6b87fc117d39c53c4842f72c", "digestId": "sha256:8d1869a0a066cdd12e48d648222866e77b5e2814f773bb3bd8774ab4052f0f1d", "sizeBytes": 6405, "command": "cp /root/saved.txt /root/.saved.txt" }, { "index": 9, + "id": "f2fc54e25cb7966dc9732ec671a77a1c5c104e732bd15ad44a2dc1ac42368f84", "digestId": "sha256:bc2e36423fa31a97223fd421f22c35466220fa160769abf697b8eb58c896b468", "sizeBytes": 0, "command": "rm -rf /root/example/" }, { "index": 10, + "id": "aad36d0b05e71c7e6d4dfe0ca9ed6be89e2e0d8995dafe83438299a314e91071", "digestId": "sha256:7f648d45ee7b6de2292162fba498b66cbaaf181da9004fcceef824c72dbae445", "sizeBytes": 2187, "command": "#(nop) ADD dir:7ec14b81316baa1a31c38c97686a8f030c98cba2035c968412749e33e0c4427e in /root/.data/ " }, { "index": 11, + "id": "3d4ad907517a021d86a4102d2764ad2161e4818bbd144e41d019bfc955434181", "digestId": "sha256:a4b8f95f266d5c063c9a9473c45f2f85ddc183e37941b5e6b6b9d3c00e8e0457", "sizeBytes": 6405, "command": "cp /root/saved.txt /tmp/saved.again1.txt" }, { "index": 12, + "id": "81b1b002d4b4c1325a9cad9990b5277e7f29f79e0f24582344c0891178f95905", "digestId": "sha256:22a44d45780a541e593a8862d80f3e14cb80b6bf76aa42ce68dc207a35bf3a4a", "sizeBytes": 6405, "command": "cp /root/saved.txt /root/.data/saved.again2.txt" }, { "index": 13, + "id": "cfb35bb5c127d848739be5ca726057e6e2c77b2849f588e7aebb642c0d3d4b7b", "digestId": "sha256:ba689cac6a98c92d121fa5c9716a1bab526b8bb1fd6d43625c575b79e97300c5", "sizeBytes": 6405, "command": "chmod +x /root/saved.txt" @@ -129,6 +144,9 @@ func Test_Export(t *testing.T) { }` actualResult := string(payload) if expectedResult != actualResult { - t.Errorf("Test_Export: unexpected export result:\n%v", actualResult) + dmp := diffmatchpatch.New() + diffs := dmp.DiffMain(expectedResult, actualResult, false) + + t.Errorf("Test_Export: unexpected export result:\n%v", dmp.DiffPrettyText(diffs)) } } diff --git a/runtime/export/layer.go b/runtime/export/layer.go index b360f48..cdbba5b 100644 --- a/runtime/export/layer.go +++ b/runtime/export/layer.go @@ -2,6 +2,7 @@ package export type layer struct { Index int `json:"index"` + ID string `json:"id"` DigestID string `json:"digestId"` SizeBytes uint64 `json:"sizeBytes"` Command string `json:"command"` diff --git a/runtime/ui/filetree_viewmodel_test.go b/runtime/ui/filetree_viewmodel_test.go index ba1d86c..af2bbb0 100644 --- a/runtime/ui/filetree_viewmodel_test.go +++ b/runtime/ui/filetree_viewmodel_test.go @@ -87,9 +87,13 @@ func initializeTestViewModel(t *testing.T) *FileTreeViewModel { treeStack, err := filetree.StackTreeRange(result.RefTrees, 0, 0) if err != nil { - t.Fatalf("%s: unable to create tree ViewModel: %v", t.Name(), err) + t.Fatalf("%s: unable to stack trees: %v", t.Name(), err) } - return NewFileTreeViewModel(treeStack, result.RefTrees, cache) + vm, err := NewFileTreeViewModel(treeStack, result.RefTrees, cache) + if err != nil { + t.Fatalf("%s: unable to create tree ViewModel: %+v", t.Name(), err) + } + return vm } func runTestCase(t *testing.T, vm *FileTreeViewModel, width, height int, filterRegex *regexp.Regexp) { From 2640bdced894da864295cd4abf2bd3dd70011fb9 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 7 Oct 2019 18:25:35 -0400 Subject: [PATCH 14/23] fix tests --- .../docker/image_archive_analysis_test.go | 63 +++++++++++++++++++ dive/image/docker/testing.go | 19 ++++-- runtime/ci/evaluator_test.go | 7 +-- runtime/export/export_test.go | 5 +- runtime/ui/filetree_viewmodel_test.go | 10 +-- 5 files changed, 85 insertions(+), 19 deletions(-) create mode 100644 dive/image/docker/image_archive_analysis_test.go diff --git a/dive/image/docker/image_archive_analysis_test.go b/dive/image/docker/image_archive_analysis_test.go new file mode 100644 index 0000000..65a8daf --- /dev/null +++ b/dive/image/docker/image_archive_analysis_test.go @@ -0,0 +1,63 @@ +package docker + +import ( + "github.com/wagoodman/dive/dive/image" + "testing" +) + +func analysisFromImageTar(t *testing.T, path string) *image.AnalysisResult { + archive, err := TestLoadArchive(path) + if err != nil { + t.Fatalf("unable to fetch archive: %v", err) + } + + img, err := archive.ToImage() + if err != nil { + t.Fatalf("unable to convert to image: %v", err) + } + + result, err := img.Analyze() + if err != nil { + t.Fatalf("unable to analyze: %v", err) + } + return result +} + +func Test_Analysis(t *testing.T) { + + table := map[string]struct { + efficiency float64 + sizeBytes uint64 + userSizeBytes uint64 + wastedBytes uint64 + wastedPercent float64 + path string + }{ + "docker-image": {0.9844212134184309, 1220598, 66237, 32025, 0.4834911001404049, "../../../.data/test-docker-image.tar"}, + } + + for name, test := range table { + result := analysisFromImageTar(t, test.path) + + if result.SizeBytes != test.sizeBytes { + t.Errorf("%s.%s: expected sizeBytes=%v, got %v", t.Name(), name, test.sizeBytes, result.SizeBytes) + } + + if result.UserSizeByes != test.userSizeBytes { + t.Errorf("%s.%s: expected userSizeBytes=%v, got %v", t.Name(), name, test.userSizeBytes, result.UserSizeByes) + } + + if result.WastedBytes != test.wastedBytes { + t.Errorf("%s.%s: expected wasterBytes=%v, got %v", t.Name(), name, test.wastedBytes, result.WastedBytes) + } + + if result.WastedUserPercent != test.wastedPercent { + t.Errorf("%s.%s: expected wastedPercent=%v, got %v", t.Name(), name, test.wastedPercent, result.WastedUserPercent) + } + + if result.Efficiency != test.efficiency { + t.Errorf("%s.%s: expected efficiency=%v, got %v", t.Name(), name, test.efficiency, result.Efficiency) + } + } + +} diff --git a/dive/image/docker/testing.go b/dive/image/docker/testing.go index f6d1777..ac70d7c 100644 --- a/dive/image/docker/testing.go +++ b/dive/image/docker/testing.go @@ -3,24 +3,33 @@ package docker import ( "github.com/wagoodman/dive/dive/image" "os" + "testing" ) -func TestLoadDockerImageTar(tarPath string) (*image.AnalysisResult, error) { +func TestLoadArchive(tarPath string) (*ImageArchive, error) { f, err := os.Open(tarPath) if err != nil { return nil, err } defer f.Close() - archive, err := NewImageArchive(f) + return NewImageArchive(f) +} + +func TestAnalysisFromArchive(t *testing.T, path string) *image.AnalysisResult { + archive, err := TestLoadArchive(path) if err != nil { - return nil, err + t.Fatalf("unable to fetch archive: %v", err) } img, err := archive.ToImage() if err != nil { - return nil, err + t.Fatalf("unable to convert to image: %v", err) } - return img.Analyze() + result, err := img.Analyze() + if err != nil { + t.Fatalf("unable to analyze: %v", err) + } + return result } diff --git a/runtime/ci/evaluator_test.go b/runtime/ci/evaluator_test.go index acd6b9b..d929452 100644 --- a/runtime/ci/evaluator_test.go +++ b/runtime/ci/evaluator_test.go @@ -10,10 +10,7 @@ import ( func Test_Evaluator(t *testing.T) { - result, err := docker.TestLoadDockerImageTar("../../.data/test-docker-image.tar") - if err != nil { - t.Fatalf("Test_Export: unable to fetch analysis: %v", err) - } + result := docker.TestAnalysisFromArchive(t, "../../.data/test-docker-image.tar") table := map[string]struct { efficiency string @@ -23,7 +20,7 @@ func Test_Evaluator(t *testing.T) { expectedResult map[string]RuleStatus }{ "allFail": {"0.99", "1B", "0.01", false, map[string]RuleStatus{"lowestEfficiency": RuleFailed, "highestWastedBytes": RuleFailed, "highestUserWastedPercent": RuleFailed}}, - "allPass": {"0.9", "50kB", "0.1", true, map[string]RuleStatus{"lowestEfficiency": RulePassed, "highestWastedBytes": RulePassed, "highestUserWastedPercent": RulePassed}}, + "allPass": {"0.9", "50kB", "0.5", true, map[string]RuleStatus{"lowestEfficiency": RulePassed, "highestWastedBytes": RulePassed, "highestUserWastedPercent": RulePassed}}, "allDisabled": {"disabled", "disabled", "disabled", true, map[string]RuleStatus{"lowestEfficiency": RuleDisabled, "highestWastedBytes": RuleDisabled, "highestUserWastedPercent": RuleDisabled}}, "misconfiguredHigh": {"1.1", "1BB", "10", false, map[string]RuleStatus{"lowestEfficiency": RuleMisconfigured, "highestWastedBytes": RuleMisconfigured, "highestUserWastedPercent": RuleMisconfigured}}, "misconfiguredLow": {"-9", "-1BB", "-0.1", false, map[string]RuleStatus{"lowestEfficiency": RuleMisconfigured, "highestWastedBytes": RuleMisconfigured, "highestUserWastedPercent": RuleMisconfigured}}, diff --git a/runtime/export/export_test.go b/runtime/export/export_test.go index 78cd2bc..f3028a5 100644 --- a/runtime/export/export_test.go +++ b/runtime/export/export_test.go @@ -7,11 +7,8 @@ import ( ) func Test_Export(t *testing.T) { + result := docker.TestAnalysisFromArchive(t, "../../.data/test-docker-image.tar") - result, err := docker.TestLoadDockerImageTar("../../.data/test-docker-image.tar") - if err != nil { - t.Fatalf("Test_Export: unable to fetch analysis: %v", err) - } export := NewExport(result) payload, err := export.marshal() if err != nil { diff --git a/runtime/ui/filetree_viewmodel_test.go b/runtime/ui/filetree_viewmodel_test.go index af2bbb0..a2228f7 100644 --- a/runtime/ui/filetree_viewmodel_test.go +++ b/runtime/ui/filetree_viewmodel_test.go @@ -72,13 +72,13 @@ func assertTestData(t *testing.T, actualBytes []byte) { helperCheckDiff(t, expectedBytes, actualBytes) } + + func initializeTestViewModel(t *testing.T) *FileTreeViewModel { - result, err := docker.TestLoadDockerImageTar("../../.data/test-docker-image.tar") - if err != nil { - t.Fatalf("%s: unable to fetch analysis: %v", t.Name(), err) - } + result := docker.TestAnalysisFromArchive(t, "../../.data/test-docker-image.tar") + cache := filetree.NewFileTreeCache(result.RefTrees) - err = cache.Build() + err := cache.Build() if err != nil { t.Fatalf("%s: unable to build cache: %+v", t.Name(), err) } From fcb5ec01ef27e6882058c4c04cfc93a77c194c8d Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 7 Oct 2019 18:25:53 -0400 Subject: [PATCH 15/23] gofmt --- dive/filetree/file_info.go | 8 +++---- dive/image/docker/image_archive.go | 11 ++++----- .../docker/image_archive_analysis_test.go | 12 +++++----- dive/image/docker/layer.go | 5 ++-- dive/image/docker/resolver.go | 3 +-- dive/image/layer.go | 12 +++++----- dive/image/podman/image_directory.go | 18 +++++++-------- dive/image/podman/layer.go | 8 +++---- dive/image/podman/resolver.go | 23 +++++++++---------- runtime/export/export.go | 6 ++--- runtime/ui/details_controller.go | 2 +- runtime/ui/filetree_viewmodel_test.go | 2 -- runtime/ui/ui.go | 1 - 13 files changed, 52 insertions(+), 59 deletions(-) diff --git a/dive/filetree/file_info.go b/dive/filetree/file_info.go index 4ea3887..788ac3c 100644 --- a/dive/filetree/file_info.go +++ b/dive/filetree/file_info.go @@ -49,7 +49,7 @@ func NewFileInfo(realPath, path string, info os.FileInfo) FileInfo { var linkName string var size int64 - if info.Mode() & os.ModeSymlink != 0 { + if info.Mode()&os.ModeSymlink != 0 { fileType = tar.TypeSymlink linkName, err = os.Readlink(realPath) @@ -83,9 +83,9 @@ func NewFileInfo(realPath, path string, info os.FileInfo) FileInfo { Size: size, Mode: info.Mode(), // todo: support UID/GID - Uid: -1, - Gid: -1, - IsDir: info.IsDir(), + Uid: -1, + Gid: -1, + IsDir: info.IsDir(), } } diff --git a/dive/image/docker/image_archive.go b/dive/image/docker/image_archive.go index 8d93301..dd4b38d 100644 --- a/dive/image/docker/image_archive.go +++ b/dive/image/docker/image_archive.go @@ -12,14 +12,14 @@ import ( ) type ImageArchive struct { - manifest manifest - config config - layerMap map[string]*filetree.FileTree + manifest manifest + config config + layerMap map[string]*filetree.FileTree } func NewImageArchive(tarFile io.ReadCloser) (*ImageArchive, error) { img := &ImageArchive{ - layerMap: make(map[string]*filetree.FileTree), + layerMap: make(map[string]*filetree.FileTree), } tarReader := tar.NewReader(tarFile) @@ -179,9 +179,8 @@ func (img *ImageArchive) ToImage() (*image.Image, error) { layers = append(layers, dockerLayer.ToLayer()) } - return &image.Image{ - Trees: trees, + Trees: trees, Layers: layers, }, nil diff --git a/dive/image/docker/image_archive_analysis_test.go b/dive/image/docker/image_archive_analysis_test.go index 65a8daf..5d9aacc 100644 --- a/dive/image/docker/image_archive_analysis_test.go +++ b/dive/image/docker/image_archive_analysis_test.go @@ -26,12 +26,12 @@ func analysisFromImageTar(t *testing.T, path string) *image.AnalysisResult { func Test_Analysis(t *testing.T) { table := map[string]struct { - efficiency float64 - sizeBytes uint64 - userSizeBytes uint64 - wastedBytes uint64 - wastedPercent float64 - path string + efficiency float64 + sizeBytes uint64 + userSizeBytes uint64 + wastedBytes uint64 + wastedPercent float64 + path string }{ "docker-image": {0.9844212134184309, 1220598, 66237, 32025, 0.4834911001404049, "../../../.data/test-docker-image.tar"}, } diff --git a/dive/image/docker/layer.go b/dive/image/docker/layer.go index 4e46933..408fea1 100644 --- a/dive/image/docker/layer.go +++ b/dive/image/docker/layer.go @@ -14,7 +14,6 @@ type layer struct { tree *filetree.FileTree } - // String represents a layer in a columnar format. func (l *layer) ToLayer() *image.Layer { id := strings.Split(l.tree.Name, "/")[0] @@ -25,7 +24,7 @@ func (l *layer) ToLayer() *image.Layer { Size: l.history.Size, Tree: l.tree, // todo: query docker api for tags - Names: []string{"(unavailable)"}, - Digest: l.history.ID, + Names: []string{"(unavailable)"}, + Digest: l.history.ID, } } diff --git a/dive/image/docker/resolver.go b/dive/image/docker/resolver.go index 38cfa45..461bbf9 100644 --- a/dive/image/docker/resolver.go +++ b/dive/image/docker/resolver.go @@ -13,7 +13,7 @@ import ( "golang.org/x/net/context" ) -type resolver struct {} +type resolver struct{} func NewResolver() *resolver { return &resolver{} @@ -100,4 +100,3 @@ func (r *resolver) fetchArchive(id string) (io.ReadCloser, error) { return readCloser, nil } - diff --git a/dive/image/layer.go b/dive/image/layer.go index 70354ad..550eba1 100644 --- a/dive/image/layer.go +++ b/dive/image/layer.go @@ -11,13 +11,13 @@ const ( ) type Layer struct { - Id string - Index int + Id string + Index int Command string - Size uint64 - Tree *filetree.FileTree - Names []string - Digest string + Size uint64 + Tree *filetree.FileTree + Names []string + Digest string } func (l *Layer) ShortId() string { diff --git a/dive/image/podman/image_directory.go b/dive/image/podman/image_directory.go index 9cad70c..3f6949b 100644 --- a/dive/image/podman/image_directory.go +++ b/dive/image/podman/image_directory.go @@ -20,8 +20,8 @@ type ImageDirectoryRef struct { func NewImageDirectoryRef(img *podmanImage.Image) (*ImageDirectoryRef, error) { imgDirRef := &ImageDirectoryRef{ layerOrder: make([]string, 0), - treeMap: make(map[string]*filetree.FileTree), - layerMap: make(map[string]*podmanImage.Image), + treeMap: make(map[string]*filetree.FileTree), + layerMap: make(map[string]*podmanImage.Image), } ctx := context.TODO() @@ -41,7 +41,7 @@ func NewImageDirectoryRef(img *podmanImage.Image) (*ImageDirectoryRef, error) { if !exists { return nil, fmt.Errorf("graph has no upper dir") } - + if _, err := os.Stat(rootDir); os.IsNotExist(err) { return nil, fmt.Errorf("graph root dir does not exist: %s", rootDir) } @@ -67,7 +67,7 @@ func NewImageDirectoryRef(img *podmanImage.Image) (*ImageDirectoryRef, error) { return imgDirRef, nil } -func processLayer(name, rootDir string) (*filetree.FileTree, error) { +func processLayer(name, rootDir string) (*filetree.FileTree, error) { tree := filetree.NewFileTree() tree.Name = name @@ -76,7 +76,7 @@ func processLayer(name, rootDir string) (*filetree.FileTree, error) { return err } // add this file to the tree... - relativeImagePath := "/"+strings.TrimPrefix(strings.TrimPrefix(path, rootDir), "/") + relativeImagePath := "/" + strings.TrimPrefix(strings.TrimPrefix(path, rootDir), "/") fileInfo := filetree.NewFileInfo(path, relativeImagePath, info) tree.FileSize += uint64(fileInfo.Size) @@ -111,15 +111,15 @@ func (img *ImageDirectoryRef) ToImage() (*image.Image, error) { // as you iterate chronologically through history (ignoring history items that have no layer contents) // Note: history is not required metadata in an OCI image! podmanLayer := layer{ - obj: img.layerMap[id], - index: layerIdx, - tree: trees[layerIdx], + obj: img.layerMap[id], + index: layerIdx, + tree: trees[layerIdx], } layers = append(layers, podmanLayer.ToLayer()) } return &image.Image{ - Trees: trees, + Trees: trees, Layers: layers, }, nil diff --git a/dive/image/podman/layer.go b/dive/image/podman/layer.go index b59eab5..5fdbd43 100644 --- a/dive/image/podman/layer.go +++ b/dive/image/podman/layer.go @@ -11,10 +11,10 @@ import ( // Layer represents a Docker image layer and metadata type layer struct { - obj *podmanImage.Image - history *podmanImage.History - index int - tree *filetree.FileTree + obj *podmanImage.Image + history *podmanImage.History + index int + tree *filetree.FileTree } func (l *layer) getHistory() (*podmanImage.History, error) { diff --git a/dive/image/podman/resolver.go b/dive/image/podman/resolver.go index 2a9126d..37f5253 100644 --- a/dive/image/podman/resolver.go +++ b/dive/image/podman/resolver.go @@ -11,7 +11,7 @@ import ( "os" ) -type resolver struct {} +type resolver struct{} func NewResolver() *resolver { return &resolver{} @@ -25,7 +25,6 @@ func (r *resolver) Build(args []string) (*image.Image, error) { return r.Fetch(id) } - func (r *resolver) Fetch(id string) (*image.Image, error) { img, err := r.resolveFromDisk(id) if err == nil { @@ -57,18 +56,18 @@ func (r *resolver) resolveFromDisk(id string) (*image.Image, error) { return nil, err } - ImageLoop: - for _, candidateImage := range images { - for _, name := range candidateImage.Names() { - if name == id { - img, err = NewImageDirectoryRef(candidateImage) - if err != nil { - return nil, err - } - break ImageLoop +ImageLoop: + for _, candidateImage := range images { + for _, name := range candidateImage.Names() { + if name == id { + img, err = NewImageDirectoryRef(candidateImage) + if err != nil { + return nil, err } + break ImageLoop } } + } if img == nil { return nil, fmt.Errorf("could not find image by name: '%s'", id) @@ -108,7 +107,7 @@ func (r *resolver) fetchDockerArchive(id string) (string, error) { return "", err } - for _, item:= range images { + for _, item := range images { for _, name := range item.Names() { if name == id { file, err := ioutil.TempFile(os.TempDir(), "dive-resolver-tar") diff --git a/runtime/export/export.go b/runtime/export/export.go index 44e09d7..4c32b67 100644 --- a/runtime/export/export.go +++ b/runtime/export/export.go @@ -14,10 +14,10 @@ type export struct { func NewExport(analysis *diveImage.AnalysisResult) *export { data := export{ Layer: make([]layer, len(analysis.Layers)), - Image: image { + Image: image{ InefficientFiles: make([]fileReference, len(analysis.Inefficiencies)), - SizeBytes: analysis.SizeBytes, - EfficiencyScore: analysis.Efficiency, + SizeBytes: analysis.SizeBytes, + EfficiencyScore: analysis.Efficiency, InefficientBytes: analysis.WastedBytes, }, } diff --git a/runtime/ui/details_controller.go b/runtime/ui/details_controller.go index 61ca251..0b9de63 100644 --- a/runtime/ui/details_controller.go +++ b/runtime/ui/details_controller.go @@ -134,7 +134,7 @@ func (controller *DetailsController) Render() error { if currentLayer.Names != nil && len(currentLayer.Names) > 0 { lines = append(lines, Formatting.Header("Tags: ")+strings.Join(currentLayer.Names, ", ")) } else { - lines = append(lines, Formatting.Header("Tags: ")+ "(none)") + lines = append(lines, Formatting.Header("Tags: ")+"(none)") } lines = append(lines, Formatting.Header("Id: ")+currentLayer.Id) lines = append(lines, Formatting.Header("Digest: ")+currentLayer.Digest) diff --git a/runtime/ui/filetree_viewmodel_test.go b/runtime/ui/filetree_viewmodel_test.go index a2228f7..df0e0ca 100644 --- a/runtime/ui/filetree_viewmodel_test.go +++ b/runtime/ui/filetree_viewmodel_test.go @@ -72,8 +72,6 @@ func assertTestData(t *testing.T, actualBytes []byte) { helperCheckDiff(t, expectedBytes, actualBytes) } - - func initializeTestViewModel(t *testing.T) *FileTreeViewModel { result := docker.TestAnalysisFromArchive(t, "../../.data/test-docker-image.tar") diff --git a/runtime/ui/ui.go b/runtime/ui/ui.go index 0e6c3d2..3b2075a 100644 --- a/runtime/ui/ui.go +++ b/runtime/ui/ui.go @@ -131,7 +131,6 @@ func toggleFilterView(g *gocui.Gui, v *gocui.View) error { return err } - return nil } From fd3459eb02b9c1cb59f674c22e42354f37b6f504 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 7 Oct 2019 18:28:44 -0400 Subject: [PATCH 16/23] fix validation errors --- cmd/root.go | 2 +- dive/image/podman/resolver.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/root.go b/cmd/root.go index 41abe52..877bca7 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -57,7 +57,7 @@ func initCli() { for _, key := range []string{"lowestEfficiency", "highestWastedBytes", "highestUserWastedPercent"} { if err := ciConfig.BindPFlag(fmt.Sprintf("rules.%s", key), rootCmd.Flags().Lookup(key)); err != nil { - log.Fatal("Unable to bind '%s' flag:", key, err) + log.Fatalf("Unable to bind '%s' flag: %v", key, err) } } diff --git a/dive/image/podman/resolver.go b/dive/image/podman/resolver.go index 37f5253..83e8a12 100644 --- a/dive/image/podman/resolver.go +++ b/dive/image/podman/resolver.go @@ -84,6 +84,9 @@ func (r *resolver) resolveFromDockerArchive(id string) (*image.Image, error) { defer os.Remove(path) file, err := os.Open(path) + if err != nil { + return nil, err + } defer file.Close() img, err := docker.NewImageArchive(ioutil.NopCloser(bufio.NewReader(file))) From c83bc9e3d03356a942a90b9a6c9832e336363229 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 8 Oct 2019 09:38:41 -0400 Subject: [PATCH 17/23] remove podman from darwin/windows builds --- .goreleaser.yml | 15 -- Dockerfile.full | 11 +- Dockerfile.slim | 3 - Makefile | 10 +- README.md | 13 +- dive/image/podman/build.go | 2 + dive/image/podman/cli.go | 2 + dive/image/podman/image_directory.go | 2 + dive/image/podman/layer.go | 2 + .../podman/{resolver.go => resolver_linux.go} | 0 dive/image/podman/resolver_notlinux.go | 21 +++ go.mod | 10 +- go.sum | 142 ++++-------------- runtime/run.go | 1 + 14 files changed, 75 insertions(+), 159 deletions(-) delete mode 100644 Dockerfile.slim rename dive/image/podman/{resolver.go => resolver_linux.go} (100%) create mode 100644 dive/image/podman/resolver_notlinux.go diff --git a/.goreleaser.yml b/.goreleaser.yml index f37ec1a..4f6cd02 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -3,8 +3,6 @@ release: builds: - binary: dive - env: - - CGO_ENABLED=0 goos: - windows - darwin @@ -14,19 +12,6 @@ builds: ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.buildTime={{.Date}}`. dockers: - - - binaries: - - dive - dockerfile: Dockerfile.slim - image_templates: - - "wagoodman/dive:{{ .Tag }}-slim" - - "wagoodman/dive:v{{ .Major }}-slim" - - "wagoodman/dive:v{{ .Major }}.{{ .Minor }}-slim" - - "wagoodman/dive:slim" - - "quay.io/wagoodman/dive:{{ .Tag }}-slim" - - "quay.io/wagoodman/dive:v{{ .Major }}-slim" - - "quay.io/wagoodman/dive:v{{ .Major }}.{{ .Minor }}-slim" - - "quay.io/wagoodman/dive:slim" - binaries: - dive diff --git a/Dockerfile.full b/Dockerfile.full index a6e7b8d..600c887 100644 --- a/Dockerfile.full +++ b/Dockerfile.full @@ -1,10 +1,7 @@ -FROM alpine:3.10 AS build +FROM alpine:3.10 ARG DOCKER_CLI_VERSION="19.03.1" -RUN apk --update add curl \ - && curl -L https://download.docker.com/linux/static/stable/x86_64/docker-$DOCKER_CLI_VERSION.tgz | tar -xzf - docker/docker --strip-component=1 -C /tmp - -FROM scratch -COPY --from=build /tmp/docker / +RUN apk --update add curl &&\ + curl -L https://download.docker.com/linux/static/stable/x86_64/docker-$DOCKER_CLI_VERSION.tgz | \ + tar -xzf - docker/docker --strip-component=1 -C /tmp COPY dist/dive_linux_amd64/dive / -ENV PATH / ENTRYPOINT ["/dive"] diff --git a/Dockerfile.slim b/Dockerfile.slim deleted file mode 100644 index d83f888..0000000 --- a/Dockerfile.slim +++ /dev/null @@ -1,3 +0,0 @@ -FROM scratch -COPY dist/dive_linux_amd64/dive / -ENTRYPOINT ["/dive"] diff --git a/Makefile b/Makefile index e9f940f..5fd62c7 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ BIN = dive -BUILD_DIR = ./dist/dive_linux_amd64/ +BUILD_DIR = ./dist/dive_linux_amd64 BUILD_PATH = $(BUILD_DIR)/$(BIN) +PWD := ${CURDIR} all: clean build @@ -14,7 +15,7 @@ run-large: build $(BUILD_PATH) amir20/clashleaders:latest build: - CGO_ENABLED=0 go build -o $(BUILD_PATH) + go build -o $(BUILD_PATH) release: test-coverage validate ./.scripts/tag.sh @@ -47,8 +48,11 @@ setup: go get ./... curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b /go/bin v1.18.0 +dev: + docker run -ti --rm -v $(PWD):/app -w /app -v dive-pkg:/go/pkg/ golang:1.13 bash + clean: rm -rf dist go clean -.PHONY: build install test lint clean release validate generate-test-data test-coverage ci +.PHONY: build install test lint clean release validate generate-test-data test-coverage ci dev diff --git a/README.md b/README.md index cbda298..cdd7d62 100644 --- a/README.md +++ b/README.md @@ -40,22 +40,15 @@ CI=true dive **Show Docker image contents broken down by layer** -As you select a layer on the left, you are shown the contents of that layer -combined with all previous layers on the right. Also, you can fully explore the -file tree with the arrow keys. +As you select a layer on the left, you are shown the contents of that layer combined with all previous layers on the right. Also, you can fully explore the file tree with the arrow keys. **Indicate what's changed in each layer** -Files that have changed, been modified, added, or removed are indicated in the -file tree. This can be adjusted to show changes for a specific layer, or -aggregated changes up to this layer. +Files that have changed, been modified, added, or removed are indicated in the file tree. This can be adjusted to show changes for a specific layer, or aggregated changes up to this layer. **Estimate "image efficiency"** -The lower left pane shows basic layer info and an experimental metric that will -guess how much wasted space your image contains. This might be from duplicating -files across layers, moving files across layers, or not fully removing files. -Both a percentage "score" and total wasted file space is provided. +The lower left pane shows basic layer info and an experimental metric that will guess how much wasted space your image contains. This might be from duplicating files across layers, moving files across layers, or not fully removing files. Both a percentage "score" and total wasted file space is provided. **Quick build/analysis cycles** diff --git a/dive/image/podman/build.go b/dive/image/podman/build.go index f8813c6..bfebc49 100644 --- a/dive/image/podman/build.go +++ b/dive/image/podman/build.go @@ -1,3 +1,5 @@ +// +build linux + package podman import ( diff --git a/dive/image/podman/cli.go b/dive/image/podman/cli.go index 90f26f3..1c4b8e8 100644 --- a/dive/image/podman/cli.go +++ b/dive/image/podman/cli.go @@ -1,3 +1,5 @@ +// +build linux + package podman import ( diff --git a/dive/image/podman/image_directory.go b/dive/image/podman/image_directory.go index 3f6949b..1b9c289 100644 --- a/dive/image/podman/image_directory.go +++ b/dive/image/podman/image_directory.go @@ -1,3 +1,5 @@ +// +build linux + package podman import ( diff --git a/dive/image/podman/layer.go b/dive/image/podman/layer.go index 5fdbd43..dd16d28 100644 --- a/dive/image/podman/layer.go +++ b/dive/image/podman/layer.go @@ -1,3 +1,5 @@ +// +build linux + package podman import ( diff --git a/dive/image/podman/resolver.go b/dive/image/podman/resolver_linux.go similarity index 100% rename from dive/image/podman/resolver.go rename to dive/image/podman/resolver_linux.go diff --git a/dive/image/podman/resolver_notlinux.go b/dive/image/podman/resolver_notlinux.go new file mode 100644 index 0000000..5bb7730 --- /dev/null +++ b/dive/image/podman/resolver_notlinux.go @@ -0,0 +1,21 @@ +// +build !linux + +package podman + +import ( + "fmt" +) + +type resolver struct{} + +func NewResolver() *resolver { + return &resolver{} +} + +func (r *resolver) Build(args []string) (*image.Image, error) { + return nil, fmt.Errorf("unsupported platform") +} + +func (r *resolver) Fetch(id string) (*image.Image, error) { + return nil, fmt.Errorf("unsupported platform") +} diff --git a/go.mod b/go.mod index 2848555..6a0edf1 100644 --- a/go.mod +++ b/go.mod @@ -3,26 +3,25 @@ module github.com/wagoodman/dive go 1.13 require ( + github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774 // indirect github.com/Microsoft/go-winio v0.4.14 // indirect github.com/cespare/xxhash v1.1.0 - github.com/containers/libpod v1.6.0 + github.com/containers/libpod v1.6.1 github.com/docker/cli v0.0.0-20190906153656-016a3232168d github.com/docker/docker v0.7.3-0.20190309235953-33c3200e0d16 - github.com/docker/engine v1.4.2-0.20190822205725-ed20165a37b4 // indirect github.com/dustin/go-humanize v1.0.0 + github.com/elazarl/goproxy/ext v0.0.0-20190911111923-ecfe977594f1 // indirect github.com/fatih/color v1.7.0 github.com/gogo/protobuf v1.3.0 // indirect - github.com/golang/lint v0.0.0-20180702182130-06c8688daad7 // indirect - github.com/golangci/golangci-lint v1.17.1 // indirect github.com/google/uuid v1.1.1 github.com/jroimartin/gocui v0.4.0 - github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect github.com/logrusorgru/aurora v0.0.0-20190803045625-94edacc10f9b github.com/lunixbochs/vtclean v1.0.0 github.com/mattn/go-colorable v0.1.2 // indirect github.com/mattn/go-isatty v0.0.9 // indirect github.com/mattn/go-runewidth v0.0.4 // indirect github.com/mitchellh/go-homedir v1.1.0 + github.com/morikuni/aec v1.0.0 // indirect github.com/nsf/termbox-go v0.0.0-20190817171036-93860e161317 // indirect github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee github.com/sergi/go-diff v1.0.0 @@ -33,7 +32,6 @@ require ( golang.org/x/crypto v0.0.0-20190907121410-71b5226ff739 // indirect golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03 // indirect - google.golang.org/genproto v0.0.0-20190620144150-6af8c5fc6601 ) // related to an invalid pseudo version in github.com/docker/distribution@v0.0.0-20181126153310-93e082742a009850ac46962150b2f652a822c5ff diff --git a/go.sum b/go.sum index 2b6849a..6550cf5 100644 --- a/go.sum +++ b/go.sum @@ -24,16 +24,15 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEV github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OpenPeeDeeP/depguard v0.0.0-20180806142446-a69c782687b2/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= @@ -42,6 +41,7 @@ github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/buger/goterm v0.0.0-20181115115552-c206103e1f37/go.mod h1:u9UyCz2eTrSGy6fbupqJ54eY5c4IC8gREQ1053dK12U= +github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/checkpoint-restore/go-criu v0.0.0-20190109184317-bdb7599cd87b h1:T4nWG1TXIxeor8mAu5bFguPJgSIGhZqv/f0z55KCrJM= @@ -52,17 +52,18 @@ github.com/containerd/continuity v0.0.0-20180814194400-c7c5070e6f6e/go.mod h1:GL github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containernetworking/cni v0.7.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v0.7.1 h1:fE3r16wpSEyaqY4Z4oFrLMmIGfBYIKpPrHK31EJ9FzE= github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/plugins v0.8.1 h1:dJbykiiSIS3Xvo8d+A6rSXcUEFGfvCjUA+bUED4qegQ= -github.com/containernetworking/plugins v0.8.1/go.mod h1:dagHaAhNjXjT9QYOklkKJDGaQPTg4pf//FrUcJeb7FU= +github.com/containernetworking/plugins v0.8.2 h1:5lnwfsAYO+V7yXhysJKy3E1A2Gy9oVut031zfdOzI9w= +github.com/containernetworking/plugins v0.8.2/go.mod h1:TxALKWZpWL79BC3GOYKJzzXr7U8R23PdhwaLp6F3adc= github.com/containers/buildah v1.11.2 h1:U6Abrp1J7H19vHvhqIran4Xvw+Z3WIqMM86fIt9L7Qk= github.com/containers/buildah v1.11.2/go.mod h1:CtnP3vsLiU3xgKvkhdb4b0IzYwXNzHRv3ezl4z+RPC0= github.com/containers/conmon v0.3.0/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image v3.0.2+incompatible h1:B1lqAE8MUPCrsBLE86J0gnXleeRq8zJnQryhiiGQNyE= github.com/containers/image v3.0.2+incompatible/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M= -github.com/containers/libpod v1.6.0 h1:OhoW3ssFTDly9KxXwwMAQUgSLK7Q+vkIPtBTzqLyxh0= -github.com/containers/libpod v1.6.0/go.mod h1:VLZ5FnOkW94RsOqYbIKEqFbSb0fg9WVJLO6Vey+k02Y= +github.com/containers/libpod v1.6.1 h1:3ktOfksBFj4tO3DKAyWZIzbZ/TaHHIP1Adx4eMwbi1g= +github.com/containers/libpod v1.6.1/go.mod h1:iYq4WQnsO1NDfn0kdIrM8VtPnrPHs3r5PiHdZMn5Dv4= github.com/containers/psgo v1.3.1 h1:1kE+jJ9Ou5f9zQT/M2IdeSclsKWsXrSFlOcnqc+F2TA= github.com/containers/psgo v1.3.1/go.mod h1:LLiRMmxZ6FWP4bB/fOUu6kDT+4okk/ZCeeykqh0O5Ns= github.com/containers/storage v1.13.2/go.mod h1:6D8nK2sU9V7nEmAraINRs88ZEscM5C5DK+8Npp27GeA= @@ -76,6 +77,7 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz github.com/coreos/go-iptables v0.4.2/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -87,6 +89,10 @@ github.com/cri-o/ocicni v0.1.1-0.20190702175919-7762645d18ca/go.mod h1:BO0al9TKb github.com/cyphar/filepath-securejoin v0.2.1/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= +github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= +github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= +github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -98,24 +104,11 @@ github.com/docker/cli v0.0.0-20190906153656-016a3232168d h1:gwX/88xJZfxZV1yjhhuQ github.com/docker/cli v0.0.0-20190906153656-016a3232168d/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.7.0-rc.0.0.20181024170156-93e082742a00+incompatible h1:YOfVNTgst//UrD5ZhDfbY0+GTSWjXfXOYLYHhw0kMpo= github.com/docker/distribution v2.7.0-rc.0.0.20181024170156-93e082742a00+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.4.2-0.20181124105010-0b7cb16dde4a h1:8DkTRrioG4IEbvyukSm2UouCOPrcPYXlleW3QeNWfw0= -github.com/docker/docker v1.4.2-0.20181124105010-0b7cb16dde4a/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v1.4.2-0.20181124105010-f5ec1e2936dcbe7b5001c2b817188b095c700c27/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.1/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= -github.com/docker/engine v0.0.0-20190822180741-9552f2b2fdde h1:6JcsC3tTf9ISGL2xesRNf0vQ/RgpVXXNYz/IFXQoqX0= -github.com/docker/engine v0.0.0-20190822180741-9552f2b2fdde/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= github.com/docker/engine v0.0.0-20190822205725-ed20165a37b4 h1:hB02X3RT4IZUr/dm+zIXpAlpvQPtRyKBEA/CbzshNZ8= github.com/docker/engine v0.0.0-20190822205725-ed20165a37b4/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= -github.com/docker/engine v1.4.2-0.20100822105010-9552f2b2fddeb0c2537b350f4b159ffe525d7a42/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= -github.com/docker/engine v1.4.2-0.20100824105010-9552f2b2fddeb0c2537b350f4b159ffe525d7a42/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= -github.com/docker/engine v1.4.2-0.20181124105010-0b7cb16dde4a h1:36nEEPrc6RgAy3HziqtYNtiOpbk3a9+2nPC0iRwOs+k= -github.com/docker/engine v1.4.2-0.20181124105010-0b7cb16dde4a/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= -github.com/docker/engine v1.4.2-0.20181124105010-9552f2b2fddeb0c2537b350f4b159ffe525d7a42/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= -github.com/docker/engine v1.4.2-0.20181124105010-f5ec1e2936dcbe7b5001c2b817188b095c700c27/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= -github.com/docker/engine v1.4.2-0.20190822205725-ed20165a37b4 h1:+VAGRKyn9Ca+ckzV/PJsaRO7UXO9KQjFmSffcSDrWdE= -github.com/docker/engine v1.4.2-0.20190822205725-ed20165a37b4/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 h1:X0fj836zx99zFu83v/M79DuBn84IL/Syx1SY6Y5ZEMA= @@ -136,7 +129,6 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f h1:8GDPb0tCY8LQ+OJ3dbHb5sA6YZWXFORQYZx5sdsTlMs= github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/elazarl/goproxy v0.0.0-20190911111923-ecfe977594f1 h1:yY9rWGoXv1U5pl4gxqlULARMQD7x0QG85lqEXTWysik= github.com/elazarl/goproxy/ext v0.0.0-20190911111923-ecfe977594f1 h1:8B7WF1rIoM8H1smfpXFvOawSAzlRDMVzoGu9zE3+OCk= github.com/elazarl/goproxy/ext v0.0.0-20190911111923-ecfe977594f1/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= @@ -148,7 +140,6 @@ github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6 github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= -github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -159,12 +150,9 @@ github.com/fsouza/go-dockerclient v1.4.1/go.mod h1:PUNHxbowDqRXfRgZqMz1OeGtbWC6V github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-critic/go-critic v0.3.5-0.20190526074819-1df300866540/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= @@ -174,20 +162,7 @@ github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcs github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= -github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= -github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg= -github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= -github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= -github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk= -github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= -github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks= -github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= -github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= -github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20181101234600-2ff6f7ffd60f h1:zlOR3rOlPAVvtfuxGKoghCmop5B0TRyu/ZieziZuGiM= github.com/godbus/dbus v0.0.0-20181101234600-2ff6f7ffd60f/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -201,8 +176,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekf github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= -github.com/golang/mock v1.0.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -212,23 +185,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= -github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0= -github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= -github.com/golangci/go-tools v0.0.0-20190318060251-af6baa5dc196/go.mod h1:unzUULGw35sjyOYjUt0jMTXqHlZPpPc6e+xfO4cd6mM= -github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o= -github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU= -github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= -github.com/golangci/golangci-lint v1.17.1/go.mod h1:+5sJSl2h3aly+fpmL2meSP8CaSKua2E4Twi9LPy7b1g= -github.com/golangci/gosec v0.0.0-20190211064107-66fb7fc33547/go.mod h1:0qUabqiIQgfmlAmulqxyiGkkyF6/tOGSnY2cnPVwrzU= -github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU= -github.com/golangci/lint-1 v0.0.0-20190420132249-ee948d087217/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= -github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= -github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI= -github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -261,7 +217,6 @@ github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -274,7 +229,6 @@ github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uP github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -289,6 +243,7 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/ishidawataru/sctp v0.0.0-20180918013207-6e2cb1366111 h1:NAAiV9ass6VReWFjuxqrMIq12WKlSULI6Gs3PxQghLA= github.com/ishidawataru/sctp v0.0.0-20180918013207-6e2cb1366111/go.mod h1:DM4VvS+hD/kDi1U1QsX2fnZowwBhqD0Dk3bRPKF/Oc8= +github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jroimartin/gocui v0.4.0 h1:52jnalstgmc25FmtGcWqa0tcbMEWS6RpFLsOIO+I+E8= github.com/jroimartin/gocui v0.4.0/go.mod h1:7i7bbj99OgFHzo7kB2zPb8pXLqMBSQegY7azfqXMkyY= @@ -298,19 +253,16 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/juju/errors v0.0.0-20180806074554-22422dad46e1/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/testing v0.0.0-20190613124551-e81189438503/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.7.2 h1:liMOoeIvFpr9kEvalrZ7VVBA4wGf7zfOgwBjzz/5g2Y= github.com/klauspost/compress v1.7.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/pgzip v1.2.1 h1:oIPZROsWuPHpOdMVWLuJZXwgjhrW8r1yEX8UqMyeNHM= @@ -326,12 +278,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/logrusorgru/aurora v0.0.0-20190803045625-94edacc10f9b h1:PMbSa9CgaiQR9NLlUTwKi+7aeLl3GG5JX5ERJxfQ3IE= github.com/logrusorgru/aurora v0.0.0-20190803045625-94edacc10f9b/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= @@ -339,10 +289,8 @@ github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= @@ -351,18 +299,15 @@ github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8Bz github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= github.com/mattn/go-shellwords v1.0.5 h1:JhhFTIOslh5ZsPrpa3Wdg8bF0WI3b44EMblmU9wIsXc= github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mistifyio/go-zfs v2.1.1+incompatible h1:gAMO1HM9xBRONLHHYnu5iFsOJUiJdNZo6oqSENd4eW8= github.com/mistifyio/go-zfs v2.1.1+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= -github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/moby v0.0.0-20171005181806-f8806b18b4b9/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= @@ -373,7 +318,8 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c h1:xa+eQWKuJ9MbB9FBL/eoNvDFvveAkz2LQoz8PzX7Q/4= github.com/mtrmac/gpgme v0.0.0-20170102180018-b2432428689c/go.mod h1:GhAqVMEWnTcW2dxoD/SO3n2enrgWl3y6Dnx4m59GvcA= @@ -381,24 +327,25 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m github.com/munnerz/goautoneg v0.0.0-20190414153302-2ae31c8b6b30/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= -github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/nsf/termbox-go v0.0.0-20181027232701-60ab7e3d12ed h1:bAVGG6B+R5qpSylrrA+BAMrzYkdAoiTaKPVxRB+4cyM= github.com/nsf/termbox-go v0.0.0-20181027232701-60ab7e3d12ed/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= github.com/nsf/termbox-go v0.0.0-20190817171036-93860e161317 h1:hhGN4SFXgXo61Q4Sjj/X9sBjyeSa2kdpaOzCO+8EVQw= github.com/nsf/termbox-go v0.0.0-20190817171036-93860e161317/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= @@ -423,7 +370,6 @@ github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsq github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw= github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc= -github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= @@ -459,13 +405,10 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/seccomp/containers-golang v0.0.0-20180629143253-cdfdaa7543f4/go.mod h1:f/98/SnvAzhAEFQJ3u836FePXvcbE8BS0YGMQNn4mhA= github.com/seccomp/containers-golang v0.0.0-20190312124753-8ca8945ccf5f h1:OtU/w6sBKmXYaw2KEODxjcYi3oPSyyslhgGFgIJVGAI= @@ -475,12 +418,7 @@ github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46r github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/sirupsen/logrus v0.0.0-20190403091019-9b3cdde74fbe/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -489,22 +427,17 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= @@ -515,7 +448,6 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= @@ -532,7 +464,6 @@ github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0 github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tchap/go-patricia v2.3.0+incompatible h1:GkY4dP3cEfEASBPPkWd+AmjYxhmDkqO9/zg7R0lSQRs= github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= -github.com/timakin/bodyclose v0.0.0-20190407043127-4a873e97b2bb/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/uber/jaeger-client-go v2.19.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v0.0.0-20190122222657-d036253de8f5/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= @@ -544,15 +475,12 @@ github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4A github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= -github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b/go.mod h1:YHaw8N660ESgMgLOZfLQqT1htFItynAUxMesFBho52s= github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02dE= github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g= github.com/vbauerster/mpb v3.4.0+incompatible h1:mfiiYw87ARaeRW6x5gWwYRUawxaW1tLAD8IceomUCNw= github.com/vbauerster/mpb v3.4.0+incompatible/go.mod h1:zAHG26FUhVKETRu+MWqYXcI70POlC6N8up9p1dID7SU= +github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM= github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= @@ -578,11 +506,11 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A= @@ -602,11 +530,10 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190607214518-6fa95d984e88/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -614,7 +541,6 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -636,7 +562,6 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180824143301-4910a1d54f87/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -661,7 +586,6 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03 h1:b3JiLYVaG9kHjTcOQIoUh978YMCO7oVTQQBLudU47zY= golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -672,25 +596,18 @@ golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180810170437-e96c4e24768d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190121143147-24cd39ecf745/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -722,7 +639,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= @@ -766,11 +682,7 @@ k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4y k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a h1:2jUDc9gJja832Ftp+QbDV0tVhQHMISFn01els+2ZAcw= k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/runtime/run.go b/runtime/run.go index 65b3670..a48267a 100644 --- a/runtime/run.go +++ b/runtime/run.go @@ -17,6 +17,7 @@ import ( ) func runCi(analysis *image.AnalysisResult, options Options) { + fmt.Printf(" efficiency: %2.4f %%\n", analysis.Efficiency*100) fmt.Printf(" wastedBytes: %d bytes (%s)\n", analysis.WastedBytes, humanize.Bytes(analysis.WastedBytes)) fmt.Printf(" userWastedPercent: %2.4f %%\n", analysis.WastedUserPercent*100) From a978266adf460065d8a1cae438fba0bb95f3ec55 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 8 Oct 2019 09:41:10 -0400 Subject: [PATCH 18/23] rm deplicate test helper --- .../docker/image_archive_analysis_test.go | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/dive/image/docker/image_archive_analysis_test.go b/dive/image/docker/image_archive_analysis_test.go index 5d9aacc..afc3825 100644 --- a/dive/image/docker/image_archive_analysis_test.go +++ b/dive/image/docker/image_archive_analysis_test.go @@ -1,28 +1,9 @@ package docker import ( - "github.com/wagoodman/dive/dive/image" "testing" ) -func analysisFromImageTar(t *testing.T, path string) *image.AnalysisResult { - archive, err := TestLoadArchive(path) - if err != nil { - t.Fatalf("unable to fetch archive: %v", err) - } - - img, err := archive.ToImage() - if err != nil { - t.Fatalf("unable to convert to image: %v", err) - } - - result, err := img.Analyze() - if err != nil { - t.Fatalf("unable to analyze: %v", err) - } - return result -} - func Test_Analysis(t *testing.T) { table := map[string]struct { @@ -37,7 +18,7 @@ func Test_Analysis(t *testing.T) { } for name, test := range table { - result := analysisFromImageTar(t, test.path) + result := TestAnalysisFromArchive(t, test.path) if result.SizeBytes != test.sizeBytes { t.Errorf("%s.%s: expected sizeBytes=%v, got %v", t.Name(), name, test.sizeBytes, result.SizeBytes) @@ -59,5 +40,4 @@ func Test_Analysis(t *testing.T) { t.Errorf("%s.%s: expected efficiency=%v, got %v", t.Name(), name, test.efficiency, result.Efficiency) } } - } From 5e4c3eedb7ed2f5d4c31f7de82929058a055e90d Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 8 Oct 2019 09:45:43 -0400 Subject: [PATCH 19/23] fix windows/darwin import --- dive/image/podman/resolver_notlinux.go | 1 + 1 file changed, 1 insertion(+) diff --git a/dive/image/podman/resolver_notlinux.go b/dive/image/podman/resolver_notlinux.go index 5bb7730..49b959a 100644 --- a/dive/image/podman/resolver_notlinux.go +++ b/dive/image/podman/resolver_notlinux.go @@ -4,6 +4,7 @@ package podman import ( "fmt" + "github.com/wagoodman/dive/dive/image" ) type resolver struct{} From 41b42cdd6844898d7ceaed4d39b1fbea6c60df85 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 8 Oct 2019 10:06:14 -0400 Subject: [PATCH 20/23] only podman via docker archive support --- Makefile | 13 ++++++++++--- dive/image/podman/resolver_linux.go | 14 ++++++-------- runtime/run.go | 1 + 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 5fd62c7..e9a2f8c 100644 --- a/Makefile +++ b/Makefile @@ -8,12 +8,19 @@ all: clean build run: build $(BUILD_PATH) build -t dive-example:latest -f .data/Dockerfile.example . -run-ci: build - CI=true $(BUILD_PATH) dive-example:latest --ci-config .data/.dive-ci - run-large: build $(BUILD_PATH) amir20/clashleaders:latest +run-podman: build + podman build -t dive-example:latest -f .data/Dockerfile.example . + $(BUILD_PATH) localhost/dive-example:latest --engine podman + +run-podman-large: build + $(BUILD_PATH) docker.io/amir20/clashleaders:latest --engine podman + +run-ci: build + CI=true $(BUILD_PATH) dive-example:latest --ci-config .data/.dive-ci + build: go build -o $(BUILD_PATH) diff --git a/dive/image/podman/resolver_linux.go b/dive/image/podman/resolver_linux.go index 83e8a12..19e25d2 100644 --- a/dive/image/podman/resolver_linux.go +++ b/dive/image/podman/resolver_linux.go @@ -26,15 +26,13 @@ func (r *resolver) Build(args []string) (*image.Image, error) { } func (r *resolver) Fetch(id string) (*image.Image, error) { - img, err := r.resolveFromDisk(id) - if err == nil { - return img, err - } + // todo: there are still a number of bugs remaining with this approach --stick with the docker archive for now + // img, err := r.resolveFromDisk(id) + // if err == nil { + // return img, err + // } - // todo: remove print of error - fmt.Println(err) - - img, err = r.resolveFromDockerArchive(id) + img, err := r.resolveFromDockerArchive(id) if err == nil { return img, err } diff --git a/runtime/run.go b/runtime/run.go index a48267a..024b4ca 100644 --- a/runtime/run.go +++ b/runtime/run.go @@ -59,6 +59,7 @@ func Run(options Options) { os.Exit(1) } } else { + fmt.Println(utils.TitleFormat("Fetching image...") + " (this can take a while for large images)") img, err = imageResolver.Fetch(options.ImageId) if err != nil { fmt.Printf("cannot fetch image: %v\n", err) From 3e490f7b5c15a9f6f81572dbb0f22780cb13dc10 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 8 Oct 2019 10:14:46 -0400 Subject: [PATCH 21/23] fix dead code lint --- dive/image/podman/resolver_linux.go | 66 ++++++++++++++--------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/dive/image/podman/resolver_linux.go b/dive/image/podman/resolver_linux.go index 19e25d2..4dbda15 100644 --- a/dive/image/podman/resolver_linux.go +++ b/dive/image/podman/resolver_linux.go @@ -40,39 +40,39 @@ func (r *resolver) Fetch(id string) (*image.Image, error) { return nil, fmt.Errorf("unable to resolve image '%s'", id) } -func (r *resolver) resolveFromDisk(id string) (*image.Image, error) { - var img *ImageDirectoryRef - var err error - - runtime, err := libpod.NewRuntime(context.TODO()) - if err != nil { - return nil, err - } - - images, err := runtime.ImageRuntime().GetImages() - if err != nil { - return nil, err - } - -ImageLoop: - for _, candidateImage := range images { - for _, name := range candidateImage.Names() { - if name == id { - img, err = NewImageDirectoryRef(candidateImage) - if err != nil { - return nil, err - } - break ImageLoop - } - } - } - - if img == nil { - return nil, fmt.Errorf("could not find image by name: '%s'", id) - } - - return img.ToImage() -} +// func (r *resolver) resolveFromDisk(id string) (*image.Image, error) { +// var img *ImageDirectoryRef +// var err error +// +// runtime, err := libpod.NewRuntime(context.TODO()) +// if err != nil { +// return nil, err +// } +// +// images, err := runtime.ImageRuntime().GetImages() +// if err != nil { +// return nil, err +// } +// +// ImageLoop: +// for _, candidateImage := range images { +// for _, name := range candidateImage.Names() { +// if name == id { +// img, err = NewImageDirectoryRef(candidateImage) +// if err != nil { +// return nil, err +// } +// break ImageLoop +// } +// } +// } +// +// if img == nil { +// return nil, fmt.Errorf("could not find image by name: '%s'", id) +// } +// +// return img.ToImage() +// } func (r *resolver) resolveFromDockerArchive(id string) (*image.Image, error) { path, err := r.fetchDockerArchive(id) From 68d3fe2ed9b72c276cb185b0f56b6536596dcf30 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 8 Oct 2019 10:18:34 -0400 Subject: [PATCH 22/23] fix ci setup --- .circleci/config.yml | 6 +++--- Makefile | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4afc0ff..74ad8fd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,7 +12,7 @@ jobs: - restore_cache: keys: - golang-1.11-{{ checksum "go.sum" }} - - run: make setup + - run: make setup-ci - save_cache: key: golang-1.11-{{ checksum "go.sum" }} paths: @@ -32,7 +32,7 @@ jobs: - restore_cache: keys: - golang-1.12-{{ checksum "go.sum" }} - - run: make setup + - run: make setup-ci - save_cache: key: golang-1.12-{{ checksum "go.sum" }} paths: @@ -52,7 +52,7 @@ jobs: - restore_cache: keys: - golang-1.13-{{ checksum "go.sum" }} - - run: make setup + - run: make setup-ci - save_cache: key: golang-1.13-{{ checksum "go.sum" }} paths: diff --git a/Makefile b/Makefile index e9a2f8c..ac291e5 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,8 @@ lint: build generate-test-data: docker build -t dive-test:latest -f .data/Dockerfile.test-image . && docker image save -o .data/test-docker-image.tar dive-test:latest && echo "Exported test data!" -setup: +setup-ci: + sudo apt update && sudo apt install -y libgpgme-dev libbtrfs-dev libdevmapper-dev go get ./... curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b /go/bin v1.18.0 From 0e49dd0fec1000fbb3f44f97eb570408ff17ff6c Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 8 Oct 2019 10:47:52 -0400 Subject: [PATCH 23/23] update readme --- README.md | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cdd7d62..6520738 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Go Report Card](https://goreportcard.com/badge/github.com/wagoodman/dive)](https://goreportcard.com/report/github.com/wagoodman/dive) [![Pipeline Status](https://circleci.com/gh/wagoodman/dive.svg?style=svg)](https://circleci.com/gh/wagoodman/dive) -**A tool for exploring a docker image, layer contents, and discovering ways to shrink your Docker image size.** +**A tool for exploring a docker image, layer contents, and discovering ways to shrink the size of your Docker/OCI image.** ![Image](.data/demo.gif) @@ -16,7 +16,7 @@ or if you want to build your image then jump straight into analyzing it: dive build -t . ``` -Building on Macbook +Building on Macbook (supporting only the Docker container engine) ```bash docker run --rm -it \ @@ -61,6 +61,13 @@ command. **CI Integration** Analyze and image and get a pass/fail result based on the image efficiency and wasted space. Simply set `CI=true` in the environment when invoking any valid dive command. +**Supported Container Engines** +- Docker (default) +- Podman (linux only) + +```bash +dive --engine podman +``` ## Installation @@ -188,6 +195,9 @@ Key Binding | Description No configuration is necessary, however, you can create a config file and override values: ```yaml +# supported options are "docker" and "podman" +container-engine: docker + log: enabled: true path: ./dive.log