From 86e979c80cfb81dbdd22f8b409d8399ec0dabb98 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 24 May 2018 20:49:59 -0400 Subject: [PATCH] added tree stacking --- .gitignore | 4 +- filetree.go | 122 ++++++++++++++++++++++++++++++++++++----------- filetree_test.go | 80 +++++++++++++++++++++++++++++++ main.go | 2 +- tree.go | 48 +++++++++++++------ tree_test.go | 31 ++++++------ 6 files changed, 227 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index f5cd106..fac93dd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.idea + # Binaries for programs and plugins *.exe *.exe~ @@ -11,4 +13,4 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out -image \ No newline at end of file +image diff --git a/filetree.go b/filetree.go index f42faf8..d92a598 100644 --- a/filetree.go +++ b/filetree.go @@ -3,31 +3,108 @@ package main import ( "errors" "strings" + "fmt" ) type FileTree interface { - AddPath(string, interface{}) - RemovePath(string) - Visit(Visiter) + AddPath(string, interface{}) *Node + RemovePath(string) error + Visit(Visiter) error + // Diff(*Tree) error + Stack(*Tree) (Tree, error) } -type Visiter func(*Node) +type Visiter func(*Node) error -func (tree *Tree) Visit(visiter Visiter) { - tree.root.Visit(visiter) +func (tree *Tree) Visit(visiter Visiter) error { + return tree.root.Visit(visiter) } -func (node *Node) Visit(visiter Visiter) { +func (node *Node) Visit(visiter Visiter) error { for _, child := range node.children { - child.Visit(visiter) + err := child.Visit(visiter) + if err != nil { + return err + } } - visiter(node) + return visiter(node) +} + +func (node *Node) IsWhiteout() bool { + return strings.HasPrefix(node.name, ".wh.") +} + +func (node *Node) Path() string { + path := []string{} + curNode := node + for { + if curNode.parent == nil{ + break + } + path = append([]string{curNode.name}, path...) + curNode = curNode.parent + } + return "/" + strings.Join(path, "/") +} + + +func (node *Node) WhiteoutPath() string { + path := []string{} + curNode := node + for { + if curNode.parent == nil{ + break + } + + name := curNode.name + if curNode == node { + name = strings.TrimPrefix(name, ".wh.") + } + + path = append([]string{name}, path...) + curNode = curNode.parent + } + return "/" + strings.Join(path, "/") +} + + + +func (tree *Tree) Stack(upper *Tree) (error) { + graft := func(node *Node) error { + if node.IsWhiteout() { + err := tree.RemovePath(node.WhiteoutPath()) + if err != nil { + return fmt.Errorf("Cannot remove node %s: %v", node.Path(), err.Error()) + } + } else { + newNode, err := tree.AddPath(node.Path(), node.data) + if err != nil { + return fmt.Errorf("Cannot add node %s: %v", newNode.Path(), err.Error()) + } + } + return nil + } + return upper.Visit(graft) +} + +func (tree *Tree) GetNode(path string) (*Node, error) { + nodeNames := strings.Split(path, "/") + node := tree.Root() + for _, name := range nodeNames { + if name == "" { + continue + } + if node.children[name] == nil { + return nil, errors.New("Path does not exist") + } + node = node.children[name] + } + return node, nil } func (tree *Tree) AddPath(path string, data interface{}) (*Node, error) { nodeNames := strings.Split(path, "/") node := tree.Root() - var err error for idx, name := range nodeNames { if name == "" { continue @@ -36,15 +113,12 @@ func (tree *Tree) AddPath(path string, data interface{}) (*Node, error) { if node.children[name] != nil { node = node.children[name] } else { - - node, _ = node.AddChild(name, nil) - if err != nil { - - return node, err - } + // don't attach the payload. The payload is destined for the + // path's end node, not any intermediary node. + node = node.AddChild(name, nil) } - // attach payload + // attach payload to the last specified node if idx == len(nodeNames)-1 { node.data = data } @@ -54,17 +128,9 @@ func (tree *Tree) AddPath(path string, data interface{}) (*Node, error) { } func (tree *Tree) RemovePath(path string) error { - nodeNames := strings.Split(path, "/") - node := tree.Root() - for _, name := range nodeNames { - if name == "" { - continue - } - if node.children[name] == nil { - return errors.New("Path does not exist") - } - node = node.children[name] + node, err := tree.GetNode(path) + if err != nil { + return err } - // this node's parent should be a leaf return node.Remove() } diff --git a/filetree_test.go b/filetree_test.go index ef322bf..9280759 100644 --- a/filetree_test.go +++ b/filetree_test.go @@ -59,3 +59,83 @@ func TestRemovePath(t *testing.T) { } } + +func TestPath(t *testing.T) { + expected := "/etc/nginx/nginx.conf" + tree := NewTree() + node, _ := tree.AddPath(expected, nil) + + actual := node.Path() + if expected != actual { + t.Errorf("Expected path '%s' got '%s'", expected, actual) + } +} + +func TestIsWhiteout(t *testing.T) { + tree1 := NewTree() + p1, _ := tree1.AddPath("/etc/nginx/public1", 2) + p2, _ := tree1.AddPath("/etc/nginx/.wh.public2", 2) + + if p1.IsWhiteout() != false { + t.Errorf("Expected path '%s' to **not** be a whiteout file", p1.name) + } + + if p2.IsWhiteout() != true { + t.Errorf("Expected path '%s' to be a whiteout file", p2.name) + } +} + + +func TestStack(t *testing.T) { + payloadKey := "/var/run/systemd" + payloadValue := 1263487 + + tree1 := NewTree() + + tree1.AddPath("/etc/nginx/public", 2) + tree1.AddPath(payloadKey, 3) + tree1.AddPath("/var/run/bashful", 4) + tree1.AddPath("/tmp", 5) + tree1.AddPath("/tmp/nonsense", 6) + + tree2 := NewTree() + // add new files + tree2.AddPath("/etc/nginx/nginx.conf", 1) + // modify current files + tree2.AddPath(payloadKey, payloadValue) + // whiteout the following files + tree2.AddPath("/var/run/.wh.bashful", nil) + tree2.AddPath("/.wh.tmp", nil) + + err := tree1.Stack(tree2) + + if err != nil { + t.Errorf("Could not stack trees: %v", err) + } + + expected := `. +├── etc +│ └── nginx +│ ├── nginx.conf +│ └── public +└── var + └── run + └── systemd +` + + node, err := tree1.GetNode(payloadKey) + if err != nil { + t.Errorf("Expected '%s' to still exist, but it doesn't", payloadKey) + } + + if node.data != payloadValue { + t.Errorf("Expected '%s' value to be %d but got %d", payloadKey, payloadValue, node.data.(int)) + } + + actual := tree1.String() + + if expected != actual { + t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual) + } + +} \ No newline at end of file diff --git a/main.go b/main.go index d23ba4f..97138b3 100644 --- a/main.go +++ b/main.go @@ -55,7 +55,7 @@ func saveImage(readCloser io.ReadCloser) { } } -func main() { +func demo() { ctx := context.Background() cli, err := client.NewEnvClient() if err != nil { diff --git a/tree.go b/tree.go index 4a2e005..83159c7 100644 --- a/tree.go +++ b/tree.go @@ -1,7 +1,6 @@ package main import ( - "errors" "sort" ) @@ -14,7 +13,7 @@ const ( ) type Tree struct { - root Node + root *Node size int } @@ -29,10 +28,9 @@ type Node struct { func NewTree() (tree *Tree) { tree = new(Tree) tree.size = 0 - root := &tree.root - root.tree = tree - root.name = "" - root.children = make(map[string]*Node) + tree.root = new(Node) + tree.root.tree = tree + tree.root.children = make(map[string]*Node) return tree } @@ -47,17 +45,19 @@ func NewNode(parent *Node, name string, data interface{}) (node *Node) { } func (tree *Tree) Root() *Node { - return &tree.root + return tree.root } -func (node *Node) AddChild(name string, data interface{}) (child *Node, error error) { - if node.children[name] != nil { - return nil, errors.New("Tree node already exists") - } +func (node *Node) AddChild(name string, data interface{}) (child *Node) { child = NewNode(node, name, data) - node.children[name] = child - node.tree.size++ - return child, nil + if node.children[name] != nil { + // tree node already exists, replace the payload, keep the children + node.children[name].data = data + } else { + node.children[name] = child + node.tree.size++ + } + return child } func (node *Node) Remove() error { @@ -116,3 +116,23 @@ func (tree *Tree) String() string { return "." + newLine + walkTree(tree.Root(), []bool{}) } + +func (node *Node) Copy() *Node { + // newNode := new(Node) + // *newNode = *node + // return newNode + newNode := NewNode(node.parent, node.name, node.data) + for name, child := range node.children { + newNode.children[name] = child.Copy() + } + return newNode +} + +func (tree *Tree) Copy() *Tree { + newTree := NewTree() + *newTree = *tree + newTree.root = tree.Root().Copy() + + return newTree +} + diff --git a/tree_test.go b/tree_test.go index 3a835a7..b143789 100644 --- a/tree_test.go +++ b/tree_test.go @@ -6,24 +6,14 @@ func TestAddChild(t *testing.T) { var expected, actual int tree := NewTree() - _, err := tree.Root().AddChild("first node!", 1) - if err != nil { - t.Errorf("Adding valued child should not result in error.") - } + one := tree.Root().AddChild("first node!", 1) - two, err := tree.Root().AddChild("nil node!", nil) - if err != nil { - t.Errorf("Adding nil child should not result in error.") - } + two := tree.Root().AddChild("nil node!", nil) tree.Root().AddChild("third node!", 3) two.AddChild("forth, one level down...", 4) two.AddChild("fifth, one level down...", 5) - - _, err = two.AddChild("fifth, one level down...", 5) - if err == nil { - t.Errorf("Expected an error when adding duplicate nodes, no error given.") - } + two.AddChild("fifth, one level down...", 5) expected, actual = 5, tree.size if expected != actual { @@ -40,6 +30,15 @@ func TestAddChild(t *testing.T) { t.Errorf("Expected 'twos' number of children to be %d got %d.", expected, actual) } + if two.data != nil { + t.Errorf("Expected 'ones' payload to be nil got %d.", two.data) + } + + expected, actual = 1, one.data.(int) + if expected != actual { + t.Errorf("Expected 'ones' payload to be %d got %d.", expected, actual) + } + } func TestRemoveChild(t *testing.T) { @@ -47,9 +46,9 @@ func TestRemoveChild(t *testing.T) { tree := NewTree() tree.Root().AddChild("first", 1) - two, _ := tree.Root().AddChild("nil", nil) + two := tree.Root().AddChild("nil", nil) tree.Root().AddChild("third", 3) - forth, _ := two.AddChild("forth", 4) + forth := two.AddChild("forth", 4) two.AddChild("fifth", 5) forth.Remove() @@ -79,7 +78,7 @@ func TestRemoveChild(t *testing.T) { func TestPrintTree(t *testing.T) { tree := NewTree() tree.Root().AddChild("first node!", nil) - two, _ := tree.Root().AddChild("second node!", nil) + two := tree.Root().AddChild("second node!", nil) tree.Root().AddChild("third node!", nil) two.AddChild("forth, one level down...", nil)