mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
Initial tofu package update for plugin manager
Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
This commit is contained in:
@@ -579,3 +579,16 @@ func ResourceModeBlockName(rm ResourceMode) string {
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
func (r ResourceMode) HumanString() string {
|
||||
switch r {
|
||||
case ManagedResourceMode:
|
||||
return "resource"
|
||||
case DataResourceMode:
|
||||
return "data source"
|
||||
case EphemeralResourceMode:
|
||||
return "ephemeral resource"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
32
internal/plugins/plugins.go
Normal file
32
internal/plugins/plugins.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/opentofu/opentofu/internal/addrs"
|
||||
"github.com/opentofu/opentofu/internal/providers"
|
||||
"github.com/opentofu/opentofu/internal/provisioners"
|
||||
)
|
||||
|
||||
type PluginSchemas interface {
|
||||
ProvisionerSchemas
|
||||
ProviderSchemas
|
||||
}
|
||||
|
||||
type PluginManager interface {
|
||||
ProvisionerManager
|
||||
ProviderManager
|
||||
}
|
||||
|
||||
func NewPluginManager(ctx context.Context,
|
||||
providerFactories map[addrs.Provider]providers.Factory,
|
||||
provisionerFactories map[string]provisioners.Factory,
|
||||
) PluginManager {
|
||||
return struct {
|
||||
ProvisionerManager
|
||||
ProviderManager
|
||||
}{
|
||||
ProvisionerManager: NewProvisionerManager(provisionerFactories),
|
||||
ProviderManager: NewProviderManager(ctx, providerFactories),
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
type ProviderSchema = providers.ProviderSchema
|
||||
|
||||
type ProviderSchemas interface {
|
||||
HasProvider(addr addrs.Provider) bool
|
||||
|
||||
// GetMetadata is not yet implemented or used at this time
|
||||
GetProviderSchema(ctx context.Context, addr addrs.Provider) ProviderSchema
|
||||
|
||||
@@ -56,10 +58,12 @@ type ProviderManager interface {
|
||||
ValidateResourceConfig(ctx context.Context, addr addrs.Provider, mode addrs.ResourceMode, typeName string, cfgVal cty.Value) tfdiags.Diagnostics
|
||||
|
||||
MoveResourceState(ctx context.Context, addr addrs.Provider, req providers.MoveResourceStateRequest) providers.MoveResourceStateResponse
|
||||
CallFunction(ctx context.Context, addr addrs.Provider, name string, arguments []cty.Value) (cty.Value, error)
|
||||
|
||||
ConfigureProvider(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, cfgVal cty.Value) tfdiags.Diagnostics
|
||||
|
||||
IsProviderConfigured(addr addrs.AbsProviderInstanceCorrect) bool
|
||||
ConfiguredProvider(addr addrs.AbsProviderInstanceCorrect) providers.Configured
|
||||
|
||||
UpgradeResourceState(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, req providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse
|
||||
ReadResource(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, req providers.ReadResourceRequest) providers.ReadResourceResponse
|
||||
PlanResourceChange(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse
|
||||
@@ -69,7 +73,10 @@ type ProviderManager interface {
|
||||
OpenEphemeralResource(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, req providers.OpenEphemeralResourceRequest) providers.OpenEphemeralResourceResponse
|
||||
RenewEphemeralResource(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, req providers.RenewEphemeralResourceRequest) providers.RenewEphemeralResourceResponse
|
||||
CloseEphemeralResource(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, req providers.CloseEphemeralResourceRequest) providers.CloseEphemeralResourceResponse
|
||||
|
||||
// These are weird due to
|
||||
GetFunctions(ctx context.Context, addr addrs.AbsProviderInstanceCorrect) providers.GetFunctionsResponse
|
||||
CallFunction(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, name string, arguments []cty.Value) (cty.Value, error)
|
||||
|
||||
CloseProvider(ctx context.Context, addr addrs.AbsProviderInstanceCorrect) error
|
||||
|
||||
|
||||
@@ -35,10 +35,11 @@ type providerUnconfigured struct {
|
||||
active atomic.Int32
|
||||
}
|
||||
|
||||
func NewProviderMananger(ctx context.Context, factories map[addrs.Provider]providers.Factory) ProviderManager {
|
||||
func NewProviderManager(ctx context.Context, factories map[addrs.Provider]providers.Factory) ProviderManager {
|
||||
manager := &providerManager{
|
||||
factories: factories,
|
||||
|
||||
schemas: map[addrs.Provider]ProviderSchema{},
|
||||
unconfigured: map[addrs.Provider]*providerUnconfigured{},
|
||||
configured: map[string]providers.Configured{},
|
||||
}
|
||||
@@ -79,6 +80,11 @@ func NewProviderMananger(ctx context.Context, factories map[addrs.Provider]provi
|
||||
return manager
|
||||
}
|
||||
|
||||
func (p *providerManager) HasProvider(addr addrs.Provider) bool {
|
||||
_, ok := p.factories[addr]
|
||||
return ok
|
||||
}
|
||||
|
||||
// newProviderInst creates a new instance of the given provider.
|
||||
//
|
||||
// The result is not retained anywhere inside the receiver. Each call to this
|
||||
@@ -248,6 +254,15 @@ func (p *providerManager) ResourceTypeSchema(ctx context.Context, addr addrs.Pro
|
||||
if !ok {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
// TODO ret.Block == nil error
|
||||
/*
|
||||
schema, currentVersion := (providerSchema).SchemaForResourceAddr(addr.Resource.ContainingResource())
|
||||
if schema == nil {
|
||||
// Shouldn't happen since we should've failed long ago if no schema is present
|
||||
return nil, diags.Append(fmt.Errorf("no schema available for %s while reading state; this is a bug in OpenTofu and should be reported", addr))
|
||||
}*/
|
||||
|
||||
return &ret, diags
|
||||
}
|
||||
|
||||
@@ -273,15 +288,41 @@ func (p *providerManager) ValidateResourceConfig(ctx context.Context, addr addrs
|
||||
if err != nil {
|
||||
return tfdiags.Diagnostics{}.Append(err)
|
||||
}
|
||||
return inst.ValidateResourceConfig(ctx, providers.ValidateResourceConfigRequest{Config: cfgVal}).Diagnostics
|
||||
|
||||
switch mode {
|
||||
case addrs.ManagedResourceMode:
|
||||
return inst.ValidateResourceConfig(ctx, providers.ValidateResourceConfigRequest{
|
||||
TypeName: typeName,
|
||||
Config: cfgVal,
|
||||
}).Diagnostics
|
||||
case addrs.DataResourceMode:
|
||||
return inst.ValidateDataResourceConfig(ctx, providers.ValidateDataResourceConfigRequest{
|
||||
TypeName: typeName,
|
||||
Config: cfgVal,
|
||||
}).Diagnostics
|
||||
case addrs.EphemeralResourceMode:
|
||||
return inst.ValidateEphemeralConfig(ctx, providers.ValidateEphemeralConfigRequest{
|
||||
TypeName: typeName,
|
||||
Config: cfgVal,
|
||||
}).Diagnostics
|
||||
default:
|
||||
// We don't support any other modes, so we'll just treat these as
|
||||
// a request for a resource type that doesn't exist at all.
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (p *providerManager) MoveResourceState(ctx context.Context, addr addrs.Provider, req providers.MoveResourceStateRequest) providers.MoveResourceStateResponse {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (p *providerManager) CallFunction(ctx context.Context, addr addrs.Provider, name string, arguments []cty.Value) (cty.Value, error) {
|
||||
panic("not implemented") // TODO: Implement
|
||||
func (p *providerManager) IsProviderConfigured(addr addrs.AbsProviderInstanceCorrect) bool {
|
||||
p.configuredLock.Lock()
|
||||
defer p.configuredLock.Unlock()
|
||||
|
||||
_, ok := p.configured[addr.String()]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (p *providerManager) ConfigureProvider(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, cfgVal cty.Value) tfdiags.Diagnostics {
|
||||
@@ -319,7 +360,8 @@ func (p *providerManager) ConfigureProvider(ctx context.Context, addr addrs.AbsP
|
||||
return diags
|
||||
}
|
||||
|
||||
// tfplugin5 may return a different PreparedConfig, but we throw that away in all code paths?
|
||||
// If the provider returns something different, log a warning to help
|
||||
// indicate to provider developers that the value is not used.
|
||||
if validate.PreparedConfig != cty.NilVal && !validate.PreparedConfig.IsNull() && !validate.PreparedConfig.RawEquals(cfgVal) {
|
||||
log.Printf("[WARN] ValidateProviderConfig from %q changed the config value, but that value is unused", addr)
|
||||
}
|
||||
@@ -343,7 +385,7 @@ func (p *providerManager) ConfigureProvider(ctx context.Context, addr addrs.AbsP
|
||||
return diags
|
||||
}
|
||||
|
||||
func (p *providerManager) configuredProvider(addr addrs.AbsProviderInstanceCorrect) providers.Configured {
|
||||
func (p *providerManager) ConfiguredProvider(addr addrs.AbsProviderInstanceCorrect) providers.Configured {
|
||||
p.configuredLock.Lock()
|
||||
defer p.configuredLock.Unlock()
|
||||
|
||||
@@ -357,53 +399,82 @@ func (p *providerManager) configuredProvider(addr addrs.AbsProviderInstanceCorre
|
||||
}
|
||||
|
||||
func (p *providerManager) UpgradeResourceState(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, req providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse {
|
||||
configured := p.configuredProvider(addr)
|
||||
configured := p.ConfiguredProvider(addr)
|
||||
return configured.UpgradeResourceState(ctx, req)
|
||||
}
|
||||
|
||||
func (p *providerManager) ReadResource(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, req providers.ReadResourceRequest) providers.ReadResourceResponse {
|
||||
configured := p.configuredProvider(addr)
|
||||
configured := p.ConfiguredProvider(addr)
|
||||
return configured.ReadResource(ctx, req)
|
||||
}
|
||||
|
||||
func (p *providerManager) PlanResourceChange(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
|
||||
configured := p.configuredProvider(addr)
|
||||
configured := p.ConfiguredProvider(addr)
|
||||
return configured.PlanResourceChange(ctx, req)
|
||||
}
|
||||
|
||||
func (p *providerManager) ApplyResourceChange(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, req providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse {
|
||||
configured := p.configuredProvider(addr)
|
||||
configured := p.ConfiguredProvider(addr)
|
||||
return configured.ApplyResourceChange(ctx, req)
|
||||
}
|
||||
|
||||
func (p *providerManager) ImportResourceState(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, req providers.ImportResourceStateRequest) providers.ImportResourceStateResponse {
|
||||
configured := p.configuredProvider(addr)
|
||||
configured := p.ConfiguredProvider(addr)
|
||||
return configured.ImportResourceState(ctx, req)
|
||||
}
|
||||
|
||||
func (p *providerManager) ReadDataSource(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
|
||||
configured := p.configuredProvider(addr)
|
||||
configured := p.ConfiguredProvider(addr)
|
||||
return configured.ReadDataSource(ctx, req)
|
||||
}
|
||||
|
||||
func (p *providerManager) OpenEphemeralResource(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, req providers.OpenEphemeralResourceRequest) providers.OpenEphemeralResourceResponse {
|
||||
configured := p.configuredProvider(addr)
|
||||
configured := p.ConfiguredProvider(addr)
|
||||
return configured.OpenEphemeralResource(ctx, req)
|
||||
}
|
||||
|
||||
func (p *providerManager) RenewEphemeralResource(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, req providers.RenewEphemeralResourceRequest) providers.RenewEphemeralResourceResponse {
|
||||
configured := p.configuredProvider(addr)
|
||||
configured := p.ConfiguredProvider(addr)
|
||||
return configured.RenewEphemeralResource(ctx, req)
|
||||
}
|
||||
|
||||
func (p *providerManager) CloseEphemeralResource(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, req providers.CloseEphemeralResourceRequest) providers.CloseEphemeralResourceResponse {
|
||||
configured := p.configuredProvider(addr)
|
||||
configured := p.ConfiguredProvider(addr)
|
||||
return configured.CloseEphemeralResource(ctx, req)
|
||||
}
|
||||
|
||||
func (p *providerManager) CallFunction(ctx context.Context, addr addrs.AbsProviderInstanceCorrect, name string, arguments []cty.Value) (cty.Value, error) {
|
||||
req := providers.CallFunctionRequest{
|
||||
Name: name,
|
||||
Arguments: arguments,
|
||||
}
|
||||
|
||||
var resp providers.CallFunctionResponse
|
||||
if p.IsProviderConfigured(addr) {
|
||||
configured := p.ConfiguredProvider(addr)
|
||||
resp = configured.CallFunction(ctx, req)
|
||||
} else {
|
||||
unconfigured, done, diags := p.unconfiguredProvider(ctx, addr.Config.Config.Provider)
|
||||
defer done()
|
||||
if diags.HasErrors() {
|
||||
return cty.NilVal, diags.Err()
|
||||
}
|
||||
resp = unconfigured.(providers.Interface).CallFunction(ctx, req)
|
||||
}
|
||||
|
||||
return resp.Result, resp.Error
|
||||
}
|
||||
func (p *providerManager) GetFunctions(ctx context.Context, addr addrs.AbsProviderInstanceCorrect) providers.GetFunctionsResponse {
|
||||
configured := p.configuredProvider(addr)
|
||||
return configured.GetFunctions(ctx)
|
||||
if p.IsProviderConfigured(addr) {
|
||||
configured := p.ConfiguredProvider(addr)
|
||||
return configured.GetFunctions(ctx)
|
||||
}
|
||||
unconfigured, done, diags := p.unconfiguredProvider(ctx, addr.Config.Config.Provider)
|
||||
defer done()
|
||||
if diags.HasErrors() {
|
||||
return providers.GetFunctionsResponse{Diagnostics: diags}
|
||||
}
|
||||
return unconfigured.(providers.Interface).GetFunctions(ctx)
|
||||
}
|
||||
|
||||
func (p *providerManager) CloseProvider(ctx context.Context, addr addrs.AbsProviderInstanceCorrect) error {
|
||||
|
||||
121
internal/plugins/provisioners.go
Normal file
121
internal/plugins/provisioners.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/opentofu/opentofu/internal/configs/configschema"
|
||||
"github.com/opentofu/opentofu/internal/provisioners"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
type ProvisionerSchemas interface {
|
||||
HasProvisioner(typ string) bool
|
||||
ProvisionerSchema(typ string) (*configschema.Block, error)
|
||||
}
|
||||
type ProvisionerManager interface {
|
||||
ProvisionerSchemas
|
||||
|
||||
ValidateProvisionerConfig(ctx context.Context, typ string, config cty.Value) tfdiags.Diagnostics
|
||||
ProvisionResource(ctx context.Context, typ string, config cty.Value, connection cty.Value, output provisioners.UIOutput) tfdiags.Diagnostics
|
||||
}
|
||||
|
||||
type provisionerManager struct {
|
||||
factories map[string]provisioners.Factory
|
||||
|
||||
instancesLock sync.Mutex
|
||||
instances map[string]provisioners.Interface
|
||||
}
|
||||
|
||||
func NewProvisionerManager(factories map[string]provisioners.Factory) ProvisionerManager {
|
||||
return &provisionerManager{
|
||||
factories: factories,
|
||||
instances: map[string]provisioners.Interface{},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *provisionerManager) HasProvisioner(typ string) bool {
|
||||
_, ok := p.factories[typ]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (p *provisionerManager) provisioner(typ string) (provisioners.Interface, error) {
|
||||
p.instancesLock.Lock()
|
||||
defer p.instancesLock.Unlock()
|
||||
|
||||
instance, ok := p.instances[typ]
|
||||
if !ok {
|
||||
f, ok := p.factories[typ]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unavailable provisioner %q", typ)
|
||||
}
|
||||
|
||||
var err error
|
||||
instance, err = f()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.instances[typ] = instance
|
||||
}
|
||||
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
// ProvisionerSchema uses a temporary instance of the provisioner with the
|
||||
// given type name to obtain the schema for that provisioner's configuration.
|
||||
//
|
||||
// ProvisionerSchema memoizes results by provisioner type name, so it's fine
|
||||
// to repeatedly call this method with the same name if various different
|
||||
// parts of OpenTofu all need the same schema information.
|
||||
func (p *provisionerManager) ProvisionerSchema(typ string) (*configschema.Block, error) {
|
||||
provisioner, err := p.provisioner(typ)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to instantiate provisioner %q to obtain schema: %w", typ, err)
|
||||
}
|
||||
defer provisioner.Close()
|
||||
|
||||
resp := provisioner.GetSchema()
|
||||
if resp.Diagnostics.HasErrors() {
|
||||
return nil, fmt.Errorf("failed to retrieve schema from provisioner %q: %w", typ, resp.Diagnostics.Err())
|
||||
}
|
||||
|
||||
return resp.Provisioner, nil
|
||||
}
|
||||
|
||||
func (p *provisionerManager) ValidateProvisionerConfig(ctx context.Context, typ string, config cty.Value) tfdiags.Diagnostics {
|
||||
provisioner, err := p.provisioner(typ)
|
||||
if err != nil {
|
||||
return tfdiags.Diagnostics{}.Append(fmt.Errorf("failed to instantiate provisioner %q to validate config: %w", typ, err))
|
||||
}
|
||||
return provisioner.ValidateProvisionerConfig(provisioners.ValidateProvisionerConfigRequest{
|
||||
Config: config,
|
||||
}).Diagnostics
|
||||
}
|
||||
|
||||
func (p *provisionerManager) ProvisionResource(ctx context.Context, typ string, config cty.Value, connection cty.Value, output provisioners.UIOutput) tfdiags.Diagnostics {
|
||||
provisioner, err := p.provisioner(typ)
|
||||
if err != nil {
|
||||
return tfdiags.Diagnostics{}.Append(fmt.Errorf("failed to instantiate provisioner %q to validate config: %w", typ, err))
|
||||
}
|
||||
return provisioner.ProvisionResource(provisioners.ProvisionResourceRequest{
|
||||
Config: config,
|
||||
Connection: connection,
|
||||
UIOutput: output,
|
||||
}).Diagnostics
|
||||
}
|
||||
|
||||
func (p *provisionerManager) CloseProvisioners() error {
|
||||
p.instancesLock.Lock()
|
||||
defer p.instancesLock.Unlock()
|
||||
|
||||
var diags tfdiags.Diagnostics
|
||||
for name, prov := range p.instances {
|
||||
err := prov.Close()
|
||||
if err != nil {
|
||||
diags = diags.Append(fmt.Errorf("provisioner.Close %s: %w", name, err))
|
||||
}
|
||||
}
|
||||
return diags.Err()
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/configs"
|
||||
"github.com/opentofu/opentofu/internal/encryption"
|
||||
"github.com/opentofu/opentofu/internal/logging"
|
||||
"github.com/opentofu/opentofu/internal/plugins"
|
||||
"github.com/opentofu/opentofu/internal/providers"
|
||||
"github.com/opentofu/opentofu/internal/provisioners"
|
||||
"github.com/opentofu/opentofu/internal/states"
|
||||
@@ -80,7 +81,7 @@ type Context struct {
|
||||
// operations.
|
||||
meta *ContextMeta
|
||||
|
||||
plugins *contextPlugins
|
||||
plugins plugins.PluginManager
|
||||
|
||||
hooks []Hook
|
||||
sh *stopHook
|
||||
@@ -135,7 +136,8 @@ func NewContext(opts *ContextOpts) (*Context, tfdiags.Diagnostics) {
|
||||
par = 10
|
||||
}
|
||||
|
||||
plugins := newContextPlugins(opts.Providers, opts.Provisioners)
|
||||
// TODO move up
|
||||
plugins := plugins.NewPluginManager(context.TODO(), opts.Providers, opts.Provisioners)
|
||||
|
||||
log.Printf("[TRACE] tofu.NewContext: complete")
|
||||
|
||||
|
||||
@@ -15,17 +15,18 @@ import (
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
|
||||
"github.com/opentofu/opentofu/internal/addrs"
|
||||
"github.com/opentofu/opentofu/internal/plugins"
|
||||
"github.com/opentofu/opentofu/internal/providers"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
)
|
||||
|
||||
// This builds a provider function using an EvalContext and some additional information
|
||||
// This is split out of BuiltinEvalContext for testing
|
||||
func evalContextProviderFunction(ctx context.Context, provider providers.Interface, op walkOperation, pf addrs.ProviderFunction, rng tfdiags.SourceRange) (*function.Function, tfdiags.Diagnostics) {
|
||||
func evalContextProviderFunction(ctx context.Context, providers plugins.ProviderManager, providerAddr addrs.AbsProviderInstanceCorrect, op walkOperation, pf addrs.ProviderFunction, rng tfdiags.SourceRange) (*function.Function, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
// First try to look up the function from provider schema
|
||||
schema := provider.GetProviderSchema(ctx)
|
||||
schema := providers.GetProviderSchema(ctx, providerAddr.Config.Config.Provider)
|
||||
if schema.Diagnostics.HasErrors() {
|
||||
return nil, schema.Diagnostics
|
||||
}
|
||||
@@ -54,7 +55,7 @@ func evalContextProviderFunction(ctx context.Context, provider providers.Interfa
|
||||
}
|
||||
|
||||
// The provider may be configured and present additional functions via GetFunctions
|
||||
specs := provider.GetFunctions(ctx)
|
||||
specs := providers.GetFunctions(ctx, providerAddr)
|
||||
if specs.Diagnostics.HasErrors() {
|
||||
return nil, specs.Diagnostics
|
||||
}
|
||||
@@ -71,7 +72,7 @@ func evalContextProviderFunction(ctx context.Context, provider providers.Interfa
|
||||
}
|
||||
}
|
||||
|
||||
fn := providerFunction(ctx, pf.Function, spec, provider)
|
||||
fn := providerFunction(ctx, pf.Function, spec, providers, providerAddr)
|
||||
|
||||
return &fn, nil
|
||||
|
||||
@@ -80,7 +81,7 @@ func evalContextProviderFunction(ctx context.Context, provider providers.Interfa
|
||||
// Turn a provider function spec into a cty callable function
|
||||
// This will use the instance factory to get a provider to support the
|
||||
// function call.
|
||||
func providerFunction(ctx context.Context, name string, spec providers.FunctionSpec, provider providers.Interface) function.Function {
|
||||
func providerFunction(ctx context.Context, name string, spec providers.FunctionSpec, pm plugins.ProviderManager, providerAddr addrs.AbsProviderInstanceCorrect) function.Function {
|
||||
params := make([]function.Parameter, len(spec.Parameters))
|
||||
for i, param := range spec.Parameters {
|
||||
params[i] = providerFunctionParameter(param)
|
||||
@@ -93,17 +94,14 @@ func providerFunction(ctx context.Context, name string, spec providers.FunctionS
|
||||
}
|
||||
|
||||
impl := func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
resp := provider.CallFunction(ctx, providers.CallFunctionRequest{
|
||||
Name: name,
|
||||
Arguments: args,
|
||||
})
|
||||
result, err := pm.CallFunction(ctx, providerAddr, name, args)
|
||||
|
||||
if argError, ok := resp.Error.(*providers.CallFunctionArgumentError); ok {
|
||||
if argError, ok := err.(*providers.CallFunctionArgumentError); ok {
|
||||
// Convert ArgumentError to cty error
|
||||
return resp.Result, function.NewArgError(argError.FunctionArgument, errors.New(argError.Text))
|
||||
return result, function.NewArgError(argError.FunctionArgument, errors.New(argError.Text))
|
||||
}
|
||||
|
||||
return resp.Result, resp.Error
|
||||
return result, err
|
||||
}
|
||||
|
||||
return function.New(&function.Spec{
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
// Copyright (c) The OpenTofu Authors
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright (c) 2023 HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package tofu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/opentofu/opentofu/internal/addrs"
|
||||
"github.com/opentofu/opentofu/internal/configs/configschema"
|
||||
"github.com/opentofu/opentofu/internal/providers"
|
||||
"github.com/opentofu/opentofu/internal/provisioners"
|
||||
)
|
||||
|
||||
// contextPlugins represents a library of available plugins (providers and
|
||||
// provisioners) which we assume will all be used with the same
|
||||
// tofu.Context, and thus it'll be safe to cache certain information
|
||||
// about the providers for performance reasons.
|
||||
type contextPlugins struct {
|
||||
providerFactories map[addrs.Provider]providers.Factory
|
||||
provisionerFactories map[string]provisioners.Factory
|
||||
}
|
||||
|
||||
func newContextPlugins(providerFactories map[addrs.Provider]providers.Factory, provisionerFactories map[string]provisioners.Factory) *contextPlugins {
|
||||
return &contextPlugins{
|
||||
providerFactories: providerFactories,
|
||||
provisionerFactories: provisionerFactories,
|
||||
}
|
||||
}
|
||||
|
||||
func (cp *contextPlugins) HasProvider(addr addrs.Provider) bool {
|
||||
_, ok := cp.providerFactories[addr]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (cp *contextPlugins) NewProviderInstance(addr addrs.Provider) (providers.Interface, error) {
|
||||
f, ok := cp.providerFactories[addr]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unavailable provider %q", addr.String())
|
||||
}
|
||||
|
||||
return f()
|
||||
|
||||
}
|
||||
|
||||
func (cp *contextPlugins) HasProvisioner(typ string) bool {
|
||||
_, ok := cp.provisionerFactories[typ]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (cp *contextPlugins) NewProvisionerInstance(typ string) (provisioners.Interface, error) {
|
||||
f, ok := cp.provisionerFactories[typ]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unavailable provisioner %q", typ)
|
||||
}
|
||||
|
||||
return f()
|
||||
}
|
||||
|
||||
// ProviderSchema uses a temporary instance of the provider with the given
|
||||
// address to obtain the full schema for all aspects of that provider.
|
||||
//
|
||||
// ProviderSchema memoizes results by unique provider address, so it's fine
|
||||
// to repeatedly call this method with the same address if various different
|
||||
// parts of OpenTofu all need the same schema information.
|
||||
func (cp *contextPlugins) ProviderSchema(ctx context.Context, addr addrs.Provider) (providers.ProviderSchema, error) {
|
||||
// Check the global schema cache first.
|
||||
// This cache is only written by the provider client, and transparently
|
||||
// used by GetProviderSchema, but we check it here because at this point we
|
||||
// may be able to avoid spinning up the provider instance at all.
|
||||
//
|
||||
// It's worth noting that ServerCapabilities.GetProviderSchemaOptional is ignored here.
|
||||
// That is because we're checking *prior* to the provider's instantiation.
|
||||
// GetProviderSchemaOptional only says that *if we instantiate a provider*,
|
||||
// then we need to run the get schema call at least once.
|
||||
// BUG This SHORT CIRCUITS the logic below and is not the only code which inserts provider schemas into the cache!!
|
||||
schemas, ok := providers.SchemaCache.Get(addr)
|
||||
if ok {
|
||||
log.Printf("[TRACE] tofu.contextPlugins: Serving provider %q schema from global schema cache", addr)
|
||||
return schemas, nil
|
||||
}
|
||||
|
||||
log.Printf("[TRACE] tofu.contextPlugins: Initializing provider %q to read its schema", addr)
|
||||
provider, err := cp.NewProviderInstance(addr)
|
||||
if err != nil {
|
||||
return schemas, fmt.Errorf("failed to instantiate provider %q to obtain schema: %w", addr, err)
|
||||
}
|
||||
defer provider.Close(ctx)
|
||||
|
||||
resp := provider.GetProviderSchema(ctx)
|
||||
if resp.Diagnostics.HasErrors() {
|
||||
return resp, fmt.Errorf("failed to retrieve schema from provider %q: %w", addr, resp.Diagnostics.Err())
|
||||
}
|
||||
|
||||
if resp.Provider.Version < 0 {
|
||||
// We're not using the version numbers here yet, but we'll check
|
||||
// for validity anyway in case we start using them in future.
|
||||
return resp, fmt.Errorf("provider %s has invalid negative schema version for its configuration blocks,which is a bug in the provider ", addr)
|
||||
}
|
||||
|
||||
for t, r := range resp.ResourceTypes {
|
||||
if err := r.Block.InternalValidate(); err != nil {
|
||||
return resp, fmt.Errorf("provider %s has invalid schema for managed resource type %q, which is a bug in the provider: %w", addr, t, err)
|
||||
}
|
||||
if r.Version < 0 {
|
||||
return resp, fmt.Errorf("provider %s has invalid negative schema version for managed resource type %q, which is a bug in the provider", addr, t)
|
||||
}
|
||||
}
|
||||
|
||||
for t, d := range resp.DataSources {
|
||||
if err := d.Block.InternalValidate(); err != nil {
|
||||
return resp, fmt.Errorf("provider %s has invalid schema for data resource type %q, which is a bug in the provider: %w", addr, t, err)
|
||||
}
|
||||
if d.Version < 0 {
|
||||
// We're not using the version numbers here yet, but we'll check
|
||||
// for validity anyway in case we start using them in future.
|
||||
return resp, fmt.Errorf("provider %s has invalid negative schema version for data resource type %q, which is a bug in the provider", addr, t)
|
||||
}
|
||||
}
|
||||
|
||||
for t, d := range resp.EphemeralResources {
|
||||
if err := d.Block.InternalValidate(); err != nil {
|
||||
return resp, fmt.Errorf("provider %s has invalid schema for ephemeral resource type %q, which is a bug in the provider: %w", addr, t, err)
|
||||
}
|
||||
if d.Version < 0 {
|
||||
// We're not using the version numbers here yet, but we'll check
|
||||
// for validity anyway in case we start using them in future.
|
||||
return resp, fmt.Errorf("provider %s has invalid negative schema version for ephemeral resource type %q, which is a bug in the provider", addr, t)
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ProviderConfigSchema is a helper wrapper around ProviderSchema which first
|
||||
// reads the full schema of the given provider and then extracts just the
|
||||
// provider's configuration schema, which defines what's expected in a
|
||||
// "provider" block in the configuration when configuring this provider.
|
||||
func (cp *contextPlugins) ProviderConfigSchema(ctx context.Context, providerAddr addrs.Provider) (*configschema.Block, error) {
|
||||
providerSchema, err := cp.ProviderSchema(ctx, providerAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return providerSchema.Provider.Block, nil
|
||||
}
|
||||
|
||||
// ResourceTypeSchema is a helper wrapper around ProviderSchema which first
|
||||
// reads the schema of the given provider and then tries to find the schema
|
||||
// for the resource type of the given resource mode in that provider.
|
||||
//
|
||||
// ResourceTypeSchema will return an error if the provider schema lookup
|
||||
// fails, but will return nil if the provider schema lookup succeeds but then
|
||||
// the provider doesn't have a resource of the requested type.
|
||||
//
|
||||
// Managed resource types have versioned schemas, so the second return value
|
||||
// is the current schema version number for the requested resource. The version
|
||||
// is irrelevant for other resource modes.
|
||||
func (cp *contextPlugins) ResourceTypeSchema(ctx context.Context, providerAddr addrs.Provider, resourceMode addrs.ResourceMode, resourceType string) (*configschema.Block, uint64, error) {
|
||||
providerSchema, err := cp.ProviderSchema(ctx, providerAddr)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
schema, version := providerSchema.SchemaForResourceType(resourceMode, resourceType)
|
||||
return schema, version, nil
|
||||
}
|
||||
|
||||
// ProvisionerSchema uses a temporary instance of the provisioner with the
|
||||
// given type name to obtain the schema for that provisioner's configuration.
|
||||
//
|
||||
// ProvisionerSchema memoizes results by provisioner type name, so it's fine
|
||||
// to repeatedly call this method with the same name if various different
|
||||
// parts of OpenTofu all need the same schema information.
|
||||
func (cp *contextPlugins) ProvisionerSchema(typ string) (*configschema.Block, error) {
|
||||
log.Printf("[TRACE] tofu.contextPlugins: Initializing provisioner %q to read its schema", typ)
|
||||
provisioner, err := cp.NewProvisionerInstance(typ)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to instantiate provisioner %q to obtain schema: %w", typ, err)
|
||||
}
|
||||
defer provisioner.Close()
|
||||
|
||||
resp := provisioner.GetSchema()
|
||||
if resp.Diagnostics.HasErrors() {
|
||||
return nil, fmt.Errorf("failed to retrieve schema from provisioner %q: %w", typ, resp.Diagnostics.Err())
|
||||
}
|
||||
|
||||
return resp.Provisioner, nil
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
// Copyright (c) The OpenTofu Authors
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright (c) 2023 HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package tofu
|
||||
|
||||
import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/opentofu/opentofu/internal/addrs"
|
||||
"github.com/opentofu/opentofu/internal/configs/configschema"
|
||||
"github.com/opentofu/opentofu/internal/providers"
|
||||
"github.com/opentofu/opentofu/internal/provisioners"
|
||||
)
|
||||
|
||||
// simpleMockPluginLibrary returns a plugin library pre-configured with
|
||||
// one provider and one provisioner, both called "test".
|
||||
//
|
||||
// The provider is built with simpleMockProvider and the provisioner with
|
||||
// simpleMockProvisioner, and all schemas used in both are as built by
|
||||
// function simpleTestSchema.
|
||||
//
|
||||
// Each call to this function produces an entirely-separate set of objects,
|
||||
// so the caller can feel free to modify the returned value to further
|
||||
// customize the mocks contained within.
|
||||
func simpleMockPluginLibrary() *contextPlugins {
|
||||
// We create these out here, rather than in the factory functions below,
|
||||
// because we want each call to the factory to return the _same_ instance,
|
||||
// so that test code can customize it before passing this component
|
||||
// factory into real code under test.
|
||||
provider := simpleMockProvider()
|
||||
provisioner := simpleMockProvisioner()
|
||||
ret := &contextPlugins{
|
||||
providerFactories: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("test"): func() (providers.Interface, error) {
|
||||
return provider, nil
|
||||
},
|
||||
},
|
||||
provisionerFactories: map[string]provisioners.Factory{
|
||||
"test": func() (provisioners.Interface, error) {
|
||||
return provisioner, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// simpleTestSchema returns a block schema that contains a few optional
|
||||
// attributes for use in tests.
|
||||
//
|
||||
// The returned schema contains the following optional attributes:
|
||||
//
|
||||
// - test_string, of type string
|
||||
// - test_number, of type number
|
||||
// - test_bool, of type bool
|
||||
// - test_list, of type list(string)
|
||||
// - test_map, of type map(string)
|
||||
//
|
||||
// Each call to this function produces an entirely new schema instance, so
|
||||
// callers can feel free to modify it once returned.
|
||||
func simpleTestSchema() *configschema.Block {
|
||||
return &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"test_string": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
"test_number": {
|
||||
Type: cty.Number,
|
||||
Optional: true,
|
||||
},
|
||||
"test_bool": {
|
||||
Type: cty.Bool,
|
||||
Optional: true,
|
||||
},
|
||||
"test_list": {
|
||||
Type: cty.List(cty.String),
|
||||
Optional: true,
|
||||
},
|
||||
"test_map": {
|
||||
Type: cty.Map(cty.String),
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,7 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/instances"
|
||||
"github.com/opentofu/opentofu/internal/lang"
|
||||
"github.com/opentofu/opentofu/internal/plans"
|
||||
"github.com/opentofu/opentofu/internal/providers"
|
||||
"github.com/opentofu/opentofu/internal/provisioners"
|
||||
"github.com/opentofu/opentofu/internal/plugins"
|
||||
"github.com/opentofu/opentofu/internal/refactoring"
|
||||
"github.com/opentofu/opentofu/internal/states"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
@@ -41,44 +40,9 @@ type EvalContext interface {
|
||||
// Input is the UIInput object for interacting with the UI.
|
||||
Input() UIInput
|
||||
|
||||
// InitProvider initializes the provider with the given address, and returns
|
||||
// the implementation of the resource provider or an error.
|
||||
//
|
||||
// It is an error to initialize the same provider more than once. This
|
||||
// method will panic if the module instance address of the given provider
|
||||
// configuration does not match the Path() of the EvalContext.
|
||||
InitProvider(ctx context.Context, addr addrs.AbsProviderConfig, key addrs.InstanceKey) (providers.Interface, error)
|
||||
Provisioners() plugins.ProvisionerManager
|
||||
|
||||
// Provider gets the provider instance with the given address (already
|
||||
// initialized) or returns nil if the provider isn't initialized.
|
||||
//
|
||||
// This method expects an _absolute_ provider configuration address, since
|
||||
// resources in one module are able to use providers from other modules.
|
||||
// InitProvider must've been called on the EvalContext of the module
|
||||
// that owns the given provider before calling this method.
|
||||
Provider(context.Context, addrs.AbsProviderConfig, addrs.InstanceKey) providers.Interface
|
||||
|
||||
// ProviderSchema retrieves the schema for a particular provider, which
|
||||
// must have already been initialized with InitProvider.
|
||||
//
|
||||
// This method expects an _absolute_ provider configuration address, since
|
||||
// resources in one module are able to use providers from other modules.
|
||||
ProviderSchema(context.Context, addrs.AbsProviderConfig) (providers.ProviderSchema, error)
|
||||
|
||||
// CloseProvider closes provider connections that aren't needed anymore.
|
||||
//
|
||||
// This method will panic if the module instance address of the given
|
||||
// provider configuration does not match the Path() of the EvalContext.
|
||||
CloseProvider(context.Context, addrs.AbsProviderConfig) error
|
||||
|
||||
// ConfigureProvider configures the provider with the given
|
||||
// configuration. This is a separate context call because this call
|
||||
// is used to store the provider configuration for inheritance lookups
|
||||
// with ParentProviderConfig().
|
||||
//
|
||||
// This method will panic if the module instance address of the given
|
||||
// provider configuration does not match the Path() of the EvalContext.
|
||||
ConfigureProvider(context.Context, addrs.AbsProviderConfig, addrs.InstanceKey, cty.Value) tfdiags.Diagnostics
|
||||
Providers() plugins.ProviderManager
|
||||
|
||||
// ProviderInput and SetProviderInput are used to configure providers
|
||||
// from user input.
|
||||
@@ -88,17 +52,6 @@ type EvalContext interface {
|
||||
ProviderInput(context.Context, addrs.AbsProviderConfig) map[string]cty.Value
|
||||
SetProviderInput(context.Context, addrs.AbsProviderConfig, map[string]cty.Value)
|
||||
|
||||
// Provisioner gets the provisioner instance with the given name.
|
||||
Provisioner(string) (provisioners.Interface, error)
|
||||
|
||||
// ProvisionerSchema retrieves the main configuration schema for a
|
||||
// particular provisioner, which must have already been initialized with
|
||||
// InitProvisioner.
|
||||
ProvisionerSchema(string) (*configschema.Block, error)
|
||||
|
||||
// CloseProvisioner closes all provisioner plugins.
|
||||
CloseProvisioners() error
|
||||
|
||||
// EvaluateBlock takes the given raw configuration block and associated
|
||||
// schema and evaluates it to produce a value of an object type that
|
||||
// conforms to the implied type of the schema.
|
||||
|
||||
@@ -22,12 +22,10 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/instances"
|
||||
"github.com/opentofu/opentofu/internal/lang"
|
||||
"github.com/opentofu/opentofu/internal/plans"
|
||||
"github.com/opentofu/opentofu/internal/providers"
|
||||
"github.com/opentofu/opentofu/internal/provisioners"
|
||||
"github.com/opentofu/opentofu/internal/plugins"
|
||||
"github.com/opentofu/opentofu/internal/refactoring"
|
||||
"github.com/opentofu/opentofu/internal/states"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
"github.com/opentofu/opentofu/version"
|
||||
)
|
||||
|
||||
// BuiltinEvalContext is an EvalContext implementation that is used by
|
||||
@@ -61,18 +59,14 @@ type BuiltinEvalContext struct {
|
||||
|
||||
// Plugins is a library of plugin components (providers and provisioners)
|
||||
// available for use during a graph walk.
|
||||
Plugins *contextPlugins
|
||||
Plugins plugins.PluginManager
|
||||
|
||||
Hooks []Hook
|
||||
InputValue UIInput
|
||||
|
||||
ProviderLock *sync.Mutex
|
||||
ProviderCache map[string]map[addrs.InstanceKey]providers.Interface
|
||||
ProviderInputConfig map[string]map[string]cty.Value
|
||||
|
||||
ProvisionerLock *sync.Mutex
|
||||
ProvisionerCache map[string]provisioners.Interface
|
||||
|
||||
ChangesValue *plans.ChangesSync
|
||||
StateValue *states.SyncState
|
||||
ChecksValue *checks.State
|
||||
@@ -128,95 +122,12 @@ func (c *BuiltinEvalContext) Input() UIInput {
|
||||
return c.InputValue
|
||||
}
|
||||
|
||||
func (c *BuiltinEvalContext) InitProvider(ctx context.Context, addr addrs.AbsProviderConfig, providerInstanceKey addrs.InstanceKey) (providers.Interface, error) {
|
||||
c.ProviderLock.Lock()
|
||||
defer c.ProviderLock.Unlock()
|
||||
|
||||
providerAddrKey := addr.String()
|
||||
|
||||
if c.ProviderCache[providerAddrKey] == nil {
|
||||
c.ProviderCache[providerAddrKey] = make(map[addrs.InstanceKey]providers.Interface)
|
||||
}
|
||||
|
||||
// If we have already initialized, it is an error
|
||||
if _, ok := c.ProviderCache[providerAddrKey][providerInstanceKey]; ok {
|
||||
return nil, fmt.Errorf("%s is already initialized", addr)
|
||||
}
|
||||
|
||||
p, err := c.Plugins.NewProviderInstance(addr.Provider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("[TRACE] BuiltinEvalContext: Initialized %q%s provider for %s", addr.String(), providerInstanceKey, addr)
|
||||
c.ProviderCache[providerAddrKey][providerInstanceKey] = p
|
||||
|
||||
return p, nil
|
||||
func (c *BuiltinEvalContext) Providers() plugins.ProviderManager {
|
||||
return c.Plugins
|
||||
}
|
||||
|
||||
func (c *BuiltinEvalContext) Provider(_ context.Context, addr addrs.AbsProviderConfig, key addrs.InstanceKey) providers.Interface {
|
||||
c.ProviderLock.Lock()
|
||||
defer c.ProviderLock.Unlock()
|
||||
|
||||
providerAddrKey := addr.String()
|
||||
|
||||
pm, ok := c.ProviderCache[providerAddrKey]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return pm[key]
|
||||
}
|
||||
|
||||
func (c *BuiltinEvalContext) ProviderSchema(ctx context.Context, addr addrs.AbsProviderConfig) (providers.ProviderSchema, error) {
|
||||
return c.Plugins.ProviderSchema(ctx, addr.Provider)
|
||||
}
|
||||
|
||||
func (c *BuiltinEvalContext) CloseProvider(ctx context.Context, addr addrs.AbsProviderConfig) error {
|
||||
c.ProviderLock.Lock()
|
||||
defer c.ProviderLock.Unlock()
|
||||
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
providerAddrKey := addr.String()
|
||||
providerMap := c.ProviderCache[providerAddrKey]
|
||||
if providerMap != nil {
|
||||
for _, provider := range providerMap {
|
||||
err := provider.Close(ctx)
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
}
|
||||
}
|
||||
delete(c.ProviderCache, providerAddrKey)
|
||||
}
|
||||
if diags.HasErrors() {
|
||||
return diags.Err()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *BuiltinEvalContext) ConfigureProvider(ctx context.Context, addr addrs.AbsProviderConfig, providerKey addrs.InstanceKey, cfg cty.Value) tfdiags.Diagnostics {
|
||||
var diags tfdiags.Diagnostics
|
||||
if !c.Path().IsForModule(addr.Module) {
|
||||
// This indicates incorrect use of ConfigureProvider: it should be used
|
||||
// only from the module that the provider configuration belongs to.
|
||||
panic(fmt.Sprintf("%s configured by wrong module %s", addr, c.Path()))
|
||||
}
|
||||
|
||||
p := c.Provider(ctx, addr, providerKey)
|
||||
if p == nil {
|
||||
diags = diags.Append(fmt.Errorf("%s not initialized", addr.InstanceString(providerKey)))
|
||||
return diags
|
||||
}
|
||||
|
||||
req := providers.ConfigureProviderRequest{
|
||||
TerraformVersion: version.String(),
|
||||
Config: cfg,
|
||||
}
|
||||
|
||||
resp := p.ConfigureProvider(ctx, req)
|
||||
return resp.Diagnostics
|
||||
func (c *BuiltinEvalContext) Provisioners() plugins.ProvisionerManager {
|
||||
return c.Plugins
|
||||
}
|
||||
|
||||
func (c *BuiltinEvalContext) ProviderInput(_ context.Context, pc addrs.AbsProviderConfig) map[string]cty.Value {
|
||||
@@ -251,43 +162,6 @@ func (c *BuiltinEvalContext) SetProviderInput(_ context.Context, pc addrs.AbsPro
|
||||
c.ProviderLock.Unlock()
|
||||
}
|
||||
|
||||
func (c *BuiltinEvalContext) Provisioner(n string) (provisioners.Interface, error) {
|
||||
c.ProvisionerLock.Lock()
|
||||
defer c.ProvisionerLock.Unlock()
|
||||
|
||||
p, ok := c.ProvisionerCache[n]
|
||||
if !ok {
|
||||
var err error
|
||||
p, err = c.Plugins.NewProvisionerInstance(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.ProvisionerCache[n] = p
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (c *BuiltinEvalContext) ProvisionerSchema(n string) (*configschema.Block, error) {
|
||||
return c.Plugins.ProvisionerSchema(n)
|
||||
}
|
||||
|
||||
func (c *BuiltinEvalContext) CloseProvisioners() error {
|
||||
var diags tfdiags.Diagnostics
|
||||
c.ProvisionerLock.Lock()
|
||||
defer c.ProvisionerLock.Unlock()
|
||||
|
||||
for name, prov := range c.ProvisionerCache {
|
||||
err := prov.Close()
|
||||
if err != nil {
|
||||
diags = diags.Append(fmt.Errorf("provisioner.Close %s: %w", name, err))
|
||||
}
|
||||
}
|
||||
|
||||
return diags.Err()
|
||||
}
|
||||
|
||||
func (c *BuiltinEvalContext) EvaluateBlock(ctx context.Context, body hcl.Body, schema *configschema.Block, self addrs.Referenceable, keyData InstanceKeyEvalData) (cty.Value, hcl.Body, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
scope := c.EvaluationScope(self, nil, keyData)
|
||||
@@ -381,15 +255,12 @@ func (c *BuiltinEvalContext) EvaluateReplaceTriggeredBy(ctx context.Context, exp
|
||||
// Since we have a traversal after the resource reference, we will need to
|
||||
// decode the changes, which means we need a schema.
|
||||
providerAddr := change.ProviderAddr
|
||||
schema, err := c.ProviderSchema(ctx, providerAddr)
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
return nil, false, diags
|
||||
}
|
||||
|
||||
resAddr := change.Addr.ContainingResource().Resource
|
||||
resSchema, _ := schema.SchemaForResourceType(resAddr.Mode, resAddr.Type)
|
||||
ty := resSchema.ImpliedType()
|
||||
resSchema, resDiags := c.Plugins.ResourceTypeSchema(ctx, providerAddr.Provider, resAddr.Mode, resAddr.Type)
|
||||
if resDiags.HasErrors() {
|
||||
return nil, false, diags.Append(resDiags)
|
||||
}
|
||||
ty := resSchema.Block.ImpliedType()
|
||||
|
||||
before, err := change.ChangeSrc.Before.Decode(ty)
|
||||
if err != nil {
|
||||
@@ -467,19 +338,7 @@ func (c *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, source ad
|
||||
}
|
||||
}
|
||||
|
||||
provider := c.Provider(ctx, providedBy.Provider, providerKey)
|
||||
|
||||
if provider == nil {
|
||||
// This should not be possible if references are tracked correctly
|
||||
return nil, tfdiags.Diagnostics{}.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Uninitialized function provider",
|
||||
Detail: fmt.Sprintf("Provider %q has not yet been initialized", providedBy.Provider.String()),
|
||||
Subject: rng.ToHCL().Ptr(),
|
||||
})
|
||||
}
|
||||
|
||||
return evalContextProviderFunction(ctx, provider, c.Evaluator.Operation, pf, rng)
|
||||
return evalContextProviderFunction(ctx, c.Providers(), providedBy.Provider.InstanceCorrect(providerKey), c.Evaluator.Operation, pf, rng)
|
||||
})
|
||||
scope.SetActiveExperiments(mc.Module.ActiveExperiments)
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/instances"
|
||||
"github.com/opentofu/opentofu/internal/lang"
|
||||
"github.com/opentofu/opentofu/internal/plans"
|
||||
"github.com/opentofu/opentofu/internal/plugins"
|
||||
"github.com/opentofu/opentofu/internal/providers"
|
||||
"github.com/opentofu/opentofu/internal/provisioners"
|
||||
"github.com/opentofu/opentofu/internal/refactoring"
|
||||
@@ -186,39 +187,12 @@ func (c *MockEvalContext) Input() UIInput {
|
||||
return c.InputInput
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) InitProvider(_ context.Context, addr addrs.AbsProviderConfig, _ addrs.InstanceKey) (providers.Interface, error) {
|
||||
c.InitProviderCalled = true
|
||||
c.InitProviderType = addr.String()
|
||||
c.InitProviderAddr = addr
|
||||
return c.InitProviderProvider, c.InitProviderError
|
||||
func (m *MockEvalContext) Providers() plugins.ProviderManager {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) Provider(_ context.Context, addr addrs.AbsProviderConfig, _ addrs.InstanceKey) providers.Interface {
|
||||
c.ProviderCalled = true
|
||||
c.ProviderAddr = addr
|
||||
return c.ProviderProvider
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) ProviderSchema(_ context.Context, addr addrs.AbsProviderConfig) (providers.ProviderSchema, error) {
|
||||
c.ProviderSchemaCalled = true
|
||||
c.ProviderSchemaAddr = addr
|
||||
return c.ProviderSchemaSchema, c.ProviderSchemaError
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) CloseProvider(_ context.Context, addr addrs.AbsProviderConfig) error {
|
||||
c.CloseProviderCalled = true
|
||||
c.CloseProviderAddr = addr
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) ConfigureProvider(_ context.Context, addr addrs.AbsProviderConfig, _ addrs.InstanceKey, cfg cty.Value) tfdiags.Diagnostics {
|
||||
c.ConfigureProviderCalled = true
|
||||
c.ConfigureProviderAddr = addr
|
||||
c.ConfigureProviderConfig = cfg
|
||||
if c.ConfigureProviderFn != nil {
|
||||
return c.ConfigureProviderFn(addr, cfg)
|
||||
}
|
||||
return c.ConfigureProviderDiags
|
||||
func (m *MockEvalContext) Provisioners() plugins.ProvisionerManager {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) ProviderInput(_ context.Context, addr addrs.AbsProviderConfig) map[string]cty.Value {
|
||||
@@ -233,18 +207,6 @@ func (c *MockEvalContext) SetProviderInput(_ context.Context, addr addrs.AbsProv
|
||||
c.SetProviderInputValues = vals
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) Provisioner(n string) (provisioners.Interface, error) {
|
||||
c.ProvisionerCalled = true
|
||||
c.ProvisionerName = n
|
||||
return c.ProvisionerProvisioner, nil
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) ProvisionerSchema(n string) (*configschema.Block, error) {
|
||||
c.ProvisionerSchemaCalled = true
|
||||
c.ProvisionerSchemaName = n
|
||||
return c.ProvisionerSchemaSchema, c.ProvisionerSchemaError
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) CloseProvisioners() error {
|
||||
c.CloseProvisionersCalled = true
|
||||
return nil
|
||||
|
||||
@@ -144,15 +144,15 @@ func getProvider(ctx context.Context, evalCtx EvalContext, addr addrs.AbsProvide
|
||||
// Should never happen
|
||||
panic("GetProvider used with uninitialized provider configuration address")
|
||||
}
|
||||
provider := evalCtx.Provider(ctx, addr, providerKey)
|
||||
provider := evalCtx.Providers().ConfiguredProvider(addr.InstanceCorrect(providerKey))
|
||||
if provider == nil {
|
||||
return nil, providers.ProviderSchema{}, fmt.Errorf("provider %s not initialized", addr.InstanceString(providerKey))
|
||||
}
|
||||
// Not all callers require a schema, so we will leave checking for a nil
|
||||
// schema to the callers.
|
||||
schema, err := evalCtx.ProviderSchema(ctx, addr)
|
||||
if err != nil {
|
||||
return nil, providers.ProviderSchema{}, fmt.Errorf("failed to read schema for provider %s: %w", addr, err)
|
||||
schema := evalCtx.Providers().GetProviderSchema(ctx, addr.Provider)
|
||||
if schema.Diagnostics.HasErrors() {
|
||||
return nil, providers.ProviderSchema{}, schema.Diagnostics.Err()
|
||||
}
|
||||
return provider, schema, nil
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/lang"
|
||||
"github.com/opentofu/opentofu/internal/lang/marks"
|
||||
"github.com/opentofu/opentofu/internal/plans"
|
||||
"github.com/opentofu/opentofu/internal/plugins"
|
||||
"github.com/opentofu/opentofu/internal/states"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
)
|
||||
@@ -58,7 +59,7 @@ type Evaluator struct {
|
||||
//
|
||||
// From this we only access the schemas of the plugins, and don't otherwise
|
||||
// interact with plugin instances.
|
||||
Plugins *contextPlugins
|
||||
Plugins plugins.PluginManager
|
||||
|
||||
// State is the current state, embedded in a wrapper that ensures that
|
||||
// it can be safely accessed and modified concurrently.
|
||||
@@ -1015,14 +1016,14 @@ func (d *evaluationStateData) GetResource(ctx context.Context, addr addrs.Resour
|
||||
|
||||
func (d *evaluationStateData) getResourceSchema(ctx context.Context, addr addrs.Resource, providerAddr addrs.Provider) *configschema.Block {
|
||||
// TODO: Plumb a useful context.Context through to here.
|
||||
schema, _, err := d.Evaluator.Plugins.ResourceTypeSchema(ctx, providerAddr, addr.Mode, addr.Type)
|
||||
if err != nil {
|
||||
schema, diags := d.Evaluator.Plugins.ResourceTypeSchema(ctx, providerAddr, addr.Mode, addr.Type)
|
||||
if diags.HasErrors() {
|
||||
// We have plenty of other codepaths that will detect and report
|
||||
// schema lookup errors before we'd reach this point, so we'll just
|
||||
// treat a failure here the same as having no schema.
|
||||
return nil
|
||||
}
|
||||
return schema
|
||||
return schema.Block
|
||||
}
|
||||
|
||||
func (d *evaluationStateData) GetTerraformAttr(_ context.Context, addr addrs.TerraformAttr, rng tfdiags.SourceRange) (cty.Value, tfdiags.Diagnostics) {
|
||||
|
||||
@@ -234,15 +234,16 @@ func (d *evaluationStateData) staticValidateResourceReference(ctx context.Contex
|
||||
|
||||
// TODO: Plugin a suitable context.Context through to here.
|
||||
providerFqn := modCfg.Module.ProviderForLocalConfig(cfg.ProviderConfigAddr())
|
||||
schema, _, err := d.Evaluator.Plugins.ResourceTypeSchema(ctx, providerFqn, addr.Mode, addr.Type)
|
||||
if err != nil {
|
||||
schema, schemaDiags := d.Evaluator.Plugins.ResourceTypeSchema(ctx, providerFqn, addr.Mode, addr.Type)
|
||||
diags = diags.Append(schemaDiags)
|
||||
if schemaDiags.HasErrors() {
|
||||
// Prior validation should've taken care of a schema lookup error,
|
||||
// so we should never get here but we'll handle it here anyway for
|
||||
// robustness.
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: `Failed provider schema lookup`,
|
||||
Detail: fmt.Sprintf(`Couldn't load schema for %s resource type %q in %s: %s.`, modeAdjective, addr.Type, providerFqn.String(), err),
|
||||
Detail: fmt.Sprintf(`Couldn't load schema for %s resource type %q in %s: %s.`, modeAdjective, addr.Type, providerFqn.String(), schemaDiags),
|
||||
Subject: rng.ToHCL().Ptr(),
|
||||
})
|
||||
}
|
||||
@@ -278,7 +279,7 @@ func (d *evaluationStateData) staticValidateResourceReference(ctx context.Contex
|
||||
|
||||
// If we got this far then we'll try to validate the remaining traversal
|
||||
// steps against our schema.
|
||||
moreDiags := schema.StaticValidateTraversal(remain)
|
||||
moreDiags := schema.Block.StaticValidateTraversal(remain)
|
||||
diags = diags.Append(moreDiags)
|
||||
|
||||
return diags
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/configs"
|
||||
"github.com/opentofu/opentofu/internal/dag"
|
||||
"github.com/opentofu/opentofu/internal/plans"
|
||||
"github.com/opentofu/opentofu/internal/plugins"
|
||||
"github.com/opentofu/opentofu/internal/states"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
)
|
||||
@@ -40,7 +41,7 @@ type ApplyGraphBuilder struct {
|
||||
|
||||
// Plugins is a library of the plug-in components (providers and
|
||||
// provisioners) available for use.
|
||||
Plugins *contextPlugins
|
||||
Plugins plugins.PluginManager
|
||||
|
||||
// Targets are resources to target. This is only required to make sure
|
||||
// unnecessary outputs aren't included in the apply graph. The plan
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/addrs"
|
||||
"github.com/opentofu/opentofu/internal/configs"
|
||||
"github.com/opentofu/opentofu/internal/dag"
|
||||
"github.com/opentofu/opentofu/internal/plugins"
|
||||
"github.com/opentofu/opentofu/internal/states"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
)
|
||||
@@ -44,7 +45,7 @@ type EvalGraphBuilder struct {
|
||||
|
||||
// Plugins is a library of plug-in components (providers and
|
||||
// provisioners) available for use.
|
||||
Plugins *contextPlugins
|
||||
Plugins plugins.PluginManager
|
||||
|
||||
ProviderFunctionTracker ProviderFunctionMapping
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/addrs"
|
||||
"github.com/opentofu/opentofu/internal/configs"
|
||||
"github.com/opentofu/opentofu/internal/dag"
|
||||
"github.com/opentofu/opentofu/internal/plugins"
|
||||
"github.com/opentofu/opentofu/internal/refactoring"
|
||||
"github.com/opentofu/opentofu/internal/states"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
@@ -43,7 +44,7 @@ type PlanGraphBuilder struct {
|
||||
|
||||
// Plugins is a library of plug-in components (providers and
|
||||
// provisioners) available for use.
|
||||
Plugins *contextPlugins
|
||||
Plugins plugins.PluginManager
|
||||
|
||||
// Targets are resources to target
|
||||
Targets []addrs.Targetable
|
||||
|
||||
@@ -110,11 +110,8 @@ func (w *ContextGraphWalker) EvalContext() EvalContext {
|
||||
Plugins: w.Context.plugins,
|
||||
MoveResultsValue: w.MoveResults,
|
||||
ImportResolverValue: w.ImportResolver,
|
||||
ProviderCache: w.providerCache,
|
||||
ProviderInputConfig: w.Context.providerInputConfig,
|
||||
ProviderLock: &w.providerLock,
|
||||
ProvisionerCache: w.provisionerCache,
|
||||
ProvisionerLock: &w.provisionerLock,
|
||||
ChangesValue: w.Changes,
|
||||
ChecksValue: w.Checks,
|
||||
StateValue: w.State,
|
||||
|
||||
@@ -222,10 +222,6 @@ func (n *nodeCloseModule) Execute(_ context.Context, evalCtx EvalContext, op wal
|
||||
return
|
||||
}
|
||||
|
||||
// If this is the root module, we are cleaning up the walk, so close
|
||||
// any running provisioners
|
||||
diags = diags.Append(evalCtx.CloseProvisioners())
|
||||
|
||||
switch op {
|
||||
case walkApply, walkDestroy:
|
||||
state := evalCtx.State().Lock()
|
||||
|
||||
@@ -11,11 +11,8 @@ import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/opentofu/opentofu/internal/addrs"
|
||||
"github.com/opentofu/opentofu/internal/configs/configschema"
|
||||
"github.com/opentofu/opentofu/internal/providers"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
"github.com/opentofu/opentofu/internal/tracing"
|
||||
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
|
||||
@@ -57,73 +54,44 @@ var (
|
||||
|
||||
// GraphNodeExecutable
|
||||
func (n *NodeApplyableProvider) Execute(ctx context.Context, evalCtx EvalContext, op walkOperation) tfdiags.Diagnostics {
|
||||
instances, diags := n.initInstances(ctx, evalCtx, op)
|
||||
|
||||
for key, provider := range instances {
|
||||
diags = diags.Append(n.executeInstance(ctx, evalCtx, op, key, provider))
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
func (n *NodeApplyableProvider) initInstances(ctx context.Context, evalCtx EvalContext, op walkOperation) (map[addrs.InstanceKey]providers.Interface, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
var initKeys []addrs.InstanceKey
|
||||
// config -> init (different due to validate skipping most for_each logic)
|
||||
instanceKeys := make(map[addrs.InstanceKey]addrs.InstanceKey)
|
||||
if n.Config == nil || n.Config.Instances == nil {
|
||||
initKeys = append(initKeys, addrs.NoKey)
|
||||
instanceKeys[addrs.NoKey] = addrs.NoKey
|
||||
} else if op == walkValidate {
|
||||
// Instances are set AND we are validating
|
||||
initKeys = append(initKeys, addrs.NoKey)
|
||||
for key := range n.Config.Instances {
|
||||
instanceKeys[key] = addrs.NoKey
|
||||
}
|
||||
} else {
|
||||
// Instances are set AND we are not validating
|
||||
for key := range n.Config.Instances {
|
||||
initKeys = append(initKeys, key)
|
||||
instanceKeys[key] = key
|
||||
}
|
||||
}
|
||||
|
||||
for _, key := range initKeys {
|
||||
_, err := evalCtx.InitProvider(ctx, n.Addr, key)
|
||||
diags = diags.Append(err)
|
||||
}
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
instances := make(map[addrs.InstanceKey]providers.Interface)
|
||||
for configKey, initKey := range instanceKeys {
|
||||
provider, _, err := getProvider(ctx, evalCtx, n.Addr, initKey)
|
||||
diags = diags.Append(err)
|
||||
instances[configKey] = provider
|
||||
}
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
return instances, diags
|
||||
}
|
||||
func (n *NodeApplyableProvider) executeInstance(ctx context.Context, evalCtx EvalContext, op walkOperation, providerKey addrs.InstanceKey, provider providers.Interface) tfdiags.Diagnostics {
|
||||
switch op {
|
||||
case walkValidate:
|
||||
log.Printf("[TRACE] NodeApplyableProvider: validating configuration for %s", n.Addr)
|
||||
return n.ValidateProvider(ctx, evalCtx, providerKey, provider)
|
||||
return n.validateProviderInstances(ctx, evalCtx)
|
||||
case walkPlan, walkPlanDestroy, walkApply, walkDestroy:
|
||||
log.Printf("[TRACE] NodeApplyableProvider: configuring %s", n.Addr)
|
||||
return n.ConfigureProvider(ctx, evalCtx, providerKey, provider, false)
|
||||
return n.configureProviderInstances(ctx, evalCtx, false)
|
||||
case walkImport:
|
||||
log.Printf("[TRACE] NodeApplyableProvider: configuring %s (requiring that configuration is wholly known)", n.Addr)
|
||||
return n.ConfigureProvider(ctx, evalCtx, providerKey, provider, true)
|
||||
return n.configureProviderInstances(ctx, evalCtx, true)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NodeApplyableProvider) ValidateProvider(ctx context.Context, evalCtx EvalContext, providerKey addrs.InstanceKey, provider providers.Interface) tfdiags.Diagnostics {
|
||||
func (n *NodeApplyableProvider) validateProviderInstances(ctx context.Context, evalCtx EvalContext) tfdiags.Diagnostics {
|
||||
if n.Config == nil || n.Config.Instances == nil {
|
||||
return n.validateProviderInstance(ctx, evalCtx, addrs.NoKey)
|
||||
}
|
||||
var diags tfdiags.Diagnostics
|
||||
for key := range n.Config.Instances {
|
||||
diags = diags.Append(n.validateProviderInstance(ctx, evalCtx, key))
|
||||
}
|
||||
return diags
|
||||
}
|
||||
|
||||
func (n *NodeApplyableProvider) configureProviderInstances(ctx context.Context, evalCtx EvalContext, verifyConfigIsKnown bool) tfdiags.Diagnostics {
|
||||
if n.Config == nil || n.Config.Instances == nil {
|
||||
return n.configureProviderInstance(ctx, evalCtx, addrs.NoKey, verifyConfigIsKnown)
|
||||
}
|
||||
|
||||
var diags tfdiags.Diagnostics
|
||||
for key := range n.Config.Instances {
|
||||
diags = diags.Append(n.configureProviderInstance(ctx, evalCtx, key, verifyConfigIsKnown))
|
||||
}
|
||||
return diags
|
||||
}
|
||||
|
||||
func (n *NodeApplyableProvider) validateProviderInstance(ctx context.Context, evalCtx EvalContext, providerKey addrs.InstanceKey) tfdiags.Diagnostics {
|
||||
_, span := tracing.Tracer().Start(
|
||||
ctx, "Validate provider configuration",
|
||||
tracing.SpanAttributes(
|
||||
@@ -149,7 +117,7 @@ func (n *NodeApplyableProvider) ValidateProvider(ctx context.Context, evalCtx Ev
|
||||
return nil
|
||||
}
|
||||
|
||||
schemaResp := provider.GetProviderSchema(ctx)
|
||||
schemaResp := evalCtx.Providers().GetProviderSchema(ctx, n.Addr.Provider)
|
||||
diags := schemaResp.Diagnostics.InConfigBody(configBody, n.Addr.InstanceString(providerKey))
|
||||
if diags.HasErrors() {
|
||||
tracing.SetSpanError(span, diags)
|
||||
@@ -180,21 +148,17 @@ func (n *NodeApplyableProvider) ValidateProvider(ctx context.Context, evalCtx Ev
|
||||
// stripped out before sending this to the provider
|
||||
unmarkedConfigVal, _ := configVal.UnmarkDeep()
|
||||
|
||||
req := providers.ValidateProviderConfigRequest{
|
||||
Config: unmarkedConfigVal,
|
||||
}
|
||||
|
||||
validateResp := provider.ValidateProviderConfig(ctx, req)
|
||||
diags = diags.Append(validateResp.Diagnostics.InConfigBody(configBody, n.Addr.InstanceString(providerKey)))
|
||||
validateResp := evalCtx.Providers().ValidateProviderConfig(ctx, n.Addr.Provider, unmarkedConfigVal)
|
||||
diags = diags.Append(validateResp.InConfigBody(configBody, n.Addr.InstanceString(providerKey)))
|
||||
|
||||
tracing.SetSpanError(span, diags)
|
||||
return diags
|
||||
}
|
||||
|
||||
// ConfigureProvider configures a provider that is already initialized and retrieved.
|
||||
// configureProviderInstance configures a provider that is already initialized and retrieved.
|
||||
// If verifyConfigIsKnown is true, ConfigureProvider will return an error if the
|
||||
// provider configVal is not wholly known and is meant only for use during import.
|
||||
func (n *NodeApplyableProvider) ConfigureProvider(ctx context.Context, evalCtx EvalContext, providerKey addrs.InstanceKey, provider providers.Interface, verifyConfigIsKnown bool) tfdiags.Diagnostics {
|
||||
func (n *NodeApplyableProvider) configureProviderInstance(ctx context.Context, evalCtx EvalContext, providerKey addrs.InstanceKey, verifyConfigIsKnown bool) tfdiags.Diagnostics {
|
||||
_, span := tracing.Tracer().Start(
|
||||
ctx, "Configure provider",
|
||||
tracing.SpanAttributes(
|
||||
@@ -214,7 +178,7 @@ func (n *NodeApplyableProvider) ConfigureProvider(ctx context.Context, evalCtx E
|
||||
|
||||
configBody := buildProviderConfig(ctx, evalCtx, n.Addr, config)
|
||||
|
||||
resp := provider.GetProviderSchema(ctx)
|
||||
resp := evalCtx.Providers().GetProviderSchema(ctx, n.Addr.Provider)
|
||||
diags := resp.Diagnostics.InConfigBody(configBody, n.Addr.InstanceString(providerKey))
|
||||
if diags.HasErrors() {
|
||||
tracing.SetSpanError(span, diags)
|
||||
@@ -249,40 +213,7 @@ func (n *NodeApplyableProvider) ConfigureProvider(ctx context.Context, evalCtx E
|
||||
// stripped out before sending this to the provider
|
||||
unmarkedConfigVal, _ := configVal.UnmarkDeep()
|
||||
|
||||
// Allow the provider to validate and insert any defaults into the full
|
||||
// configuration.
|
||||
req := providers.ValidateProviderConfigRequest{
|
||||
Config: unmarkedConfigVal,
|
||||
}
|
||||
|
||||
// ValidateProviderConfig is only used for validation. We are intentionally
|
||||
// ignoring the PreparedConfig field to maintain existing behavior.
|
||||
validateResp := provider.ValidateProviderConfig(ctx, req)
|
||||
diags = diags.Append(validateResp.Diagnostics.InConfigBody(configBody, n.Addr.InstanceString(providerKey)))
|
||||
if diags.HasErrors() && config == nil {
|
||||
// If there isn't an explicit "provider" block in the configuration,
|
||||
// this error message won't be very clear. Add some detail to the error
|
||||
// message in this case.
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Invalid provider configuration",
|
||||
fmt.Sprintf(providerConfigErr, n.Addr.Provider),
|
||||
))
|
||||
}
|
||||
|
||||
if diags.HasErrors() {
|
||||
tracing.SetSpanError(span, diags)
|
||||
return diags
|
||||
}
|
||||
|
||||
// If the provider returns something different, log a warning to help
|
||||
// indicate to provider developers that the value is not used.
|
||||
preparedCfg := validateResp.PreparedConfig
|
||||
if preparedCfg != cty.NilVal && !preparedCfg.IsNull() && !preparedCfg.RawEquals(unmarkedConfigVal) {
|
||||
log.Printf("[WARN] ValidateProviderConfig from %q changed the config value, but that value is unused", n.Addr)
|
||||
}
|
||||
|
||||
configDiags := evalCtx.ConfigureProvider(ctx, n.Addr, providerKey, unmarkedConfigVal)
|
||||
configDiags := evalCtx.Providers().ConfigureProvider(ctx, n.Addr.InstanceCorrect(providerKey), unmarkedConfigVal)
|
||||
diags = diags.Append(configDiags.InConfigBody(configBody, n.Addr.InstanceString(providerKey)))
|
||||
if diags.HasErrors() && config == nil {
|
||||
// If there isn't an explicit "provider" block in the configuration,
|
||||
|
||||
@@ -8,7 +8,6 @@ package tofu
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/opentofu/opentofu/internal/addrs"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
)
|
||||
|
||||
@@ -24,6 +23,6 @@ var _ GraphNodeExecutable = (*NodeEvalableProvider)(nil)
|
||||
|
||||
// GraphNodeExecutable
|
||||
func (n *NodeEvalableProvider) Execute(ctx context.Context, evalCtx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) {
|
||||
_, err := evalCtx.InitProvider(ctx, n.Addr, addrs.NoKey)
|
||||
return diags.Append(err)
|
||||
// TODO remove me
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -616,7 +616,7 @@ func (n *NodeAbstractResourceInstance) readResourceInstanceState(ctx context.Con
|
||||
}
|
||||
if isResourceMovedToDifferentType(addr, prevAddr) {
|
||||
src, diags = moveResourceState(transformArgs)
|
||||
} else {
|
||||
} else if !n.ResolvedProvider.IsMocked {
|
||||
src, diags = upgradeResourceState(transformArgs)
|
||||
}
|
||||
|
||||
@@ -675,7 +675,7 @@ func (n *NodeAbstractResourceInstance) readResourceInstanceStateDeposed(ctx cont
|
||||
}
|
||||
if isResourceMovedToDifferentType(addr, prevAddr) {
|
||||
src, diags = moveResourceState(transformArgs)
|
||||
} else {
|
||||
} else if !n.ResolvedProvider.IsMocked {
|
||||
src, diags = upgradeResourceState(transformArgs)
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/plans"
|
||||
"github.com/opentofu/opentofu/internal/plans/objchange"
|
||||
"github.com/opentofu/opentofu/internal/providers"
|
||||
"github.com/opentofu/opentofu/internal/provisioners"
|
||||
"github.com/opentofu/opentofu/internal/states"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
)
|
||||
@@ -282,8 +281,12 @@ func (n *NodeAbstractResourceInstance) resolveProvider(ctx context.Context, eval
|
||||
panic("EnsureProvider used with uninitialized provider configuration address")
|
||||
}
|
||||
|
||||
provider := evalCtx.Provider(ctx, n.ResolvedProvider.ProviderConfig, n.ResolvedProviderKey)
|
||||
if provider != nil {
|
||||
if n.ResolvedProvider.IsMocked {
|
||||
// All good
|
||||
return nil
|
||||
}
|
||||
|
||||
if evalCtx.Providers().IsProviderConfigured(n.ResolvedProvider.ProviderConfig.InstanceCorrect(n.ResolvedProviderKey)) {
|
||||
// All good
|
||||
return nil
|
||||
}
|
||||
@@ -2684,13 +2687,7 @@ func (n *NodeAbstractResourceInstance) applyProvisioners(ctx context.Context, ev
|
||||
for _, prov := range provs {
|
||||
log.Printf("[TRACE] applyProvisioners: provisioning %s with %q", n.Addr, prov.Type)
|
||||
|
||||
// Get the provisioner
|
||||
provisioner, err := evalCtx.Provisioner(prov.Type)
|
||||
if err != nil {
|
||||
return diags.Append(err)
|
||||
}
|
||||
|
||||
schema, err := evalCtx.ProvisionerSchema(prov.Type)
|
||||
schema, err := evalCtx.Provisioners().ProvisionerSchema(prov.Type)
|
||||
if err != nil {
|
||||
// This error probably won't be a great diagnostic, but in practice
|
||||
// we typically catch this problem long before we get here, so
|
||||
@@ -2790,12 +2787,13 @@ func (n *NodeAbstractResourceInstance) applyProvisioners(ctx context.Context, ev
|
||||
}
|
||||
|
||||
output := CallbackUIOutput{OutputFn: outputFn}
|
||||
resp := provisioner.ProvisionResource(provisioners.ProvisionResourceRequest{
|
||||
Config: unmarkedConfig,
|
||||
Connection: unmarkedConnInfo,
|
||||
UIOutput: &output,
|
||||
})
|
||||
applyDiags := resp.Diagnostics.InConfigBody(prov.Config, n.Addr.String())
|
||||
applyDiags := evalCtx.Provisioners().ProvisionResource(
|
||||
ctx,
|
||||
prov.Type,
|
||||
unmarkedConfig,
|
||||
unmarkedConnInfo,
|
||||
&output,
|
||||
).InConfigBody(prov.Config, n.Addr.String())
|
||||
|
||||
// Call post hook
|
||||
hookErr := evalCtx.Hook(func(h Hook) (HookAction, error) {
|
||||
@@ -3215,10 +3213,7 @@ func resourceInstancePrevRunAddr(evalCtx EvalContext, currentAddr addrs.AbsResou
|
||||
}
|
||||
|
||||
func (n *NodeAbstractResourceInstance) getProvider(ctx context.Context, evalCtx EvalContext) (providers.Interface, providers.ProviderSchema, error) {
|
||||
underlyingProvider, schema, err := getProvider(ctx, evalCtx, n.ResolvedProvider.ProviderConfig, n.ResolvedProviderKey)
|
||||
if err != nil {
|
||||
return nil, providers.ProviderSchema{}, err
|
||||
}
|
||||
schema := evalCtx.Providers().GetProviderSchema(ctx, n.ResolvedProvider.ProviderConfig.Provider)
|
||||
|
||||
var isOverridden bool
|
||||
var overrideValues map[string]cty.Value
|
||||
@@ -3250,9 +3245,13 @@ func (n *NodeAbstractResourceInstance) getProvider(ctx context.Context, evalCtx
|
||||
}
|
||||
|
||||
if isOverridden {
|
||||
provider, err := newProviderForTestWithSchema(underlyingProvider, schema, overrideValues)
|
||||
provider, err := newProviderForTestWithSchema(n.ResolvedProvider.ProviderConfig.Provider, evalCtx.Providers(), schema, overrideValues)
|
||||
return provider, schema, err
|
||||
}
|
||||
underlyingProvider, _, err := getProvider(ctx, evalCtx, n.ResolvedProvider.ProviderConfig, n.ResolvedProviderKey)
|
||||
if err != nil {
|
||||
return nil, providers.ProviderSchema{}, err
|
||||
}
|
||||
|
||||
return underlyingProvider, schema, err
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/instances"
|
||||
"github.com/opentofu/opentofu/internal/lang"
|
||||
"github.com/opentofu/opentofu/internal/providers"
|
||||
"github.com/opentofu/opentofu/internal/provisioners"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
"github.com/opentofu/opentofu/internal/tracing"
|
||||
"github.com/opentofu/opentofu/internal/tracing/traceattrs"
|
||||
@@ -130,16 +129,7 @@ func (n *NodeValidatableResource) Execute(ctx context.Context, evalCtx EvalConte
|
||||
func (n *NodeValidatableResource) validateProvisioner(ctx context.Context, evalCtx EvalContext, p *configs.Provisioner) tfdiags.Diagnostics {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
provisioner, err := evalCtx.Provisioner(p.Type)
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
return diags
|
||||
}
|
||||
|
||||
if provisioner == nil {
|
||||
return diags.Append(fmt.Errorf("provisioner %s not initialized", p.Type))
|
||||
}
|
||||
provisionerSchema, err := evalCtx.ProvisionerSchema(p.Type)
|
||||
provisionerSchema, err := evalCtx.Provisioners().ProvisionerSchema(p.Type)
|
||||
if err != nil {
|
||||
return diags.Append(fmt.Errorf("failed to read schema for provisioner %s: %w", p.Type, err))
|
||||
}
|
||||
@@ -158,12 +148,8 @@ func (n *NodeValidatableResource) validateProvisioner(ctx context.Context, evalC
|
||||
|
||||
// Use unmarked value for validate request
|
||||
unmarkedConfigVal, _ := configVal.UnmarkDeep()
|
||||
req := provisioners.ValidateProvisionerConfigRequest{
|
||||
Config: unmarkedConfigVal,
|
||||
}
|
||||
|
||||
resp := provisioner.ValidateProvisionerConfig(req)
|
||||
diags = diags.Append(resp.Diagnostics)
|
||||
valDiags := evalCtx.Provisioners().ValidateProvisionerConfig(ctx, p.Type, unmarkedConfigVal)
|
||||
diags = diags.Append(valDiags)
|
||||
|
||||
if p.Connection != nil {
|
||||
// We can't comprehensively validate the connection config since its
|
||||
@@ -187,12 +173,6 @@ func (n *NodeValidatableResource) evaluateBlock(ctx context.Context, evalCtx Eva
|
||||
func (n *NodeValidatableResource) validateResource(ctx context.Context, evalCtx EvalContext) tfdiags.Diagnostics {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
provider, providerSchema, err := getProvider(ctx, evalCtx, n.ResolvedProvider.ProviderConfig, addrs.NoKey) // Provider Instance Keys are ignored during validate
|
||||
diags = diags.Append(err)
|
||||
if diags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
|
||||
keyData := EvalDataForNoInstanceKey
|
||||
|
||||
switch {
|
||||
@@ -256,130 +236,68 @@ func (n *NodeValidatableResource) validateResource(ctx context.Context, evalCtx
|
||||
// because the ProviderAddr for the resource isn't available on the EvalValidate
|
||||
// struct.
|
||||
|
||||
// Provider entry point varies depending on resource mode, because
|
||||
// managed resources and data resources are two distinct concepts
|
||||
// in the provider abstraction.
|
||||
switch n.Config.Mode {
|
||||
case addrs.ManagedResourceMode:
|
||||
schema, _ := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type)
|
||||
if schema == nil {
|
||||
suggestion := n.noResourceSchemaSuggestion(providerSchema)
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid resource type",
|
||||
Detail: fmt.Sprintf("The provider %s does not support resource type %q.%s", n.Provider().ForDisplay(), n.Config.Type, suggestion),
|
||||
Subject: &n.Config.TypeRange,
|
||||
})
|
||||
return diags
|
||||
}
|
||||
providerSchema := evalCtx.Providers().GetProviderSchema(ctx, n.ResolvedProvider.ProviderConfig.Provider)
|
||||
diags = diags.Append(providerSchema.Diagnostics)
|
||||
if diags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
|
||||
configVal, _, valDiags := evalCtx.EvaluateBlock(ctx, n.Config.Config, schema, nil, keyData)
|
||||
diags = diags.Append(valDiags.InConfigBody(n.Config.Config, n.Addr.String()))
|
||||
if valDiags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
schema, _ := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type)
|
||||
if schema == nil {
|
||||
suggestion := n.noResourceSchemaSuggestion(providerSchema)
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Invalid %s type", n.Config.Mode.HumanString()),
|
||||
Detail: fmt.Sprintf("The provider %s does not support %s type %q.%s", n.Provider().ForDisplay(), n.Config.Mode.HumanString(), n.Config.Type, suggestion),
|
||||
Subject: &n.Config.TypeRange,
|
||||
})
|
||||
return diags
|
||||
}
|
||||
|
||||
if n.Config.Managed != nil { // can be nil only in tests with poorly-configured mocks
|
||||
for _, traversal := range n.Config.Managed.IgnoreChanges {
|
||||
// validate the ignore_changes traversals apply.
|
||||
moreDiags := schema.StaticValidateTraversal(traversal)
|
||||
diags = diags.Append(moreDiags)
|
||||
configVal, _, valDiags := evalCtx.EvaluateBlock(ctx, n.Config.Config, schema, nil, keyData)
|
||||
diags = diags.Append(valDiags.InConfigBody(n.Config.Config, n.Addr.String()))
|
||||
if valDiags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
|
||||
// ignore_changes cannot be used for Computed attributes,
|
||||
// unless they are also Optional.
|
||||
// If the traversal was valid, convert it to a cty.Path and
|
||||
// use that to check whether the Attribute is Computed and
|
||||
// non-Optional.
|
||||
if !diags.HasErrors() {
|
||||
path := traversalToPath(traversal)
|
||||
if n.Config.Managed != nil { // can be nil only in tests with poorly-configured mocks
|
||||
for _, traversal := range n.Config.Managed.IgnoreChanges {
|
||||
// validate the ignore_changes traversals apply.
|
||||
moreDiags := schema.StaticValidateTraversal(traversal)
|
||||
diags = diags.Append(moreDiags)
|
||||
|
||||
attrSchema := schema.AttributeByPath(path)
|
||||
// ignore_changes cannot be used for Computed attributes,
|
||||
// unless they are also Optional.
|
||||
// If the traversal was valid, convert it to a cty.Path and
|
||||
// use that to check whether the Attribute is Computed and
|
||||
// non-Optional.
|
||||
if !diags.HasErrors() {
|
||||
path := traversalToPath(traversal)
|
||||
|
||||
if attrSchema != nil && !attrSchema.Optional && attrSchema.Computed {
|
||||
// ignore_changes uses absolute traversal syntax in config despite
|
||||
// using relative traversals, so we strip the leading "." added by
|
||||
// FormatCtyPath for a better error message.
|
||||
attrDisplayPath := strings.TrimPrefix(tfdiags.FormatCtyPath(path), ".")
|
||||
attrSchema := schema.AttributeByPath(path)
|
||||
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagWarning,
|
||||
Summary: "Redundant ignore_changes element",
|
||||
Detail: fmt.Sprintf("Adding an attribute name to ignore_changes tells OpenTofu to ignore future changes to the argument in configuration after the object has been created, retaining the value originally configured.\n\nThe attribute %s is decided by the provider alone and therefore there can be no configured value to compare with. Including this attribute in ignore_changes has no effect. Remove the attribute from ignore_changes to quiet this warning.", attrDisplayPath),
|
||||
Subject: &n.Config.TypeRange,
|
||||
})
|
||||
}
|
||||
if attrSchema != nil && !attrSchema.Optional && attrSchema.Computed {
|
||||
// ignore_changes uses absolute traversal syntax in config despite
|
||||
// using relative traversals, so we strip the leading "." added by
|
||||
// FormatCtyPath for a better error message.
|
||||
attrDisplayPath := strings.TrimPrefix(tfdiags.FormatCtyPath(path), ".")
|
||||
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagWarning,
|
||||
Summary: "Redundant ignore_changes element",
|
||||
Detail: fmt.Sprintf("Adding an attribute name to ignore_changes tells OpenTofu to ignore future changes to the argument in configuration after the object has been created, retaining the value originally configured.\n\nThe attribute %s is decided by the provider alone and therefore there can be no configured value to compare with. Including this attribute in ignore_changes has no effect. Remove the attribute from ignore_changes to quiet this warning.", attrDisplayPath),
|
||||
Subject: &n.Config.TypeRange,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use unmarked value for validate request
|
||||
unmarkedConfigVal, _ := configVal.UnmarkDeep()
|
||||
req := providers.ValidateResourceConfigRequest{
|
||||
TypeName: n.Config.Type,
|
||||
Config: unmarkedConfigVal,
|
||||
}
|
||||
|
||||
resp := provider.ValidateResourceConfig(ctx, req)
|
||||
diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config, n.Addr.String()))
|
||||
|
||||
case addrs.DataResourceMode:
|
||||
schema, _ := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type)
|
||||
if schema == nil {
|
||||
suggestion := n.noResourceSchemaSuggestion(providerSchema)
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid data source",
|
||||
Detail: fmt.Sprintf("The provider %s does not support data source %q.%s", n.Provider().ForDisplay(), n.Config.Type, suggestion),
|
||||
Subject: &n.Config.TypeRange,
|
||||
})
|
||||
return diags
|
||||
}
|
||||
|
||||
configVal, _, valDiags := evalCtx.EvaluateBlock(ctx, n.Config.Config, schema, nil, keyData)
|
||||
diags = diags.Append(valDiags.InConfigBody(n.Config.Config, n.Addr.String()))
|
||||
if valDiags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
|
||||
// Use unmarked value for validate request
|
||||
unmarkedConfigVal, _ := configVal.UnmarkDeep()
|
||||
req := providers.ValidateDataResourceConfigRequest{
|
||||
TypeName: n.Config.Type,
|
||||
Config: unmarkedConfigVal,
|
||||
}
|
||||
|
||||
resp := provider.ValidateDataResourceConfig(ctx, req)
|
||||
diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config, n.Addr.String()))
|
||||
case addrs.EphemeralResourceMode:
|
||||
schema, _ := providerSchema.SchemaForResourceType(n.Config.Mode, n.Config.Type)
|
||||
if schema == nil {
|
||||
suggestion := n.noResourceSchemaSuggestion(providerSchema)
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid ephemeral resource",
|
||||
Detail: fmt.Sprintf("The provider %s does not support ephemeral resource %q.%s", n.Provider().ForDisplay(), n.Config.Type, suggestion),
|
||||
Subject: &n.Config.TypeRange,
|
||||
})
|
||||
return diags
|
||||
}
|
||||
|
||||
configVal, _, valDiags := evalCtx.EvaluateBlock(ctx, n.Config.Config, schema, nil, keyData)
|
||||
diags = diags.Append(valDiags)
|
||||
if valDiags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
|
||||
// Use unmarked value for validate request
|
||||
unmarkedConfigVal, _ := configVal.UnmarkDeep()
|
||||
req := providers.ValidateEphemeralConfigRequest{
|
||||
TypeName: n.Config.Type,
|
||||
Config: unmarkedConfigVal,
|
||||
}
|
||||
|
||||
resp := provider.ValidateEphemeralConfig(ctx, req)
|
||||
diags = diags.Append(resp.Diagnostics.InConfigBody(n.Config.Config, n.Addr.String()))
|
||||
}
|
||||
|
||||
// Use unmarked value for validate request
|
||||
unmarkedConfigVal, _ := configVal.UnmarkDeep()
|
||||
resp := evalCtx.Providers().ValidateResourceConfig(ctx, n.ResolvedProvider.ProviderConfig.Provider, n.Config.Mode, n.Config.Type, unmarkedConfigVal)
|
||||
diags = diags.Append(resp.InConfigBody(n.Config.Config, n.Addr.String()))
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/opentofu/opentofu/internal/addrs"
|
||||
"github.com/opentofu/opentofu/internal/configs/hcl2shim"
|
||||
"github.com/opentofu/opentofu/internal/plugins"
|
||||
"github.com/opentofu/opentofu/internal/providers"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
@@ -24,21 +25,22 @@ var _ providers.Interface = &providerForTest{}
|
||||
// and overrides for that one specific resource instance, any other usage is a bug in OpenTofu and should
|
||||
// be corrected.
|
||||
type providerForTest struct {
|
||||
// providers.Interface is not embedded to make it safer to extend
|
||||
// the interface without silently breaking providerForTest functionality.
|
||||
internal providers.Interface
|
||||
schema providers.ProviderSchema
|
||||
addr addrs.Provider
|
||||
providers plugins.ProviderManager
|
||||
|
||||
schema providers.ProviderSchema
|
||||
|
||||
overrideValues map[string]cty.Value
|
||||
}
|
||||
|
||||
func newProviderForTestWithSchema(internal providers.Interface, schema providers.ProviderSchema, overrideValues map[string]cty.Value) (providerForTest, error) {
|
||||
func newProviderForTestWithSchema(addr addrs.Provider, providers plugins.ProviderManager, schema providers.ProviderSchema, overrideValues map[string]cty.Value) (providerForTest, error) {
|
||||
if schema.Diagnostics.HasErrors() {
|
||||
return providerForTest{}, fmt.Errorf("invalid provider schema for test wrapper: %w", schema.Diagnostics.Err())
|
||||
}
|
||||
|
||||
return providerForTest{
|
||||
internal: internal,
|
||||
addr: addr,
|
||||
providers: providers,
|
||||
schema: schema,
|
||||
overrideValues: overrideValues,
|
||||
}, nil
|
||||
@@ -120,10 +122,7 @@ func (p providerForTest) ValidateProviderConfig(_ context.Context, _ providers.V
|
||||
// accessible so it is safe to wipe metadata as well. See Config.transformProviderConfigsForTest
|
||||
// for more details.
|
||||
func (p providerForTest) GetProviderSchema(ctx context.Context) providers.GetProviderSchemaResponse {
|
||||
providerSchema := p.internal.GetProviderSchema(ctx)
|
||||
providerSchema.Provider = providers.Schema{}
|
||||
providerSchema.ProviderMeta = providers.Schema{}
|
||||
return providerSchema
|
||||
return p.schema
|
||||
}
|
||||
|
||||
// providerForTest doesn't configure its internal provider because it is mocked.
|
||||
@@ -144,35 +143,41 @@ func (p providerForTest) MoveResourceState(context.Context, providers.MoveResour
|
||||
// if called via providerForTest because importing is not supported in testing framework.
|
||||
|
||||
func (p providerForTest) ValidateResourceConfig(ctx context.Context, r providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse {
|
||||
return p.internal.ValidateResourceConfig(ctx, r)
|
||||
return providers.ValidateResourceConfigResponse{
|
||||
Diagnostics: p.providers.ValidateResourceConfig(ctx, p.addr, addrs.ManagedResourceMode, r.TypeName, r.Config),
|
||||
}
|
||||
}
|
||||
|
||||
func (p providerForTest) ValidateDataResourceConfig(ctx context.Context, r providers.ValidateDataResourceConfigRequest) providers.ValidateDataResourceConfigResponse {
|
||||
return p.internal.ValidateDataResourceConfig(ctx, r)
|
||||
return providers.ValidateDataResourceConfigResponse{
|
||||
Diagnostics: p.providers.ValidateResourceConfig(ctx, p.addr, addrs.DataResourceMode, r.TypeName, r.Config),
|
||||
}
|
||||
}
|
||||
|
||||
func (p providerForTest) UpgradeResourceState(ctx context.Context, r providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse {
|
||||
return p.internal.UpgradeResourceState(ctx, r)
|
||||
panic("Upgrading is not supported in testing context. providerForTest must not be used to call UpgradeResourceState")
|
||||
}
|
||||
|
||||
func (p providerForTest) ValidateEphemeralConfig(ctx context.Context, request providers.ValidateEphemeralConfigRequest) providers.ValidateEphemeralConfigResponse {
|
||||
return p.internal.ValidateEphemeralConfig(ctx, request)
|
||||
func (p providerForTest) ValidateEphemeralConfig(ctx context.Context, r providers.ValidateEphemeralConfigRequest) providers.ValidateEphemeralConfigResponse {
|
||||
return providers.ValidateEphemeralConfigResponse{
|
||||
Diagnostics: p.providers.ValidateResourceConfig(ctx, p.addr, addrs.EphemeralResourceMode, r.TypeName, r.Config),
|
||||
}
|
||||
}
|
||||
|
||||
func (p providerForTest) Stop(ctx context.Context) error {
|
||||
return p.internal.Stop(ctx)
|
||||
panic("Stopping is not supported in testing context. providerForTest must not be used to call Stop")
|
||||
}
|
||||
|
||||
func (p providerForTest) GetFunctions(ctx context.Context) providers.GetFunctionsResponse {
|
||||
return p.internal.GetFunctions(ctx)
|
||||
panic("Functions are not supported in testing context. providerForTest must not be used to call GetFunctions")
|
||||
}
|
||||
|
||||
func (p providerForTest) CallFunction(ctx context.Context, r providers.CallFunctionRequest) providers.CallFunctionResponse {
|
||||
return p.internal.CallFunction(ctx, r)
|
||||
panic("Functions are not supported in testing context. providerForTest must not be used to call CallFunction")
|
||||
}
|
||||
|
||||
func (p providerForTest) Close(ctx context.Context) error {
|
||||
return p.internal.Close(ctx)
|
||||
panic("Closing is not supported in testing context. providerForTest must not be used to call Close")
|
||||
}
|
||||
|
||||
func newMockValueComposer(typeName string) hcl2shim.MockValueComposer {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/addrs"
|
||||
"github.com/opentofu/opentofu/internal/configs"
|
||||
"github.com/opentofu/opentofu/internal/configs/configschema"
|
||||
"github.com/opentofu/opentofu/internal/plugins"
|
||||
"github.com/opentofu/opentofu/internal/providers"
|
||||
"github.com/opentofu/opentofu/internal/states"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
@@ -71,7 +72,7 @@ func (ss *Schemas) ProvisionerConfig(name string) *configschema.Block {
|
||||
// either misbehavior on the part of one of the providers or of the provider
|
||||
// protocol itself. When returned with errors, the returned schemas object is
|
||||
// still valid but may be incomplete.
|
||||
func loadSchemas(ctx context.Context, config *configs.Config, state *states.State, plugins *contextPlugins) (*Schemas, error) {
|
||||
func loadSchemas(ctx context.Context, config *configs.Config, state *states.State, plugins plugins.PluginSchemas) (*Schemas, error) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
provisioners, provisionerDiags := loadProvisionerSchemas(ctx, config, plugins)
|
||||
@@ -86,7 +87,7 @@ func loadSchemas(ctx context.Context, config *configs.Config, state *states.Stat
|
||||
}, diags.Err()
|
||||
}
|
||||
|
||||
func loadProviderSchemas(ctx context.Context, config *configs.Config, state *states.State, plugins *contextPlugins) (map[addrs.Provider]providers.ProviderSchema, tfdiags.Diagnostics) {
|
||||
func loadProviderSchemas(ctx context.Context, config *configs.Config, state *states.State, plugins plugins.ProviderSchemas) (map[addrs.Provider]providers.ProviderSchema, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
schemas := map[addrs.Provider]providers.ProviderSchema{}
|
||||
|
||||
@@ -109,14 +110,14 @@ func loadProviderSchemas(ctx context.Context, config *configs.Config, state *sta
|
||||
for fqn := range schemas {
|
||||
wg.Go(func() {
|
||||
log.Printf("[TRACE] LoadSchemas: retrieving schema for provider type %q", fqn.String())
|
||||
schema, err := plugins.ProviderSchema(ctx, fqn)
|
||||
schema := plugins.GetProviderSchema(ctx, fqn)
|
||||
|
||||
// Ensure that we don't race on diags or schemas now that the hard work is done
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
if err != nil {
|
||||
diags = diags.Append(err)
|
||||
if schema.Diagnostics != nil {
|
||||
diags = diags.Append(schema.Diagnostics)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -133,7 +134,7 @@ func loadProviderSchemas(ctx context.Context, config *configs.Config, state *sta
|
||||
return schemas, diags
|
||||
}
|
||||
|
||||
func loadProvisionerSchemas(ctx context.Context, config *configs.Config, plugins *contextPlugins) (map[string]*configschema.Block, tfdiags.Diagnostics) {
|
||||
func loadProvisionerSchemas(ctx context.Context, config *configs.Config, plugins plugins.ProvisionerSchemas) (map[string]*configschema.Block, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
schemas := map[string]*configschema.Block{}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ package tofu
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
@@ -22,7 +21,6 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/lang/marks"
|
||||
"github.com/opentofu/opentofu/internal/moduletest"
|
||||
"github.com/opentofu/opentofu/internal/plans"
|
||||
"github.com/opentofu/opentofu/internal/providers"
|
||||
"github.com/opentofu/opentofu/internal/states"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
)
|
||||
@@ -100,49 +98,13 @@ func (tc *TestContext) evaluate(state *states.SyncState, changes *plans.ChangesS
|
||||
PlanTimestamp: tc.Plan.Timestamp,
|
||||
// InstanceExpander is intentionally nil for test contexts
|
||||
// The GetModule function will fall back to using state/changes when it's nil
|
||||
InstanceExpander: nil,
|
||||
InstanceExpander: nil,
|
||||
},
|
||||
ModulePath: nil, // nil for the root module
|
||||
InstanceKeyData: EvalDataForNoInstanceKey,
|
||||
Operation: operation,
|
||||
}
|
||||
|
||||
var providerInstanceLock sync.Mutex
|
||||
providerInstances := make(map[addrs.Provider]providers.Interface)
|
||||
defer func() {
|
||||
for addr, inst := range providerInstances {
|
||||
log.Printf("[INFO] Shutting down test provider %s", addr)
|
||||
inst.Close(context.TODO())
|
||||
}
|
||||
}()
|
||||
|
||||
providerSupplier := func(addr addrs.Provider) providers.Interface {
|
||||
providerInstanceLock.Lock()
|
||||
defer providerInstanceLock.Unlock()
|
||||
|
||||
if inst, ok := providerInstances[addr]; ok {
|
||||
return inst
|
||||
}
|
||||
|
||||
factory, ok := tc.plugins.providerFactories[addr]
|
||||
if !ok {
|
||||
log.Printf("[WARN] Unable to find provider %s in test context", addr)
|
||||
providerInstances[addr] = nil
|
||||
return nil
|
||||
}
|
||||
log.Printf("[INFO] Starting test provider %s", addr)
|
||||
inst, err := factory()
|
||||
if err != nil {
|
||||
log.Printf("[WARN] Unable to start provider %s in test context", addr)
|
||||
providerInstances[addr] = nil
|
||||
return nil
|
||||
} else {
|
||||
log.Printf("[INFO] Shutting down test provider %s", addr)
|
||||
providerInstances[addr] = inst
|
||||
return inst
|
||||
}
|
||||
}
|
||||
|
||||
scope := &lang.Scope{
|
||||
Data: data,
|
||||
BaseDir: ".",
|
||||
@@ -160,10 +122,17 @@ func (tc *TestContext) evaluate(state *states.SyncState, changes *plans.ChangesS
|
||||
Subject: rng.ToHCL().Ptr(),
|
||||
})
|
||||
}
|
||||
|
||||
provider := providerSupplier(pr.Type)
|
||||
|
||||
return evalContextProviderFunction(ctx, provider, walkPlan, pf, rng)
|
||||
addr := addrs.AbsProviderInstanceCorrect{
|
||||
Config: addrs.AbsProviderConfigCorrect{
|
||||
Module: addrs.RootModuleInstance,
|
||||
Config: addrs.ProviderConfigCorrect{
|
||||
Provider: pr.Type,
|
||||
Alias: "",
|
||||
},
|
||||
},
|
||||
Key: addrs.NoKey,
|
||||
}
|
||||
return evalContextProviderFunction(ctx, tc.plugins, addr, walkPlan, pf, rng)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/configs"
|
||||
"github.com/opentofu/opentofu/internal/configs/configschema"
|
||||
"github.com/opentofu/opentofu/internal/dag"
|
||||
"github.com/opentofu/opentofu/internal/plugins"
|
||||
)
|
||||
|
||||
// GraphNodeAttachResourceSchema is an interface implemented by node types
|
||||
@@ -49,7 +50,7 @@ type GraphNodeAttachProvisionerSchema interface {
|
||||
// GraphNodeAttachProvisionerSchema, looks up the needed schemas for each
|
||||
// and then passes them to a method implemented by the node.
|
||||
type AttachSchemaTransformer struct {
|
||||
Plugins *contextPlugins
|
||||
Plugins plugins.PluginManager
|
||||
Config *configs.Config
|
||||
}
|
||||
|
||||
@@ -69,7 +70,7 @@ func (t *AttachSchemaTransformer) Transform(ctx context.Context, g *Graph) error
|
||||
providerFqn := tv.Provider()
|
||||
|
||||
// TODO: Plumb a useful context.Context through to here.
|
||||
schema, version, err := t.Plugins.ResourceTypeSchema(ctx, providerFqn, mode, typeName)
|
||||
schema, err := t.Plugins.ResourceTypeSchema(ctx, providerFqn, mode, typeName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read schema for %s in %s: %w", addr, providerFqn, err)
|
||||
}
|
||||
@@ -78,7 +79,7 @@ func (t *AttachSchemaTransformer) Transform(ctx context.Context, g *Graph) error
|
||||
continue
|
||||
}
|
||||
log.Printf("[TRACE] AttachSchemaTransformer: attaching resource schema to %s", dag.VertexName(v))
|
||||
tv.AttachResourceSchema(schema, version)
|
||||
tv.AttachResourceSchema(schema.Block, uint64(schema.Version))
|
||||
}
|
||||
|
||||
if tv, ok := v.(GraphNodeAttachProviderConfigSchema); ok {
|
||||
@@ -93,7 +94,7 @@ func (t *AttachSchemaTransformer) Transform(ctx context.Context, g *Graph) error
|
||||
continue
|
||||
}
|
||||
log.Printf("[TRACE] AttachSchemaTransformer: attaching provider config schema to %s", dag.VertexName(v))
|
||||
tv.AttachProviderConfigSchema(schema)
|
||||
tv.AttachProviderConfigSchema(schema.Block)
|
||||
}
|
||||
|
||||
if tv, ok := v.(GraphNodeAttachProvisionerSchema); ok {
|
||||
|
||||
@@ -488,7 +488,7 @@ func (t *CloseProviderTransformer) Transform(_ context.Context, g *Graph) error
|
||||
|
||||
if closer == nil {
|
||||
// create a closer for this provider type
|
||||
closer = &graphNodeCloseProvider{Addr: p.ProviderAddr()}
|
||||
closer = &graphNodeCloseProvider{Provider: p}
|
||||
g.Add(closer)
|
||||
cpm[key] = closer
|
||||
}
|
||||
@@ -629,7 +629,7 @@ func providerVertexMap(g *Graph) map[string]GraphNodeProvider {
|
||||
}
|
||||
|
||||
type graphNodeCloseProvider struct {
|
||||
Addr addrs.AbsProviderConfig
|
||||
Provider GraphNodeProvider
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -638,21 +638,37 @@ var (
|
||||
)
|
||||
|
||||
func (n *graphNodeCloseProvider) Name() string {
|
||||
return n.Addr.String() + " (close)"
|
||||
return n.Provider.ProviderAddr().String() + " (close)"
|
||||
}
|
||||
|
||||
// GraphNodeModulePath
|
||||
func (n *graphNodeCloseProvider) ModulePath() addrs.Module {
|
||||
return n.Addr.Module
|
||||
return n.Provider.ProviderAddr().Module
|
||||
}
|
||||
|
||||
// GraphNodeExecutable impl.
|
||||
func (n *graphNodeCloseProvider) Execute(ctx context.Context, evalCtx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) {
|
||||
return diags.Append(evalCtx.CloseProvider(ctx, n.Addr))
|
||||
// TODO This whole function is a bad hack now, we should probably send a close function into here instead of the whole provider + inspection
|
||||
|
||||
if configured, ok := n.Provider.(*NodeApplyableProvider); ok && configured.Config != nil && configured.Config.Instances != nil {
|
||||
for key := range configured.Config.Instances {
|
||||
addr := n.Provider.ProviderAddr().InstanceCorrect(key)
|
||||
if evalCtx.Providers().IsProviderConfigured(addr) {
|
||||
diags = diags.Append(evalCtx.Providers().CloseProvider(ctx, addr))
|
||||
}
|
||||
}
|
||||
return diags
|
||||
} else {
|
||||
addr := n.Provider.ProviderAddr().InstanceCorrect(addrs.NoKey)
|
||||
if evalCtx.Providers().IsProviderConfigured(addr) {
|
||||
return diags.Append(evalCtx.Providers().CloseProvider(ctx, addr))
|
||||
}
|
||||
return diags
|
||||
}
|
||||
}
|
||||
|
||||
func (n *graphNodeCloseProvider) CloseProviderAddr() addrs.AbsProviderConfig {
|
||||
return n.Addr
|
||||
return n.Provider.ProviderAddr()
|
||||
}
|
||||
|
||||
// GraphNodeDotter impl.
|
||||
|
||||
Reference in New Issue
Block a user