1package terraform 2 3import ( 4 "context" 5 "fmt" 6 "log" 7 "sync" 8 9 "github.com/hashicorp/terraform/plans" 10 "github.com/hashicorp/terraform/providers" 11 "github.com/hashicorp/terraform/provisioners" 12 "github.com/hashicorp/terraform/version" 13 14 "github.com/hashicorp/terraform/states" 15 16 "github.com/hashicorp/hcl2/hcl" 17 "github.com/hashicorp/terraform/configs/configschema" 18 "github.com/hashicorp/terraform/lang" 19 "github.com/hashicorp/terraform/tfdiags" 20 21 "github.com/hashicorp/terraform/addrs" 22 "github.com/zclconf/go-cty/cty" 23) 24 25// BuiltinEvalContext is an EvalContext implementation that is used by 26// Terraform by default. 27type BuiltinEvalContext struct { 28 // StopContext is the context used to track whether we're complete 29 StopContext context.Context 30 31 // PathValue is the Path that this context is operating within. 32 PathValue addrs.ModuleInstance 33 34 // Evaluator is used for evaluating expressions within the scope of this 35 // eval context. 36 Evaluator *Evaluator 37 38 // Schemas is a repository of all of the schemas we should need to 39 // decode configuration blocks and expressions. This must be constructed by 40 // the caller to include schemas for all of the providers, resource types, 41 // data sources and provisioners used by the given configuration and 42 // state. 43 // 44 // This must not be mutated during evaluation. 45 Schemas *Schemas 46 47 // VariableValues contains the variable values across all modules. This 48 // structure is shared across the entire containing context, and so it 49 // may be accessed only when holding VariableValuesLock. 50 // The keys of the first level of VariableValues are the string 51 // representations of addrs.ModuleInstance values. The second-level keys 52 // are variable names within each module instance. 53 VariableValues map[string]map[string]cty.Value 54 VariableValuesLock *sync.Mutex 55 56 Components contextComponentFactory 57 Hooks []Hook 58 InputValue UIInput 59 ProviderCache map[string]providers.Interface 60 ProviderInputConfig map[string]map[string]cty.Value 61 ProviderLock *sync.Mutex 62 ProvisionerCache map[string]provisioners.Interface 63 ProvisionerLock *sync.Mutex 64 ChangesValue *plans.ChangesSync 65 StateValue *states.SyncState 66 67 once sync.Once 68} 69 70// BuiltinEvalContext implements EvalContext 71var _ EvalContext = (*BuiltinEvalContext)(nil) 72 73func (ctx *BuiltinEvalContext) Stopped() <-chan struct{} { 74 // This can happen during tests. During tests, we just block forever. 75 if ctx.StopContext == nil { 76 return nil 77 } 78 79 return ctx.StopContext.Done() 80} 81 82func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error { 83 for _, h := range ctx.Hooks { 84 action, err := fn(h) 85 if err != nil { 86 return err 87 } 88 89 switch action { 90 case HookActionContinue: 91 continue 92 case HookActionHalt: 93 // Return an early exit error to trigger an early exit 94 log.Printf("[WARN] Early exit triggered by hook: %T", h) 95 return EvalEarlyExitError{} 96 } 97 } 98 99 return nil 100} 101 102func (ctx *BuiltinEvalContext) Input() UIInput { 103 return ctx.InputValue 104} 105 106func (ctx *BuiltinEvalContext) InitProvider(typeName string, addr addrs.ProviderConfig) (providers.Interface, error) { 107 ctx.once.Do(ctx.init) 108 absAddr := addr.Absolute(ctx.Path()) 109 110 // If we already initialized, it is an error 111 if p := ctx.Provider(absAddr); p != nil { 112 return nil, fmt.Errorf("%s is already initialized", addr) 113 } 114 115 // Warning: make sure to acquire these locks AFTER the call to Provider 116 // above, since it also acquires locks. 117 ctx.ProviderLock.Lock() 118 defer ctx.ProviderLock.Unlock() 119 120 key := absAddr.String() 121 122 p, err := ctx.Components.ResourceProvider(typeName, key) 123 if err != nil { 124 return nil, err 125 } 126 127 log.Printf("[TRACE] BuiltinEvalContext: Initialized %q provider for %s", typeName, absAddr) 128 ctx.ProviderCache[key] = p 129 130 return p, nil 131} 132 133func (ctx *BuiltinEvalContext) Provider(addr addrs.AbsProviderConfig) providers.Interface { 134 ctx.once.Do(ctx.init) 135 136 ctx.ProviderLock.Lock() 137 defer ctx.ProviderLock.Unlock() 138 139 return ctx.ProviderCache[addr.String()] 140} 141 142func (ctx *BuiltinEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) *ProviderSchema { 143 ctx.once.Do(ctx.init) 144 145 return ctx.Schemas.ProviderSchema(addr.ProviderConfig.Type) 146} 147 148func (ctx *BuiltinEvalContext) CloseProvider(addr addrs.ProviderConfig) error { 149 ctx.once.Do(ctx.init) 150 151 ctx.ProviderLock.Lock() 152 defer ctx.ProviderLock.Unlock() 153 154 key := addr.Absolute(ctx.Path()).String() 155 provider := ctx.ProviderCache[key] 156 if provider != nil { 157 delete(ctx.ProviderCache, key) 158 return provider.Close() 159 } 160 161 return nil 162} 163 164func (ctx *BuiltinEvalContext) ConfigureProvider(addr addrs.ProviderConfig, cfg cty.Value) tfdiags.Diagnostics { 165 var diags tfdiags.Diagnostics 166 absAddr := addr.Absolute(ctx.Path()) 167 p := ctx.Provider(absAddr) 168 if p == nil { 169 diags = diags.Append(fmt.Errorf("%s not initialized", addr)) 170 return diags 171 } 172 173 providerSchema := ctx.ProviderSchema(absAddr) 174 if providerSchema == nil { 175 diags = diags.Append(fmt.Errorf("schema for %s is not available", absAddr)) 176 return diags 177 } 178 179 req := providers.ConfigureRequest{ 180 TerraformVersion: version.String(), 181 Config: cfg, 182 } 183 184 resp := p.Configure(req) 185 return resp.Diagnostics 186} 187 188func (ctx *BuiltinEvalContext) ProviderInput(pc addrs.ProviderConfig) map[string]cty.Value { 189 ctx.ProviderLock.Lock() 190 defer ctx.ProviderLock.Unlock() 191 192 if !ctx.Path().IsRoot() { 193 // Only root module provider configurations can have input. 194 return nil 195 } 196 197 return ctx.ProviderInputConfig[pc.String()] 198} 199 200func (ctx *BuiltinEvalContext) SetProviderInput(pc addrs.ProviderConfig, c map[string]cty.Value) { 201 absProvider := pc.Absolute(ctx.Path()) 202 203 if !ctx.Path().IsRoot() { 204 // Only root module provider configurations can have input. 205 log.Printf("[WARN] BuiltinEvalContext: attempt to SetProviderInput for non-root module") 206 return 207 } 208 209 // Save the configuration 210 ctx.ProviderLock.Lock() 211 ctx.ProviderInputConfig[absProvider.String()] = c 212 ctx.ProviderLock.Unlock() 213} 214 215func (ctx *BuiltinEvalContext) InitProvisioner(n string) (provisioners.Interface, 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 key := PathObjectCacheKey(ctx.Path(), n) 229 230 p, err := ctx.Components.ResourceProvisioner(n, key) 231 if err != nil { 232 return nil, err 233 } 234 235 ctx.ProvisionerCache[key] = p 236 237 return p, nil 238} 239 240func (ctx *BuiltinEvalContext) Provisioner(n string) provisioners.Interface { 241 ctx.once.Do(ctx.init) 242 243 ctx.ProvisionerLock.Lock() 244 defer ctx.ProvisionerLock.Unlock() 245 246 key := PathObjectCacheKey(ctx.Path(), n) 247 return ctx.ProvisionerCache[key] 248} 249 250func (ctx *BuiltinEvalContext) ProvisionerSchema(n string) *configschema.Block { 251 ctx.once.Do(ctx.init) 252 253 return ctx.Schemas.ProvisionerConfig(n) 254} 255 256func (ctx *BuiltinEvalContext) CloseProvisioner(n string) error { 257 ctx.once.Do(ctx.init) 258 259 ctx.ProvisionerLock.Lock() 260 defer ctx.ProvisionerLock.Unlock() 261 262 key := PathObjectCacheKey(ctx.Path(), n) 263 264 prov := ctx.ProvisionerCache[key] 265 if prov != nil { 266 return prov.Close() 267 } 268 269 return nil 270} 271 272func (ctx *BuiltinEvalContext) EvaluateBlock(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, keyData InstanceKeyEvalData) (cty.Value, hcl.Body, tfdiags.Diagnostics) { 273 var diags tfdiags.Diagnostics 274 scope := ctx.EvaluationScope(self, keyData) 275 body, evalDiags := scope.ExpandBlock(body, schema) 276 diags = diags.Append(evalDiags) 277 val, evalDiags := scope.EvalBlock(body, schema) 278 diags = diags.Append(evalDiags) 279 return val, body, diags 280} 281 282func (ctx *BuiltinEvalContext) EvaluateExpr(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) { 283 scope := ctx.EvaluationScope(self, EvalDataForNoInstanceKey) 284 return scope.EvalExpr(expr, wantType) 285} 286 287func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, keyData InstanceKeyEvalData) *lang.Scope { 288 data := &evaluationStateData{ 289 Evaluator: ctx.Evaluator, 290 ModulePath: ctx.PathValue, 291 InstanceKeyData: keyData, 292 Operation: ctx.Evaluator.Operation, 293 } 294 return ctx.Evaluator.Scope(data, self) 295} 296 297func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance { 298 return ctx.PathValue 299} 300 301func (ctx *BuiltinEvalContext) SetModuleCallArguments(n addrs.ModuleCallInstance, vals map[string]cty.Value) { 302 ctx.VariableValuesLock.Lock() 303 defer ctx.VariableValuesLock.Unlock() 304 305 childPath := n.ModuleInstance(ctx.PathValue) 306 key := childPath.String() 307 308 args := ctx.VariableValues[key] 309 if args == nil { 310 args = make(map[string]cty.Value) 311 ctx.VariableValues[key] = vals 312 return 313 } 314 315 for k, v := range vals { 316 args[k] = v 317 } 318} 319 320func (ctx *BuiltinEvalContext) Changes() *plans.ChangesSync { 321 return ctx.ChangesValue 322} 323 324func (ctx *BuiltinEvalContext) State() *states.SyncState { 325 return ctx.StateValue 326} 327 328func (ctx *BuiltinEvalContext) init() { 329} 330