github.com/r3labs/terraform@v0.8.4/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 Components contextComponentFactory 30 Hooks []Hook 31 InputValue UIInput 32 ProviderCache map[string]ResourceProvider 33 ProviderConfigCache map[string]*ResourceConfig 34 ProviderInputConfig map[string]map[string]interface{} 35 ProviderLock *sync.Mutex 36 ProvisionerCache map[string]ResourceProvisioner 37 ProvisionerLock *sync.Mutex 38 DiffValue *Diff 39 DiffLock *sync.RWMutex 40 StateValue *State 41 StateLock *sync.RWMutex 42 43 once sync.Once 44 } 45 46 func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error { 47 for _, h := range ctx.Hooks { 48 action, err := fn(h) 49 if err != nil { 50 return err 51 } 52 53 switch action { 54 case HookActionContinue: 55 continue 56 case HookActionHalt: 57 // Return an early exit error to trigger an early exit 58 log.Printf("[WARN] Early exit triggered by hook: %T", h) 59 return EvalEarlyExitError{} 60 } 61 } 62 63 return nil 64 } 65 66 func (ctx *BuiltinEvalContext) Input() UIInput { 67 return ctx.InputValue 68 } 69 70 func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error) { 71 ctx.once.Do(ctx.init) 72 73 // If we already initialized, it is an error 74 if p := ctx.Provider(n); p != nil { 75 return nil, fmt.Errorf("Provider '%s' already initialized", n) 76 } 77 78 // Warning: make sure to acquire these locks AFTER the call to Provider 79 // above, since it also acquires locks. 80 ctx.ProviderLock.Lock() 81 defer ctx.ProviderLock.Unlock() 82 83 providerPath := make([]string, len(ctx.Path())+1) 84 copy(providerPath, ctx.Path()) 85 providerPath[len(providerPath)-1] = n 86 key := PathCacheKey(providerPath) 87 88 typeName := strings.SplitN(n, ".", 2)[0] 89 p, err := ctx.Components.ResourceProvider(typeName, key) 90 if err != nil { 91 return nil, err 92 } 93 94 ctx.ProviderCache[key] = p 95 return p, nil 96 } 97 98 func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider { 99 ctx.once.Do(ctx.init) 100 101 ctx.ProviderLock.Lock() 102 defer ctx.ProviderLock.Unlock() 103 104 providerPath := make([]string, len(ctx.Path())+1) 105 copy(providerPath, ctx.Path()) 106 providerPath[len(providerPath)-1] = n 107 108 return ctx.ProviderCache[PathCacheKey(providerPath)] 109 } 110 111 func (ctx *BuiltinEvalContext) CloseProvider(n string) error { 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 var provider interface{} 122 provider = ctx.ProviderCache[PathCacheKey(providerPath)] 123 if provider != nil { 124 if p, ok := provider.(ResourceProviderCloser); ok { 125 delete(ctx.ProviderCache, PathCacheKey(providerPath)) 126 return p.Close() 127 } 128 } 129 130 return nil 131 } 132 133 func (ctx *BuiltinEvalContext) ConfigureProvider( 134 n string, cfg *ResourceConfig) error { 135 p := ctx.Provider(n) 136 if p == nil { 137 return fmt.Errorf("Provider '%s' not initialized", n) 138 } 139 140 if err := ctx.SetProviderConfig(n, cfg); err != nil { 141 return nil 142 } 143 144 return p.Configure(cfg) 145 } 146 147 func (ctx *BuiltinEvalContext) SetProviderConfig( 148 n string, cfg *ResourceConfig) error { 149 providerPath := make([]string, len(ctx.Path())+1) 150 copy(providerPath, ctx.Path()) 151 providerPath[len(providerPath)-1] = n 152 153 // Save the configuration 154 ctx.ProviderLock.Lock() 155 ctx.ProviderConfigCache[PathCacheKey(providerPath)] = cfg 156 ctx.ProviderLock.Unlock() 157 158 return nil 159 } 160 161 func (ctx *BuiltinEvalContext) ProviderInput(n string) map[string]interface{} { 162 ctx.ProviderLock.Lock() 163 defer ctx.ProviderLock.Unlock() 164 165 // Make a copy of the path so we can safely edit it 166 path := ctx.Path() 167 pathCopy := make([]string, len(path)+1) 168 copy(pathCopy, path) 169 170 // Go up the tree. 171 for i := len(path) - 1; i >= 0; i-- { 172 pathCopy[i+1] = n 173 k := PathCacheKey(pathCopy[:i+2]) 174 if v, ok := ctx.ProviderInputConfig[k]; ok { 175 return v 176 } 177 } 178 179 return nil 180 } 181 182 func (ctx *BuiltinEvalContext) SetProviderInput(n string, c map[string]interface{}) { 183 providerPath := make([]string, len(ctx.Path())+1) 184 copy(providerPath, ctx.Path()) 185 providerPath[len(providerPath)-1] = n 186 187 // Save the configuration 188 ctx.ProviderLock.Lock() 189 ctx.ProviderInputConfig[PathCacheKey(providerPath)] = c 190 ctx.ProviderLock.Unlock() 191 } 192 193 func (ctx *BuiltinEvalContext) ParentProviderConfig(n string) *ResourceConfig { 194 ctx.ProviderLock.Lock() 195 defer ctx.ProviderLock.Unlock() 196 197 // Make a copy of the path so we can safely edit it 198 path := ctx.Path() 199 pathCopy := make([]string, len(path)+1) 200 copy(pathCopy, path) 201 202 // Go up the tree. 203 for i := len(path) - 1; i >= 0; i-- { 204 pathCopy[i+1] = n 205 k := PathCacheKey(pathCopy[:i+2]) 206 if v, ok := ctx.ProviderConfigCache[k]; ok { 207 return v 208 } 209 } 210 211 return nil 212 } 213 214 func (ctx *BuiltinEvalContext) InitProvisioner( 215 n string) (ResourceProvisioner, error) { 216 ctx.once.Do(ctx.init) 217 218 // If we already initialized, it is an error 219 if p := ctx.Provisioner(n); p != nil { 220 return nil, fmt.Errorf("Provisioner '%s' already initialized", n) 221 } 222 223 // Warning: make sure to acquire these locks AFTER the call to Provisioner 224 // above, since it also acquires locks. 225 ctx.ProvisionerLock.Lock() 226 defer ctx.ProvisionerLock.Unlock() 227 228 provPath := make([]string, len(ctx.Path())+1) 229 copy(provPath, ctx.Path()) 230 provPath[len(provPath)-1] = n 231 key := PathCacheKey(provPath) 232 233 p, err := ctx.Components.ResourceProvisioner(n, key) 234 if err != nil { 235 return nil, err 236 } 237 238 ctx.ProvisionerCache[key] = p 239 return p, nil 240 } 241 242 func (ctx *BuiltinEvalContext) Provisioner(n string) ResourceProvisioner { 243 ctx.once.Do(ctx.init) 244 245 ctx.ProvisionerLock.Lock() 246 defer ctx.ProvisionerLock.Unlock() 247 248 provPath := make([]string, len(ctx.Path())+1) 249 copy(provPath, ctx.Path()) 250 provPath[len(provPath)-1] = n 251 252 return ctx.ProvisionerCache[PathCacheKey(provPath)] 253 } 254 255 func (ctx *BuiltinEvalContext) CloseProvisioner(n string) error { 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 var prov interface{} 266 prov = ctx.ProvisionerCache[PathCacheKey(provPath)] 267 if prov != nil { 268 if p, ok := prov.(ResourceProvisionerCloser); ok { 269 delete(ctx.ProvisionerCache, PathCacheKey(provPath)) 270 return p.Close() 271 } 272 } 273 274 return nil 275 } 276 277 func (ctx *BuiltinEvalContext) Interpolate( 278 cfg *config.RawConfig, r *Resource) (*ResourceConfig, error) { 279 if cfg != nil { 280 scope := &InterpolationScope{ 281 Path: ctx.Path(), 282 Resource: r, 283 } 284 285 vs, err := ctx.Interpolater.Values(scope, cfg.Variables) 286 if err != nil { 287 return nil, err 288 } 289 290 // Do the interpolation 291 if err := cfg.Interpolate(vs); err != nil { 292 return nil, err 293 } 294 } 295 296 result := NewResourceConfig(cfg) 297 result.interpolateForce() 298 return result, nil 299 } 300 301 func (ctx *BuiltinEvalContext) Path() []string { 302 return ctx.PathValue 303 } 304 305 func (ctx *BuiltinEvalContext) SetVariables(n string, vs map[string]interface{}) { 306 ctx.InterpolaterVarLock.Lock() 307 defer ctx.InterpolaterVarLock.Unlock() 308 309 path := make([]string, len(ctx.Path())+1) 310 copy(path, ctx.Path()) 311 path[len(path)-1] = n 312 key := PathCacheKey(path) 313 314 vars := ctx.InterpolaterVars[key] 315 if vars == nil { 316 vars = make(map[string]interface{}) 317 ctx.InterpolaterVars[key] = vars 318 } 319 320 for k, v := range vs { 321 vars[k] = v 322 } 323 } 324 325 func (ctx *BuiltinEvalContext) Diff() (*Diff, *sync.RWMutex) { 326 return ctx.DiffValue, ctx.DiffLock 327 } 328 329 func (ctx *BuiltinEvalContext) State() (*State, *sync.RWMutex) { 330 return ctx.StateValue, ctx.StateLock 331 } 332 333 func (ctx *BuiltinEvalContext) init() { 334 }