diff --git a/config/module/tree.go b/config/module/tree.go index 639a925b5..d08b047bd 100644 --- a/config/module/tree.go +++ b/config/module/tree.go @@ -1,7 +1,10 @@ package module import ( + "bufio" + "bytes" "fmt" + "strings" "sync" "github.com/hashicorp/terraform/config" @@ -13,9 +16,10 @@ import ( // all the modules without getting, flatten the tree into something // Terraform can use, etc. type Tree struct { + name string config *config.Config children []*Tree - lock sync.Mutex + lock sync.RWMutex } // GetMode is an enum that describes how modules are loaded. @@ -47,7 +51,9 @@ func NewTree(c *config.Config) *Tree { // // This will only return a non-nil value after Load is called. func (t *Tree) Children() []*Tree { - return nil + t.lock.RLock() + defer t.lock.RUnlock() + return t.children } // Flatten takes the entire module tree and flattens it into a single @@ -77,6 +83,16 @@ func (t *Tree) Modules() []*Module { return result } +// Name returns the name of the tree. This will be "" for the root +// tree and then the module name given for any children. +func (t *Tree) Name() string { + if t.name == "" { + return "" + } + + return t.name +} + // Load loads the configuration of the entire tree. // // The parameters are used to tell the tree where to find modules and @@ -130,6 +146,7 @@ func (t *Tree) Load(s Storage, mode GetMode) error { "module %s: %s", m.Name, err) } children[i] = NewTree(c) + children[i].name = m.Name } // Go through all the children and load them. @@ -145,6 +162,31 @@ func (t *Tree) Load(s Storage, mode GetMode) error { return nil } +// String gives a nice output to describe the tree. +func (t *Tree) String() string { + var result bytes.Buffer + result.WriteString(t.Name() + "\n") + + cs := t.Children() + if cs == nil { + result.WriteString(" not loaded") + } else { + // Go through each child and get its string value, then indent it + // by two. + for _, c := range cs { + r := strings.NewReader(c.String()) + scanner := bufio.NewScanner(r) + for scanner.Scan() { + result.WriteString(" ") + result.WriteString(scanner.Text()) + result.WriteString("\n") + } + } + } + + return result.String() +} + // Validate does semantic checks on the entire tree of configurations. // // This will call the respective config.Config.Validate() functions as well diff --git a/config/module/tree_test.go b/config/module/tree_test.go index fbb5e73fe..fe9e09604 100644 --- a/config/module/tree_test.go +++ b/config/module/tree_test.go @@ -2,14 +2,34 @@ package module import ( "reflect" + "strings" "testing" ) func TestTree_Load(t *testing.T) { + storage := testStorage(t) tree := NewTree(testConfig(t, "basic")) - if err := tree.Load(testStorage(t), GetModeGet); err != nil { + + // This should error because we haven't gotten things yet + if err := tree.Load(storage, GetModeNone); err == nil { + t.Fatal("should error") + } + + // This should get things + if err := tree.Load(storage, GetModeGet); err != nil { t.Fatalf("err: %s", err) } + + // This should no longer error + if err := tree.Load(storage, GetModeNone); err != nil { + t.Fatalf("err: %s", err) + } + + actual := strings.TrimSpace(tree.String()) + expected := strings.TrimSpace(treeLoadStr) + if actual != expected { + t.Fatalf("bad: \n\n%s", actual) + } } func TestTree_Modules(t *testing.T) { @@ -24,3 +44,17 @@ func TestTree_Modules(t *testing.T) { t.Fatalf("bad: %#v", actual) } } + +func TestTree_Name(t *testing.T) { + tree := NewTree(testConfig(t, "basic")) + actual := tree.Name() + + if actual != "" { + t.Fatalf("bad: %#v", actual) + } +} + +const treeLoadStr = ` + + foo +`