terraform: modules are put into the graph

This commit is contained in:
Mitchell Hashimoto 2014-09-22 15:11:57 -07:00
parent 6f7c3caab3
commit 1d106d3fa4
3 changed files with 104 additions and 12 deletions

View File

@ -50,6 +50,12 @@ type GraphOpts struct {
// graph. This node is just a placemarker and has no associated functionality.
const GraphRootNode = "root"
// GraphNodeModule is a node type in the graph that represents a module
// that will be created/managed.
type GraphNodeModule struct {
Config *config.Module
}
// GraphNodeResource is a node type in the graph that represents a resource
// that will be created or managed. Unlike the GraphNodeResourceMeta node,
// this represents a _single_, _resource_ to be managed, not a set of resources
@ -108,6 +114,9 @@ func Graph(opts *GraphOpts) (*depgraph.Graph, error) {
// and not "orphans" (that are in the state, but not in the config).
graphAddConfigResources(g, opts.Config, opts.State)
// Add the modules that are in the configuration.
graphAddConfigModules(g, opts)
// Add explicit dependsOn dependencies to the graph
graphAddExplicitDeps(g)
@ -220,6 +229,33 @@ func graphInitState(s *State, g *depgraph.Graph) {
}
}
// graphAddConfigModules adds the modules from a configuration structure
// into the graph, expanding each to their own sub-graph.
func graphAddConfigModules(g *depgraph.Graph, opts *GraphOpts) {
c := opts.Config
// Just short-circuit the whole thing if we don't have modules
if len(c.Modules) == 0 {
return
}
// Build the list of nouns to add to the graph
nounsList := make([]*depgraph.Noun, 0, len(c.Modules))
for _, m := range c.Modules {
name := fmt.Sprintf("module.%s", m.Name)
n := &depgraph.Noun{
Name: name,
Meta: &GraphNodeModule{
Config: m,
},
}
nounsList = append(nounsList, n)
}
g.Nouns = append(g.Nouns, nounsList...)
}
// configGraph turns a configuration structure into a dependency graph.
func graphAddConfigResources(
g *depgraph.Graph, c *config.Config, s *State) {
@ -752,18 +788,21 @@ func graphAddRoot(g *depgraph.Graph) {
// based on variable values.
func graphAddVariableDeps(g *depgraph.Graph) {
for _, n := range g.Nouns {
var vars map[string]config.InterpolatedVariable
switch m := n.Meta.(type) {
case *GraphNodeModule:
vars := m.Config.RawConfig.Variables
nounAddVariableDeps(g, n, vars, false)
case *GraphNodeResource:
if m.Config != nil {
// Handle the resource variables
vars = m.Config.RawConfig.Variables
vars := m.Config.RawConfig.Variables
nounAddVariableDeps(g, n, vars, false)
}
// Handle the variables of the resource provisioners
for _, p := range m.Resource.Provisioners {
vars = p.RawConfig.Variables
vars := p.RawConfig.Variables
nounAddVariableDeps(g, n, vars, true)
vars = p.ConnInfo.Variables
@ -771,10 +810,11 @@ func graphAddVariableDeps(g *depgraph.Graph) {
}
case *GraphNodeResourceProvider:
vars = m.Config.RawConfig.Variables
vars := m.Config.RawConfig.Variables
nounAddVariableDeps(g, n, vars, false)
default:
// Other node types don't have dependencies or we don't support it
continue
}
}
@ -854,15 +894,20 @@ func nounAddVariableDeps(
n *depgraph.Noun,
vars map[string]config.InterpolatedVariable,
removeSelf bool) {
for _, v := range vars {
// Only resource variables impose dependencies
rv, ok := v.(*config.ResourceVariable)
if !ok {
continue
for _, rawV := range vars {
var name string
var target *depgraph.Noun
switch v := rawV.(type) {
case *config.ModuleVariable:
name = fmt.Sprintf("module.%s", v.Name)
target = g.Noun(name)
case *config.ResourceVariable:
name = v.ResourceId()
target = g.Noun(v.ResourceId())
default:
}
// Find the target
target := g.Noun(rv.ResourceId())
if target == nil {
continue
}
@ -875,7 +920,7 @@ func nounAddVariableDeps(
// Build the dependency
dep := &depgraph.Dependency{
Name: rv.ResourceId(),
Name: name,
Source: n,
Target: target,
}

View File

@ -81,6 +81,21 @@ func TestGraph_dependsOnCount(t *testing.T) {
}
}
func TestGraph_modules(t *testing.T) {
config := testConfig(t, "graph-modules")
g, err := Graph(&GraphOpts{Config: config})
if err != nil {
t.Fatalf("err: %s", err)
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTerraformGraphModulesStr)
if actual != expected {
t.Fatalf("bad:\n\n%s", actual)
}
}
func TestGraph_state(t *testing.T) {
config := testConfig(t, "graph-basic")
state := &State{
@ -767,6 +782,23 @@ root
root -> aws_load_balancer.weblb
`
const testTerraformGraphModulesStr = `
root: root
aws_instance.web
aws_instance.web -> aws_security_group.firewall
aws_instance.web -> module.consul
aws_instance.web -> provider.aws
aws_security_group.firewall
aws_security_group.firewall -> provider.aws
module.consul
module.consul -> aws_security_group.firewall
provider.aws
root
root -> aws_instance.web
root -> aws_security_group.firewall
root -> module.consul
`
const testTerraformGraphStateStr = `
root: root
aws_instance.old

View File

@ -0,0 +1,15 @@
module "consul" {
foo = "${aws_security_group.firewall.foo}"
}
provider "aws" {}
resource "aws_security_group" "firewall" {}
resource "aws_instance" "web" {
security_groups = [
"foo",
"${aws_security_group.firewall.foo}",
"${module.consul.security_group}"
]
}