terraform: provider inheritence is functional

This commit is contained in:
Mitchell Hashimoto 2014-09-24 13:58:07 -07:00
parent 8dbc7e0ccb
commit 1f1563c95b
4 changed files with 100 additions and 8 deletions

View File

@ -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)

View File

@ -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"}

View File

@ -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.

View File

@ -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
`