diff --git a/cmd/commands/env.go b/cmd/commands/env.go index ac83990..5b2d9f1 100644 --- a/cmd/commands/env.go +++ b/cmd/commands/env.go @@ -47,6 +47,7 @@ func explorerEnvironment(clictx *cli.Context) (context.Context, explorers.Contai dockerroot := clictx.GlobalString("docker-root") metadatafile := clictx.GlobalString("metadata-file") snapshotfile := clictx.GlobalString("snapshot-metadata-file") + layercache := clictx.GlobalString("layer-cache") // Read support container data if provided using global switch. var sc *explorers.SupportContainer @@ -119,7 +120,10 @@ func explorerEnvironment(clictx *cli.Context) (context.Context, explorers.Contai "snapshotfile": snapshotfile, }).Debug("containerd container environment") - cde, err := containerd.NewExplorer(imageroot, containerdroot, metadatafile, snapshotfile, sc) + if !clictx.GlobalBool("use-layer-cache") { + layercache = "" + } + cde, err := containerd.NewExplorer(imageroot, containerdroot, metadatafile, snapshotfile, layercache, sc) if err != nil { return ctx, nil, func() { cancel() }, err } diff --git a/cmd/main.go b/cmd/main.go index 13f0488..7310911 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -84,6 +84,15 @@ func main() { Name: "snapshot-metadata-file, s", Usage: "specify the path to containerd snapshot metadata file i.e. metadata.db.", }, + cli.BoolFlag{ + Name: "use-layer-cache", + Usage: "attempt to use cached layers where layers are symlinks", + }, + cli.StringFlag{ + Name: "layer-cache", + Usage: "cached layer folder within the snapshot root", + Value: "layers", + }, cli.StringFlag{ Name: "namespace, n", Usage: "specify container namespace", diff --git a/explorers/containerd/containerd.go b/explorers/containerd/containerd.go index 763c207..07d9e32 100644 --- a/explorers/containerd/containerd.go +++ b/explorers/containerd/containerd.go @@ -38,16 +38,17 @@ import ( ) type explorer struct { - imageroot string // mounted image path - root string // containerd root - manifest string // path to manifest database file i.e. meta.db - snapshot string // path to snapshot database file i.e. metadata.db - mdb *bolt.DB // manifest database - sc *explorers.SupportContainer // support container structure object + imageroot string // mounted image path + root string // containerd root + manifest string // path to manifest database file i.e. meta.db + snapshot string // path to snapshot database file i.e. metadata.db + layercache string // layer cache folder within snapshot root + mdb *bolt.DB // manifest database + sc *explorers.SupportContainer // support container structure object } // NewExplorer returns a ContainerExplorer interface to explore containerd. -func NewExplorer(imageroot string, root string, manifest string, snapshot string, sc *explorers.SupportContainer) (explorers.ContainerExplorer, error) { +func NewExplorer(imageroot string, root string, manifest string, snapshot string, layercache string, sc *explorers.SupportContainer) (explorers.ContainerExplorer, error) { opt := &bolt.Options{ ReadOnly: true, } @@ -57,12 +58,13 @@ func NewExplorer(imageroot string, root string, manifest string, snapshot string } return &explorer{ - imageroot: imageroot, - root: root, - manifest: manifest, - snapshot: snapshot, - mdb: db, - sc: sc, + imageroot: imageroot, + root: root, + manifest: manifest, + snapshot: snapshot, + layercache: layercache, + mdb: db, + sc: sc, }, nil } @@ -260,7 +262,7 @@ func (e *explorer) ListSnapshots(ctx context.Context) ([]explorers.SnapshotKeyIn }).Error(err) } - store := NewSnaptshotStore(e.root, e.mdb, ssdb) + store := NewSnaptshotStore(e.root, e.layercache, e.mdb, ssdb) for _, ns := range nss { ctx = namespaces.WithNamespace(ctx, ns) @@ -480,7 +482,7 @@ func (e *explorer) MountContainer(ctx context.Context, containerid string, mount } // snapshot store - ssstore := NewSnaptshotStore(e.root, e.mdb, ssdb) + ssstore := NewSnaptshotStore(e.root, e.layercache, e.mdb, ssdb) var mountArgs []string hasWorkDir := false snapshotRoot, _ := filepath.Split(e.snapshot) diff --git a/explorers/containerd/snapshot.go b/explorers/containerd/snapshot.go index 2e00169..78f5762 100644 --- a/explorers/containerd/snapshot.go +++ b/explorers/containerd/snapshot.go @@ -21,6 +21,7 @@ import ( "encoding/binary" "fmt" "io/fs" + "os" "path/filepath" "strings" @@ -34,9 +35,10 @@ import ( ) type snapshotStore struct { - root string // containerd root directory - db *bolt.DB - sdb *bolt.DB + root string // containerd root directory + layercache string + db *bolt.DB + sdb *bolt.DB } // NewSnapshotStore returns snapshotStore which handles viewing of snapshot information @@ -54,11 +56,12 @@ type snapshotStore struct { // Snapshot path in snapshot database: metadata.db/v1/snapshots/ // - id - Snapshot file system ID i.e. /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots//fs // - kind - ACTIVE vs COMMITTED -func NewSnaptshotStore(root string, db *bolt.DB, sdb *bolt.DB) *snapshotStore { +func NewSnaptshotStore(root string, layercache string, db *bolt.DB, sdb *bolt.DB) *snapshotStore { return &snapshotStore{ - root: root, - db: db, - sdb: sdb, + root: root, + layercache: layercache, + db: db, + sdb: sdb, } } @@ -213,6 +216,18 @@ func (s *snapshotStore) OverlayPath(ctx context.Context, container containers.Co upperdir = filepath.Join(snapshotroot, "snapshots", fmt.Sprintf("%d", upperdirID), "fs") workdir = filepath.Join(snapshotroot, "snapshots", fmt.Sprintf("%d", upperdirID), "work") + if s.layercache != "" { + symlink, err := os.Readlink(upperdir) + if err == nil { + log.WithFields(log.Fields{ + "id": upperdirID, + "symlink": symlink, + }).Debug("upperdir") + _, layer := filepath.Split(symlink) + upperdir = filepath.Join(snapshotroot, s.layercache, layer) + } + } + // compute lowerdir for _, ssk := range snapshotkeys[1:] { id, err := getSnapshotID(tx, ssk) @@ -220,6 +235,17 @@ func (s *snapshotStore) OverlayPath(ctx context.Context, container containers.Co return err } ldir := filepath.Join(snapshotroot, "snapshots", fmt.Sprintf("%d", id), "fs") + if s.layercache != "" { + symlink, err := os.Readlink(ldir) + if err == nil { + log.WithFields(log.Fields{ + "id": id, + "symlink": symlink, + }).Debug("layer") + _, layer := filepath.Split(symlink) + ldir = filepath.Join(snapshotroot, "layers", layer) + } + } if lowerdir == "" { lowerdir = ldir