github.com/adrian-bl/terraform@v0.7.0-rc2.0.20160705220747-de0a34fc3517/terraform/eval_context_builtin.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  	"sync"
     8  
     9  	"github.com/hashicorp/terraform/config"
    10  )
    11  
    12  // BuiltinEvalContext is an EvalContext implementation that is used by
    13  // Terraform by default.
    14  type BuiltinEvalContext struct {
    15  	// PathValue is the Path that this context is operating within.
    16  	PathValue []string
    17  
    18  	// Interpolater setting below affect the interpolation of variables.
    19  	//
    20  	// The InterpolaterVars are the exact value for ${var.foo} values.
    21  	// The map is shared between all contexts and is a mapping of
    22  	// PATH to KEY to VALUE. Because it is shared by all contexts as well
    23  	// as the Interpolater itself, it is protected by InterpolaterVarLock
    24  	// which must be locked during any access to the map.
    25  	Interpolater        *Interpolater
    26  	InterpolaterVars    map[string]map[string]interface{}
    27  	InterpolaterVarLock *sync.Mutex
    28  
    29  	Hooks               []Hook
    30  	InputValue          UIInput
    31  	Providers           map[string]ResourceProviderFactory
    32  	ProviderCache       map[string]ResourceProvider
    33  	ProviderConfigCache map[string]*ResourceConfig
    34  	ProviderInputConfig map[string]map[string]interface{}
    35  	ProviderLock        *sync.Mutex
    36  	Provisioners        map[string]ResourceProvisionerFactory
    37  	ProvisionerCache    map[string]ResourceProvisioner
    38  	ProvisionerLock     *sync.Mutex
    39  	DiffValue           *Diff
    40  	DiffLock            *sync.RWMutex
    41  	StateValue          *State
    42  	StateLock           *sync.RWMutex
    43  
    44  	once sync.Once
    45  }
    46  
    47  func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error {
    48  	for _, h := range ctx.Hooks {
    49  		action, err := fn(h)
    50  		if err != nil {
    51  			return err
    52  		}
    53  
    54  		switch action {
    55  		case HookActionContinue:
    56  			continue
    57  		case HookActionHalt:
    58  			// Return an early exit error to trigger an early exit
    59  			log.Printf("[WARN] Early exit triggered by hook: %T", h)
    60  			return EvalEarlyExitError{}
    61  		}
    62  	}
    63  
    64  	return nil
    65  }
    66  
    67  func (ctx *BuiltinEvalContext) Input() UIInput {
    68  	return ctx.InputValue
    69  }
    70  
    71  func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error) {
    72  	ctx.once.Do(ctx.init)
    73  
    74  	// If we already initialized, it is an error
    75  	if p := ctx.Provider(n); p != nil {
    76  		return nil, fmt.Errorf("Provider '%s' already initialized", n)
    77  	}
    78  
    79  	// Warning: make sure to acquire these locks AFTER the call to Provider
    80  	// above, since it also acquires locks.
    81  	ctx.ProviderLock.Lock()
    82  	defer ctx.ProviderLock.Unlock()
    83  
    84  	typeName := strings.SplitN(n, ".", 2)[0]
    85  
    86  	f, ok := ctx.Providers[typeName]
    87  	if !ok {
    88  		return nil, fmt.Errorf("Provider '%s' not found", typeName)
    89  	}
    90  
    91  	p, err := f()
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	providerPath := make([]string, len(ctx.Path())+1)
    97  	copy(providerPath, ctx.Path())
    98  	providerPath[len(providerPath)-1] = n
    99  
   100  	ctx.ProviderCache[PathCacheKey(providerPath)] = p
   101  	return p, nil
   102  }
   103  
   104  func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider {
   105  	ctx.once.Do(ctx.init)
   106  
   107  	ctx.ProviderLock.Lock()
   108  	defer ctx.ProviderLock.Unlock()
   109  
   110  	providerPath := make([]string, len(ctx.Path())+1)
   111  	copy(providerPath, ctx.Path())
   112  	providerPath[len(providerPath)-1] = n
   113  
   114  	return ctx.ProviderCache[PathCacheKey(providerPath)]
   115  }
   116  
   117  func (ctx *BuiltinEvalContext) CloseProvider(n string) error {
   118  	ctx.once.Do(ctx.init)
   119  
   120  	ctx.ProviderLock.Lock()
   121  	defer ctx.ProviderLock.Unlock()
   122  
   123  	providerPath := make([]string, len(ctx.Path())+1)
   124  	copy(providerPath, ctx.Path())
   125  	providerPath[len(providerPath)-1] = n
   126  
   127  	var provider interface{}
   128  	provider = ctx.ProviderCache[PathCacheKey(providerPath)]
   129  	if provider != nil {
   130  		if p, ok := provider.(ResourceProviderCloser); ok {
   131  			delete(ctx.ProviderCache, PathCacheKey(providerPath))
   132  			return p.Close()
   133  		}
   134  	}
   135  
   136  	return nil
   137  }
   138  
   139  func (ctx *BuiltinEvalContext) ConfigureProvider(
   140  	n string, cfg *ResourceConfig) error {
   141  	p := ctx.Provider(n)
   142  	if p == nil {
   143  		return fmt.Errorf("Provider '%s' not initialized", n)
   144  	}
   145  
   146  	if err := ctx.SetProviderConfig(n, cfg); err != nil {
   147  		return nil
   148  	}
   149  
   150  	return p.Configure(cfg)
   151  }
   152  
   153  func (ctx *BuiltinEvalContext) SetProviderConfig(
   154  	n string, cfg *ResourceConfig) error {
   155  	providerPath := make([]string, len(ctx.Path())+1)
   156  	copy(providerPath, ctx.Path())
   157  	providerPath[len(providerPath)-1] = n
   158  
   159  	// Save the configuration
   160  	ctx.ProviderLock.Lock()
   161  	ctx.ProviderConfigCache[PathCacheKey(providerPath)] = cfg
   162  	ctx.ProviderLock.Unlock()
   163  
   164  	return nil
   165  }
   166  
   167  func (ctx *BuiltinEvalContext) ProviderInput(n string) map[string]interface{} {
   168  	ctx.ProviderLock.Lock()
   169  	defer ctx.ProviderLock.Unlock()
   170  
   171  	// Make a copy of the path so we can safely edit it
   172  	path := ctx.Path()
   173  	pathCopy := make([]string, len(path)+1)
   174  	copy(pathCopy, path)
   175  
   176  	// Go up the tree.
   177  	for i := len(path) - 1; i >= 0; i-- {
   178  		pathCopy[i+1] = n
   179  		k := PathCacheKey(pathCopy[:i+2])
   180  		if v, ok := ctx.ProviderInputConfig[k]; ok {
   181  			return v
   182  		}
   183  	}
   184  
   185  	return nil
   186  }
   187  
   188  func (ctx *BuiltinEvalContext) SetProviderInput(n string, c map[string]interface{}) {
   189  	providerPath := make([]string, len(ctx.Path())+1)
   190  	copy(providerPath, ctx.Path())
   191  	providerPath[len(providerPath)-1] = n
   192  
   193  	// Save the configuration
   194  	ctx.ProviderLock.Lock()
   195  	ctx.ProviderInputConfig[PathCacheKey(providerPath)] = c
   196  	ctx.ProviderLock.Unlock()
   197  }
   198  
   199  func (ctx *BuiltinEvalContext) ParentProviderConfig(n string) *ResourceConfig {
   200  	ctx.ProviderLock.Lock()
   201  	defer ctx.ProviderLock.Unlock()
   202  
   203  	// Make a copy of the path so we can safely edit it
   204  	path := ctx.Path()
   205  	pathCopy := make([]string, len(path)+1)
   206  	copy(pathCopy, path)
   207  
   208  	// Go up the tree.
   209  	for i := len(path) - 1; i >= 0; i-- {
   210  		pathCopy[i+1] = n
   211  		k := PathCacheKey(pathCopy[:i+2])
   212  		if v, ok := ctx.ProviderConfigCache[k]; ok {
   213  			return v
   214  		}
   215  	}
   216  
   217  	return nil
   218  }
   219  
   220  func (ctx *BuiltinEvalContext) InitProvisioner(
   221  	n string) (ResourceProvisioner, error) {
   222  	ctx.once.Do(ctx.init)
   223  
   224  	// If we already initialized, it is an error
   225  	if p := ctx.Provisioner(n); p != nil {
   226  		return nil, fmt.Errorf("Provisioner '%s' already initialized", n)
   227  	}
   228  
   229  	// Warning: make sure to acquire these locks AFTER the call to Provisioner
   230  	// above, since it also acquires locks.
   231  	ctx.ProvisionerLock.Lock()
   232  	defer ctx.ProvisionerLock.Unlock()
   233  
   234  	f, ok := ctx.Provisioners[n]
   235  	if !ok {
   236  		return nil, fmt.Errorf("Provisioner '%s' not found", n)
   237  	}
   238  
   239  	p, err := f()
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  
   244  	provPath := make([]string, len(ctx.Path())+1)
   245  	copy(provPath, ctx.Path())
   246  	provPath[len(provPath)-1] = n
   247  
   248  	ctx.ProvisionerCache[PathCacheKey(provPath)] = p
   249  	return p, nil
   250  }
   251  
   252  func (ctx *BuiltinEvalContext) Provisioner(n string) ResourceProvisioner {
   253  	ctx.once.Do(ctx.init)
   254  
   255  	ctx.ProvisionerLock.Lock()
   256  	defer ctx.ProvisionerLock.Unlock()
   257  
   258  	provPath := make([]string, len(ctx.Path())+1)
   259  	copy(provPath, ctx.Path())
   260  	provPath[len(provPath)-1] = n
   261  
   262  	return ctx.ProvisionerCache[PathCacheKey(provPath)]
   263  }
   264  
   265  func (ctx *BuiltinEvalContext) CloseProvisioner(n string) error {
   266  	ctx.once.Do(ctx.init)
   267  
   268  	ctx.ProvisionerLock.Lock()
   269  	defer ctx.ProvisionerLock.Unlock()
   270  
   271  	provPath := make([]string, len(ctx.Path())+1)
   272  	copy(provPath, ctx.Path())
   273  	provPath[len(provPath)-1] = n
   274  
   275  	var prov interface{}
   276  	prov = ctx.ProvisionerCache[PathCacheKey(provPath)]
   277  	if prov != nil {
   278  		if p, ok := prov.(ResourceProvisionerCloser); ok {
   279  			delete(ctx.ProvisionerCache, PathCacheKey(provPath))
   280  			return p.Close()
   281  		}
   282  	}
   283  
   284  	return nil
   285  }
   286  
   287  func (ctx *BuiltinEvalContext) Interpolate(
   288  	cfg *config.RawConfig, r *Resource) (*ResourceConfig, error) {
   289  	if cfg != nil {
   290  		scope := &InterpolationScope{
   291  			Path:     ctx.Path(),
   292  			Resource: r,
   293  		}
   294  
   295  		vs, err := ctx.Interpolater.Values(scope, cfg.Variables)
   296  		if err != nil {
   297  			return nil, err
   298  		}
   299  
   300  		// Do the interpolation
   301  		if err := cfg.Interpolate(vs); err != nil {
   302  			return nil, err
   303  		}
   304  	}
   305  
   306  	result := NewResourceConfig(cfg)
   307  	result.interpolateForce()
   308  	return result, nil
   309  }
   310  
   311  func (ctx *BuiltinEvalContext) Path() []string {
   312  	return ctx.PathValue
   313  }
   314  
   315  func (ctx *BuiltinEvalContext) SetVariables(n string, vs map[string]interface{}) {
   316  	ctx.InterpolaterVarLock.Lock()
   317  	defer ctx.InterpolaterVarLock.Unlock()
   318  
   319  	path := make([]string, len(ctx.Path())+1)
   320  	copy(path, ctx.Path())
   321  	path[len(path)-1] = n
   322  	key := PathCacheKey(path)
   323  
   324  	vars := ctx.InterpolaterVars[key]
   325  	if vars == nil {
   326  		vars = make(map[string]interface{})
   327  		ctx.InterpolaterVars[key] = vars
   328  	}
   329  
   330  	for k, v := range vs {
   331  		vars[k] = v
   332  	}
   333  }
   334  
   335  func (ctx *BuiltinEvalContext) Diff() (*Diff, *sync.RWMutex) {
   336  	return ctx.DiffValue, ctx.DiffLock
   337  }
   338  
   339  func (ctx *BuiltinEvalContext) State() (*State, *sync.RWMutex) {
   340  	return ctx.StateValue, ctx.StateLock
   341  }
   342  
   343  func (ctx *BuiltinEvalContext) init() {
   344  	// We nil-check the things below because they're meant to be configured,
   345  	// and we just default them to non-nil.
   346  	if ctx.Providers == nil {
   347  		ctx.Providers = make(map[string]ResourceProviderFactory)
   348  	}
   349  }