dive-zfs/dive/image/zfs/resolver.go
norohind 534bb92185
Some checks failed
Validations / Static analysis (push) Failing after 32m53s
Validations / Unit tests (ubuntu-latest) (push) Failing after 10m54s
Validations / Build snapshot artifacts (push) Failing after 32m5s
Validations / Acceptance tests (Linux) (push) Has been skipped
Validations / Acceptance tests (Mac) (push) Has been skipped
Validations / Acceptance tests (Windows) (push) Has been skipped
PoC: zfs support
2025-02-27 21:28:51 +01:00

125 lines
2.9 KiB
Go

package zfs
import (
"errors"
"fmt"
"github.com/mistifyio/go-zfs"
"github.com/sirupsen/logrus"
"github.com/wagoodman/dive/dive/filetree"
"github.com/wagoodman/dive/dive/image"
"os"
"os/exec"
"strings"
)
type resolver struct{}
func NewResolverFromEngine() *resolver {
return &resolver{}
}
func (r *resolver) Build(args []string) (*image.Image, error) {
return nil, errors.New("can't build ZFS")
}
func (r *resolver) Fetch(id string) (*image.Image, error) {
img, err := r.resolveFromZfsDataset(id)
if err == nil {
return img, err
}
return nil, fmt.Errorf("unable to resolve image '%s': %+v", id, err)
}
func filterOutDatasetSnapshots(snapshots []*zfs.Dataset, datasetName string) []*zfs.Dataset {
var result []*zfs.Dataset
for _, snap := range snapshots {
if strings.HasPrefix(snap.Name, datasetName+"@") {
result = append(result, snap)
}
}
return result
}
func iterate(path string) ([]string, error) {
cmd := exec.Command("find", path, "-type", "f", "-o", "-type", "d")
//cmd.Stderr = os.Stderr
stdout, _ := cmd.Output()
//if err != nil {
// println("Error is")
// println(err.Error())
// panic(err)
//}
//println("Returning iterate")
//println(stdout)
//time.Sleep(100000)
return strings.Split(string(stdout), "\n"), nil
}
func (r *resolver) resolveFromZfsDataset(id string) (*image.Image, error) {
ds, err := zfs.GetDataset(id)
if err != nil {
return nil, err
}
snapshots, err := ds.Snapshots()
snapshots = filterOutDatasetSnapshots(snapshots, ds.Name)
if err != nil {
return nil, err
}
var trees []*filetree.FileTree
var layers []*image.Layer
dsMountpoint := ds.Mountpoint
if dsMountpoint == "" {
return nil, errors.New("Failed to find mountpoint for " + ds.Name)
}
for idx, snap := range snapshots {
snapName := strings.TrimPrefix(snap.Name, ds.Name+"@")
logrus.Info("Processing snapshot " + snapName)
snapMountpoint := dsMountpoint + ".zfs/snapshot/" + snapName
tree := filetree.NewFileTree()
files, err := iterate(snapMountpoint)
if err != nil {
return nil, err
}
for _, realPath := range files {
virtualPath := strings.TrimPrefix(realPath, snapMountpoint)
if virtualPath == "" {
continue
}
osStat, err := os.Lstat(realPath)
if err != nil {
return nil, err
}
//print("Adding virtualPath: " + virtualPath)
println("Adding realPath: " + realPath)
_, _, err = tree.AddPath(virtualPath, filetree.NewFileInfo(realPath, virtualPath, osStat))
if err != nil {
return nil, err
}
}
trees = append(trees, tree)
layers = append(layers, &image.Layer{
Id: snapName,
Index: idx,
Command: "zfs snapshot command",
Size: uint64(tree.VisibleSize()),
Tree: tree,
Names: make([]string, 0),
Digest: "zfs snapshot digest-" + snapName,
})
}
logrus.Info("Returning resolved zfs image obj")
return &image.Image{
Trees: trees,
Layers: layers,
}, nil
}