github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/terraform/eval_context_builtin.go (about) 1 package terraform 2 3 import ( 4 "context" 5 "fmt" 6 "log" 7 "sync" 8 9 "github.com/hashicorp/terraform/internal/checks" 10 "github.com/hashicorp/terraform/internal/instances" 11 "github.com/hashicorp/terraform/internal/plans" 12 "github.com/hashicorp/terraform/internal/providers" 13 "github.com/hashicorp/terraform/internal/provisioners" 14 "github.com/hashicorp/terraform/internal/refactoring" 15 "github.com/hashicorp/terraform/version" 16 17 "github.com/hashicorp/terraform/internal/states" 18 19 "github.com/hashicorp/hcl/v2" 20 "github.com/hashicorp/terraform/internal/configs/configschema" 21 "github.com/hashicorp/terraform/internal/lang" 22 "github.com/hashicorp/terraform/internal/tfdiags" 23 24 "github.com/hashicorp/terraform/internal/addrs" 25 "github.com/zclconf/go-cty/cty" 26 ) 27 28 // BuiltinEvalContext is an EvalContext implementation that is used by 29 // Terraform by default. 30 type BuiltinEvalContext struct { 31 // StopContext is the context used to track whether we're complete 32 StopContext context.Context 33 34 // PathValue is the Path that this context is operating within. 35 PathValue addrs.ModuleInstance 36 37 // pathSet indicates that this context was explicitly created for a 38 // specific path, and can be safely used for evaluation. This lets us 39 // differentiate between PathValue being unset, and the zero value which is 40 // equivalent to RootModuleInstance. Path and Evaluation methods will 41 // panic if this is not set. 42 pathSet bool 43 44 // Evaluator is used for evaluating expressions within the scope of this 45 // eval context. 46 Evaluator *Evaluator 47 48 // VariableValues contains the variable values across all modules. This 49 // structure is shared across the entire containing context, and so it 50 // may be accessed only when holding VariableValuesLock. 51 // The keys of the first level of VariableValues are the string 52 // representations of addrs.ModuleInstance values. The second-level keys 53 // are variable names within each module instance. 54 VariableValues map[string]map[string]cty.Value 55 VariableValuesLock *sync.Mutex 56 57 // Plugins is a library of plugin components (providers and provisioners) 58 // available for use during a graph walk. 59 Plugins *contextPlugins 60 61 Hooks []Hook 62 InputValue UIInput 63 ProviderCache map[string]providers.Interface 64 ProviderInputConfig map[string]map[string]cty.Value 65 ProviderLock *sync.Mutex 66 ProvisionerCache map[string]provisioners.Interface 67 ProvisionerLock *sync.Mutex 68 ChangesValue *plans.ChangesSync 69 StateValue *states.SyncState 70 ChecksValue *checks.State 71 RefreshStateValue *states.SyncState 72 PrevRunStateValue *states.SyncState 73 InstanceExpanderValue *instances.Expander 74 MoveResultsValue refactoring.MoveResults 75 } 76 77 // BuiltinEvalContext implements EvalContext 78 var _ EvalContext = (*BuiltinEvalContext)(nil) 79 80 func (ctx *BuiltinEvalContext) WithPath(path addrs.ModuleInstance) EvalContext { 81 newCtx := *ctx 82 newCtx.pathSet = true 83 newCtx.PathValue = path 84 return &newCtx 85 } 86 87 func (ctx *BuiltinEvalContext) Stopped() <-chan struct{} { 88 // This can happen during tests. During tests, we just block forever. 89 if ctx.StopContext == nil { 90 return nil 91 } 92 93 return ctx.StopContext.Done() 94 } 95 96 func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error { 97 for _, h := range ctx.Hooks { 98 action, err := fn(h) 99 if err != nil { 100 return err 101 } 102 103 switch action { 104 case HookActionContinue: 105 continue 106 case HookActionHalt: 107 // Return an early exit error to trigger an early exit 108 log.Printf("[WARN] Early exit triggered by hook: %T", h) 109 return nil 110 } 111 } 112 113 return nil 114 } 115 116 func (ctx *BuiltinEvalContext) Input() UIInput { 117 return ctx.InputValue 118 } 119 120 func (ctx *BuiltinEvalContext) InitProvider(addr addrs.AbsProviderConfig) (providers.Interface, error) { 121 // If we already initialized, it is an error 122 if p := ctx.Provider(addr); p != nil { 123 return nil, fmt.Errorf("%s is already initialized", addr) 124 } 125 126 // Warning: make sure to acquire these locks AFTER the call to Provider 127 // above, since it also acquires locks. 128 ctx.ProviderLock.Lock() 129 defer ctx.ProviderLock.Unlock() 130 131 key := addr.String() 132 133 p, err := ctx.Plugins.NewProviderInstance(addr.Provider) 134 if err != nil { 135 return nil, err 136 } 137 138 log.Printf("[TRACE] BuiltinEvalContext: Initialized %q provider for %s", addr.String(), addr) 139 ctx.ProviderCache[key] = p 140 141 return p, nil 142 } 143 144 func (ctx *BuiltinEvalContext) Provider(addr addrs.AbsProviderConfig) providers.Interface { 145 ctx.ProviderLock.Lock() 146 defer ctx.ProviderLock.Unlock() 147 148 return ctx.ProviderCache[addr.String()] 149 } 150 151 func (ctx *BuiltinEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) (*ProviderSchema, error) { 152 return ctx.Plugins.ProviderSchema(addr.Provider) 153 } 154 155 func (ctx *BuiltinEvalContext) CloseProvider(addr addrs.AbsProviderConfig) error { 156 ctx.ProviderLock.Lock() 157 defer ctx.ProviderLock.Unlock() 158 159 key := addr.String() 160 provider := ctx.ProviderCache[key] 161 if provider != nil { 162 delete(ctx.ProviderCache, key) 163 return provider.Close() 164 } 165 166 return nil 167 } 168 169 func (ctx *BuiltinEvalContext) ConfigureProvider(addr addrs.AbsProviderConfig, cfg cty.Value) tfdiags.Diagnostics { 170 var diags tfdiags.Diagnostics 171 if !addr.Module.Equal(ctx.Path().Module()) { 172 // This indicates incorrect use of ConfigureProvider: it should be used 173 // only from the module that the provider configuration belongs to. 174 panic(fmt.Sprintf("%s configured by wrong module %s", addr, ctx.Path())) 175 } 176 177 p := ctx.Provider(addr) 178 if p == nil { 179 diags = diags.Append(fmt.Errorf("%s not initialized", addr)) 180 return diags 181 } 182 183 providerSchema, err := ctx.ProviderSchema(addr) 184 if err != nil { 185 diags = diags.Append(fmt.Errorf("failed to read schema for %s: %s", addr, err)) 186 return diags 187 } 188 if providerSchema == nil { 189 diags = diags.Append(fmt.Errorf("schema for %s is not available", addr)) 190 return diags 191 } 192 193 req := providers.ConfigureProviderRequest{ 194 TerraformVersion: version.String(), 195 Config: cfg, 196 } 197 198 resp := p.ConfigureProvider(req) 199 return resp.Diagnostics 200 } 201 202 func (ctx *BuiltinEvalContext) ProviderInput(pc addrs.AbsProviderConfig) map[string]cty.Value { 203 ctx.ProviderLock.Lock() 204 defer ctx.ProviderLock.Unlock() 205 206 if !pc.Module.Equal(ctx.Path().Module()) { 207 // This indicates incorrect use of InitProvider: it should be used 208 // only from the module that the provider configuration belongs to. 209 panic(fmt.Sprintf("%s initialized by wrong module %s", pc, ctx.Path())) 210 } 211 212 if !ctx.Path().IsRoot() { 213 // Only root module provider configurations can have input. 214 return nil 215 } 216 217 return ctx.ProviderInputConfig[pc.String()] 218 } 219 220 func (ctx *BuiltinEvalContext) SetProviderInput(pc addrs.AbsProviderConfig, c map[string]cty.Value) { 221 absProvider := pc 222 if !pc.Module.IsRoot() { 223 // Only root module provider configurations can have input. 224 log.Printf("[WARN] BuiltinEvalContext: attempt to SetProviderInput for non-root module") 225 return 226 } 227 228 // Save the configuration 229 ctx.ProviderLock.Lock() 230 ctx.ProviderInputConfig[absProvider.String()] = c 231 ctx.ProviderLock.Unlock() 232 } 233 234 func (ctx *BuiltinEvalContext) Provisioner(n string) (provisioners.Interface, error) { 235 ctx.ProvisionerLock.Lock() 236 defer ctx.ProvisionerLock.Unlock() 237 238 p, ok := ctx.ProvisionerCache[n] 239 if !ok { 240 var err error 241 p, err = ctx.Plugins.NewProvisionerInstance(n) 242 if err != nil { 243 return nil, err 244 } 245 246 ctx.ProvisionerCache[n] = p 247 } 248 249 return p, nil 250 } 251 252 func (ctx *BuiltinEvalContext) ProvisionerSchema(n string) (*configschema.Block, error) { 253 return ctx.Plugins.ProvisionerSchema(n) 254 } 255 256 func (ctx *BuiltinEvalContext) CloseProvisioners() error { 257 var diags tfdiags.Diagnostics 258 ctx.ProvisionerLock.Lock() 259 defer ctx.ProvisionerLock.Unlock() 260 261 for name, prov := range ctx.ProvisionerCache { 262 err := prov.Close() 263 if err != nil { 264 diags = diags.Append(fmt.Errorf("provisioner.Close %s: %s", name, err)) 265 } 266 } 267 268 return diags.Err() 269 } 270 271 func (ctx *BuiltinEvalContext) EvaluateBlock(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, keyData InstanceKeyEvalData) (cty.Value, hcl.Body, tfdiags.Diagnostics) { 272 var diags tfdiags.Diagnostics 273 scope := ctx.EvaluationScope(self, keyData) 274 body, evalDiags := scope.ExpandBlock(body, schema) 275 diags = diags.Append(evalDiags) 276 val, evalDiags := scope.EvalBlock(body, schema) 277 diags = diags.Append(evalDiags) 278 return val, body, diags 279 } 280 281 func (ctx *BuiltinEvalContext) EvaluateExpr(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) { 282 scope := ctx.EvaluationScope(self, EvalDataForNoInstanceKey) 283 return scope.EvalExpr(expr, wantType) 284 } 285 286 func (ctx *BuiltinEvalContext) EvaluateReplaceTriggeredBy(expr hcl.Expression, repData instances.RepetitionData) (*addrs.Reference, bool, tfdiags.Diagnostics) { 287 288 // get the reference to lookup changes in the plan 289 ref, diags := evalReplaceTriggeredByExpr(expr, repData) 290 if diags.HasErrors() { 291 return nil, false, diags 292 } 293 294 var changes []*plans.ResourceInstanceChangeSrc 295 // store the address once we get it for validation 296 var resourceAddr addrs.Resource 297 298 // The reference is either a resource or resource instance 299 switch sub := ref.Subject.(type) { 300 case addrs.Resource: 301 resourceAddr = sub 302 rc := sub.Absolute(ctx.Path()) 303 changes = ctx.Changes().GetChangesForAbsResource(rc) 304 case addrs.ResourceInstance: 305 resourceAddr = sub.ContainingResource() 306 rc := sub.Absolute(ctx.Path()) 307 change := ctx.Changes().GetResourceInstanceChange(rc, states.CurrentGen) 308 if change != nil { 309 // we'll generate an error below if there was no change 310 changes = append(changes, change) 311 } 312 } 313 314 // Do some validation to make sure we are expecting a change at all 315 cfg := ctx.Evaluator.Config.Descendent(ctx.Path().Module()) 316 resCfg := cfg.Module.ResourceByAddr(resourceAddr) 317 if resCfg == nil { 318 diags = diags.Append(&hcl.Diagnostic{ 319 Severity: hcl.DiagError, 320 Summary: `Reference to undeclared resource`, 321 Detail: fmt.Sprintf(`A resource %s has not been declared in %s`, ref.Subject, moduleDisplayAddr(ctx.Path())), 322 Subject: expr.Range().Ptr(), 323 }) 324 return nil, false, diags 325 } 326 327 if len(changes) == 0 { 328 // If the resource is valid there should always be at least one change. 329 diags = diags.Append(fmt.Errorf("no change found for %s in %s", ref.Subject, moduleDisplayAddr(ctx.Path()))) 330 return nil, false, diags 331 } 332 333 // If we don't have a traversal beyond the resource, then we can just look 334 // for any change. 335 if len(ref.Remaining) == 0 { 336 for _, c := range changes { 337 switch c.ChangeSrc.Action { 338 // Only immediate changes to the resource will trigger replacement. 339 case plans.Update, plans.DeleteThenCreate, plans.CreateThenDelete: 340 return ref, true, diags 341 } 342 } 343 344 // no change triggered 345 return nil, false, diags 346 } 347 348 // This must be an instances to have a remaining traversal, which means a 349 // single change. 350 change := changes[0] 351 352 // Make sure the change is actionable. A create or delete action will have 353 // a change in value, but are not valid for our purposes here. 354 switch change.ChangeSrc.Action { 355 case plans.Update, plans.DeleteThenCreate, plans.CreateThenDelete: 356 // OK 357 default: 358 return nil, false, diags 359 } 360 361 // Since we have a traversal after the resource reference, we will need to 362 // decode the changes, which means we need a schema. 363 providerAddr := change.ProviderAddr 364 schema, err := ctx.ProviderSchema(providerAddr) 365 if err != nil { 366 diags = diags.Append(err) 367 return nil, false, diags 368 } 369 370 resAddr := change.Addr.ContainingResource().Resource 371 resSchema, _ := schema.SchemaForResourceType(resAddr.Mode, resAddr.Type) 372 ty := resSchema.ImpliedType() 373 374 before, err := change.ChangeSrc.Before.Decode(ty) 375 if err != nil { 376 diags = diags.Append(err) 377 return nil, false, diags 378 } 379 380 after, err := change.ChangeSrc.After.Decode(ty) 381 if err != nil { 382 diags = diags.Append(err) 383 return nil, false, diags 384 } 385 386 path := traversalToPath(ref.Remaining) 387 attrBefore, _ := path.Apply(before) 388 attrAfter, _ := path.Apply(after) 389 390 if attrBefore == cty.NilVal || attrAfter == cty.NilVal { 391 replace := attrBefore != attrAfter 392 return ref, replace, diags 393 } 394 395 replace := !attrBefore.RawEquals(attrAfter) 396 397 return ref, replace, diags 398 } 399 400 func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, keyData instances.RepetitionData) *lang.Scope { 401 if !ctx.pathSet { 402 panic("context path not set") 403 } 404 data := &evaluationStateData{ 405 Evaluator: ctx.Evaluator, 406 ModulePath: ctx.PathValue, 407 InstanceKeyData: keyData, 408 Operation: ctx.Evaluator.Operation, 409 } 410 scope := ctx.Evaluator.Scope(data, self) 411 412 // ctx.PathValue is the path of the module that contains whatever 413 // expression the caller will be trying to evaluate, so this will 414 // activate only the experiments from that particular module, to 415 // be consistent with how experiment checking in the "configs" 416 // package itself works. The nil check here is for robustness in 417 // incompletely-mocked testing situations; mc should never be nil in 418 // real situations. 419 if mc := ctx.Evaluator.Config.DescendentForInstance(ctx.PathValue); mc != nil { 420 scope.SetActiveExperiments(mc.Module.ActiveExperiments) 421 } 422 return scope 423 } 424 425 func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance { 426 if !ctx.pathSet { 427 panic("context path not set") 428 } 429 return ctx.PathValue 430 } 431 432 func (ctx *BuiltinEvalContext) SetRootModuleArgument(addr addrs.InputVariable, v cty.Value) { 433 ctx.VariableValuesLock.Lock() 434 defer ctx.VariableValuesLock.Unlock() 435 436 log.Printf("[TRACE] BuiltinEvalContext: Storing final value for variable %s", addr.Absolute(addrs.RootModuleInstance)) 437 key := addrs.RootModuleInstance.String() 438 args := ctx.VariableValues[key] 439 if args == nil { 440 args = make(map[string]cty.Value) 441 ctx.VariableValues[key] = args 442 } 443 args[addr.Name] = v 444 } 445 446 func (ctx *BuiltinEvalContext) SetModuleCallArgument(callAddr addrs.ModuleCallInstance, varAddr addrs.InputVariable, v cty.Value) { 447 ctx.VariableValuesLock.Lock() 448 defer ctx.VariableValuesLock.Unlock() 449 450 if !ctx.pathSet { 451 panic("context path not set") 452 } 453 454 childPath := callAddr.ModuleInstance(ctx.PathValue) 455 log.Printf("[TRACE] BuiltinEvalContext: Storing final value for variable %s", varAddr.Absolute(childPath)) 456 key := childPath.String() 457 args := ctx.VariableValues[key] 458 if args == nil { 459 args = make(map[string]cty.Value) 460 ctx.VariableValues[key] = args 461 } 462 args[varAddr.Name] = v 463 } 464 465 func (ctx *BuiltinEvalContext) GetVariableValue(addr addrs.AbsInputVariableInstance) cty.Value { 466 ctx.VariableValuesLock.Lock() 467 defer ctx.VariableValuesLock.Unlock() 468 469 modKey := addr.Module.String() 470 modVars := ctx.VariableValues[modKey] 471 val, ok := modVars[addr.Variable.Name] 472 if !ok { 473 return cty.DynamicVal 474 } 475 return val 476 } 477 478 func (ctx *BuiltinEvalContext) Changes() *plans.ChangesSync { 479 return ctx.ChangesValue 480 } 481 482 func (ctx *BuiltinEvalContext) State() *states.SyncState { 483 return ctx.StateValue 484 } 485 486 func (ctx *BuiltinEvalContext) Checks() *checks.State { 487 return ctx.ChecksValue 488 } 489 490 func (ctx *BuiltinEvalContext) RefreshState() *states.SyncState { 491 return ctx.RefreshStateValue 492 } 493 494 func (ctx *BuiltinEvalContext) PrevRunState() *states.SyncState { 495 return ctx.PrevRunStateValue 496 } 497 498 func (ctx *BuiltinEvalContext) InstanceExpander() *instances.Expander { 499 return ctx.InstanceExpanderValue 500 } 501 502 func (ctx *BuiltinEvalContext) MoveResults() refactoring.MoveResults { 503 return ctx.MoveResultsValue 504 }