terraform: start implementing interfaces for semantic checks

This commit is contained in:
Mitchell Hashimoto 2015-01-23 17:52:51 -08:00
parent b48a0cc3b9
commit 21e4501edb
5 changed files with 82 additions and 8 deletions

14
dag/dag.go Normal file
View File

@ -0,0 +1,14 @@
package dag
// AcyclicGraph is a specialization of Graph that cannot have cycles. With
// this property, we get the property of sane graph traversal.
type AcyclicGraph struct {
*Graph
}
// WalkFunc is the callback used for walking the graph.
type WalkFunc func(Vertex)
// Walk walks the graph, calling your callback as each node is visited.
func (g *AcyclicGraph) Walk(cb WalkFunc) {
}

View File

@ -26,9 +26,6 @@ type NamedVertex interface {
Name() string Name() string
} }
// WalkFunc is the callback used for walking the graph.
type WalkFunc func(Vertex)
// Vertices returns the list of all the vertices in the graph. // Vertices returns the list of all the vertices in the graph.
func (g *Graph) Vertices() []Vertex { func (g *Graph) Vertices() []Vertex {
return g.vertices return g.vertices
@ -81,10 +78,6 @@ func (g *Graph) Connect(edge Edge) {
s.Add(source) s.Add(source)
} }
// Walk walks the graph, calling your callback as each node is visited.
func (g *Graph) Walk(cb WalkFunc) {
}
// String outputs some human-friendly output for the graph structure. // String outputs some human-friendly output for the graph structure.
func (g *Graph) String() string { func (g *Graph) String() string {
var buf bytes.Buffer var buf bytes.Buffer

View File

@ -39,8 +39,12 @@ func Graph2(mod *module.Tree) (*dag.Graph, error) {
} }
// Write all the modules out // Write all the modules out
children := mod.Children()
for _, m := range config.Modules { for _, m := range config.Modules {
nodes = append(nodes, &GraphNodeConfigModule{Module: m}) nodes = append(nodes, &GraphNodeConfigModule{
Module: m,
Tree: children[m.Name],
})
} }
// Build the full map of the var names to the nodes. // Build the full map of the var names to the nodes.

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/config/module"
"github.com/hashicorp/terraform/dag" "github.com/hashicorp/terraform/dag"
) )
@ -27,6 +28,7 @@ type graphNodeConfig interface {
// GraphNodeConfigModule represents a module within the configuration graph. // GraphNodeConfigModule represents a module within the configuration graph.
type GraphNodeConfigModule struct { type GraphNodeConfigModule struct {
Module *config.Module Module *config.Module
Tree *module.Tree
} }
func (n *GraphNodeConfigModule) Name() string { func (n *GraphNodeConfigModule) Name() string {

View File

@ -3,9 +3,70 @@ package terraform
import ( import (
"fmt" "fmt"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/dag"
) )
// GraphSemanticChecker is the interface that semantic checks across
// the entire Terraform graph implement.
//
// The graph should NOT be modified by the semantic checker.
type GraphSemanticChecker interface {
Check(*dag.Graph) error
}
// UnorderedSemanticCheckRunner is an implementation of GraphSemanticChecker
// that runs a list of SemanticCheckers against the vertices of the graph
// in no specified order.
type UnorderedSemanticCheckRunner struct {
Checks []SemanticChecker
}
func (sc *UnorderedSemanticCheckRunner) Check(g *dag.Graph) error {
var err error
for _, v := range g.Vertices() {
for _, check := range sc.Checks {
if e := check.Check(g, v); e != nil {
err = multierror.Append(err, e)
}
}
}
return err
}
// SemanticChecker is the interface that semantic checks across the
// Terraform graph implement. Errors are accumulated. Even after an error
// is returned, child vertices in the graph will still be visited.
//
// The graph should NOT be modified by the semantic checker.
//
// The order in which vertices are visited is left unspecified, so the
// semantic checks should not rely on that.
type SemanticChecker interface {
Check(*dag.Graph, dag.Vertex) error
}
// SemanticCheckModulesExist is an implementation of SemanticChecker that
// verifies that all the modules that are referenced in the graph exist.
type SemanticCheckModulesExist struct{}
// TODO: test
func (*SemanticCheckModulesExist) Check(g *dag.Graph, v dag.Vertex) error {
mn, ok := v.(*GraphNodeConfigModule)
if !ok {
return nil
}
if mn.Tree == nil {
return fmt.Errorf(
"module '%s' not found", mn.Module.Name)
}
return nil
}
// smcUserVariables does all the semantic checks to verify that the // smcUserVariables does all the semantic checks to verify that the
// variables given satisfy the configuration itself. // variables given satisfy the configuration itself.
func smcUserVariables(c *config.Config, vs map[string]string) []error { func smcUserVariables(c *config.Config, vs map[string]string) []error {