diff --git a/config/config.go b/config/config.go index c6df4f619..0336e11f0 100644 --- a/config/config.go +++ b/config/config.go @@ -118,6 +118,7 @@ func (r *Resource) ReplaceVariables(vs map[string]string) *Resource { // ResourceGraph returns a dependency graph of the resources from this // Terraform configuration. func (c *Config) ResourceGraph() *depgraph.Graph { + // This tracks all the resource nouns nouns := make(map[string]*depgraph.Noun) for _, r := range c.Resources { noun := &depgraph.Noun{ @@ -127,9 +128,30 @@ func (c *Config) ResourceGraph() *depgraph.Graph { nouns[noun.Name] = noun } - for _, noun := range nouns { - r := noun.Meta.(*Resource) - for _, v := range r.Variables { + // Build the list of nouns that we iterate over + nounsList := make([]*depgraph.Noun, 0, len(nouns)) + for _, n := range nouns { + nounsList = append(nounsList, n) + } + + // This tracks the provider configs that are nouns in our dep graph + pcNouns := make(map[string]*depgraph.Noun) + + i := 0 + for i < len(nounsList) { + noun := nounsList[i] + i += 1 + + // Determine depenencies based on variables. Both resources + // and provider configurations have dependencies in this case. + var vars map[string]InterpolatedVariable + switch n := noun.Meta.(type) { + case *Resource: + vars = n.Variables + case *ProviderConfig: + vars = n.Variables + } + for _, v := range vars { // Only resource variables impose dependencies rv, ok := v.(*ResourceVariable) if !ok { @@ -145,12 +167,32 @@ func (c *Config) ResourceGraph() *depgraph.Graph { noun.Deps = append(noun.Deps, dep) } - } - // Create the list of nouns that the depgraph.Graph struct expects - nounsList := make([]*depgraph.Noun, 0, len(nouns)) - for _, n := range nouns { - nounsList = append(nounsList, n) + // If this is a Resource, then check if we have to also + // depend on a provider configuration. + if r, ok := noun.Meta.(*Resource); ok { + // If there is a provider config that matches this resource + // then we add that as a dependency. + if pcName := r.ProviderConfigName(c.ProviderConfigs); pcName != "" { + pcNoun, ok := pcNouns[pcName] + if !ok { + pcNoun = &depgraph.Noun{ + Name: fmt.Sprintf("provider.%s", pcName), + Meta: c.ProviderConfigs[pcName], + } + pcNouns[pcName] = pcNoun + nounsList = append(nounsList, pcNoun) + } + + dep := &depgraph.Dependency{ + Name: pcName, + Source: noun, + Target: pcNoun, + } + + noun.Deps = append(noun.Deps, dep) + } + } } // Create a root that just depends on everything else finishing. @@ -170,6 +212,14 @@ func (c *Config) ResourceGraph() *depgraph.Graph { } } +// Validate does some basic semantic checking of the configuration. +func (c *Config) Validate() error { + // TODO(mitchellh): make sure all referenced variables exist + // TODO(mitchellh): make sure types/names have valid values (characters) + + return nil +} + // Required tests whether a variable is required or not. func (v *Variable) Required() bool { return !v.defaultSet diff --git a/config/config_test.go b/config/config_test.go index 572a23a80..27162838f 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -128,16 +128,21 @@ func TestResourceReplaceVariables(t *testing.T) { const resourceGraphValue = ` root: root - root -> aws_security_group.firewall - root -> aws_instance.web - root -> aws_load_balancer.weblb +openstack_floating_ip.random aws_security_group.firewall + aws_security_group.firewall -> provider.aws aws_instance.web aws_instance.web -> aws_security_group.firewall + aws_instance.web -> provider.aws aws_load_balancer.weblb aws_load_balancer.weblb -> aws_instance.web + aws_load_balancer.weblb -> provider.aws +provider.aws + provider.aws -> openstack_floating_ip.random root + root -> openstack_floating_ip.random root -> aws_security_group.firewall root -> aws_instance.web root -> aws_load_balancer.weblb + root -> provider.aws ` diff --git a/config/test-fixtures/resource_graph.tf b/config/test-fixtures/resource_graph.tf index 1ff19d02c..cd84eb51a 100644 --- a/config/test-fixtures/resource_graph.tf +++ b/config/test-fixtures/resource_graph.tf @@ -3,6 +3,12 @@ variable "foo" { description = "bar"; } +provider "aws" { + foo = "${openstack_floating_ip.random.value}" +} + +resource "openstack_floating_ip" "random" {} + resource "aws_security_group" "firewall" {} resource "aws_instance" "web" { diff --git a/depgraph/graph.go b/depgraph/graph.go index 9f719bebd..be521cacc 100644 --- a/depgraph/graph.go +++ b/depgraph/graph.go @@ -114,13 +114,6 @@ func (g *Graph) String() string { var buf bytes.Buffer buf.WriteString(fmt.Sprintf("root: %s\n", g.Root.Name)) - for _, dep := range g.Root.Deps { - buf.WriteString(fmt.Sprintf( - " %s -> %s\n", - dep.Source, - dep.Target)) - } - for _, n := range g.Nouns { buf.WriteString(fmt.Sprintf("%s\n", n.Name)) for _, dep := range n.Deps {