github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/legacy/helper/schema/shims.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package schema 5 6 import ( 7 "encoding/json" 8 9 "github.com/zclconf/go-cty/cty" 10 ctyjson "github.com/zclconf/go-cty/cty/json" 11 12 "github.com/terramate-io/tf/configs/configschema" 13 "github.com/terramate-io/tf/configs/hcl2shim" 14 "github.com/terramate-io/tf/legacy/terraform" 15 ) 16 17 // DiffFromValues takes the current state and desired state as cty.Values and 18 // derives a terraform.InstanceDiff to give to the legacy providers. This is 19 // used to take the states provided by the new ApplyResourceChange method and 20 // convert them to a state+diff required for the legacy Apply method. 21 func DiffFromValues(prior, planned cty.Value, res *Resource) (*terraform.InstanceDiff, error) { 22 return diffFromValues(prior, planned, res, nil) 23 } 24 25 // diffFromValues takes an additional CustomizeDiffFunc, so we can generate our 26 // test fixtures from the legacy tests. In the new provider protocol the diff 27 // only needs to be created for the apply operation, and any customizations 28 // have already been done. 29 func diffFromValues(prior, planned cty.Value, res *Resource, cust CustomizeDiffFunc) (*terraform.InstanceDiff, error) { 30 instanceState, err := res.ShimInstanceStateFromValue(prior) 31 if err != nil { 32 return nil, err 33 } 34 35 configSchema := res.CoreConfigSchema() 36 37 cfg := terraform.NewResourceConfigShimmed(planned, configSchema) 38 removeConfigUnknowns(cfg.Config) 39 removeConfigUnknowns(cfg.Raw) 40 41 diff, err := schemaMap(res.Schema).Diff(instanceState, cfg, cust, nil, false) 42 if err != nil { 43 return nil, err 44 } 45 46 return diff, err 47 } 48 49 // During apply the only unknown values are those which are to be computed by 50 // the resource itself. These may have been marked as unknown config values, and 51 // need to be removed to prevent the UnknownVariableValue from appearing the diff. 52 func removeConfigUnknowns(cfg map[string]interface{}) { 53 for k, v := range cfg { 54 switch v := v.(type) { 55 case string: 56 if v == hcl2shim.UnknownVariableValue { 57 delete(cfg, k) 58 } 59 case []interface{}: 60 for _, i := range v { 61 if m, ok := i.(map[string]interface{}); ok { 62 removeConfigUnknowns(m) 63 } 64 } 65 case map[string]interface{}: 66 removeConfigUnknowns(v) 67 } 68 } 69 } 70 71 // ApplyDiff takes a cty.Value state and applies a terraform.InstanceDiff to 72 // get a new cty.Value state. This is used to convert the diff returned from 73 // the legacy provider Diff method to the state required for the new 74 // PlanResourceChange method. 75 func ApplyDiff(base cty.Value, d *terraform.InstanceDiff, schema *configschema.Block) (cty.Value, error) { 76 return d.ApplyToValue(base, schema) 77 } 78 79 // StateValueToJSONMap converts a cty.Value to generic JSON map via the cty JSON 80 // encoding. 81 func StateValueToJSONMap(val cty.Value, ty cty.Type) (map[string]interface{}, error) { 82 js, err := ctyjson.Marshal(val, ty) 83 if err != nil { 84 return nil, err 85 } 86 87 var m map[string]interface{} 88 if err := json.Unmarshal(js, &m); err != nil { 89 return nil, err 90 } 91 92 return m, nil 93 } 94 95 // JSONMapToStateValue takes a generic json map[string]interface{} and converts it 96 // to the specific type, ensuring that the values conform to the schema. 97 func JSONMapToStateValue(m map[string]interface{}, block *configschema.Block) (cty.Value, error) { 98 var val cty.Value 99 100 js, err := json.Marshal(m) 101 if err != nil { 102 return val, err 103 } 104 105 val, err = ctyjson.Unmarshal(js, block.ImpliedType()) 106 if err != nil { 107 return val, err 108 } 109 110 return block.CoerceValue(val) 111 } 112 113 // StateValueFromInstanceState converts a terraform.InstanceState to a 114 // cty.Value as described by the provided cty.Type, and maintains the resource 115 // ID as the "id" attribute. 116 func StateValueFromInstanceState(is *terraform.InstanceState, ty cty.Type) (cty.Value, error) { 117 return is.AttrsAsObjectValue(ty) 118 }