230 lines
4.4 KiB
Go
230 lines
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"log"
|
|
|
|
"github.com/docker/docker/client"
|
|
"github.com/jroimartin/gocui"
|
|
"golang.org/x/net/context"
|
|
)
|
|
|
|
var data struct {
|
|
refTrees []*FileTree
|
|
manifest *Manifest
|
|
}
|
|
|
|
var views struct {
|
|
treeView *FileTreeView
|
|
layerView *LayerView
|
|
}
|
|
|
|
func check(e error) {
|
|
if e != nil {
|
|
panic(e)
|
|
}
|
|
}
|
|
|
|
func saveImage(readCloser io.ReadCloser) {
|
|
defer readCloser.Close()
|
|
|
|
path := "image"
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
os.Mkdir(path, 0755)
|
|
}
|
|
|
|
fo, err := os.Create("image/cache.tar")
|
|
check(err)
|
|
|
|
defer func() {
|
|
if err := fo.Close(); err != nil {
|
|
panic(err)
|
|
}
|
|
}()
|
|
w := bufio.NewWriter(fo)
|
|
|
|
buf := make([]byte, 1024)
|
|
for {
|
|
n, err := readCloser.Read(buf)
|
|
if err != nil && err != io.EOF {
|
|
panic(err)
|
|
}
|
|
if n == 0 {
|
|
break
|
|
}
|
|
|
|
if _, err := w.Write(buf[:n]); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
if err = w.Flush(); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func demo() {
|
|
ctx := context.Background()
|
|
cli, err := client.NewEnvClient()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// imageID := "golang:alpine"
|
|
imageID := "die-test:latest"
|
|
|
|
fmt.Println("Saving Image...")
|
|
readCloser, err := cli.ImageSave(ctx, []string{imageID})
|
|
check(err)
|
|
saveImage(readCloser)
|
|
|
|
for {
|
|
inspect, _, err := cli.ImageInspectWithRaw(ctx, imageID)
|
|
check(err)
|
|
|
|
history, err := cli.ImageHistory(ctx, imageID)
|
|
check(err)
|
|
|
|
historyStr, err := json.MarshalIndent(history, "", " ")
|
|
check(err)
|
|
|
|
layerStr := ""
|
|
for idx, layer := range inspect.RootFS.Layers {
|
|
prefix := "├── "
|
|
if idx == len(inspect.RootFS.Layers)-1 {
|
|
prefix = "└── "
|
|
}
|
|
layerStr += fmt.Sprintf("%s%s\n", prefix, layer)
|
|
}
|
|
|
|
fmt.Printf("Image: %s\nId: %s\nParent: %s\nLayers: %d\n%sHistory: %s\n", imageID, inspect.ID, inspect.Parent, len(inspect.RootFS.Layers), layerStr, historyStr)
|
|
|
|
fmt.Println("\n")
|
|
|
|
if inspect.Parent == "" {
|
|
break
|
|
} else {
|
|
imageID = inspect.Parent
|
|
}
|
|
}
|
|
fmt.Println("See './image' for the cached image tar")
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
func nextView(g *gocui.Gui, v *gocui.View) error {
|
|
if v == nil || v.Name() == views.layerView.name {
|
|
_, err := g.SetCurrentView(views.treeView.name)
|
|
return err
|
|
}
|
|
_, err := g.SetCurrentView(views.layerView.name)
|
|
return err
|
|
}
|
|
|
|
func cursorDown(g *gocui.Gui, v *gocui.View) error {
|
|
cx, cy := v.Cursor()
|
|
|
|
// if there isn't a next line
|
|
line, err := v.Line(cy + 1)
|
|
if err != nil {
|
|
// todo: handle error
|
|
}
|
|
if len(line) == 0 {
|
|
return nil
|
|
}
|
|
if err := v.SetCursor(cx, cy+1); err != nil {
|
|
ox, oy := v.Origin()
|
|
if err := v.SetOrigin(ox, oy+1); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func cursorUp(g *gocui.Gui, v *gocui.View) error {
|
|
ox, oy := v.Origin()
|
|
cx, cy := v.Cursor()
|
|
if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 {
|
|
if err := v.SetOrigin(ox, oy-1); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func quit(g *gocui.Gui, v *gocui.View) error {
|
|
return gocui.ErrQuit
|
|
}
|
|
|
|
func keybindings(g *gocui.Gui) error {
|
|
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
|
|
return err
|
|
}
|
|
//if err := g.SetKeybinding("main", gocui.MouseLeft, gocui.ModNone, toggleCollapse); err != nil {
|
|
// return err
|
|
//}
|
|
if err := g.SetKeybinding("side", gocui.KeyCtrlSpace, gocui.ModNone, nextView); err != nil {
|
|
return err
|
|
}
|
|
if err := g.SetKeybinding("main", gocui.KeyCtrlSpace, gocui.ModNone, nextView); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func layout(g *gocui.Gui) error {
|
|
maxX, maxY := g.Size()
|
|
splitCol := 50
|
|
if v, err := g.SetView("side", -1, -1, splitCol, maxY); err != nil {
|
|
if err != gocui.ErrUnknownView {
|
|
return err
|
|
}
|
|
views.layerView = NewLayerView("side", g, v, data.manifest)
|
|
views.layerView.keybindings()
|
|
|
|
}
|
|
if v, err := g.SetView("main", splitCol, -1, maxX, maxY); err != nil {
|
|
if err != gocui.ErrUnknownView {
|
|
return err
|
|
}
|
|
|
|
views.treeView = NewFileTreeView("main", g, v, StackRange(data.refTrees, 0))
|
|
views.treeView.keybindings()
|
|
|
|
if _, err := g.SetCurrentView(views.treeView.name); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
demo()
|
|
initializeData()
|
|
|
|
g, err := gocui.NewGui(gocui.OutputNormal)
|
|
if err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
defer g.Close()
|
|
|
|
g.Cursor = false
|
|
//g.Mouse = true
|
|
g.SetManagerFunc(layout)
|
|
|
|
if err := keybindings(g); err != nil {
|
|
log.Panicln(err)
|
|
}
|
|
|
|
if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
|
|
log.Panicln(err)
|
|
}
|
|
|
|
}
|