github.com/hobbeswalsh/terraform@v0.3.7-0.20150619183303-ad17cf55a0fa/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]string
    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) ConfigureProvider(
   118  	n string, cfg *ResourceConfig) error {
   119  	p := ctx.Provider(n)
   120  	if p == nil {
   121  		return fmt.Errorf("Provider '%s' not initialized", n)
   122  	}
   123  
   124  	if err := ctx.SetProviderConfig(n, cfg); err != nil {
   125  		return nil
   126  	}
   127  
   128  	return p.Configure(cfg)
   129  }
   130  
   131  func (ctx *BuiltinEvalContext) SetProviderConfig(
   132  	n string, cfg *ResourceConfig) error {
   133  	providerPath := make([]string, len(ctx.Path())+1)
   134  	copy(providerPath, ctx.Path())
   135  	providerPath[len(providerPath)-1] = n
   136  
   137  	// Save the configuration
   138  	ctx.ProviderLock.Lock()
   139  	ctx.ProviderConfigCache[PathCacheKey(providerPath)] = cfg
   140  	ctx.ProviderLock.Unlock()
   141  
   142  	return nil
   143  }
   144  
   145  func (ctx *BuiltinEvalContext) ProviderInput(n string) map[string]interface{} {
   146  	ctx.ProviderLock.Lock()
   147  	defer ctx.ProviderLock.Unlock()
   148  
   149  	return ctx.ProviderInputConfig[n]
   150  }
   151  
   152  func (ctx *BuiltinEvalContext) SetProviderInput(n string, c map[string]interface{}) {
   153  	ctx.ProviderLock.Lock()
   154  	defer ctx.ProviderLock.Unlock()
   155  
   156  	ctx.ProviderInputConfig[n] = c
   157  }
   158  
   159  func (ctx *BuiltinEvalContext) ParentProviderConfig(n string) *ResourceConfig {
   160  	ctx.ProviderLock.Lock()
   161  	defer ctx.ProviderLock.Unlock()
   162  
   163  	// Make a copy of the path so we can safely edit it
   164  	path := ctx.Path()
   165  	pathCopy := make([]string, len(path)+1)
   166  	copy(pathCopy, path)
   167  
   168  	// Go up the tree.
   169  	for i := len(path) - 1; i >= 0; i-- {
   170  		pathCopy[i+1] = n
   171  		k := PathCacheKey(pathCopy[:i+2])
   172  		if v, ok := ctx.ProviderConfigCache[k]; ok {
   173  			return v
   174  		}
   175  	}
   176  
   177  	return nil
   178  }
   179  
   180  func (ctx *BuiltinEvalContext) InitProvisioner(
   181  	n string) (ResourceProvisioner, error) {
   182  	ctx.once.Do(ctx.init)
   183  
   184  	// If we already initialized, it is an error
   185  	if p := ctx.Provisioner(n); p != nil {
   186  		return nil, fmt.Errorf("Provisioner '%s' already initialized", n)
   187  	}
   188  
   189  	// Warning: make sure to acquire these locks AFTER the call to Provisioner
   190  	// above, since it also acquires locks.
   191  	ctx.ProvisionerLock.Lock()
   192  	defer ctx.ProvisionerLock.Unlock()
   193  
   194  	f, ok := ctx.Provisioners[n]
   195  	if !ok {
   196  		return nil, fmt.Errorf("Provisioner '%s' not found", n)
   197  	}
   198  
   199  	p, err := f()
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  
   204  	provPath := make([]string, len(ctx.Path())+1)
   205  	copy(provPath, ctx.Path())
   206  	provPath[len(provPath)-1] = n
   207  
   208  	ctx.ProvisionerCache[PathCacheKey(provPath)] = p
   209  	return p, nil
   210  }
   211  
   212  func (ctx *BuiltinEvalContext) Provisioner(n string) ResourceProvisioner {
   213  	ctx.once.Do(ctx.init)
   214  
   215  	ctx.ProvisionerLock.Lock()
   216  	defer ctx.ProvisionerLock.Unlock()
   217  
   218  	provPath := make([]string, len(ctx.Path())+1)
   219  	copy(provPath, ctx.Path())
   220  	provPath[len(provPath)-1] = n
   221  
   222  	return ctx.ProvisionerCache[PathCacheKey(provPath)]
   223  }
   224  
   225  func (ctx *BuiltinEvalContext) Interpolate(
   226  	cfg *config.RawConfig, r *Resource) (*ResourceConfig, error) {
   227  	if cfg != nil {
   228  		scope := &InterpolationScope{
   229  			Path:     ctx.Path(),
   230  			Resource: r,
   231  		}
   232  		vs, err := ctx.Interpolater.Values(scope, cfg.Variables)
   233  		if err != nil {
   234  			return nil, err
   235  		}
   236  
   237  		// Do the interpolation
   238  		if err := cfg.Interpolate(vs); err != nil {
   239  			return nil, err
   240  		}
   241  	}
   242  
   243  	result := NewResourceConfig(cfg)
   244  	result.interpolateForce()
   245  	return result, nil
   246  }
   247  
   248  func (ctx *BuiltinEvalContext) Path() []string {
   249  	return ctx.PathValue
   250  }
   251  
   252  func (ctx *BuiltinEvalContext) SetVariables(n string, vs map[string]string) {
   253  	ctx.InterpolaterVarLock.Lock()
   254  	defer ctx.InterpolaterVarLock.Unlock()
   255  
   256  	path := make([]string, len(ctx.Path())+1)
   257  	copy(path, ctx.Path())
   258  	path[len(path)-1] = n
   259  	key := PathCacheKey(path)
   260  
   261  	vars := ctx.InterpolaterVars[key]
   262  	if vars == nil {
   263  		vars = make(map[string]string)
   264  		ctx.InterpolaterVars[key] = vars
   265  	}
   266  
   267  	for k, v := range vs {
   268  		vars[k] = v
   269  	}
   270  }
   271  
   272  func (ctx *BuiltinEvalContext) Diff() (*Diff, *sync.RWMutex) {
   273  	return ctx.DiffValue, ctx.DiffLock
   274  }
   275  
   276  func (ctx *BuiltinEvalContext) State() (*State, *sync.RWMutex) {
   277  	return ctx.StateValue, ctx.StateLock
   278  }
   279  
   280  func (ctx *BuiltinEvalContext) init() {
   281  	// We nil-check the things below because they're meant to be configured,
   282  	// and we just default them to non-nil.
   283  	if ctx.Providers == nil {
   284  		ctx.Providers = make(map[string]ResourceProviderFactory)
   285  	}
   286  }