Skip to content

Commit

Permalink
move pf write and add resource writing
Browse files Browse the repository at this point in the history
  • Loading branch information
VenelinMartinov committed Nov 1, 2024
1 parent 0a8ebb4 commit de1e97c
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 91 deletions.
90 changes: 0 additions & 90 deletions pkg/internal/tests/cross-tests/tfwrite.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"sort"

"github.com/hashicorp/hcl/v2/hclwrite"
pfproviderschema "github.com/hashicorp/terraform-plugin-framework/provider/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
"github.com/zclconf/go-cty/cty"
Expand Down Expand Up @@ -61,95 +60,6 @@ func (w SDKv2Writer) Resource(
return err
}

type PFWriter struct{ out io.Writer }

func WritePF(out io.Writer) PFWriter { return PFWriter{out} }

func (w PFWriter) Provider(sch pfproviderschema.Schema, providerName string, config map[string]cty.Value) error {
if !cty.ObjectVal(config).IsWhollyKnown() {
return fmt.Errorf("WriteHCL cannot yet write unknowns")
}
f := hclwrite.NewEmptyFile()
block := f.Body().AppendNewBlock("provider", []string{providerName})
writePfProvider(block.Body(), sch, config)
_, err := f.WriteTo(w.out)
return err
}

func writePfProvider(body *hclwrite.Body, schemas pfproviderschema.Schema, values map[string]cty.Value) {
writePfObject(body, pfproviderschema.NestedBlockObject{
Attributes: schemas.Attributes,
Blocks: schemas.Blocks,
}, values)
}

// writePfBlock writes the values for a single schema block to parentBody.
//
// Because blocks can be repeated (ListNestedBlock and SetNestedBlock), writePfBlock
// can write an arbitrary number of blocks.
//
// For example, writing a list would add two blocks to parentBody:
//
// writePfBlock("key", parentBody, ListNestedBlock{count: int}, cty.Value([{count: 1}, {count: 2}]))
//
// key {
// count = 1
// }
// key {
// count = 2
// }
//
// This is why writePfBlock is called with parentBody, instead of with the block body
// already created (as with [writeBlock]).
func writePfBlock(key string, parentBody *hclwrite.Body, schemas pfproviderschema.Block, value cty.Value) {
switch schemas := schemas.(type) {
case pfproviderschema.ListNestedBlock:
for _, v := range value.AsValueSlice() {
b := parentBody.AppendNewBlock(key, nil).Body()
writePfObject(b, schemas.NestedObject, v.AsValueMap())
}
case pfproviderschema.SetNestedBlock:
values := value.AsValueSet().Values()
for _, v := range values {
b := parentBody.AppendNewBlock(key, nil).Body()
writePfObject(b, schemas.NestedObject, v.AsValueMap())
}
case pfproviderschema.SingleNestedBlock:
body := parentBody.AppendNewBlock(key, nil).Body()

if value.IsNull() {
return
}

writePfObject(body, pfproviderschema.NestedBlockObject{
Attributes: schemas.Attributes,
Blocks: schemas.Blocks,
}, value.AsValueMap())
default:
contract.Failf("Unknown block type: %T", schemas)
}
}

func writePfObject(body *hclwrite.Body, schemas pfproviderschema.NestedBlockObject, values map[string]cty.Value) {
keys := make([]string, 0, len(values))
for k := range values {
keys = append(keys, k)
}
sort.Strings(keys)

for _, key := range keys {
if _, ok := schemas.Attributes[key]; ok {
body.SetAttributeValue(key, values[key])
continue
}
if block, ok := schemas.Blocks[key]; ok {
writePfBlock(key, body, block, values[key])
continue
}
contract.Failf("Could not find a attr or block for value key %q", key)
}
}

