mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
Allow configuration aliases in root module to impact validation only (#2905)
Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
ENHANCEMENTS:
|
||||
|
||||
* `tofu show` now supports a `-config` option, to be used in conjunction with `-json` to produce a machine-readable summary of the configuration without first creating a plan. ([#2820](https://github.com/opentofu/opentofu/pull/2820))
|
||||
* `tofu validate` now supports running in a module that contains provider configuration_aliases. ([#2905](https://github.com/opentofu/opentofu/pull/2905))
|
||||
|
||||
## Previous Releases
|
||||
|
||||
|
||||
@@ -2506,3 +2506,73 @@ func TestContext2Validate_rangeOverZeroPlanTimestamp(t *testing.T) {
|
||||
t.Fatal(diags.ErrWithWarnings())
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Validate_providerAliasesInRoot(t *testing.T) {
|
||||
// This tests the scenario where a user is running tofu validate in a module, instead of the root
|
||||
// It should allow configuration_aliases to function, even in the root, similar to how input
|
||||
// variables function in validate.
|
||||
m := testModuleInline(t, map[string]string{
|
||||
"main.tf": `
|
||||
terraform {
|
||||
required_providers {
|
||||
test = {
|
||||
source = "hashicorp/test"
|
||||
configuration_aliases = [test.alias]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "test_object" "t" {
|
||||
provider = test.alias
|
||||
}
|
||||
`,
|
||||
})
|
||||
|
||||
p := simpleMockProvider()
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
diags := ctx.Validate(context.Background(), m)
|
||||
if diags.HasErrors() {
|
||||
t.Fatal(diags.ErrWithWarnings())
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Validate_providerAliasesInRootMisconfigured(t *testing.T) {
|
||||
m := testModuleInline(t, map[string]string{
|
||||
"main.tf": `
|
||||
terraform {
|
||||
required_providers {
|
||||
test = {
|
||||
source = "hashicorp/test"
|
||||
configuration_aliases = [test.alias]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "test_object" "t" {
|
||||
provider = test.typo
|
||||
}
|
||||
`,
|
||||
})
|
||||
|
||||
p := simpleMockProvider()
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
diags := ctx.Validate(context.Background(), m)
|
||||
|
||||
if !diags.HasErrors() {
|
||||
t.Fatal("Expected error")
|
||||
}
|
||||
|
||||
if !strings.Contains(diags.Err().Error(), `Provider configuration not present: To work with test_object.t its original provider configuration at provider["registry.opentofu.org/hashicorp/test"].typo is required, but it has been removed`) {
|
||||
t.Fatalf("expected error, got: %q\n", diags.Err().Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
|
||||
&AttachResourceConfigTransformer{Config: b.Config},
|
||||
|
||||
// add providers
|
||||
transformProviders(concreteProvider, b.Config),
|
||||
transformProviders(concreteProvider, b.Config, b.Operation),
|
||||
|
||||
// Remove modules no longer present in the config
|
||||
&RemovedModuleTransformer{Config: b.Config, State: b.State},
|
||||
|
||||
@@ -87,7 +87,7 @@ func (b *EvalGraphBuilder) Steps() []GraphTransformer {
|
||||
// Attach the state
|
||||
&AttachStateTransformer{State: b.State},
|
||||
|
||||
transformProviders(concreteProvider, b.Config),
|
||||
transformProviders(concreteProvider, b.Config, walkEval),
|
||||
|
||||
// Must attach schemas before ReferenceTransformer so that we can
|
||||
// analyze the configuration to find references.
|
||||
|
||||
@@ -197,7 +197,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
|
||||
&AttachResourceConfigTransformer{Config: b.Config},
|
||||
|
||||
// add providers
|
||||
transformProviders(b.ConcreteProvider, b.Config),
|
||||
transformProviders(b.ConcreteProvider, b.Config, b.Operation),
|
||||
|
||||
// Remove modules no longer present in the config
|
||||
&RemovedModuleTransformer{Config: b.Config, State: b.State},
|
||||
|
||||
@@ -17,12 +17,13 @@ import (
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
)
|
||||
|
||||
func transformProviders(concrete ConcreteProviderNodeFunc, config *configs.Config) GraphTransformer {
|
||||
func transformProviders(concrete ConcreteProviderNodeFunc, config *configs.Config, walkOp walkOperation) GraphTransformer {
|
||||
return GraphTransformMulti(
|
||||
// Add providers from the config
|
||||
&ProviderConfigTransformer{
|
||||
Config: config,
|
||||
Concrete: concrete,
|
||||
Config: config,
|
||||
Concrete: concrete,
|
||||
Operation: walkOp,
|
||||
},
|
||||
// Add any remaining missing providers
|
||||
&MissingProviderTransformer{
|
||||
@@ -686,6 +687,9 @@ type ProviderConfigTransformer struct {
|
||||
|
||||
// Config is the root node of the configuration tree to add providers from.
|
||||
Config *configs.Config
|
||||
|
||||
// Operation is needed to add workarounds for validate
|
||||
Operation walkOperation
|
||||
}
|
||||
|
||||
func (t *ProviderConfigTransformer) Transform(_ context.Context, g *Graph) error {
|
||||
@@ -753,19 +757,35 @@ func (t *ProviderConfigTransformer) transformSingle(g *Graph, c *configs.Config)
|
||||
continue
|
||||
}
|
||||
|
||||
abstract := &NodeAbstractProvider{
|
||||
Addr: addr,
|
||||
}
|
||||
addNode := func(alias string) {
|
||||
abstract := &NodeAbstractProvider{
|
||||
Addr: addrs.AbsProviderConfig{
|
||||
Provider: addr.Provider,
|
||||
Module: addr.Module,
|
||||
Alias: alias,
|
||||
},
|
||||
}
|
||||
|
||||
var v dag.Vertex
|
||||
if t.Concrete != nil {
|
||||
v = t.Concrete(abstract)
|
||||
} else {
|
||||
v = abstract
|
||||
}
|
||||
var v dag.Vertex
|
||||
if t.Concrete != nil {
|
||||
v = t.Concrete(abstract)
|
||||
} else {
|
||||
v = abstract
|
||||
}
|
||||
|
||||
g.Add(v)
|
||||
t.providers[addr.String()] = v.(GraphNodeProvider)
|
||||
g.Add(v)
|
||||
t.providers[abstract.Addr.String()] = v.(GraphNodeProvider)
|
||||
}
|
||||
// Add unaliased instance for the provider in the root
|
||||
addNode("")
|
||||
|
||||
if t.Operation == walkValidate {
|
||||
// Add a workaround for validating modules by running them as a root module in `tofu validate`
|
||||
// See the discussion in https://github.com/opentofu/opentofu/issues/2862 for more details
|
||||
for _, alias := range p.Aliases {
|
||||
addNode(alias.Alias)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user