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 }