add shared layer type (instead of interface)

This commit is contained in:
Alex Goodman 2019-10-07 12:09:31 -04:00
parent d53d8926bc
commit a310732060
No known key found for this signature in database
GPG Key ID: 98AF011C5C78EB7E
14 changed files with 104 additions and 138 deletions

View File

@ -9,7 +9,7 @@ type Analyzer interface {
}
type AnalysisResult struct {
Layers []Layer
Layers []*Layer
RefTrees []*filetree.FileTree
Efficiency float64
SizeBytes uint64

View File

@ -147,7 +147,7 @@ func (img *ImageArchive) ToImage() (*image.Image, error) {
}
// build the layers array
layers := make([]image.Layer, len(trees))
layers := make([]*image.Layer, len(trees))
// 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)
@ -174,11 +174,12 @@ func (img *ImageArchive) ToImage() (*image.Image, error) {
historyObj.Size = tree.FileSize
layers[layerIdx] = &layer{
dockerLayer := layer{
history: historyObj,
index: tarPathIdx,
tree: trees[layerIdx],
}
layers[layerIdx] = dockerLayer.ToLayer()
tarPathIdx++
}

View File

@ -1,11 +1,9 @@
package docker
import (
"fmt"
"github.com/wagoodman/dive/dive/image"
"strings"
"github.com/dustin/go-humanize"
"github.com/wagoodman/dive/dive/filetree"
)
@ -16,52 +14,14 @@ type layer struct {
tree *filetree.FileTree
}
// ShortId returns the truncated id of the current layer.
func (l *layer) Id() string {
return l.history.ID
}
// index returns the relative position of the layer within the image.
func (l *layer) Index() int {
return l.index
}
// Size returns the number of bytes that this image is.
func (l *layer) Size() uint64 {
return l.history.Size
}
// Tree returns the file tree representing the current layer.
func (l *layer) Tree() *filetree.FileTree {
return l.tree
}
// ShortId returns the truncated id of the current layer.
func (l *layer) Command() string {
return strings.TrimPrefix(l.history.CreatedBy, "/bin/sh -c ")
}
// ShortId returns the truncated id of the current layer.
func (l *layer) ShortId() string {
rangeBound := 15
id := l.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) String() string {
if l.index == 0 {
return fmt.Sprintf(image.LayerFormat,
humanize.Bytes(l.Size()),
"FROM "+l.ShortId())
func (l *layer) ToLayer() *image.Layer {
return &image.Layer{
Id: l.history.ID,
Index: l.index,
Command: strings.TrimPrefix(l.history.CreatedBy, "/bin/sh -c "),
Size: l.history.Size,
Tree: l.tree,
}
return fmt.Sprintf(image.LayerFormat,
humanize.Bytes(l.Size()),
l.Command())
}

View File

@ -6,7 +6,7 @@ import (
type Image struct {
Trees []*filetree.FileTree
Layers []Layer
Layers []*Layer
}
func (img *Image) Analyze() (*AnalysisResult, error) {
@ -15,9 +15,9 @@ func (img *Image) Analyze() (*AnalysisResult, error) {
var sizeBytes, userSizeBytes uint64
for i, v := range img.Layers {
sizeBytes += v.Size()
sizeBytes += v.Size
if i != 0 {
userSizeBytes += v.Size()
userSizeBytes += v.Size
}
}

View File

@ -1,6 +1,8 @@
package image
import (
"fmt"
"github.com/dustin/go-humanize"
"github.com/wagoodman/dive/dive/filetree"
)
@ -8,12 +10,32 @@ 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
}
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)
}

View File

@ -110,17 +110,18 @@ func (img *ImageDirectoryRef) ToImage() (*image.Image, error) {
return nil, fmt.Errorf("could not find '%s' in parsed trees", id)
}
layers := make([]image.Layer, len(trees))
layers := make([]*image.Layer, len(trees))
// 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!
// Note: history is not required metadata in an OCI image!
for layerIdx, id := range img.layerOrder {
layers[layerIdx] = &layer{
podmanLayer := layer{
obj: img.layerMap[id],
index: layerIdx,
tree: trees[layerIdx],
}
layers[layerIdx] = podmanLayer.ToLayer()
}
return &image.Image{

View File

@ -1,9 +1,7 @@
package podman
import (
"fmt"
podmanImage "github.com/containers/libpod/libpod/image"
"github.com/dustin/go-humanize"
"github.com/wagoodman/dive/dive/filetree"
"github.com/wagoodman/dive/dive/image"
"strings"
@ -16,36 +14,19 @@ type layer struct {
tree *filetree.FileTree
}
// ShortId returns the truncated id of the current layer.
func (l *layer) Id() string {
return l.obj.ID()
}
// index returns the relative position of the layer within the image.
func (l *layer) Index() int {
return l.index
}
// Size returns the number of bytes that this image is.
func (l *layer) Size() uint64 {
return uint64(l.obj.ImageData.Size)
}
// Tree returns the file tree representing the current layer.
func (l *layer) Tree() *filetree.FileTree {
return l.tree
}
// ShortId returns the truncated id of the current layer.
func (l *layer) Command() string {
// todo: is 0 right?
return strings.TrimPrefix(l.obj.ImageData.History[0].CreatedBy, "/bin/sh -c ")
if len(l.obj.ImageData.History) > 0 {
hist := l.obj.ImageData.History
return strings.TrimPrefix(hist[len(hist)-1].CreatedBy, "/bin/sh -c ")
}
return "unknown"
}
// ShortId returns the truncated id of the current layer.
func (l *layer) ShortId() string {
rangeBound := 15
id := l.Id()
id := l.obj.ID()
if length := len(id); length < 15 {
rangeBound = length
}
@ -55,14 +36,12 @@ func (l *layer) ShortId() string {
}
// String represents a layer in a columnar format.
func (l *layer) String() string {
if l.index == 0 {
return fmt.Sprintf(image.LayerFormat,
humanize.Bytes(l.Size()),
"FROM "+l.ShortId())
func (l *layer) ToLayer() *image.Layer {
return &image.Layer{
Id: l.obj.ID(),
Index: l.index,
Command: l.Command(),
Size: uint64(l.obj.ImageData.Size),
Tree: l.tree,
}
return fmt.Sprintf(image.LayerFormat,
humanize.Bytes(l.Size()),
l.Command())
}

View File

@ -2,50 +2,30 @@ 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 {
func NewExport(analysis *diveImage.AnalysisResult) *export {
data := export{}
data.Layer = make([]exportLayer, len(analysis.Layers))
data.Image.InefficientFiles = make([]exportReferenceFile, len(analysis.Inefficiencies))
data.Layer = make([]layer, len(analysis.Layers))
data.Image.InefficientFiles = make([]fileReference, len(analysis.Inefficiencies))
// export layers in order
for revIdx := len(analysis.Layers) - 1; revIdx >= 0; revIdx-- {
layer := analysis.Layers[revIdx]
curLayer := 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(),
data.Layer[idx] = layer{
Index: curLayer.Index,
DigestID: curLayer.Id,
SizeBytes: curLayer.Size,
Command: curLayer.Command,
}
}
@ -56,7 +36,7 @@ func NewExport(analysis *image.AnalysisResult) *export {
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,

View File

@ -108,7 +108,7 @@ func Test_Export(t *testing.T) {
"sizeBytes": 1220598,
"inefficientBytes": 32025,
"efficiencyScore": 0.9844212134184309,
"exportReferenceFile": [
"fileReference": [
{
"count": 2,
"sizeBytes": 12810,

View 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
View 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"`
}

8
runtime/export/layer.go Normal file
View File

@ -0,0 +1,8 @@
package export
type layer struct {
Index int `json:"index"`
DigestID string `json:"digestId"`
SizeBytes uint64 `json:"sizeBytes"`
Command string `json:"command"`
}

View File

@ -131,9 +131,9 @@ func (controller *DetailsController) Render() error {
controller.view.Clear()
var lines = make([]string, 0)
lines = append(lines, Formatting.Header("Digest: ")+currentLayer.Id())
lines = append(lines, Formatting.Header("Digest: ")+currentLayer.Id)
lines = append(lines, Formatting.Header("Command:"))
lines = append(lines, currentLayer.Command())
lines = append(lines, currentLayer.Command)
lines = append(lines, "\n"+Formatting.Header(vtclean.Clean(imageHeaderStr, false)))
lines = append(lines, imageSizeStr)
lines = append(lines, wastedSpaceStr)

View File

@ -20,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
@ -32,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, err error) {
func NewLayerController(name string, gui *gocui.Gui, layers []*image.Layer) (controller *LayerController, err error) {
controller = new(LayerController)
// populate main fields
@ -209,7 +209,7 @@ func (controller *LayerController) SetCursor(layer int) error {
}
// currentLayer returns the Layer object currently selected.
func (controller *LayerController) currentLayer() image.Layer {
func (controller *LayerController) currentLayer() *image.Layer {
return controller.Layers[(len(controller.Layers)-1)-controller.LayerIndex]
}
@ -262,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
}