config/module: Validate

This commit is contained in:
Mitchell Hashimoto 2014-09-15 09:37:40 -07:00
parent 30b76ef820
commit c9fd910c41
7 changed files with 131 additions and 3 deletions

View File

@ -0,0 +1,3 @@
# Duplicate resources
resource "aws_instance" "foo" {}
resource "aws_instance" "foo" {}

View File

@ -0,0 +1,3 @@
module "foo" {
source = "./child"
}

View File

@ -0,0 +1 @@
# Good

View File

@ -0,0 +1,3 @@
module "child" {
source = "./child"
}

View File

@ -0,0 +1,3 @@
# Duplicate resources
resource "aws_instance" "foo" {}
resource "aws_instance" "foo" {}

View File

@ -67,6 +67,13 @@ func (t *Tree) Flatten() (*config.Config, error) {
return nil, nil
}
// Loaded says whether or not this tree has been loaded or not yet.
func (t *Tree) Loaded() bool {
t.lock.RLock()
defer t.lock.RUnlock()
return t.children != nil
}
// Modules returns the list of modules that this tree imports.
//
// This is only the imports of _this_ level of the tree. To retrieve the
@ -191,6 +198,58 @@ func (t *Tree) String() string {
//
// This will call the respective config.Config.Validate() functions as well
// as verifying things such as parameters/outputs between the various modules.
//
// Load must be called prior to calling Validate or an error will be returned.
func (t *Tree) Validate() error {
if !t.Loaded() {
return fmt.Errorf("tree must be loaded before calling Validate")
}
// Validate our configuration first.
if err := t.config.Validate(); err != nil {
return &ValidateError{
Name: []string{t.Name()},
Err: err,
}
}
// Validate all our children
for _, c := range t.Children() {
err := c.Validate()
if err == nil {
continue
}
verr, ok := err.(*ValidateError)
if !ok {
// Unknown error, just return...
return err
}
// Append ourselves to the error and then return
verr.Name = append(verr.Name, t.Name())
return verr
}
return nil
}
// ValidateError is an error returned by Tree.Validate if an error occurs
// with validation.
type ValidateError struct {
Name []string
Err error
}
func (e *ValidateError) Error() string {
// Build up the name
var buf bytes.Buffer
for _, n := range e.Name {
buf.WriteString(n)
buf.WriteString(".")
}
buf.Truncate(buf.Len()-1)
// Format the value
return fmt.Sprintf("module %s: %s", buf.String(), e.Err)
}

View File

@ -6,20 +6,32 @@ import (
"testing"
)
func TestTree_Load(t *testing.T) {
func TestTreeLoad(t *testing.T) {
storage := testStorage(t)
tree := NewTree(testConfig(t, "basic"))
if tree.Loaded() {
t.Fatal("should not be loaded")
}
// This should error because we haven't gotten things yet
if err := tree.Load(storage, GetModeNone); err == nil {
t.Fatal("should error")
}
if tree.Loaded() {
t.Fatal("should not be loaded")
}
// This should get things
if err := tree.Load(storage, GetModeGet); err != nil {
t.Fatalf("err: %s", err)
}
if !tree.Loaded() {
t.Fatal("should be loaded")
}
// This should no longer error
if err := tree.Load(storage, GetModeNone); err != nil {
t.Fatalf("err: %s", err)
@ -32,7 +44,7 @@ func TestTree_Load(t *testing.T) {
}
}
func TestTree_Modules(t *testing.T) {
func TestTreeModules(t *testing.T) {
tree := NewTree(testConfig(t, "basic"))
actual := tree.Modules()
@ -45,7 +57,7 @@ func TestTree_Modules(t *testing.T) {
}
}
func TestTree_Name(t *testing.T) {
func TestTreeName(t *testing.T) {
tree := NewTree(testConfig(t, "basic"))
actual := tree.Name()
@ -54,6 +66,50 @@ func TestTree_Name(t *testing.T) {
}
}
func TestTreeValidate_badChild(t *testing.T) {
tree := NewTree(testConfig(t, "validate-child-bad"))
if err := tree.Load(testStorage(t), GetModeGet); err != nil {
t.Fatalf("err: %s", err)
}
if err := tree.Validate(); err == nil {
t.Fatal("should error")
}
}
func TestTreeValidate_badRoot(t *testing.T) {
tree := NewTree(testConfig(t, "validate-root-bad"))
if err := tree.Load(testStorage(t), GetModeGet); err != nil {
t.Fatalf("err: %s", err)
}
if err := tree.Validate(); err == nil {
t.Fatal("should error")
}
}
func TestTreeValidate_good(t *testing.T) {
tree := NewTree(testConfig(t, "validate-child-good"))
if err := tree.Load(testStorage(t), GetModeGet); err != nil {
t.Fatalf("err: %s", err)
}
if err := tree.Validate(); err != nil {
t.Fatalf("err: %s", err)
}
}
func TestTreeValidate_notLoaded(t *testing.T) {
tree := NewTree(testConfig(t, "basic"))
if err := tree.Validate(); err == nil {
t.Fatal("should error")
}
}
const treeLoadStr = `
<root>
foo