diff --git a/dive/filetree/file_info.go b/dive/filetree/file_info.go index 603deaf..4ea3887 100644 --- a/dive/filetree/file_info.go +++ b/dive/filetree/file_info.go @@ -46,12 +46,23 @@ func NewFileInfo(realPath, path string, info os.FileInfo) FileInfo { // todo: don't use tar types here, create our own... var fileType byte + var linkName string + var size int64 + if info.Mode() & os.ModeSymlink != 0 { fileType = tar.TypeSymlink + + linkName, err = os.Readlink(realPath) + if err != nil { + logrus.Panic("unable to read link:", realPath, err) + } + } else if info.IsDir() { fileType = tar.TypeDir } else { fileType = tar.TypeReg + + size = info.Size() } var hash uint64 @@ -64,20 +75,12 @@ func NewFileInfo(realPath, path string, info os.FileInfo) FileInfo { hash = getHashFromReader(file) } - var linkName string - if fileType == tar.TypeSymlink { - linkName, err = os.Readlink(realPath) - if err != nil { - logrus.Panic("unable to read link:", realPath, err) - } - } - return FileInfo{ Path: path, TypeFlag: fileType, Linkname: linkName, hash: hash, - Size: info.Size(), + Size: size, Mode: info.Mode(), // todo: support UID/GID Uid: -1, diff --git a/dive/image/docker/layer.go b/dive/image/docker/layer.go index c7581d9..4e46933 100644 --- a/dive/image/docker/layer.go +++ b/dive/image/docker/layer.go @@ -17,11 +17,15 @@ type layer struct { // String represents a layer in a columnar format. func (l *layer) ToLayer() *image.Layer { + id := strings.Split(l.tree.Name, "/")[0] return &image.Layer{ - Id: l.history.ID, + Id: id, Index: l.index, Command: strings.TrimPrefix(l.history.CreatedBy, "/bin/sh -c "), Size: l.history.Size, Tree: l.tree, + // todo: query docker api for tags + Names: []string{"(unavailable)"}, + Digest: l.history.ID, } } diff --git a/dive/image/layer.go b/dive/image/layer.go index b92eabe..70354ad 100644 --- a/dive/image/layer.go +++ b/dive/image/layer.go @@ -16,6 +16,8 @@ type Layer struct { Command string Size uint64 Tree *filetree.FileTree + Names []string + Digest string } func (l *Layer) ShortId() string { diff --git a/dive/image/podman/image_directory.go b/dive/image/podman/image_directory.go index 333e967..9cad70c 100644 --- a/dive/image/podman/image_directory.go +++ b/dive/image/podman/image_directory.go @@ -28,9 +28,6 @@ func NewImageDirectoryRef(img *podmanImage.Image) (*ImageDirectoryRef, error) { curImg := img for { - // h, _ := img.History(ctx) - // fmt.Printf("%+v %+v %+v\n", img.ID(), h[0].Size, h[0].CreatedBy) - driver, err := curImg.DriverData() if err != nil { return nil, fmt.Errorf("graph driver error: %+v", err) @@ -79,7 +76,8 @@ func processLayer(name, rootDir string) (*filetree.FileTree, error) { return err } // add this file to the tree... - fileInfo := filetree.NewFileInfo(path, "/"+strings.TrimPrefix(path, rootDir), info) + relativeImagePath := "/"+strings.TrimPrefix(strings.TrimPrefix(path, rootDir), "/") + fileInfo := filetree.NewFileInfo(path, relativeImagePath, info) tree.FileSize += uint64(fileInfo.Size) diff --git a/dive/image/podman/layer.go b/dive/image/podman/layer.go index daad3d3..b59eab5 100644 --- a/dive/image/podman/layer.go +++ b/dive/image/podman/layer.go @@ -2,6 +2,7 @@ package podman import ( "context" + "fmt" podmanImage "github.com/containers/libpod/libpod/image" "github.com/wagoodman/dive/dive/filetree" "github.com/wagoodman/dive/dive/image" @@ -11,20 +12,42 @@ import ( // Layer represents a Docker image layer and metadata type layer struct { obj *podmanImage.Image + history *podmanImage.History index int tree *filetree.FileTree } +func (l *layer) getHistory() (*podmanImage.History, error) { + if l.history != nil { + return l.history, nil + } + history, err := l.obj.History(context.TODO()) + if err != nil { + return nil, err + } + if len(history) > 0 { + l.history = history[0] + return history[0], nil + } + return nil, fmt.Errorf("could not find history") +} + +func (l *layer) Size() uint64 { + history, err := l.getHistory() + if err != nil { + // todo: what should be done here??? + panic(err) + } + return uint64(history.Size) +} + // ShortId returns the truncated id of the current layer. func (l *layer) Command() string { - history, err := l.obj.History(context.TODO()) + history, err := l.getHistory() if err != nil { return "error: " + err.Error() } - if len(history) > 0 { - return strings.TrimPrefix(history[0].CreatedBy, "/bin/sh -c ") - } - return "unknown" + return strings.TrimPrefix(history.CreatedBy, "/bin/sh -c ") } // ShortId returns the truncated id of the current layer. @@ -45,7 +68,9 @@ func (l *layer) ToLayer() *image.Layer { Id: l.obj.ID(), Index: l.index, Command: l.Command(), - Size: uint64(l.obj.ImageData.Size), + Size: l.Size(), Tree: l.tree, + Names: l.obj.Names(), + Digest: l.obj.Digest().String(), } } diff --git a/runtime/ui/details_controller.go b/runtime/ui/details_controller.go index f1d9c5b..61ca251 100644 --- a/runtime/ui/details_controller.go +++ b/runtime/ui/details_controller.go @@ -131,7 +131,13 @@ func (controller *DetailsController) Render() error { controller.view.Clear() var lines = make([]string, 0) - lines = append(lines, Formatting.Header("Digest: ")+currentLayer.Id) + if currentLayer.Names != nil && len(currentLayer.Names) > 0 { + lines = append(lines, Formatting.Header("Tags: ")+strings.Join(currentLayer.Names, ", ")) + } else { + lines = append(lines, Formatting.Header("Tags: ")+ "(none)") + } + lines = append(lines, Formatting.Header("Id: ")+currentLayer.Id) + lines = append(lines, Formatting.Header("Digest: ")+currentLayer.Digest) lines = append(lines, Formatting.Header("Command:")) lines = append(lines, currentLayer.Command) lines = append(lines, "\n"+Formatting.Header(vtclean.Clean(imageHeaderStr, false))) diff --git a/runtime/ui/layer_controller.go b/runtime/ui/layer_controller.go index 56cce36..5695bb5 100644 --- a/runtime/ui/layer_controller.go +++ b/runtime/ui/layer_controller.go @@ -210,7 +210,7 @@ func (controller *LayerController) SetCursor(layer int) error { // currentLayer returns the Layer object currently selected. func (controller *LayerController) currentLayer() *image.Layer { - return controller.Layers[(len(controller.Layers)-1)-controller.LayerIndex] + return controller.Layers[controller.LayerIndex] } // setCompareMode switches the layer comparison between a single-layer comparison to an aggregated comparison.