mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-23 23:50:12 -06:00
Allow unconfigured provider functions in test context (#1603)
Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
This commit is contained in:
parent
08469452b6
commit
015b79b139
@ -12,6 +12,7 @@ BUG FIXES:
|
|||||||
* Fix inmem backend crash due to missing struct field ([#1619](https://github.com/opentofu/opentofu/pull/1619))
|
* Fix inmem backend crash due to missing struct field ([#1619](https://github.com/opentofu/opentofu/pull/1619))
|
||||||
* Added a check in the `tofu test` to validate that the names of test run blocks do not contain spaces. ([#1489](https://github.com/opentofu/opentofu/pull/1489))
|
* Added a check in the `tofu test` to validate that the names of test run blocks do not contain spaces. ([#1489](https://github.com/opentofu/opentofu/pull/1489))
|
||||||
* `tofu test` now supports accessing module outputs when the module has no resources. ([#1409](https://github.com/opentofu/opentofu/pull/1409))
|
* `tofu test` now supports accessing module outputs when the module has no resources. ([#1409](https://github.com/opentofu/opentofu/pull/1409))
|
||||||
|
* Fixed support for provider functions in tests ([#1603](https://github.com/opentofu/opentofu/pull/1603))
|
||||||
|
|
||||||
## Previous Releases
|
## Previous Releases
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
|
|
||||||
// This builds a provider function using an EvalContext and some additional information
|
// This builds a provider function using an EvalContext and some additional information
|
||||||
// This is split out of BuiltinEvalContext for testing
|
// This is split out of BuiltinEvalContext for testing
|
||||||
func evalContextProviderFunction(ctx EvalContext, mc *configs.Config, op walkOperation, pf addrs.ProviderFunction, rng tfdiags.SourceRange) (*function.Function, tfdiags.Diagnostics) {
|
func evalContextProviderFunction(providers func(addrs.AbsProviderConfig) providers.Interface, mc *configs.Config, op walkOperation, pf addrs.ProviderFunction, rng tfdiags.SourceRange) (*function.Function, tfdiags.Diagnostics) {
|
||||||
var diags tfdiags.Diagnostics
|
var diags tfdiags.Diagnostics
|
||||||
|
|
||||||
pr, ok := mc.Module.ProviderRequirements.RequiredProviders[pf.ProviderName]
|
pr, ok := mc.Module.ProviderRequirements.RequiredProviders[pf.ProviderName]
|
||||||
@ -35,7 +35,7 @@ func evalContextProviderFunction(ctx EvalContext, mc *configs.Config, op walkOpe
|
|||||||
Alias: pf.ProviderAlias,
|
Alias: pf.ProviderAlias,
|
||||||
}
|
}
|
||||||
|
|
||||||
provider := ctx.Provider(absPc)
|
provider := providers(absPc)
|
||||||
|
|
||||||
if provider == nil {
|
if provider == nil {
|
||||||
// Configured provider (NodeApplyableProvider) not required via transform_provider.go. Instead we should use the unconfigured instance (NodeEvalableProvider) in the root.
|
// Configured provider (NodeApplyableProvider) not required via transform_provider.go. Instead we should use the unconfigured instance (NodeEvalableProvider) in the root.
|
||||||
@ -59,7 +59,7 @@ func evalContextProviderFunction(ctx EvalContext, mc *configs.Config, op walkOpe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
provider = ctx.Provider(addrs.AbsProviderConfig{Provider: pr.Type})
|
provider = providers(addrs.AbsProviderConfig{Provider: pr.Type})
|
||||||
if provider == nil {
|
if provider == nil {
|
||||||
// This should not be possible
|
// This should not be possible
|
||||||
return nil, diags.Append(&hcl.Diagnostic{
|
return nil, diags.Append(&hcl.Diagnostic{
|
||||||
|
@ -134,7 +134,7 @@ func TestFunctions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Provider missing
|
// Provider missing
|
||||||
_, diags := evalContextProviderFunction(mockCtx, cfg, walkValidate, providerFunc("provider::invalid::unknown"), rng)
|
_, diags := evalContextProviderFunction(mockCtx.Provider, cfg, walkValidate, providerFunc("provider::invalid::unknown"), rng)
|
||||||
if !diags.HasErrors() {
|
if !diags.HasErrors() {
|
||||||
t.Fatal("expected unknown function provider")
|
t.Fatal("expected unknown function provider")
|
||||||
}
|
}
|
||||||
@ -143,7 +143,7 @@ func TestFunctions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Provider not initialized
|
// Provider not initialized
|
||||||
_, diags = evalContextProviderFunction(mockCtx, cfg, walkValidate, providerFunc("provider::mockname::missing"), rng)
|
_, diags = evalContextProviderFunction(mockCtx.Provider, cfg, walkValidate, providerFunc("provider::mockname::missing"), rng)
|
||||||
if !diags.HasErrors() {
|
if !diags.HasErrors() {
|
||||||
t.Fatal("expected unknown function provider")
|
t.Fatal("expected unknown function provider")
|
||||||
}
|
}
|
||||||
@ -156,7 +156,7 @@ func TestFunctions(t *testing.T) {
|
|||||||
|
|
||||||
// Function missing (validate)
|
// Function missing (validate)
|
||||||
mockProvider.GetFunctionsCalled = false
|
mockProvider.GetFunctionsCalled = false
|
||||||
_, diags = evalContextProviderFunction(mockCtx, cfg, walkValidate, providerFunc("provider::mockname::missing"), rng)
|
_, diags = evalContextProviderFunction(mockCtx.Provider, cfg, walkValidate, providerFunc("provider::mockname::missing"), rng)
|
||||||
if diags.HasErrors() {
|
if diags.HasErrors() {
|
||||||
t.Fatal(diags.Err())
|
t.Fatal(diags.Err())
|
||||||
}
|
}
|
||||||
@ -166,7 +166,7 @@ func TestFunctions(t *testing.T) {
|
|||||||
|
|
||||||
// Function missing (Non-validate)
|
// Function missing (Non-validate)
|
||||||
mockProvider.GetFunctionsCalled = false
|
mockProvider.GetFunctionsCalled = false
|
||||||
_, diags = evalContextProviderFunction(mockCtx, cfg, walkPlan, providerFunc("provider::mockname::missing"), rng)
|
_, diags = evalContextProviderFunction(mockCtx.Provider, cfg, walkPlan, providerFunc("provider::mockname::missing"), rng)
|
||||||
if !diags.HasErrors() {
|
if !diags.HasErrors() {
|
||||||
t.Fatal("expected unknown function")
|
t.Fatal("expected unknown function")
|
||||||
}
|
}
|
||||||
@ -188,7 +188,7 @@ func TestFunctions(t *testing.T) {
|
|||||||
// Load functions into ctx
|
// Load functions into ctx
|
||||||
for _, fn := range []string{"echo", "concat", "coalesce", "unknown_param", "error_param"} {
|
for _, fn := range []string{"echo", "concat", "coalesce", "unknown_param", "error_param"} {
|
||||||
pf := providerFunc("provider::mockname::" + fn)
|
pf := providerFunc("provider::mockname::" + fn)
|
||||||
impl, diags := evalContextProviderFunction(mockCtx, cfg, walkPlan, pf, rng)
|
impl, diags := evalContextProviderFunction(mockCtx.Provider, cfg, walkPlan, pf, rng)
|
||||||
if diags.HasErrors() {
|
if diags.HasErrors() {
|
||||||
t.Fatal(diags.Err())
|
t.Fatal(diags.Err())
|
||||||
}
|
}
|
||||||
|
@ -526,7 +526,7 @@ func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, source
|
|||||||
}
|
}
|
||||||
|
|
||||||
scope := ctx.Evaluator.Scope(data, self, source, func(pf addrs.ProviderFunction, rng tfdiags.SourceRange) (*function.Function, tfdiags.Diagnostics) {
|
scope := ctx.Evaluator.Scope(data, self, source, func(pf addrs.ProviderFunction, rng tfdiags.SourceRange) (*function.Function, tfdiags.Diagnostics) {
|
||||||
return evalContextProviderFunction(ctx, mc, ctx.Evaluator.Operation, pf, rng)
|
return evalContextProviderFunction(ctx.Provider, mc, ctx.Evaluator.Operation, pf, rng)
|
||||||
})
|
})
|
||||||
scope.SetActiveExperiments(mc.Module.ActiveExperiments)
|
scope.SetActiveExperiments(mc.Module.ActiveExperiments)
|
||||||
|
|
||||||
|
@ -7,17 +7,20 @@ package tofu
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/v2"
|
"github.com/hashicorp/hcl/v2"
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
"github.com/zclconf/go-cty/cty/convert"
|
"github.com/zclconf/go-cty/cty/convert"
|
||||||
|
"github.com/zclconf/go-cty/cty/function"
|
||||||
|
|
||||||
"github.com/opentofu/opentofu/internal/addrs"
|
"github.com/opentofu/opentofu/internal/addrs"
|
||||||
"github.com/opentofu/opentofu/internal/configs"
|
"github.com/opentofu/opentofu/internal/configs"
|
||||||
"github.com/opentofu/opentofu/internal/lang"
|
"github.com/opentofu/opentofu/internal/lang"
|
||||||
"github.com/opentofu/opentofu/internal/moduletest"
|
"github.com/opentofu/opentofu/internal/moduletest"
|
||||||
"github.com/opentofu/opentofu/internal/plans"
|
"github.com/opentofu/opentofu/internal/plans"
|
||||||
|
"github.com/opentofu/opentofu/internal/providers"
|
||||||
"github.com/opentofu/opentofu/internal/states"
|
"github.com/opentofu/opentofu/internal/states"
|
||||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||||
)
|
)
|
||||||
@ -99,11 +102,50 @@ func (ctx *TestContext) evaluate(state *states.SyncState, changes *plans.Changes
|
|||||||
Operation: operation,
|
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()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
providerSupplier := func(addr addrs.AbsProviderConfig) providers.Interface {
|
||||||
|
providerInstanceLock.Lock()
|
||||||
|
defer providerInstanceLock.Unlock()
|
||||||
|
|
||||||
|
if inst, ok := providerInstances[addr.Provider]; ok {
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
|
||||||
|
factory, ok := ctx.plugins.providerFactories[addr.Provider]
|
||||||
|
if !ok {
|
||||||
|
log.Printf("[WARN] Unable to find provider %s in test context", addr)
|
||||||
|
providerInstances[addr.Provider] = 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.Provider] = nil
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
log.Printf("[INFO] Shutting down test provider %s", addr)
|
||||||
|
providerInstances[addr.Provider] = inst
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
scope := &lang.Scope{
|
scope := &lang.Scope{
|
||||||
Data: data,
|
Data: data,
|
||||||
BaseDir: ".",
|
BaseDir: ".",
|
||||||
PureOnly: operation != walkApply,
|
PureOnly: operation != walkApply,
|
||||||
PlanTimestamp: ctx.Plan.Timestamp,
|
PlanTimestamp: ctx.Plan.Timestamp,
|
||||||
|
ProviderFunctions: func(pf addrs.ProviderFunction, rng tfdiags.SourceRange) (*function.Function, tfdiags.Diagnostics) {
|
||||||
|
return evalContextProviderFunction(providerSupplier, ctx.Config, walkPlan, pf, rng)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're going to assume the run has passed, and then if anything fails this
|
// We're going to assume the run has passed, and then if anything fails this
|
||||||
|
Loading…
Reference in New Issue
Block a user