added tree stacking

This commit is contained in:
Alex Goodman 2018-05-24 20:49:59 -04:00
parent ea66c0e810
commit 86e979c80c
No known key found for this signature in database
GPG Key ID: 05328C611D8A520E
6 changed files with 227 additions and 60 deletions

4
.gitignore vendored
View File

@ -1,3 +1,5 @@
.idea
# Binaries for programs and plugins # Binaries for programs and plugins
*.exe *.exe
*.exe~ *.exe~
@ -11,4 +13,4 @@
# Output of the go coverage tool, specifically when used with LiteIDE # Output of the go coverage tool, specifically when used with LiteIDE
*.out *.out
image image

View File

@ -3,31 +3,108 @@ package main
import ( import (
"errors" "errors"
"strings" "strings"
"fmt"
) )
type FileTree interface { type FileTree interface {
AddPath(string, interface{}) AddPath(string, interface{}) *Node
RemovePath(string) RemovePath(string) error
Visit(Visiter) 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) { func (tree *Tree) Visit(visiter Visiter) error {
tree.root.Visit(visiter) return tree.root.Visit(visiter)
} }
func (node *Node) Visit(visiter Visiter) { func (node *Node) Visit(visiter Visiter) error {
for _, child := range node.children { 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) { func (tree *Tree) AddPath(path string, data interface{}) (*Node, error) {
nodeNames := strings.Split(path, "/") nodeNames := strings.Split(path, "/")
node := tree.Root() node := tree.Root()
var err error
for idx, name := range nodeNames { for idx, name := range nodeNames {
if name == "" { if name == "" {
continue continue
@ -36,15 +113,12 @@ func (tree *Tree) AddPath(path string, data interface{}) (*Node, error) {
if node.children[name] != nil { if node.children[name] != nil {
node = node.children[name] node = node.children[name]
} else { } else {
// don't attach the payload. The payload is destined for the
node, _ = node.AddChild(name, nil) // path's end node, not any intermediary node.
if err != nil { node = node.AddChild(name, nil)
return node, err
}
} }
// attach payload // attach payload to the last specified node
if idx == len(nodeNames)-1 { if idx == len(nodeNames)-1 {
node.data = data node.data = data
} }
@ -54,17 +128,9 @@ func (tree *Tree) AddPath(path string, data interface{}) (*Node, error) {
} }
func (tree *Tree) RemovePath(path string) error { func (tree *Tree) RemovePath(path string) error {
nodeNames := strings.Split(path, "/") node, err := tree.GetNode(path)
node := tree.Root() if err != nil {
for _, name := range nodeNames { return err
if name == "" {
continue
}
if node.children[name] == nil {
return errors.New("Path does not exist")
}
node = node.children[name]
} }
// this node's parent should be a leaf
return node.Remove() return node.Remove()
} }

View File

@ -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)
}
}

View File

@ -55,7 +55,7 @@ func saveImage(readCloser io.ReadCloser) {
} }
} }
func main() { func demo() {
ctx := context.Background() ctx := context.Background()
cli, err := client.NewEnvClient() cli, err := client.NewEnvClient()
if err != nil { if err != nil {

48
tree.go
View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"errors"
"sort" "sort"
) )
@ -14,7 +13,7 @@ const (
) )
type Tree struct { type Tree struct {
root Node root *Node
size int size int
} }
@ -29,10 +28,9 @@ type Node struct {
func NewTree() (tree *Tree) { func NewTree() (tree *Tree) {
tree = new(Tree) tree = new(Tree)
tree.size = 0 tree.size = 0
root := &tree.root tree.root = new(Node)
root.tree = tree tree.root.tree = tree
root.name = "" tree.root.children = make(map[string]*Node)
root.children = make(map[string]*Node)
return tree return tree
} }
@ -47,17 +45,19 @@ func NewNode(parent *Node, name string, data interface{}) (node *Node) {
} }
func (tree *Tree) Root() *Node { func (tree *Tree) Root() *Node {
return &tree.root return tree.root
} }
func (node *Node) AddChild(name string, data interface{}) (child *Node, error error) { func (node *Node) AddChild(name string, data interface{}) (child *Node) {
if node.children[name] != nil {
return nil, errors.New("Tree node already exists")
}
child = NewNode(node, name, data) child = NewNode(node, name, data)
node.children[name] = child if node.children[name] != nil {
node.tree.size++ // tree node already exists, replace the payload, keep the children
return child, nil node.children[name].data = data
} else {
node.children[name] = child
node.tree.size++
}
return child
} }
func (node *Node) Remove() error { func (node *Node) Remove() error {
@ -116,3 +116,23 @@ func (tree *Tree) String() string {
return "." + newLine + walkTree(tree.Root(), []bool{}) 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
}

View File

@ -6,24 +6,14 @@ func TestAddChild(t *testing.T) {
var expected, actual int var expected, actual int
tree := NewTree() tree := NewTree()
_, err := tree.Root().AddChild("first node!", 1) one := tree.Root().AddChild("first node!", 1)
if err != nil {
t.Errorf("Adding valued child should not result in error.")
}
two, err := tree.Root().AddChild("nil node!", nil) two := tree.Root().AddChild("nil node!", nil)
if err != nil {
t.Errorf("Adding nil child should not result in error.")
}
tree.Root().AddChild("third node!", 3) tree.Root().AddChild("third node!", 3)
two.AddChild("forth, one level down...", 4) two.AddChild("forth, one level down...", 4)
two.AddChild("fifth, one level down...", 5) two.AddChild("fifth, one level down...", 5)
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.")
}
expected, actual = 5, tree.size expected, actual = 5, tree.size
if expected != actual { 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) 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) { func TestRemoveChild(t *testing.T) {
@ -47,9 +46,9 @@ func TestRemoveChild(t *testing.T) {
tree := NewTree() tree := NewTree()
tree.Root().AddChild("first", 1) tree.Root().AddChild("first", 1)
two, _ := tree.Root().AddChild("nil", nil) two := tree.Root().AddChild("nil", nil)
tree.Root().AddChild("third", 3) tree.Root().AddChild("third", 3)
forth, _ := two.AddChild("forth", 4) forth := two.AddChild("forth", 4)
two.AddChild("fifth", 5) two.AddChild("fifth", 5)
forth.Remove() forth.Remove()
@ -79,7 +78,7 @@ func TestRemoveChild(t *testing.T) {
func TestPrintTree(t *testing.T) { func TestPrintTree(t *testing.T) {
tree := NewTree() tree := NewTree()
tree.Root().AddChild("first node!", nil) 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) tree.Root().AddChild("third node!", nil)
two.AddChild("forth, one level down...", nil) two.AddChild("forth, one level down...", nil)