diff --git a/internal/tofu/context_plan2_test.go b/internal/tofu/context_plan2_test.go index b25d7d061b..835c6670a8 100644 --- a/internal/tofu/context_plan2_test.go +++ b/internal/tofu/context_plan2_test.go @@ -18,7 +18,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/zclconf/go-cty/cty" - // "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2" "github.com/opentofu/opentofu/internal/addrs" "github.com/opentofu/opentofu/internal/checks" @@ -7327,3 +7327,89 @@ func TestContext2Plan_importResourceWithSensitiveDataSource(t *testing.T) { } }) } + +func TestContext2Plan_insuffient_block(t *testing.T) { + type testcase struct { + filename string + start hcl.Pos + end hcl.Pos + } + + tests := map[string]testcase{ + "insufficient-features-blocks-aliased-provider": { + filename: "provider[\"registry.opentofu.org/hashicorp/test\"] with no configuration", + start: hcl.InitialPos, + end: hcl.InitialPos, + }, + "insufficient-features-blocks-nested_module": { + filename: "provider[\"registry.opentofu.org/hashicorp/test\"] with no configuration", + start: hcl.InitialPos, + end: hcl.InitialPos, + }, + "insufficient-features-blocks-no-feats": { + filename: "testdata/insufficient-features-blocks-no-feats/main.tf", + start: hcl.Pos{Line: 9, Column: 17, Byte: 146}, + end: hcl.Pos{Line: 9, Column: 17, Byte: 146}, + }, + } + + for testName, tc := range tests { + t.Run(testName, func(t *testing.T) { + m := testModule(t, testName) + p := mockProviderWithFeaturesBlock() + + ctx := testContext2(t, &ContextOpts{ + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + _, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts) + var expectedDiags tfdiags.Diagnostics + + expectedDiags = expectedDiags.Append( + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Insufficient features blocks", + Detail: "At least 1 \"features\" blocks are required.", + Subject: &hcl.Range{ + Filename: tc.filename, + Start: tc.start, + End: tc.end, + }, + }, + ) + + assertDiagnosticsMatch(t, diags, expectedDiags) + }) + } +} + +func mockProviderWithFeaturesBlock() *MockProvider { + return &MockProvider{ + GetProviderSchemaResponse: &providers.GetProviderSchemaResponse{ + Provider: providers.Schema{Block: featuresBlockTestSchema()}, + ResourceTypes: map[string]providers.Schema{ + "test_object": {Block: simpleTestSchema()}, + }, + }, + } +} + +func featuresBlockTestSchema() *configschema.Block { + return &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "test_string": { + Type: cty.String, + Optional: true, + }, + }, + BlockTypes: map[string]*configschema.NestedBlock{ + "features": { + MinItems: 1, + MaxItems: 1, + Nesting: configschema.NestingList, + }, + }, + } +} diff --git a/internal/tofu/eval_provider.go b/internal/tofu/eval_provider.go index 7b17369bf6..26f428d1b8 100644 --- a/internal/tofu/eval_provider.go +++ b/internal/tofu/eval_provider.go @@ -10,9 +10,11 @@ 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" + "github.com/opentofu/opentofu/internal/configs/hcl2shim" "github.com/opentofu/opentofu/internal/providers" ) @@ -40,7 +42,8 @@ func buildProviderConfig(ctx EvalContext, addr addrs.AbsProviderConfig, config * return inputBody default: log.Printf("[TRACE] buildProviderConfig for %s: no configuration at all", addr) - return hcl.EmptyBody() + addr := fmt.Sprintf("%s with no configuration", addr) + return hcl2shim.SynthBody(addr, make(map[string]cty.Value)) } } diff --git a/internal/tofu/testdata/insufficient-features-blocks-aliased-provider/main.tf b/internal/tofu/testdata/insufficient-features-blocks-aliased-provider/main.tf new file mode 100644 index 0000000000..55d7bc6e44 --- /dev/null +++ b/internal/tofu/testdata/insufficient-features-blocks-aliased-provider/main.tf @@ -0,0 +1,16 @@ +terraform { + required_providers { + test = { + source = "registry.opentofu.org/hashicorp/test" + } + } +} + +provider "test" { + alias = "test2" + test_string = "config" + features {} +} + +resource "test_object" "a" { +} \ No newline at end of file diff --git a/internal/tofu/testdata/insufficient-features-blocks-nested_module/main.tf b/internal/tofu/testdata/insufficient-features-blocks-nested_module/main.tf new file mode 100644 index 0000000000..8e004da2ad --- /dev/null +++ b/internal/tofu/testdata/insufficient-features-blocks-nested_module/main.tf @@ -0,0 +1,11 @@ +terraform { + required_providers { + test = { + source = "registry.opentofu.org/hashicorp/test" + } + } +} + +module "nested" { + source = "./nested" +} diff --git a/internal/tofu/testdata/insufficient-features-blocks-nested_module/nested/main.tf b/internal/tofu/testdata/insufficient-features-blocks-nested_module/nested/main.tf new file mode 100644 index 0000000000..b70dd91d71 --- /dev/null +++ b/internal/tofu/testdata/insufficient-features-blocks-nested_module/nested/main.tf @@ -0,0 +1,8 @@ +provider "test" { + alias = "test2" + test_string = "config" + features {} +} + +resource "test_object" "a" { +} \ No newline at end of file diff --git a/internal/tofu/testdata/insufficient-features-blocks-no-feats/main.tf b/internal/tofu/testdata/insufficient-features-blocks-no-feats/main.tf new file mode 100644 index 0000000000..42e12b2f12 --- /dev/null +++ b/internal/tofu/testdata/insufficient-features-blocks-no-feats/main.tf @@ -0,0 +1,14 @@ +terraform { + required_providers { + test = { + source = "registry.opentofu.org/hashicorp/test" + } + } +} + +provider "test" { + test_string = "config" +} + +resource "test_object" "a" { +} \ No newline at end of file