Skip to content

Commit f93f37b

Browse files
Avoid roundtrip to json when converting Cty to map[string]any
1 parent 1036ce2 commit f93f37b

File tree

3 files changed

+56
-239
lines changed

3 files changed

+56
-239
lines changed

pkg/tfshim/sdk-v2/cty_json.go

Lines changed: 0 additions & 207 deletions
This file was deleted.

pkg/tfshim/sdk-v2/instance_state.go

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,10 @@
1515
package sdkv2
1616

1717
import (
18-
"bytes"
19-
"encoding/json"
2018
"strings"
2119

22-
"github.com/hashicorp/go-cty/cty"
2320
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
2421
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
25-
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
2622
diff_reader "github.com/pulumi/terraform-diff-reader/sdk-v2"
2723

2824
shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim"
@@ -67,34 +63,6 @@ func (s v2InstanceState) Object(sch shim.SchemaMap) (map[string]interface{}, err
6763
return s.objectV1(sch)
6864
}
6965

70-
// This is needed because json.Unmarshal uses float64 for numbers by default which truncates int64 numbers.
71-
func unmarshalJSON(data []byte, v interface{}) error {
72-
dec := json.NewDecoder(bytes.NewReader(data))
73-
dec.UseNumber()
74-
return dec.Decode(v)
75-
}
76-
77-
// objectFromCtyValue takes a cty.Value and converts it to JSON object.
78-
// We do not care about type checking the values, we just want to do our best to recursively convert
79-
// the cty.Value to the underlying value
80-
//
81-
// NOTE: one of the transforms this needs to handle is converting unknown values.
82-
// cty.Value that are also unknown cannot be converted to their underlying value. To get
83-
// around this we just convert to a sentinel, which so far does not seem to cause any issues downstream
84-
func objectFromCtyValue(v cty.Value) map[string]interface{} {
85-
var path cty.Path
86-
buf := &bytes.Buffer{}
87-
// The round trip here to JSON is redundant, we could instead convert from cty to map[string]interface{} directly
88-
err := marshal(v, v.Type(), path, buf)
89-
contract.AssertNoErrorf(err, "Failed to marshal cty.Value to a JSON string value")
90-
91-
var m map[string]interface{}
92-
err = unmarshalJSON(buf.Bytes(), &m)
93-
contract.AssertNoErrorf(err, "failed to unmarshal: %s", buf.String())
94-
95-
return m
96-
}
97-
9866
// The legacy version of Object used custom Pulumi code forked from TF sources.
9967
func (s v2InstanceState) objectV1(sch shim.SchemaMap) (map[string]interface{}, error) {
10068
obj := make(map[string]interface{})

pkg/tfshim/sdk-v2/object_from_cty.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package sdkv2
2+
3+
import (
4+
"github.com/hashicorp/go-cty/cty"
5+
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
6+
)
7+
8+
func objectFromCtyValue(val cty.Value) map[string]any {
9+
return objectFromCtyValueInner(val).(map[string]any)
10+
}
11+
12+
func objectFromCtyValueInner(val cty.Value) any {
13+
contract.Assertf(!val.IsMarked(), "value has marks, so it cannot be serialized")
14+
if val.IsNull() {
15+
return nil
16+
}
17+
18+
if !val.IsKnown() {
19+
return terraformUnknownVariableValue
20+
}
21+
22+
switch {
23+
case val.Type().IsPrimitiveType():
24+
switch val.Type() {
25+
case cty.String:
26+
return val.AsString()
27+
case cty.Number:
28+
return val.AsBigFloat().Text('f', -1)
29+
case cty.Bool:
30+
return val.True()
31+
default:
32+
contract.Failf("unsupported primitive type: %s", val.Type().FriendlyName())
33+
}
34+
case val.Type().IsListType(), val.Type().IsSetType(), val.Type().IsTupleType():
35+
l := make([]interface{}, 0, val.LengthInt())
36+
it := val.ElementIterator()
37+
for it.Next() {
38+
_, ev := it.Element()
39+
elem := objectFromCtyValueInner(ev)
40+
l = append(l, elem)
41+
}
42+
return l
43+
case val.Type().IsObjectType(), val.Type().IsMapType():
44+
l := make(map[string]interface{})
45+
it := val.ElementIterator()
46+
for it.Next() {
47+
ek, ev := it.Element()
48+
cv := objectFromCtyValueInner(ev)
49+
l[ek.AsString()] = cv
50+
}
51+
return l
52+
}
53+
54+
contract.Failf("unsupported type: %s", val.Type().FriendlyName())
55+
return nil
56+
}

0 commit comments

Comments
 (0)