terraform: provider should be cached by path

This commit is contained in:
Mitchell Hashimoto 2015-02-08 17:58:02 -08:00
parent a41ec59510
commit b8bc3dc19b
7 changed files with 98 additions and 40 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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