Merge pull request #234 from wagoodman/add-podman-support
Add initial Podman support
This commit is contained in:
commit
9cebcfd690
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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"]
|
||||
|
@ -1,3 +0,0 @@
|
||||
FROM scratch
|
||||
COPY dist/dive_linux_amd64/dive /
|
||||
ENTRYPOINT ["/dive"]
|
26
Makefile
26
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
|
||||
|
27
README.md
27
README.md
@ -2,7 +2,7 @@
|
||||
[](https://goreportcard.com/report/github.com/wagoodman/dive)
|
||||
[](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.**
|
||||
|
||||

|
||||
|
||||
@ -16,7 +16,7 @@ or if you want to build your image then jump straight into analyzing it:
|
||||
dive build -t <some-tag> .
|
||||
```
|
||||
|
||||
Building on Macbook
|
||||
Building on Macbook (supporting only the Docker container engine)
|
||||
|
||||
```bash
|
||||
docker run --rm -it \
|
||||
@ -40,22 +40,15 @@ CI=true dive <your-image>
|
||||
|
||||
**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 <your-image-tag> --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
|
||||
|
@ -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,
|
||||
|
10
cmd/build.go
10
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,
|
||||
|
22
cmd/root.go
22
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")
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
44
dive/get_image_handler.go
Normal file
44
dive/get_image_handler.go
Normal file
@ -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")
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
}
|
27
dive/image/docker/build.go
Normal file
27
dive/image/docker/build.go
Normal file
@ -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
|
||||
}
|
31
dive/image/docker/cli.go
Normal file
31
dive/image/docker/cli.go
Normal file
@ -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
|
||||
}
|
@ -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)
|
187
dive/image/docker/image_archive.go
Normal file
187
dive/image/docker/image_archive.go
Normal file
@ -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
|
||||
|
||||
}
|
43
dive/image/docker/image_archive_analysis_test.go
Normal file
43
dive/image/docker/image_archive_analysis_test.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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())
|
||||
}
|
||||
|
@ -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)
|
102
dive/image/docker/resolver.go
Normal file
102
dive/image/docker/resolver.go
Normal file
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
39
dive/image/image.go
Normal file
39
dive/image/image.go
Normal file
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
29
dive/image/podman/build.go
Normal file
29
dive/image/podman/build.go
Normal file
@ -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
|
||||
}
|
33
dive/image/podman/cli.go
Normal file
33
dive/image/podman/cli.go
Normal file
@ -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
|
||||
}
|
128
dive/image/podman/image_directory.go
Normal file
128
dive/image/podman/image_directory.go
Normal file
@ -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
|
||||
|
||||
}
|
78
dive/image/podman/layer.go
Normal file
78
dive/image/podman/layer.go
Normal file
@ -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(),
|
||||
}
|
||||
}
|
130
dive/image/podman/resolver_linux.go
Normal file
130
dive/image/podman/resolver_linux.go
Normal file
@ -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")
|
||||
}
|
22
dive/image/podman/resolver_notlinux.go
Normal file
22
dive/image/podman/resolver_notlinux.go
Normal file
@ -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")
|
||||
}
|
6
dive/image/resolver.go
Normal file
6
dive/image/resolver.go
Normal file
@ -0,0 +1,6 @@
|
||||
package image
|
||||
|
||||
type Resolver interface {
|
||||
Fetch(id string) (*Image, error)
|
||||
Build(options []string) (*Image, error)
|
||||
}
|
28
go.mod
28
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
|
||||
|
615
go.sum
615
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=
|
||||
|
@ -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}},
|
||||
|
@ -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,
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
7
runtime/export/file_reference.go
Normal file
7
runtime/export/file_reference.go
Normal file
@ -0,0 +1,7 @@
|
||||
package export
|
||||
|
||||
type fileReference struct {
|
||||
References int `json:"count"`
|
||||
SizeBytes uint64 `json:"sizeBytes"`
|
||||
Path string `json:"file"`
|
||||
}
|
8
runtime/export/image.go
Normal file
8
runtime/export/image.go
Normal file
@ -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"`
|
||||
}
|
9
runtime/export/layer.go
Normal file
9
runtime/export/layer.go
Normal file
@ -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"`
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
151
runtime/ui/ui.go
151
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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user