aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/plans
diff options
context:
space:
mode:
authorNathan Dench <ndenc2@gmail.com>2019-05-24 15:16:44 +1000
committerNathan Dench <ndenc2@gmail.com>2019-05-24 15:16:44 +1000
commit107c1cdb09c575aa2f61d97f48d8587eb6bada4c (patch)
treeca7d008643efc555c388baeaf1d986e0b6b3e28c /vendor/github.com/hashicorp/terraform/plans
parent844b5a68d8af4791755b8f0ad293cc99f5959183 (diff)
downloadterraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.gz
terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.zst
terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.zip
Upgrade to 0.12
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/plans')
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/action.go22
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/action_string.go49
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/changes.go308
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/changes_src.go190
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/changes_state.go15
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/changes_sync.go144
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/doc.go5
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/dynamic_value.go96
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/objchange/all_null.go18
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/objchange/compatible.go437
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/objchange/doc.go4
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/objchange/lcs.go104
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/objchange/normalize_obj.go132
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/objchange/objchange.go390
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/objchange/plan_valid.go267
-rw-r--r--vendor/github.com/hashicorp/terraform/plans/plan.go92
16 files changed, 2273 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/plans/action.go b/vendor/github.com/hashicorp/terraform/plans/action.go
new file mode 100644
index 0000000..c3e6a32
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/action.go
@@ -0,0 +1,22 @@
1package plans
2
3type Action rune
4
5const (
6 NoOp Action = 0
7 Create Action = '+'
8 Read Action = '←'
9 Update Action = '~'
10 DeleteThenCreate Action = '∓'
11 CreateThenDelete Action = '±'
12 Delete Action = '-'
13)
14
15//go:generate stringer -type Action
16
17// IsReplace returns true if the action is one of the two actions that
18// represents replacing an existing object with a new object:
19// DeleteThenCreate or CreateThenDelete.
20func (a Action) IsReplace() bool {
21 return a == DeleteThenCreate || a == CreateThenDelete
22}
diff --git a/vendor/github.com/hashicorp/terraform/plans/action_string.go b/vendor/github.com/hashicorp/terraform/plans/action_string.go
new file mode 100644
index 0000000..be43ab1
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/action_string.go
@@ -0,0 +1,49 @@
1// Code generated by "stringer -type Action"; DO NOT EDIT.
2
3package plans
4
5import "strconv"
6
7func _() {
8 // An "invalid array index" compiler error signifies that the constant values have changed.
9 // Re-run the stringer command to generate them again.
10 var x [1]struct{}
11 _ = x[NoOp-0]
12 _ = x[Create-43]
13 _ = x[Read-8592]
14 _ = x[Update-126]
15 _ = x[DeleteThenCreate-8723]
16 _ = x[CreateThenDelete-177]
17 _ = x[Delete-45]
18}
19
20const (
21 _Action_name_0 = "NoOp"
22 _Action_name_1 = "Create"
23 _Action_name_2 = "Delete"
24 _Action_name_3 = "Update"
25 _Action_name_4 = "CreateThenDelete"
26 _Action_name_5 = "Read"
27 _Action_name_6 = "DeleteThenCreate"
28)
29
30func (i Action) String() string {
31 switch {
32 case i == 0:
33 return _Action_name_0
34 case i == 43:
35 return _Action_name_1
36 case i == 45:
37 return _Action_name_2
38 case i == 126:
39 return _Action_name_3
40 case i == 177:
41 return _Action_name_4
42 case i == 8592:
43 return _Action_name_5
44 case i == 8723:
45 return _Action_name_6
46 default:
47 return "Action(" + strconv.FormatInt(int64(i), 10) + ")"
48 }
49}
diff --git a/vendor/github.com/hashicorp/terraform/plans/changes.go b/vendor/github.com/hashicorp/terraform/plans/changes.go
new file mode 100644
index 0000000..d7e0dcd
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/changes.go
@@ -0,0 +1,308 @@
1package plans
2
3import (
4 "github.com/hashicorp/terraform/addrs"
5 "github.com/hashicorp/terraform/states"
6 "github.com/zclconf/go-cty/cty"
7)
8
9// Changes describes various actions that Terraform will attempt to take if
10// the corresponding plan is applied.
11//
12// A Changes object can be rendered into a visual diff (by the caller, using
13// code in another package) for display to the user.
14type Changes struct {
15 // Resources tracks planned changes to resource instance objects.
16 Resources []*ResourceInstanceChangeSrc
17
18 // Outputs tracks planned changes output values.
19 //
20 // Note that although an in-memory plan contains planned changes for
21 // outputs throughout the configuration, a plan serialized
22 // to disk retains only the root outputs because they are
23 // externally-visible, while other outputs are implementation details and
24 // can be easily re-calculated during the apply phase. Therefore only root
25 // module outputs will survive a round-trip through a plan file.
26 Outputs []*OutputChangeSrc
27}
28
29// NewChanges returns a valid Changes object that describes no changes.
30func NewChanges() *Changes {
31 return &Changes{}
32}
33
34func (c *Changes) Empty() bool {
35 for _, res := range c.Resources {
36 if res.Action != NoOp {
37 return false
38 }
39 }
40 return true
41}
42
43// ResourceInstance returns the planned change for the current object of the
44// resource instance of the given address, if any. Returns nil if no change is
45// planned.
46func (c *Changes) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstanceChangeSrc {
47 addrStr := addr.String()
48 for _, rc := range c.Resources {
49 if rc.Addr.String() == addrStr && rc.DeposedKey == states.NotDeposed {
50 return rc
51 }
52 }
53
54 return nil
55}
56
57// ResourceInstanceDeposed returns the plan change of a deposed object of
58// the resource instance of the given address, if any. Returns nil if no change
59// is planned.
60func (c *Changes) ResourceInstanceDeposed(addr addrs.AbsResourceInstance, key states.DeposedKey) *ResourceInstanceChangeSrc {
61 addrStr := addr.String()
62 for _, rc := range c.Resources {
63 if rc.Addr.String() == addrStr && rc.DeposedKey == key {
64 return rc
65 }
66 }
67
68 return nil
69}
70
71// OutputValue returns the planned change for the output value with the
72// given address, if any. Returns nil if no change is planned.
73func (c *Changes) OutputValue(addr addrs.AbsOutputValue) *OutputChangeSrc {
74 addrStr := addr.String()
75 for _, oc := range c.Outputs {
76 if oc.Addr.String() == addrStr {
77 return oc
78 }
79 }
80
81 return nil
82}
83
84// SyncWrapper returns a wrapper object around the receiver that can be used
85// to make certain changes to the receiver in a concurrency-safe way, as long
86// as all callers share the same wrapper object.
87func (c *Changes) SyncWrapper() *ChangesSync {
88 return &ChangesSync{
89 changes: c,
90 }
91}
92
93// ResourceInstanceChange describes a change to a particular resource instance
94// object.
95type ResourceInstanceChange struct {
96 // Addr is the absolute address of the resource instance that the change
97 // will apply to.
98 Addr addrs.AbsResourceInstance
99
100 // DeposedKey is the identifier for a deposed object associated with the
101 // given instance, or states.NotDeposed if this change applies to the
102 // current object.
103 //
104 // A Replace change for a resource with create_before_destroy set will
105 // create a new DeposedKey temporarily during replacement. In that case,
106 // DeposedKey in the plan is always states.NotDeposed, representing that
107 // the current object is being replaced with the deposed.
108 DeposedKey states.DeposedKey
109
110 // Provider is the address of the provider configuration that was used
111 // to plan this change, and thus the configuration that must also be
112 // used to apply it.
113 ProviderAddr addrs.AbsProviderConfig
114
115 // Change is an embedded description of the change.
116 Change
117
118 // RequiredReplace is a set of paths that caused the change action to be
119 // Replace rather than Update. Always nil if the change action is not
120 // Replace.
121 //
122 // This is retained only for UI-plan-rendering purposes and so it does not
123 // currently survive a round-trip through a saved plan file.
124 RequiredReplace cty.PathSet
125
126 // Private allows a provider to stash any extra data that is opaque to
127 // Terraform that relates to this change. Terraform will save this
128 // byte-for-byte and return it to the provider in the apply call.
129 Private []byte
130}
131
132// Encode produces a variant of the reciever that has its change values
133// serialized so it can be written to a plan file. Pass the implied type of the
134// corresponding resource type schema for correct operation.
135func (rc *ResourceInstanceChange) Encode(ty cty.Type) (*ResourceInstanceChangeSrc, error) {
136 cs, err := rc.Change.Encode(ty)
137 if err != nil {
138 return nil, err
139 }
140 return &ResourceInstanceChangeSrc{
141 Addr: rc.Addr,
142 DeposedKey: rc.DeposedKey,
143 ProviderAddr: rc.ProviderAddr,
144 ChangeSrc: *cs,
145 RequiredReplace: rc.RequiredReplace,
146 Private: rc.Private,
147 }, err
148}
149
150// Simplify will, where possible, produce a change with a simpler action than
151// the receiever given a flag indicating whether the caller is dealing with
152// a normal apply or a destroy. This flag deals with the fact that Terraform
153// Core uses a specialized graph node type for destroying; only that
154// specialized node should set "destroying" to true.
155//
156// The following table shows the simplification behavior:
157//
158// Action Destroying? New Action
159// --------+-------------+-----------
160// Create true NoOp
161// Delete false NoOp
162// Replace true Delete
163// Replace false Create
164//
165// For any combination not in the above table, the Simplify just returns the
166// receiver as-is.
167func (rc *ResourceInstanceChange) Simplify(destroying bool) *ResourceInstanceChange {
168 if destroying {
169 switch rc.Action {
170 case Delete:
171 // We'll fall out and just return rc verbatim, then.
172 case CreateThenDelete, DeleteThenCreate:
173 return &ResourceInstanceChange{
174 Addr: rc.Addr,
175 DeposedKey: rc.DeposedKey,
176 Private: rc.Private,
177 ProviderAddr: rc.ProviderAddr,
178 Change: Change{
179 Action: Delete,
180 Before: rc.Before,
181 After: cty.NullVal(rc.Before.Type()),
182 },
183 }
184 default:
185 return &ResourceInstanceChange{
186 Addr: rc.Addr,
187 DeposedKey: rc.DeposedKey,
188 Private: rc.Private,
189 ProviderAddr: rc.ProviderAddr,
190 Change: Change{
191 Action: NoOp,
192 Before: rc.Before,
193 After: rc.Before,
194 },
195 }
196 }
197 } else {
198 switch rc.Action {
199 case Delete:
200 return &ResourceInstanceChange{
201 Addr: rc.Addr,
202 DeposedKey: rc.DeposedKey,
203 Private: rc.Private,
204 ProviderAddr: rc.ProviderAddr,
205 Change: Change{
206 Action: NoOp,
207 Before: rc.Before,
208 After: rc.Before,
209 },
210 }
211 case CreateThenDelete, DeleteThenCreate:
212 return &ResourceInstanceChange{
213 Addr: rc.Addr,
214 DeposedKey: rc.DeposedKey,
215 Private: rc.Private,
216 ProviderAddr: rc.ProviderAddr,
217 Change: Change{
218 Action: Create,
219 Before: cty.NullVal(rc.After.Type()),
220 After: rc.After,
221 },
222 }
223 }
224 }
225
226 // If we fall out here then our change is already simple enough.
227 return rc
228}
229
230// OutputChange describes a change to an output value.
231type OutputChange struct {
232 // Addr is the absolute address of the output value that the change
233 // will apply to.
234 Addr addrs.AbsOutputValue
235
236 // Change is an embedded description of the change.
237 //
238 // For output value changes, the type constraint for the DynamicValue
239 // instances is always cty.DynamicPseudoType.
240 Change
241
242 // Sensitive, if true, indicates that either the old or new value in the
243 // change is sensitive and so a rendered version of the plan in the UI
244 // should elide the actual values while still indicating the action of the
245 // change.
246 Sensitive bool
247}
248
249// Encode produces a variant of the reciever that has its change values
250// serialized so it can be written to a plan file.
251func (oc *OutputChange) Encode() (*OutputChangeSrc, error) {
252 cs, err := oc.Change.Encode(cty.DynamicPseudoType)
253 if err != nil {
254 return nil, err
255 }
256 return &OutputChangeSrc{
257 Addr: oc.Addr,
258 ChangeSrc: *cs,
259 Sensitive: oc.Sensitive,
260 }, err
261}
262
263// Change describes a single change with a given action.
264type Change struct {
265 // Action defines what kind of change is being made.
266 Action Action
267
268 // Interpretation of Before and After depend on Action:
269 //
270 // NoOp Before and After are the same, unchanged value
271 // Create Before is nil, and After is the expected value after create.
272 // Read Before is any prior value (nil if no prior), and After is the
273 // value that was or will be read.
274 // Update Before is the value prior to update, and After is the expected
275 // value after update.
276 // Replace As with Update.
277 // Delete Before is the value prior to delete, and After is always nil.
278 //
279 // Unknown values may appear anywhere within the Before and After values,
280 // either as the values themselves or as nested elements within known
281 // collections/structures.
282 Before, After cty.Value
283}
284
285// Encode produces a variant of the reciever that has its change values
286// serialized so it can be written to a plan file. Pass the type constraint
287// that the values are expected to conform to; to properly decode the values
288// later an identical type constraint must be provided at that time.
289//
290// Where a Change is embedded in some other struct, it's generally better
291// to call the corresponding Encode method of that struct rather than working
292// directly with its embedded Change.
293func (c *Change) Encode(ty cty.Type) (*ChangeSrc, error) {
294 beforeDV, err := NewDynamicValue(c.Before, ty)
295 if err != nil {
296 return nil, err
297 }
298 afterDV, err := NewDynamicValue(c.After, ty)
299 if err != nil {
300 return nil, err
301 }
302
303 return &ChangeSrc{
304 Action: c.Action,
305 Before: beforeDV,
306 After: afterDV,
307 }, nil
308}
diff --git a/vendor/github.com/hashicorp/terraform/plans/changes_src.go b/vendor/github.com/hashicorp/terraform/plans/changes_src.go
new file mode 100644
index 0000000..90153ea
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/changes_src.go
@@ -0,0 +1,190 @@
1package plans
2
3import (
4 "fmt"
5
6 "github.com/hashicorp/terraform/addrs"
7 "github.com/hashicorp/terraform/states"
8 "github.com/zclconf/go-cty/cty"
9)
10
11// ResourceInstanceChangeSrc is a not-yet-decoded ResourceInstanceChange.
12// Pass the associated resource type's schema type to method Decode to
13// obtain a ResourceInstancChange.
14type ResourceInstanceChangeSrc struct {
15 // Addr is the absolute address of the resource instance that the change
16 // will apply to.
17 Addr addrs.AbsResourceInstance
18
19 // DeposedKey is the identifier for a deposed object associated with the
20 // given instance, or states.NotDeposed if this change applies to the
21 // current object.
22 //
23 // A Replace change for a resource with create_before_destroy set will
24 // create a new DeposedKey temporarily during replacement. In that case,
25 // DeposedKey in the plan is always states.NotDeposed, representing that
26 // the current object is being replaced with the deposed.
27 DeposedKey states.DeposedKey
28
29 // Provider is the address of the provider configuration that was used
30 // to plan this change, and thus the configuration that must also be
31 // used to apply it.
32 ProviderAddr addrs.AbsProviderConfig
33
34 // ChangeSrc is an embedded description of the not-yet-decoded change.
35 ChangeSrc
36
37 // RequiredReplace is a set of paths that caused the change action to be
38 // Replace rather than Update. Always nil if the change action is not
39 // Replace.
40 //
41 // This is retained only for UI-plan-rendering purposes and so it does not
42 // currently survive a round-trip through a saved plan file.
43 RequiredReplace cty.PathSet
44
45 // Private allows a provider to stash any extra data that is opaque to
46 // Terraform that relates to this change. Terraform will save this
47 // byte-for-byte and return it to the provider in the apply call.
48 Private []byte
49}
50
51// Decode unmarshals the raw representation of the instance object being
52// changed. Pass the implied type of the corresponding resource type schema
53// for correct operation.
54func (rcs *ResourceInstanceChangeSrc) Decode(ty cty.Type) (*ResourceInstanceChange, error) {
55 change, err := rcs.ChangeSrc.Decode(ty)
56 if err != nil {
57 return nil, err
58 }
59 return &ResourceInstanceChange{
60 Addr: rcs.Addr,
61 DeposedKey: rcs.DeposedKey,
62 ProviderAddr: rcs.ProviderAddr,
63 Change: *change,
64 RequiredReplace: rcs.RequiredReplace,
65 Private: rcs.Private,
66 }, nil
67}
68
69// DeepCopy creates a copy of the receiver where any pointers to nested mutable
70// values are also copied, thus ensuring that future mutations of the receiver
71// will not affect the copy.
72//
73// Some types used within a resource change are immutable by convention even
74// though the Go language allows them to be mutated, such as the types from
75// the addrs package. These are _not_ copied by this method, under the
76// assumption that callers will behave themselves.
77func (rcs *ResourceInstanceChangeSrc) DeepCopy() *ResourceInstanceChangeSrc {
78 if rcs == nil {
79 return nil
80 }
81 ret := *rcs
82
83 ret.RequiredReplace = cty.NewPathSet(ret.RequiredReplace.List()...)
84
85 if len(ret.Private) != 0 {
86 private := make([]byte, len(ret.Private))
87 copy(private, ret.Private)
88 ret.Private = private
89 }
90
91 ret.ChangeSrc.Before = ret.ChangeSrc.Before.Copy()
92 ret.ChangeSrc.After = ret.ChangeSrc.After.Copy()
93
94 return &ret
95}
96
97// OutputChangeSrc describes a change to an output value.
98type OutputChangeSrc struct {
99 // Addr is the absolute address of the output value that the change
100 // will apply to.
101 Addr addrs.AbsOutputValue
102
103 // ChangeSrc is an embedded description of the not-yet-decoded change.
104 //
105 // For output value changes, the type constraint for the DynamicValue
106 // instances is always cty.DynamicPseudoType.
107 ChangeSrc
108
109 // Sensitive, if true, indicates that either the old or new value in the
110 // change is sensitive and so a rendered version of the plan in the UI
111 // should elide the actual values while still indicating the action of the
112 // change.
113 Sensitive bool
114}
115
116// Decode unmarshals the raw representation of the output value being
117// changed.
118func (ocs *OutputChangeSrc) Decode() (*OutputChange, error) {
119 change, err := ocs.ChangeSrc.Decode(cty.DynamicPseudoType)
120 if err != nil {
121 return nil, err
122 }
123 return &OutputChange{
124 Addr: ocs.Addr,
125 Change: *change,
126 Sensitive: ocs.Sensitive,
127 }, nil
128}
129
130// DeepCopy creates a copy of the receiver where any pointers to nested mutable
131// values are also copied, thus ensuring that future mutations of the receiver
132// will not affect the copy.
133//
134// Some types used within a resource change are immutable by convention even
135// though the Go language allows them to be mutated, such as the types from
136// the addrs package. These are _not_ copied by this method, under the
137// assumption that callers will behave themselves.
138func (ocs *OutputChangeSrc) DeepCopy() *OutputChangeSrc {
139 if ocs == nil {
140 return nil
141 }
142 ret := *ocs
143
144 ret.ChangeSrc.Before = ret.ChangeSrc.Before.Copy()
145 ret.ChangeSrc.After = ret.ChangeSrc.After.Copy()
146
147 return &ret
148}
149
150// ChangeSrc is a not-yet-decoded Change.
151type ChangeSrc struct {
152 // Action defines what kind of change is being made.
153 Action Action
154
155 // Before and After correspond to the fields of the same name in Change,
156 // but have not yet been decoded from the serialized value used for
157 // storage.
158 Before, After DynamicValue
159}
160
161// Decode unmarshals the raw representations of the before and after values
162// to produce a Change object. Pass the type constraint that the result must
163// conform to.
164//
165// Where a ChangeSrc is embedded in some other struct, it's generally better
166// to call the corresponding Decode method of that struct rather than working
167// directly with its embedded Change.
168func (cs *ChangeSrc) Decode(ty cty.Type) (*Change, error) {
169 var err error
170 before := cty.NullVal(ty)
171 after := cty.NullVal(ty)
172
173 if len(cs.Before) > 0 {
174 before, err = cs.Before.Decode(ty)
175 if err != nil {
176 return nil, fmt.Errorf("error decoding 'before' value: %s", err)
177 }
178 }
179 if len(cs.After) > 0 {
180 after, err = cs.After.Decode(ty)
181 if err != nil {
182 return nil, fmt.Errorf("error decoding 'after' value: %s", err)
183 }
184 }
185 return &Change{
186 Action: cs.Action,
187 Before: before,
188 After: after,
189 }, nil
190}
diff --git a/vendor/github.com/hashicorp/terraform/plans/changes_state.go b/vendor/github.com/hashicorp/terraform/plans/changes_state.go
new file mode 100644
index 0000000..543e6c2
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/changes_state.go
@@ -0,0 +1,15 @@
1package plans
2
3import (
4 "github.com/hashicorp/terraform/states"
5)
6
7// PlannedState merges the set of changes described by the receiver into the
8// given prior state to produce the planned result state.
9//
10// The result is an approximation of the state as it would exist after
11// applying these changes, omitting any values that cannot be determined until
12// the changes are actually applied.
13func (c *Changes) PlannedState(prior *states.State) (*states.State, error) {
14 panic("Changes.PlannedState not yet implemented")
15}
diff --git a/vendor/github.com/hashicorp/terraform/plans/changes_sync.go b/vendor/github.com/hashicorp/terraform/plans/changes_sync.go
new file mode 100644
index 0000000..6b4ff98
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/changes_sync.go
@@ -0,0 +1,144 @@
1package plans
2
3import (
4 "fmt"
5 "sync"
6
7 "github.com/hashicorp/terraform/addrs"
8 "github.com/hashicorp/terraform/states"
9)
10
11// ChangesSync is a wrapper around a Changes that provides a concurrency-safe
12// interface to insert new changes and retrieve copies of existing changes.
13//
14// Each ChangesSync is independent of all others, so all concurrent writers
15// to a particular Changes must share a single ChangesSync. Behavior is
16// undefined if any other caller makes changes to the underlying Changes
17// object or its nested objects concurrently with any of the methods of a
18// particular ChangesSync.
19type ChangesSync struct {
20 lock sync.Mutex
21 changes *Changes
22}
23
24// AppendResourceInstanceChange records the given resource instance change in
25// the set of planned resource changes.
26//
27// The caller must ensure that there are no concurrent writes to the given
28// change while this method is running, but it is safe to resume mutating
29// it after this method returns without affecting the saved change.
30func (cs *ChangesSync) AppendResourceInstanceChange(changeSrc *ResourceInstanceChangeSrc) {
31 if cs == nil {
32 panic("AppendResourceInstanceChange on nil ChangesSync")
33 }
34 cs.lock.Lock()
35 defer cs.lock.Unlock()
36
37 s := changeSrc.DeepCopy()
38 cs.changes.Resources = append(cs.changes.Resources, s)
39}
40
41// GetResourceInstanceChange searches the set of resource instance changes for
42// one matching the given address and generation, returning it if it exists.
43//
44// If no such change exists, nil is returned.
45//
46// The returned object is a deep copy of the change recorded in the plan, so
47// callers may mutate it although it's generally better (less confusing) to
48// treat planned changes as immutable after they've been initially constructed.
49func (cs *ChangesSync) GetResourceInstanceChange(addr addrs.AbsResourceInstance, gen states.Generation) *ResourceInstanceChangeSrc {
50 if cs == nil {
51 panic("GetResourceInstanceChange on nil ChangesSync")
52 }
53 cs.lock.Lock()
54 defer cs.lock.Unlock()
55
56 if gen == states.CurrentGen {
57 return cs.changes.ResourceInstance(addr).DeepCopy()
58 }
59 if dk, ok := gen.(states.DeposedKey); ok {
60 return cs.changes.ResourceInstanceDeposed(addr, dk).DeepCopy()
61 }
62 panic(fmt.Sprintf("unsupported generation value %#v", gen))
63}
64
65// RemoveResourceInstanceChange searches the set of resource instance changes
66// for one matching the given address and generation, and removes it from the
67// set if it exists.
68func (cs *ChangesSync) RemoveResourceInstanceChange(addr addrs.AbsResourceInstance, gen states.Generation) {
69 if cs == nil {
70 panic("RemoveResourceInstanceChange on nil ChangesSync")
71 }
72 cs.lock.Lock()
73 defer cs.lock.Unlock()
74
75 dk := states.NotDeposed
76 if realDK, ok := gen.(states.DeposedKey); ok {
77 dk = realDK
78 }
79
80 addrStr := addr.String()
81 for i, r := range cs.changes.Resources {
82 if r.Addr.String() != addrStr || r.DeposedKey != dk {
83 continue
84 }
85 copy(cs.changes.Resources[i:], cs.changes.Resources[i+1:])
86 cs.changes.Resources = cs.changes.Resources[:len(cs.changes.Resources)-1]
87 return
88 }
89}
90
91// AppendOutputChange records the given output value change in the set of
92// planned value changes.
93//
94// The caller must ensure that there are no concurrent writes to the given
95// change while this method is running, but it is safe to resume mutating
96// it after this method returns without affecting the saved change.
97func (cs *ChangesSync) AppendOutputChange(changeSrc *OutputChangeSrc) {
98 if cs == nil {
99 panic("AppendOutputChange on nil ChangesSync")
100 }
101 cs.lock.Lock()
102 defer cs.lock.Unlock()
103
104 s := changeSrc.DeepCopy()
105 cs.changes.Outputs = append(cs.changes.Outputs, s)
106}
107
108// GetOutputChange searches the set of output value changes for one matching
109// the given address, returning it if it exists.
110//
111// If no such change exists, nil is returned.
112//
113// The returned object is a deep copy of the change recorded in the plan, so
114// callers may mutate it although it's generally better (less confusing) to
115// treat planned changes as immutable after they've been initially constructed.
116func (cs *ChangesSync) GetOutputChange(addr addrs.AbsOutputValue) *OutputChangeSrc {
117 if cs == nil {
118 panic("GetOutputChange on nil ChangesSync")
119 }
120 cs.lock.Lock()
121 defer cs.lock.Unlock()
122
123 return cs.changes.OutputValue(addr)
124}
125
126// RemoveOutputChange searches the set of output value changes for one matching
127// the given address, and removes it from the set if it exists.
128func (cs *ChangesSync) RemoveOutputChange(addr addrs.AbsOutputValue) {
129 if cs == nil {
130 panic("RemoveOutputChange on nil ChangesSync")
131 }
132 cs.lock.Lock()
133 defer cs.lock.Unlock()
134
135 addrStr := addr.String()
136 for i, o := range cs.changes.Outputs {
137 if o.Addr.String() != addrStr {
138 continue
139 }
140 copy(cs.changes.Outputs[i:], cs.changes.Outputs[i+1:])
141 cs.changes.Outputs = cs.changes.Outputs[:len(cs.changes.Outputs)-1]
142 return
143 }
144}
diff --git a/vendor/github.com/hashicorp/terraform/plans/doc.go b/vendor/github.com/hashicorp/terraform/plans/doc.go
new file mode 100644
index 0000000..01ca389
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/doc.go
@@ -0,0 +1,5 @@
1// Package plans contains the types that are used to represent Terraform plans.
2//
3// A plan describes a set of changes that Terraform will make to update remote
4// objects to match with changes to the configuration.
5package plans
diff --git a/vendor/github.com/hashicorp/terraform/plans/dynamic_value.go b/vendor/github.com/hashicorp/terraform/plans/dynamic_value.go
new file mode 100644
index 0000000..51fbb24
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/dynamic_value.go
@@ -0,0 +1,96 @@
1package plans
2
3import (
4 "github.com/zclconf/go-cty/cty"
5 ctymsgpack "github.com/zclconf/go-cty/cty/msgpack"
6)
7
8// DynamicValue is the representation in the plan of a value whose type cannot
9// be determined at compile time, such as because it comes from a schema
10// defined in a plugin.
11//
12// This type is used as an indirection so that the overall plan structure can
13// be decoded without schema available, and then the dynamic values accessed
14// at a later time once the appropriate schema has been determined.
15//
16// Internally, DynamicValue is a serialized version of a cty.Value created
17// against a particular type constraint. Callers should not access directly
18// the serialized form, whose format may change in future. Values of this
19// type must always be created by calling NewDynamicValue.
20//
21// The zero value of DynamicValue is nil, and represents the absense of a
22// value within the Go type system. This is distinct from a cty.NullVal
23// result, which represents the absense of a value within the cty type system.
24type DynamicValue []byte
25
26// NewDynamicValue creates a DynamicValue by serializing the given value
27// against the given type constraint. The value must conform to the type
28// constraint, or the result is undefined.
29//
30// If the value to be encoded has no predefined schema (for example, for
31// module output values and input variables), set the type constraint to
32// cty.DynamicPseudoType in order to save type information as part of the
33// value, and then also pass cty.DynamicPseudoType to method Decode to recover
34// the original value.
35//
36// cty.NilVal can be used to represent the absense of a value, but callers
37// must be careful to distinguish values that are absent at the Go layer
38// (cty.NilVal) vs. values that are absent at the cty layer (cty.NullVal
39// results).
40func NewDynamicValue(val cty.Value, ty cty.Type) (DynamicValue, error) {
41 // If we're given cty.NilVal (the zero value of cty.Value, which is
42 // distinct from a typed null value created by cty.NullVal) then we'll
43 // assume the caller is trying to represent the _absense_ of a value,
44 // and so we'll return a nil DynamicValue.
45 if val == cty.NilVal {
46 return DynamicValue(nil), nil
47 }
48
49 // Currently our internal encoding is msgpack, via ctymsgpack.
50 buf, err := ctymsgpack.Marshal(val, ty)
51 if err != nil {
52 return nil, err
53 }
54
55 return DynamicValue(buf), nil
56}
57
58// Decode retrieves the effective value from the receiever by interpreting the
59// serialized form against the given type constraint. For correct results,
60// the type constraint must match (or be consistent with) the one that was
61// used to create the receiver.
62//
63// A nil DynamicValue decodes to cty.NilVal, which is not a valid value and
64// instead represents the absense of a value.
65func (v DynamicValue) Decode(ty cty.Type) (cty.Value, error) {
66 if v == nil {
67 return cty.NilVal, nil
68 }
69
70 return ctymsgpack.Unmarshal([]byte(v), ty)
71}
72
73// ImpliedType returns the type implied by the serialized structure of the
74// receiving value.
75//
76// This will not necessarily be exactly the type that was given when the
77// value was encoded, and in particular must not be used for values that
78// were encoded with their static type given as cty.DynamicPseudoType.
79// It is however safe to use this method for values that were encoded using
80// their runtime type as the conforming type, with the result being
81// semantically equivalent but with all lists and sets represented as tuples,
82// and maps as objects, due to ambiguities of the serialization.
83func (v DynamicValue) ImpliedType() (cty.Type, error) {
84 return ctymsgpack.ImpliedType([]byte(v))
85}
86
87// Copy produces a copy of the receiver with a distinct backing array.
88func (v DynamicValue) Copy() DynamicValue {
89 if v == nil {
90 return nil
91 }
92
93 ret := make(DynamicValue, len(v))
94 copy(ret, v)
95 return ret
96}
diff --git a/vendor/github.com/hashicorp/terraform/plans/objchange/all_null.go b/vendor/github.com/hashicorp/terraform/plans/objchange/all_null.go
new file mode 100644
index 0000000..18a7e99
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/objchange/all_null.go
@@ -0,0 +1,18 @@
1package objchange
2
3import (
4 "github.com/hashicorp/terraform/configs/configschema"
5 "github.com/zclconf/go-cty/cty"
6)
7
8// AllAttributesNull constructs a non-null cty.Value of the object type implied
9// by the given schema that has all of its leaf attributes set to null and all
10// of its nested block collections set to zero-length.
11//
12// This simulates what would result from decoding an empty configuration block
13// with the given schema, except that it does not produce errors
14func AllAttributesNull(schema *configschema.Block) cty.Value {
15 // "All attributes null" happens to be the definition of EmptyValue for
16 // a Block, so we can just delegate to that.
17 return schema.EmptyValue()
18}
diff --git a/vendor/github.com/hashicorp/terraform/plans/objchange/compatible.go b/vendor/github.com/hashicorp/terraform/plans/objchange/compatible.go
new file mode 100644
index 0000000..8b7ef43
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/objchange/compatible.go
@@ -0,0 +1,437 @@
1package objchange
2
3import (
4 "fmt"
5 "strconv"
6
7 "github.com/zclconf/go-cty/cty"
8 "github.com/zclconf/go-cty/cty/convert"
9
10 "github.com/hashicorp/terraform/configs/configschema"
11)
12
13// AssertObjectCompatible checks whether the given "actual" value is a valid
14// completion of the possibly-partially-unknown "planned" value.
15//
16// This means that any known leaf value in "planned" must be equal to the
17// corresponding value in "actual", and various other similar constraints.
18//
19// Any inconsistencies are reported by returning a non-zero number of errors.
20// These errors are usually (but not necessarily) cty.PathError values
21// referring to a particular nested value within the "actual" value.
22//
23// The two values must have types that conform to the given schema's implied
24// type, or this function will panic.
25func AssertObjectCompatible(schema *configschema.Block, planned, actual cty.Value) []error {
26 return assertObjectCompatible(schema, planned, actual, nil)
27}
28
29func assertObjectCompatible(schema *configschema.Block, planned, actual cty.Value, path cty.Path) []error {
30 var errs []error
31 if planned.IsNull() && !actual.IsNull() {
32 errs = append(errs, path.NewErrorf("was absent, but now present"))
33 return errs
34 }
35 if actual.IsNull() && !planned.IsNull() {
36 errs = append(errs, path.NewErrorf("was present, but now absent"))
37 return errs
38 }
39 if planned.IsNull() {
40 // No further checks possible if both values are null
41 return errs
42 }
43
44 for name, attrS := range schema.Attributes {
45 plannedV := planned.GetAttr(name)
46 actualV := actual.GetAttr(name)
47
48 path := append(path, cty.GetAttrStep{Name: name})
49 moreErrs := assertValueCompatible(plannedV, actualV, path)
50 if attrS.Sensitive {
51 if len(moreErrs) > 0 {
52 // Use a vague placeholder message instead, to avoid disclosing
53 // sensitive information.
54 errs = append(errs, path.NewErrorf("inconsistent values for sensitive attribute"))
55 }
56 } else {
57 errs = append(errs, moreErrs...)
58 }
59 }
60 for name, blockS := range schema.BlockTypes {
61 plannedV := planned.GetAttr(name)
62 actualV := actual.GetAttr(name)
63
64 // As a special case, if there were any blocks whose leaf attributes
65 // are all unknown then we assume (possibly incorrectly) that the
66 // HCL dynamic block extension is in use with an unknown for_each
67 // argument, and so we will do looser validation here that allows
68 // for those blocks to have expanded into a different number of blocks
69 // if the for_each value is now known.
70 maybeUnknownBlocks := couldHaveUnknownBlockPlaceholder(plannedV, blockS, false)
71
72 path := append(path, cty.GetAttrStep{Name: name})
73 switch blockS.Nesting {
74 case configschema.NestingSingle, configschema.NestingGroup:
75 // If an unknown block placeholder was present then the placeholder
76 // may have expanded out into zero blocks, which is okay.
77 if maybeUnknownBlocks && actualV.IsNull() {
78 continue
79 }
80 moreErrs := assertObjectCompatible(&blockS.Block, plannedV, actualV, path)
81 errs = append(errs, moreErrs...)
82 case configschema.NestingList:
83 // A NestingList might either be a list or a tuple, depending on
84 // whether there are dynamically-typed attributes inside. However,
85 // both support a similar-enough API that we can treat them the
86 // same for our purposes here.
87 if !plannedV.IsKnown() || plannedV.IsNull() || actualV.IsNull() {
88 continue
89 }
90
91 if maybeUnknownBlocks {
92 // When unknown blocks are present the final blocks may be
93 // at different indices than the planned blocks, so unfortunately
94 // we can't do our usual checks in this case without generating
95 // false negatives.
96 continue
97 }
98
99 plannedL := plannedV.LengthInt()
100 actualL := actualV.LengthInt()
101 if plannedL != actualL {
102 errs = append(errs, path.NewErrorf("block count changed from %d to %d", plannedL, actualL))
103 continue
104 }
105 for it := plannedV.ElementIterator(); it.Next(); {
106 idx, plannedEV := it.Element()
107 if !actualV.HasIndex(idx).True() {
108 continue
109 }
110 actualEV := actualV.Index(idx)
111 moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.IndexStep{Key: idx}))
112 errs = append(errs, moreErrs...)
113 }
114 case configschema.NestingMap:
115 // A NestingMap might either be a map or an object, depending on
116 // whether there are dynamically-typed attributes inside, but
117 // that's decided statically and so both values will have the same
118 // kind.
119 if plannedV.Type().IsObjectType() {
120 plannedAtys := plannedV.Type().AttributeTypes()
121 actualAtys := actualV.Type().AttributeTypes()
122 for k := range plannedAtys {
123 if _, ok := actualAtys[k]; !ok {
124 errs = append(errs, path.NewErrorf("block key %q has vanished", k))
125 continue
126 }
127
128 plannedEV := plannedV.GetAttr(k)
129 actualEV := actualV.GetAttr(k)
130 moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.GetAttrStep{Name: k}))
131 errs = append(errs, moreErrs...)
132 }
133 if !maybeUnknownBlocks { // new blocks may appear if unknown blocks were present in the plan
134 for k := range actualAtys {
135 if _, ok := plannedAtys[k]; !ok {
136 errs = append(errs, path.NewErrorf("new block key %q has appeared", k))
137 continue
138 }
139 }
140 }
141 } else {
142 if !plannedV.IsKnown() || plannedV.IsNull() || actualV.IsNull() {
143 continue
144 }
145 plannedL := plannedV.LengthInt()
146 actualL := actualV.LengthInt()
147 if plannedL != actualL && !maybeUnknownBlocks { // new blocks may appear if unknown blocks were persent in the plan
148 errs = append(errs, path.NewErrorf("block count changed from %d to %d", plannedL, actualL))
149 continue
150 }
151 for it := plannedV.ElementIterator(); it.Next(); {
152 idx, plannedEV := it.Element()
153 if !actualV.HasIndex(idx).True() {
154 continue
155 }
156 actualEV := actualV.Index(idx)
157 moreErrs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.IndexStep{Key: idx}))
158 errs = append(errs, moreErrs...)
159 }
160 }
161 case configschema.NestingSet:
162 if !plannedV.IsKnown() || !actualV.IsKnown() || plannedV.IsNull() || actualV.IsNull() {
163 continue
164 }
165
166 setErrs := assertSetValuesCompatible(plannedV, actualV, path, func(plannedEV, actualEV cty.Value) bool {
167 errs := assertObjectCompatible(&blockS.Block, plannedEV, actualEV, append(path, cty.IndexStep{Key: actualEV}))
168 return len(errs) == 0
169 })
170 errs = append(errs, setErrs...)
171
172 // There can be fewer elements in a set after its elements are all
173 // known (values that turn out to be equal will coalesce) but the
174 // number of elements must never get larger.
175 plannedL := plannedV.LengthInt()
176 actualL := actualV.LengthInt()
177 if plannedL < actualL {
178 errs = append(errs, path.NewErrorf("block set length changed from %d to %d", plannedL, actualL))
179 }
180 default:
181 panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting))
182 }
183 }
184 return errs
185}
186
187func assertValueCompatible(planned, actual cty.Value, path cty.Path) []error {
188 // NOTE: We don't normally use the GoString rendering of cty.Value in
189 // user-facing error messages as a rule, but we make an exception
190 // for this function because we expect the user to pass this message on
191 // verbatim to the provider development team and so more detail is better.
192
193 var errs []error
194 if planned.Type() == cty.DynamicPseudoType {
195 // Anything goes, then
196 return errs
197 }
198 if problems := planned.Type().TestConformance(actual.Type()); len(problems) > 0 {
199 errs = append(errs, path.NewErrorf("wrong final value type: %s", convert.MismatchMessage(actual.Type(), planned.Type())))
200 // If the types don't match then we can't do any other comparisons,
201 // so we bail early.
202 return errs
203 }
204
205 if !planned.IsKnown() {
206 // We didn't know what were going to end up with during plan, so
207 // anything goes during apply.
208 return errs
209 }
210
211 if actual.IsNull() {
212 if planned.IsNull() {
213 return nil
214 }
215 errs = append(errs, path.NewErrorf("was %#v, but now null", planned))
216 return errs
217 }
218 if planned.IsNull() {
219 errs = append(errs, path.NewErrorf("was null, but now %#v", actual))
220 return errs
221 }
222
223 ty := planned.Type()
224 switch {
225
226 case !actual.IsKnown():
227 errs = append(errs, path.NewErrorf("was known, but now unknown"))
228
229 case ty.IsPrimitiveType():
230 if !actual.Equals(planned).True() {
231 errs = append(errs, path.NewErrorf("was %#v, but now %#v", planned, actual))
232 }
233
234 case ty.IsListType() || ty.IsMapType() || ty.IsTupleType():
235 for it := planned.ElementIterator(); it.Next(); {
236 k, plannedV := it.Element()
237 if !actual.HasIndex(k).True() {
238 errs = append(errs, path.NewErrorf("element %s has vanished", indexStrForErrors(k)))
239 continue
240 }
241
242 actualV := actual.Index(k)
243 moreErrs := assertValueCompatible(plannedV, actualV, append(path, cty.IndexStep{Key: k}))
244 errs = append(errs, moreErrs...)
245 }
246
247 for it := actual.ElementIterator(); it.Next(); {
248 k, _ := it.Element()
249 if !planned.HasIndex(k).True() {
250 errs = append(errs, path.NewErrorf("new element %s has appeared", indexStrForErrors(k)))
251 }
252 }
253
254 case ty.IsObjectType():
255 atys := ty.AttributeTypes()
256 for name := range atys {
257 // Because we already tested that the two values have the same type,
258 // we can assume that the same attributes are present in both and
259 // focus just on testing their values.
260 plannedV := planned.GetAttr(name)
261 actualV := actual.GetAttr(name)
262 moreErrs := assertValueCompatible(plannedV, actualV, append(path, cty.GetAttrStep{Name: name}))
263 errs = append(errs, moreErrs...)
264 }
265
266 case ty.IsSetType():
267 // We can't really do anything useful for sets here because changing
268 // an unknown element to known changes the identity of the element, and
269 // so we can't correlate them properly. However, we will at least check
270 // to ensure that the number of elements is consistent, along with
271 // the general type-match checks we ran earlier in this function.
272 if planned.IsKnown() && !planned.IsNull() && !actual.IsNull() {
273
274 setErrs := assertSetValuesCompatible(planned, actual, path, func(plannedV, actualV cty.Value) bool {
275 errs := assertValueCompatible(plannedV, actualV, append(path, cty.IndexStep{Key: actualV}))
276 return len(errs) == 0
277 })
278 errs = append(errs, setErrs...)
279
280 // There can be fewer elements in a set after its elements are all
281 // known (values that turn out to be equal will coalesce) but the
282 // number of elements must never get larger.
283
284 plannedL := planned.LengthInt()
285 actualL := actual.LengthInt()
286 if plannedL < actualL {
287 errs = append(errs, path.NewErrorf("length changed from %d to %d", plannedL, actualL))
288 }
289 }
290 }
291
292 return errs
293}
294
295func indexStrForErrors(v cty.Value) string {
296 switch v.Type() {
297 case cty.Number:
298 return v.AsBigFloat().Text('f', -1)
299 case cty.String:
300 return strconv.Quote(v.AsString())
301 default:
302 // Should be impossible, since no other index types are allowed!
303 return fmt.Sprintf("%#v", v)
304 }
305}
306
307// couldHaveUnknownBlockPlaceholder is a heuristic that recognizes how the
308// HCL dynamic block extension behaves when it's asked to expand a block whose
309// for_each argument is unknown. In such cases, it generates a single placeholder
310// block with all leaf attribute values unknown, and once the for_each
311// expression becomes known the placeholder may be replaced with any number
312// of blocks, so object compatibility checks would need to be more liberal.
313//
314// Set "nested" if testing a block that is nested inside a candidate block
315// placeholder; this changes the interpretation of there being no blocks of
316// a type to allow for there being zero nested blocks.
317func couldHaveUnknownBlockPlaceholder(v cty.Value, blockS *configschema.NestedBlock, nested bool) bool {
318 switch blockS.Nesting {
319 case configschema.NestingSingle, configschema.NestingGroup:
320 if nested && v.IsNull() {
321 return true // for nested blocks, a single block being unset doesn't disqualify from being an unknown block placeholder
322 }
323 return couldBeUnknownBlockPlaceholderElement(v, &blockS.Block)
324 default:
325 // These situations should be impossible for correct providers, but
326 // we permit the legacy SDK to produce some incorrect outcomes
327 // for compatibility with its existing logic, and so we must be
328 // tolerant here.
329 if !v.IsKnown() {
330 return true
331 }
332 if v.IsNull() {
333 return false // treated as if the list were empty, so we would see zero iterations below
334 }
335
336 // For all other nesting modes, our value should be something iterable.
337 for it := v.ElementIterator(); it.Next(); {
338 _, ev := it.Element()
339 if couldBeUnknownBlockPlaceholderElement(ev, &blockS.Block) {
340 return true
341 }
342 }
343
344 // Our default changes depending on whether we're testing the candidate
345 // block itself or something nested inside of it: zero blocks of a type
346 // can never contain a dynamic block placeholder, but a dynamic block
347 // placeholder might contain zero blocks of one of its own nested block
348 // types, if none were set in the config at all.
349 return nested
350 }
351}
352
353func couldBeUnknownBlockPlaceholderElement(v cty.Value, schema *configschema.Block) bool {
354 if v.IsNull() {
355 return false // null value can never be a placeholder element
356 }
357 if !v.IsKnown() {
358 return true // this should never happen for well-behaved providers, but can happen with the legacy SDK opt-outs
359 }
360 for name := range schema.Attributes {
361 av := v.GetAttr(name)
362
363 // Unknown block placeholders contain only unknown or null attribute
364 // values, depending on whether or not a particular attribute was set
365 // explicitly inside the content block. Note that this is imprecise:
366 // non-placeholders can also match this, so this function can generate
367 // false positives.
368 if av.IsKnown() && !av.IsNull() {
369 return false
370 }
371 }
372 for name, blockS := range schema.BlockTypes {
373 if !couldHaveUnknownBlockPlaceholder(v.GetAttr(name), blockS, true) {
374 return false
375 }
376 }
377 return true
378}
379
380// assertSetValuesCompatible checks that each of the elements in a can
381// be correlated with at least one equivalent element in b and vice-versa,
382// using the given correlation function.
383//
384// This allows the number of elements in the sets to change as long as all
385// elements in both sets can be correlated, making this function safe to use
386// with sets that may contain unknown values as long as the unknown case is
387// addressed in some reasonable way in the callback function.
388//
389// The callback always recieves values from set a as its first argument and
390// values from set b in its second argument, so it is safe to use with
391// non-commutative functions.
392//
393// As with assertValueCompatible, we assume that the target audience of error
394// messages here is a provider developer (via a bug report from a user) and so
395// we intentionally violate our usual rule of keeping cty implementation
396// details out of error messages.
397func assertSetValuesCompatible(planned, actual cty.Value, path cty.Path, f func(aVal, bVal cty.Value) bool) []error {
398 a := planned
399 b := actual
400
401 // Our methodology here is a little tricky, to deal with the fact that
402 // it's impossible to directly correlate two non-equal set elements because
403 // they don't have identities separate from their values.
404 // The approach is to count the number of equivalent elements each element
405 // of a has in b and vice-versa, and then return true only if each element
406 // in both sets has at least one equivalent.
407 as := a.AsValueSlice()
408 bs := b.AsValueSlice()
409 aeqs := make([]bool, len(as))
410 beqs := make([]bool, len(bs))
411 for ai, av := range as {
412 for bi, bv := range bs {
413 if f(av, bv) {
414 aeqs[ai] = true
415 beqs[bi] = true
416 }
417 }
418 }
419
420 var errs []error
421 for i, eq := range aeqs {
422 if !eq {
423 errs = append(errs, path.NewErrorf("planned set element %#v does not correlate with any element in actual", as[i]))
424 }
425 }
426 if len(errs) > 0 {
427 // Exit early since otherwise we're likely to generate duplicate
428 // error messages from the other perspective in the subsequent loop.
429 return errs
430 }
431 for i, eq := range beqs {
432 if !eq {
433 errs = append(errs, path.NewErrorf("actual set element %#v does not correlate with any element in plan", bs[i]))
434 }
435 }
436 return errs
437}
diff --git a/vendor/github.com/hashicorp/terraform/plans/objchange/doc.go b/vendor/github.com/hashicorp/terraform/plans/objchange/doc.go
new file mode 100644
index 0000000..2c18a01
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/objchange/doc.go
@@ -0,0 +1,4 @@
1// Package objchange deals with the business logic of taking a prior state
2// value and a config value and producing a proposed new merged value, along
3// with other related rules in this domain.
4package objchange
diff --git a/vendor/github.com/hashicorp/terraform/plans/objchange/lcs.go b/vendor/github.com/hashicorp/terraform/plans/objchange/lcs.go
new file mode 100644
index 0000000..cbfefdd
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/objchange/lcs.go
@@ -0,0 +1,104 @@
1package objchange
2
3import (
4 "github.com/zclconf/go-cty/cty"
5)
6
7// LongestCommonSubsequence finds a sequence of values that are common to both
8// x and y, with the same relative ordering as in both collections. This result
9// is useful as a first step towards computing a diff showing added/removed
10// elements in a sequence.
11//
12// The approached used here is a "naive" one, assuming that both xs and ys will
13// generally be small in most reasonable Terraform configurations. For larger
14// lists the time/space usage may be sub-optimal.
15//
16// A pair of lists may have multiple longest common subsequences. In that
17// case, the one selected by this function is undefined.
18func LongestCommonSubsequence(xs, ys []cty.Value) []cty.Value {
19 if len(xs) == 0 || len(ys) == 0 {
20 return make([]cty.Value, 0)
21 }
22
23 c := make([]int, len(xs)*len(ys))
24 eqs := make([]bool, len(xs)*len(ys))
25 w := len(xs)
26
27 for y := 0; y < len(ys); y++ {
28 for x := 0; x < len(xs); x++ {
29 eqV := xs[x].Equals(ys[y])
30 eq := false
31 if eqV.IsKnown() && eqV.True() {
32 eq = true
33 eqs[(w*y)+x] = true // equality tests can be expensive, so cache it
34 }
35 if eq {
36 // Sequence gets one longer than for the cell at top left,
37 // since we'd append a new item to the sequence here.
38 if x == 0 || y == 0 {
39 c[(w*y)+x] = 1
40 } else {
41 c[(w*y)+x] = c[(w*(y-1))+(x-1)] + 1
42 }
43 } else {
44 // We follow the longest of the sequence above and the sequence
45 // to the left of us in the matrix.
46 l := 0
47 u := 0
48 if x > 0 {
49 l = c[(w*y)+(x-1)]
50 }
51 if y > 0 {
52 u = c[(w*(y-1))+x]
53 }
54 if l > u {
55 c[(w*y)+x] = l
56 } else {
57 c[(w*y)+x] = u
58 }
59 }
60 }
61 }
62
63 // The bottom right cell tells us how long our longest sequence will be
64 seq := make([]cty.Value, c[len(c)-1])
65
66 // Now we will walk back from the bottom right cell, finding again all
67 // of the equal pairs to construct our sequence.
68 x := len(xs) - 1
69 y := len(ys) - 1
70 i := len(seq) - 1
71
72 for x > -1 && y > -1 {
73 if eqs[(w*y)+x] {
74 // Add the value to our result list and then walk diagonally
75 // up and to the left.
76 seq[i] = xs[x]
77 x--
78 y--
79 i--
80 } else {
81 // Take the path with the greatest sequence length in the matrix.
82 l := 0
83 u := 0
84 if x > 0 {
85 l = c[(w*y)+(x-1)]
86 }
87 if y > 0 {
88 u = c[(w*(y-1))+x]
89 }
90 if l > u {
91 x--
92 } else {
93 y--
94 }
95 }
96 }
97
98 if i > -1 {
99 // should never happen if the matrix was constructed properly
100 panic("not enough elements in sequence")
101 }
102
103 return seq
104}
diff --git a/vendor/github.com/hashicorp/terraform/plans/objchange/normalize_obj.go b/vendor/github.com/hashicorp/terraform/plans/objchange/normalize_obj.go
new file mode 100644
index 0000000..c23f44d
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/objchange/normalize_obj.go
@@ -0,0 +1,132 @@
1package objchange
2
3import (
4 "github.com/hashicorp/terraform/configs/configschema"
5 "github.com/zclconf/go-cty/cty"
6)
7
8// NormalizeObjectFromLegacySDK takes an object that may have been generated
9// by the legacy Terraform SDK (i.e. returned from a provider with the
10// LegacyTypeSystem opt-out set) and does its best to normalize it for the
11// assumptions we would normally enforce if the provider had not opted out.
12//
13// In particular, this function guarantees that a value representing a nested
14// block will never itself be unknown or null, instead representing that as
15// a non-null value that may contain null/unknown values.
16//
17// The input value must still conform to the implied type of the given schema,
18// or else this function may produce garbage results or panic. This is usually
19// okay because type consistency is enforced when deserializing the value
20// returned from the provider over the RPC wire protocol anyway.
21func NormalizeObjectFromLegacySDK(val cty.Value, schema *configschema.Block) cty.Value {
22 if val == cty.NilVal || val.IsNull() {
23 // This should never happen in reasonable use, but we'll allow it
24 // and normalize to a null of the expected type rather than panicking
25 // below.
26 return cty.NullVal(schema.ImpliedType())
27 }
28
29 vals := make(map[string]cty.Value)
30 for name := range schema.Attributes {
31 // No normalization for attributes, since them being type-conformant
32 // is all that we require.
33 vals[name] = val.GetAttr(name)
34 }
35 for name, blockS := range schema.BlockTypes {
36 lv := val.GetAttr(name)
37
38 // Legacy SDK never generates dynamically-typed attributes and so our
39 // normalization code doesn't deal with them, but we need to make sure
40 // we still pass them through properly so that we don't interfere with
41 // objects generated by other SDKs.
42 if ty := blockS.Block.ImpliedType(); ty.HasDynamicTypes() {
43 vals[name] = lv
44 continue
45 }
46
47 switch blockS.Nesting {
48 case configschema.NestingSingle, configschema.NestingGroup:
49 if lv.IsKnown() {
50 if lv.IsNull() && blockS.Nesting == configschema.NestingGroup {
51 vals[name] = blockS.EmptyValue()
52 } else {
53 vals[name] = NormalizeObjectFromLegacySDK(lv, &blockS.Block)
54 }
55 } else {
56 vals[name] = unknownBlockStub(&blockS.Block)
57 }
58 case configschema.NestingList:
59 switch {
60 case !lv.IsKnown():
61 vals[name] = cty.ListVal([]cty.Value{unknownBlockStub(&blockS.Block)})
62 case lv.IsNull() || lv.LengthInt() == 0:
63 vals[name] = cty.ListValEmpty(blockS.Block.ImpliedType())
64 default:
65 subVals := make([]cty.Value, 0, lv.LengthInt())
66 for it := lv.ElementIterator(); it.Next(); {
67 _, subVal := it.Element()
68 subVals = append(subVals, NormalizeObjectFromLegacySDK(subVal, &blockS.Block))
69 }
70 vals[name] = cty.ListVal(subVals)
71 }
72 case configschema.NestingSet:
73 switch {
74 case !lv.IsKnown():
75 vals[name] = cty.SetVal([]cty.Value{unknownBlockStub(&blockS.Block)})
76 case lv.IsNull() || lv.LengthInt() == 0:
77 vals[name] = cty.SetValEmpty(blockS.Block.ImpliedType())
78 default:
79 subVals := make([]cty.Value, 0, lv.LengthInt())
80 for it := lv.ElementIterator(); it.Next(); {
81 _, subVal := it.Element()
82 subVals = append(subVals, NormalizeObjectFromLegacySDK(subVal, &blockS.Block))
83 }
84 vals[name] = cty.SetVal(subVals)
85 }
86 default:
87 // The legacy SDK doesn't support NestingMap, so we just assume
88 // maps are always okay. (If not, we would've detected and returned
89 // an error to the user before we got here.)
90 vals[name] = lv
91 }
92 }
93 return cty.ObjectVal(vals)
94}
95
96// unknownBlockStub constructs an object value that approximates an unknown
97// block by producing a known block object with all of its leaf attribute
98// values set to unknown.
99//
100// Blocks themselves cannot be unknown, so if the legacy SDK tries to return
101// such a thing, we'll use this result instead. This convention mimics how
102// the dynamic block feature deals with being asked to iterate over an unknown
103// value, because our value-checking functions already accept this convention
104// as a special case.
105func unknownBlockStub(schema *configschema.Block) cty.Value {
106 vals := make(map[string]cty.Value)
107 for name, attrS := range schema.Attributes {
108 vals[name] = cty.UnknownVal(attrS.Type)
109 }
110 for name, blockS := range schema.BlockTypes {
111 switch blockS.Nesting {
112 case configschema.NestingSingle, configschema.NestingGroup:
113 vals[name] = unknownBlockStub(&blockS.Block)
114 case configschema.NestingList:
115 // In principle we may be expected to produce a tuple value here,
116 // if there are any dynamically-typed attributes in our nested block,
117 // but the legacy SDK doesn't support that, so we just assume it'll
118 // never be necessary to normalize those. (Incorrect usage in any
119 // other SDK would be caught and returned as an error before we
120 // get here.)
121 vals[name] = cty.ListVal([]cty.Value{unknownBlockStub(&blockS.Block)})
122 case configschema.NestingSet:
123 vals[name] = cty.SetVal([]cty.Value{unknownBlockStub(&blockS.Block)})
124 case configschema.NestingMap:
125 // A nesting map can never be unknown since we then wouldn't know
126 // what the keys are. (Legacy SDK doesn't support NestingMap anyway,
127 // so this should never arise.)
128 vals[name] = cty.MapValEmpty(blockS.Block.ImpliedType())
129 }
130 }
131 return cty.ObjectVal(vals)
132}
diff --git a/vendor/github.com/hashicorp/terraform/plans/objchange/objchange.go b/vendor/github.com/hashicorp/terraform/plans/objchange/objchange.go
new file mode 100644
index 0000000..5a8af14
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/objchange/objchange.go
@@ -0,0 +1,390 @@
1package objchange
2
3import (
4 "fmt"
5
6 "github.com/zclconf/go-cty/cty"
7
8 "github.com/hashicorp/terraform/configs/configschema"
9)
10
11// ProposedNewObject constructs a proposed new object value by combining the
12// computed attribute values from "prior" with the configured attribute values
13// from "config".
14//
15// Both value must conform to the given schema's implied type, or this function
16// will panic.
17//
18// The prior value must be wholly known, but the config value may be unknown
19// or have nested unknown values.
20//
21// The merging of the two objects includes the attributes of any nested blocks,
22// which will be correlated in a manner appropriate for their nesting mode.
23// Note in particular that the correlation for blocks backed by sets is a
24// heuristic based on matching non-computed attribute values and so it may
25// produce strange results with more "extreme" cases, such as a nested set
26// block where _all_ attributes are computed.
27func ProposedNewObject(schema *configschema.Block, prior, config cty.Value) cty.Value {
28 // If the config and prior are both null, return early here before
29 // populating the prior block. The prevents non-null blocks from appearing
30 // the proposed state value.
31 if config.IsNull() && prior.IsNull() {
32 return prior
33 }
34
35 if prior.IsNull() {
36 // In this case, we will construct a synthetic prior value that is
37 // similar to the result of decoding an empty configuration block,
38 // which simplifies our handling of the top-level attributes/blocks
39 // below by giving us one non-null level of object to pull values from.
40 prior = AllAttributesNull(schema)
41 }
42 return proposedNewObject(schema, prior, config)
43}
44
45// PlannedDataResourceObject is similar to ProposedNewObject but tailored for
46// planning data resources in particular. Specifically, it replaces the values
47// of any Computed attributes not set in the configuration with an unknown
48// value, which serves as a placeholder for a value to be filled in by the
49// provider when the data resource is finally read.
50//
51// Data resources are different because the planning of them is handled
52// entirely within Terraform Core and not subject to customization by the
53// provider. This function is, in effect, producing an equivalent result to
54// passing the ProposedNewObject result into a provider's PlanResourceChange
55// function, assuming a fixed implementation of PlanResourceChange that just
56// fills in unknown values as needed.
57func PlannedDataResourceObject(schema *configschema.Block, config cty.Value) cty.Value {
58 // Our trick here is to run the ProposedNewObject logic with an
59 // entirely-unknown prior value. Because of cty's unknown short-circuit
60 // behavior, any operation on prior returns another unknown, and so
61 // unknown values propagate into all of the parts of the resulting value
62 // that would normally be filled in by preserving the prior state.
63 prior := cty.UnknownVal(schema.ImpliedType())
64 return proposedNewObject(schema, prior, config)
65}
66
67func proposedNewObject(schema *configschema.Block, prior, config cty.Value) cty.Value {
68 if config.IsNull() || !config.IsKnown() {
69 // This is a weird situation, but we'll allow it anyway to free
70 // callers from needing to specifically check for these cases.
71 return prior
72 }
73 if (!prior.Type().IsObjectType()) || (!config.Type().IsObjectType()) {
74 panic("ProposedNewObject only supports object-typed values")
75 }
76
77 // From this point onwards, we can assume that both values are non-null
78 // object types, and that the config value itself is known (though it
79 // may contain nested values that are unknown.)
80
81 newAttrs := map[string]cty.Value{}
82 for name, attr := range schema.Attributes {
83 priorV := prior.GetAttr(name)
84 configV := config.GetAttr(name)
85 var newV cty.Value
86 switch {
87 case attr.Computed && attr.Optional:
88 // This is the trickiest scenario: we want to keep the prior value
89 // if the config isn't overriding it. Note that due to some
90 // ambiguity here, setting an optional+computed attribute from
91 // config and then later switching the config to null in a
92 // subsequent change causes the initial config value to be "sticky"
93 // unless the provider specifically overrides it during its own
94 // plan customization step.
95 if configV.IsNull() {
96 newV = priorV
97 } else {
98 newV = configV
99 }
100 case attr.Computed:
101 // configV will always be null in this case, by definition.
102 // priorV may also be null, but that's okay.
103 newV = priorV
104 default:
105 // For non-computed attributes, we always take the config value,
106 // even if it is null. If it's _required_ then null values
107 // should've been caught during an earlier validation step, and
108 // so we don't really care about that here.
109 newV = configV
110 }
111 newAttrs[name] = newV
112 }
113
114 // Merging nested blocks is a little more complex, since we need to
115 // correlate blocks between both objects and then recursively propose
116 // a new object for each. The correlation logic depends on the nesting
117 // mode for each block type.
118 for name, blockType := range schema.BlockTypes {
119 priorV := prior.GetAttr(name)
120 configV := config.GetAttr(name)
121 var newV cty.Value
122 switch blockType.Nesting {
123
124 case configschema.NestingSingle, configschema.NestingGroup:
125 newV = ProposedNewObject(&blockType.Block, priorV, configV)
126
127 case configschema.NestingList:
128 // Nested blocks are correlated by index.
129 configVLen := 0
130 if configV.IsKnown() && !configV.IsNull() {
131 configVLen = configV.LengthInt()
132 }
133 if configVLen > 0 {
134 newVals := make([]cty.Value, 0, configVLen)
135 for it := configV.ElementIterator(); it.Next(); {
136 idx, configEV := it.Element()
137 if priorV.IsKnown() && (priorV.IsNull() || !priorV.HasIndex(idx).True()) {
138 // If there is no corresponding prior element then
139 // we just take the config value as-is.
140 newVals = append(newVals, configEV)
141 continue
142 }
143 priorEV := priorV.Index(idx)
144
145 newEV := ProposedNewObject(&blockType.Block, priorEV, configEV)
146 newVals = append(newVals, newEV)
147 }
148 // Despite the name, a NestingList might also be a tuple, if
149 // its nested schema contains dynamically-typed attributes.
150 if configV.Type().IsTupleType() {
151 newV = cty.TupleVal(newVals)
152 } else {
153 newV = cty.ListVal(newVals)
154 }
155 } else {
156 // Despite the name, a NestingList might also be a tuple, if
157 // its nested schema contains dynamically-typed attributes.
158 if configV.Type().IsTupleType() {
159 newV = cty.EmptyTupleVal
160 } else {
161 newV = cty.ListValEmpty(blockType.ImpliedType())
162 }
163 }
164
165 case configschema.NestingMap:
166 // Despite the name, a NestingMap may produce either a map or
167 // object value, depending on whether the nested schema contains
168 // dynamically-typed attributes.
169 if configV.Type().IsObjectType() {
170 // Nested blocks are correlated by key.
171 configVLen := 0
172 if configV.IsKnown() && !configV.IsNull() {
173 configVLen = configV.LengthInt()
174 }
175 if configVLen > 0 {
176 newVals := make(map[string]cty.Value, configVLen)
177 atys := configV.Type().AttributeTypes()
178 for name := range atys {
179 configEV := configV.GetAttr(name)
180 if !priorV.IsKnown() || priorV.IsNull() || !priorV.Type().HasAttribute(name) {
181 // If there is no corresponding prior element then
182 // we just take the config value as-is.
183 newVals[name] = configEV
184 continue
185 }
186 priorEV := priorV.GetAttr(name)
187
188 newEV := ProposedNewObject(&blockType.Block, priorEV, configEV)
189 newVals[name] = newEV
190 }
191 // Although we call the nesting mode "map", we actually use
192 // object values so that elements might have different types
193 // in case of dynamically-typed attributes.
194 newV = cty.ObjectVal(newVals)
195 } else {
196 newV = cty.EmptyObjectVal
197 }
198 } else {
199 configVLen := 0
200 if configV.IsKnown() && !configV.IsNull() {
201 configVLen = configV.LengthInt()
202 }
203 if configVLen > 0 {
204 newVals := make(map[string]cty.Value, configVLen)
205 for it := configV.ElementIterator(); it.Next(); {
206 idx, configEV := it.Element()
207 k := idx.AsString()
208 if priorV.IsKnown() && (priorV.IsNull() || !priorV.HasIndex(idx).True()) {
209 // If there is no corresponding prior element then
210 // we just take the config value as-is.
211 newVals[k] = configEV
212 continue
213 }
214 priorEV := priorV.Index(idx)
215
216 newEV := ProposedNewObject(&blockType.Block, priorEV, configEV)
217 newVals[k] = newEV
218 }
219 newV = cty.MapVal(newVals)
220 } else {
221 newV = cty.MapValEmpty(blockType.ImpliedType())
222 }
223 }
224
225 case configschema.NestingSet:
226 if !configV.Type().IsSetType() {
227 panic("configschema.NestingSet value is not a set as expected")
228 }
229
230 // Nested blocks are correlated by comparing the element values
231 // after eliminating all of the computed attributes. In practice,
232 // this means that any config change produces an entirely new
233 // nested object, and we only propagate prior computed values
234 // if the non-computed attribute values are identical.
235 var cmpVals [][2]cty.Value
236 if priorV.IsKnown() && !priorV.IsNull() {
237 cmpVals = setElementCompareValues(&blockType.Block, priorV, false)
238 }
239 configVLen := 0
240 if configV.IsKnown() && !configV.IsNull() {
241 configVLen = configV.LengthInt()
242 }
243 if configVLen > 0 {
244 used := make([]bool, len(cmpVals)) // track used elements in case multiple have the same compare value
245 newVals := make([]cty.Value, 0, configVLen)
246 for it := configV.ElementIterator(); it.Next(); {
247 _, configEV := it.Element()
248 var priorEV cty.Value
249 for i, cmp := range cmpVals {
250 if used[i] {
251 continue
252 }
253 if cmp[1].RawEquals(configEV) {
254 priorEV = cmp[0]
255 used[i] = true // we can't use this value on a future iteration
256 break
257 }
258 }
259 if priorEV == cty.NilVal {
260 priorEV = cty.NullVal(blockType.ImpliedType())
261 }
262
263 newEV := ProposedNewObject(&blockType.Block, priorEV, configEV)
264 newVals = append(newVals, newEV)
265 }
266 newV = cty.SetVal(newVals)
267 } else {
268 newV = cty.SetValEmpty(blockType.Block.ImpliedType())
269 }
270
271 default:
272 // Should never happen, since the above cases are comprehensive.
273 panic(fmt.Sprintf("unsupported block nesting mode %s", blockType.Nesting))
274 }
275
276 newAttrs[name] = newV
277 }
278
279 return cty.ObjectVal(newAttrs)
280}
281
282// setElementCompareValues takes a known, non-null value of a cty.Set type and
283// returns a table -- constructed of two-element arrays -- that maps original
284// set element values to corresponding values that have all of the computed
285// values removed, making them suitable for comparison with values obtained
286// from configuration. The element type of the set must conform to the implied
287// type of the given schema, or this function will panic.
288//
289// In the resulting slice, the zeroth element of each array is the original
290// value and the one-indexed element is the corresponding "compare value".
291//
292// This is intended to help correlate prior elements with configured elements
293// in ProposedNewObject. The result is a heuristic rather than an exact science,
294// since e.g. two separate elements may reduce to the same value through this
295// process. The caller must therefore be ready to deal with duplicates.
296func setElementCompareValues(schema *configschema.Block, set cty.Value, isConfig bool) [][2]cty.Value {
297 ret := make([][2]cty.Value, 0, set.LengthInt())
298 for it := set.ElementIterator(); it.Next(); {
299 _, ev := it.Element()
300 ret = append(ret, [2]cty.Value{ev, setElementCompareValue(schema, ev, isConfig)})
301 }
302 return ret
303}
304
305// setElementCompareValue creates a new value that has all of the same
306// non-computed attribute values as the one given but has all computed
307// attribute values forced to null.
308//
309// If isConfig is true then non-null Optional+Computed attribute values will
310// be preserved. Otherwise, they will also be set to null.
311//
312// The input value must conform to the schema's implied type, and the return
313// value is guaranteed to conform to it.
314func setElementCompareValue(schema *configschema.Block, v cty.Value, isConfig bool) cty.Value {
315 if v.IsNull() || !v.IsKnown() {
316 return v
317 }
318
319 attrs := map[string]cty.Value{}
320 for name, attr := range schema.Attributes {
321 switch {
322 case attr.Computed && attr.Optional:
323 if isConfig {
324 attrs[name] = v.GetAttr(name)
325 } else {
326 attrs[name] = cty.NullVal(attr.Type)
327 }
328 case attr.Computed:
329 attrs[name] = cty.NullVal(attr.Type)
330 default:
331 attrs[name] = v.GetAttr(name)
332 }
333 }
334
335 for name, blockType := range schema.BlockTypes {
336 switch blockType.Nesting {
337
338 case configschema.NestingSingle, configschema.NestingGroup:
339 attrs[name] = setElementCompareValue(&blockType.Block, v.GetAttr(name), isConfig)
340
341 case configschema.NestingList, configschema.NestingSet:
342 cv := v.GetAttr(name)
343 if cv.IsNull() || !cv.IsKnown() {
344 attrs[name] = cv
345 continue
346 }
347 if l := cv.LengthInt(); l > 0 {
348 elems := make([]cty.Value, 0, l)
349 for it := cv.ElementIterator(); it.Next(); {
350 _, ev := it.Element()
351 elems = append(elems, setElementCompareValue(&blockType.Block, ev, isConfig))
352 }
353 if blockType.Nesting == configschema.NestingSet {
354 // SetValEmpty would panic if given elements that are not
355 // all of the same type, but that's guaranteed not to
356 // happen here because our input value was _already_ a
357 // set and we've not changed the types of any elements here.
358 attrs[name] = cty.SetVal(elems)
359 } else {
360 attrs[name] = cty.TupleVal(elems)
361 }
362 } else {
363 if blockType.Nesting == configschema.NestingSet {
364 attrs[name] = cty.SetValEmpty(blockType.Block.ImpliedType())
365 } else {
366 attrs[name] = cty.EmptyTupleVal
367 }
368 }
369
370 case configschema.NestingMap:
371 cv := v.GetAttr(name)
372 if cv.IsNull() || !cv.IsKnown() {
373 attrs[name] = cv
374 continue
375 }
376 elems := make(map[string]cty.Value)
377 for it := cv.ElementIterator(); it.Next(); {
378 kv, ev := it.Element()
379 elems[kv.AsString()] = setElementCompareValue(&blockType.Block, ev, isConfig)
380 }
381 attrs[name] = cty.ObjectVal(elems)
382
383 default:
384 // Should never happen, since the above cases are comprehensive.
385 panic(fmt.Sprintf("unsupported block nesting mode %s", blockType.Nesting))
386 }
387 }
388
389 return cty.ObjectVal(attrs)
390}
diff --git a/vendor/github.com/hashicorp/terraform/plans/objchange/plan_valid.go b/vendor/github.com/hashicorp/terraform/plans/objchange/plan_valid.go
new file mode 100644
index 0000000..69acb89
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/objchange/plan_valid.go
@@ -0,0 +1,267 @@
1package objchange
2
3import (
4 "fmt"
5
6 "github.com/zclconf/go-cty/cty"
7
8 "github.com/hashicorp/terraform/configs/configschema"
9)
10
11// AssertPlanValid checks checks whether a planned new state returned by a
12// provider's PlanResourceChange method is suitable to achieve a change
13// from priorState to config. It returns a slice with nonzero length if
14// any problems are detected. Because problems here indicate bugs in the
15// provider that generated the plannedState, they are written with provider
16// developers as an audience, rather than end-users.
17//
18// All of the given values must have the same type and must conform to the
19// implied type of the given schema, or this function may panic or produce
20// garbage results.
21//
22// During planning, a provider may only make changes to attributes that are
23// null (unset) in the configuration and are marked as "computed" in the
24// resource type schema, in order to insert any default values the provider
25// may know about. If the default value cannot be determined until apply time,
26// the provider can return an unknown value. Providers are forbidden from
27// planning a change that disagrees with any non-null argument in the
28// configuration.
29//
30// As a special exception, providers _are_ allowed to provide attribute values
31// conflicting with configuration if and only if the planned value exactly
32// matches the corresponding attribute value in the prior state. The provider
33// can use this to signal that the new value is functionally equivalent to
34// the old and thus no change is required.
35func AssertPlanValid(schema *configschema.Block, priorState, config, plannedState cty.Value) []error {
36 return assertPlanValid(schema, priorState, config, plannedState, nil)
37}
38
39func assertPlanValid(schema *configschema.Block, priorState, config, plannedState cty.Value, path cty.Path) []error {
40 var errs []error
41 if plannedState.IsNull() && !config.IsNull() {
42 errs = append(errs, path.NewErrorf("planned for absense but config wants existence"))
43 return errs
44 }
45 if config.IsNull() && !plannedState.IsNull() {
46 errs = append(errs, path.NewErrorf("planned for existence but config wants absense"))
47 return errs
48 }
49 if plannedState.IsNull() {
50 // No further checks possible if the planned value is null
51 return errs
52 }
53
54 impTy := schema.ImpliedType()
55
56 for name, attrS := range schema.Attributes {
57 plannedV := plannedState.GetAttr(name)
58 configV := config.GetAttr(name)
59 priorV := cty.NullVal(attrS.Type)
60 if !priorState.IsNull() {
61 priorV = priorState.GetAttr(name)
62 }
63
64 path := append(path, cty.GetAttrStep{Name: name})
65 moreErrs := assertPlannedValueValid(attrS, priorV, configV, plannedV, path)
66 errs = append(errs, moreErrs...)
67 }
68 for name, blockS := range schema.BlockTypes {
69 path := append(path, cty.GetAttrStep{Name: name})
70 plannedV := plannedState.GetAttr(name)
71 configV := config.GetAttr(name)
72 priorV := cty.NullVal(impTy.AttributeType(name))
73 if !priorState.IsNull() {
74 priorV = priorState.GetAttr(name)
75 }
76 if plannedV.RawEquals(configV) {
77 // Easy path: nothing has changed at all
78 continue
79 }
80 if !plannedV.IsKnown() {
81 errs = append(errs, path.NewErrorf("attribute representing nested block must not be unknown itself; set nested attribute values to unknown instead"))
82 continue
83 }
84
85 switch blockS.Nesting {
86 case configschema.NestingSingle, configschema.NestingGroup:
87 moreErrs := assertPlanValid(&blockS.Block, priorV, configV, plannedV, path)
88 errs = append(errs, moreErrs...)
89 case configschema.NestingList:
90 // A NestingList might either be a list or a tuple, depending on
91 // whether there are dynamically-typed attributes inside. However,
92 // both support a similar-enough API that we can treat them the
93 // same for our purposes here.
94 if plannedV.IsNull() {
95 errs = append(errs, path.NewErrorf("attribute representing a list of nested blocks must be empty to indicate no blocks, not null"))
96 continue
97 }
98
99 plannedL := plannedV.LengthInt()
100 configL := configV.LengthInt()
101 if plannedL != configL {
102 errs = append(errs, path.NewErrorf("block count in plan (%d) disagrees with count in config (%d)", plannedL, configL))
103 continue
104 }
105 for it := plannedV.ElementIterator(); it.Next(); {
106 idx, plannedEV := it.Element()
107 path := append(path, cty.IndexStep{Key: idx})
108 if !plannedEV.IsKnown() {
109 errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead"))
110 continue
111 }
112 if !configV.HasIndex(idx).True() {
113 continue // should never happen since we checked the lengths above
114 }
115 configEV := configV.Index(idx)
116 priorEV := cty.NullVal(blockS.ImpliedType())
117 if !priorV.IsNull() && priorV.HasIndex(idx).True() {
118 priorEV = priorV.Index(idx)
119 }
120
121 moreErrs := assertPlanValid(&blockS.Block, priorEV, configEV, plannedEV, path)
122 errs = append(errs, moreErrs...)
123 }
124 case configschema.NestingMap:
125 if plannedV.IsNull() {
126 errs = append(errs, path.NewErrorf("attribute representing a map of nested blocks must be empty to indicate no blocks, not null"))
127 continue
128 }
129
130 // A NestingMap might either be a map or an object, depending on
131 // whether there are dynamically-typed attributes inside, but
132 // that's decided statically and so all values will have the same
133 // kind.
134 if plannedV.Type().IsObjectType() {
135 plannedAtys := plannedV.Type().AttributeTypes()
136 configAtys := configV.Type().AttributeTypes()
137 for k := range plannedAtys {
138 if _, ok := configAtys[k]; !ok {
139 errs = append(errs, path.NewErrorf("block key %q from plan is not present in config", k))
140 continue
141 }
142 path := append(path, cty.GetAttrStep{Name: k})
143
144 plannedEV := plannedV.GetAttr(k)
145 if !plannedEV.IsKnown() {
146 errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead"))
147 continue
148 }
149 configEV := configV.GetAttr(k)
150 priorEV := cty.NullVal(blockS.ImpliedType())
151 if !priorV.IsNull() && priorV.Type().HasAttribute(k) {
152 priorEV = priorV.GetAttr(k)
153 }
154 moreErrs := assertPlanValid(&blockS.Block, priorEV, configEV, plannedEV, path)
155 errs = append(errs, moreErrs...)
156 }
157 for k := range configAtys {
158 if _, ok := plannedAtys[k]; !ok {
159 errs = append(errs, path.NewErrorf("block key %q from config is not present in plan", k))
160 continue
161 }
162 }
163 } else {
164 plannedL := plannedV.LengthInt()
165 configL := configV.LengthInt()
166 if plannedL != configL {
167 errs = append(errs, path.NewErrorf("block count in plan (%d) disagrees with count in config (%d)", plannedL, configL))
168 continue
169 }
170 for it := plannedV.ElementIterator(); it.Next(); {
171 idx, plannedEV := it.Element()
172 path := append(path, cty.IndexStep{Key: idx})
173 if !plannedEV.IsKnown() {
174 errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead"))
175 continue
176 }
177 k := idx.AsString()
178 if !configV.HasIndex(idx).True() {
179 errs = append(errs, path.NewErrorf("block key %q from plan is not present in config", k))
180 continue
181 }
182 configEV := configV.Index(idx)
183 priorEV := cty.NullVal(blockS.ImpliedType())
184 if !priorV.IsNull() && priorV.HasIndex(idx).True() {
185 priorEV = priorV.Index(idx)
186 }
187 moreErrs := assertPlanValid(&blockS.Block, priorEV, configEV, plannedEV, path)
188 errs = append(errs, moreErrs...)
189 }
190 for it := configV.ElementIterator(); it.Next(); {
191 idx, _ := it.Element()
192 if !plannedV.HasIndex(idx).True() {
193 errs = append(errs, path.NewErrorf("block key %q from config is not present in plan", idx.AsString()))
194 continue
195 }
196 }
197 }
198 case configschema.NestingSet:
199 if plannedV.IsNull() {
200 errs = append(errs, path.NewErrorf("attribute representing a set of nested blocks must be empty to indicate no blocks, not null"))
201 continue
202 }
203
204 // Because set elements have no identifier with which to correlate
205 // them, we can't robustly validate the plan for a nested block
206 // backed by a set, and so unfortunately we need to just trust the
207 // provider to do the right thing. :(
208 //
209 // (In principle we could correlate elements by matching the
210 // subset of attributes explicitly set in config, except for the
211 // special diff suppression rule which allows for there to be a
212 // planned value that is constructed by mixing part of a prior
213 // value with part of a config value, creating an entirely new
214 // element that is not present in either prior nor config.)
215 for it := plannedV.ElementIterator(); it.Next(); {
216 idx, plannedEV := it.Element()
217 path := append(path, cty.IndexStep{Key: idx})
218 if !plannedEV.IsKnown() {
219 errs = append(errs, path.NewErrorf("element representing nested block must not be unknown itself; set nested attribute values to unknown instead"))
220 continue
221 }
222 }
223
224 default:
225 panic(fmt.Sprintf("unsupported nesting mode %s", blockS.Nesting))
226 }
227 }
228
229 return errs
230}
231
232func assertPlannedValueValid(attrS *configschema.Attribute, priorV, configV, plannedV cty.Value, path cty.Path) []error {
233 var errs []error
234 if plannedV.RawEquals(configV) {
235 // This is the easy path: provider didn't change anything at all.
236 return errs
237 }
238 if plannedV.RawEquals(priorV) && !priorV.IsNull() {
239 // Also pretty easy: there is a prior value and the provider has
240 // returned it unchanged. This indicates that configV and plannedV
241 // are functionally equivalent and so the provider wishes to disregard
242 // the configuration value in favor of the prior.
243 return errs
244 }
245 if attrS.Computed && configV.IsNull() {
246 // The provider is allowed to change the value of any computed
247 // attribute that isn't explicitly set in the config.
248 return errs
249 }
250
251 // If none of the above conditions match, the provider has made an invalid
252 // change to this attribute.
253 if priorV.IsNull() {
254 if attrS.Sensitive {
255 errs = append(errs, path.NewErrorf("sensitive planned value does not match config value"))
256 } else {
257 errs = append(errs, path.NewErrorf("planned value %#v does not match config value %#v", plannedV, configV))
258 }
259 return errs
260 }
261 if attrS.Sensitive {
262 errs = append(errs, path.NewErrorf("sensitive planned value does not match config value nor prior value"))
263 } else {
264 errs = append(errs, path.NewErrorf("planned value %#v does not match config value %#v nor prior value %#v", plannedV, configV, priorV))
265 }
266 return errs
267}
diff --git a/vendor/github.com/hashicorp/terraform/plans/plan.go b/vendor/github.com/hashicorp/terraform/plans/plan.go
new file mode 100644
index 0000000..5a3e454
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/plans/plan.go
@@ -0,0 +1,92 @@
1package plans
2
3import (
4 "sort"
5
6 "github.com/hashicorp/terraform/addrs"
7 "github.com/hashicorp/terraform/configs/configschema"
8 "github.com/zclconf/go-cty/cty"
9)
10
11// Plan is the top-level type representing a planned set of changes.
12//
13// A plan is a summary of the set of changes required to move from a current
14// state to a goal state derived from configuration. The described changes
15// are not applied directly, but contain an approximation of the final
16// result that will be completed during apply by resolving any values that
17// cannot be predicted.
18//
19// A plan must always be accompanied by the state and configuration it was
20// built from, since the plan does not itself include all of the information
21// required to make the changes indicated.
22type Plan struct {
23 VariableValues map[string]DynamicValue
24 Changes *Changes
25 TargetAddrs []addrs.Targetable
26 ProviderSHA256s map[string][]byte
27 Backend Backend
28}
29
30// Backend represents the backend-related configuration and other data as it
31// existed when a plan was created.
32type Backend struct {
33 // Type is the type of backend that the plan will apply against.
34 Type string
35
36 // Config is the configuration of the backend, whose schema is decided by
37 // the backend Type.
38 Config DynamicValue
39
40 // Workspace is the name of the workspace that was active when the plan
41 // was created. It is illegal to apply a plan created for one workspace
42 // to the state of another workspace.
43 // (This constraint is already enforced by the statefile lineage mechanism,
44 // but storing this explicitly allows us to return a better error message
45 // in the situation where the user has the wrong workspace selected.)
46 Workspace string
47}
48
49func NewBackend(typeName string, config cty.Value, configSchema *configschema.Block, workspaceName string) (*Backend, error) {
50 dv, err := NewDynamicValue(config, configSchema.ImpliedType())
51 if err != nil {
52 return nil, err
53 }
54
55 return &Backend{
56 Type: typeName,
57 Config: dv,
58 Workspace: workspaceName,
59 }, nil
60}
61
62// ProviderAddrs returns a list of all of the provider configuration addresses
63// referenced throughout the receiving plan.
64//
65// The result is de-duplicated so that each distinct address appears only once.
66func (p *Plan) ProviderAddrs() []addrs.AbsProviderConfig {
67 if p == nil || p.Changes == nil {
68 return nil
69 }
70
71 m := map[string]addrs.AbsProviderConfig{}
72 for _, rc := range p.Changes.Resources {
73 m[rc.ProviderAddr.String()] = rc.ProviderAddr
74 }
75 if len(m) == 0 {
76 return nil
77 }
78
79 // This is mainly just so we'll get stable results for testing purposes.
80 keys := make([]string, 0, len(m))
81 for k := range m {
82 keys = append(keys, k)
83 }
84 sort.Strings(keys)
85
86 ret := make([]addrs.AbsProviderConfig, len(keys))
87 for i, key := range keys {
88 ret[i] = m[key]
89 }
90
91 return ret
92}