mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-31 11:17:25 -06:00
db7596c045
Use the configured providers directly, rather than looking for inherited provider configuration during graph evaluation. First remove the provider config cache, and the associated SetProviderConfig and ParentProviderConfig methods on the eval context. Every provider must be configured, so there's no need to look for configuration from other provider instances. The config.ProviderConfig struct now has a Scope field which stores the proper path for the interpolation scope. To get this metadata to the interpolator, we add an EvalInterpolatProvider node which can carry the ProviderConfig, and an InterpolateProvider context method to carry the ProviderConfig.Scope into the InterplationScope. Some of the tests could be adjusted to account for the new inheritance behavior, and some were simply no longer valid and will be removed. The remaining tests have questions on how they should work in practice. This mostly concerns orphaned modules where there is no longer a way to obtain a provider. In some cases we may require that a minimal provider config be present to handle the destroy process, but we need further testing. All disabled code was commented out in this commit to record any additional comments. The following commit will be a cleanup pass.
385 lines
9.3 KiB
Go
385 lines
9.3 KiB
Go
package terraform
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/hashicorp/terraform/config"
|
|
)
|
|
|
|
// BuiltinEvalContext is an EvalContext implementation that is used by
|
|
// Terraform by default.
|
|
type BuiltinEvalContext struct {
|
|
// StopContext is the context used to track whether we're complete
|
|
StopContext context.Context
|
|
|
|
// PathValue is the Path that this context is operating within.
|
|
PathValue []string
|
|
|
|
// Interpolater setting below affect the interpolation of variables.
|
|
//
|
|
// The InterpolaterVars are the exact value for ${var.foo} values.
|
|
// The map is shared between all contexts and is a mapping of
|
|
// PATH to KEY to VALUE. Because it is shared by all contexts as well
|
|
// as the Interpolater itself, it is protected by InterpolaterVarLock
|
|
// which must be locked during any access to the map.
|
|
Interpolater *Interpolater
|
|
InterpolaterVars map[string]map[string]interface{}
|
|
InterpolaterVarLock *sync.Mutex
|
|
|
|
Components contextComponentFactory
|
|
Hooks []Hook
|
|
InputValue UIInput
|
|
ProviderCache map[string]ResourceProvider
|
|
//ProviderConfigCache map[string]*ResourceConfig
|
|
ProviderInputConfig map[string]map[string]interface{}
|
|
ProviderLock *sync.Mutex
|
|
ProvisionerCache map[string]ResourceProvisioner
|
|
ProvisionerLock *sync.Mutex
|
|
DiffValue *Diff
|
|
DiffLock *sync.RWMutex
|
|
StateValue *State
|
|
StateLock *sync.RWMutex
|
|
|
|
once sync.Once
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) Stopped() <-chan struct{} {
|
|
// This can happen during tests. During tests, we just block forever.
|
|
if ctx.StopContext == nil {
|
|
return nil
|
|
}
|
|
|
|
return ctx.StopContext.Done()
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error {
|
|
for _, h := range ctx.Hooks {
|
|
action, err := fn(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch action {
|
|
case HookActionContinue:
|
|
continue
|
|
case HookActionHalt:
|
|
// Return an early exit error to trigger an early exit
|
|
log.Printf("[WARN] Early exit triggered by hook: %T", h)
|
|
return EvalEarlyExitError{}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) Input() UIInput {
|
|
return ctx.InputValue
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error) {
|
|
ctx.once.Do(ctx.init)
|
|
|
|
// If we already initialized, it is an error
|
|
if p := ctx.Provider(n); p != nil {
|
|
return nil, fmt.Errorf("Provider '%s' already initialized", n)
|
|
}
|
|
|
|
// Warning: make sure to acquire these locks AFTER the call to Provider
|
|
// above, since it also acquires locks.
|
|
ctx.ProviderLock.Lock()
|
|
defer ctx.ProviderLock.Unlock()
|
|
|
|
providerPath := make([]string, len(ctx.Path())+1)
|
|
copy(providerPath, ctx.Path())
|
|
providerPath[len(providerPath)-1] = n
|
|
key := PathCacheKey(providerPath)
|
|
|
|
typeName := strings.SplitN(n, ".", 2)[0]
|
|
p, err := ctx.Components.ResourceProvider(typeName, key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ctx.ProviderCache[key] = p
|
|
return p, nil
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider {
|
|
ctx.once.Do(ctx.init)
|
|
|
|
ctx.ProviderLock.Lock()
|
|
defer ctx.ProviderLock.Unlock()
|
|
|
|
providerPath := make([]string, len(ctx.Path())+1)
|
|
copy(providerPath, ctx.Path())
|
|
providerPath[len(providerPath)-1] = n
|
|
|
|
return ctx.ProviderCache[PathCacheKey(providerPath)]
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) CloseProvider(n string) error {
|
|
ctx.once.Do(ctx.init)
|
|
|
|
ctx.ProviderLock.Lock()
|
|
defer ctx.ProviderLock.Unlock()
|
|
|
|
providerPath := make([]string, len(ctx.Path())+1)
|
|
copy(providerPath, ctx.Path())
|
|
providerPath[len(providerPath)-1] = n
|
|
|
|
var provider interface{}
|
|
provider = ctx.ProviderCache[PathCacheKey(providerPath)]
|
|
if provider != nil {
|
|
if p, ok := provider.(ResourceProviderCloser); ok {
|
|
delete(ctx.ProviderCache, PathCacheKey(providerPath))
|
|
return p.Close()
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) ConfigureProvider(
|
|
n string, cfg *ResourceConfig) error {
|
|
p := ctx.Provider(n)
|
|
if p == nil {
|
|
return fmt.Errorf("Provider '%s' not initialized", n)
|
|
}
|
|
|
|
//if err := ctx.SetProviderConfig(n, cfg); err != nil {
|
|
// return nil
|
|
//}
|
|
|
|
return p.Configure(cfg)
|
|
}
|
|
|
|
// REMOVING: each provider will contain its own configuration
|
|
//func (ctx *BuiltinEvalContext) SetProviderConfig(
|
|
// n string, cfg *ResourceConfig) error {
|
|
// providerPath := make([]string, len(ctx.Path())+1)
|
|
// copy(providerPath, ctx.Path())
|
|
// providerPath[len(providerPath)-1] = n
|
|
|
|
// // Save the configuration
|
|
// ctx.ProviderLock.Lock()
|
|
// ctx.ProviderConfigCache[PathCacheKey(providerPath)] = cfg
|
|
// ctx.ProviderLock.Unlock()
|
|
|
|
// return nil
|
|
//}
|
|
|
|
func (ctx *BuiltinEvalContext) ProviderInput(n string) map[string]interface{} {
|
|
ctx.ProviderLock.Lock()
|
|
defer ctx.ProviderLock.Unlock()
|
|
|
|
// Make a copy of the path so we can safely edit it
|
|
path := ctx.Path()
|
|
pathCopy := make([]string, len(path)+1)
|
|
copy(pathCopy, path)
|
|
|
|
// Go up the tree.
|
|
for i := len(path) - 1; i >= 0; i-- {
|
|
pathCopy[i+1] = n
|
|
k := PathCacheKey(pathCopy[:i+2])
|
|
if v, ok := ctx.ProviderInputConfig[k]; ok {
|
|
return v
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) SetProviderInput(n string, c map[string]interface{}) {
|
|
providerPath := make([]string, len(ctx.Path())+1)
|
|
copy(providerPath, ctx.Path())
|
|
providerPath[len(providerPath)-1] = n
|
|
|
|
// Save the configuration
|
|
ctx.ProviderLock.Lock()
|
|
ctx.ProviderInputConfig[PathCacheKey(providerPath)] = c
|
|
ctx.ProviderLock.Unlock()
|
|
}
|
|
|
|
// REMOVING: Each provider will contain its own configuration
|
|
//func (ctx *BuiltinEvalContext) ParentProviderConfig(n string) *ResourceConfig {
|
|
// ctx.ProviderLock.Lock()
|
|
// defer ctx.ProviderLock.Unlock()
|
|
|
|
// // Make a copy of the path so we can safely edit it
|
|
// path := ctx.Path()
|
|
// pathCopy := make([]string, len(path)+1)
|
|
// copy(pathCopy, path)
|
|
|
|
// // Go up the tree.
|
|
// for i := len(path) - 1; i >= 0; i-- {
|
|
// pathCopy[i+1] = n
|
|
// k := PathCacheKey(pathCopy[:i+2])
|
|
// if v, ok := ctx.ProviderConfigCache[k]; ok {
|
|
// return v
|
|
// }
|
|
// }
|
|
|
|
// return nil
|
|
//}
|
|
|
|
func (ctx *BuiltinEvalContext) InitProvisioner(
|
|
n string) (ResourceProvisioner, error) {
|
|
ctx.once.Do(ctx.init)
|
|
|
|
// If we already initialized, it is an error
|
|
if p := ctx.Provisioner(n); p != nil {
|
|
return nil, fmt.Errorf("Provisioner '%s' already initialized", n)
|
|
}
|
|
|
|
// Warning: make sure to acquire these locks AFTER the call to Provisioner
|
|
// above, since it also acquires locks.
|
|
ctx.ProvisionerLock.Lock()
|
|
defer ctx.ProvisionerLock.Unlock()
|
|
|
|
provPath := make([]string, len(ctx.Path())+1)
|
|
copy(provPath, ctx.Path())
|
|
provPath[len(provPath)-1] = n
|
|
key := PathCacheKey(provPath)
|
|
|
|
p, err := ctx.Components.ResourceProvisioner(n, key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ctx.ProvisionerCache[key] = p
|
|
return p, nil
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) Provisioner(n string) ResourceProvisioner {
|
|
ctx.once.Do(ctx.init)
|
|
|
|
ctx.ProvisionerLock.Lock()
|
|
defer ctx.ProvisionerLock.Unlock()
|
|
|
|
provPath := make([]string, len(ctx.Path())+1)
|
|
copy(provPath, ctx.Path())
|
|
provPath[len(provPath)-1] = n
|
|
|
|
return ctx.ProvisionerCache[PathCacheKey(provPath)]
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) CloseProvisioner(n string) error {
|
|
ctx.once.Do(ctx.init)
|
|
|
|
ctx.ProvisionerLock.Lock()
|
|
defer ctx.ProvisionerLock.Unlock()
|
|
|
|
provPath := make([]string, len(ctx.Path())+1)
|
|
copy(provPath, ctx.Path())
|
|
provPath[len(provPath)-1] = n
|
|
|
|
var prov interface{}
|
|
prov = ctx.ProvisionerCache[PathCacheKey(provPath)]
|
|
if prov != nil {
|
|
if p, ok := prov.(ResourceProvisionerCloser); ok {
|
|
delete(ctx.ProvisionerCache, PathCacheKey(provPath))
|
|
return p.Close()
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) Interpolate(
|
|
cfg *config.RawConfig, r *Resource) (*ResourceConfig, error) {
|
|
|
|
if cfg != nil {
|
|
scope := &InterpolationScope{
|
|
Path: ctx.Path(),
|
|
Resource: r,
|
|
}
|
|
|
|
vs, err := ctx.Interpolater.Values(scope, cfg.Variables)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Do the interpolation
|
|
if err := cfg.Interpolate(vs); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
result := NewResourceConfig(cfg)
|
|
result.interpolateForce()
|
|
return result, nil
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) InterpolateProvider(
|
|
pc *config.ProviderConfig, r *Resource) (*ResourceConfig, error) {
|
|
|
|
var cfg *config.RawConfig
|
|
|
|
if pc != nil && pc.RawConfig != nil {
|
|
path := pc.Scope
|
|
if len(path) == 0 {
|
|
path = ctx.Path()
|
|
}
|
|
|
|
scope := &InterpolationScope{
|
|
Path: path,
|
|
Resource: r,
|
|
}
|
|
|
|
cfg = pc.RawConfig
|
|
|
|
vs, err := ctx.Interpolater.Values(scope, cfg.Variables)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Do the interpolation
|
|
if err := cfg.Interpolate(vs); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
result := NewResourceConfig(cfg)
|
|
result.interpolateForce()
|
|
return result, nil
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) Path() []string {
|
|
return ctx.PathValue
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) SetVariables(n string, vs map[string]interface{}) {
|
|
ctx.InterpolaterVarLock.Lock()
|
|
defer ctx.InterpolaterVarLock.Unlock()
|
|
|
|
path := make([]string, len(ctx.Path())+1)
|
|
copy(path, ctx.Path())
|
|
path[len(path)-1] = n
|
|
key := PathCacheKey(path)
|
|
|
|
vars := ctx.InterpolaterVars[key]
|
|
if vars == nil {
|
|
vars = make(map[string]interface{})
|
|
ctx.InterpolaterVars[key] = vars
|
|
}
|
|
|
|
for k, v := range vs {
|
|
vars[k] = v
|
|
}
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) Diff() (*Diff, *sync.RWMutex) {
|
|
return ctx.DiffValue, ctx.DiffLock
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) State() (*State, *sync.RWMutex) {
|
|
return ctx.StateValue, ctx.StateLock
|
|
}
|
|
|
|
func (ctx *BuiltinEvalContext) init() {
|
|
}
|