github.com/hashicorp/terraform-plugin-sdk@v1.17.2/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-plugin-sdk/internal/plans" 10 "github.com/hashicorp/terraform-plugin-sdk/internal/providers" 11 "github.com/hashicorp/terraform-plugin-sdk/internal/provisioners" 12 "github.com/hashicorp/terraform-plugin-sdk/internal/version" 13 14 "github.com/hashicorp/terraform-plugin-sdk/internal/states" 15 16 "github.com/hashicorp/hcl/v2" 17 "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" 18 "github.com/hashicorp/terraform-plugin-sdk/internal/lang" 19 "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" 20 21 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 22 "github.com/zclconf/go-cty/cty" 23 ) 24 25 // BuiltinEvalContext is an EvalContext implementation that is used by 26 // Terraform by default. 27 type 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 71 var _ EvalContext = (*BuiltinEvalContext)(nil) 72 73 func (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 82 func (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 102 func (ctx *BuiltinEvalContext) Input() UIInput { 103 return ctx.InputValue 104 } 105 106 func (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 133 func (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 142 func (ctx *BuiltinEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) *ProviderSchema { 143 ctx.once.Do(ctx.init) 144 145 return ctx.Schemas.ProviderSchema(addr.ProviderConfig.Type) 146 } 147 148 func (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 164 func (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 188 func (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 200 func (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 215 func (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 240 func (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 250 func (ctx *BuiltinEvalContext) ProvisionerSchema(n string) *configschema.Block { 251 ctx.once.Do(ctx.init) 252 253 return ctx.Schemas.ProvisionerConfig(n) 254 } 255 256 func (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 272 func (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 282 func (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 287 func (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 297 func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance { 298 return ctx.PathValue 299 } 300 301 func (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 320 func (ctx *BuiltinEvalContext) Changes() *plans.ChangesSync { 321 return ctx.ChangesValue 322 } 323 324 func (ctx *BuiltinEvalContext) State() *states.SyncState { 325 return ctx.StateValue 326 } 327 328 func (ctx *BuiltinEvalContext) init() { 329 }