pull images when not found (closes #25); small fixes
This commit is contained in:
parent
4195374c5b
commit
d6549ea16d
2
Makefile
2
Makefile
@ -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:
|
||||
|
@ -1,8 +1,7 @@
|
||||
# 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
|
||||
|
35
cmd/build.go
35
cmd/build.go
@ -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
|
||||
}
|
||||
|
24
cmd/root.go
24
cmd/root.go
@ -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.
|
||||
|
@ -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
|
@ -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
33
utils/docker.go
Normal 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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user