pull images when not found (closes #25); small fixes

This commit is contained in:
Alex Goodman 2018-10-20 07:42:01 -04:00
parent 4195374c5b
commit d6549ea16d
No known key found for this signature in database
GPG key ID: 05328C611D8A520E
7 changed files with 75 additions and 57 deletions

View file

@ -3,7 +3,7 @@ BIN = dive
all: clean build
run: build
docker image ls | grep "dive-test" >/dev/null || docker build -t dive-test:latest .
docker image ls | grep "dive-test" >/dev/null || docker build -t dive-test:latest -f data/Dockerfile .
./build/$(BIN) dive-test
build:

View file

@ -1,8 +1,7 @@
# dive
[![Go Report Card](https://goreportcard.com/badge/github.com/wagoodman/dive)](https://goreportcard.com/report/github.com/wagoodman/dive)
A tool for interrogating docker images.
**A tool for interrogating docker images.**
To analyze a Docker image simply run dive with an image tag/id/digest:
```bash

View file

@ -7,15 +7,13 @@ import (
"github.com/wagoodman/dive/ui"
"io/ioutil"
"os"
"os/exec"
"strings"
"github.com/wagoodman/dive/utils"
)
// buildCmd represents the build command
var buildCmd = &cobra.Command{
Use: "build",
Short: "Build and analyze a docker image",
Long: `Build and analyze a docker image`,
Use: "build [any valid `docker build` arguments]",
Short: "Builds and analyzes a docker image from a Dockerfile (this is a thin wrapper for the `docker build` command).",
DisableFlagParsing: true,
Run: doBuild,
}
@ -33,7 +31,7 @@ func doBuild(cmd *cobra.Command, args []string) {
defer os.Remove(iidfile.Name())
allArgs := append([]string{"--iidfile", iidfile.Name()}, args...)
err = runDockerCmd("build", allArgs...)
err = utils.RunDockerCmd("build", allArgs...)
if err != nil {
log.Fatal(err)
}
@ -46,28 +44,3 @@ func doBuild(cmd *cobra.Command, args []string) {
manifest, refTrees := image.InitializeData(string(imageId))
ui.Run(manifest, refTrees)
}
// 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.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
}

View file

@ -7,15 +7,18 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"os"
"github.com/tebeka/atexit"
"github.com/k0kubun/go-ansi"
)
var cfgFile string
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "dive",
Use: "dive [IMAGE]",
Short: "Docker Image Visualizer & Explorer",
Long: `Docker Image Visualizer & Explorer`,
Long: `This tool provides a way to discover and explore the contents of a docker image. Additionally the tool estimates
the amount of wasted space and identifies the offending files from the image.`,
Args: cobra.ExactArgs(1),
Run: analyze,
}
@ -28,18 +31,19 @@ func Execute() {
}
}
func exitHandler() {
ansi.CursorShow()
}
func init() {
ansi.CursorHide()
atexit.Register(exitHandler)
cobra.OnInitialize(initConfig)
cobra.OnInitialize(initLogging)
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.dive.yaml)")
// Cobra also supports local flags, which will only run
// when this action is called directly.
rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
// TODO: add config options
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.dive.yaml)")
}
// initConfig reads in config file and ENV variables if set.

View file

@ -1,5 +1,5 @@
FROM alpine:latest
ADD README.md /somefile.txt
ADD ../README.md /somefile.txt
RUN mkdir /root/example
RUN cp /somefile.txt /root/example/somefile1.txt
RUN cp /somefile.txt /root/example/somefile2.txt

View file

@ -13,10 +13,10 @@ import (
"strings"
"github.com/docker/docker/client"
"github.com/k0kubun/go-ansi"
"github.com/wagoodman/dive/filetree"
"github.com/wagoodman/jotframe"
"golang.org/x/net/context"
"github.com/wagoodman/dive/utils"
)
// TODO: this file should be rethought... but since it's only for preprocessing it'll be tech debt for now.
@ -57,11 +57,14 @@ func (pb *ProgressBar) Update(currentValue int64) (hasChanged bool) {
func (pb *ProgressBar) String() string {
width := 40
done := int((pb.percent * width) / 100.0)
if done > width {
done = width
}
todo := width - done
if todo < 0 {
todo = 0
}
head := 1
// if pb.percent >= 100 {
// head = 0
// }
return "[" + strings.Repeat("=", done) + strings.Repeat(">", head) + strings.Repeat(" ", todo) + "]" + fmt.Sprintf(" %d %% (%d/%d)", pb.percent, pb.rawCurrent, pb.rawTotal)
}
@ -194,13 +197,21 @@ func InitializeData(imageID string) ([]*Layer, []*filetree.FileTree) {
var layerMap = make(map[string]*filetree.FileTree)
var trees = make([]*filetree.FileTree, 0)
ansi.CursorHide()
// pull the image if it does not exist
ctx := context.Background()
dockerClient, err := client.NewClientWithOpts()
if err != nil {
fmt.Println("Could not connect to the Docker daemon:"+err.Error())
os.Exit(1)
}
_, _, err = dockerClient.ImageInspectWithRaw(ctx, imageID)
if err != nil {
// don't use the API, the CLI has more informative output
utils.RunDockerCmd("pull", imageID)
}
// save this image to disk temporarily to get the content info
imageTarPath, tmpDir := saveImage(imageID)
// imageTarPath := "/tmp/dive516670682/image.tar"
// tmpDir := "/tmp/dive516670682"
// fmt.Println(tmpDir)
defer os.RemoveAll(tmpDir)
// read through the image contents and build a tree
@ -313,8 +324,6 @@ func InitializeData(imageID string) ([]*Layer, []*filetree.FileTree) {
layerIdx--
}
ansi.CursorShow()
return layers, trees
}
@ -322,7 +331,8 @@ func saveImage(imageID string) (string, string) {
ctx := context.Background()
dockerClient, err := client.NewClientWithOpts()
if err != nil {
panic(err)
fmt.Println("Could not connect to the Docker daemon:"+err.Error())
os.Exit(1)
}
frame := jotframe.NewFixedFrame(0, false, false, true)
@ -331,7 +341,6 @@ func saveImage(imageID string) (string, string) {
io.WriteString(line, " Fetching metadata...")
result, _, err := dockerClient.ImageInspectWithRaw(ctx, imageID)
check(err)
totalSize := result.Size
frame.Remove(line)

33
utils/docker.go Normal file
View file

@ -0,0 +1,33 @@
package utils
import (
"os/exec"
"os"
"strings"
)
// 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.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
}