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/.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..ac291e5 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,28 @@ BIN = dive -BUILD_DIR = ./dist/dive_linux_amd64/ +BUILD_DIR = ./dist/dive_linux_amd64 BUILD_PATH = $(BUILD_DIR)/$(BIN) +PWD := ${CURDIR} 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: - CGO_ENABLED=0 go build -o $(BUILD_PATH) + go build -o $(BUILD_PATH) release: test-coverage validate ./.scripts/tag.sh @@ -43,12 +51,16 @@ 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 +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..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 \ @@ -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** @@ -68,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 @@ -195,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 diff --git a/cmd/analyze.go b/cmd/analyze.go index 8061de9..4e6c27e 100644 --- a/cmd/analyze.go +++ b/cmd/analyze.go @@ -2,16 +2,16 @@ 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") @@ -21,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() @@ -36,11 +36,18 @@ 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) + os.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..0cff973 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -2,8 +2,9 @@ package cmd import ( "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/wagoodman/dive/dive" "github.com/wagoodman/dive/runtime" - "github.com/wagoodman/dive/utils" ) // buildCmd represents the build command @@ -20,12 +21,15 @@ 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 + // 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, + Engine: dive.GetEngine(engine), BuildArgs: args, ExportFile: exportFile, CiConfig: ciConfig, diff --git a/cmd/root.go b/cmd/root.go index 9dadad2..877bca7 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" @@ -12,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 @@ -35,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() { @@ -58,9 +57,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.Fatalf("Unable to bind '%s' flag: %v", key, err) } } + + rootCmd.PersistentFlags().String("engine", "docker", "The container engine to fetch the image from. Allowed values: "+strings.Join(dive.AllowedEngines, ", ")) + + if err := viper.BindPFlag("container-engine", 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,7 +102,12 @@ func initConfig() { viper.SetDefault("filetree.pane-width", 0.5) viper.SetDefault("filetree.show-attributes", true) - viper.AutomaticEnv() // read in environment variables that match + viper.SetDefault("container-engine", "docker") + + 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 { @@ -148,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/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_info.go b/dive/filetree/file_info.go index 4d24925..788ac3c 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,54 @@ 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 + 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 + 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) + } + + return FileInfo{ + Path: path, + TypeFlag: fileType, + Linkname: linkName, + hash: hash, + Size: 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/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/dive/get_analyzer.go b/dive/get_analyzer.go deleted file mode 100644 index 1b3d465..0000000 --- a/dive/get_analyzer.go +++ /dev/null @@ -1,12 +0,0 @@ -package dive - -import ( - "github.com/wagoodman/dive/dive/image" - "github.com/wagoodman/dive/dive/image/docker" -) - -func GetAnalyzer(imageID string) image.Analyzer { - // u, _ := url.Parse(imageID) - // fmt.Printf("\n\nurl: %+v\n", u.Scheme) - return docker.NewImageAnalyzer(imageID) -} diff --git a/dive/get_image_handler.go b/dive/get_image_handler.go new file mode 100644 index 0000000..d8e5cec --- /dev/null +++ b/dive/get_image_handler.go @@ -0,0 +1,44 @@ +package dive + +import ( + "fmt" + "github.com/wagoodman/dive/dive/image" + "github.com/wagoodman/dive/dive/image/docker" + "github.com/wagoodman/dive/dive/image/podman" +) + +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.Resolver, error) { + switch engine { + case EngineDocker: + return docker.NewResolver(), nil + case EnginePodman: + return podman.NewResolver(), nil + } + + return nil, fmt.Errorf("unable to determine image provider") +} diff --git a/dive/image/analyzer.go b/dive/image/analyzer.go index 851774b..58b8da8 100644 --- a/dive/image/analyzer.go +++ b/dive/image/analyzer.go @@ -2,17 +2,14 @@ package image import ( "github.com/wagoodman/dive/dive/filetree" - "io" ) type Analyzer interface { - Fetch() (io.ReadCloser, error) - Parse(io.ReadCloser) error Analyze() (*AnalysisResult, error) } type AnalysisResult struct { - Layers []Layer + Layers []*Layer RefTrees []*filetree.FileTree Efficiency float64 SizeBytes uint64 diff --git a/dive/image/docker/analyzer.go b/dive/image/docker/analyzer.go deleted file mode 100644 index 75fb0bf..0000000 --- a/dive/image/docker/analyzer.go +++ /dev/null @@ -1,272 +0,0 @@ -package docker - -import ( - "archive/tar" - "fmt" - "github.com/wagoodman/dive/dive/image" - "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 imageAnalyzer struct { - id string - client *client.Client - jsonFiles map[string][]byte - trees []*filetree.FileTree - layerMap map[string]*filetree.FileTree - layers []*dockerLayer -} - -func NewImageAnalyzer(imageId string) *imageAnalyzer { - return &imageAnalyzer{ - // 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 *imageAnalyzer) 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 - - 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 { - - 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) - 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 *imageAnalyzer) 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 -} - -func (img *imageAnalyzer) 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 -} - -func (img *imageAnalyzer) 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 *imageAnalyzer) 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/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_config.go b/dive/image/docker/config.go similarity index 57% rename from dive/image/docker/image_config.go rename to dive/image/docker/config.go index 704299a..0efcb57 100644 --- a/dive/image/docker/image_config.go +++ b/dive/image/docker/config.go @@ -5,9 +5,9 @@ import ( "github.com/sirupsen/logrus" ) -type imageConfig struct { - History []imageHistoryEntry `json:"history"` - RootFs rootFs `json:"rootfs"` +type config struct { + History []historyEntry `json:"history"` + RootFs rootFs `json:"rootfs"` } type rootFs struct { @@ -15,8 +15,17 @@ type rootFs struct { DiffIds []string `json:"diff_ids"` } -func newDockerImageConfig(configBytes []byte) imageConfig { - var imageConfig imageConfig +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) if err != nil { logrus.Panic(err) diff --git a/dive/image/docker/image_archive.go b/dive/image/docker/image_archive.go new file mode 100644 index 0000000..dd4b38d --- /dev/null +++ b/dive/image/docker/image_archive.go @@ -0,0 +1,187 @@ +package docker + +import ( + "archive/tar" + "fmt" + "github.com/wagoodman/dive/dive/filetree" + "github.com/wagoodman/dive/dive/image" + "io" + "io/ioutil" + "os" + "strings" +) + +type ImageArchive struct { + manifest manifest + config config + layerMap map[string]*filetree.FileTree +} + +func NewImageArchive(tarFile io.ReadCloser) (*ImageArchive, error) { + img := &ImageArchive{ + 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() + + if err == io.EOF { + break + } + + if err != nil { + fmt.Println(err) + os.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, ".tar") { + currentLayer++ + if err != nil { + return img, err + } + layerReader := tar.NewReader(tarReader) + tree, err := processLayerTar(name, layerReader) + + if err != nil { + 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 img, err + } + 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 = 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 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 { + return nil, err + } + + 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.NewFileInfoFromTarHeader(tarReader, header, name)) + } + } + return files, nil +} + +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 { + trees = append(trees, tr) + continue + } + return nil, fmt.Errorf("could not find '%s' in parsed layers", treeName) + } + + // build the layers array + 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! + histIdx := 0 + for idx, tree := range trees { + // ignore empty layers, we are only observing layers with content + historyObj := historyEntry{ + CreatedBy: "(missing)", + } + for nextHistIdx := histIdx; nextHistIdx < len(img.config.History); nextHistIdx++ { + if !img.config.History[nextHistIdx].EmptyLayer { + histIdx = nextHistIdx + break + } + } + if histIdx < len(img.config.History) && !img.config.History[histIdx].EmptyLayer { + historyObj = img.config.History[histIdx] + histIdx++ + } + + historyObj.Size = tree.FileSize + + dockerLayer := layer{ + history: historyObj, + index: idx, + tree: tree, + } + layers = append(layers, dockerLayer.ToLayer()) + } + + return &image.Image{ + 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 new file mode 100644 index 0000000..afc3825 --- /dev/null +++ b/dive/image/docker/image_archive_analysis_test.go @@ -0,0 +1,43 @@ +package docker + +import ( + "testing" +) + +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 := 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) + } + + 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/layer.go b/dive/image/docker/layer.go index 08ff582..408fea1 100644 --- a/dive/image/docker/layer.go +++ b/dive/image/docker/layer.go @@ -1,95 +1,30 @@ package docker import ( - "fmt" "github.com/wagoodman/dive/dive/image" "strings" - "github.com/dustin/go-humanize" "github.com/wagoodman/dive/dive/filetree" ) // 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 -} - -// index returns the relative position of the layer within the image. -func (layer *dockerLayer) Index() int { - return layer.index -} - -// Size returns the number of bytes that this image is. -func (layer *dockerLayer) Size() uint64 { - return layer.history.Size -} - -// Tree returns the file tree representing the current layer. -func (layer *dockerLayer) Tree() *filetree.FileTree { - return layer.tree -} - -// ShortId returns the truncated id of the current layer. -func (layer *dockerLayer) Command() string { - return strings.TrimPrefix(layer.history.CreatedBy, "/bin/sh -c ") -} - -// ShortId returns the truncated id of the current layer. -func (layer *dockerLayer) ShortId() string { - rangeBound := 15 - id := layer.Id() - if length := len(id); length < 15 { - rangeBound = length - } - 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()) +func (l *layer) ToLayer() *image.Layer { + id := strings.Split(l.tree.Name, "/")[0] + return &image.Layer{ + 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, } - 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/resolver.go b/dive/image/docker/resolver.go new file mode 100644 index 0000000..461bbf9 --- /dev/null +++ b/dive/image/docker/resolver.go @@ -0,0 +1,102 @@ +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" +) + +type resolver struct{} + +func NewResolver() *resolver { + return &resolver{} +} + +func (r *resolver) Fetch(id string) (*image.Image, error) { + + reader, err := r.fetchArchive(id) + if err != nil { + return nil, err + } + defer reader.Close() + + img, err := NewImageArchive(reader) + if err != nil { + return nil, err + } + return img.ToImage() +} + +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(id string) (io.ReadCloser, error) { + var err error + var dockerClient *client.Client + + // pull the resolver 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.WithAPIVersionNegotiation()) + dockerClient, err = client.NewClientWithOpts(clientOpts...) + if err != nil { + return nil, err + } + _, _, 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 + "'...") + err = runDockerCmd("pull", id) + if err != nil { + return nil, err + } + } + + readCloser, err := dockerClient.ImageSave(ctx, []string{id}) + if err != nil { + return nil, err + } + + return readCloser, nil +} diff --git a/dive/image/docker/testing.go b/dive/image/docker/testing.go index 8998a29..ac70d7c 100644 --- a/dive/image/docker/testing.go +++ b/dive/image/docker/testing.go @@ -3,18 +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() - analyzer := NewImageAnalyzer("dive-test:latest") - err = analyzer.Parse(f) - if err != nil { - return nil, err - } - return analyzer.Analyze() + + return NewImageArchive(f) +} + +func TestAnalysisFromArchive(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 } diff --git a/dive/image/image.go b/dive/image/image.go new file mode 100644 index 0000000..de72c7b --- /dev/null +++ b/dive/image/image.go @@ -0,0 +1,39 @@ +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 _, file := range inefficiencies { + wastedBytes += uint64(file.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/layer.go b/dive/image/layer.go index 71e48e0..550eba1 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,34 @@ 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 + Names []string + Digest string +} + +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/build.go b/dive/image/podman/build.go new file mode 100644 index 0000000..bfebc49 --- /dev/null +++ b/dive/image/podman/build.go @@ -0,0 +1,29 @@ +// +build linux + +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..1c4b8e8 --- /dev/null +++ b/dive/image/podman/cli.go @@ -0,0 +1,33 @@ +// +build linux + +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_directory.go b/dive/image/podman/image_directory.go new file mode 100644 index 0000000..1b9c289 --- /dev/null +++ b/dive/image/podman/image_directory.go @@ -0,0 +1,128 @@ +// +build linux + +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 { + 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([]string{curImg.ID()}, imgDirRef.layerOrder...) + + // 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... + relativeImagePath := "/" + strings.TrimPrefix(strings.TrimPrefix(path, rootDir), "/") + fileInfo := filetree.NewFileInfo(path, relativeImagePath, 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) + layers := make([]*image.Layer, 0) + + // build the content tree + 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 = append(layers, podmanLayer.ToLayer()) + } + + 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..dd16d28 --- /dev/null +++ b/dive/image/podman/layer.go @@ -0,0 +1,78 @@ +// +build linux + +package podman + +import ( + "context" + "fmt" + podmanImage "github.com/containers/libpod/libpod/image" + "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 + 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.getHistory() + if err != nil { + return "error: " + err.Error() + } + return strings.TrimPrefix(history.CreatedBy, "/bin/sh -c ") +} + +// ShortId returns the truncated id of the current layer. +func (l *layer) ShortId() string { + rangeBound := 15 + id := l.obj.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) ToLayer() *image.Layer { + return &image.Layer{ + Id: l.obj.ID(), + Index: l.index, + Command: l.Command(), + Size: l.Size(), + Tree: l.tree, + Names: l.obj.Names(), + Digest: l.obj.Digest().String(), + } +} diff --git a/dive/image/podman/resolver_linux.go b/dive/image/podman/resolver_linux.go new file mode 100644 index 0000000..4dbda15 --- /dev/null +++ b/dive/image/podman/resolver_linux.go @@ -0,0 +1,130 @@ +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 resolver struct{} + +func NewResolver() *resolver { + return &resolver{} +} + +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) Fetch(id string) (*image.Image, error) { + // 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 + // } + + img, err := r.resolveFromDockerArchive(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 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) + if err != nil { + return nil, err + } + 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))) + if err != nil { + return nil, err + } + return img.ToImage() +} + +func (r *resolver) fetchDockerArchive(id string) (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 == id { + file, err := ioutil.TempFile(os.TempDir(), "dive-resolver-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") +} diff --git a/dive/image/podman/resolver_notlinux.go b/dive/image/podman/resolver_notlinux.go new file mode 100644 index 0000000..49b959a --- /dev/null +++ b/dive/image/podman/resolver_notlinux.go @@ -0,0 +1,22 @@ +// +build !linux + +package podman + +import ( + "fmt" + "github.com/wagoodman/dive/dive/image" +) + +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/dive/image/resolver.go b/dive/image/resolver.go new file mode 100644 index 0000000..aaa2407 --- /dev/null +++ b/dive/image/resolver.go @@ -0,0 +1,6 @@ +package image + +type Resolver interface { + Fetch(id string) (*Image, error) + Build(options []string) (*Image, error) +} diff --git a/go.mod b/go.mod index 0baa9cc..6a0edf1 100644 --- a/go.mod +++ b/go.mod @@ -3,58 +3,42 @@ module github.com/wagoodman/dive go 1.13 require ( - github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect + github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774 // 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.1 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/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/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/morikuni/aec v1.0.0 // indirect 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 ) // 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..6550cf5 100644 --- a/go.sum +++ b/go.sum @@ -1,208 +1,296 @@ 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/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +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/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= 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/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= +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.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.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.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= +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-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= +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/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= 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-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-20190822205725-ed20165a37b4 h1:hB02X3RT4IZUr/dm+zIXpAlpvQPtRyKBEA/CbzshNZ8= +github.com/docker/engine v0.0.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/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +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/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.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-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= 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/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= +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 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/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= +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/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 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= -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/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +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 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= 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= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +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.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,229 +299,390 @@ 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/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= +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/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/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +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.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/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= +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/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= +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/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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= 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 v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +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.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= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +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.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/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +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/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 v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v0.0.0-20190403091019-9b3cdde74fbe/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +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= -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= 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/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/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.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/timakin/bodyclose v0.0.0-20190407043127-4a873e97b2bb/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= +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/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/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/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/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= +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-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 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/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +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-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= +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-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/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-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/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-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-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +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-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-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= -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= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= +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= +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= 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.go b/runtime/export/export.go index 58aa095..4c32b67 100644 --- a/runtime/export/export.go +++ b/runtime/export/export.go @@ -2,61 +2,42 @@ 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 { - data := export{} - data.Layer = make([]exportLayer, len(analysis.Layers)) - data.Image.InefficientFiles = make([]exportReferenceFile, len(analysis.Inefficiencies)) +func NewExport(analysis *diveImage.AnalysisResult) *export { + 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-- { - layer := 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(), + for idx, curLayer := range analysis.Layers { + data.Layer[idx] = layer{ + Index: curLayer.Index, + 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] - 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..f3028a5 100644 --- a/runtime/export/export_test.go +++ b/runtime/export/export_test.go @@ -1,16 +1,14 @@ package export import ( + "github.com/sergi/go-diff/diffmatchpatch" "github.com/wagoodman/dive/dive/image/docker" "testing" ) 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 { @@ -21,84 +19,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" @@ -108,7 +120,7 @@ func Test_Export(t *testing.T) { "sizeBytes": 1220598, "inefficientBytes": 32025, "efficiencyScore": 0.9844212134184309, - "exportReferenceFile": [ + "fileReference": [ { "count": 2, "sizeBytes": 12810, @@ -129,6 +141,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/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..cdbba5b --- /dev/null +++ b/runtime/export/layer.go @@ -0,0 +1,9 @@ +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/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..024b4ca 100644 --- a/runtime/run.go +++ b/runtime/run.go @@ -2,11 +2,10 @@ 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" - "io/ioutil" - "log" "os" "time" @@ -18,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) @@ -28,60 +28,47 @@ func runCi(analysis *image.AnalysisResult, options Options) { evaluator.Report() if pass { - utils.Exit(0) + os.Exit(0) } - 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) + os.Exit(1) } func Run(options Options) { + var err error doExport := options.ExportFile != "" doBuild := len(options.BuildArgs) > 0 + // 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 + + imageResolver, err := dive.GetImageHandler(options.Engine) + if err != nil { + fmt.Printf("cannot determine image provider: %v\n", err) + os.Exit(1) + } + + var img *image.Image + if doBuild { fmt.Println(utils.TitleFormat("Building image...")) - options.ImageId = runBuild(options.BuildArgs) + img, err = imageResolver.Build(options.BuildArgs) + if err != nil { + fmt.Printf("cannot build image: %v\n", err) + 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) + os.Exit(1) + } } - analyzer := dive.GetAnalyzer(options.ImageId) - - fmt.Println(utils.TitleFormat("Fetching image...") + " (this can take a while with large images)") - reader, err := analyzer.Fetch() - 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 should return error for cleanup? if doExport { fmt.Println(utils.TitleFormat(fmt.Sprintf("Analyzing image... (export to '%s')", options.ExportFile))) @@ -89,17 +76,17 @@ 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) + 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) } } @@ -107,12 +94,16 @@ func Run(options Options) { runCi(result, options) } else { if doExport { - utils.Exit(0) + os.Exit(0) } fmt.Println(utils.TitleFormat("Building cache...")) cache := filetree.NewFileTreeCache(result.RefTrees) - cache.Build() + err := cache.Build() + if err != nil { + logrus.Error(err) + os.Exit(1) + } // it appears there is a race condition where termbox.Init() will // block nearly indefinitely when running as the first process in @@ -121,6 +112,11 @@ 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) + os.Exit(1) + } + os.Exit(0) } } diff --git a/runtime/ui/details_controller.go b/runtime/ui/details_controller.go index ab9d7ec..0b9de63 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,24 +122,35 @@ 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()) - // 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()) - _, _ = fmt.Fprintln(controller.view, "\n"+Formatting.Header(vtclean.Clean(imageHeaderStr, false))) + var lines = make([]string, 0) + 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))) + 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 46e9af5..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. @@ -102,7 +100,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 +114,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 @@ -416,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/filetree_viewmodel_test.go b/runtime/ui/filetree_viewmodel_test.go index a0ae5cd..df0e0ca 100644 --- a/runtime/ui/filetree_viewmodel_test.go +++ b/runtime/ui/filetree_viewmodel_test.go @@ -73,16 +73,25 @@ func assertTestData(t *testing.T, actualBytes []byte) { } 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) - 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 stack trees: %v", t.Name(), err) + } + 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) { 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..5695bb5 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" ) @@ -21,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 @@ -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. @@ -211,15 +209,18 @@ 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] +func (controller *LayerController) currentLayer() *image.Layer { + return controller.Layers[controller.LayerIndex] } // 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()) } @@ -261,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 } @@ -283,21 +284,27 @@ 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() - 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) 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 0182607..3b2075a 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,20 +114,21 @@ 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 - } + return UpdateAndRender() + } - err = Controllers.Filter.view.SetCursor(0, 0) - if err != nil { - return err - } + 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 @@ -251,20 +271,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 +307,65 @@ 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() { - for _, view := range Controllers.lookup { - _ = view.Update() +func Update() error { + for _, controller := range Controllers.lookup { + err := controller.Update() + if err != nil { + logrus.Debug("unable to update controller: ") + return err + } } + return nil } // Render flushes the state objects to the screen. -func Render() { - for _, view := range Controllers.lookup { - if view.IsVisible() { - _ = view.Render() +func Render() error { + for _, controller := range Controllers.lookup { + if controller.IsVisible() { + err := controller.Render() + if err != nil { + return err + } } } + return nil } // renderStatusOption formats key help bindings-to-title pairs. @@ -321,7 +378,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,30 +392,39 @@ 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() 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 - 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, 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) @@ -381,15 +447,18 @@ func Run(analysis *image.AnalysisResult, cache filetree.TreeCache) { // } // perform the first update and render now that all resources have been loaded - Update() - Render() + err = UpdateAndRender() + 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 } 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 -} 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() - } -} 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 +}