From da2282374d3b7fb2227e526d2ecede9db7b8c03b Mon Sep 17 00:00:00 2001 From: VenelinMartinov Date: Tue, 23 Jul 2024 19:16:53 +0300 Subject: [PATCH] Fix nested computed read (#2181) Fixes an issue with the SDKv2 and PF input extraction. During import we wrongly return values for properties which are fully computed which causes invalid programs to be generated. ~The object check in `CastToTypeObject` was wrong for SDKV2 objects: https://github.com/pulumi/pulumi-terraform-bridge/blob/48fd41bd16e2f7f933bb9390ab7619d52faabb6d/pkg/tfshim/util/types.go#L36~ The `CastToTypeObject` object check is wrong for both SDKv2 and PF. For the SDKV2 only sets and lists can have a `Resource` `.Elem`. For PF it list-nested blocks are represented with a `Resource` `.Elem` in the shim layer, so the check did not catch these. PF details here: https://github.com/pulumi/pulumi-terraform-bridge/pull/2231 This fixes the check and adds regression tests for the broken imports which resulted from 2180. Test coverage added in https://github.com/pulumi/pulumi-terraform-bridge/pull/2224 for sdkv2 and https://github.com/pulumi/pulumi-terraform-bridge/pull/2225 for pf. fixes https://github.com/pulumi/pulumi-terraform-bridge/issues/2180 depends on https://github.com/pulumi/providertest/pull/91 stacked on https://github.com/pulumi/pulumi-terraform-bridge/pull/2225 --- dynamic/go.sum | 4 +- go.mod | 2 +- go.sum | 4 +- pf/go.sum | 4 +- pf/tests/extract_inputs_test.go | 20 +++------ pf/tests/go.mod | 2 +- pf/tests/go.sum | 4 +- pkg/tests/go.mod | 2 +- pkg/tests/go.sum | 4 +- pkg/tests/schema_pulumi_test.go | 78 +++++++++++++++++++++++++++++++++ pkg/tfbridge/schema.go | 7 +-- pkg/tfbridge/schema_test.go | 67 +++++++++++++++++++--------- pkg/tfshim/util/types.go | 1 - 13 files changed, 146 insertions(+), 53 deletions(-) diff --git a/dynamic/go.sum b/dynamic/go.sum index dce986ae1..0c37ab841 100644 --- a/dynamic/go.sum +++ b/dynamic/go.sum @@ -760,8 +760,8 @@ github.com/pulumi/esc v0.9.1 h1:HH5eEv8sgyxSpY5a8yePyqFXzA8cvBvapfH8457+mIs= github.com/pulumi/esc v0.9.1/go.mod h1:oEJ6bOsjYlQUpjf70GiX+CXn3VBmpwFDxUTlmtUN84c= github.com/pulumi/inflector v0.1.1 h1:dvlxlWtXwOJTUUtcYDvwnl6Mpg33prhK+7mzeF+SobA= github.com/pulumi/inflector v0.1.1/go.mod h1:HUFCjcPTz96YtTuUlwG3i3EZG4WlniBvR9bd+iJxCUY= -github.com/pulumi/providertest v0.0.13 h1:9CAaoviOTuCVHDI15h3znXa5JsKYtXLYHIIdxOCzo3Y= -github.com/pulumi/providertest v0.0.13/go.mod h1:REAoaN+hGOtdWJGirfWYqcSjCejlbGfzyVTUuemJTuE= +github.com/pulumi/providertest v0.0.14 h1:5QlAPAAs82jkQraHsJvq1xgVfC7xtW8sFJwv2pHgxQ8= +github.com/pulumi/providertest v0.0.14/go.mod h1:GcsqEGgSngwaNOD+kICJPIUQlnA911fGBU8HDlJvVL0= github.com/pulumi/pulumi-java/pkg v0.11.0 h1:Jw9gBvyfmfOMq/EkYDm9+zGPxsDAA8jfeMpHmtZ+1oA= github.com/pulumi/pulumi-java/pkg v0.11.0/go.mod h1:sXAk25P47AQVQL6ilAbFmRNgZykC7og/+87ihnqzFTc= github.com/pulumi/pulumi-terraform-bridge/x/muxer v0.0.8 h1:mav2tSitA9BPJPLLahKgepHyYsMzwaTm4cvp0dcTMYw= diff --git a/go.mod b/go.mod index 52b6ac2e2..ff0ac63b3 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 github.com/pkg/errors v0.9.1 github.com/pulumi/inflector v0.1.1 - github.com/pulumi/providertest v0.0.13 + github.com/pulumi/providertest v0.0.14 github.com/pulumi/pulumi-java/pkg v0.11.0 github.com/pulumi/pulumi-terraform-bridge/x/muxer v0.0.8 github.com/pulumi/pulumi-yaml v1.9.1 diff --git a/go.sum b/go.sum index 9457bfad4..0201dc482 100644 --- a/go.sum +++ b/go.sum @@ -1902,8 +1902,8 @@ github.com/pulumi/esc v0.9.1 h1:HH5eEv8sgyxSpY5a8yePyqFXzA8cvBvapfH8457+mIs= github.com/pulumi/esc v0.9.1/go.mod h1:oEJ6bOsjYlQUpjf70GiX+CXn3VBmpwFDxUTlmtUN84c= github.com/pulumi/inflector v0.1.1 h1:dvlxlWtXwOJTUUtcYDvwnl6Mpg33prhK+7mzeF+SobA= github.com/pulumi/inflector v0.1.1/go.mod h1:HUFCjcPTz96YtTuUlwG3i3EZG4WlniBvR9bd+iJxCUY= -github.com/pulumi/providertest v0.0.13 h1:9CAaoviOTuCVHDI15h3znXa5JsKYtXLYHIIdxOCzo3Y= -github.com/pulumi/providertest v0.0.13/go.mod h1:REAoaN+hGOtdWJGirfWYqcSjCejlbGfzyVTUuemJTuE= +github.com/pulumi/providertest v0.0.14 h1:5QlAPAAs82jkQraHsJvq1xgVfC7xtW8sFJwv2pHgxQ8= +github.com/pulumi/providertest v0.0.14/go.mod h1:GcsqEGgSngwaNOD+kICJPIUQlnA911fGBU8HDlJvVL0= github.com/pulumi/pulumi-java/pkg v0.11.0 h1:Jw9gBvyfmfOMq/EkYDm9+zGPxsDAA8jfeMpHmtZ+1oA= github.com/pulumi/pulumi-java/pkg v0.11.0/go.mod h1:sXAk25P47AQVQL6ilAbFmRNgZykC7og/+87ihnqzFTc= github.com/pulumi/pulumi-yaml v1.9.1 h1:JPeI80M23SPactxgnCFS1casZlSr7ZhAXwSx4H55QQ4= diff --git a/pf/go.sum b/pf/go.sum index 0a4b1df0a..0b6a9cefa 100644 --- a/pf/go.sum +++ b/pf/go.sum @@ -1901,8 +1901,8 @@ github.com/pulumi/esc v0.9.1 h1:HH5eEv8sgyxSpY5a8yePyqFXzA8cvBvapfH8457+mIs= github.com/pulumi/esc v0.9.1/go.mod h1:oEJ6bOsjYlQUpjf70GiX+CXn3VBmpwFDxUTlmtUN84c= github.com/pulumi/inflector v0.1.1 h1:dvlxlWtXwOJTUUtcYDvwnl6Mpg33prhK+7mzeF+SobA= github.com/pulumi/inflector v0.1.1/go.mod h1:HUFCjcPTz96YtTuUlwG3i3EZG4WlniBvR9bd+iJxCUY= -github.com/pulumi/providertest v0.0.13 h1:9CAaoviOTuCVHDI15h3znXa5JsKYtXLYHIIdxOCzo3Y= -github.com/pulumi/providertest v0.0.13/go.mod h1:REAoaN+hGOtdWJGirfWYqcSjCejlbGfzyVTUuemJTuE= +github.com/pulumi/providertest v0.0.14 h1:5QlAPAAs82jkQraHsJvq1xgVfC7xtW8sFJwv2pHgxQ8= +github.com/pulumi/providertest v0.0.14/go.mod h1:GcsqEGgSngwaNOD+kICJPIUQlnA911fGBU8HDlJvVL0= github.com/pulumi/pulumi-java/pkg v0.11.0 h1:Jw9gBvyfmfOMq/EkYDm9+zGPxsDAA8jfeMpHmtZ+1oA= github.com/pulumi/pulumi-java/pkg v0.11.0/go.mod h1:sXAk25P47AQVQL6ilAbFmRNgZykC7og/+87ihnqzFTc= github.com/pulumi/pulumi-yaml v1.9.1 h1:JPeI80M23SPactxgnCFS1casZlSr7ZhAXwSx4H55QQ4= diff --git a/pf/tests/extract_inputs_test.go b/pf/tests/extract_inputs_test.go index c514a0423..84872053e 100644 --- a/pf/tests/extract_inputs_test.go +++ b/pf/tests/extract_inputs_test.go @@ -703,7 +703,6 @@ func TestExtractInputsFromOutputsPF(t *testing.T) { }}}, }), }, - // TODO[pulumi/pulumi-terraform-bridge#2180]: This should not yield values for computed properties { name: "list nested block computed not extracted", props: resource.NewPropertyMapFromMap(map[string]interface{}{ @@ -727,12 +726,9 @@ func TestExtractInputsFromOutputsPF(t *testing.T) { V: []resource.PropertyValue{}, }, resource.PropertyKey("block_field"): resource.PropertyValue{V: []resource.PropertyValue{{ - V: resource.PropertyMap{ - resource.PropertyKey("__defaults"): resource.PropertyValue{ - V: []resource.PropertyValue{}, - }, - resource.PropertyKey("nested_field"): resource.PropertyValue{V: "nested_value"}, // wrong - }, + V: resource.PropertyMap{resource.PropertyKey("__defaults"): resource.PropertyValue{ + V: []resource.PropertyValue{}, + }}, }}}, }), }, @@ -801,7 +797,6 @@ func TestExtractInputsFromOutputsPF(t *testing.T) { }}}, }), }, - // TODO[pulumi/pulumi-terraform-bridge#2180]: This should not yield values for computed properties { name: "set nested block computed not extracted", props: resource.NewPropertyMapFromMap(map[string]interface{}{ @@ -825,12 +820,9 @@ func TestExtractInputsFromOutputsPF(t *testing.T) { V: []resource.PropertyValue{}, }, resource.PropertyKey("block_field"): resource.PropertyValue{V: []resource.PropertyValue{{ - V: resource.PropertyMap{ - resource.PropertyKey("__defaults"): resource.PropertyValue{ - V: []resource.PropertyValue{}, - }, - resource.PropertyKey("nested_field"): resource.PropertyValue{V: "nested_value"}, //wrong - }, + V: resource.PropertyMap{resource.PropertyKey("__defaults"): resource.PropertyValue{ + V: []resource.PropertyValue{}, + }}, }}}, }), }, diff --git a/pf/tests/go.mod b/pf/tests/go.mod index 75581997a..3040546d3 100644 --- a/pf/tests/go.mod +++ b/pf/tests/go.mod @@ -8,7 +8,7 @@ require ( github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 github.com/hashicorp/terraform-provider-tls/shim v0.0.0-00010101000000-000000000000 github.com/hexops/autogold/v2 v2.2.1 - github.com/pulumi/providertest v0.0.13 + github.com/pulumi/providertest v0.0.14 github.com/pulumi/pulumi-terraform-bridge/pf v0.0.0 github.com/pulumi/pulumi-terraform-bridge/v3 v3.87.0 github.com/stretchr/testify v1.9.0 diff --git a/pf/tests/go.sum b/pf/tests/go.sum index 61ecbbb4e..6b9799be5 100644 --- a/pf/tests/go.sum +++ b/pf/tests/go.sum @@ -1928,8 +1928,8 @@ github.com/pulumi/esc v0.9.1 h1:HH5eEv8sgyxSpY5a8yePyqFXzA8cvBvapfH8457+mIs= github.com/pulumi/esc v0.9.1/go.mod h1:oEJ6bOsjYlQUpjf70GiX+CXn3VBmpwFDxUTlmtUN84c= github.com/pulumi/inflector v0.1.1 h1:dvlxlWtXwOJTUUtcYDvwnl6Mpg33prhK+7mzeF+SobA= github.com/pulumi/inflector v0.1.1/go.mod h1:HUFCjcPTz96YtTuUlwG3i3EZG4WlniBvR9bd+iJxCUY= -github.com/pulumi/providertest v0.0.13 h1:9CAaoviOTuCVHDI15h3znXa5JsKYtXLYHIIdxOCzo3Y= -github.com/pulumi/providertest v0.0.13/go.mod h1:REAoaN+hGOtdWJGirfWYqcSjCejlbGfzyVTUuemJTuE= +github.com/pulumi/providertest v0.0.14 h1:5QlAPAAs82jkQraHsJvq1xgVfC7xtW8sFJwv2pHgxQ8= +github.com/pulumi/providertest v0.0.14/go.mod h1:GcsqEGgSngwaNOD+kICJPIUQlnA911fGBU8HDlJvVL0= github.com/pulumi/pulumi-java/pkg v0.11.0 h1:Jw9gBvyfmfOMq/EkYDm9+zGPxsDAA8jfeMpHmtZ+1oA= github.com/pulumi/pulumi-java/pkg v0.11.0/go.mod h1:sXAk25P47AQVQL6ilAbFmRNgZykC7og/+87ihnqzFTc= github.com/pulumi/pulumi-yaml v1.9.1 h1:JPeI80M23SPactxgnCFS1casZlSr7ZhAXwSx4H55QQ4= diff --git a/pkg/tests/go.mod b/pkg/tests/go.mod index 6eab9c18c..0ddef8b37 100644 --- a/pkg/tests/go.mod +++ b/pkg/tests/go.mod @@ -11,7 +11,7 @@ require ( github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 github.com/hexops/autogold/v2 v2.2.1 github.com/hexops/valast v1.4.4 - github.com/pulumi/providertest v0.0.13 + github.com/pulumi/providertest v0.0.14 github.com/pulumi/pulumi-terraform-bridge/v3 v3.80.0 github.com/stretchr/testify v1.9.0 gotest.tools v2.2.0+incompatible diff --git a/pkg/tests/go.sum b/pkg/tests/go.sum index 39dfce91f..82f245ece 100644 --- a/pkg/tests/go.sum +++ b/pkg/tests/go.sum @@ -1912,8 +1912,8 @@ github.com/pulumi/esc v0.9.1 h1:HH5eEv8sgyxSpY5a8yePyqFXzA8cvBvapfH8457+mIs= github.com/pulumi/esc v0.9.1/go.mod h1:oEJ6bOsjYlQUpjf70GiX+CXn3VBmpwFDxUTlmtUN84c= github.com/pulumi/inflector v0.1.1 h1:dvlxlWtXwOJTUUtcYDvwnl6Mpg33prhK+7mzeF+SobA= github.com/pulumi/inflector v0.1.1/go.mod h1:HUFCjcPTz96YtTuUlwG3i3EZG4WlniBvR9bd+iJxCUY= -github.com/pulumi/providertest v0.0.13 h1:9CAaoviOTuCVHDI15h3znXa5JsKYtXLYHIIdxOCzo3Y= -github.com/pulumi/providertest v0.0.13/go.mod h1:REAoaN+hGOtdWJGirfWYqcSjCejlbGfzyVTUuemJTuE= +github.com/pulumi/providertest v0.0.14 h1:5QlAPAAs82jkQraHsJvq1xgVfC7xtW8sFJwv2pHgxQ8= +github.com/pulumi/providertest v0.0.14/go.mod h1:GcsqEGgSngwaNOD+kICJPIUQlnA911fGBU8HDlJvVL0= github.com/pulumi/pulumi-java/pkg v0.11.0 h1:Jw9gBvyfmfOMq/EkYDm9+zGPxsDAA8jfeMpHmtZ+1oA= github.com/pulumi/pulumi-java/pkg v0.11.0/go.mod h1:sXAk25P47AQVQL6ilAbFmRNgZykC7og/+87ihnqzFTc= github.com/pulumi/pulumi-yaml v1.9.1 h1:JPeI80M23SPactxgnCFS1casZlSr7ZhAXwSx4H55QQ4= diff --git a/pkg/tests/schema_pulumi_test.go b/pkg/tests/schema_pulumi_test.go index 09246e822..aa32df8e4 100644 --- a/pkg/tests/schema_pulumi_test.go +++ b/pkg/tests/schema_pulumi_test.go @@ -1396,3 +1396,81 @@ Resources: }) } } + +func TestFullyComputedNestedAttribute(t *testing.T) { + resMap := map[string]*schema.Resource{ + "prov_test": { + Schema: map[string]*schema.Schema{ + "attached_disks": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Optional: true, + Type: schema.TypeString, + }, + "key256": { + Computed: true, + Type: schema.TypeString, + }, + }, + }, + }, + "top_level_computed": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + } + + importer := func(val any) func(context.Context, *schema.ResourceData, interface{}) ([]*schema.ResourceData, error) { + return func(ctx context.Context, rd *schema.ResourceData, i interface{}) ([]*schema.ResourceData, error) { + elMap := map[string]any{ + "name": "disk1", + "key256": val, + } + err := rd.Set("attached_disks", []map[string]any{elMap}) + require.NoError(t, err) + + err = rd.Set("top_level_computed", "computed_val") + require.NoError(t, err) + + return []*schema.ResourceData{rd}, nil + } + } + bridgedProvider := pulcheck.BridgedProvider(t, "prov", resMap) + + program := ` +name: test +runtime: yaml +` + for _, tc := range []struct { + name string + importVal any + }{ + { + "non-nil", + "val1", + }, + { + "nil", + nil, + }, + } { + t.Run(tc.name, func(t *testing.T) { + resMap["prov_test"].Importer = &schema.ResourceImporter{ + StateContext: importer(tc.importVal), + } + + pt := pulcheck.PulCheck(t, bridgedProvider, program) + + res := pt.Import("prov:index/test:Test", "res1", "id1", "") + + t.Logf(res.Stdout) + + require.NotContains(t, res.Stdout, "One or more imported inputs failed to validate") + }) + } +} diff --git a/pkg/tfbridge/schema.go b/pkg/tfbridge/schema.go index e03e28209..f3849ddad 100644 --- a/pkg/tfbridge/schema.go +++ b/pkg/tfbridge/schema.go @@ -33,7 +33,6 @@ import ( shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim" "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/schema" - shimutil "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/util" ) // This file deals with translating between the Pulumi representations of a resource's configuration and state and the @@ -497,8 +496,6 @@ func (ctx *conversionContext) makeTerraformInput( var tfflds shim.SchemaMap - // We cannot use [shimutil.CastToTypeObject] because we have machinery that constructs invalid - // resource objects, such as [elemSchemas]. if tfs != nil { if r, ok := tfs.Elem().(shim.Resource); ok { tfflds = r.Schema() @@ -1719,9 +1716,9 @@ func extractSchemaInputs( return resource.NewArrayProperty(v) case state.IsObject(): obj := state.ObjectValue() - if tfflds, ok := shimutil.CastToTypeObject(tfs); ok { + if tfflds, ok := tfs.Elem().(shim.Resource); ok { return resource.NewProperty( - extractSchemaInputsObject(obj, tfflds, ps.Fields), + extractSchemaInputsObject(obj, tfflds.Schema(), ps.Fields), ) } diff --git a/pkg/tfbridge/schema_test.go b/pkg/tfbridge/schema_test.go index 1743722c6..10795d7b5 100644 --- a/pkg/tfbridge/schema_test.go +++ b/pkg/tfbridge/schema_test.go @@ -2179,6 +2179,46 @@ func TestRefreshExtractInputsFromOutputsMaxItemsOne(t *testing.T) { assert.NoError(t, err) } +func TestRefreshExtractInputsFromOutputsListOfObjects(t *testing.T) { + t.Parallel() + + ruleSetProps := resource.PropertyMap{ + "attachedDisks": resource.NewArrayProperty([]resource.PropertyValue{ + resource.NewObjectProperty(resource.PropertyMap{ + "name": resource.NewStringProperty("name1"), + "key256": resource.NewNullProperty(), + }), + }), + } + + ruleSetSchema := func() shim.SchemaMap { + blockList := func(elem schema.SchemaMap) shim.Schema { + s := schema.Schema{ + Type: shim.TypeList, + Optional: true, + Elem: (&schema.Resource{ + Schema: elem, + }).Shim(), + } + return s.Shim() + } + + return schema.SchemaMap{ + "attachedDisks": blockList(schema.SchemaMap{ + "name": (&schema.Schema{Type: shim.TypeString, Optional: true}).Shim(), + "key256": (&schema.Schema{Type: shim.TypeString, Computed: true}).Shim(), + }), + } + } + + out, err := ExtractInputsFromOutputs(nil, ruleSetProps, ruleSetSchema(), nil, false) + assert.NoError(t, err) + t.Logf("out: %v", out) + attachedDiskVal := out["attachedDisks"].ArrayValue()[0].ObjectValue() + _, ok := attachedDiskVal["key256"] + assert.False(t, ok) +} + func TestFailureReasonForMissingRequiredFields(t *testing.T) { // Define two required inputs tfProvider := makeTestTFProviderV1( @@ -2742,7 +2782,6 @@ func TestExtractSchemaInputsNestedMaxItemsOne(t *testing.T) { "listObjects": resource.NewProperty([]resource.PropertyValue{ resource.NewProperty(resource.PropertyMap{ "__defaults": resource.NewProperty([]resource.PropertyValue{}), - "field1": resource.NewProperty(false), "listScalar": resource.NewProperty(1.0), }), }), @@ -3665,7 +3704,6 @@ func TestExtractInputsFromOutputsSdkv2(t *testing.T) { // }, // expected: autogold.Expect(), // }, - // TODO[pulumi/pulumi-terraform-bridge#2180]: This is wrong as an input should not be produced for computed values. { name: "list block with computed element not extracted", props: resource.NewPropertyMapFromMap(map[string]interface{}{ @@ -3687,20 +3725,16 @@ func TestExtractInputsFromOutputsSdkv2(t *testing.T) { V: []resource.PropertyValue{}, }, resource.PropertyKey("foo"): resource.PropertyValue{V: []resource.PropertyValue{{ - V: resource.PropertyMap{ - resource.PropertyKey("__defaults"): resource.PropertyValue{ - V: []resource.PropertyValue{}, - }, - resource.PropertyKey("bar"): resource.PropertyValue{V: "baz"}, - }, + V: resource.PropertyMap{resource.PropertyKey("__defaults"): resource.PropertyValue{ + V: []resource.PropertyValue{}, + }}, }}}, }), }, - // TODO[pulumi/pulumi-terraform-bridge#2180]: This is wrong as an input should not be produced for computed values. { name: "list block max items one with computed element not extracted", props: resource.NewPropertyMapFromMap(map[string]interface{}{ - "foo": []interface{}{map[string]string{"bar": "baz"}}, + "foo": map[string]string{"bar": "baz"}, }), schemaMap: map[string]*schemav2.Schema{ "foo": { @@ -3714,16 +3748,9 @@ func TestExtractInputsFromOutputsSdkv2(t *testing.T) { }, }, }, - expected: autogold.Expect(resource.PropertyMap{ - resource.PropertyKey("__defaults"): resource.PropertyValue{ - V: []resource.PropertyValue{}, - }, - resource.PropertyKey("foo"): resource.PropertyValue{V: []resource.PropertyValue{{ - V: resource.PropertyMap{resource.PropertyKey("bar"): resource.PropertyValue{ - V: "baz", - }}, - }}}, - }), + expected: autogold.Expect(resource.PropertyMap{resource.PropertyKey("__defaults"): resource.PropertyValue{ + V: []resource.PropertyValue{}, + }}), }, } diff --git a/pkg/tfshim/util/types.go b/pkg/tfshim/util/types.go index 1edfd22bc..79b7e333d 100644 --- a/pkg/tfshim/util/types.go +++ b/pkg/tfshim/util/types.go @@ -31,7 +31,6 @@ func IsOfTypeMap(tfs shim.Schema) bool { // CastToTypeObject performs a checked cast from shim.Schema to a TF object (a collection // of fields). -// // See [shim.Schema.Elem()] comment for all the details of the encoding. func CastToTypeObject(tfs shim.Schema) (shim.SchemaMap, bool) { if tfs == nil {