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