diff --git a/pkg/tfbridge/schema.go b/pkg/tfbridge/schema.go index 5c234fdef..50bfbad1d 100644 --- a/pkg/tfbridge/schema.go +++ b/pkg/tfbridge/schema.go @@ -393,6 +393,15 @@ func MakeTerraformAttributesFromRPC(res *PulumiResource, m *pbstruct.Struct, return MakeTerraformAttributes(res, props, tfs, ps, defaults) } +// flattenValue takes a single value and recursively flattens its properties into the given string -> string map under +// the provided prefix. It expects that the value has been "schema-fied" by being read out of a schema.FieldReader (in +// particular, all sets *must* be represented as schema.Set values). The flattened value may then be used as the value +// of a terraform.InstanceState.Attributes field. +// +// Note that this duplicates much of the logic in TF's schema.MapFieldWriter. Ideally, we would just use that type, +// but there are various API/implementation challenges that preclude that option. The most worrying (and potentially +// fragile) piece of duplication is the code that calculates a set member's hash code; see the code under +// `case *schema.Set`. func flattenValue(result map[string]string, prefix string, value interface{}) { if value == nil { return @@ -423,6 +432,8 @@ func flattenValue(result map[string]string, prefix string, value interface{}) { // Flatten each element. setList := t.List() for _, elem := range setList { + // Note that the logic below is duplicated from `scheme.Set.hash`. If that logic ever changes, this will + // need to change in kind. code := t.F(elem) if code < 0 { code = -code @@ -449,11 +460,16 @@ func flattenValue(result map[string]string, prefix string, value interface{}) { func MakeTerraformAttributesFromInputs(inputs map[string]interface{}, tfs map[string]*schema.Schema) (map[string]string, error) { + // In order to flatten the TF inputs into a TF attribute map, we must first schema-ify them by reading them out of + // a FieldReader. The most straightforward way to do this is to turn the inputs into a TF config.Config value and + // use the same to create a schema.ConfigFieldReader. cfg, err := MakeTerraformConfigFromInputs(inputs) if err != nil { return nil, err } + // Read each top-level value out of the config we created above using a ConfigFieldReader and recursively flatten + // them into their TF attribute form. The result is our set of TF attributes. result := make(map[string]string) reader := &schema.ConfigFieldReader{Config: cfg, Schema: tfs} for k := range inputs {