From 1f1563c95bc8e6003320d7cd9cf7fff4bf91ba21 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 24 Sep 2014 13:58:07 -0700 Subject: [PATCH] terraform: provider inheritence is functional --- terraform/context.go | 31 +++++++++++++++++++ terraform/context_test.go | 8 ----- terraform/graph.go | 63 +++++++++++++++++++++++++++++++++++++++ terraform/graph_test.go | 6 ++++ 4 files changed, 100 insertions(+), 8 deletions(-) diff --git a/terraform/context.go b/terraform/context.go index 39253338f6..92198559b7 100644 --- a/terraform/context.go +++ b/terraform/context.go @@ -913,6 +913,37 @@ func (c *walkContext) genericWalkFn(cb genericWalkFunc) depgraph.WalkFunc { raw = sharedProvider.Config.RawConfig } + // If we have a parent, then merge in the parent configurations + // properly so we "inherit" the configurations. + if sharedProvider.Parent != nil { + var rawMap map[string]interface{} + if raw != nil { + rawMap = raw.Raw + } + + parent := sharedProvider.Parent + for parent != nil { + if parent.Config != nil { + if rawMap == nil { + rawMap = parent.Config.RawConfig.Raw + } + + for k, v := range parent.Config.RawConfig.Config() { + rawMap[k] = v + } + } + + parent = parent.Parent + } + + // Update our configuration to be the merged result + var err error + raw, err = config.NewRawConfig(rawMap) + if err != nil { + return fmt.Errorf("Error merging configurations: %s", err) + } + } + rc := NewResourceConfig(raw) rc.interpolate(c) diff --git a/terraform/context_test.go b/terraform/context_test.go index d2c1ba1ee8..c769c3a75b 100644 --- a/terraform/context_test.go +++ b/terraform/context_test.go @@ -1592,10 +1592,7 @@ func TestContextPlan_moduleOrphans(t *testing.T) { } func TestContextPlan_moduleProviderInherit(t *testing.T) { - t.Skip() - var l sync.Mutex - var ps []*MockResourceProvider var calls []string m := testModule(t, "plan-module-provider-inherit") @@ -1622,7 +1619,6 @@ func TestContextPlan_moduleProviderInherit(t *testing.T) { calls = append(calls, v.(string)) return testDiffFn(info, state, c) } - ps = append(ps, p) return p, nil }, }, @@ -1633,10 +1629,6 @@ func TestContextPlan_moduleProviderInherit(t *testing.T) { t.Fatalf("err: %s", err) } - if len(ps) != 2 { - t.Fatalf("bad: %#v", ps) - } - actual := calls sort.Strings(actual) expected := []string{"child", "root"} diff --git a/terraform/graph.go b/terraform/graph.go index 52e6061090..d257c1c8e4 100644 --- a/terraform/graph.go +++ b/terraform/graph.go @@ -68,6 +68,9 @@ type GraphOpts struct { // graph. This node is just a placemarker and has no associated functionality. const GraphRootNode = "root" +// GraphMeta is the metadata attached to the graph itself. +type GraphMeta struct{} + // GraphNodeModule is a node type in the graph that represents a module // that will be created/managed. type GraphNodeModule struct { @@ -111,6 +114,9 @@ type graphSharedProvider struct { Config *config.ProviderConfig Providers map[string]ResourceProvider ProviderKeys []string + Parent *graphSharedProvider + + parentNoun *depgraph.Noun } // Graph builds a dependency graph of all the resources for infrastructure @@ -165,6 +171,7 @@ func Graph(opts *GraphOpts) (*depgraph.Graph, error) { log.Printf("[DEBUG] Creating graph for path: %v", opts.ModulePath) g := new(depgraph.Graph) + g.Meta = new(GraphMeta) // First, build the initial resource graph. This only has the resources // and no dependencies. This only adds resources that are in the config @@ -791,6 +798,42 @@ func graphAddOrphans(g *depgraph.Graph, c *config.Config, mod *ModuleState) { // graphAddParentProviderConfigs goes through and adds/merges provider // configurations from the parent. func graphAddParentProviderConfigs(g, parent *depgraph.Graph) { + var nounsList []*depgraph.Noun + for _, n := range parent.Nouns { + pn, ok := n.Meta.(*GraphNodeResourceProvider) + if !ok { + continue + } + + // If we have a provider configuration with the exact same + // name, then set specify the parent pointer to their shared + // config. + ourProviderRaw := g.Noun(n.Name) + + // If we don't have a matching configuration, then create one. + if ourProviderRaw == nil { + noun := &depgraph.Noun{ + Name: n.Name, + Meta: &GraphNodeResourceProvider{ + ID: pn.ID, + Provider: &graphSharedProvider{ + Parent: pn.Provider, + parentNoun: n, + }, + }, + } + + nounsList = append(nounsList, noun) + continue + } + + // If we have a matching configuration, then set the parent pointer + ourProvider := ourProviderRaw.Meta.(*GraphNodeResourceProvider) + ourProvider.Provider.Parent = pn.Provider + ourProvider.Provider.parentNoun = n + } + + g.Nouns = append(g.Nouns, nounsList...) } // graphAddConfigProviderConfigs adds a GraphNodeResourceProvider for every @@ -1093,6 +1136,26 @@ func graphInitResourceProviders( func graphAddResourceProviderDeps(g *depgraph.Graph) { for _, rawN := range g.Nouns { switch n := rawN.Meta.(type) { + case *GraphNodeModule: + // Check if the module depends on any of our providers + // by seeing if there is a parent node back. + for _, moduleRaw := range n.Graph.Nouns { + pn, ok := moduleRaw.Meta.(*GraphNodeResourceProvider) + if !ok { + continue + } + if pn.Provider.parentNoun == nil { + continue + } + + // Create the dependency to the provider + dep := &depgraph.Dependency{ + Name: pn.Provider.parentNoun.Name, + Source: rawN, + Target: pn.Provider.parentNoun, + } + rawN.Deps = append(rawN.Deps, dep) + } case *GraphNodeResource: // Not sure how this would happen, but we might as well // check for it. diff --git a/terraform/graph_test.go b/terraform/graph_test.go index b189f474e9..f965fdc8f7 100644 --- a/terraform/graph_test.go +++ b/terraform/graph_test.go @@ -868,6 +868,7 @@ aws_security_group.firewall aws_security_group.firewall -> provider.aws module.consul module.consul -> aws_security_group.firewall + module.consul -> provider.aws provider.aws root root -> aws_instance.web @@ -878,6 +879,8 @@ root const testTerraformGraphModulesConsulStr = ` root: root aws_instance.server + aws_instance.server -> provider.aws +provider.aws root root -> aws_instance.server ` @@ -890,6 +893,7 @@ aws_instance.web aws_security_group.firewall aws_security_group.firewall -> provider.aws module.consul + module.consul -> provider.aws provider.aws root root -> aws_instance.web @@ -900,6 +904,8 @@ root const testTerraformGraphModuleOrphanConsulStr = ` root: root aws_instance.old + aws_instance.old -> provider.aws +provider.aws root root -> aws_instance.old `