github.com/dougneal/terraform@v0.6.15-0.20170330092735-b6a3840768a4/terraform/eval_context_builtin.go (about)

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