2015-02-04 02:46:11 +01:00
|
|
|
package terraform
|
|
|
|
|
|
|
|
import (
|
2016-12-22 20:33:26 +01:00
|
|
|
"context"
|
2015-02-04 02:46:11 +01:00
|
|
|
"fmt"
|
2015-02-13 18:05:09 +01:00
|
|
|
"log"
|
2015-03-23 23:36:53 +01:00
|
|
|
"strings"
|
2015-02-04 02:46:11 +01:00
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/config"
|
|
|
|
)
|
|
|
|
|
|
|
|
// BuiltinEvalContext is an EvalContext implementation that is used by
|
|
|
|
// Terraform by default.
|
|
|
|
type BuiltinEvalContext struct {
|
2016-12-22 20:33:26 +01:00
|
|
|
// StopContext is the context used to track whether we're complete
|
|
|
|
StopContext context.Context
|
|
|
|
|
2015-05-04 19:51:34 +02:00
|
|
|
// PathValue is the Path that this context is operating within.
|
|
|
|
PathValue []string
|
|
|
|
|
|
|
|
// Interpolater setting below affect the interpolation of variables.
|
|
|
|
//
|
|
|
|
// The InterpolaterVars are the exact value for ${var.foo} values.
|
|
|
|
// The map is shared between all contexts and is a mapping of
|
|
|
|
// PATH to KEY to VALUE. Because it is shared by all contexts as well
|
|
|
|
// as the Interpolater itself, it is protected by InterpolaterVarLock
|
|
|
|
// which must be locked during any access to the map.
|
2015-02-10 08:32:28 +01:00
|
|
|
Interpolater *Interpolater
|
2016-04-11 19:40:06 +02:00
|
|
|
InterpolaterVars map[string]map[string]interface{}
|
2015-05-02 01:29:19 +02:00
|
|
|
InterpolaterVarLock *sync.Mutex
|
2015-05-04 19:51:34 +02:00
|
|
|
|
2016-10-04 03:23:37 +02:00
|
|
|
Components contextComponentFactory
|
2015-02-11 22:43:07 +01:00
|
|
|
Hooks []Hook
|
2015-02-14 02:59:54 +01:00
|
|
|
InputValue UIInput
|
2015-02-10 08:32:28 +01:00
|
|
|
ProviderCache map[string]ResourceProvider
|
|
|
|
ProviderConfigCache map[string]*ResourceConfig
|
2015-02-14 02:59:54 +01:00
|
|
|
ProviderInputConfig map[string]map[string]interface{}
|
2015-02-10 08:32:28 +01:00
|
|
|
ProviderLock *sync.Mutex
|
|
|
|
ProvisionerCache map[string]ResourceProvisioner
|
|
|
|
ProvisionerLock *sync.Mutex
|
2015-02-12 00:22:03 +01:00
|
|
|
DiffValue *Diff
|
|
|
|
DiffLock *sync.RWMutex
|
2015-02-11 17:48:45 +01:00
|
|
|
StateValue *State
|
|
|
|
StateLock *sync.RWMutex
|
2015-02-04 02:46:11 +01:00
|
|
|
|
2015-05-02 01:29:19 +02:00
|
|
|
once sync.Once
|
2015-02-04 02:46:11 +01:00
|
|
|
}
|
|
|
|
|
2016-12-22 20:33:26 +01:00
|
|
|
func (ctx *BuiltinEvalContext) Stopped() <-chan struct{} {
|
|
|
|
// This can happen during tests. During tests, we just block forever.
|
|
|
|
if ctx.StopContext == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return ctx.StopContext.Done()
|
|
|
|
}
|
|
|
|
|
2015-02-11 22:43:07 +01:00
|
|
|
func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error {
|
|
|
|
for _, h := range ctx.Hooks {
|
|
|
|
action, err := fn(h)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch action {
|
|
|
|
case HookActionContinue:
|
|
|
|
continue
|
|
|
|
case HookActionHalt:
|
|
|
|
// Return an early exit error to trigger an early exit
|
2015-02-13 18:05:09 +01:00
|
|
|
log.Printf("[WARN] Early exit triggered by hook: %T", h)
|
2015-02-11 22:43:07 +01:00
|
|
|
return EvalEarlyExitError{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-02-14 02:59:54 +01:00
|
|
|
func (ctx *BuiltinEvalContext) Input() UIInput {
|
|
|
|
return ctx.InputValue
|
|
|
|
}
|
|
|
|
|
2015-02-04 02:46:11 +01:00
|
|
|
func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error) {
|
|
|
|
ctx.once.Do(ctx.init)
|
|
|
|
|
2015-02-09 02:58:02 +01:00
|
|
|
// If we already initialized, it is an error
|
2015-02-04 02:46:11 +01:00
|
|
|
if p := ctx.Provider(n); p != nil {
|
|
|
|
return nil, fmt.Errorf("Provider '%s' already initialized", n)
|
|
|
|
}
|
|
|
|
|
2015-02-09 02:58:02 +01:00
|
|
|
// 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()
|
|
|
|
|
2016-10-04 04:34:07 +02:00
|
|
|
providerPath := make([]string, len(ctx.Path())+1)
|
|
|
|
copy(providerPath, ctx.Path())
|
|
|
|
providerPath[len(providerPath)-1] = n
|
|
|
|
key := PathCacheKey(providerPath)
|
2015-03-23 23:36:53 +01:00
|
|
|
|
2016-10-04 04:34:07 +02:00
|
|
|
typeName := strings.SplitN(n, ".", 2)[0]
|
|
|
|
p, err := ctx.Components.ResourceProvider(typeName, key)
|
2015-02-05 02:02:18 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-10-04 04:34:07 +02:00
|
|
|
ctx.ProviderCache[key] = p
|
2015-02-05 02:02:18 +01:00
|
|
|
return p, nil
|
2015-02-04 02:46:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider {
|
|
|
|
ctx.once.Do(ctx.init)
|
2015-02-09 02:58:02 +01:00
|
|
|
|
|
|
|
ctx.ProviderLock.Lock()
|
|
|
|
defer ctx.ProviderLock.Unlock()
|
|
|
|
|
2015-02-17 04:04:05 +01:00
|
|
|
providerPath := make([]string, len(ctx.Path())+1)
|
|
|
|
copy(providerPath, ctx.Path())
|
|
|
|
providerPath[len(providerPath)-1] = n
|
|
|
|
|
|
|
|
return ctx.ProviderCache[PathCacheKey(providerPath)]
|
2015-02-10 08:32:28 +01:00
|
|
|
}
|
|
|
|
|
2015-06-19 21:52:50 +02:00
|
|
|
func (ctx *BuiltinEvalContext) CloseProvider(n string) error {
|
|
|
|
ctx.once.Do(ctx.init)
|
|
|
|
|
|
|
|
ctx.ProviderLock.Lock()
|
|
|
|
defer ctx.ProviderLock.Unlock()
|
|
|
|
|
|
|
|
providerPath := make([]string, len(ctx.Path())+1)
|
|
|
|
copy(providerPath, ctx.Path())
|
|
|
|
providerPath[len(providerPath)-1] = n
|
|
|
|
|
|
|
|
var provider interface{}
|
|
|
|
provider = ctx.ProviderCache[PathCacheKey(providerPath)]
|
|
|
|
if provider != nil {
|
|
|
|
if p, ok := provider.(ResourceProviderCloser); ok {
|
|
|
|
delete(ctx.ProviderCache, PathCacheKey(providerPath))
|
|
|
|
return p.Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-02-10 08:32:28 +01:00
|
|
|
func (ctx *BuiltinEvalContext) ConfigureProvider(
|
|
|
|
n string, cfg *ResourceConfig) error {
|
|
|
|
p := ctx.Provider(n)
|
|
|
|
if p == nil {
|
|
|
|
return fmt.Errorf("Provider '%s' not initialized", n)
|
|
|
|
}
|
|
|
|
|
2015-04-09 17:48:08 +02:00
|
|
|
if err := ctx.SetProviderConfig(n, cfg); err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return p.Configure(cfg)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *BuiltinEvalContext) SetProviderConfig(
|
|
|
|
n string, cfg *ResourceConfig) error {
|
2015-02-21 00:48:06 +01:00
|
|
|
providerPath := make([]string, len(ctx.Path())+1)
|
|
|
|
copy(providerPath, ctx.Path())
|
|
|
|
providerPath[len(providerPath)-1] = n
|
|
|
|
|
2015-02-10 08:32:28 +01:00
|
|
|
// Save the configuration
|
|
|
|
ctx.ProviderLock.Lock()
|
2015-02-21 00:48:06 +01:00
|
|
|
ctx.ProviderConfigCache[PathCacheKey(providerPath)] = cfg
|
2015-02-10 08:32:28 +01:00
|
|
|
ctx.ProviderLock.Unlock()
|
|
|
|
|
2015-04-09 17:48:08 +02:00
|
|
|
return nil
|
2015-02-10 08:32:28 +01:00
|
|
|
}
|
|
|
|
|
2015-02-14 02:59:54 +01:00
|
|
|
func (ctx *BuiltinEvalContext) ProviderInput(n string) map[string]interface{} {
|
|
|
|
ctx.ProviderLock.Lock()
|
|
|
|
defer ctx.ProviderLock.Unlock()
|
|
|
|
|
2015-06-24 18:34:21 +02:00
|
|
|
// Make a copy of the path so we can safely edit it
|
|
|
|
path := ctx.Path()
|
|
|
|
pathCopy := make([]string, len(path)+1)
|
|
|
|
copy(pathCopy, path)
|
|
|
|
|
|
|
|
// Go up the tree.
|
|
|
|
for i := len(path) - 1; i >= 0; i-- {
|
|
|
|
pathCopy[i+1] = n
|
|
|
|
k := PathCacheKey(pathCopy[:i+2])
|
|
|
|
if v, ok := ctx.ProviderInputConfig[k]; ok {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2015-02-14 02:59:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *BuiltinEvalContext) SetProviderInput(n string, c map[string]interface{}) {
|
2015-06-24 18:34:21 +02:00
|
|
|
providerPath := make([]string, len(ctx.Path())+1)
|
|
|
|
copy(providerPath, ctx.Path())
|
|
|
|
providerPath[len(providerPath)-1] = n
|
2015-02-14 02:59:54 +01:00
|
|
|
|
2015-06-24 18:34:21 +02:00
|
|
|
// Save the configuration
|
|
|
|
ctx.ProviderLock.Lock()
|
|
|
|
ctx.ProviderInputConfig[PathCacheKey(providerPath)] = c
|
|
|
|
ctx.ProviderLock.Unlock()
|
2015-02-14 02:59:54 +01:00
|
|
|
}
|
|
|
|
|
2015-02-10 08:32:28 +01:00
|
|
|
func (ctx *BuiltinEvalContext) ParentProviderConfig(n string) *ResourceConfig {
|
|
|
|
ctx.ProviderLock.Lock()
|
|
|
|
defer ctx.ProviderLock.Unlock()
|
|
|
|
|
2015-02-21 00:48:06 +01:00
|
|
|
// Make a copy of the path so we can safely edit it
|
2015-02-10 08:32:28 +01:00
|
|
|
path := ctx.Path()
|
2015-02-21 00:48:06 +01:00
|
|
|
pathCopy := make([]string, len(path)+1)
|
|
|
|
copy(pathCopy, path)
|
|
|
|
|
|
|
|
// Go up the tree.
|
|
|
|
for i := len(path) - 1; i >= 0; i-- {
|
|
|
|
pathCopy[i+1] = n
|
|
|
|
k := PathCacheKey(pathCopy[:i+2])
|
2015-02-10 08:32:28 +01:00
|
|
|
if v, ok := ctx.ProviderConfigCache[k]; ok {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2015-02-04 02:46:11 +01:00
|
|
|
}
|
|
|
|
|
2015-02-09 20:15:54 +01:00
|
|
|
func (ctx *BuiltinEvalContext) InitProvisioner(
|
|
|
|
n string) (ResourceProvisioner, error) {
|
|
|
|
ctx.once.Do(ctx.init)
|
|
|
|
|
|
|
|
// If we already initialized, it is an error
|
|
|
|
if p := ctx.Provisioner(n); p != nil {
|
|
|
|
return nil, fmt.Errorf("Provisioner '%s' already initialized", n)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Warning: make sure to acquire these locks AFTER the call to Provisioner
|
|
|
|
// above, since it also acquires locks.
|
|
|
|
ctx.ProvisionerLock.Lock()
|
|
|
|
defer ctx.ProvisionerLock.Unlock()
|
|
|
|
|
2015-02-20 19:50:36 +01:00
|
|
|
provPath := make([]string, len(ctx.Path())+1)
|
|
|
|
copy(provPath, ctx.Path())
|
|
|
|
provPath[len(provPath)-1] = n
|
2016-10-04 04:34:07 +02:00
|
|
|
key := PathCacheKey(provPath)
|
|
|
|
|
|
|
|
p, err := ctx.Components.ResourceProvisioner(n, key)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-02-20 19:50:36 +01:00
|
|
|
|
2016-10-04 04:34:07 +02:00
|
|
|
ctx.ProvisionerCache[key] = p
|
2015-02-09 20:15:54 +01:00
|
|
|
return p, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *BuiltinEvalContext) Provisioner(n string) ResourceProvisioner {
|
|
|
|
ctx.once.Do(ctx.init)
|
|
|
|
|
|
|
|
ctx.ProvisionerLock.Lock()
|
|
|
|
defer ctx.ProvisionerLock.Unlock()
|
|
|
|
|
2015-02-20 19:50:36 +01:00
|
|
|
provPath := make([]string, len(ctx.Path())+1)
|
|
|
|
copy(provPath, ctx.Path())
|
|
|
|
provPath[len(provPath)-1] = n
|
|
|
|
|
|
|
|
return ctx.ProvisionerCache[PathCacheKey(provPath)]
|
2015-02-09 20:15:54 +01:00
|
|
|
}
|
|
|
|
|
2015-06-19 21:52:50 +02:00
|
|
|
func (ctx *BuiltinEvalContext) CloseProvisioner(n string) error {
|
|
|
|
ctx.once.Do(ctx.init)
|
|
|
|
|
|
|
|
ctx.ProvisionerLock.Lock()
|
|
|
|
defer ctx.ProvisionerLock.Unlock()
|
|
|
|
|
|
|
|
provPath := make([]string, len(ctx.Path())+1)
|
|
|
|
copy(provPath, ctx.Path())
|
|
|
|
provPath[len(provPath)-1] = n
|
|
|
|
|
|
|
|
var prov interface{}
|
|
|
|
prov = ctx.ProvisionerCache[PathCacheKey(provPath)]
|
|
|
|
if prov != nil {
|
|
|
|
if p, ok := prov.(ResourceProvisionerCloser); ok {
|
|
|
|
delete(ctx.ProvisionerCache, PathCacheKey(provPath))
|
|
|
|
return p.Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-02-04 02:46:11 +01:00
|
|
|
func (ctx *BuiltinEvalContext) Interpolate(
|
2015-02-06 02:09:57 +01:00
|
|
|
cfg *config.RawConfig, r *Resource) (*ResourceConfig, error) {
|
|
|
|
if cfg != nil {
|
|
|
|
scope := &InterpolationScope{
|
2015-02-08 23:00:13 +01:00
|
|
|
Path: ctx.Path(),
|
2015-02-06 02:09:57 +01:00
|
|
|
Resource: r,
|
|
|
|
}
|
2016-05-19 19:46:51 +02:00
|
|
|
|
2015-02-06 02:09:57 +01:00
|
|
|
vs, err := ctx.Interpolater.Values(scope, cfg.Variables)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2015-02-04 17:30:53 +01:00
|
|
|
}
|
|
|
|
|
2015-02-06 02:09:57 +01:00
|
|
|
// Do the interpolation
|
|
|
|
if err := cfg.Interpolate(vs); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2015-02-04 17:30:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
result := NewResourceConfig(cfg)
|
|
|
|
result.interpolateForce()
|
|
|
|
return result, nil
|
2015-02-04 02:46:11 +01:00
|
|
|
}
|
|
|
|
|
2015-02-08 23:00:13 +01:00
|
|
|
func (ctx *BuiltinEvalContext) Path() []string {
|
|
|
|
return ctx.PathValue
|
|
|
|
}
|
|
|
|
|
2016-04-11 19:40:06 +02:00
|
|
|
func (ctx *BuiltinEvalContext) SetVariables(n string, vs map[string]interface{}) {
|
2015-05-02 01:29:19 +02:00
|
|
|
ctx.InterpolaterVarLock.Lock()
|
|
|
|
defer ctx.InterpolaterVarLock.Unlock()
|
|
|
|
|
|
|
|
path := make([]string, len(ctx.Path())+1)
|
|
|
|
copy(path, ctx.Path())
|
|
|
|
path[len(path)-1] = n
|
|
|
|
key := PathCacheKey(path)
|
|
|
|
|
|
|
|
vars := ctx.InterpolaterVars[key]
|
|
|
|
if vars == nil {
|
2016-04-11 19:40:06 +02:00
|
|
|
vars = make(map[string]interface{})
|
2015-05-02 01:29:19 +02:00
|
|
|
ctx.InterpolaterVars[key] = vars
|
|
|
|
}
|
2015-05-01 23:10:41 +02:00
|
|
|
|
2015-02-12 02:01:08 +01:00
|
|
|
for k, v := range vs {
|
2015-05-02 01:29:19 +02:00
|
|
|
vars[k] = v
|
2015-02-12 02:01:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-12 00:22:03 +01:00
|
|
|
func (ctx *BuiltinEvalContext) Diff() (*Diff, *sync.RWMutex) {
|
|
|
|
return ctx.DiffValue, ctx.DiffLock
|
|
|
|
}
|
|
|
|
|
2015-02-11 17:48:45 +01:00
|
|
|
func (ctx *BuiltinEvalContext) State() (*State, *sync.RWMutex) {
|
|
|
|
return ctx.StateValue, ctx.StateLock
|
|
|
|
}
|
|
|
|
|
2015-02-04 02:46:11 +01:00
|
|
|
func (ctx *BuiltinEvalContext) init() {
|
2015-02-09 02:58:02 +01:00
|
|
|
}
|