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) {
|
||||
p := testProvider("aws")
|
||||
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) {
|
||||
m := testModule(t, "validate-bad-rc")
|
||||
p := testProvider("aws")
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
// EvalNode is the interface that must be implemented by graph nodes to
|
||||
// evaluate/execute.
|
||||
type EvalNode interface {
|
||||
|
@ -37,5 +41,11 @@ func Eval(n EvalNode, ctx EvalContext) (interface{}, error) {
|
|||
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
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
|
@ -10,21 +12,28 @@ import (
|
|||
// BuiltinEvalContext is an EvalContext implementation that is used by
|
||||
// Terraform by default.
|
||||
type BuiltinEvalContext struct {
|
||||
PathValue []string
|
||||
Interpolater *Interpolater
|
||||
Providers map[string]ResourceProviderFactory
|
||||
PathValue []string
|
||||
Interpolater *Interpolater
|
||||
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) {
|
||||
ctx.once.Do(ctx.init)
|
||||
|
||||
// If we already initialized, it is an error
|
||||
if p := ctx.Provider(n); p != nil {
|
||||
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]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Provider '%s' not found", n)
|
||||
|
@ -35,13 +44,17 @@ func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ctx.providers[n] = p
|
||||
ctx.ProviderCache[ctx.pathCacheKey()] = p
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider {
|
||||
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(
|
||||
|
@ -77,8 +90,15 @@ func (ctx *BuiltinEvalContext) init() {
|
|||
if ctx.Providers == nil {
|
||||
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.
|
||||
ctx.providers = make(map[string]ResourceProvider)
|
||||
}
|
||||
|
||||
func (ctx *BuiltinEvalContext) pathCacheKey() string {
|
||||
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 {
|
||||
Provider EvalNode
|
||||
Config EvalNode
|
||||
ProviderType string
|
||||
ResourceType string
|
||||
}
|
||||
|
||||
func (n *EvalValidateResource) Args() ([]EvalNode, []EvalType) {
|
||||
|
@ -108,8 +108,18 @@ func (n *EvalValidateResource) Eval(
|
|||
ctx EvalContext, args []interface{}) (interface{}, error) {
|
||||
// TODO: test
|
||||
|
||||
//provider := args[0].(ResourceProvider)
|
||||
return nil, nil
|
||||
provider := args[0].(ResourceProvider)
|
||||
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 {
|
||||
|
|
|
@ -2,6 +2,7 @@ package terraform
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/terraform/dag"
|
||||
|
@ -134,6 +135,8 @@ func (g *Graph) walk(walker GraphWalker) error {
|
|||
// Walk the graph.
|
||||
var walkFn dag.WalkFunc
|
||||
walkFn = func(v dag.Vertex) (rerr error) {
|
||||
log.Printf("[DEBUG] vertex %s: walking", dag.VertexName(v))
|
||||
|
||||
walker.EnterVertex(v)
|
||||
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,
|
||||
// then callback with the output.
|
||||
log.Printf("[DEBUG] vertex %s: evaluating", dag.VertexName(v))
|
||||
tree = walker.EnterEvalTree(v, tree)
|
||||
output, err := Eval(tree, ctx)
|
||||
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 ev, ok := v.(GraphNodeDynamicExpandable); ok {
|
||||
log.Printf(
|
||||
"[DEBUG] vertex %s: expanding dynamic subgraph",
|
||||
dag.VertexName(v))
|
||||
g, err := ev.DynamicExpand(ctx)
|
||||
if err != nil {
|
||||
rerr = err
|
||||
|
|
|
@ -22,13 +22,20 @@ type ContextGraphWalker struct {
|
|||
ValidationWarnings []string
|
||||
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 {
|
||||
w.once.Do(w.init)
|
||||
|
||||
return &BuiltinEvalContext{
|
||||
PathValue: g.Path,
|
||||
Providers: w.Context.providers,
|
||||
PathValue: g.Path,
|
||||
Providers: w.Context.providers,
|
||||
ProviderCache: w.providerCache,
|
||||
ProviderLock: &w.providerLock,
|
||||
Interpolater: &Interpolater{
|
||||
Operation: w.Operation,
|
||||
Module: w.Context.module,
|
||||
|
@ -62,3 +69,7 @@ func (w *ContextGraphWalker) ExitEvalTree(
|
|||
w.ValidationWarnings = append(w.ValidationWarnings, verr.Warnings...)
|
||||
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{
|
||||
Provider: &EvalGetProvider{Name: n.ProvidedBy()},
|
||||
Config: &EvalInterpolate{Config: n.Resource.RawConfig},
|
||||
ProviderType: n.ProvidedBy(),
|
||||
ResourceType: n.Resource.Type,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue