github.com/cycloidio/terraform@v1.1.10-0.20220513142504-76d5c768dc63/terraform/eval_context_builtin.go (about) 1 package terraform 2 3 import ( 4 "context" 5 "fmt" 6 "log" 7 "sync" 8 9 "github.com/cycloidio/terraform/instances" 10 "github.com/cycloidio/terraform/plans" 11 "github.com/cycloidio/terraform/providers" 12 "github.com/cycloidio/terraform/provisioners" 13 "github.com/cycloidio/terraform/refactoring" 14 "github.com/cycloidio/terraform/version" 15 16 "github.com/cycloidio/terraform/states" 17 18 "github.com/hashicorp/hcl/v2" 19 "github.com/cycloidio/terraform/configs/configschema" 20 "github.com/cycloidio/terraform/lang" 21 "github.com/cycloidio/terraform/tfdiags" 22 23 "github.com/cycloidio/terraform/addrs" 24 "github.com/zclconf/go-cty/cty" 25 ) 26 27 // BuiltinEvalContext is an EvalContext implementation that is used by 28 // Terraform by default. 29 type BuiltinEvalContext struct { 30 // StopContext is the context used to track whether we're complete 31 StopContext context.Context 32 33 // PathValue is the Path that this context is operating within. 34 PathValue addrs.ModuleInstance 35 36 // pathSet indicates that this context was explicitly created for a 37 // specific path, and can be safely used for evaluation. This lets us 38 // differentiate between PathValue being unset, and the zero value which is 39 // equivalent to RootModuleInstance. Path and Evaluation methods will 40 // panic if this is not set. 41 pathSet bool 42 43 // Evaluator is used for evaluating expressions within the scope of this 44 // eval context. 45 Evaluator *Evaluator 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 // Plugins is a library of plugin components (providers and provisioners) 57 // available for use during a graph walk. 58 Plugins *contextPlugins 59 60 Hooks []Hook 61 InputValue UIInput 62 ProviderCache map[string]providers.Interface 63 ProviderInputConfig map[string]map[string]cty.Value 64 ProviderLock *sync.Mutex 65 ProvisionerCache map[string]provisioners.Interface 66 ProvisionerLock *sync.Mutex 67 ChangesValue *plans.ChangesSync 68 StateValue *states.SyncState 69 RefreshStateValue *states.SyncState 70 PrevRunStateValue *states.SyncState 71 InstanceExpanderValue *instances.Expander 72 MoveResultsValue refactoring.MoveResults 73 } 74 75 // BuiltinEvalContext implements EvalContext 76 var _ EvalContext = (*BuiltinEvalContext)(nil) 77 78 func (ctx *BuiltinEvalContext) WithPath(path addrs.ModuleInstance) EvalContext { 79 newCtx := *ctx 80 newCtx.pathSet = true 81 newCtx.PathValue = path 82 return &newCtx 83 } 84 85 func (ctx *BuiltinEvalContext) Stopped() <-chan struct{} { 86 // This can happen during tests. During tests, we just block forever. 87 if ctx.StopContext == nil { 88 return nil 89 } 90 91 return ctx.StopContext.Done() 92 } 93 94 func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error { 95 for _, h := range ctx.Hooks { 96 action, err := fn(h) 97 if err != nil { 98 return err 99 } 100 101 switch action { 102 case HookActionContinue: 103 continue 104 case HookActionHalt: 105 // Return an early exit error to trigger an early exit 106 log.Printf("[WARN] Early exit triggered by hook: %T", h) 107 return nil 108 } 109 } 110 111 return nil 112 } 113 114 func (ctx *BuiltinEvalContext) Input() UIInput { 115 return ctx.InputValue 116 } 117 118 func (ctx *BuiltinEvalContext) InitProvider(addr addrs.AbsProviderConfig) (providers.Interface, error) { 119 // If we already initialized, it is an error 120 if p := ctx.Provider(addr); p != nil { 121 return nil, fmt.Errorf("%s is already initialized", addr) 122 } 123 124 // Warning: make sure to acquire these locks AFTER the call to Provider 125 // above, since it also acquires locks. 126 ctx.ProviderLock.Lock() 127 defer ctx.ProviderLock.Unlock() 128 129 key := addr.String() 130 131 p, err := ctx.Plugins.NewProviderInstance(addr.Provider) 132 if err != nil { 133 return nil, err 134 } 135 136 log.Printf("[TRACE] BuiltinEvalContext: Initialized %q provider for %s", addr.String(), addr) 137 ctx.ProviderCache[key] = p 138 139 return p, nil 140 } 141 142 func (ctx *BuiltinEvalContext) Provider(addr addrs.AbsProviderConfig) providers.Interface { 143 ctx.ProviderLock.Lock() 144 defer ctx.ProviderLock.Unlock() 145 146 return ctx.ProviderCache[addr.String()] 147 } 148 149 func (ctx *BuiltinEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) (*ProviderSchema, error) { 150 return ctx.Plugins.ProviderSchema(addr.Provider) 151 } 152 153 func (ctx *BuiltinEvalContext) CloseProvider(addr addrs.AbsProviderConfig) error { 154 ctx.ProviderLock.Lock() 155 defer ctx.ProviderLock.Unlock() 156 157 key := addr.String() 158 provider := ctx.ProviderCache[key] 159 if provider != nil { 160 delete(ctx.ProviderCache, key) 161 return provider.Close() 162 } 163 164 return nil 165 } 166 167 func (ctx *BuiltinEvalContext) ConfigureProvider(addr addrs.AbsProviderConfig, cfg cty.Value) tfdiags.Diagnostics { 168 var diags tfdiags.Diagnostics 169 if !addr.Module.Equal(ctx.Path().Module()) { 170 // This indicates incorrect use of ConfigureProvider: it should be used 171 // only from the module that the provider configuration belongs to. 172 panic(fmt.Sprintf("%s configured by wrong module %s", addr, ctx.Path())) 173 } 174 175 p := ctx.Provider(addr) 176 if p == nil { 177 diags = diags.Append(fmt.Errorf("%s not initialized", addr)) 178 return diags 179 } 180 181 providerSchema, err := ctx.ProviderSchema(addr) 182 if err != nil { 183 diags = diags.Append(fmt.Errorf("failed to read schema for %s: %s", addr, err)) 184 return diags 185 } 186 if providerSchema == nil { 187 diags = diags.Append(fmt.Errorf("schema for %s is not available", addr)) 188 return diags 189 } 190 191 req := providers.ConfigureProviderRequest{ 192 TerraformVersion: version.String(), 193 Config: cfg, 194 } 195 196 resp := p.ConfigureProvider(req) 197 return resp.Diagnostics 198 } 199 200 func (ctx *BuiltinEvalContext) ProviderInput(pc addrs.AbsProviderConfig) map[string]cty.Value { 201 ctx.ProviderLock.Lock() 202 defer ctx.ProviderLock.Unlock() 203 204 if !pc.Module.Equal(ctx.Path().Module()) { 205 // This indicates incorrect use of InitProvider: it should be used 206 // only from the module that the provider configuration belongs to. 207 panic(fmt.Sprintf("%s initialized by wrong module %s", pc, ctx.Path())) 208 } 209 210 if !ctx.Path().IsRoot() { 211 // Only root module provider configurations can have input. 212 return nil 213 } 214 215 return ctx.ProviderInputConfig[pc.String()] 216 } 217 218 func (ctx *BuiltinEvalContext) SetProviderInput(pc addrs.AbsProviderConfig, c map[string]cty.Value) { 219 absProvider := pc 220 if !pc.Module.IsRoot() { 221 // Only root module provider configurations can have input. 222 log.Printf("[WARN] BuiltinEvalContext: attempt to SetProviderInput for non-root module") 223 return 224 } 225 226 // Save the configuration 227 ctx.ProviderLock.Lock() 228 ctx.ProviderInputConfig[absProvider.String()] = c 229 ctx.ProviderLock.Unlock() 230 } 231 232 func (ctx *BuiltinEvalContext) Provisioner(n string) (provisioners.Interface, error) { 233 ctx.ProvisionerLock.Lock() 234 defer ctx.ProvisionerLock.Unlock() 235 236 p, ok := ctx.ProvisionerCache[n] 237 if !ok { 238 var err error 239 p, err = ctx.Plugins.NewProvisionerInstance(n) 240 if err != nil { 241 return nil, err 242 } 243 244 ctx.ProvisionerCache[n] = p 245 } 246 247 return p, nil 248 } 249 250 func (ctx *BuiltinEvalContext) ProvisionerSchema(n string) (*configschema.Block, error) { 251 return ctx.Plugins.ProvisionerSchema(n) 252 } 253 254 func (ctx *BuiltinEvalContext) CloseProvisioners() error { 255 var diags tfdiags.Diagnostics 256 ctx.ProvisionerLock.Lock() 257 defer ctx.ProvisionerLock.Unlock() 258 259 for name, prov := range ctx.ProvisionerCache { 260 err := prov.Close() 261 if err != nil { 262 diags = diags.Append(fmt.Errorf("provisioner.Close %s: %s", name, err)) 263 } 264 } 265 266 return diags.Err() 267 } 268 269 func (ctx *BuiltinEvalContext) EvaluateBlock(body hcl.Body, schema *configschema.Block, self addrs.Referenceable, keyData InstanceKeyEvalData) (cty.Value, hcl.Body, tfdiags.Diagnostics) { 270 var diags tfdiags.Diagnostics 271 scope := ctx.EvaluationScope(self, keyData) 272 body, evalDiags := scope.ExpandBlock(body, schema) 273 diags = diags.Append(evalDiags) 274 val, evalDiags := scope.EvalBlock(body, schema) 275 diags = diags.Append(evalDiags) 276 return val, body, diags 277 } 278 279 func (ctx *BuiltinEvalContext) EvaluateExpr(expr hcl.Expression, wantType cty.Type, self addrs.Referenceable) (cty.Value, tfdiags.Diagnostics) { 280 scope := ctx.EvaluationScope(self, EvalDataForNoInstanceKey) 281 return scope.EvalExpr(expr, wantType) 282 } 283 284 func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, keyData InstanceKeyEvalData) *lang.Scope { 285 if !ctx.pathSet { 286 panic("context path not set") 287 } 288 data := &evaluationStateData{ 289 Evaluator: ctx.Evaluator, 290 ModulePath: ctx.PathValue, 291 InstanceKeyData: keyData, 292 Operation: ctx.Evaluator.Operation, 293 } 294 scope := ctx.Evaluator.Scope(data, self) 295 296 // ctx.PathValue is the path of the module that contains whatever 297 // expression the caller will be trying to evaluate, so this will 298 // activate only the experiments from that particular module, to 299 // be consistent with how experiment checking in the "configs" 300 // package itself works. The nil check here is for robustness in 301 // incompletely-mocked testing situations; mc should never be nil in 302 // real situations. 303 if mc := ctx.Evaluator.Config.DescendentForInstance(ctx.PathValue); mc != nil { 304 scope.SetActiveExperiments(mc.Module.ActiveExperiments) 305 } 306 return scope 307 } 308 309 func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance { 310 if !ctx.pathSet { 311 panic("context path not set") 312 } 313 return ctx.PathValue 314 } 315 316 func (ctx *BuiltinEvalContext) SetModuleCallArguments(n addrs.ModuleCallInstance, vals map[string]cty.Value) { 317 ctx.VariableValuesLock.Lock() 318 defer ctx.VariableValuesLock.Unlock() 319 320 if !ctx.pathSet { 321 panic("context path not set") 322 } 323 324 childPath := n.ModuleInstance(ctx.PathValue) 325 key := childPath.String() 326 327 args := ctx.VariableValues[key] 328 if args == nil { 329 ctx.VariableValues[key] = vals 330 return 331 } 332 333 for k, v := range vals { 334 args[k] = v 335 } 336 } 337 338 func (ctx *BuiltinEvalContext) GetVariableValue(addr addrs.AbsInputVariableInstance) cty.Value { 339 ctx.VariableValuesLock.Lock() 340 defer ctx.VariableValuesLock.Unlock() 341 342 modKey := addr.Module.String() 343 modVars := ctx.VariableValues[modKey] 344 val, ok := modVars[addr.Variable.Name] 345 if !ok { 346 return cty.DynamicVal 347 } 348 return val 349 } 350 351 func (ctx *BuiltinEvalContext) Changes() *plans.ChangesSync { 352 return ctx.ChangesValue 353 } 354 355 func (ctx *BuiltinEvalContext) State() *states.SyncState { 356 return ctx.StateValue 357 } 358 359 func (ctx *BuiltinEvalContext) RefreshState() *states.SyncState { 360 return ctx.RefreshStateValue 361 } 362 363 func (ctx *BuiltinEvalContext) PrevRunState() *states.SyncState { 364 return ctx.PrevRunStateValue 365 } 366 367 func (ctx *BuiltinEvalContext) InstanceExpander() *instances.Expander { 368 return ctx.InstanceExpanderValue 369 } 370 371 func (ctx *BuiltinEvalContext) MoveResults() refactoring.MoveResults { 372 return ctx.MoveResultsValue 373 }