Merge branch 'master' into filter-view
This commit is contained in:
commit
ef2c76930f
2
.gitignore
vendored
2
.gitignore
vendored
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
*.out
|
*.out
|
||||||
|
/tmp
|
||||||
/build
|
/build
|
||||||
/_vendor*
|
/_vendor*
|
||||||
/vendor
|
/vendor
|
||||||
|
@ -18,7 +18,7 @@ const (
|
|||||||
|
|
||||||
type NodeData struct {
|
type NodeData struct {
|
||||||
ViewInfo ViewInfo
|
ViewInfo ViewInfo
|
||||||
FileInfo *FileInfo
|
FileInfo FileInfo
|
||||||
DiffType DiffType
|
DiffType DiffType
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,6 +31,7 @@ type FileInfo struct {
|
|||||||
Path string
|
Path string
|
||||||
Typeflag byte
|
Typeflag byte
|
||||||
MD5sum [16]byte
|
MD5sum [16]byte
|
||||||
|
TarHeader tar.Header
|
||||||
}
|
}
|
||||||
|
|
||||||
type DiffType int
|
type DiffType int
|
||||||
@ -38,7 +39,7 @@ type DiffType int
|
|||||||
func NewNodeData() (*NodeData) {
|
func NewNodeData() (*NodeData) {
|
||||||
return &NodeData{
|
return &NodeData{
|
||||||
ViewInfo: *NewViewInfo(),
|
ViewInfo: *NewViewInfo(),
|
||||||
FileInfo: nil,
|
FileInfo: FileInfo{},
|
||||||
DiffType: Unchanged,
|
DiffType: Unchanged,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,7 +47,7 @@ func NewNodeData() (*NodeData) {
|
|||||||
func (data *NodeData) Copy() (*NodeData) {
|
func (data *NodeData) Copy() (*NodeData) {
|
||||||
return &NodeData{
|
return &NodeData{
|
||||||
ViewInfo: *data.ViewInfo.Copy(),
|
ViewInfo: *data.ViewInfo.Copy(),
|
||||||
FileInfo: data.FileInfo.Copy(),
|
FileInfo: *data.FileInfo.Copy(),
|
||||||
DiffType: data.DiffType,
|
DiffType: data.DiffType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,6 +72,7 @@ func NewFileInfo(reader *tar.Reader, header *tar.Header, path string) FileInfo {
|
|||||||
Path: path,
|
Path: path,
|
||||||
Typeflag: header.Typeflag,
|
Typeflag: header.Typeflag,
|
||||||
MD5sum: [16]byte{},
|
MD5sum: [16]byte{},
|
||||||
|
TarHeader: *header,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fileBytes := make([]byte, header.Size)
|
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 {
|
if err != nil && err != io.EOF {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return FileInfo{
|
return FileInfo{
|
||||||
Path: path,
|
Path: path,
|
||||||
Typeflag: header.Typeflag,
|
Typeflag: header.Typeflag,
|
||||||
MD5sum: md5.Sum(fileBytes),
|
MD5sum: md5.Sum(fileBytes),
|
||||||
|
TarHeader: *header,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,16 +119,11 @@ func (data *FileInfo) Copy() *FileInfo {
|
|||||||
Path: data.Path,
|
Path: data.Path,
|
||||||
Typeflag: data.Typeflag,
|
Typeflag: data.Typeflag,
|
||||||
MD5sum: data.MD5sum,
|
MD5sum: data.MD5sum,
|
||||||
|
TarHeader: data.TarHeader,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (data *FileInfo) getDiffType(other *FileInfo) DiffType {
|
func (data *FileInfo) getDiffType(other FileInfo) DiffType {
|
||||||
if data == nil && other == nil {
|
|
||||||
return Unchanged
|
|
||||||
}
|
|
||||||
if data == nil || other == nil {
|
|
||||||
return Changed
|
|
||||||
}
|
|
||||||
if data.Typeflag == other.Typeflag {
|
if data.Typeflag == other.Typeflag {
|
||||||
if bytes.Compare(data.MD5sum[:], other.MD5sum[:]) == 0 {
|
if bytes.Compare(data.MD5sum[:], other.MD5sum[:]) == 0 {
|
||||||
return Unchanged
|
return Unchanged
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
func TestAssignDiffType(t *testing.T) {
|
func TestAssignDiffType(t *testing.T) {
|
||||||
tree := NewFileTree()
|
tree := NewFileTree()
|
||||||
node, err := tree.AddPath("/usr", BlankFileChangeInfo("/usr"))
|
node, err := tree.AddPath("/usr", *BlankFileChangeInfo("/usr"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected no error from fetching path. got: %v", err)
|
t.Errorf("Expected no error from fetching path. got: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,13 @@ import (
|
|||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"fmt"
|
"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 {
|
type FileNode struct {
|
||||||
@ -16,13 +23,12 @@ type FileNode struct {
|
|||||||
Children map[string]*FileNode
|
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 = new(FileNode)
|
||||||
node.Name = name
|
node.Name = name
|
||||||
node.Data = *NewNodeData()
|
node.Data = *NewNodeData()
|
||||||
if data != nil {
|
node.Data.FileInfo = *data.Copy()
|
||||||
node.Data.FileInfo = data.Copy()
|
|
||||||
}
|
|
||||||
node.Children = make(map[string]*FileNode)
|
node.Children = make(map[string]*FileNode)
|
||||||
node.Parent = parent
|
node.Parent = parent
|
||||||
if parent != nil {
|
if parent != nil {
|
||||||
@ -42,11 +48,11 @@ func (node *FileNode) Copy(parent *FileNode) *FileNode {
|
|||||||
return newNode
|
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)
|
child = NewNode(node, name, data)
|
||||||
if node.Children[name] != nil {
|
if node.Children[name] != nil {
|
||||||
// tree node already exists, replace the payload, keep the children
|
// 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 {
|
} else {
|
||||||
node.Children[name] = child
|
node.Children[name] = child
|
||||||
node.Tree.Size++
|
node.Tree.Size++
|
||||||
@ -68,6 +74,7 @@ func (node *FileNode) Remove() error {
|
|||||||
|
|
||||||
func (node *FileNode) String() string {
|
func (node *FileNode) String() string {
|
||||||
var style *color.Color
|
var style *color.Color
|
||||||
|
var display string
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@ -83,7 +90,42 @@ func (node *FileNode) String() string {
|
|||||||
default:
|
default:
|
||||||
style = color.New(color.BgMagenta)
|
style = color.New(color.BgMagenta)
|
||||||
}
|
}
|
||||||
return style.Sprint(node.Name)
|
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 ""
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
func (node *FileNode) VisitDepthChildFirst(visiter Visiter, evaluator VisitEvaluator) error {
|
||||||
|
@ -12,14 +12,14 @@ func TestAddChild(t *testing.T) {
|
|||||||
Path: "stufffffs",
|
Path: "stufffffs",
|
||||||
}
|
}
|
||||||
|
|
||||||
one := tree.Root.AddChild("first node!", &payload)
|
one := tree.Root.AddChild("first node!", payload)
|
||||||
|
|
||||||
two := tree.Root.AddChild("nil node!", nil)
|
two := tree.Root.AddChild("nil node!", FileInfo{})
|
||||||
|
|
||||||
tree.Root.AddChild("third node!", nil)
|
tree.Root.AddChild("third node!", FileInfo{})
|
||||||
two.AddChild("forth, one level down...", nil)
|
two.AddChild("forth, one level down...", FileInfo{})
|
||||||
two.AddChild("fifth, one level down...", nil)
|
two.AddChild("fifth, one level down...", FileInfo{})
|
||||||
two.AddChild("fifth, one level down...", nil)
|
two.AddChild("fifth, one level down...", FileInfo{})
|
||||||
|
|
||||||
expected, actual = 5, tree.Size
|
expected, actual = 5, tree.Size
|
||||||
if expected != actual {
|
if expected != actual {
|
||||||
@ -36,17 +36,14 @@ func TestAddChild(t *testing.T) {
|
|||||||
t.Errorf("Expected 'twos' number of children to be %d got %d.", expected, actual)
|
t.Errorf("Expected 'twos' number of children to be %d got %d.", expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedFC := &FileInfo{
|
expectedFC := FileInfo{
|
||||||
Path: "stufffffs",
|
Path: "stufffffs",
|
||||||
}
|
}
|
||||||
actualFC := one.Data.FileInfo
|
actualFC := one.Data.FileInfo
|
||||||
if *expectedFC != *actualFC {
|
if expectedFC.Path != actualFC.Path {
|
||||||
t.Errorf("Expected 'ones' payload to be %+v got %+v.", expectedFC, actualFC)
|
t.Errorf("Expected 'ones' payload to be %+v got %+v.", expectedFC, actualFC)
|
||||||
}
|
}
|
||||||
|
|
||||||
if two.Data.FileInfo != nil {
|
|
||||||
t.Errorf("Expected 'twos' payload to be nil got %+v.", two.Data.FileInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,11 +51,11 @@ func TestRemoveChild(t *testing.T) {
|
|||||||
var expected, actual int
|
var expected, actual int
|
||||||
|
|
||||||
tree := NewFileTree()
|
tree := NewFileTree()
|
||||||
tree.Root.AddChild("first", nil)
|
tree.Root.AddChild("first", FileInfo{})
|
||||||
two := tree.Root.AddChild("nil", nil)
|
two := tree.Root.AddChild("nil", FileInfo{})
|
||||||
tree.Root.AddChild("third", nil)
|
tree.Root.AddChild("third", FileInfo{})
|
||||||
forth := two.AddChild("forth", nil)
|
forth := two.AddChild("forth", FileInfo{})
|
||||||
two.AddChild("fifth", nil)
|
two.AddChild("fifth", FileInfo{})
|
||||||
|
|
||||||
forth.Remove()
|
forth.Remove()
|
||||||
|
|
||||||
@ -87,7 +84,7 @@ func TestRemoveChild(t *testing.T) {
|
|||||||
func TestPath(t *testing.T) {
|
func TestPath(t *testing.T) {
|
||||||
expected := "/etc/nginx/nginx.conf"
|
expected := "/etc/nginx/nginx.conf"
|
||||||
tree := NewFileTree()
|
tree := NewFileTree()
|
||||||
node, _ := tree.AddPath(expected, nil)
|
node, _ := tree.AddPath(expected, FileInfo{})
|
||||||
|
|
||||||
actual := node.Path()
|
actual := node.Path()
|
||||||
if expected != actual {
|
if expected != actual {
|
||||||
@ -97,8 +94,8 @@ func TestPath(t *testing.T) {
|
|||||||
|
|
||||||
func TestIsWhiteout(t *testing.T) {
|
func TestIsWhiteout(t *testing.T) {
|
||||||
tree1 := NewFileTree()
|
tree1 := NewFileTree()
|
||||||
p1, _ := tree1.AddPath("/etc/nginx/public1", nil)
|
p1, _ := tree1.AddPath("/etc/nginx/public1", FileInfo{})
|
||||||
p2, _ := tree1.AddPath("/etc/nginx/.wh.public2", nil)
|
p2, _ := tree1.AddPath("/etc/nginx/.wh.public2", FileInfo{})
|
||||||
|
|
||||||
if p1.IsWhiteout() != false {
|
if p1.IsWhiteout() != false {
|
||||||
t.Errorf("Expected Path '%s' to **not** be a whiteout file", p1.Name)
|
t.Errorf("Expected Path '%s' to **not** be a whiteout file", p1.Name)
|
||||||
@ -111,15 +108,15 @@ func TestIsWhiteout(t *testing.T) {
|
|||||||
|
|
||||||
func TestDiffTypeFromAddedChildren(t *testing.T) {
|
func TestDiffTypeFromAddedChildren(t *testing.T) {
|
||||||
tree := NewFileTree()
|
tree := NewFileTree()
|
||||||
node, _ := tree.AddPath("/usr", BlankFileChangeInfo("/usr"))
|
node, _ := tree.AddPath("/usr", *BlankFileChangeInfo("/usr"))
|
||||||
node.Data.DiffType = Unchanged
|
node.Data.DiffType = Unchanged
|
||||||
|
|
||||||
info1 := BlankFileChangeInfo("/usr/bin")
|
info1 := BlankFileChangeInfo("/usr/bin")
|
||||||
node, _ = tree.AddPath("/usr/bin", info1)
|
node, _ = tree.AddPath("/usr/bin", *info1)
|
||||||
node.Data.DiffType = Added
|
node.Data.DiffType = Added
|
||||||
|
|
||||||
info2 := BlankFileChangeInfo("/usr/bin2")
|
info2 := BlankFileChangeInfo("/usr/bin2")
|
||||||
node, _ = tree.AddPath("/usr/bin2", info2)
|
node, _ = tree.AddPath("/usr/bin2", *info2)
|
||||||
node.Data.DiffType = Removed
|
node.Data.DiffType = Removed
|
||||||
|
|
||||||
tree.Root.Children["usr"].deriveDiffType(Unchanged)
|
tree.Root.Children["usr"].deriveDiffType(Unchanged)
|
||||||
@ -130,14 +127,14 @@ func TestDiffTypeFromAddedChildren(t *testing.T) {
|
|||||||
}
|
}
|
||||||
func TestDiffTypeFromRemovedChildren(t *testing.T) {
|
func TestDiffTypeFromRemovedChildren(t *testing.T) {
|
||||||
tree := NewFileTree()
|
tree := NewFileTree()
|
||||||
node, _ := tree.AddPath("/usr", BlankFileChangeInfo("/usr"))
|
node, _ := tree.AddPath("/usr", *BlankFileChangeInfo("/usr"))
|
||||||
|
|
||||||
info1 := BlankFileChangeInfo("/usr/.wh.bin")
|
info1 := BlankFileChangeInfo("/usr/.wh.bin")
|
||||||
node, _ = tree.AddPath("/usr/.wh.bin", info1)
|
node, _ = tree.AddPath("/usr/.wh.bin", *info1)
|
||||||
node.Data.DiffType = Removed
|
node.Data.DiffType = Removed
|
||||||
|
|
||||||
info2 := BlankFileChangeInfo("/usr/.wh.bin2")
|
info2 := BlankFileChangeInfo("/usr/.wh.bin2")
|
||||||
node, _ = tree.AddPath("/usr/.wh.bin2", info2)
|
node, _ = tree.AddPath("/usr/.wh.bin2", *info2)
|
||||||
node.Data.DiffType = Removed
|
node.Data.DiffType = Removed
|
||||||
|
|
||||||
tree.Root.Children["usr"].deriveDiffType(Unchanged)
|
tree.Root.Children["usr"].deriveDiffType(Unchanged)
|
||||||
|
@ -32,11 +32,11 @@ func NewFileTree() (tree *FileTree) {
|
|||||||
return tree
|
return tree
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tree *FileTree) String() string {
|
func (tree *FileTree) String(showAttributes bool) string {
|
||||||
var renderLine func(string, []bool, bool, bool) string
|
var renderTreeLine func(string, []bool, bool, bool) string
|
||||||
var walkTree func(*FileNode, []bool, int) 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
|
var otherBranches string
|
||||||
for _, space := range spaces {
|
for _, space := range spaces {
|
||||||
if space {
|
if space {
|
||||||
@ -73,7 +73,10 @@ func (tree *FileTree) String() string {
|
|||||||
}
|
}
|
||||||
last := idx == (len(node.Children) - 1)
|
last := idx == (len(node.Children) - 1)
|
||||||
showCollapsed := child.Data.ViewInfo.Collapsed && len(child.Children) > 0
|
showCollapsed := child.Data.ViewInfo.Collapsed && len(child.Children) > 0
|
||||||
result += renderLine(child.String(), spaces, last, showCollapsed)
|
if showAttributes {
|
||||||
|
result += child.MetadataString() + " "
|
||||||
|
}
|
||||||
|
result += renderTreeLine(child.String(), spaces, last, showCollapsed)
|
||||||
if len(child.Children) > 0 && !child.Data.ViewInfo.Collapsed {
|
if len(child.Children) > 0 && !child.Data.ViewInfo.Collapsed {
|
||||||
spacesChild := append(spaces, last)
|
spacesChild := append(spaces, last)
|
||||||
result += walkTree(child, spacesChild, depth+1)
|
result += walkTree(child, spacesChild, depth+1)
|
||||||
@ -82,7 +85,7 @@ func (tree *FileTree) String() string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
return "." + newLine + walkTree(tree.Root, []bool{}, 0)
|
return walkTree(tree.Root, []bool{}, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tree *FileTree) Copy() *FileTree {
|
func (tree *FileTree) Copy() *FileTree {
|
||||||
@ -131,7 +134,7 @@ func (tree *FileTree) Stack(upper *FileTree) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tree *FileTree) GetNode(path string) (*FileNode, error) {
|
func (tree *FileTree) GetNode(path string) (*FileNode, error) {
|
||||||
nodeNames := strings.Split(path, "/")
|
nodeNames := strings.Split(strings.Trim(path, "/"), "/")
|
||||||
node := tree.Root
|
node := tree.Root
|
||||||
for _, name := range nodeNames {
|
for _, name := range nodeNames {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
@ -145,9 +148,9 @@ func (tree *FileTree) GetNode(path string) (*FileNode, error) {
|
|||||||
return node, nil
|
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)
|
// fmt.Printf("ADDPATH: %s %+v\n", path, data)
|
||||||
nodeNames := strings.Split(path, "/")
|
nodeNames := strings.Split(strings.Trim(path, "/"), "/")
|
||||||
node := tree.Root
|
node := tree.Root
|
||||||
for idx, name := range nodeNames {
|
for idx, name := range nodeNames {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
@ -159,7 +162,7 @@ func (tree *FileTree) AddPath(path string, data *FileInfo) (*FileNode, error) {
|
|||||||
} else {
|
} else {
|
||||||
// don't attach the payload. The payload is destined for the
|
// don't attach the payload. The payload is destined for the
|
||||||
// Path's end node, not any intermediary node.
|
// 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
|
// attach payload to the last specified node
|
||||||
|
@ -15,9 +15,6 @@ func stringInSlice(a string, list []string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AssertDiffType(node *FileNode, expectedDiffType DiffType) error {
|
func AssertDiffType(node *FileNode, expectedDiffType DiffType) error {
|
||||||
if node.Data.FileInfo == nil {
|
|
||||||
return fmt.Errorf("expected *FileInfo but got nil at Path %s", node.Path())
|
|
||||||
}
|
|
||||||
if node.Data.DiffType != expectedDiffType {
|
if node.Data.DiffType != expectedDiffType {
|
||||||
return fmt.Errorf("Expecting node at %s to have DiffType %v, but had %v", node.Path(), expectedDiffType, node.Data.DiffType)
|
return fmt.Errorf("Expecting node at %s to have DiffType %v, but had %v", node.Path(), expectedDiffType, node.Data.DiffType)
|
||||||
}
|
}
|
||||||
@ -26,18 +23,18 @@ func AssertDiffType(node *FileNode, expectedDiffType DiffType) error {
|
|||||||
|
|
||||||
func TestPrintTree(t *testing.T) {
|
func TestPrintTree(t *testing.T) {
|
||||||
tree := NewFileTree()
|
tree := NewFileTree()
|
||||||
tree.Root.AddChild("first node!", nil)
|
tree.Root.AddChild("first node!", FileInfo{})
|
||||||
two := tree.Root.AddChild("second node!", nil)
|
two := tree.Root.AddChild("second node!", FileInfo{})
|
||||||
tree.Root.AddChild("third node!", nil)
|
tree.Root.AddChild("third node!", FileInfo{})
|
||||||
two.AddChild("forth, one level down...", nil)
|
two.AddChild("forth, one level down...", FileInfo{})
|
||||||
|
|
||||||
expected := `.
|
expected :=
|
||||||
├── first node!
|
`├── first node!
|
||||||
├── second node!
|
├── second node!
|
||||||
│ └── forth, one level down...
|
│ └── forth, one level down...
|
||||||
└── third node!
|
└── third node!
|
||||||
`
|
`
|
||||||
actual := tree.String()
|
actual := tree.String(false)
|
||||||
|
|
||||||
if expected != actual {
|
if expected != actual {
|
||||||
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
|
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
|
||||||
@ -47,15 +44,15 @@ func TestPrintTree(t *testing.T) {
|
|||||||
|
|
||||||
func TestAddPath(t *testing.T) {
|
func TestAddPath(t *testing.T) {
|
||||||
tree := NewFileTree()
|
tree := NewFileTree()
|
||||||
tree.AddPath("/etc/nginx/nginx.conf", nil)
|
tree.AddPath("/etc/nginx/nginx.conf", FileInfo{})
|
||||||
tree.AddPath("/etc/nginx/public", nil)
|
tree.AddPath("/etc/nginx/public", FileInfo{})
|
||||||
tree.AddPath("/var/run/systemd", nil)
|
tree.AddPath("/var/run/systemd", FileInfo{})
|
||||||
tree.AddPath("/var/run/bashful", nil)
|
tree.AddPath("/var/run/bashful", FileInfo{})
|
||||||
tree.AddPath("/tmp", nil)
|
tree.AddPath("/tmp", FileInfo{})
|
||||||
tree.AddPath("/tmp/nonsense", nil)
|
tree.AddPath("/tmp/nonsense", FileInfo{})
|
||||||
|
|
||||||
expected := `.
|
expected :=
|
||||||
├── etc
|
`├── etc
|
||||||
│ └── nginx
|
│ └── nginx
|
||||||
│ ├── nginx.conf
|
│ ├── nginx.conf
|
||||||
│ └── public
|
│ └── public
|
||||||
@ -66,7 +63,7 @@ func TestAddPath(t *testing.T) {
|
|||||||
├── bashful
|
├── bashful
|
||||||
└── systemd
|
└── systemd
|
||||||
`
|
`
|
||||||
actual := tree.String()
|
actual := tree.String(false)
|
||||||
|
|
||||||
if expected != actual {
|
if expected != actual {
|
||||||
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
|
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
|
||||||
@ -76,18 +73,18 @@ func TestAddPath(t *testing.T) {
|
|||||||
|
|
||||||
func TestRemovePath(t *testing.T) {
|
func TestRemovePath(t *testing.T) {
|
||||||
tree := NewFileTree()
|
tree := NewFileTree()
|
||||||
tree.AddPath("/etc/nginx/nginx.conf", nil)
|
tree.AddPath("/etc/nginx/nginx.conf", FileInfo{})
|
||||||
tree.AddPath("/etc/nginx/public", nil)
|
tree.AddPath("/etc/nginx/public", FileInfo{})
|
||||||
tree.AddPath("/var/run/systemd", nil)
|
tree.AddPath("/var/run/systemd", FileInfo{})
|
||||||
tree.AddPath("/var/run/bashful", nil)
|
tree.AddPath("/var/run/bashful", FileInfo{})
|
||||||
tree.AddPath("/tmp", nil)
|
tree.AddPath("/tmp", FileInfo{})
|
||||||
tree.AddPath("/tmp/nonsense", nil)
|
tree.AddPath("/tmp/nonsense", FileInfo{})
|
||||||
|
|
||||||
tree.RemovePath("/var/run/bashful")
|
tree.RemovePath("/var/run/bashful")
|
||||||
tree.RemovePath("/tmp")
|
tree.RemovePath("/tmp")
|
||||||
|
|
||||||
expected := `.
|
expected :=
|
||||||
├── etc
|
`├── etc
|
||||||
│ └── nginx
|
│ └── nginx
|
||||||
│ ├── nginx.conf
|
│ ├── nginx.conf
|
||||||
│ └── public
|
│ └── public
|
||||||
@ -95,7 +92,7 @@ func TestRemovePath(t *testing.T) {
|
|||||||
└── run
|
└── run
|
||||||
└── systemd
|
└── systemd
|
||||||
`
|
`
|
||||||
actual := tree.String()
|
actual := tree.String(false)
|
||||||
|
|
||||||
if expected != actual {
|
if expected != actual {
|
||||||
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
|
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
|
||||||
@ -111,20 +108,20 @@ func TestStack(t *testing.T) {
|
|||||||
|
|
||||||
tree1 := NewFileTree()
|
tree1 := NewFileTree()
|
||||||
|
|
||||||
tree1.AddPath("/etc/nginx/public", nil)
|
tree1.AddPath("/etc/nginx/public", FileInfo{})
|
||||||
tree1.AddPath(payloadKey, nil)
|
tree1.AddPath(payloadKey, FileInfo{})
|
||||||
tree1.AddPath("/var/run/bashful", nil)
|
tree1.AddPath("/var/run/bashful", FileInfo{})
|
||||||
tree1.AddPath("/tmp", nil)
|
tree1.AddPath("/tmp", FileInfo{})
|
||||||
tree1.AddPath("/tmp/nonsense", nil)
|
tree1.AddPath("/tmp/nonsense", FileInfo{})
|
||||||
|
|
||||||
tree2 := NewFileTree()
|
tree2 := NewFileTree()
|
||||||
// add new files
|
// add new files
|
||||||
tree2.AddPath("/etc/nginx/nginx.conf", nil)
|
tree2.AddPath("/etc/nginx/nginx.conf", FileInfo{})
|
||||||
// modify current files
|
// modify current files
|
||||||
tree2.AddPath(payloadKey, &payloadValue)
|
tree2.AddPath(payloadKey, payloadValue)
|
||||||
// whiteout the following files
|
// whiteout the following files
|
||||||
tree2.AddPath("/var/run/.wh.bashful", nil)
|
tree2.AddPath("/var/run/.wh.bashful", FileInfo{})
|
||||||
tree2.AddPath("/.wh.tmp", nil)
|
tree2.AddPath("/.wh.tmp", FileInfo{})
|
||||||
|
|
||||||
err := tree1.Stack(tree2)
|
err := tree1.Stack(tree2)
|
||||||
|
|
||||||
@ -132,8 +129,8 @@ func TestStack(t *testing.T) {
|
|||||||
t.Errorf("Could not stack refTrees: %v", err)
|
t.Errorf("Could not stack refTrees: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := `.
|
expected :=
|
||||||
├── etc
|
`├── etc
|
||||||
│ └── nginx
|
│ └── nginx
|
||||||
│ ├── nginx.conf
|
│ ├── nginx.conf
|
||||||
│ └── public
|
│ └── public
|
||||||
@ -147,11 +144,11 @@ func TestStack(t *testing.T) {
|
|||||||
t.Errorf("Expected '%s' to still exist, but it doesn't", payloadKey)
|
t.Errorf("Expected '%s' to still exist, but it doesn't", payloadKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *node.Data.FileInfo != payloadValue {
|
if node.Data.FileInfo.Path != payloadValue.Path {
|
||||||
t.Errorf("Expected '%s' value to be %+v but got %+v", payloadKey, payloadValue, node.Data.FileInfo)
|
t.Errorf("Expected '%s' value to be %+v but got %+v", payloadKey, payloadValue, node.Data.FileInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual := tree1.String()
|
actual := tree1.String(false)
|
||||||
|
|
||||||
if expected != actual {
|
if expected != actual {
|
||||||
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
|
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
|
||||||
@ -161,18 +158,18 @@ func TestStack(t *testing.T) {
|
|||||||
|
|
||||||
func TestCopy(t *testing.T) {
|
func TestCopy(t *testing.T) {
|
||||||
tree := NewFileTree()
|
tree := NewFileTree()
|
||||||
tree.AddPath("/etc/nginx/nginx.conf", nil)
|
tree.AddPath("/etc/nginx/nginx.conf", FileInfo{})
|
||||||
tree.AddPath("/etc/nginx/public", nil)
|
tree.AddPath("/etc/nginx/public", FileInfo{})
|
||||||
tree.AddPath("/var/run/systemd", nil)
|
tree.AddPath("/var/run/systemd", FileInfo{})
|
||||||
tree.AddPath("/var/run/bashful", nil)
|
tree.AddPath("/var/run/bashful", FileInfo{})
|
||||||
tree.AddPath("/tmp", nil)
|
tree.AddPath("/tmp", FileInfo{})
|
||||||
tree.AddPath("/tmp/nonsense", nil)
|
tree.AddPath("/tmp/nonsense", FileInfo{})
|
||||||
|
|
||||||
tree.RemovePath("/var/run/bashful")
|
tree.RemovePath("/var/run/bashful")
|
||||||
tree.RemovePath("/tmp")
|
tree.RemovePath("/tmp")
|
||||||
|
|
||||||
expected := `.
|
expected :=
|
||||||
├── etc
|
`├── etc
|
||||||
│ └── nginx
|
│ └── nginx
|
||||||
│ ├── nginx.conf
|
│ ├── nginx.conf
|
||||||
│ └── public
|
│ └── public
|
||||||
@ -182,7 +179,7 @@ func TestCopy(t *testing.T) {
|
|||||||
`
|
`
|
||||||
|
|
||||||
NewFileTree := tree.Copy()
|
NewFileTree := tree.Copy()
|
||||||
actual := NewFileTree.String()
|
actual := NewFileTree.String(false)
|
||||||
|
|
||||||
if expected != actual {
|
if expected != actual {
|
||||||
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
|
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
|
||||||
@ -201,18 +198,14 @@ func TestCompareWithNoChanges(t *testing.T) {
|
|||||||
Typeflag: 1,
|
Typeflag: 1,
|
||||||
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
}
|
}
|
||||||
lowerTree.AddPath(value, &fakeData)
|
lowerTree.AddPath(value, fakeData)
|
||||||
upperTree.AddPath(value, &fakeData)
|
upperTree.AddPath(value, fakeData)
|
||||||
}
|
}
|
||||||
lowerTree.Compare(upperTree)
|
lowerTree.Compare(upperTree)
|
||||||
asserter := func(n *FileNode) error {
|
asserter := func(n *FileNode) error {
|
||||||
if n.Path() == "/" {
|
if n.Path() == "/" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if n.Data.FileInfo == nil {
|
|
||||||
t.Errorf("Expected *FileInfo but got nil")
|
|
||||||
return fmt.Errorf("expected *FileInfo but got nil")
|
|
||||||
}
|
|
||||||
if (n.Data.DiffType) != Unchanged {
|
if (n.Data.DiffType) != Unchanged {
|
||||||
t.Errorf("Expecting node at %s to have DiffType unchanged, but had %v", n.Path(), n.Data.DiffType)
|
t.Errorf("Expecting node at %s to have DiffType unchanged, but had %v", n.Path(), n.Data.DiffType)
|
||||||
}
|
}
|
||||||
@ -231,7 +224,7 @@ func TestCompareWithAdds(t *testing.T) {
|
|||||||
upperPaths := [...]string{"/etc", "/etc/sudoers", "/usr", "/etc/hosts", "/usr/bin", "/usr/bin/bash"}
|
upperPaths := [...]string{"/etc", "/etc/sudoers", "/usr", "/etc/hosts", "/usr/bin", "/usr/bin/bash"}
|
||||||
|
|
||||||
for _, value := range lowerPaths {
|
for _, value := range lowerPaths {
|
||||||
lowerTree.AddPath(value, &FileInfo{
|
lowerTree.AddPath(value, FileInfo{
|
||||||
Path: value,
|
Path: value,
|
||||||
Typeflag: 1,
|
Typeflag: 1,
|
||||||
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
@ -239,7 +232,7 @@ func TestCompareWithAdds(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, value := range upperPaths {
|
for _, value := range upperPaths {
|
||||||
upperTree.AddPath(value, &FileInfo{
|
upperTree.AddPath(value, FileInfo{
|
||||||
Path: value,
|
Path: value,
|
||||||
Typeflag: 1,
|
Typeflag: 1,
|
||||||
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
@ -291,12 +284,12 @@ func TestCompareWithChanges(t *testing.T) {
|
|||||||
paths := [...]string{"/etc", "/usr", "/etc/hosts", "/etc/sudoers", "/usr/bin"}
|
paths := [...]string{"/etc", "/usr", "/etc/hosts", "/etc/sudoers", "/usr/bin"}
|
||||||
|
|
||||||
for _, value := range paths {
|
for _, value := range paths {
|
||||||
lowerTree.AddPath(value, &FileInfo{
|
lowerTree.AddPath(value, FileInfo{
|
||||||
Path: value,
|
Path: value,
|
||||||
Typeflag: 1,
|
Typeflag: 1,
|
||||||
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
})
|
})
|
||||||
upperTree.AddPath(value, &FileInfo{
|
upperTree.AddPath(value, FileInfo{
|
||||||
Path: value,
|
Path: value,
|
||||||
Typeflag: 1,
|
Typeflag: 1,
|
||||||
MD5sum: [16]byte{1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0},
|
MD5sum: [16]byte{1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0},
|
||||||
@ -348,7 +341,7 @@ func TestCompareWithRemoves(t *testing.T) {
|
|||||||
Typeflag: 1,
|
Typeflag: 1,
|
||||||
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
}
|
}
|
||||||
lowerTree.AddPath(value, &fakeData)
|
lowerTree.AddPath(value, fakeData)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, value := range upperPaths {
|
for _, value := range upperPaths {
|
||||||
@ -357,7 +350,7 @@ func TestCompareWithRemoves(t *testing.T) {
|
|||||||
Typeflag: 1,
|
Typeflag: 1,
|
||||||
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
}
|
}
|
||||||
upperTree.AddPath(value, &fakeData)
|
upperTree.AddPath(value, fakeData)
|
||||||
}
|
}
|
||||||
|
|
||||||
lowerTree.Compare(upperTree)
|
lowerTree.Compare(upperTree)
|
||||||
@ -397,12 +390,12 @@ func TestCompareWithRemoves(t *testing.T) {
|
|||||||
|
|
||||||
func TestStackRange(t *testing.T) {
|
func TestStackRange(t *testing.T) {
|
||||||
tree := NewFileTree()
|
tree := NewFileTree()
|
||||||
tree.AddPath("/etc/nginx/nginx.conf", nil)
|
tree.AddPath("/etc/nginx/nginx.conf", FileInfo{})
|
||||||
tree.AddPath("/etc/nginx/public", nil)
|
tree.AddPath("/etc/nginx/public", FileInfo{})
|
||||||
tree.AddPath("/var/run/systemd", nil)
|
tree.AddPath("/var/run/systemd", FileInfo{})
|
||||||
tree.AddPath("/var/run/bashful", nil)
|
tree.AddPath("/var/run/bashful", FileInfo{})
|
||||||
tree.AddPath("/tmp", nil)
|
tree.AddPath("/tmp", FileInfo{})
|
||||||
tree.AddPath("/tmp/nonsense", nil)
|
tree.AddPath("/tmp/nonsense", FileInfo{})
|
||||||
|
|
||||||
tree.RemovePath("/var/run/bashful")
|
tree.RemovePath("/var/run/bashful")
|
||||||
tree.RemovePath("/tmp")
|
tree.RemovePath("/tmp")
|
||||||
@ -418,7 +411,7 @@ func TestStackRange(t *testing.T) {
|
|||||||
Typeflag: 1,
|
Typeflag: 1,
|
||||||
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
}
|
}
|
||||||
lowerTree.AddPath(value, &fakeData)
|
lowerTree.AddPath(value, fakeData)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, value := range upperPaths {
|
for _, value := range upperPaths {
|
||||||
@ -427,7 +420,7 @@ func TestStackRange(t *testing.T) {
|
|||||||
Typeflag: 1,
|
Typeflag: 1,
|
||||||
MD5sum: [16]byte{1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0},
|
MD5sum: [16]byte{1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0},
|
||||||
}
|
}
|
||||||
upperTree.AddPath(value, &fakeData)
|
upperTree.AddPath(value, fakeData)
|
||||||
}
|
}
|
||||||
trees := []*FileTree{lowerTree, upperTree, tree}
|
trees := []*FileTree{lowerTree, upperTree, tree}
|
||||||
StackRange(trees, 2)
|
StackRange(trees, 2)
|
||||||
@ -445,7 +438,7 @@ func TestRemoveOnIterate(t *testing.T) {
|
|||||||
Typeflag: 1,
|
Typeflag: 1,
|
||||||
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
}
|
}
|
||||||
node, err := tree.AddPath(value, &fakeData)
|
node, err := tree.AddPath(value, fakeData)
|
||||||
if err == nil && stringInSlice(node.Path(), []string{"/etc"}) {
|
if err == nil && stringInSlice(node.Path(), []string{"/etc"}) {
|
||||||
node.Data.ViewInfo.Hidden = true
|
node.Data.ViewInfo.Hidden = true
|
||||||
}
|
}
|
||||||
@ -458,12 +451,12 @@ func TestRemoveOnIterate(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
expected := `.
|
expected :=
|
||||||
└── usr
|
`└── usr
|
||||||
├── bin
|
├── bin
|
||||||
└── something
|
└── something
|
||||||
`
|
`
|
||||||
actual := tree.String()
|
actual := tree.String(false)
|
||||||
if expected != actual {
|
if expected != actual {
|
||||||
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
|
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,10 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LayerFormat = "%-25s %7s %s"
|
||||||
|
)
|
||||||
|
|
||||||
func check(e error) {
|
func check(e error) {
|
||||||
if e != nil {
|
if e != nil {
|
||||||
panic(e)
|
panic(e)
|
||||||
@ -56,7 +60,7 @@ func (layer *Layer) String() string {
|
|||||||
if len(layer.History.Tags) > 0 {
|
if len(layer.History.Tags) > 0 {
|
||||||
id = "[" + strings.Join(layer.History.Tags, ",") + "]"
|
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) {
|
func InitializeData(imageID string) ([]*Layer, []*filetree.FileTree) {
|
||||||
@ -103,7 +107,7 @@ func InitializeData(imageID string) ([]*Layer, []*filetree.FileTree) {
|
|||||||
tree.Name = name
|
tree.Name = name
|
||||||
fileInfos := getFileList(tarReader, header)
|
fileInfos := getFileList(tarReader, header)
|
||||||
for _, element := range fileInfos {
|
for _, element := range fileInfos {
|
||||||
tree.AddPath(element.Path, &element)
|
tree.AddPath(element.Path, element)
|
||||||
}
|
}
|
||||||
layerMap[tree.Name] = tree
|
layerMap[tree.Name] = tree
|
||||||
}
|
}
|
||||||
@ -184,9 +188,9 @@ func saveImage(imageID string) (string, string) {
|
|||||||
return imageTarPath, tmpDir
|
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 files []filetree.FileInfo
|
||||||
var tarredBytes = make([]byte, h.Size)
|
var tarredBytes = make([]byte, header.Size)
|
||||||
|
|
||||||
_, err := parentReader.Read(tarredBytes)
|
_, err := parentReader.Read(tarredBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -7,8 +7,11 @@ import (
|
|||||||
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/jroimartin/gocui"
|
"github.com/jroimartin/gocui"
|
||||||
|
"github.com/lunixbochs/vtclean"
|
||||||
"github.com/wagoodman/docker-image-explorer/filetree"
|
"github.com/wagoodman/docker-image-explorer/filetree"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,6 +19,7 @@ type FileTreeView struct {
|
|||||||
Name string
|
Name string
|
||||||
gui *gocui.Gui
|
gui *gocui.Gui
|
||||||
view *gocui.View
|
view *gocui.View
|
||||||
|
header *gocui.View
|
||||||
TreeIndex int
|
TreeIndex int
|
||||||
ModelTree *filetree.FileTree
|
ModelTree *filetree.FileTree
|
||||||
ViewTree *filetree.FileTree
|
ViewTree *filetree.FileTree
|
||||||
@ -36,7 +40,7 @@ func NewFileTreeView(name string, gui *gocui.Gui, tree *filetree.FileTree, refTr
|
|||||||
return treeview
|
return treeview
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *FileTreeView) Setup(v *gocui.View) error {
|
func (view *FileTreeView) Setup(v *gocui.View, header *gocui.View) error {
|
||||||
|
|
||||||
// set view options
|
// set view options
|
||||||
view.view = v
|
view.view = v
|
||||||
@ -45,7 +49,12 @@ func (view *FileTreeView) Setup(v *gocui.View) error {
|
|||||||
//view.view.Highlight = true
|
//view.view.Highlight = true
|
||||||
//view.view.SelBgColor = gocui.ColorGreen
|
//view.view.SelBgColor = gocui.ColorGreen
|
||||||
//view.view.SelFgColor = gocui.ColorBlack
|
//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
|
// set keybindings
|
||||||
if err := view.gui.SetKeybinding(view.Name, gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorDown() }); err != nil {
|
if err := view.gui.SetKeybinding(view.Name, gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorDown() }); err != nil {
|
||||||
@ -76,6 +85,9 @@ func (view *FileTreeView) Setup(v *gocui.View) error {
|
|||||||
view.updateViewTree()
|
view.updateViewTree()
|
||||||
view.Render()
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,6 +122,8 @@ func (view *FileTreeView) setLayer(layerIndex int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (view *FileTreeView) CursorDown() error {
|
func (view *FileTreeView) CursorDown() error {
|
||||||
|
// cannot easily (quickly) check the model length, allow the view
|
||||||
|
// to let us know what is a valid bounds (i.e. when it hits an empty line)
|
||||||
err := CursorDown(view.gui, view.view)
|
err := CursorDown(view.gui, view.view)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
view.TreeIndex++
|
view.TreeIndex++
|
||||||
@ -118,10 +132,12 @@ func (view *FileTreeView) CursorDown() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (view *FileTreeView) CursorUp() error {
|
func (view *FileTreeView) CursorUp() error {
|
||||||
|
if view.TreeIndex > 0 {
|
||||||
err := CursorUp(view.gui, view.view)
|
err := CursorUp(view.gui, view.view)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
view.TreeIndex--
|
view.TreeIndex--
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return view.Render()
|
return view.Render()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,16 +146,11 @@ func (view *FileTreeView) getAbsPositionNode() (node *filetree.FileNode) {
|
|||||||
var evaluator func(*filetree.FileNode) bool
|
var evaluator func(*filetree.FileNode) bool
|
||||||
var dfsCounter int
|
var dfsCounter int
|
||||||
|
|
||||||
// special case: the root node is never visited
|
|
||||||
if view.TreeIndex == 0 {
|
|
||||||
return view.ModelTree.Root
|
|
||||||
}
|
|
||||||
|
|
||||||
visiter = func(curNode *filetree.FileNode) error {
|
visiter = func(curNode *filetree.FileNode) error {
|
||||||
dfsCounter++
|
|
||||||
if dfsCounter == view.TreeIndex {
|
if dfsCounter == view.TreeIndex {
|
||||||
node = curNode
|
node = curNode
|
||||||
}
|
}
|
||||||
|
dfsCounter++
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var filterBytes []byte
|
var filterBytes []byte
|
||||||
@ -236,12 +247,12 @@ func (view *FileTreeView) KeyHelp() string {
|
|||||||
|
|
||||||
func (view *FileTreeView) Render() error {
|
func (view *FileTreeView) Render() error {
|
||||||
// print the tree to the view
|
// print the tree to the view
|
||||||
lines := strings.Split(view.ViewTree.String(), "\n")
|
lines := strings.Split(view.ViewTree.String(true), "\n")
|
||||||
view.gui.Update(func(g *gocui.Gui) error {
|
view.gui.Update(func(g *gocui.Gui) error {
|
||||||
view.view.Clear()
|
view.view.Clear()
|
||||||
for idx, line := range lines {
|
for idx, line := range lines {
|
||||||
if idx == view.TreeIndex {
|
if idx == view.TreeIndex {
|
||||||
fmt.Fprintln(view.view, Formatting.Header(line))
|
fmt.Fprintln(view.view, Formatting.StatusBar(vtclean.Clean(line, false)))
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintln(view.view, line)
|
fmt.Fprintln(view.view, line)
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,14 @@ import (
|
|||||||
|
|
||||||
"github.com/jroimartin/gocui"
|
"github.com/jroimartin/gocui"
|
||||||
"github.com/wagoodman/docker-image-explorer/image"
|
"github.com/wagoodman/docker-image-explorer/image"
|
||||||
|
"github.com/lunixbochs/vtclean"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LayerView struct {
|
type LayerView struct {
|
||||||
Name string
|
Name string
|
||||||
gui *gocui.Gui
|
gui *gocui.Gui
|
||||||
view *gocui.View
|
view *gocui.View
|
||||||
|
header *gocui.View
|
||||||
LayerIndex int
|
LayerIndex int
|
||||||
Layers []*image.Layer
|
Layers []*image.Layer
|
||||||
}
|
}
|
||||||
@ -26,16 +28,22 @@ func NewLayerView(name string, gui *gocui.Gui, layers []*image.Layer) (layerview
|
|||||||
return 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
|
// set view options
|
||||||
view.view = v
|
view.view = v
|
||||||
|
view.view.Editable = false
|
||||||
view.view.Wrap = false
|
view.view.Wrap = false
|
||||||
//view.view.Highlight = true
|
//view.view.Highlight = true
|
||||||
//view.view.SelBgColor = gocui.ColorGreen
|
//view.view.SelBgColor = gocui.ColorGreen
|
||||||
//view.view.SelFgColor = gocui.ColorBlack
|
//view.view.SelFgColor = gocui.ColorBlack
|
||||||
view.view.Frame = false
|
view.view.Frame = false
|
||||||
|
|
||||||
|
view.header = header
|
||||||
|
view.header.Editable = false
|
||||||
|
view.header.Wrap = false
|
||||||
|
view.header.Frame = false
|
||||||
|
|
||||||
// set keybindings
|
// set keybindings
|
||||||
if err := view.gui.SetKeybinding(view.Name, gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorDown() }); err != nil {
|
if err := view.gui.SetKeybinding(view.Name, gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { return view.CursorDown() }); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -44,9 +52,10 @@ func (view *LayerView) Setup(v *gocui.View) error {
|
|||||||
return err
|
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 {
|
func (view *LayerView) Render() error {
|
||||||
@ -57,7 +66,7 @@ func (view *LayerView) Render() error {
|
|||||||
idx := (len(view.Layers)-1) - revIdx
|
idx := (len(view.Layers)-1) - revIdx
|
||||||
|
|
||||||
if idx == view.LayerIndex {
|
if idx == view.LayerIndex {
|
||||||
fmt.Fprintln(view.view, Formatting.Header(layer.String()))
|
fmt.Fprintln(view.view, Formatting.StatusBar(layer.String()))
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintln(view.view, layer.String())
|
fmt.Fprintln(view.view, layer.String())
|
||||||
}
|
}
|
||||||
@ -70,7 +79,7 @@ func (view *LayerView) Render() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (view *LayerView) CursorDown() error {
|
func (view *LayerView) CursorDown() error {
|
||||||
if int(view.LayerIndex) < len(view.Layers) {
|
if view.LayerIndex < len(view.Layers) {
|
||||||
err := CursorDown(view.gui, view.view)
|
err := CursorDown(view.gui, view.view)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
view.LayerIndex++
|
view.LayerIndex++
|
||||||
@ -82,7 +91,7 @@ func (view *LayerView) CursorDown() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (view *LayerView) CursorUp() error {
|
func (view *LayerView) CursorUp() error {
|
||||||
if int(view.LayerIndex) > 0 {
|
if view.LayerIndex > 0 {
|
||||||
err := CursorUp(view.gui, view.view)
|
err := CursorUp(view.gui, view.view)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
view.LayerIndex--
|
view.LayerIndex--
|
||||||
|
@ -23,7 +23,7 @@ func NewStatusView(name string, gui *gocui.Gui) (statusview *StatusView) {
|
|||||||
return 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
|
// set view options
|
||||||
view.view = v
|
view.view = v
|
||||||
|
37
ui/ui.go
37
ui/ui.go
@ -1,6 +1,7 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
@ -13,6 +14,7 @@ const debug = false
|
|||||||
|
|
||||||
var Formatting struct {
|
var Formatting struct {
|
||||||
Header func(...interface{}) string
|
Header func(...interface{}) string
|
||||||
|
StatusBar func(...interface{}) string
|
||||||
}
|
}
|
||||||
|
|
||||||
var Views struct {
|
var Views struct {
|
||||||
@ -24,7 +26,7 @@ var Views struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type View interface {
|
type View interface {
|
||||||
Setup(*gocui.View) error
|
Setup(*gocui.View, *gocui.View) error
|
||||||
CursorDown() error
|
CursorDown() error
|
||||||
CursorUp() error
|
CursorUp() error
|
||||||
Render() error
|
Render() error
|
||||||
@ -63,7 +65,7 @@ func CursorDown(g *gocui.Gui, v *gocui.View) error {
|
|||||||
// todo: handle error
|
// todo: handle error
|
||||||
}
|
}
|
||||||
if len(line) == 0 {
|
if len(line) == 0 {
|
||||||
return nil
|
return errors.New("unable to move cursor down, empty line")
|
||||||
}
|
}
|
||||||
if err := v.SetCursor(cx, cy+1); err != nil {
|
if err := v.SetCursor(cx, cy+1); err != nil {
|
||||||
ox, oy := v.Origin()
|
ox, oy := v.Origin()
|
||||||
@ -121,24 +123,40 @@ func layout(g *gocui.Gui) error {
|
|||||||
}
|
}
|
||||||
debugCols := maxX - debugWidth
|
debugCols := maxX - debugWidth
|
||||||
bottomRows := 1
|
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 {
|
if err != gocui.ErrUnknownView {
|
||||||
return err
|
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 {
|
if _, err := g.SetCurrentView(Views.Layer.Name); err != nil {
|
||||||
return err
|
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 {
|
if err != gocui.ErrUnknownView {
|
||||||
return err
|
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 debug {
|
||||||
if _, err := g.SetView("debug", debugCols, -1, maxX, maxY-bottomRows); err != nil {
|
if _, err := g.SetView("debug", debugCols, -1, maxX, maxY-bottomRows); err != nil {
|
||||||
if err != gocui.ErrUnknownView {
|
if err != gocui.ErrUnknownView {
|
||||||
@ -146,11 +164,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 view, err := g.SetView(Views.Status.Name, -1, maxY-bottomRows-1, maxX, maxY); err != nil {
|
||||||
if err != gocui.ErrUnknownView {
|
if err != gocui.ErrUnknownView {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
Views.Status.Setup(view)
|
Views.Status.Setup(view, nil)
|
||||||
|
|
||||||
}
|
}
|
||||||
if view, err := g.SetView(Views.Command.Name, -1, maxY-bottomRows-2, maxX, maxY-1); err != nil {
|
if view, err := g.SetView(Views.Command.Name, -1, maxY-bottomRows-2, maxX, maxY-1); err != nil {
|
||||||
@ -171,7 +191,8 @@ func Render() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Run(layers []*image.Layer, refTrees []*filetree.FileTree) {
|
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)
|
g, err := gocui.NewGui(gocui.OutputNormal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user