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 }