summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Goodman <wagoodman@gmail.com>2018-06-24 12:20:12 -0400
committerAlex Goodman <wagoodman@gmail.com>2018-06-24 12:20:12 -0400
commit3590a7cf4666d1c58aa942fea112b0da11f23433 (patch)
tree1ad547ebbc94dfb5a6e2126a2cbaecb82c02e28d
parent90c86234c4a99e2a36ce000a8ffde3c293c5f2fa (diff)
added file attributes + headers
-rw-r--r--.gitignore2
-rw-r--r--filetree/data.go37
-rw-r--r--filetree/node.go56
-rw-r--r--filetree/tree.go14
-rw-r--r--image/image.go12
-rw-r--r--ui/filetreeview.go16
-rw-r--r--ui/layerview.go17
-rw-r--r--ui/statusview.go2
-rw-r--r--ui/ui.go39
9 files changed, 140 insertions, 55 deletions
diff --git a/.gitignore b/.gitignore
index 79dd381..cada87e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,7 +12,7 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
-
+/tmp
/build
/_vendor*
/vendor
diff --git a/filetree/data.go b/filetree/data.go
index 05114b9..7617842 100644
--- a/filetree/data.go
+++ b/filetree/data.go
@@ -18,7 +18,7 @@ const (
type NodeData struct {
ViewInfo ViewInfo
- FileInfo *FileInfo
+ FileInfo FileInfo
DiffType DiffType
}
@@ -28,9 +28,10 @@ type ViewInfo struct {
}
type FileInfo struct {
- Path string
- Typeflag byte
- MD5sum [16]byte
+ Path string
+ Typeflag byte
+ MD5sum [16]byte
+ TarHeader tar.Header
}
type DiffType int
@@ -38,7 +39,7 @@ type DiffType int
func NewNodeData() (*NodeData) {
return &NodeData{
ViewInfo: *NewViewInfo(),
- FileInfo: nil,
+ FileInfo: FileInfo{},
DiffType: Unchanged,
}
}
@@ -46,7 +47,7 @@ func NewNodeData() (*NodeData) {
func (data *NodeData) Copy() (*NodeData) {
return &NodeData{
ViewInfo: *data.ViewInfo.Copy(),
- FileInfo: data.FileInfo.Copy(),
+ FileInfo: *data.FileInfo.Copy(),
DiffType: data.DiffType,
}
}
@@ -71,6 +72,7 @@ func NewFileInfo(reader *tar.Reader, header *tar.Header, path string) FileInfo {
Path: path,
Typeflag: header.Typeflag,
MD5sum: [16]byte{},
+ TarHeader: *header,
}
}
fileBytes := make([]byte, header.Size)
@@ -78,10 +80,12 @@ func NewFileInfo(reader *tar.Reader, header *tar.Header, path string) FileInfo {
if err != nil && err != io.EOF {
panic(err)
}
+
return FileInfo{
- Path: path,
- Typeflag: header.Typeflag,
- MD5sum: md5.Sum(fileBytes),
+ Path: path,
+ Typeflag: header.Typeflag,
+ MD5sum: md5.Sum(fileBytes),
+ TarHeader: *header,
}
}
@@ -112,19 +116,14 @@ func (data *FileInfo) Copy() *FileInfo {
return nil
}
return &FileInfo{
- Path: data.Path,
- Typeflag: data.Typeflag,
- MD5sum: data.MD5sum,
+ Path: data.Path,
+ Typeflag: data.Typeflag,
+ MD5sum: data.MD5sum,
+ TarHeader: data.TarHeader,
}
}
-func (data *FileInfo) getDiffType(other *FileInfo) DiffType {
- if data == nil && other == nil {
- return Unchanged
- }
- if data == nil || other == nil {
- return Changed
- }
+func (data *FileInfo) getDiffType(other FileInfo) DiffType {
if data.Typeflag == other.Typeflag {
if bytes.Compare(data.MD5sum[:], other.MD5sum[:]) == 0 {
return Unchanged
diff --git a/filetree/node.go b/filetree/node.go
index 7acd9d4..979ecf1 100644
--- a/filetree/node.go
+++ b/filetree/node.go
@@ -6,6 +6,13 @@ import (
"github.com/fatih/color"
"fmt"
+ "github.com/phayes/permbits"
+ "github.com/dustin/go-humanize"
+ "github.com/wagoodman/docker-image-explorer/_vendor-20180604210951/github.com/Microsoft/go-winio/archive/tar"
+)
+
+const (
+ AttributeFormat = "%s%s %10s %10s "
)
type FileNode struct {
@@ -16,13 +23,12 @@ type FileNode struct {
Children map[string]*FileNode
}
-func NewNode(parent *FileNode, name string, data *FileInfo) (node *FileNode) {
+func NewNode(parent *FileNode, name string, data FileInfo) (node *FileNode) {
node = new(FileNode)
node.Name = name
node.Data = *NewNodeData()
- if data != nil {
- node.Data.FileInfo = data.Copy()
- }
+ node.Data.FileInfo = *data.Copy()
+
node.Children = make(map[string]*FileNode)
node.Parent = parent
if parent != nil {
@@ -42,11 +48,11 @@ func (node *FileNode) Copy(parent *FileNode) *FileNode {
return newNode
}
-func (node *FileNode) AddChild(name string, data *FileInfo) (child *FileNode) {
+func (node *FileNode) AddChild(name string, data FileInfo) (child *FileNode) {
child = NewNode(node, name, data)
if node.Children[name] != nil {
// tree node already exists, replace the payload, keep the children
- node.Children[name].Data.FileInfo = data.Copy()
+ node.Children[name].Data.FileInfo = *data.Copy()
} else {
node.Children[name] = child
node.Tree.Size++
@@ -68,6 +74,31 @@ func (node *FileNode) Remove() error {
func (node *FileNode) String() string {
var style *color.Color
+ var display string
+ if node == nil {
+ return ""
+ }
+ switch node.Data.DiffType {
+ case Added:
+ style = color.New(color.FgGreen)
+ case Removed:
+ style = color.New(color.FgRed)
+ case Changed:
+ style = color.New(color.FgYellow)
+ case Unchanged:
+ style = color.New(color.Reset)
+ default:
+ style = color.New(color.BgMagenta)
+ }
+ display = node.Name
+ if node.Data.FileInfo.TarHeader.Typeflag == tar.TypeSymlink || node.Data.FileInfo.TarHeader.Typeflag == tar.TypeLink {
+ display += " -> " + node.Data.FileInfo.TarHeader.Linkname
+ }
+ return style.Sprint(display)
+}
+
+func (node *FileNode) MetadataString() string {
+ var style *color.Color
if node == nil {
return ""
}
@@ -83,7 +114,18 @@ func (node *FileNode) String() string {
default:
style = color.New(color.BgMagenta)
}
- return style.Sprint(node.Name)
+
+ fileMode := permbits.FileMode(node.Data.FileInfo.TarHeader.FileInfo().Mode()).String()
+ dir := "-"
+ if node.Data.FileInfo.TarHeader.FileInfo().IsDir() {
+ dir = "d"
+ }
+ user := node.Data.FileInfo.TarHeader.Uid
+ group := node.Data.FileInfo.TarHeader.Gid
+ userGroup := fmt.Sprintf("%d:%d", user, group)
+ size := humanize.Bytes(uint64(node.Data.FileInfo.TarHeader.FileInfo().Size()))
+
+ return style.Sprint(fmt.Sprintf(AttributeFormat,dir, fileMode, userGroup, size))
}
func (node *FileNode) VisitDepthChildFirst(visiter Visiter, evaluator VisitEvaluator) error {
diff --git a/filetree/tree.go b/filetree/tree.go
index e1b4efe..921cc1e 100644
--- a/filetree/tree.go
+++ b/filetree/tree.go
@@ -33,10 +33,10 @@ func NewFileTree() (tree *FileTree) {
}
func (tree *FileTree) String() string {
- var renderLine func(string, []bool, bool, bool) string
+ var renderTreeLine func(string, []bool, bool, bool) string
var walkTree func(*FileNode, []bool, int) string
- renderLine = func(nodeText string, spaces []bool, last bool, collapsed bool) string {
+ renderTreeLine = func(nodeText string, spaces []bool, last bool, collapsed bool) string {
var otherBranches string
for _, space := range spaces {
if space {
@@ -73,7 +73,7 @@ func (tree *FileTree) String() string {
}
last := idx == (len(node.Children) - 1)
showCollapsed := child.Data.ViewInfo.Collapsed && len(child.Children) > 0
- result += renderLine(child.String(), spaces, last, showCollapsed)
+ result += child.MetadataString() + " " + renderTreeLine(child.String(), spaces, last, showCollapsed)
if len(child.Children) > 0 && !child.Data.ViewInfo.Collapsed {
spacesChild := append(spaces, last)
result += walkTree(child, spacesChild, depth+1)
@@ -131,7 +131,7 @@ func (tree *FileTree) Stack(upper *FileTree) error {
}
func (tree *FileTree) GetNode(path string) (*FileNode, error) {
- nodeNames := strings.Split(path, "/")
+ nodeNames := strings.Split(strings.Trim(path, "/"), "/")
node := tree.Root
for _, name := range nodeNames {
if name == "" {
@@ -145,9 +145,9 @@ func (tree *FileTree) GetNode(path string) (*FileNode, error) {
return node, nil
}
-func (tree *FileTree) AddPath(path string, data *FileInfo) (*FileNode, error) {
+func (tree *FileTree) AddPath(path string, data FileInfo) (*FileNode, error) {
// fmt.Printf("ADDPATH: %s %+v\n", path, data)
- nodeNames := strings.Split(path, "/")
+ nodeNames := strings.Split(strings.Trim(path, "/"), "/")
node := tree.Root
for idx, name := range nodeNames {
if name == "" {
@@ -159,7 +159,7 @@ func (tree *FileTree) AddPath(path string, data *FileInfo) (*FileNode, error) {
} else {
// don't attach the payload. The payload is destined for the
// Path's end node, not any intermediary node.
- node = node.AddChild(name, nil)
+ node = node.AddChild(name, FileInfo{})
}
// attach payload to the last specified node
diff --git a/image/image.go b/image/image.go
index 96bc0cd..6cdd462 100644
--- a/image/image.go
+++ b/image/image.go
@@ -19,6 +19,10 @@ import (
"golang.org/x/net/context"
)
+const (
+ LayerFormat = "%25s %7s %s"
+)
+
func check(e error) {
if e != nil {
panic(e)
@@ -56,7 +60,7 @@ func (layer *Layer) String() string {
if len(layer.History.Tags) > 0 {
id = "[" + strings.Join(layer.History.Tags, ",") + "]"
}
- return fmt.Sprintf("%25s %7s %s", id, humanize.Bytes(uint64(layer.History.Size)), layer.History.CreatedBy)
+ return fmt.Sprintf(LayerFormat, id, humanize.Bytes(uint64(layer.History.Size)), layer.History.CreatedBy)
}
func InitializeData(imageID string) ([]*Layer, []*filetree.FileTree) {
@@ -103,7 +107,7 @@ func InitializeData(imageID string) ([]*Layer, []*filetree.FileTree) {
tree.Name = name
fileInfos := getFileList(tarReader, header)
for _, element := range fileInfos {
- tree.AddPath(element.Path, &element)
+ tree.AddPath(element.Path, element)
}
layerMap[tree.Name] = tree
}
@@ -184,9 +188,9 @@ func saveImage(imageID string) (string, string) {
return imageTarPath, tmpDir
}
-func getFileList(parentReader *tar.Reader, h *tar.Header) []filetree.FileInfo {
+func getFileList(parentReader *tar.Reader, header *tar.Header) []filetree.FileInfo {
var files []filetree.FileInfo
- var tarredBytes = make([]byte, h.Size)
+ var tarredBytes = make([]byte, header.Size)
_, err := parentReader.Read(tarredBytes)
if err != nil {
diff --git a/ui/filetreeview.go b/ui/filetreeview.go
index 50110b2..5bbfd99 100644
--- a/ui/filetreeview.go
+++ b/ui/filetreeview.go
@@ -8,12 +8,14 @@ import (
"github.com/wagoodman/docker-image-explorer/filetree"
"github.com/fatih/color"
"strings"
+ "github.com/lunixbochs/vtclean"
)
type FileTreeView struct {
Name string
gui *gocui.Gui
view *gocui.View
+ header *gocui.View
TreeIndex int
ModelTree *filetree.FileTree
ViewTree *filetree.FileTree
@@ -34,7 +36,7 @@ func NewFileTreeView(name string, gui *gocui.Gui, tree *filetree.FileTree, refTr
return treeview
}
-func (view *FileTreeView) Setup(v *gocui.View) error {
+func (view *FileTreeView) Setup(v *gocui.View, header *gocui.View) error {
// set view options
view.view = v
@@ -43,7 +45,12 @@ func (view *FileTreeView) Setup(v *gocui.View) error {
//view.view.Highlight = true
//view.view.SelBgColor = gocui.ColorGreen
//view.view.SelFgColor = gocui.ColorBlack
- view.view.Frame = true
+ view.view.Frame = false
+
+ view.header = header
+ view.header.Editable = false
+ view.header.Wrap = false
+ view.header.Frame = false
// set keybindings
if err := view.gui.SetKeybinding(view.Name, gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorDown() }); err != nil {
@@ -71,6 +78,9 @@ func (view *FileTreeView) Setup(v *gocui.View) error {
view.updateViewTree()
view.Render()
+ headerStr := fmt.Sprintf(filetree.AttributeFormat + " %s", "P","ermission", "UID:GID", "Size", "Filetree")
+ fmt.Fprintln(view.header, Formatting.Header(vtclean.Clean(headerStr, false)))
+
return nil
}
@@ -200,7 +210,7 @@ func (view *FileTreeView) Render() error {
view.view.Clear()
for idx, line := range lines {
if idx == view.TreeIndex {
- fmt.Fprintln(view.view, Formatting.Header(line))
+ fmt.Fprintln(view.view, Formatting.StatusBar(vtclean.Clean(line, false)))
} else {
fmt.Fprintln(view.view, line)
}
diff --git a/ui/layerview.go b/ui/layerview.go
index 76af82b..77c3e6f 100644
--- a/ui/layerview.go
+++ b/ui/layerview.go
@@ -5,12 +5,14 @@ import (
"github.com/jroimartin/gocui"
"github.com/wagoodman/docker-image-explorer/image"
+ "github.com/lunixbochs/vtclean"
)
type LayerView struct {
Name string
gui *gocui.Gui
view *gocui.View
+ header *gocui.View
LayerIndex int
Layers []*image.Layer
}
@@ -26,16 +28,22 @@ func NewLayerView(name string, gui *gocui.Gui, layers []*image.Layer) (layerview
return layerview
}
-func (view *LayerView) Setup(v *gocui.View) error {
+func (view *LayerView) Setup(v *gocui.View, header *gocui.View) error {
// set view options
view.view = v
+ view.view.Editable = false
view.view.Wrap = false
//view.view.Highlight = true
//view.view.SelBgColor = gocui.ColorGreen
//view.view.SelFgColor = gocui.ColorBlack
view.view.Frame = false
+ view.header = header
+ view.header.Editable = false
+ view.header.Wrap = false
+ view.header.Frame = false
+
// set keybindings
if err := view.gui.SetKeybinding(view.Name, gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorDown() }); err != nil {
return err
@@ -44,9 +52,10 @@ func (view *LayerView) Setup(v *gocui.View) error {
return err
}
- view.Render()
+ headerStr := fmt.Sprintf(image.LayerFormat, "Image ID", "Size", "Command")
+ fmt.Fprintln(view.header, Formatting.Header(vtclean.Clean(headerStr, false)))
- return nil
+ return view.Render()
}
func (view *LayerView) Render() error {
@@ -57,7 +66,7 @@ func (view *LayerView) Render() error {
idx := (len(view.Layers)-1) - revIdx
if idx == view.LayerIndex {
- fmt.Fprintln(view.view, Formatting.Header(layer.String()))
+ fmt.Fprintln(view.view, Formatting.StatusBar(layer.String()))
} else {
fmt.Fprintln(view.view, layer.String())
}
diff --git a/ui/statusview.go b/ui/statusview.go
index 719d097..99cc579 100644
--- a/ui/statusview.go
+++ b/ui/statusview.go
@@ -23,7 +23,7 @@ func NewStatusView(name string, gui *gocui.Gui) (statusview *StatusView) {
return statusview
}
-func (view *StatusView) Setup(v *gocui.View) error {
+func (view *StatusView) Setup(v *gocui.View, header *gocui.View) error {
// set view options
view.view = v
diff --git a/ui/ui.go b/ui/ui.go
index b5ee34a..0ac6759 100644
--- a/ui/ui.go
+++ b/ui/ui.go
@@ -13,6 +13,7 @@ const debug = false
var Formatting struct {
Header func(...interface{})(string)
+ StatusBar func(...interface{})(string)
}
var Views struct {
@@ -23,7 +24,7 @@ var Views struct {
}
type View interface {
- Setup(*gocui.View) error
+ Setup(*gocui.View, *gocui.View) error
CursorDown() error
CursorUp() error
Render() error
@@ -102,24 +103,41 @@ func layout(g *gocui.Gui) error {
}
debugCols := maxX - debugWidth
bottomRows := 1
- if view, err := g.SetView(Views.Layer.Name, -1, -1, splitCols, maxY-bottomRows); err != nil {
+ headerRows := 1
+
+ // Layers
+ if view, err := g.SetView(Views.Layer.Name, -1, -1+headerRows, splitCols, maxY-bottomRows); err != nil {
if err != gocui.ErrUnknownView {
return err
}
- Views.Layer.Setup(view)
+ if header, err := g.SetView(Views.Layer.Name+"header", -1, -1, splitCols, headerRows); err != nil {
+ if err != gocui.ErrUnknownView {
+ return err
+ }
+ Views.Layer.Setup(view, header)
- if _, err := g.SetCurrentView(Views.Layer.Name); err != nil {
- return err
+ if _, err := g.SetCurrentView(Views.Layer.Name); err != nil {
+ return err
+ }
}
+
}
- if view, err := g.SetView(Views.Tree.Name, splitCols, -1, debugCols, maxY-bottomRows); err != nil {
+ // Filetree
+ if view, err := g.SetView(Views.Tree.Name, splitCols, -1+headerRows, debugCols, maxY-bottomRows); err != nil {
if err != gocui.ErrUnknownView {
return err
}
- Views.Tree.Setup(view)
+ if header, err := g.SetView(Views.Tree.Name+"header", splitCols, -1, debugCols, headerRows); err != nil {
+ if err != gocui.ErrUnknownView {
+ return err
+ }
+ Views.Tree.Setup(view, header)
+ }
}
+
+ // Debug pane
if debug {
if _, err := g.SetView("debug", debugCols, -1, maxX, maxY-bottomRows); err != nil {
if err != gocui.ErrUnknownView {
@@ -127,11 +145,13 @@ func layout(g *gocui.Gui) error {
}
}
}
+
+ // StatusBar
if view, err := g.SetView(Views.Status.Name, -1, maxY-bottomRows-1, maxX, maxY); err != nil {
if err != gocui.ErrUnknownView {
return err
}
- Views.Status.Setup(view)
+ Views.Status.Setup(view, nil)
}
@@ -145,7 +165,8 @@ func Render() {
}
func Run(layers []*image.Layer, refTrees []*filetree.FileTree) {
- Formatting.Header = color.New(color.ReverseVideo, color.Bold).SprintFunc()
+ Formatting.StatusBar = color.New(color.ReverseVideo, color.Bold).SprintFunc()
+ Formatting.Header = color.New(color.Bold).SprintFunc()
g, err := gocui.NewGui(gocui.OutputNormal)
if err != nil {