Skip to content

Commit

Permalink
Special handling for azapi cfg tunning to keep O+C attrs/blks (#7)
Browse files Browse the repository at this point in the history
* Special handling for azapi cfg tunning as many interesting attributes are O+C

* Cover block
  • Loading branch information
magodo authored Oct 25, 2023
1 parent 2f7d896 commit d9c6bf9
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 28 deletions.
60 changes: 36 additions & 24 deletions tfadd/internal/tune_tpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
"github.com/zclconf/go-cty/cty"
)

func TuneTpl(sch schema.Schema, tpl []byte, rt string) ([]byte, error) {
func TuneTpl(sch schema.Schema, tpl []byte, rt string, ocToKeep map[string]bool) ([]byte, error) {
f, diag := hclwrite.ParseConfig(tpl, "", hcl.InitialPos)
if diag.HasErrors() {
return nil, fmt.Errorf("parsing the generated template for %s: %s", rt, diag.Error())
Expand All @@ -28,13 +28,13 @@ func TuneTpl(sch schema.Schema, tpl []byte, rt string) ([]byte, error) {
rb.RemoveAttribute("id")
rb.RemoveBlock(rb.FirstMatchingBlock("timeouts", nil))

if err := tuneForBlock(rb, sch.Block, nil); err != nil {
if err := tuneForBlock(rb, sch.Block, nil, ocToKeep); err != nil {
return nil, err
}
return f.Bytes(), nil
}

func tuneForBlock(rb *hclwrite.Body, sch *tfpluginschema.Block, parentAttrNames []string) error {
func tuneForBlock(rb *hclwrite.Body, sch *tfpluginschema.Block, parentAttrNames []string, ocToKeep map[string]bool) error {
for attrName, attrVal := range rb.Attributes() {
schAttr, ok := sch.Attributes[attrName]
if !ok {
Expand Down Expand Up @@ -62,9 +62,11 @@ func tuneForBlock(rb *hclwrite.Body, sch *tfpluginschema.Block, parentAttrNames
continue
}
} else if len(schAttr.AtLeastOneOf) == 0 {
// For O+C attribute that has "AtLeastOneOf" constraint, keep it.
rb.RemoveAttribute(attrName)
continue
// For O+C attribute that has "AtLeastOneOf" constraint, or is explicitly specified, keep it.
if !(len(ocToKeep) != 0 && ocToKeep[attrName]) {
rb.RemoveAttribute(attrName)
continue
}
}
} else {
rb.RemoveAttribute(attrName)
Expand All @@ -73,22 +75,10 @@ func tuneForBlock(rb *hclwrite.Body, sch *tfpluginschema.Block, parentAttrNames
}

// For optional only attributes, remove it from the output config if it either holds the default value or is null.
attrExpr, diags := hclwrite.ParseConfig(attrVal.BuildTokens(nil).Bytes(), "generate_attr", hcl.InitialPos)
if diags.HasErrors() {
return fmt.Errorf(`building attribute %q attribute: %s`, attrName, diags.Error())
}
attrValLit := attrExpr.Body().GetAttribute(attrName).Expr().BuildTokens(nil).Bytes()
dexpr, diags := hclsyntax.ParseExpression(attrValLit, "", hcl.InitialPos)
if diags.HasErrors() {
return fmt.Errorf(`parsing HCL expression %q: %s`, string(attrValLit), diags.Error())
}
aval, diags := dexpr.Value(&hcl.EvalContext{Functions: map[string]function.Function{
"jsonencode": stdlib.JSONEncodeFunc,
}})
if diags.HasErrors() {
return fmt.Errorf(`evaluating value of HCL expression %q: %s`, string(attrValLit), diags.Error())
aval, err := attrValue(attrName, attrVal)
if err != nil {
return err
}

if aval.IsNull() {
rb.RemoveAttribute(attrName)
continue
Expand Down Expand Up @@ -162,8 +152,11 @@ func tuneForBlock(rb *hclwrite.Body, sch *tfpluginschema.Block, parentAttrNames
continue
}
} else if len(scht.AtLeastOneOf) == 0 {
// For O+C block that has "AtLeastOneOf" constraint, keep it.
rb.RemoveBlock(blkVal)
// For O+blocks attribute that has "AtLeastOneOf" constraint, or is explicitly specified, keep it.
if !(len(ocToKeep) != 0 && ocToKeep[blkVal.Type()]) {
rb.RemoveBlock(blkVal)
continue
}
continue
}
} else {
Expand All @@ -173,9 +166,28 @@ func tuneForBlock(rb *hclwrite.Body, sch *tfpluginschema.Block, parentAttrNames
}
}

if err := tuneForBlock(blkVal.Body(), scht.Block, append(parentAttrNames, blkVal.Type())); err != nil {
if err := tuneForBlock(blkVal.Body(), scht.Block, append(parentAttrNames, blkVal.Type()), nil); err != nil {
return err
}
}
return nil
}

func attrValue(attrName string, attr *hclwrite.Attribute) (cty.Value, error) {
attrExpr, diags := hclwrite.ParseConfig(attr.BuildTokens(nil).Bytes(), "generate_attr", hcl.InitialPos)
if diags.HasErrors() {
return cty.Zero, fmt.Errorf(`building attribute %q attribute: %s`, attrName, diags.Error())
}
attrValLit := attrExpr.Body().GetAttribute(attrName).Expr().BuildTokens(nil).Bytes()
dexpr, diags := hclsyntax.ParseExpression(attrValLit, "", hcl.InitialPos)
if diags.HasErrors() {
return cty.Zero, fmt.Errorf(`parsing HCL expression %q: %s`, string(attrValLit), diags.Error())
}
aval, diags := dexpr.Value(&hcl.EvalContext{Functions: map[string]function.Function{
"jsonencode": stdlib.JSONEncodeFunc,
}})
if diags.HasErrors() {
return cty.Zero, fmt.Errorf(`evaluating value of HCL expression %q: %s`, string(attrValLit), diags.Error())
}
return aval, nil
}
55 changes: 53 additions & 2 deletions tfadd/internal/tune_tpl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func TestTuneTpl(t *testing.T) {
expect := `resource "foo" "test" {
req {}
}`
actual, err := TuneTpl(sch, []byte(input), "foo")
actual, err := TuneTpl(sch, []byte(input), "foo", nil)
require.NoError(t, err)
require.Equal(t, expect, string(actual))
}
Expand All @@ -47,6 +47,7 @@ func TestTuneForBlock(t *testing.T) {
schema tfpluginschema.Block
input string
expect string
ocKeep map[string]bool
}{
{
name: "primary attributes only",
Expand Down Expand Up @@ -302,6 +303,31 @@ func TestTuneForBlock(t *testing.T) {
expect: `resource "foo" "test" {
attr1 = 1
attr2 = 2
}`,
},
{
name: "O+C attributes that is specified to keep",
schema: tfpluginschema.Block{
Attributes: map[string]*tfpluginschema.Attribute{
"attr1": {
Type: cty.Number,
Optional: true,
Computed: true,
},
"attr2": {
Type: cty.Number,
Optional: true,
Computed: true,
},
},
},
ocKeep: map[string]bool{"attr1": true},
input: `resource "foo" "test" {
attr1 = 1
attr2 = 2
}`,
expect: `resource "foo" "test" {
attr1 = 1
}`,
},
{
Expand Down Expand Up @@ -457,6 +483,31 @@ func TestTuneForBlock(t *testing.T) {
expect: `resource "foo" "test" {
blk1 {}
blk2 {}
}`,
},
{
name: "O+C blocks that is specified to keep",
schema: tfpluginschema.Block{
NestedBlocks: map[string]*tfpluginschema.NestedBlock{
"blk1": {
NestingMode: tfpluginschema.NestingSingle,
Optional: true,
Computed: true,
},
"blk2": {
NestingMode: tfpluginschema.NestingSingle,
Optional: true,
Computed: true,
},
},
},
ocKeep: map[string]bool{"blk1": true},
input: `resource "foo" "test" {
blk1 {}
blk2 {}
}`,
expect: `resource "foo" "test" {
blk1 {}
}`,
},
}
Expand All @@ -465,7 +516,7 @@ func TestTuneForBlock(t *testing.T) {
f, diag := hclwrite.ParseConfig([]byte(c.input), "", hcl.InitialPos)
require.False(t, diag.HasErrors(), diag.Error())
rb := f.Body().Blocks()[0].Body()
require.NoError(t, tuneForBlock(rb, &c.schema, nil))
require.NoError(t, tuneForBlock(rb, &c.schema, nil, c.ocKeep))
require.Equal(t, c.expect, string(f.Bytes()))
})
}
Expand Down
17 changes: 15 additions & 2 deletions tfadd/tfadd_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ func GenerateForOneResource(rsch *tfjson.Schema, res tfstate.StateResource, full
return nil, fmt.Errorf("generate template from state for %s: %v", res.Type, err)
}
if !full {
pinfo, ok := supportedProviders[strings.TrimPrefix(res.ProviderName, "registry.terraform.io/")]
providerName := strings.TrimPrefix(res.ProviderName, "registry.terraform.io/")
pinfo, ok := supportedProviders[providerName]
if !ok {
return b, nil
}
Expand All @@ -176,7 +177,19 @@ func GenerateForOneResource(rsch *tfjson.Schema, res tfstate.StateResource, full
if !ok {
return b, nil
}
b, err = internal.TuneTpl(*sch, b, res.Type)
if providerName == "azure/azapi" {
b, err = internal.TuneTpl(*sch, b, res.Type,
map[string]bool{
"name": true,
"parent_id": true,
"identity": true,
"location": true,
"tags": true,
},
)
} else {
b, err = internal.TuneTpl(*sch, b, res.Type, nil)
}
if err != nil {
return nil, fmt.Errorf("tune template for %s: %v", res.Type, err)
}
Expand Down

0 comments on commit d9c6bf9

Please sign in to comment.