Skip to content

Commit af4a734

Browse files
author
Liam Cervante
authored
import: only filter id attribute at root level when generating configuration (#35220)
1 parent 69991ff commit af4a734

File tree

2 files changed

+98
-26
lines changed

2 files changed

+98
-26
lines changed

internal/configs/configschema/filter.go

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,35 @@
33

44
package configschema
55

6-
type FilterT[T any] func(string, T) bool
6+
import "github.com/zclconf/go-cty/cty"
7+
8+
type FilterT[T any] func(cty.Path, T) bool
79

810
var (
9-
FilterReadOnlyAttribute = func(name string, attribute *Attribute) bool {
11+
FilterReadOnlyAttribute = func(path cty.Path, attribute *Attribute) bool {
1012
return attribute.Computed && !attribute.Optional
1113
}
1214

13-
FilterHelperSchemaIdAttribute = func(name string, attribute *Attribute) bool {
14-
if name == "id" && attribute.Computed && attribute.Optional {
15+
FilterHelperSchemaIdAttribute = func(path cty.Path, attribute *Attribute) bool {
16+
if path.Equals(cty.GetAttrPath("id")) && attribute.Computed && attribute.Optional {
1517
return true
1618
}
1719
return false
1820
}
1921

20-
FilterDeprecatedAttribute = func(name string, attribute *Attribute) bool {
22+
FilterDeprecatedAttribute = func(path cty.Path, attribute *Attribute) bool {
2123
return attribute.Deprecated
2224
}
2325

24-
FilterDeprecatedBlock = func(name string, block *NestedBlock) bool {
26+
FilterDeprecatedBlock = func(path cty.Path, block *NestedBlock) bool {
2527
return block.Deprecated
2628
}
2729
)
2830

2931
func FilterOr[T any](filters ...FilterT[T]) FilterT[T] {
30-
return func(name string, value T) bool {
32+
return func(path cty.Path, value T) bool {
3133
for _, f := range filters {
32-
if f(name, value) {
34+
if f(path, value) {
3335
return true
3436
}
3537
}
@@ -38,6 +40,10 @@ func FilterOr[T any](filters ...FilterT[T]) FilterT[T] {
3840
}
3941

4042
func (b *Block) Filter(filterAttribute FilterT[*Attribute], filterBlock FilterT[*NestedBlock]) *Block {
43+
return b.filter(nil, filterAttribute, filterBlock)
44+
}
45+
46+
func (b *Block) filter(path cty.Path, filterAttribute FilterT[*Attribute], filterBlock FilterT[*NestedBlock]) *Block {
4147
ret := &Block{
4248
Description: b.Description,
4349
DescriptionKind: b.DescriptionKind,
@@ -48,10 +54,11 @@ func (b *Block) Filter(filterAttribute FilterT[*Attribute], filterBlock FilterT[
4854
ret.Attributes = make(map[string]*Attribute, len(b.Attributes))
4955
}
5056
for name, attrS := range b.Attributes {
51-
if filterAttribute == nil || !filterAttribute(name, attrS) {
57+
path := path.GetAttr(name)
58+
if filterAttribute == nil || !filterAttribute(path, attrS) {
5259
ret.Attributes[name] = attrS
5360
if attrS.NestedType != nil {
54-
ret.Attributes[name].NestedType = filterNestedType(attrS.NestedType, filterAttribute)
61+
ret.Attributes[name].NestedType = filterNestedType(attrS.NestedType, path, filterAttribute)
5562
}
5663
}
5764
}
@@ -60,8 +67,9 @@ func (b *Block) Filter(filterAttribute FilterT[*Attribute], filterBlock FilterT[
6067
ret.BlockTypes = make(map[string]*NestedBlock, len(b.BlockTypes))
6168
}
6269
for name, blockS := range b.BlockTypes {
63-
if filterBlock == nil || !filterBlock(name, blockS) {
64-
block := blockS.Filter(filterAttribute, filterBlock)
70+
path := path.GetAttr(name)
71+
if filterBlock == nil || !filterBlock(path, blockS) {
72+
block := blockS.filter(path, filterAttribute, filterBlock)
6573
ret.BlockTypes[name] = &NestedBlock{
6674
Block: *block,
6775
Nesting: blockS.Nesting,
@@ -74,7 +82,7 @@ func (b *Block) Filter(filterAttribute FilterT[*Attribute], filterBlock FilterT[
7482
return ret
7583
}
7684

77-
func filterNestedType(obj *Object, filterAttribute FilterT[*Attribute]) *Object {
85+
func filterNestedType(obj *Object, path cty.Path, filterAttribute FilterT[*Attribute]) *Object {
7886
if obj == nil {
7987
return nil
8088
}
@@ -85,10 +93,11 @@ func filterNestedType(obj *Object, filterAttribute FilterT[*Attribute]) *Object
8593
}
8694

8795
for name, attrS := range obj.Attributes {
88-
if filterAttribute == nil || !filterAttribute(name, attrS) {
96+
path := path.GetAttr(name)
97+
if filterAttribute == nil || !filterAttribute(path, attrS) {
8998
ret.Attributes[name] = attrS
9099
if attrS.NestedType != nil {
91-
ret.Attributes[name].NestedType = filterNestedType(attrS.NestedType, filterAttribute)
100+
ret.Attributes[name].NestedType = filterNestedType(attrS.NestedType, path, filterAttribute)
92101
}
93102
}
94103
}

internal/terraform/context_plan_import_test.go

Lines changed: 74 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,80 @@ func TestContext2Plan_importIdInvalidUnknown(t *testing.T) {
671671
}
672672
}
673673

674+
func TestContext2Plan_generateConfigWithNestedId(t *testing.T) {
675+
m := testModuleInline(t, map[string]string{
676+
"main.tf": `
677+
import {
678+
to = test_object.a
679+
id = "foo"
680+
}
681+
`,
682+
})
683+
684+
p := simpleMockProvider()
685+
686+
p.GetProviderSchemaResponse.ResourceTypes = map[string]providers.Schema{
687+
"test_object": {
688+
Block: &configschema.Block{
689+
Attributes: map[string]*configschema.Attribute{
690+
"test_id": {
691+
Type: cty.String,
692+
Required: true,
693+
},
694+
"list_val": {
695+
Optional: true,
696+
NestedType: &configschema.Object{
697+
Nesting: configschema.NestingList,
698+
Attributes: map[string]*configschema.Attribute{
699+
"id": {
700+
Type: cty.String,
701+
Optional: true,
702+
Computed: true,
703+
},
704+
},
705+
},
706+
},
707+
},
708+
},
709+
},
710+
}
711+
712+
ctx := testContext2(t, &ContextOpts{
713+
Providers: map[addrs.Provider]providers.Factory{
714+
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
715+
},
716+
})
717+
p.ReadResourceResponse = &providers.ReadResourceResponse{
718+
NewState: cty.ObjectVal(map[string]cty.Value{
719+
"test_id": cty.StringVal("foo"),
720+
"list_val": cty.ListVal([]cty.Value{
721+
cty.ObjectVal(map[string]cty.Value{
722+
"id": cty.StringVal("list_id"),
723+
}),
724+
}),
725+
}),
726+
}
727+
p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{
728+
ImportedResources: []providers.ImportedResource{
729+
{
730+
TypeName: "test_object",
731+
State: cty.ObjectVal(map[string]cty.Value{
732+
"test_id": cty.StringVal("foo"),
733+
}),
734+
},
735+
},
736+
}
737+
738+
// Actual plan doesn't matter, just want to make sure there are no errors.
739+
_, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
740+
Mode: plans.NormalMode,
741+
GenerateConfigPath: "generated.tf", // Actual value here doesn't matter, as long as it is not empty.
742+
})
743+
if diags.HasErrors() {
744+
t.Fatalf("unexpected errors\n%s", diags.Err().Error())
745+
}
746+
}
747+
674748
func TestContext2Plan_importIntoModuleWithGeneratedConfig(t *testing.T) {
675749
m := testModuleInline(t, map[string]string{
676750
"main.tf": `
@@ -717,17 +791,6 @@ resource "test_object" "a" {
717791
},
718792
}
719793

720-
p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{
721-
ImportedResources: []providers.ImportedResource{
722-
{
723-
TypeName: "test_object",
724-
State: cty.ObjectVal(map[string]cty.Value{
725-
"test_string": cty.StringVal("foo"),
726-
}),
727-
},
728-
},
729-
}
730-
731794
plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
732795
Mode: plans.NormalMode,
733796
GenerateConfigPath: "generated.tf", // Actual value here doesn't matter, as long as it is not empty.

0 commit comments

Comments
 (0)