func writeBlock(body *hclwrite.Body, schemas map[string]*schema.Schema, values map[string]cty.Value) {
internalMap := schema.InternalMap(schemas)
coreConfigSchema := internalMap.CoreConfigSchema()
Expand Down
2 changes: 1 addition & 1 deletion pkg/pf/tests/internal/cross-tests/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func Configure(t *testing.T, schema schema.Schema, tfConfig map[string]cty.Value
t.Run("tf", func(t *testing.T) {
defer propagateSkip(topLevelT, t)
var hcl bytes.Buffer
err := crosstests.WritePF(&hcl).Provider(schema, providerName, tfConfig)
err := WritePF(&hcl).Provider(schema, providerName, tfConfig)
require.NoError(t, err)
// TF does not configure providers unless they are involved with creating
// a resource or datasource, so we create "res" to give the TF provider a
Expand Down
169 changes: 169 additions & 0 deletions pkg/pf/tests/internal/cross-tests/tfwrite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package crosstests

import (
"fmt"
"io"
"sort"

"github.com/hashicorp/hcl/v2/hclwrite"
pschema "github.com/hashicorp/terraform-plugin-framework/provider/schema"
rschema "github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
"github.com/zclconf/go-cty/cty"
)

type PFWriter struct{ out io.Writer }

func WritePF(out io.Writer) PFWriter { return PFWriter{out} }

func (w PFWriter) Provider(sch pschema.Schema, providerName string, config map[string]cty.Value) error {
if !cty.ObjectVal(config).IsWhollyKnown() {
return fmt.Errorf("WriteHCL cannot yet write unknowns")
}
f := hclwrite.NewEmptyFile()
block := f.Body().AppendNewBlock("provider", []string{providerName})
writePfProvider(block.Body(), sch, config)
_, err := f.WriteTo(w.out)
return err
}

func (w PFWriter) Resource(sch rschema.Schema, resourceType, resourceName string, config map[string]cty.Value) error {
if !cty.ObjectVal(config).IsWhollyKnown() {
return fmt.Errorf("WriteHCL cannot yet write unknowns")
}
f := hclwrite.NewEmptyFile()
block := f.Body().AppendNewBlock("resource", []string{resourceType, resourceName})
writePfResource(block.Body(), sch, config)
_, err := f.WriteTo(w.out)
return err
}

func writePfProvider(body *hclwrite.Body, schemas pschema.Schema, values map[string]cty.Value) {
writePfObjectProvider(body, pschema.NestedBlockObject{
Attributes: schemas.Attributes,
Blocks: schemas.Blocks,
}, values)
}

func writePfResource(body *hclwrite.Body, schemas rschema.Schema, values map[string]cty.Value) {
writePfObjectResource(body, rschema.NestedBlockObject{
Attributes: schemas.Attributes,
Blocks: schemas.Blocks,
}, values)
}

// writePfBlockProvider writes the values for a single schema block to parentBody.
//
// Because blocks can be repeated (ListNestedBlock and SetNestedBlock), writePfBlockProvider
// can write an arbitrary number of blocks.
//
// For example, writing a list would add two blocks to parentBody:
//
// writePfBlockProvider("key", parentBody, ListNestedBlock{count: int}, cty.Value([{count: 1}, {count: 2}]))
//
// key {
// count = 1
// }
// key {
// count = 2
// }
//
// This is why writePfBlockProvider is called with parentBody, instead of with the block body
// already created (as with [writeBlock]).
func writePfBlockProvider(key string, parentBody *hclwrite.Body, schemas pschema.Block, value cty.Value) {
switch schemas := schemas.(type) {
case pschema.ListNestedBlock:
for _, v := range value.AsValueSlice() {
b := parentBody.AppendNewBlock(key, nil).Body()
writePfObjectProvider(b, schemas.NestedObject, v.AsValueMap())
}
case pschema.SetNestedBlock:
values := value.AsValueSet().Values()
for _, v := range values {
b := parentBody.AppendNewBlock(key, nil).Body()
writePfObjectProvider(b, schemas.NestedObject, v.AsValueMap())
}
case pschema.SingleNestedBlock:
body := parentBody.AppendNewBlock(key, nil).Body()

if value.IsNull() {
return
}

writePfObjectProvider(body, pschema.NestedBlockObject{
Attributes: schemas.Attributes,
Blocks: schemas.Blocks,
}, value.AsValueMap())
default:
contract.Failf("Unknown block type: %T", schemas)
}
}

func writePfBlockResource(key string, parentBody *hclwrite.Body, schemas rschema.Block, value cty.Value) {
switch schemas := schemas.(type) {
case rschema.ListNestedBlock:
for _, v := range value.AsValueSlice() {
b := parentBody.AppendNewBlock(key, nil).Body()
writePfObjectResource(b, schemas.NestedObject, v.AsValueMap())
}
case rschema.SetNestedBlock:
values := value.AsValueSet().Values()
for _, v := range values {
b := parentBody.AppendNewBlock(key, nil).Body()
writePfObjectResource(b, schemas.NestedObject, v.AsValueMap())
}
case rschema.SingleNestedBlock:
body := parentBody.AppendNewBlock(key, nil).Body()

if value.IsNull() {
return
}

writePfObjectResource(body, rschema.NestedBlockObject{
Attributes: schemas.Attributes,
Blocks: schemas.Blocks,
}, value.AsValueMap())
default:
contract.Failf("Unknown block type: %T", schemas)
}
}

func writePfObjectProvider(body *hclwrite.Body, schemas pschema.NestedBlockObject, values map[string]cty.Value) {
keys := make([]string, 0, len(values))
for k := range values {
keys = append(keys, k)
}
sort.Strings(keys)

for _, key := range keys {
if _, ok := schemas.Attributes[key]; ok {
body.SetAttributeValue(key, values[key])
continue
}
if block, ok := schemas.Blocks[key]; ok {
writePfBlockProvider(key, body, block, values[key])
continue
}
contract.Failf("Could not find a attr or block for value key %q", key)
}
}

func writePfObjectResource(body *hclwrite.Body, schemas rschema.NestedBlockObject, values map[string]cty.Value) {
keys := make([]string, 0, len(values))
for k := range values {
keys = append(keys, k)
}
sort.Strings(keys)

for _, key := range keys {
if _, ok := schemas.Attributes[key]; ok {
body.SetAttributeValue(key, values[key])
continue
}
if block, ok := schemas.Blocks[key]; ok {
writePfBlockResource(key, body, block, values[key])
continue
}
contract.Failf("Could not find a attr or block for value key %q", key)
}
}

0 comments on commit de1e97c

Please sign in to comment.