mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-26 00:41:27 -06:00
terraform: provider config inheritance in modules
This commit is contained in:
parent
0a68576746
commit
d847b2b672
@ -160,6 +160,29 @@ func TestContext2Validate_moduleBadResource(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Validate_moduleProviderInherit(t *testing.T) {
|
||||
m := testModule(t, "validate-module-pc-inherit")
|
||||
p := testProvider("aws")
|
||||
c := testContext2(t, &ContextOpts{
|
||||
Module: m,
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
p.ValidateFn = func(c *ResourceConfig) ([]string, []error) {
|
||||
return nil, c.CheckSet([]string{"set"})
|
||||
}
|
||||
|
||||
w, e := c.Validate()
|
||||
if len(w) > 0 {
|
||||
t.Fatalf("bad: %#v", w)
|
||||
}
|
||||
if len(e) > 0 {
|
||||
t.Fatalf("bad: %s", e)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Validate_orphans(t *testing.T) {
|
||||
p := testProvider("aws")
|
||||
m := testModule(t, "validate-good")
|
||||
@ -543,29 +566,6 @@ func TestContext2Validate_varRefFilled(t *testing.T) {
|
||||
}
|
||||
|
||||
/*
|
||||
func TestContextValidate_moduleProviderInherit(t *testing.T) {
|
||||
m := testModule(t, "validate-module-pc-inherit")
|
||||
p := testProvider("aws")
|
||||
c := testContext(t, &ContextOpts{
|
||||
Module: m,
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
p.ValidateFn = func(c *ResourceConfig) ([]string, []error) {
|
||||
return nil, c.CheckSet([]string{"set"})
|
||||
}
|
||||
|
||||
w, e := c.Validate()
|
||||
if len(w) > 0 {
|
||||
t.Fatalf("bad: %#v", w)
|
||||
}
|
||||
if len(e) > 0 {
|
||||
t.Fatalf("bad: %#v", e)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextInput(t *testing.T) {
|
||||
input := new(MockUIInput)
|
||||
m := testModule(t, "input-vars")
|
||||
|
@ -19,6 +19,13 @@ type EvalContext interface {
|
||||
// initialized) or returns nil if the provider isn't initialized.
|
||||
Provider(string) ResourceProvider
|
||||
|
||||
// ConfigureProvider configures the provider with the given
|
||||
// configuration. This is a separate context call because this call
|
||||
// is used to store the provider configuration for inheritance lookups
|
||||
// with ParentProviderConfig().
|
||||
ConfigureProvider(string, *ResourceConfig) error
|
||||
ParentProviderConfig(string) *ResourceConfig
|
||||
|
||||
// InitProvisioner initializes the provisioner with the given name and
|
||||
// returns the implementation of the resource provisioner or an error.
|
||||
//
|
||||
@ -49,6 +56,15 @@ type MockEvalContext struct {
|
||||
ProviderName string
|
||||
ProviderProvider ResourceProvider
|
||||
|
||||
ConfigureProviderCalled bool
|
||||
ConfigureProviderName string
|
||||
ConfigureProviderConfig *ResourceConfig
|
||||
ConfigureProviderError error
|
||||
|
||||
ParentProviderConfigCalled bool
|
||||
ParentProviderConfigName string
|
||||
ParentProviderConfigConfig *ResourceConfig
|
||||
|
||||
InitProvisionerCalled bool
|
||||
InitProvisionerName string
|
||||
InitProvisionerProvisioner ResourceProvisioner
|
||||
@ -80,6 +96,19 @@ func (c *MockEvalContext) Provider(n string) ResourceProvider {
|
||||
return c.ProviderProvider
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) ConfigureProvider(n string, cfg *ResourceConfig) error {
|
||||
c.ConfigureProviderCalled = true
|
||||
c.ConfigureProviderName = n
|
||||
c.ConfigureProviderConfig = cfg
|
||||
return c.ConfigureProviderError
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) ParentProviderConfig(n string) *ResourceConfig {
|
||||
c.ParentProviderConfigCalled = true
|
||||
c.ParentProviderConfigName = n
|
||||
return c.ParentProviderConfigConfig
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) InitProvisioner(n string) (ResourceProvisioner, error) {
|
||||
c.InitProvisionerCalled = true
|
||||
c.InitProvisionerName = n
|
||||
|
@ -12,14 +12,15 @@ import (
|
||||
// BuiltinEvalContext is an EvalContext implementation that is used by
|
||||
// Terraform by default.
|
||||
type BuiltinEvalContext struct {
|
||||
PathValue []string
|
||||
Interpolater *Interpolater
|
||||
Providers map[string]ResourceProviderFactory
|
||||
ProviderCache map[string]ResourceProvider
|
||||
ProviderLock *sync.Mutex
|
||||
Provisioners map[string]ResourceProvisionerFactory
|
||||
ProvisionerCache map[string]ResourceProvisioner
|
||||
ProvisionerLock *sync.Mutex
|
||||
PathValue []string
|
||||
Interpolater *Interpolater
|
||||
Providers map[string]ResourceProviderFactory
|
||||
ProviderCache map[string]ResourceProvider
|
||||
ProviderConfigCache map[string]*ResourceConfig
|
||||
ProviderLock *sync.Mutex
|
||||
Provisioners map[string]ResourceProvisionerFactory
|
||||
ProvisionerCache map[string]ResourceProvisioner
|
||||
ProvisionerLock *sync.Mutex
|
||||
|
||||
once sync.Once
|
||||
}
|
||||
@ -47,7 +48,7 @@ func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx.ProviderCache[ctx.pathCacheKey()] = p
|
||||
ctx.ProviderCache[ctx.pathCacheKey(ctx.Path())] = p
|
||||
return p, nil
|
||||
}
|
||||
|
||||
@ -57,7 +58,37 @@ func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider {
|
||||
ctx.ProviderLock.Lock()
|
||||
defer ctx.ProviderLock.Unlock()
|
||||
|
||||
return ctx.ProviderCache[ctx.pathCacheKey()]
|
||||
return ctx.ProviderCache[ctx.pathCacheKey(ctx.Path())]
|
||||
}
|
||||
|
||||
func (ctx *BuiltinEvalContext) ConfigureProvider(
|
||||
n string, cfg *ResourceConfig) error {
|
||||
p := ctx.Provider(n)
|
||||
if p == nil {
|
||||
return fmt.Errorf("Provider '%s' not initialized", n)
|
||||
}
|
||||
|
||||
// Save the configuration
|
||||
ctx.ProviderLock.Lock()
|
||||
ctx.ProviderConfigCache[ctx.pathCacheKey(ctx.Path())] = cfg
|
||||
ctx.ProviderLock.Unlock()
|
||||
|
||||
return p.Configure(cfg)
|
||||
}
|
||||
|
||||
func (ctx *BuiltinEvalContext) ParentProviderConfig(n string) *ResourceConfig {
|
||||
ctx.ProviderLock.Lock()
|
||||
defer ctx.ProviderLock.Unlock()
|
||||
|
||||
path := ctx.Path()
|
||||
for i := len(path) - 1; i >= 1; i-- {
|
||||
k := ctx.pathCacheKey(path[:i])
|
||||
if v, ok := ctx.ProviderConfigCache[k]; ok {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *BuiltinEvalContext) InitProvisioner(
|
||||
@ -84,7 +115,7 @@ func (ctx *BuiltinEvalContext) InitProvisioner(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx.ProvisionerCache[ctx.pathCacheKey()] = p
|
||||
ctx.ProvisionerCache[ctx.pathCacheKey(ctx.Path())] = p
|
||||
return p, nil
|
||||
}
|
||||
|
||||
@ -94,7 +125,7 @@ func (ctx *BuiltinEvalContext) Provisioner(n string) ResourceProvisioner {
|
||||
ctx.ProvisionerLock.Lock()
|
||||
defer ctx.ProvisionerLock.Unlock()
|
||||
|
||||
return ctx.ProvisionerCache[ctx.pathCacheKey()]
|
||||
return ctx.ProvisionerCache[ctx.pathCacheKey(ctx.Path())]
|
||||
}
|
||||
|
||||
func (ctx *BuiltinEvalContext) Interpolate(
|
||||
@ -137,12 +168,12 @@ func (ctx *BuiltinEvalContext) init() {
|
||||
//
|
||||
// This is used because there is a variety of information that needs to be
|
||||
// cached per-path, rather than per-context.
|
||||
func (ctx *BuiltinEvalContext) pathCacheKey() string {
|
||||
func (ctx *BuiltinEvalContext) pathCacheKey(path []string) string {
|
||||
// There is probably a better way to do this, but this is working for now.
|
||||
// We just create an MD5 hash of all the MD5 hashes of all the path
|
||||
// elements. This gets us the property that it is unique per ordering.
|
||||
hash := md5.New()
|
||||
for _, p := range ctx.Path() {
|
||||
for _, p := range path {
|
||||
single := md5.Sum([]byte(p))
|
||||
if _, err := hash.Write(single[:]); err != nil {
|
||||
panic(err)
|
||||
|
@ -7,20 +7,25 @@ import (
|
||||
// EvalConfigProvider is an EvalNode implementation that configures
|
||||
// a provider that is already initialized and retrieved.
|
||||
type EvalConfigProvider struct {
|
||||
Provider EvalNode
|
||||
Provider string
|
||||
Config EvalNode
|
||||
}
|
||||
|
||||
func (n *EvalConfigProvider) Args() ([]EvalNode, []EvalType) {
|
||||
return []EvalNode{n.Provider, n.Config},
|
||||
[]EvalType{EvalTypeResourceProvider, EvalTypeConfig}
|
||||
return []EvalNode{n.Config}, []EvalType{EvalTypeConfig}
|
||||
}
|
||||
|
||||
func (n *EvalConfigProvider) Eval(
|
||||
ctx EvalContext, args []interface{}) (interface{}, error) {
|
||||
provider := args[0].(ResourceProvider)
|
||||
config := args[1].(*ResourceConfig)
|
||||
return nil, provider.Configure(config)
|
||||
config := args[0].(*ResourceConfig)
|
||||
|
||||
// Get the parent configuration if there is one
|
||||
if parent := ctx.ParentProviderConfig(n.Provider); parent != nil {
|
||||
merged := parent.raw.Merge(config.raw)
|
||||
config = NewResourceConfig(merged)
|
||||
}
|
||||
|
||||
return nil, ctx.ConfigureProvider(n.Provider, config)
|
||||
}
|
||||
|
||||
func (n *EvalConfigProvider) Type() EvalType {
|
||||
|
@ -15,7 +15,7 @@ func TestEvalConfigProvider(t *testing.T) {
|
||||
n := &EvalConfigProvider{}
|
||||
|
||||
ctx := &MockEvalContext{ProviderProvider: provider}
|
||||
args := []interface{}{provider, config}
|
||||
args := []interface{}{config}
|
||||
if actual, err := n.Eval(ctx, args); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
} else if actual != nil {
|
||||
@ -32,14 +32,12 @@ func TestEvalConfigProvider(t *testing.T) {
|
||||
|
||||
func TestEvalConfigProvider_args(t *testing.T) {
|
||||
config := testResourceConfig(t, map[string]interface{}{})
|
||||
provider := &MockResourceProvider{}
|
||||
providerNode := &EvalLiteral{Value: provider}
|
||||
configNode := &EvalLiteral{Value: config}
|
||||
n := &EvalConfigProvider{Provider: providerNode, Config: configNode}
|
||||
n := &EvalConfigProvider{Provider: "foo", Config: configNode}
|
||||
|
||||
args, types := n.Args()
|
||||
expectedArgs := []EvalNode{providerNode, configNode}
|
||||
expectedTypes := []EvalType{EvalTypeResourceProvider, EvalTypeConfig}
|
||||
expectedArgs := []EvalNode{configNode}
|
||||
expectedTypes := []EvalType{EvalTypeConfig}
|
||||
if !reflect.DeepEqual(args, expectedArgs) {
|
||||
t.Fatalf("bad: %#v", args)
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ type EvalValidateError struct {
|
||||
}
|
||||
|
||||
func (e *EvalValidateError) Error() string {
|
||||
return ""
|
||||
return fmt.Sprintf("Warnings: %s. Errors: %s", e.Warnings, e.Errors)
|
||||
}
|
||||
|
||||
// EvalValidateCount is an EvalNode implementation that validates
|
||||
@ -63,8 +63,9 @@ func (n *EvalValidateCount) Type() EvalType {
|
||||
// EvalValidateProvider is an EvalNode implementation that validates
|
||||
// the configuration of a resource.
|
||||
type EvalValidateProvider struct {
|
||||
Provider EvalNode
|
||||
Config EvalNode
|
||||
ProviderName string
|
||||
Provider EvalNode
|
||||
Config EvalNode
|
||||
}
|
||||
|
||||
func (n *EvalValidateProvider) Args() ([]EvalNode, []EvalType) {
|
||||
@ -76,6 +77,13 @@ func (n *EvalValidateProvider) Eval(
|
||||
ctx EvalContext, args []interface{}) (interface{}, error) {
|
||||
provider := args[0].(ResourceProvider)
|
||||
config := args[1].(*ResourceConfig)
|
||||
|
||||
// Get the parent configuration if there is one
|
||||
if parent := ctx.ParentProviderConfig(n.ProviderName); parent != nil {
|
||||
merged := parent.raw.Merge(config.raw)
|
||||
config = NewResourceConfig(merged)
|
||||
}
|
||||
|
||||
warns, errs := provider.Validate(config)
|
||||
if len(warns) == 0 && len(errs) == 0 {
|
||||
return nil, nil
|
||||
|
@ -11,11 +11,12 @@ func ProviderEvalTree(n string, config *config.RawConfig) EvalNode {
|
||||
Nodes: []EvalNode{
|
||||
&EvalInitProvider{Name: n},
|
||||
&EvalValidateProvider{
|
||||
Provider: &EvalGetProvider{Name: n},
|
||||
Config: &EvalInterpolate{Config: config},
|
||||
ProviderName: n,
|
||||
Provider: &EvalGetProvider{Name: n},
|
||||
Config: &EvalInterpolate{Config: config},
|
||||
},
|
||||
&EvalConfigProvider{
|
||||
Provider: &EvalGetProvider{Name: n},
|
||||
Provider: n,
|
||||
Config: &EvalInterpolate{Config: config},
|
||||
},
|
||||
},
|
||||
|
@ -3,6 +3,7 @@ package terraform
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/terraform/dag"
|
||||
@ -132,10 +133,13 @@ func (g *Graph) walk(walker GraphWalker) error {
|
||||
ctx := walker.EnterGraph(g)
|
||||
defer walker.ExitGraph(g)
|
||||
|
||||
// Get the path for logs
|
||||
path := strings.Join(ctx.Path(), ".")
|
||||
|
||||
// Walk the graph.
|
||||
var walkFn dag.WalkFunc
|
||||
walkFn = func(v dag.Vertex) (rerr error) {
|
||||
log.Printf("[DEBUG] vertex %s: walking", dag.VertexName(v))
|
||||
log.Printf("[DEBUG] vertex %s.%s: walking", path, dag.VertexName(v))
|
||||
|
||||
walker.EnterVertex(v)
|
||||
defer func() { walker.ExitVertex(v, rerr) }()
|
||||
@ -145,12 +149,12 @@ func (g *Graph) walk(walker GraphWalker) error {
|
||||
tree := ev.EvalTree()
|
||||
if tree == nil {
|
||||
panic(fmt.Sprintf(
|
||||
"%s (%T): nil eval tree", dag.VertexName(v), v))
|
||||
"%s.%s (%T): nil eval tree", path, dag.VertexName(v), v))
|
||||
}
|
||||
|
||||
// Allow the walker to change our tree if needed. Eval,
|
||||
// then callback with the output.
|
||||
log.Printf("[DEBUG] vertex %s: evaluating", dag.VertexName(v))
|
||||
log.Printf("[DEBUG] vertex %s.%s: evaluating", path, dag.VertexName(v))
|
||||
tree = walker.EnterEvalTree(v, tree)
|
||||
output, err := Eval(tree, ctx)
|
||||
walker.ExitEvalTree(v, output, err)
|
||||
@ -159,7 +163,8 @@ func (g *Graph) walk(walker GraphWalker) error {
|
||||
// If the node is dynamically expanded, then expand it
|
||||
if ev, ok := v.(GraphNodeDynamicExpandable); ok {
|
||||
log.Printf(
|
||||
"[DEBUG] vertex %s: expanding dynamic subgraph",
|
||||
"[DEBUG] vertex %s.%s: expanding/walking dynamic subgraph",
|
||||
path,
|
||||
dag.VertexName(v))
|
||||
g, err := ev.DynamicExpand(ctx)
|
||||
if err != nil {
|
||||
@ -176,7 +181,8 @@ func (g *Graph) walk(walker GraphWalker) error {
|
||||
// If the node has a subgraph, then walk the subgraph
|
||||
if sn, ok := v.(GraphNodeSubgraph); ok {
|
||||
log.Printf(
|
||||
"[DEBUG] vertex %s: walking subgraph",
|
||||
"[DEBUG] vertex %s.%s: walking subgraph",
|
||||
path,
|
||||
dag.VertexName(v))
|
||||
|
||||
if rerr = sn.Subgraph().walk(walker); rerr != nil {
|
||||
|
@ -122,5 +122,6 @@ aws_security_group.firewall
|
||||
provider.aws
|
||||
module.consul (expanded)
|
||||
aws_security_group.firewall
|
||||
provider.aws
|
||||
provider.aws
|
||||
`
|
||||
|
@ -52,6 +52,30 @@ func (n *GraphNodeConfigModule) Expand(b GraphBuilder) (*Graph, error) {
|
||||
return b.Build(n.Path)
|
||||
}
|
||||
|
||||
// GraphNodeExpandable
|
||||
func (n *GraphNodeConfigModule) ProvidedBy() []string {
|
||||
// Build up the list of providers by simply going over our configuration
|
||||
// to find the providers that are configured there as well as the
|
||||
// providers that the resources use.
|
||||
config := n.Tree.Config()
|
||||
providers := make(map[string]struct{})
|
||||
for _, p := range config.ProviderConfigs {
|
||||
providers[p.Name] = struct{}{}
|
||||
}
|
||||
for _, r := range config.Resources {
|
||||
providers[resourceProvider(r.Type)] = struct{}{}
|
||||
}
|
||||
|
||||
// Turn the map into a string. This makes sure that the list is
|
||||
// de-dupped since we could be going over potentially many resources.
|
||||
result := make([]string, 0, len(providers))
|
||||
for p, _ := range providers {
|
||||
result = append(result, p)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GraphNodeConfigProvider represents a configured provider within the
|
||||
// configuration graph. These are only immediately in the graph when an
|
||||
// explicit `provider` configuration block is in the configuration.
|
||||
@ -144,8 +168,8 @@ func (n *GraphNodeConfigResource) EvalTree() EvalNode {
|
||||
}
|
||||
|
||||
// GraphNodeProviderConsumer
|
||||
func (n *GraphNodeConfigResource) ProvidedBy() string {
|
||||
return resourceProvider(n.Resource.Type)
|
||||
func (n *GraphNodeConfigResource) ProvidedBy() []string {
|
||||
return []string{resourceProvider(n.Resource.Type)}
|
||||
}
|
||||
|
||||
// GraphNodeProvisionerConsumer
|
||||
|
@ -71,7 +71,7 @@ func TestGraphNodeConfigResource_ProvidedBy(t *testing.T) {
|
||||
Resource: &config.Resource{Type: "aws_instance"},
|
||||
}
|
||||
|
||||
if v := n.ProvidedBy(); v != "aws" {
|
||||
if v := n.ProvidedBy(); v[0] != "aws" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
}
|
||||
}
|
||||
|
@ -22,25 +22,27 @@ type ContextGraphWalker struct {
|
||||
ValidationWarnings []string
|
||||
ValidationErrors []error
|
||||
|
||||
errorLock sync.Mutex
|
||||
once sync.Once
|
||||
providerCache map[string]ResourceProvider
|
||||
providerLock sync.Mutex
|
||||
provisionerCache map[string]ResourceProvisioner
|
||||
provisionerLock sync.Mutex
|
||||
errorLock sync.Mutex
|
||||
once sync.Once
|
||||
providerCache map[string]ResourceProvider
|
||||
providerConfigCache map[string]*ResourceConfig
|
||||
providerLock sync.Mutex
|
||||
provisionerCache map[string]ResourceProvisioner
|
||||
provisionerLock sync.Mutex
|
||||
}
|
||||
|
||||
func (w *ContextGraphWalker) EnterGraph(g *Graph) EvalContext {
|
||||
w.once.Do(w.init)
|
||||
|
||||
return &BuiltinEvalContext{
|
||||
PathValue: g.Path,
|
||||
Providers: w.Context.providers,
|
||||
ProviderCache: w.providerCache,
|
||||
ProviderLock: &w.providerLock,
|
||||
Provisioners: w.Context.provisioners,
|
||||
ProvisionerCache: w.provisionerCache,
|
||||
ProvisionerLock: &w.provisionerLock,
|
||||
PathValue: g.Path,
|
||||
Providers: w.Context.providers,
|
||||
ProviderCache: w.providerCache,
|
||||
ProviderConfigCache: w.providerConfigCache,
|
||||
ProviderLock: &w.providerLock,
|
||||
Provisioners: w.Context.provisioners,
|
||||
ProvisionerCache: w.provisionerCache,
|
||||
ProvisionerLock: &w.provisionerLock,
|
||||
Interpolater: &Interpolater{
|
||||
Operation: w.Operation,
|
||||
Module: w.Context.module,
|
||||
@ -77,5 +79,6 @@ func (w *ContextGraphWalker) ExitEvalTree(
|
||||
|
||||
func (w *ContextGraphWalker) init() {
|
||||
w.providerCache = make(map[string]ResourceProvider, 5)
|
||||
w.providerConfigCache = make(map[string]*ResourceConfig, 5)
|
||||
w.provisionerCache = make(map[string]ResourceProvisioner, 5)
|
||||
}
|
||||
|
@ -114,8 +114,8 @@ func (n *graphNodeOrphanResource) Name() string {
|
||||
return fmt.Sprintf("%s (orphan)", n.ResourceName)
|
||||
}
|
||||
|
||||
func (n *graphNodeOrphanResource) ProvidedBy() string {
|
||||
return resourceProvider(n.ResourceName)
|
||||
func (n *graphNodeOrphanResource) ProvidedBy() []string {
|
||||
return []string{resourceProvider(n.ResourceName)}
|
||||
}
|
||||
|
||||
// GraphNodeEvalable impl.
|
||||
|
@ -269,7 +269,7 @@ func TestGraphNodeOrphanResource_impl(t *testing.T) {
|
||||
|
||||
func TestGraphNodeOrphanResource_ProvidedBy(t *testing.T) {
|
||||
n := &graphNodeOrphanResource{ResourceName: "aws_instance.foo"}
|
||||
if v := n.ProvidedBy(); v != "aws" {
|
||||
if v := n.ProvidedBy(); v[0] != "aws" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ type GraphNodeProvider interface {
|
||||
// a provider must implement. ProvidedBy must return the name of the provider
|
||||
// to use.
|
||||
type GraphNodeProviderConsumer interface {
|
||||
ProvidedBy() string
|
||||
ProvidedBy() []string
|
||||
}
|
||||
|
||||
// ProviderTransformer is a GraphTransformer that maps resources to
|
||||
@ -32,15 +32,17 @@ func (t *ProviderTransformer) Transform(g *Graph) error {
|
||||
m := providerVertexMap(g)
|
||||
for _, v := range g.Vertices() {
|
||||
if pv, ok := v.(GraphNodeProviderConsumer); ok {
|
||||
target := m[pv.ProvidedBy()]
|
||||
if target == nil {
|
||||
err = multierror.Append(err, fmt.Errorf(
|
||||
"%s: provider %s couldn't be found",
|
||||
dag.VertexName(v), pv.ProvidedBy()))
|
||||
continue
|
||||
}
|
||||
for _, p := range pv.ProvidedBy() {
|
||||
target := m[p]
|
||||
if target == nil {
|
||||
err = multierror.Append(err, fmt.Errorf(
|
||||
"%s: provider %s couldn't be found",
|
||||
dag.VertexName(v), p))
|
||||
continue
|
||||
}
|
||||
|
||||
g.Connect(dag.BasicEdge(v, target))
|
||||
g.Connect(dag.BasicEdge(v, target))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,6 @@ func (n *graphNodeTaintedResource) Name() string {
|
||||
return fmt.Sprintf("%s (tainted #%d)", n.ResourceName, n.Index+1)
|
||||
}
|
||||
|
||||
func (n *graphNodeTaintedResource) ProvidedBy() string {
|
||||
return resourceProvider(n.ResourceName)
|
||||
func (n *graphNodeTaintedResource) ProvidedBy() []string {
|
||||
return []string{resourceProvider(n.ResourceName)}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ func TestGraphNodeTaintedResource_impl(t *testing.T) {
|
||||
|
||||
func TestGraphNodeTaintedResource_ProvidedBy(t *testing.T) {
|
||||
n := &graphNodeTaintedResource{ResourceName: "aws_instance.foo"}
|
||||
if v := n.ProvidedBy(); v != "aws" {
|
||||
if v := n.ProvidedBy(); v[0] != "aws" {
|
||||
t.Fatalf("bad: %#v", v)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user