2015-01-21 16:20:51 -06:00
|
|
|
package terraform
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
|
2015-01-23 17:21:27 -06:00
|
|
|
"github.com/hashicorp/go-multierror"
|
2015-01-21 16:20:51 -06:00
|
|
|
"github.com/hashicorp/terraform/config"
|
|
|
|
"github.com/hashicorp/terraform/config/module"
|
2015-01-22 19:12:32 -06:00
|
|
|
"github.com/hashicorp/terraform/dag"
|
2015-01-21 16:20:51 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
// Graph takes a module tree and builds a logical graph of all the nodes
|
|
|
|
// in that module.
|
2015-01-22 19:12:32 -06:00
|
|
|
func Graph2(mod *module.Tree) (*dag.Graph, error) {
|
2015-01-21 16:20:51 -06:00
|
|
|
// A module is required and also must be completely loaded.
|
|
|
|
if mod == nil {
|
|
|
|
return nil, errors.New("module must not be nil")
|
|
|
|
}
|
|
|
|
if !mod.Loaded() {
|
|
|
|
return nil, errors.New("module must be loaded")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the configuration for this module
|
|
|
|
config := mod.Config()
|
|
|
|
|
|
|
|
// Create the node list we'll use for the graph
|
|
|
|
nodes := make([]graphNodeConfig, 0,
|
2015-01-21 16:53:50 -06:00
|
|
|
(len(config.ProviderConfigs)+len(config.Modules)+len(config.Resources))*2)
|
2015-01-21 16:20:51 -06:00
|
|
|
|
2015-01-21 16:39:16 -06:00
|
|
|
// Write all the provider configs out
|
|
|
|
for _, pc := range config.ProviderConfigs {
|
|
|
|
nodes = append(nodes, &GraphNodeConfigProvider{Provider: pc})
|
|
|
|
}
|
|
|
|
|
2015-01-21 16:20:51 -06:00
|
|
|
// Write all the resources out
|
|
|
|
for _, r := range config.Resources {
|
2015-01-21 16:39:16 -06:00
|
|
|
nodes = append(nodes, &GraphNodeConfigResource{Resource: r})
|
2015-01-21 16:20:51 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Write all the modules out
|
2015-01-23 19:52:51 -06:00
|
|
|
children := mod.Children()
|
2015-01-21 16:53:50 -06:00
|
|
|
for _, m := range config.Modules {
|
2015-01-23 19:52:51 -06:00
|
|
|
nodes = append(nodes, &GraphNodeConfigModule{
|
|
|
|
Module: m,
|
|
|
|
Tree: children[m.Name],
|
|
|
|
})
|
2015-01-21 16:53:50 -06:00
|
|
|
}
|
2015-01-21 16:20:51 -06:00
|
|
|
|
|
|
|
// Build the full map of the var names to the nodes.
|
2015-01-23 17:11:11 -06:00
|
|
|
fullMap := make(map[string]dag.Vertex)
|
2015-01-21 16:20:51 -06:00
|
|
|
for _, n := range nodes {
|
|
|
|
fullMap[n.VarName()] = n
|
|
|
|
}
|
|
|
|
|
2015-01-23 17:11:11 -06:00
|
|
|
// Build the graph vertices
|
|
|
|
var g dag.Graph
|
|
|
|
for _, n := range nodes {
|
|
|
|
g.Add(n)
|
|
|
|
}
|
|
|
|
|
2015-01-23 17:21:27 -06:00
|
|
|
// Err is where the final error value will go if there is one
|
|
|
|
var err error
|
|
|
|
|
2015-01-23 17:11:11 -06:00
|
|
|
// Go through all the nodes and build up the actual graph edges. We
|
2015-01-21 16:20:51 -06:00
|
|
|
// do this by getting the variables that each node depends on and then
|
|
|
|
// building the dep map based on the fullMap which contains the mapping
|
|
|
|
// of var names to the actual node with that name.
|
|
|
|
for _, n := range nodes {
|
2015-01-21 17:37:17 -06:00
|
|
|
for _, id := range n.Variables() {
|
2015-01-23 17:21:27 -06:00
|
|
|
if id == "" {
|
|
|
|
// Empty name means its a variable we don't care about
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
target, ok := fullMap[id]
|
|
|
|
if !ok {
|
|
|
|
// We can't find the target meaning the dependency
|
|
|
|
// is missing. Accumulate the error.
|
|
|
|
err = multierror.Append(err, fmt.Errorf(
|
|
|
|
"%s: missing dependency: %s", n, id))
|
|
|
|
continue
|
2015-01-23 17:11:11 -06:00
|
|
|
}
|
2015-01-23 17:21:27 -06:00
|
|
|
|
|
|
|
g.Connect(dag.BasicEdge(n, target))
|
2015-01-21 16:20:51 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-23 17:21:27 -06:00
|
|
|
return &g, err
|
2015-01-21 16:20:51 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// varNameForVar returns the VarName value for an interpolated variable.
|
|
|
|
// This value is compared to the VarName() value for the nodes within the
|
|
|
|
// graph to build the graph edges.
|
|
|
|
func varNameForVar(raw config.InterpolatedVariable) string {
|
|
|
|
switch v := raw.(type) {
|
|
|
|
case *config.ModuleVariable:
|
|
|
|
return fmt.Sprintf("module.%s", v.Name)
|
|
|
|
case *config.ResourceVariable:
|
|
|
|
return v.ResourceId()
|
|
|
|
default:
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|