terraform: change the graph a bit to better support providers with
modules This doesn't cause inheritence to work yet. That is coming
This commit is contained in:
parent
86a4a6c7c8
commit
8dbc7e0ccb
|
@ -163,7 +163,11 @@ func (g *Graph) String() string {
|
||||||
}
|
}
|
||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
|
|
||||||
buf.WriteString(fmt.Sprintf("root: %s\n", g.Root.Name))
|
if g.Root != nil {
|
||||||
|
buf.WriteString(fmt.Sprintf("root: %s\n", g.Root.Name))
|
||||||
|
} else {
|
||||||
|
buf.WriteString("root: <unknown>\n")
|
||||||
|
}
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
n := mapping[k]
|
n := mapping[k]
|
||||||
buf.WriteString(fmt.Sprintf("%s\n", n.Name))
|
buf.WriteString(fmt.Sprintf("%s\n", n.Name))
|
||||||
|
|
|
@ -353,14 +353,16 @@ func (c *Context) validateWalkFn(rws *[]string, res *[]error) depgraph.WalkFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
case *GraphNodeResourceProvider:
|
case *GraphNodeResourceProvider:
|
||||||
|
sharedProvider := rn.Provider
|
||||||
|
|
||||||
var raw *config.RawConfig
|
var raw *config.RawConfig
|
||||||
if rn.Config != nil {
|
if sharedProvider.Config != nil {
|
||||||
raw = rn.Config.RawConfig
|
raw = sharedProvider.Config.RawConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
rc := NewResourceConfig(raw)
|
rc := NewResourceConfig(raw)
|
||||||
|
|
||||||
for k, p := range rn.Providers {
|
for k, p := range sharedProvider.Providers {
|
||||||
log.Printf("[INFO] Validating provider: %s", k)
|
log.Printf("[INFO] Validating provider: %s", k)
|
||||||
ws, es := p.Validate(rc)
|
ws, es := p.Validate(rc)
|
||||||
for i, w := range ws {
|
for i, w := range ws {
|
||||||
|
@ -903,16 +905,18 @@ func (c *walkContext) genericWalkFn(cb genericWalkFunc) depgraph.WalkFunc {
|
||||||
// Skip it
|
// Skip it
|
||||||
return nil
|
return nil
|
||||||
case *GraphNodeResourceProvider:
|
case *GraphNodeResourceProvider:
|
||||||
|
sharedProvider := m.Provider
|
||||||
|
|
||||||
// Interpolate in the variables and configure all the providers
|
// Interpolate in the variables and configure all the providers
|
||||||
var raw *config.RawConfig
|
var raw *config.RawConfig
|
||||||
if m.Config != nil {
|
if sharedProvider.Config != nil {
|
||||||
raw = m.Config.RawConfig
|
raw = sharedProvider.Config.RawConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
rc := NewResourceConfig(raw)
|
rc := NewResourceConfig(raw)
|
||||||
rc.interpolate(c)
|
rc.interpolate(c)
|
||||||
|
|
||||||
for k, p := range m.Providers {
|
for k, p := range sharedProvider.Providers {
|
||||||
log.Printf("[INFO] Configuring provider: %s", k)
|
log.Printf("[INFO] Configuring provider: %s", k)
|
||||||
err := p.Configure(rc)
|
err := p.Configure(rc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -4,7 +4,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestContextGraph(t *testing.T) {
|
func TestContextGraph(t *testing.T) {
|
||||||
|
@ -1589,6 +1591,60 @@ 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")
|
||||||
|
ctx := testContext(t, &ContextOpts{
|
||||||
|
Module: m,
|
||||||
|
Providers: map[string]ResourceProviderFactory{
|
||||||
|
"aws": func() (ResourceProvider, error) {
|
||||||
|
l.Lock()
|
||||||
|
defer l.Unlock()
|
||||||
|
|
||||||
|
p := testProvider("aws")
|
||||||
|
p.ConfigureFn = func(c *ResourceConfig) error {
|
||||||
|
if v, ok := c.Get("from"); !ok || v.(string) != "root" {
|
||||||
|
return fmt.Errorf("bad")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
p.DiffFn = func(
|
||||||
|
info *InstanceInfo,
|
||||||
|
state *InstanceState,
|
||||||
|
c *ResourceConfig) (*InstanceDiff, error) {
|
||||||
|
v, _ := c.Get("from")
|
||||||
|
calls = append(calls, v.(string))
|
||||||
|
return testDiffFn(info, state, c)
|
||||||
|
}
|
||||||
|
ps = append(ps, p)
|
||||||
|
return p, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
_, err := ctx.Plan(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ps) != 2 {
|
||||||
|
t.Fatalf("bad: %#v", ps)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := calls
|
||||||
|
sort.Strings(actual)
|
||||||
|
expected := []string{"child", "root"}
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Fatalf("bad: %#v", actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestContextPlan_moduleVar(t *testing.T) {
|
func TestContextPlan_moduleVar(t *testing.T) {
|
||||||
m := testModule(t, "plan-module-var")
|
m := testModule(t, "plan-module-var")
|
||||||
p := testProvider("aws")
|
p := testProvider("aws")
|
||||||
|
|
|
@ -58,6 +58,10 @@ type GraphOpts struct {
|
||||||
// Provisioners is a mapping of names to a resource provisioner.
|
// Provisioners is a mapping of names to a resource provisioner.
|
||||||
// These must be provided to support resource provisioners.
|
// These must be provided to support resource provisioners.
|
||||||
Provisioners map[string]ResourceProvisionerFactory
|
Provisioners map[string]ResourceProvisionerFactory
|
||||||
|
|
||||||
|
// parent specifies the parent graph if there is one. This should not be
|
||||||
|
// set manually.
|
||||||
|
parent *depgraph.Graph
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphRootNode is the name of the root node in the Terraform resource
|
// GraphRootNode is the name of the root node in the Terraform resource
|
||||||
|
@ -77,10 +81,10 @@ type GraphNodeModule struct {
|
||||||
// this represents a _single_, _resource_ to be managed, not a set of resources
|
// this represents a _single_, _resource_ to be managed, not a set of resources
|
||||||
// or a component of a resource.
|
// or a component of a resource.
|
||||||
type GraphNodeResource struct {
|
type GraphNodeResource struct {
|
||||||
Index int
|
Index int
|
||||||
Config *config.Resource
|
Config *config.Resource
|
||||||
Resource *Resource
|
Resource *Resource
|
||||||
ResourceProviderID string
|
ResourceProviderNode string
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeResourceMeta is a node type in the graph that represents the
|
// GraphNodeResourceMeta is a node type in the graph that represents the
|
||||||
|
@ -96,10 +100,17 @@ type GraphNodeResourceMeta struct {
|
||||||
// GraphNodeResourceProvider is a node type in the graph that represents
|
// GraphNodeResourceProvider is a node type in the graph that represents
|
||||||
// the configuration for a resource provider.
|
// the configuration for a resource provider.
|
||||||
type GraphNodeResourceProvider struct {
|
type GraphNodeResourceProvider struct {
|
||||||
ID string
|
ID string
|
||||||
|
Provider *graphSharedProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
// graphSharedProvider is a structure that stores a configuration
|
||||||
|
// with initialized providers and might be shared across different
|
||||||
|
// graphs in order to have only one instance of a provider.
|
||||||
|
type graphSharedProvider struct {
|
||||||
|
Config *config.ProviderConfig
|
||||||
Providers map[string]ResourceProvider
|
Providers map[string]ResourceProvider
|
||||||
ProviderKeys []string
|
ProviderKeys []string
|
||||||
Config *config.ProviderConfig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Graph builds a dependency graph of all the resources for infrastructure
|
// Graph builds a dependency graph of all the resources for infrastructure
|
||||||
|
@ -160,51 +171,30 @@ func Graph(opts *GraphOpts) (*depgraph.Graph, error) {
|
||||||
// and not "orphans" (that are in the state, but not in the config).
|
// and not "orphans" (that are in the state, but not in the config).
|
||||||
graphAddConfigResources(g, conf, modState)
|
graphAddConfigResources(g, conf, modState)
|
||||||
|
|
||||||
// Add the modules that are in the configuration.
|
|
||||||
if err := graphAddConfigModules(g, conf, opts); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add explicit dependsOn dependencies to the graph
|
|
||||||
graphAddExplicitDeps(g)
|
|
||||||
|
|
||||||
if modState != nil {
|
if modState != nil {
|
||||||
// Next, add the state orphans if we have any
|
// Next, add the state orphans if we have any
|
||||||
graphAddOrphans(g, conf, modState)
|
graphAddOrphans(g, conf, modState)
|
||||||
|
|
||||||
// Add tainted resources if we have any.
|
// Add tainted resources if we have any.
|
||||||
graphAddTainted(g, modState)
|
graphAddTainted(g, modState)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.State != nil {
|
// Create the resource provider nodes for explicitly configured
|
||||||
// Add module orphans if we have any of those
|
// providers within our graph.
|
||||||
if ms := opts.State.Children(opts.ModulePath); len(ms) > 0 {
|
graphAddConfigProviderConfigs(g, conf)
|
||||||
if err := graphAddModuleOrphans(g, conf, ms, opts); err != nil {
|
|
||||||
return nil, err
|
if opts.parent != nil {
|
||||||
}
|
// Add/merge the provider configurations from the parent so that
|
||||||
}
|
// we properly "inherit" providers.
|
||||||
|
graphAddParentProviderConfigs(g, opts.parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map the provider configurations to all of the resources
|
// First pass matching resources to providers. This will allow us to
|
||||||
graphAddProviderConfigs(g, conf)
|
// determine what providers are missing.
|
||||||
|
graphMapResourceProviderId(g)
|
||||||
|
|
||||||
// Setup the provisioners. These may have variable dependencies,
|
|
||||||
// and must be done before dependency setup
|
|
||||||
if err := graphMapResourceProvisioners(g, opts.Provisioners); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add all the variable dependencies
|
|
||||||
graphAddVariableDeps(g)
|
|
||||||
|
|
||||||
// Build the root so that we have a single valid root
|
|
||||||
graphAddRoot(g)
|
|
||||||
|
|
||||||
// If providers were given, lets associate the proper providers and
|
|
||||||
// instantiate them.
|
|
||||||
if len(opts.Providers) > 0 {
|
if len(opts.Providers) > 0 {
|
||||||
// Add missing providers from the mapping
|
// Add missing providers from the mapping.
|
||||||
if err := graphAddMissingResourceProviders(g, opts.Providers); err != nil {
|
if err := graphAddMissingResourceProviders(g, opts.Providers); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -220,6 +210,38 @@ func Graph(opts *GraphOpts) (*depgraph.Graph, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the modules that are in the configuration.
|
||||||
|
if err := graphAddConfigModules(g, conf, opts); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.State != nil {
|
||||||
|
// Add module orphans if we have any of those
|
||||||
|
if ms := opts.State.Children(opts.ModulePath); len(ms) > 0 {
|
||||||
|
if err := graphAddModuleOrphans(g, conf, ms, opts); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the provider dependencies
|
||||||
|
graphAddResourceProviderDeps(g)
|
||||||
|
|
||||||
|
// Add explicit dependsOn dependencies to the graph
|
||||||
|
graphAddExplicitDeps(g)
|
||||||
|
|
||||||
|
// Setup the provisioners. These may have variable dependencies,
|
||||||
|
// and must be done before dependency setup
|
||||||
|
if err := graphMapResourceProvisioners(g, opts.Provisioners); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all the variable dependencies
|
||||||
|
graphAddVariableDeps(g)
|
||||||
|
|
||||||
|
// Build the root so that we have a single valid root
|
||||||
|
graphAddRoot(g)
|
||||||
|
|
||||||
// If we have a diff, then make sure to add that in
|
// If we have a diff, then make sure to add that in
|
||||||
if modDiff != nil {
|
if modDiff != nil {
|
||||||
if err := graphAddDiff(g, modDiff); err != nil {
|
if err := graphAddDiff(g, modDiff); err != nil {
|
||||||
|
@ -296,7 +318,7 @@ func graphAddConfigModules(
|
||||||
// Build the list of nouns to add to the graph
|
// Build the list of nouns to add to the graph
|
||||||
nounsList := make([]*depgraph.Noun, 0, len(c.Modules))
|
nounsList := make([]*depgraph.Noun, 0, len(c.Modules))
|
||||||
for _, m := range c.Modules {
|
for _, m := range c.Modules {
|
||||||
if n, err := graphModuleNoun(m.Name, m, opts); err != nil {
|
if n, err := graphModuleNoun(m.Name, m, g, opts); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
nounsList = append(nounsList, n)
|
nounsList = append(nounsList, n)
|
||||||
|
@ -590,7 +612,7 @@ func graphAddExplicitDeps(g *depgraph.Graph) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rs[rn.Resource.Id] = n
|
rs[rn.Resource.Id] = n
|
||||||
if len(rn.Config.DependsOn) > 0 {
|
if rn.Config != nil && len(rn.Config.DependsOn) > 0 {
|
||||||
depends = true
|
depends = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -632,7 +654,7 @@ func graphAddMissingResourceProviders(
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if rn.ResourceProviderID != "" {
|
if rn.ResourceProviderNode != "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,28 +669,20 @@ func graphAddMissingResourceProviders(
|
||||||
// The resource provider ID is simply the shortest matching
|
// The resource provider ID is simply the shortest matching
|
||||||
// prefix, since that'll give us the most resource providers
|
// prefix, since that'll give us the most resource providers
|
||||||
// to choose from.
|
// to choose from.
|
||||||
rn.ResourceProviderID = prefixes[len(prefixes)-1]
|
id := prefixes[len(prefixes)-1]
|
||||||
|
rn.ResourceProviderNode = fmt.Sprintf("provider.%s", id)
|
||||||
|
|
||||||
// If we don't have a matching noun for this yet, insert it.
|
// If we don't have a matching noun for this yet, insert it.
|
||||||
pn := g.Noun(fmt.Sprintf("provider.%s", rn.ResourceProviderID))
|
if g.Noun(rn.ResourceProviderNode) == nil {
|
||||||
if pn == nil {
|
pn := &depgraph.Noun{
|
||||||
pn = &depgraph.Noun{
|
Name: rn.ResourceProviderNode,
|
||||||
Name: fmt.Sprintf("provider.%s", rn.ResourceProviderID),
|
|
||||||
Meta: &GraphNodeResourceProvider{
|
Meta: &GraphNodeResourceProvider{
|
||||||
ID: rn.ResourceProviderID,
|
ID: id,
|
||||||
Config: nil,
|
Provider: new(graphSharedProvider),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
g.Nouns = append(g.Nouns, pn)
|
g.Nouns = append(g.Nouns, pn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the provider configuration noun as a dependency
|
|
||||||
dep := &depgraph.Dependency{
|
|
||||||
Name: pn.Name,
|
|
||||||
Source: n,
|
|
||||||
Target: pn,
|
|
||||||
}
|
|
||||||
n.Deps = append(n.Deps, dep)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
|
@ -699,7 +713,7 @@ func graphAddModuleOrphans(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := graphModuleNoun(k, nil, opts); err != nil {
|
if n, err := graphModuleNoun(k, nil, g, opts); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
nounsList = append(nounsList, n)
|
nounsList = append(nounsList, n)
|
||||||
|
@ -774,60 +788,28 @@ func graphAddOrphans(g *depgraph.Graph, c *config.Config, mod *ModuleState) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// graphAddProviderConfigs cycles through all the resource-like nodes
|
// graphAddParentProviderConfigs goes through and adds/merges provider
|
||||||
// and adds the provider configuration nouns into the tree.
|
// configurations from the parent.
|
||||||
func graphAddProviderConfigs(g *depgraph.Graph, c *config.Config) {
|
func graphAddParentProviderConfigs(g, parent *depgraph.Graph) {
|
||||||
nounsList := make([]*depgraph.Noun, 0, 2)
|
}
|
||||||
pcNouns := make(map[string]*depgraph.Noun)
|
|
||||||
for _, noun := range g.Nouns {
|
|
||||||
resourceNode, ok := noun.Meta.(*GraphNodeResource)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look up the provider config for this resource
|
// graphAddConfigProviderConfigs adds a GraphNodeResourceProvider for every
|
||||||
pcName := config.ProviderConfigName(
|
// `provider` configuration block. Note that a provider may exist that
|
||||||
resourceNode.Resource.Info.Type, c.ProviderConfigs)
|
// isn't used for any resources. These will be pruned later.
|
||||||
if pcName == "" {
|
func graphAddConfigProviderConfigs(g *depgraph.Graph, c *config.Config) {
|
||||||
continue
|
nounsList := make([]*depgraph.Noun, 0, len(c.ProviderConfigs))
|
||||||
}
|
for _, pc := range c.ProviderConfigs {
|
||||||
|
noun := &depgraph.Noun{
|
||||||
// We have one, so build the noun if it hasn't already been made
|
Name: fmt.Sprintf("provider.%s", pc.Name),
|
||||||
pcNoun, ok := pcNouns[pcName]
|
Meta: &GraphNodeResourceProvider{
|
||||||
if !ok {
|
ID: pc.Name,
|
||||||
var pc *config.ProviderConfig
|
Provider: &graphSharedProvider{
|
||||||
for _, v := range c.ProviderConfigs {
|
|
||||||
if v.Name == pcName {
|
|
||||||
pc = v
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if pc == nil {
|
|
||||||
panic("pc not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
pcNoun = &depgraph.Noun{
|
|
||||||
Name: fmt.Sprintf("provider.%s", pcName),
|
|
||||||
Meta: &GraphNodeResourceProvider{
|
|
||||||
ID: pcName,
|
|
||||||
Config: pc,
|
Config: pc,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
pcNouns[pcName] = pcNoun
|
|
||||||
nounsList = append(nounsList, pcNoun)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the resource provider ID for this noun so we can look it
|
nounsList = append(nounsList, noun)
|
||||||
// up later easily.
|
|
||||||
resourceNode.ResourceProviderID = pcName
|
|
||||||
|
|
||||||
// Add the provider configuration noun as a dependency
|
|
||||||
dep := &depgraph.Dependency{
|
|
||||||
Name: pcName,
|
|
||||||
Source: noun,
|
|
||||||
Target: pcNoun,
|
|
||||||
}
|
|
||||||
noun.Deps = append(noun.Deps, dep)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all the provider config nouns to the graph
|
// Add all the provider config nouns to the graph
|
||||||
|
@ -890,8 +872,10 @@ func graphAddVariableDeps(g *depgraph.Graph) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case *GraphNodeResourceProvider:
|
case *GraphNodeResourceProvider:
|
||||||
vars := m.Config.RawConfig.Variables
|
if m.Provider != nil && m.Provider.Config != nil {
|
||||||
nounAddVariableDeps(g, n, vars, false)
|
vars := m.Provider.Config.RawConfig.Variables
|
||||||
|
nounAddVariableDeps(g, n, vars, false)
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Other node types don't have dependencies or we don't support it
|
// Other node types don't have dependencies or we don't support it
|
||||||
|
@ -963,7 +947,8 @@ func graphAddTainted(g *depgraph.Graph, mod *ModuleState) {
|
||||||
|
|
||||||
// graphModuleNoun creates a noun for a module.
|
// graphModuleNoun creates a noun for a module.
|
||||||
func graphModuleNoun(
|
func graphModuleNoun(
|
||||||
n string, m *config.Module, opts *GraphOpts) (*depgraph.Noun, error) {
|
n string, m *config.Module,
|
||||||
|
g *depgraph.Graph, opts *GraphOpts) (*depgraph.Noun, error) {
|
||||||
name := fmt.Sprintf("module.%s", n)
|
name := fmt.Sprintf("module.%s", n)
|
||||||
path := make([]string, len(opts.ModulePath)+1)
|
path := make([]string, len(opts.ModulePath)+1)
|
||||||
copy(path, opts.ModulePath)
|
copy(path, opts.ModulePath)
|
||||||
|
@ -972,6 +957,7 @@ func graphModuleNoun(
|
||||||
// Build the opts we'll use to make the next graph
|
// Build the opts we'll use to make the next graph
|
||||||
subOpts := *opts
|
subOpts := *opts
|
||||||
subOpts.ModulePath = path
|
subOpts.ModulePath = path
|
||||||
|
subOpts.parent = g
|
||||||
subGraph, err := Graph(&subOpts)
|
subGraph, err := Graph(&subOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
|
@ -1063,10 +1049,12 @@ func graphInitResourceProviders(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sharedProvider := rn.Provider
|
||||||
|
|
||||||
// Go through each prefix and instantiate if necessary, then
|
// Go through each prefix and instantiate if necessary, then
|
||||||
// verify if this provider is of use to us or not.
|
// verify if this provider is of use to us or not.
|
||||||
rn.Providers = make(map[string]ResourceProvider)
|
sharedProvider.Providers = make(map[string]ResourceProvider)
|
||||||
rn.ProviderKeys = prefixes
|
sharedProvider.ProviderKeys = prefixes
|
||||||
for _, prefix := range prefixes {
|
for _, prefix := range prefixes {
|
||||||
p, err := ps[prefix]()
|
p, err := ps[prefix]()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1081,11 +1069,11 @@ func graphInitResourceProviders(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
rn.Providers[prefix] = p
|
sharedProvider.Providers[prefix] = p
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we never found a provider, then error and continue
|
// If we never found a provider, then error and continue
|
||||||
if len(rn.Providers) == 0 {
|
if len(sharedProvider.Providers) == 0 {
|
||||||
errs = append(errs, fmt.Errorf(
|
errs = append(errs, fmt.Errorf(
|
||||||
"Provider for configuration '%s' not found.",
|
"Provider for configuration '%s' not found.",
|
||||||
rn.ID))
|
rn.ID))
|
||||||
|
@ -1100,6 +1088,74 @@ func graphInitResourceProviders(
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// graphAddResourceProviderDeps goes through all the nodes in the graph
|
||||||
|
// and adds any dependencies to resource providers as needed.
|
||||||
|
func graphAddResourceProviderDeps(g *depgraph.Graph) {
|
||||||
|
for _, rawN := range g.Nouns {
|
||||||
|
switch n := rawN.Meta.(type) {
|
||||||
|
case *GraphNodeResource:
|
||||||
|
// Not sure how this would happen, but we might as well
|
||||||
|
// check for it.
|
||||||
|
if n.ResourceProviderNode == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the noun this depends on.
|
||||||
|
target := g.Noun(n.ResourceProviderNode)
|
||||||
|
|
||||||
|
// Create the dependency to the provider
|
||||||
|
dep := &depgraph.Dependency{
|
||||||
|
Name: target.Name,
|
||||||
|
Source: rawN,
|
||||||
|
Target: target,
|
||||||
|
}
|
||||||
|
rawN.Deps = append(rawN.Deps, dep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// graphMapResourceProviderId goes through the graph and maps the
|
||||||
|
// ID of a resource provider node to each resource. This lets us know which
|
||||||
|
// configuration is for which resource.
|
||||||
|
//
|
||||||
|
// This is safe to call multiple times.
|
||||||
|
func graphMapResourceProviderId(g *depgraph.Graph) {
|
||||||
|
// Build the list of provider configs we have
|
||||||
|
ps := make(map[string]string)
|
||||||
|
for _, n := range g.Nouns {
|
||||||
|
pn, ok := n.Meta.(*GraphNodeResourceProvider)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ps[n.Name] = pn.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go through every resource and find the shortest matching provider
|
||||||
|
for _, n := range g.Nouns {
|
||||||
|
rn, ok := n.Meta.(*GraphNodeResource)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var match, matchNode string
|
||||||
|
for n, p := range ps {
|
||||||
|
if !strings.HasPrefix(rn.Resource.Info.Type, p) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(p) > len(match) {
|
||||||
|
match = p
|
||||||
|
matchNode = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matchNode == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rn.ResourceProviderNode = matchNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// graphMapResourceProviders takes a graph that already has initialized
|
// graphMapResourceProviders takes a graph that already has initialized
|
||||||
// the resource providers (using graphInitResourceProviders) and maps the
|
// the resource providers (using graphInitResourceProviders) and maps the
|
||||||
// resource providers to the resources themselves.
|
// resource providers to the resources themselves.
|
||||||
|
@ -1124,24 +1180,25 @@ func graphMapResourceProviders(g *depgraph.Graph) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
rpn, ok := mapping[rn.ResourceProviderID]
|
rpnRaw := g.Noun(rn.ResourceProviderNode)
|
||||||
if !ok {
|
if rpnRaw == nil {
|
||||||
// This should never happen since when building the graph
|
// This should never happen since when building the graph
|
||||||
// we ensure that everything matches up.
|
// we ensure that everything matches up.
|
||||||
panic(fmt.Sprintf(
|
panic(fmt.Sprintf(
|
||||||
"Resource provider ID not found: %s (type: %s)",
|
"Resource provider not found: %s (type: %s)",
|
||||||
rn.ResourceProviderID,
|
rn.ResourceProviderNode,
|
||||||
rn.Resource.Info.Type))
|
rn.Resource.Info.Type))
|
||||||
}
|
}
|
||||||
|
rpn := rpnRaw.Meta.(*GraphNodeResourceProvider)
|
||||||
|
|
||||||
var provider ResourceProvider
|
var provider ResourceProvider
|
||||||
for _, k := range rpn.ProviderKeys {
|
for _, k := range rpn.Provider.ProviderKeys {
|
||||||
// Only try this provider if it has the right prefix
|
// Only try this provider if it has the right prefix
|
||||||
if !strings.HasPrefix(rn.Resource.Info.Type, k) {
|
if !strings.HasPrefix(rn.Resource.Info.Type, k) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
rp := rpn.Providers[k]
|
rp := rpn.Provider.Providers[k]
|
||||||
if ProviderSatisfies(rp, rn.Resource.Info.Type) {
|
if ProviderSatisfies(rp, rn.Resource.Info.Type) {
|
||||||
provider = rp
|
provider = rp
|
||||||
break
|
break
|
||||||
|
|
|
@ -310,7 +310,7 @@ func TestGraphFull(t *testing.T) {
|
||||||
t.Fatalf("bad: %#v", m)
|
t.Fatalf("bad: %#v", m)
|
||||||
}
|
}
|
||||||
case *GraphNodeResourceProvider:
|
case *GraphNodeResourceProvider:
|
||||||
if len(m.Providers) == 0 {
|
if len(m.Provider.Providers) == 0 {
|
||||||
t.Fatalf("bad: %#v", m)
|
t.Fatalf("bad: %#v", m)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -21,6 +21,7 @@ type MockResourceProvider struct {
|
||||||
ApplyReturnError error
|
ApplyReturnError error
|
||||||
ConfigureCalled bool
|
ConfigureCalled bool
|
||||||
ConfigureConfig *ResourceConfig
|
ConfigureConfig *ResourceConfig
|
||||||
|
ConfigureFn func(*ResourceConfig) error
|
||||||
ConfigureReturnError error
|
ConfigureReturnError error
|
||||||
DiffCalled bool
|
DiffCalled bool
|
||||||
DiffInfo *InstanceInfo
|
DiffInfo *InstanceInfo
|
||||||
|
@ -79,6 +80,11 @@ func (p *MockResourceProvider) Configure(c *ResourceConfig) error {
|
||||||
|
|
||||||
p.ConfigureCalled = true
|
p.ConfigureCalled = true
|
||||||
p.ConfigureConfig = c
|
p.ConfigureConfig = c
|
||||||
|
|
||||||
|
if p.ConfigureFn != nil {
|
||||||
|
return p.ConfigureFn(c)
|
||||||
|
}
|
||||||
|
|
||||||
return p.ConfigureReturnError
|
return p.ConfigureReturnError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
resource "aws_instance" "foo" {
|
||||||
|
from = "child"
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
module "child" {
|
||||||
|
source = "./child"
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "aws" {
|
||||||
|
from = "root"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "foo" {
|
||||||
|
from = "root"
|
||||||
|
}
|
Loading…
Reference in New Issue