add shared layer type (instead of interface)
This commit is contained in:
parent
d53d8926bc
commit
a310732060
@ -9,7 +9,7 @@ type Analyzer interface {
|
||||
}
|
||||
|
||||
type AnalysisResult struct {
|
||||
Layers []Layer
|
||||
Layers []*Layer
|
||||
RefTrees []*filetree.FileTree
|
||||
Efficiency float64
|
||||
SizeBytes uint64
|
||||
|
@ -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++
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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{
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -108,7 +108,7 @@ func Test_Export(t *testing.T) {
|
||||
"sizeBytes": 1220598,
|
||||
"inefficientBytes": 32025,
|
||||
"efficiencyScore": 0.9844212134184309,
|
||||
"exportReferenceFile": [
|
||||
"fileReference": [
|
||||
{
|
||||
"count": 2,
|
||||
"sizeBytes": 12810,
|
||||
|
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"`
|
||||
}
|
8
runtime/export/layer.go
Normal file
8
runtime/export/layer.go
Normal 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"`
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user