Skip to content

Commit

Permalink
Merge 78b5bba into 734aee4
Browse files Browse the repository at this point in the history
  • Loading branch information
t0yv0 authored Dec 22, 2022
2 parents 734aee4 + 78b5bba commit 586be32
Show file tree
Hide file tree
Showing 21 changed files with 2,647 additions and 166 deletions.
17 changes: 17 additions & 0 deletions pkg/tfpfbridge/ids.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,20 @@ func (ie idExtractor) extractID(state tftypes.Value) (string, error) {
}
return idString, nil
}

// Drills down into a Value. Returns the found value and a flag indicating if it was found or not.
func valueAtPath(p *tftypes.AttributePath, root tftypes.Value) (tftypes.Value, bool, error) {
result, _, err := tftypes.WalkAttributePath(root, p)
if err == tftypes.ErrInvalidStep {
return tftypes.Value{}, false, nil // not found
}
if err != nil {
return tftypes.Value{}, false, err // error
}
resultValue, ok := result.(tftypes.Value)
if !ok {
return tftypes.Value{}, false, fmt.Errorf(
"Expected a value of type tftypes.Value but got: %v", result)
}
return resultValue, true, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,31 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package schemashim
package pfutils

import (
"fmt"

"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"reflect"

pfattr "github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

// attr type works around not being able to link to fwschema.Attribute from
// Attr type works around not being able to link to fwschema.Attribute from
// "github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
//
// Most methods from fwschema.Attribute have simple signatures and are copied into attrLike interface. Casting to
// attrLike exposes these methods.
//
// GetAttributes method is special since it returns a NestedAttributes interface that is also internal and cannot be
// linked to. Instead, NestedAttriutes information is recorded in a dedicated new field.
type attr struct {
attrLike
nested map[string]attr
type Attr struct {
AttrLike
Nested map[string]Attr
}

type attrLike interface {
type AttrLike interface {
FrameworkType() pfattr.Type
IsComputed() bool
IsOptional() bool
Expand All @@ -46,9 +47,28 @@ type attrLike interface {
GetMarkdownDescription() string
}

func schemaToAttrMap(schema *tfsdk.Schema) map[string]attr {
func AttributeAtTerraformPath(schema *tfsdk.Schema, path *tftypes.AttributePath) (Attr, error) {
res, remaining, err := tftypes.WalkAttributePath(*schema, path)
if err != nil {
return Attr{}, fmt.Errorf("%v still remains in the path: %w", remaining, err)
}
switch r := res.(type) {
case tfsdk.Attribute:
m := SchemaToAttrMap(&tfsdk.Schema{
Attributes: map[string]tfsdk.Attribute{
"x": r,
},
})
return m["x"], nil
default:
return Attr{}, fmt.Errorf("Expected a Block but found %s at path %s",
reflect.TypeOf(r), path)
}
}

func SchemaToAttrMap(schema *tfsdk.Schema) map[string]Attr {
if schema.GetAttributes() == nil || len(schema.GetAttributes()) == 0 {
return map[string]attr{}
return map[string]Attr{}
}

// unable to reference fwschema.Attribute type directly, use GetAttriutes to hijack this type and get a
Expand All @@ -65,15 +85,15 @@ func schemaToAttrMap(schema *tfsdk.Schema) map[string]attr {
//
// dests[k].toMap[dests[k].key] = convert(queue[k])
type dest = struct {
toMap map[string]attr
toMap map[string]Attr
key string
}
dests := map[string]dest{}

jobCounter := 0

// queue up converting schema.GetAttributes() into finalMap
finalMap := map[string]attr{}
finalMap := map[string]Attr{}
for k, v := range schema.GetAttributes() {
job := fmt.Sprintf("%d", jobCounter)
jobCounter++
Expand All @@ -87,36 +107,19 @@ func schemaToAttrMap(schema *tfsdk.Schema) map[string]attr {
attrDest := popAt(dests, job)

// outAttr := convert(inAttr)
outAttr := attr{attrLike: inAttr}
outAttr := Attr{AttrLike: inAttr}
if nested := inAttr.GetAttributes(); nested != nil && nested.GetAttributes() != nil {
outAttr.nested = map[string]attr{}
outAttr.Nested = map[string]Attr{}
for k, v := range nested.GetAttributes() {
// schedule outAttr.nested[k] = convert(v)
job := fmt.Sprintf("%d", jobCounter)
jobCounter++
queue[job] = v
dests[job] = dest{toMap: outAttr.nested, key: k}
dests[job] = dest{toMap: outAttr.Nested, key: k}
}
}
attrDest.toMap[attrDest.key] = outAttr
}

return finalMap
}

// Remove and return the value at a random key.
func pop[T any](q map[string]T) (string, T) {
for k := range q {
return k, popAt(q, k)
}
panic("empty queue")
}

// Remove and return the value at key.
func popAt[T any](q map[string]T, key string) T {
if v, ok := q[key]; ok {
delete(q, key)
return v
}
panic("key no found: " + key)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package schemashim
package pfutils

import (
"fmt"
"reflect"

pfattr "github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

// block type works around not being able to link to fwschema.Block from
// Block type works around not being able to link to fwschema.Block from
// "github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
//
// Most methods from fwschema.Block have simple signatures and are copied into blockLike interface. Casting to blockLike
Expand All @@ -30,14 +32,14 @@ import (
// There are some exceptions though such as GetBlocks() map[string]Block and GetAttributes() map[string]Attribute. These
// signatures refer to further internal types. Instead of direct linking, this information is recovered and recorded in
// new dedicated fields.
type block struct {
blockLike
blockNestingMode blockNestingMode
nestedBlocks map[string]block
nestedAttrs map[string]attr
type Block struct {
BlockLike
BlockNestingMode BlockNestingMode
NestedBlocks map[string]Block
NestedAttrs map[string]Attr
}

type blockLike interface {
type BlockLike interface {
GetDeprecationMessage() string
GetDescription() string
GetMarkdownDescription() string
Expand All @@ -48,9 +50,28 @@ type blockLike interface {
Type() pfattr.Type
}

func schemaToBlockMap(schema *tfsdk.Schema) map[string]block {
func BlockAtTerraformPath(schema *tfsdk.Schema, path *tftypes.AttributePath) (Block, error) {
res, remaining, err := tftypes.WalkAttributePath(*schema, path)
if err != nil {
return Block{}, fmt.Errorf("%v still remains in the path: %w", remaining, err)
}
switch r := res.(type) {
case tfsdk.Block:
m := SchemaToBlockMap(&tfsdk.Schema{
Blocks: map[string]tfsdk.Block{
"x": r,
},
})
return m["x"], nil
default:
return Block{}, fmt.Errorf("Expected a Block but found %s at path %s",
reflect.TypeOf(r), path)
}
}

func SchemaToBlockMap(schema *tfsdk.Schema) map[string]Block {
if schema.GetBlocks() == nil || len(schema.GetBlocks()) == 0 {
return map[string]block{}
return map[string]Block{}
}

queue := schema.GetBlocks()
Expand All @@ -59,15 +80,15 @@ func schemaToBlockMap(schema *tfsdk.Schema) map[string]block {
}

type dest struct {
toMap map[string]block
toMap map[string]Block
key string
}

dests := map[string]dest{}

jobCounter := 0

finalMap := map[string]block{}
finalMap := map[string]Block{}
for k, v := range schema.GetBlocks() {
job := fmt.Sprintf("%d", jobCounter)
jobCounter++
Expand All @@ -80,26 +101,26 @@ func schemaToBlockMap(schema *tfsdk.Schema) map[string]block {
blockDest := popAt(dests, job)

// outBlock := convert(inBlock)
outBlock := block{
blockLike: inBlock,
blockNestingMode: blockNestingMode(uint8(inBlock.GetNestingMode())),
nestedBlocks: map[string]block{},
nestedAttrs: map[string]attr{},
outBlock := Block{
BlockLike: inBlock,
BlockNestingMode: BlockNestingMode(uint8(inBlock.GetNestingMode())),
NestedBlocks: map[string]Block{},
NestedAttrs: map[string]Attr{},
}

for k, v := range inBlock.GetBlocks() {
job := fmt.Sprintf("%d", jobCounter)
jobCounter++
queue[job] = v
dests[job] = dest{toMap: outBlock.nestedBlocks, key: k}
dests[job] = dest{toMap: outBlock.NestedBlocks, key: k}
}

if attributes := inBlock.GetAttributes(); attributes != nil {
m := make(map[string]tfsdk.Attribute)
for k, v := range attributes {
m[k] = v.(tfsdk.Attribute)
}
outBlock.nestedAttrs = schemaToAttrMap(&tfsdk.Schema{Attributes: m})
outBlock.NestedAttrs = SchemaToAttrMap(&tfsdk.Schema{Attributes: m})
}

blockDest.toMap[blockDest.key] = outBlock
Expand All @@ -108,11 +129,11 @@ func schemaToBlockMap(schema *tfsdk.Schema) map[string]block {
return finalMap
}

type blockNestingMode uint8
type BlockNestingMode uint8

const (
blockNestingModeUnknown blockNestingMode = 0
blockNestingModeList blockNestingMode = 1
blockNestingModeSet blockNestingMode = 2
blockNestingModeSingle blockNestingMode = 3
BlockNestingModeUnknown BlockNestingMode = 0
BlockNestingModeList BlockNestingMode = 1
BlockNestingModeSet BlockNestingMode = 2
BlockNestingModeSingle BlockNestingMode = 3
)
70 changes: 70 additions & 0 deletions pkg/tfpfbridge/pfutils/eq.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright 2016-2022, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pfutils

import (
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

type Eq interface {
Equal(path *tftypes.AttributePath, a, b tftypes.Value) (bool, error)
}

type defaultEq int

func (defaultEq) Equal(_ *tftypes.AttributePath, a, b tftypes.Value) (bool, error) {
return a.Equal(b), nil
}

// Default equality for tftype.Value.
var DefaultEq Eq = defaultEq(0)

type nonComputedEq struct {
Schema *tfsdk.Schema
}

func (eq *nonComputedEq) Equal(p *tftypes.AttributePath, a, b tftypes.Value) (bool, error) {
aNorm, err := replaceComputedAttributesWithNull(eq.Schema, p, a)
if err != nil {
return false, err
}
bNorm, err := replaceComputedAttributesWithNull(eq.Schema, p, b)
if err != nil {
return false, err
}
res := aNorm.Equal(bNorm)
return res, nil
}

// Considers two tftype.Value values equal if all their non-computed attributes are equal.
func NonComputedEq(schema *tfsdk.Schema) Eq {
return &nonComputedEq{schema}
}

func replaceComputedAttributesWithNull(schema *tfsdk.Schema,
offset *tftypes.AttributePath, val tftypes.Value) (tftypes.Value, error) {
return tftypes.Transform(val, func(p *tftypes.AttributePath, v tftypes.Value) (tftypes.Value, error) {
realPath := joinPaths(offset, p)
if attr, err := AttributeAtTerraformPath(schema, realPath); err == nil && attr.IsComputed() {
return tftypes.NewValue(v.Type(), nil), nil
}
return v, nil
})
}

func joinPaths(a, b *tftypes.AttributePath) *tftypes.AttributePath {
return tftypes.NewAttributePathWithSteps(append(a.Steps(), b.Steps()...))
}
Loading

0 comments on commit 586be32

Please sign in to comment.