Skip to content

Commit

Permalink
import: only filter id attribute at root level when generating config…
Browse files Browse the repository at this point in the history
…uration (#35220)
  • Loading branch information
liamcervante authored May 21, 2024
1 parent 69991ff commit af4a734
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 26 deletions.
39 changes: 24 additions & 15 deletions internal/configs/configschema/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,35 @@

package configschema

type FilterT[T any] func(string, T) bool
import "github.com/zclconf/go-cty/cty"

type FilterT[T any] func(cty.Path, T) bool

var (
FilterReadOnlyAttribute = func(name string, attribute *Attribute) bool {
FilterReadOnlyAttribute = func(path cty.Path, attribute *Attribute) bool {
return attribute.Computed && !attribute.Optional
}

FilterHelperSchemaIdAttribute = func(name string, attribute *Attribute) bool {
if name == "id" && attribute.Computed && attribute.Optional {
FilterHelperSchemaIdAttribute = func(path cty.Path, attribute *Attribute) bool {
if path.Equals(cty.GetAttrPath("id")) && attribute.Computed && attribute.Optional {
return true
}
return false
}

FilterDeprecatedAttribute = func(name string, attribute *Attribute) bool {
FilterDeprecatedAttribute = func(path cty.Path, attribute *Attribute) bool {
return attribute.Deprecated
}

FilterDeprecatedBlock = func(name string, block *NestedBlock) bool {
FilterDeprecatedBlock = func(path cty.Path, block *NestedBlock) bool {
return block.Deprecated
}
)

func FilterOr[T any](filters ...FilterT[T]) FilterT[T] {
return func(name string, value T) bool {
return func(path cty.Path, value T) bool {
for _, f := range filters {
if f(name, value) {
if f(path, value) {
return true
}
}
Expand All @@ -38,6 +40,10 @@ func FilterOr[T any](filters ...FilterT[T]) FilterT[T] {
}

func (b *Block) Filter(filterAttribute FilterT[*Attribute], filterBlock FilterT[*NestedBlock]) *Block {
return b.filter(nil, filterAttribute, filterBlock)
}

func (b *Block) filter(path cty.Path, filterAttribute FilterT[*Attribute], filterBlock FilterT[*NestedBlock]) *Block {
ret := &Block{
Description: b.Description,
DescriptionKind: b.DescriptionKind,
Expand All @@ -48,10 +54,11 @@ func (b *Block) Filter(filterAttribute FilterT[*Attribute], filterBlock FilterT[
ret.Attributes = make(map[string]*Attribute, len(b.Attributes))
}
for name, attrS := range b.Attributes {
if filterAttribute == nil || !filterAttribute(name, attrS) {
path := path.GetAttr(name)
if filterAttribute == nil || !filterAttribute(path, attrS) {
ret.Attributes[name] = attrS
if attrS.NestedType != nil {
ret.Attributes[name].NestedType = filterNestedType(attrS.NestedType, filterAttribute)
ret.Attributes[name].NestedType = filterNestedType(attrS.NestedType, path, filterAttribute)
}
}
}
Expand All @@ -60,8 +67,9 @@ func (b *Block) Filter(filterAttribute FilterT[*Attribute], filterBlock FilterT[
ret.BlockTypes = make(map[string]*NestedBlock, len(b.BlockTypes))
}
for name, blockS := range b.BlockTypes {
if filterBlock == nil || !filterBlock(name, blockS) {
block := blockS.Filter(filterAttribute, filterBlock)
path := path.GetAttr(name)
if filterBlock == nil || !filterBlock(path, blockS) {
block := blockS.filter(path, filterAttribute, filterBlock)
ret.BlockTypes[name] = &NestedBlock{
Block: *block,
Nesting: blockS.Nesting,
Expand All @@ -74,7 +82,7 @@ func (b *Block) Filter(filterAttribute FilterT[*Attribute], filterBlock FilterT[
return ret
}

func filterNestedType(obj *Object, filterAttribute FilterT[*Attribute]) *Object {
func filterNestedType(obj *Object, path cty.Path, filterAttribute FilterT[*Attribute]) *Object {
if obj == nil {
return nil
}
Expand All @@ -85,10 +93,11 @@ func filterNestedType(obj *Object, filterAttribute FilterT[*Attribute]) *Object
}

for name, attrS := range obj.Attributes {
if filterAttribute == nil || !filterAttribute(name, attrS) {
path := path.GetAttr(name)
if filterAttribute == nil || !filterAttribute(path, attrS) {
ret.Attributes[name] = attrS
if attrS.NestedType != nil {
ret.Attributes[name].NestedType = filterNestedType(attrS.NestedType, filterAttribute)
ret.Attributes[name].NestedType = filterNestedType(attrS.NestedType, path, filterAttribute)
}
}
}
Expand Down
85 changes: 74 additions & 11 deletions internal/terraform/context_plan_import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,80 @@ func TestContext2Plan_importIdInvalidUnknown(t *testing.T) {
}
}

func TestContext2Plan_generateConfigWithNestedId(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
import {
to = test_object.a
id = "foo"
}
`,
})

p := simpleMockProvider()

p.GetProviderSchemaResponse.ResourceTypes = map[string]providers.Schema{
"test_object": {
Block: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"test_id": {
Type: cty.String,
Required: true,
},
"list_val": {
Optional: true,
NestedType: &configschema.Object{
Nesting: configschema.NestingList,
Attributes: map[string]*configschema.Attribute{
"id": {
Type: cty.String,
Optional: true,
Computed: true,
},
},
},
},
},
},
},
}

ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
p.ReadResourceResponse = &providers.ReadResourceResponse{
NewState: cty.ObjectVal(map[string]cty.Value{
"test_id": cty.StringVal("foo"),
"list_val": cty.ListVal([]cty.Value{
cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("list_id"),
}),
}),
}),
}
p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{
ImportedResources: []providers.ImportedResource{
{
TypeName: "test_object",
State: cty.ObjectVal(map[string]cty.Value{
"test_id": cty.StringVal("foo"),
}),
},
},
}

// Actual plan doesn't matter, just want to make sure there are no errors.
_, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
Mode: plans.NormalMode,
GenerateConfigPath: "generated.tf", // Actual value here doesn't matter, as long as it is not empty.
})
if diags.HasErrors() {
t.Fatalf("unexpected errors\n%s", diags.Err().Error())
}
}

func TestContext2Plan_importIntoModuleWithGeneratedConfig(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
Expand Down Expand Up @@ -717,17 +791,6 @@ resource "test_object" "a" {
},
}

p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{
ImportedResources: []providers.ImportedResource{
{
TypeName: "test_object",
State: cty.ObjectVal(map[string]cty.Value{
"test_string": cty.StringVal("foo"),
}),
},
},
}

plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
Mode: plans.NormalMode,
GenerateConfigPath: "generated.tf", // Actual value here doesn't matter, as long as it is not empty.
Expand Down

0 comments on commit af4a734

Please sign in to comment.