dive/dive/image/podman/image_directory.go
2019-10-08 09:38:41 -04:00

129 lines
3.1 KiB
Go

// +build linux
package podman
import (
"context"
"fmt"
podmanImage "github.com/containers/libpod/libpod/image"
"github.com/wagoodman/dive/dive/filetree"
"github.com/wagoodman/dive/dive/image"
"os"
"path/filepath"
"strings"
)
type ImageDirectoryRef struct {
layerOrder []string
treeMap map[string]*filetree.FileTree
layerMap map[string]*podmanImage.Image
}
func NewImageDirectoryRef(img *podmanImage.Image) (*ImageDirectoryRef, error) {
imgDirRef := &ImageDirectoryRef{
layerOrder: make([]string, 0),
treeMap: make(map[string]*filetree.FileTree),
layerMap: make(map[string]*podmanImage.Image),
}
ctx := context.TODO()
curImg := img
for {
driver, err := curImg.DriverData()
if err != nil {
return nil, fmt.Errorf("graph driver error: %+v", err)
}
if driver.Name != "overlay" {
return nil, fmt.Errorf("unsupported graph driver: %s", driver.Name)
}
rootDir, exists := driver.Data["UpperDir"]
if !exists {
return nil, fmt.Errorf("graph has no upper dir")
}
if _, err := os.Stat(rootDir); os.IsNotExist(err) {
return nil, fmt.Errorf("graph root dir does not exist: %s", rootDir)
}
// build tree from directory...
tree, err := processLayer(curImg.ID(), rootDir)
if err != nil {
return nil, err
}
// record the tree and layer info
imgDirRef.treeMap[curImg.ID()] = tree
imgDirRef.layerMap[curImg.ID()] = curImg
imgDirRef.layerOrder = append([]string{curImg.ID()}, imgDirRef.layerOrder...)
// continue to the next image
curImg, err = curImg.GetParent(ctx)
if err != nil || curImg == nil {
break
}
}
return imgDirRef, nil
}
func processLayer(name, rootDir string) (*filetree.FileTree, error) {
tree := filetree.NewFileTree()
tree.Name = name
err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// add this file to the tree...
relativeImagePath := "/" + strings.TrimPrefix(strings.TrimPrefix(path, rootDir), "/")
fileInfo := filetree.NewFileInfo(path, relativeImagePath, info)
tree.FileSize += uint64(fileInfo.Size)
_, _, err = tree.AddPath(fileInfo.Path, fileInfo)
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, fmt.Errorf("unable to walk upper directory tree")
}
return tree, nil
}
func (img *ImageDirectoryRef) ToImage() (*image.Image, error) {
trees := make([]*filetree.FileTree, 0)
layers := make([]*image.Layer, 0)
// build the content tree
for layerIdx, id := range img.layerOrder {
tr, exists := img.treeMap[id]
if !exists {
return nil, fmt.Errorf("could not find '%s' in parsed trees", id)
}
trees = append(trees, tr)
// 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 an OCI image!
podmanLayer := layer{
obj: img.layerMap[id],
index: layerIdx,
tree: trees[layerIdx],
}
layers = append(layers, podmanLayer.ToLayer())
}
return &image.Image{
Trees: trees,
Layers: layers,
}, nil
}