terraform: provider should be cached by path
This commit is contained in:
parent
a41ec59510
commit
b8bc3dc19b
|
@ -262,6 +262,27 @@ func TestContext2Validate_requiredVar(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContext2Validate_resourceConfig_bad(t *testing.T) {
|
||||||
|
m := testModule(t, "validate-bad-rc")
|
||||||
|
p := testProvider("aws")
|
||||||
|
c := testContext2(t, &ContextOpts{
|
||||||
|
Module: m,
|
||||||
|
Providers: map[string]ResourceProviderFactory{
|
||||||
|
"aws": testProviderFuncFixed(p),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
p.ValidateResourceReturnErrors = []error{fmt.Errorf("bad")}
|
||||||
|
|
||||||
|
w, e := c.Validate()
|
||||||
|
if len(w) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", w)
|
||||||
|
}
|
||||||
|
if len(e) == 0 {
|
||||||
|
t.Fatalf("bad: %s", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestContext2Validate_selfRef(t *testing.T) {
|
func TestContext2Validate_selfRef(t *testing.T) {
|
||||||
p := testProvider("aws")
|
p := testProvider("aws")
|
||||||
m := testModule(t, "validate-self-ref")
|
m := testModule(t, "validate-self-ref")
|
||||||
|
@ -406,27 +427,6 @@ func TestContextValidate_tainted(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContextValidate_resourceConfig_bad(t *testing.T) {
|
|
||||||
m := testModule(t, "validate-bad-rc")
|
|
||||||
p := testProvider("aws")
|
|
||||||
c := testContext(t, &ContextOpts{
|
|
||||||
Module: m,
|
|
||||||
Providers: map[string]ResourceProviderFactory{
|
|
||||||
"aws": testProviderFuncFixed(p),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
p.ValidateResourceReturnErrors = []error{fmt.Errorf("bad")}
|
|
||||||
|
|
||||||
w, e := c.Validate()
|
|
||||||
if len(w) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", w)
|
|
||||||
}
|
|
||||||
if len(e) == 0 {
|
|
||||||
t.Fatalf("bad: %#v", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContextValidate_resourceConfig_good(t *testing.T) {
|
func TestContextValidate_resourceConfig_good(t *testing.T) {
|
||||||
m := testModule(t, "validate-bad-rc")
|
m := testModule(t, "validate-bad-rc")
|
||||||
p := testProvider("aws")
|
p := testProvider("aws")
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
// EvalNode is the interface that must be implemented by graph nodes to
|
// EvalNode is the interface that must be implemented by graph nodes to
|
||||||
// evaluate/execute.
|
// evaluate/execute.
|
||||||
type EvalNode interface {
|
type EvalNode interface {
|
||||||
|
@ -37,5 +41,11 @@ func Eval(n EvalNode, ctx EvalContext) (interface{}, error) {
|
||||||
args[i] = v
|
args[i] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
return n.Eval(ctx, args)
|
log.Printf("[DEBUG] eval: %T", n)
|
||||||
|
output, err := n.Eval(ctx, args)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] eval: %T, err: %s", n, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return output, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -10,21 +12,28 @@ import (
|
||||||
// BuiltinEvalContext is an EvalContext implementation that is used by
|
// BuiltinEvalContext is an EvalContext implementation that is used by
|
||||||
// Terraform by default.
|
// Terraform by default.
|
||||||
type BuiltinEvalContext struct {
|
type BuiltinEvalContext struct {
|
||||||
PathValue []string
|
PathValue []string
|
||||||
Interpolater *Interpolater
|
Interpolater *Interpolater
|
||||||
Providers map[string]ResourceProviderFactory
|
Providers map[string]ResourceProviderFactory
|
||||||
|
ProviderCache map[string]ResourceProvider
|
||||||
|
ProviderLock *sync.Mutex
|
||||||
|
|
||||||
providers map[string]ResourceProvider
|
once sync.Once
|
||||||
once sync.Once
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error) {
|
func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error) {
|
||||||
ctx.once.Do(ctx.init)
|
ctx.once.Do(ctx.init)
|
||||||
|
|
||||||
|
// If we already initialized, it is an error
|
||||||
if p := ctx.Provider(n); p != nil {
|
if p := ctx.Provider(n); p != nil {
|
||||||
return nil, fmt.Errorf("Provider '%s' already initialized", n)
|
return nil, fmt.Errorf("Provider '%s' already initialized", n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Warning: make sure to acquire these locks AFTER the call to Provider
|
||||||
|
// above, since it also acquires locks.
|
||||||
|
ctx.ProviderLock.Lock()
|
||||||
|
defer ctx.ProviderLock.Unlock()
|
||||||
|
|
||||||
f, ok := ctx.Providers[n]
|
f, ok := ctx.Providers[n]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("Provider '%s' not found", n)
|
return nil, fmt.Errorf("Provider '%s' not found", n)
|
||||||
|
@ -35,13 +44,17 @@ func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.providers[n] = p
|
ctx.ProviderCache[ctx.pathCacheKey()] = p
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider {
|
func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider {
|
||||||
ctx.once.Do(ctx.init)
|
ctx.once.Do(ctx.init)
|
||||||
return ctx.providers[n]
|
|
||||||
|
ctx.ProviderLock.Lock()
|
||||||
|
defer ctx.ProviderLock.Unlock()
|
||||||
|
|
||||||
|
return ctx.ProviderCache[ctx.pathCacheKey()]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *BuiltinEvalContext) Interpolate(
|
func (ctx *BuiltinEvalContext) Interpolate(
|
||||||
|
@ -77,8 +90,15 @@ func (ctx *BuiltinEvalContext) init() {
|
||||||
if ctx.Providers == nil {
|
if ctx.Providers == nil {
|
||||||
ctx.Providers = make(map[string]ResourceProviderFactory)
|
ctx.Providers = make(map[string]ResourceProviderFactory)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// We always reset the things below since we only call this once and
|
|
||||||
// they can't be initialized externally.
|
func (ctx *BuiltinEvalContext) pathCacheKey() string {
|
||||||
ctx.providers = make(map[string]ResourceProvider)
|
hash := md5.New()
|
||||||
|
for _, p := range ctx.Path() {
|
||||||
|
if _, err := hash.Write([]byte(p)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex.EncodeToString(hash.Sum(nil))
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,7 +96,7 @@ func (n *EvalValidateProvider) Type() EvalType {
|
||||||
type EvalValidateResource struct {
|
type EvalValidateResource struct {
|
||||||
Provider EvalNode
|
Provider EvalNode
|
||||||
Config EvalNode
|
Config EvalNode
|
||||||
ProviderType string
|
ResourceType string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *EvalValidateResource) Args() ([]EvalNode, []EvalType) {
|
func (n *EvalValidateResource) Args() ([]EvalNode, []EvalType) {
|
||||||
|
@ -108,8 +108,18 @@ func (n *EvalValidateResource) Eval(
|
||||||
ctx EvalContext, args []interface{}) (interface{}, error) {
|
ctx EvalContext, args []interface{}) (interface{}, error) {
|
||||||
// TODO: test
|
// TODO: test
|
||||||
|
|
||||||
//provider := args[0].(ResourceProvider)
|
provider := args[0].(ResourceProvider)
|
||||||
return nil, nil
|
config := args[1].(*ResourceConfig)
|
||||||
|
|
||||||
|
warns, errs := provider.ValidateResource(n.ResourceType, config)
|
||||||
|
if len(warns) == 0 && len(errs) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, &EvalValidateError{
|
||||||
|
Warnings: warns,
|
||||||
|
Errors: errs,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *EvalValidateResource) Type() EvalType {
|
func (n *EvalValidateResource) Type() EvalType {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/dag"
|
"github.com/hashicorp/terraform/dag"
|
||||||
|
@ -134,6 +135,8 @@ func (g *Graph) walk(walker GraphWalker) error {
|
||||||
// Walk the graph.
|
// Walk the graph.
|
||||||
var walkFn dag.WalkFunc
|
var walkFn dag.WalkFunc
|
||||||
walkFn = func(v dag.Vertex) (rerr error) {
|
walkFn = func(v dag.Vertex) (rerr error) {
|
||||||
|
log.Printf("[DEBUG] vertex %s: walking", dag.VertexName(v))
|
||||||
|
|
||||||
walker.EnterVertex(v)
|
walker.EnterVertex(v)
|
||||||
defer func() { walker.ExitVertex(v, rerr) }()
|
defer func() { walker.ExitVertex(v, rerr) }()
|
||||||
|
|
||||||
|
@ -147,6 +150,7 @@ func (g *Graph) walk(walker GraphWalker) error {
|
||||||
|
|
||||||
// Allow the walker to change our tree if needed. Eval,
|
// Allow the walker to change our tree if needed. Eval,
|
||||||
// then callback with the output.
|
// then callback with the output.
|
||||||
|
log.Printf("[DEBUG] vertex %s: evaluating", dag.VertexName(v))
|
||||||
tree = walker.EnterEvalTree(v, tree)
|
tree = walker.EnterEvalTree(v, tree)
|
||||||
output, err := Eval(tree, ctx)
|
output, err := Eval(tree, ctx)
|
||||||
walker.ExitEvalTree(v, output, err)
|
walker.ExitEvalTree(v, output, err)
|
||||||
|
@ -154,6 +158,9 @@ func (g *Graph) walk(walker GraphWalker) error {
|
||||||
|
|
||||||
// If the node is dynamically expanded, then expand it
|
// If the node is dynamically expanded, then expand it
|
||||||
if ev, ok := v.(GraphNodeDynamicExpandable); ok {
|
if ev, ok := v.(GraphNodeDynamicExpandable); ok {
|
||||||
|
log.Printf(
|
||||||
|
"[DEBUG] vertex %s: expanding dynamic subgraph",
|
||||||
|
dag.VertexName(v))
|
||||||
g, err := ev.DynamicExpand(ctx)
|
g, err := ev.DynamicExpand(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rerr = err
|
rerr = err
|
||||||
|
|
|
@ -22,13 +22,20 @@ type ContextGraphWalker struct {
|
||||||
ValidationWarnings []string
|
ValidationWarnings []string
|
||||||
ValidationErrors []error
|
ValidationErrors []error
|
||||||
|
|
||||||
errorLock sync.Mutex
|
errorLock sync.Mutex
|
||||||
|
once sync.Once
|
||||||
|
providerCache map[string]ResourceProvider
|
||||||
|
providerLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *ContextGraphWalker) EnterGraph(g *Graph) EvalContext {
|
func (w *ContextGraphWalker) EnterGraph(g *Graph) EvalContext {
|
||||||
|
w.once.Do(w.init)
|
||||||
|
|
||||||
return &BuiltinEvalContext{
|
return &BuiltinEvalContext{
|
||||||
PathValue: g.Path,
|
PathValue: g.Path,
|
||||||
Providers: w.Context.providers,
|
Providers: w.Context.providers,
|
||||||
|
ProviderCache: w.providerCache,
|
||||||
|
ProviderLock: &w.providerLock,
|
||||||
Interpolater: &Interpolater{
|
Interpolater: &Interpolater{
|
||||||
Operation: w.Operation,
|
Operation: w.Operation,
|
||||||
Module: w.Context.module,
|
Module: w.Context.module,
|
||||||
|
@ -62,3 +69,7 @@ func (w *ContextGraphWalker) ExitEvalTree(
|
||||||
w.ValidationWarnings = append(w.ValidationWarnings, verr.Warnings...)
|
w.ValidationWarnings = append(w.ValidationWarnings, verr.Warnings...)
|
||||||
w.ValidationErrors = append(w.ValidationErrors, verr.Errors...)
|
w.ValidationErrors = append(w.ValidationErrors, verr.Errors...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *ContextGraphWalker) init() {
|
||||||
|
w.providerCache = make(map[string]ResourceProvider, 5)
|
||||||
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ func (n *graphNodeExpandedResource) EvalTree() EvalNode {
|
||||||
&EvalValidateResource{
|
&EvalValidateResource{
|
||||||
Provider: &EvalGetProvider{Name: n.ProvidedBy()},
|
Provider: &EvalGetProvider{Name: n.ProvidedBy()},
|
||||||
Config: &EvalInterpolate{Config: n.Resource.RawConfig},
|
Config: &EvalInterpolate{Config: n.Resource.RawConfig},
|
||||||
ProviderType: n.ProvidedBy(),
|
ResourceType: n.Resource.Type,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue