aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/states
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/states')
-rw-r--r--vendor/github.com/hashicorp/terraform/states/doc.go3
-rw-r--r--vendor/github.com/hashicorp/terraform/states/eachmode_string.go35
-rw-r--r--vendor/github.com/hashicorp/terraform/states/instance_generation.go24
-rw-r--r--vendor/github.com/hashicorp/terraform/states/instance_object.go120
-rw-r--r--vendor/github.com/hashicorp/terraform/states/instance_object_src.go113
-rw-r--r--vendor/github.com/hashicorp/terraform/states/module.go285
-rw-r--r--vendor/github.com/hashicorp/terraform/states/objectstatus_string.go33
-rw-r--r--vendor/github.com/hashicorp/terraform/states/output_value.go14
-rw-r--r--vendor/github.com/hashicorp/terraform/states/resource.go239
-rw-r--r--vendor/github.com/hashicorp/terraform/states/state.go229
-rw-r--r--vendor/github.com/hashicorp/terraform/states/state_deepcopy.go218
-rw-r--r--vendor/github.com/hashicorp/terraform/states/state_equal.go18
-rw-r--r--vendor/github.com/hashicorp/terraform/states/state_string.go279
-rw-r--r--vendor/github.com/hashicorp/terraform/states/statefile/diagnostics.go62
-rw-r--r--vendor/github.com/hashicorp/terraform/states/statefile/doc.go3
-rw-r--r--vendor/github.com/hashicorp/terraform/states/statefile/file.go62
-rw-r--r--vendor/github.com/hashicorp/terraform/states/statefile/marshal_equal.go40
-rw-r--r--vendor/github.com/hashicorp/terraform/states/statefile/read.go209
-rw-r--r--vendor/github.com/hashicorp/terraform/states/statefile/version0.go23
-rw-r--r--vendor/github.com/hashicorp/terraform/states/statefile/version1.go174
-rw-r--r--vendor/github.com/hashicorp/terraform/states/statefile/version1_upgrade.go172
-rw-r--r--vendor/github.com/hashicorp/terraform/states/statefile/version2.go209
-rw-r--r--vendor/github.com/hashicorp/terraform/states/statefile/version2_upgrade.go145
-rw-r--r--vendor/github.com/hashicorp/terraform/states/statefile/version3.go50
-rw-r--r--vendor/github.com/hashicorp/terraform/states/statefile/version3_upgrade.go431
-rw-r--r--vendor/github.com/hashicorp/terraform/states/statefile/version4.go604
-rw-r--r--vendor/github.com/hashicorp/terraform/states/statefile/write.go17
-rw-r--r--vendor/github.com/hashicorp/terraform/states/sync.go537
28 files changed, 4348 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/states/doc.go b/vendor/github.com/hashicorp/terraform/states/doc.go
new file mode 100644
index 0000000..7dd74ac
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/doc.go
@@ -0,0 +1,3 @@
1// Package states contains the types that are used to represent Terraform
2// states.
3package states
diff --git a/vendor/github.com/hashicorp/terraform/states/eachmode_string.go b/vendor/github.com/hashicorp/terraform/states/eachmode_string.go
new file mode 100644
index 0000000..0dc7349
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/eachmode_string.go
@@ -0,0 +1,35 @@
1// Code generated by "stringer -type EachMode"; DO NOT EDIT.
2
3package states
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[NoEach-0]
12 _ = x[EachList-76]
13 _ = x[EachMap-77]
14}
15
16const (
17 _EachMode_name_0 = "NoEach"
18 _EachMode_name_1 = "EachListEachMap"
19)
20
21var (
22 _EachMode_index_1 = [...]uint8{0, 8, 15}
23)
24
25func (i EachMode) String() string {
26 switch {
27 case i == 0:
28 return _EachMode_name_0
29 case 76 <= i && i <= 77:
30 i -= 76
31 return _EachMode_name_1[_EachMode_index_1[i]:_EachMode_index_1[i+1]]
32 default:
33 return "EachMode(" + strconv.FormatInt(int64(i), 10) + ")"
34 }
35}
diff --git a/vendor/github.com/hashicorp/terraform/states/instance_generation.go b/vendor/github.com/hashicorp/terraform/states/instance_generation.go
new file mode 100644
index 0000000..617ad4e
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/instance_generation.go
@@ -0,0 +1,24 @@
1package states
2
3// Generation is used to represent multiple objects in a succession of objects
4// represented by a single resource instance address. A resource instance can
5// have multiple generations over its lifetime due to object replacement
6// (when a change can't be applied without destroying and re-creating), and
7// multiple generations can exist at the same time when create_before_destroy
8// is used.
9//
10// A Generation value can either be the value of the variable "CurrentGen" or
11// a value of type DeposedKey. Generation values can be compared for equality
12// using "==" and used as map keys. The zero value of Generation (nil) is not
13// a valid generation and must not be used.
14type Generation interface {
15 generation()
16}
17
18// CurrentGen is the Generation representing the currently-active object for
19// a resource instance.
20var CurrentGen Generation
21
22type currentGen struct{}
23
24func (g currentGen) generation() {}
diff --git a/vendor/github.com/hashicorp/terraform/states/instance_object.go b/vendor/github.com/hashicorp/terraform/states/instance_object.go
new file mode 100644
index 0000000..1374c59
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/instance_object.go
@@ -0,0 +1,120 @@
1package states
2
3import (
4 "github.com/zclconf/go-cty/cty"
5 ctyjson "github.com/zclconf/go-cty/cty/json"
6
7 "github.com/hashicorp/terraform/addrs"
8)
9
10// ResourceInstanceObject is the local representation of a specific remote
11// object associated with a resource instance. In practice not all remote
12// objects are actually remote in the sense of being accessed over the network,
13// but this is the most common case.
14//
15// It is not valid to mutate a ResourceInstanceObject once it has been created.
16// Instead, create a new object and replace the existing one.
17type ResourceInstanceObject struct {
18 // Value is the object-typed value representing the remote object within
19 // Terraform.
20 Value cty.Value
21
22 // Private is an opaque value set by the provider when this object was
23 // last created or updated. Terraform Core does not use this value in
24 // any way and it is not exposed anywhere in the user interface, so
25 // a provider can use it for retaining any necessary private state.
26 Private []byte
27
28 // Status represents the "readiness" of the object as of the last time
29 // it was updated.
30 Status ObjectStatus
31
32 // Dependencies is a set of other addresses in the same module which
33 // this instance depended on when the given attributes were evaluated.
34 // This is used to construct the dependency relationships for an object
35 // whose configuration is no longer available, such as if it has been
36 // removed from configuration altogether, or is now deposed.
37 Dependencies []addrs.Referenceable
38}
39
40// ObjectStatus represents the status of a RemoteObject.
41type ObjectStatus rune
42
43//go:generate stringer -type ObjectStatus
44
45const (
46 // ObjectReady is an object status for an object that is ready to use.
47 ObjectReady ObjectStatus = 'R'
48
49 // ObjectTainted is an object status representing an object that is in
50 // an unrecoverable bad state due to a partial failure during a create,
51 // update, or delete operation. Since it cannot be moved into the
52 // ObjectRead state, a tainted object must be replaced.
53 ObjectTainted ObjectStatus = 'T'
54
55 // ObjectPlanned is a special object status used only for the transient
56 // placeholder objects we place into state during the refresh and plan
57 // walks to stand in for objects that will be created during apply.
58 //
59 // Any object of this status must have a corresponding change recorded
60 // in the current plan, whose value must then be used in preference to
61 // the value stored in state when evaluating expressions. A planned
62 // object stored in state will be incomplete if any of its attributes are
63 // not yet known, and the plan must be consulted in order to "see" those
64 // unknown values, because the state is not able to represent them.
65 ObjectPlanned ObjectStatus = 'P'
66)
67
68// Encode marshals the value within the receiver to produce a
69// ResourceInstanceObjectSrc ready to be written to a state file.
70//
71// The given type must be the implied type of the resource type schema, and
72// the given value must conform to it. It is important to pass the schema
73// type and not the object's own type so that dynamically-typed attributes
74// will be stored correctly. The caller must also provide the version number
75// of the schema that the given type was derived from, which will be recorded
76// in the source object so it can be used to detect when schema migration is
77// required on read.
78//
79// The returned object may share internal references with the receiver and
80// so the caller must not mutate the receiver any further once once this
81// method is called.
82func (o *ResourceInstanceObject) Encode(ty cty.Type, schemaVersion uint64) (*ResourceInstanceObjectSrc, error) {
83 // Our state serialization can't represent unknown values, so we convert
84 // them to nulls here. This is lossy, but nobody should be writing unknown
85 // values here and expecting to get them out again later.
86 //
87 // We get unknown values here while we're building out a "planned state"
88 // during the plan phase, but the value stored in the plan takes precedence
89 // for expression evaluation. The apply step should never produce unknown
90 // values, but if it does it's the responsibility of the caller to detect
91 // and raise an error about that.
92 val := cty.UnknownAsNull(o.Value)
93
94 src, err := ctyjson.Marshal(val, ty)
95 if err != nil {
96 return nil, err
97 }
98
99 return &ResourceInstanceObjectSrc{
100 SchemaVersion: schemaVersion,
101 AttrsJSON: src,
102 Private: o.Private,
103 Status: o.Status,
104 Dependencies: o.Dependencies,
105 }, nil
106}
107
108// AsTainted returns a deep copy of the receiver with the status updated to
109// ObjectTainted.
110func (o *ResourceInstanceObject) AsTainted() *ResourceInstanceObject {
111 if o == nil {
112 // A nil object can't be tainted, but we'll allow this anyway to
113 // avoid a crash, since we presumably intend to eventually record
114 // the object has having been deleted anyway.
115 return nil
116 }
117 ret := o.DeepCopy()
118 ret.Status = ObjectTainted
119 return ret
120}
diff --git a/vendor/github.com/hashicorp/terraform/states/instance_object_src.go b/vendor/github.com/hashicorp/terraform/states/instance_object_src.go
new file mode 100644
index 0000000..6cb3c27
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/instance_object_src.go
@@ -0,0 +1,113 @@
1package states
2
3import (
4 "github.com/zclconf/go-cty/cty"
5 ctyjson "github.com/zclconf/go-cty/cty/json"
6
7 "github.com/hashicorp/terraform/addrs"
8 "github.com/hashicorp/terraform/config/hcl2shim"
9)
10
11// ResourceInstanceObjectSrc is a not-fully-decoded version of
12// ResourceInstanceObject. Decoding of it can be completed by first handling
13// any schema migration steps to get to the latest schema version and then
14// calling method Decode with the implied type of the latest schema.
15type ResourceInstanceObjectSrc struct {
16 // SchemaVersion is the resource-type-specific schema version number that
17 // was current when either AttrsJSON or AttrsFlat was encoded. Migration
18 // steps are required if this is less than the current version number
19 // reported by the corresponding provider.
20 SchemaVersion uint64
21
22 // AttrsJSON is a JSON-encoded representation of the object attributes,
23 // encoding the value (of the object type implied by the associated resource
24 // type schema) that represents this remote object in Terraform Language
25 // expressions, and is compared with configuration when producing a diff.
26 //
27 // This is retained in JSON format here because it may require preprocessing
28 // before decoding if, for example, the stored attributes are for an older
29 // schema version which the provider must upgrade before use. If the
30 // version is current, it is valid to simply decode this using the
31 // type implied by the current schema, without the need for the provider
32 // to perform an upgrade first.
33 //
34 // When writing a ResourceInstanceObject into the state, AttrsJSON should
35 // always be conformant to the current schema version and the current
36 // schema version should be recorded in the SchemaVersion field.
37 AttrsJSON []byte
38
39 // AttrsFlat is a legacy form of attributes used in older state file
40 // formats, and in the new state format for objects that haven't yet been
41 // upgraded. This attribute is mutually exclusive with Attrs: for any
42 // ResourceInstanceObject, only one of these attributes may be populated
43 // and the other must be nil.
44 //
45 // An instance object with this field populated should be upgraded to use
46 // Attrs at the earliest opportunity, since this legacy flatmap-based
47 // format will be phased out over time. AttrsFlat should not be used when
48 // writing new or updated objects to state; instead, callers must follow
49 // the recommendations in the AttrsJSON documentation above.
50 AttrsFlat map[string]string
51
52 // These fields all correspond to the fields of the same name on
53 // ResourceInstanceObject.
54 Private []byte
55 Status ObjectStatus
56 Dependencies []addrs.Referenceable
57}
58
59// Decode unmarshals the raw representation of the object attributes. Pass the
60// implied type of the corresponding resource type schema for correct operation.
61//
62// Before calling Decode, the caller must check that the SchemaVersion field
63// exactly equals the version number of the schema whose implied type is being
64// passed, or else the result is undefined.
65//
66// The returned object may share internal references with the receiver and
67// so the caller must not mutate the receiver any further once once this
68// method is called.
69func (os *ResourceInstanceObjectSrc) Decode(ty cty.Type) (*ResourceInstanceObject, error) {
70 var val cty.Value
71 var err error
72 if os.AttrsFlat != nil {
73 // Legacy mode. We'll do our best to unpick this from the flatmap.
74 val, err = hcl2shim.HCL2ValueFromFlatmap(os.AttrsFlat, ty)
75 if err != nil {
76 return nil, err
77 }
78 } else {
79 val, err = ctyjson.Unmarshal(os.AttrsJSON, ty)
80 if err != nil {
81 return nil, err
82 }
83 }
84
85 return &ResourceInstanceObject{
86 Value: val,
87 Status: os.Status,
88 Dependencies: os.Dependencies,
89 Private: os.Private,
90 }, nil
91}
92
93// CompleteUpgrade creates a new ResourceInstanceObjectSrc by copying the
94// metadata from the receiver and writing in the given new schema version
95// and attribute value that are presumed to have resulted from upgrading
96// from an older schema version.
97func (os *ResourceInstanceObjectSrc) CompleteUpgrade(newAttrs cty.Value, newType cty.Type, newSchemaVersion uint64) (*ResourceInstanceObjectSrc, error) {
98 new := os.DeepCopy()
99 new.AttrsFlat = nil // We always use JSON after an upgrade, even if the source used flatmap
100
101 // This is the same principle as ResourceInstanceObject.Encode, but
102 // avoiding a decode/re-encode cycle because we don't have type info
103 // available for the "old" attributes.
104 newAttrs = cty.UnknownAsNull(newAttrs)
105 src, err := ctyjson.Marshal(newAttrs, newType)
106 if err != nil {
107 return nil, err
108 }
109
110 new.AttrsJSON = src
111 new.SchemaVersion = newSchemaVersion
112 return new, nil
113}
diff --git a/vendor/github.com/hashicorp/terraform/states/module.go b/vendor/github.com/hashicorp/terraform/states/module.go
new file mode 100644
index 0000000..d89e787
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/module.go
@@ -0,0 +1,285 @@
1package states
2
3import (
4 "github.com/zclconf/go-cty/cty"
5
6 "github.com/hashicorp/terraform/addrs"
7)
8
9// Module is a container for the states of objects within a particular module.
10type Module struct {
11 Addr addrs.ModuleInstance
12
13 // Resources contains the state for each resource. The keys in this map are
14 // an implementation detail and must not be used by outside callers.
15 Resources map[string]*Resource
16
17 // OutputValues contains the state for each output value. The keys in this
18 // map are output value names.
19 OutputValues map[string]*OutputValue
20
21 // LocalValues contains the value for each named output value. The keys
22 // in this map are local value names.
23 LocalValues map[string]cty.Value
24}
25
26// NewModule constructs an empty module state for the given module address.
27func NewModule(addr addrs.ModuleInstance) *Module {
28 return &Module{
29 Addr: addr,
30 Resources: map[string]*Resource{},
31 OutputValues: map[string]*OutputValue{},
32 LocalValues: map[string]cty.Value{},
33 }
34}
35
36// Resource returns the state for the resource with the given address within
37// the receiving module state, or nil if the requested resource is not tracked
38// in the state.
39func (ms *Module) Resource(addr addrs.Resource) *Resource {
40 return ms.Resources[addr.String()]
41}
42
43// ResourceInstance returns the state for the resource instance with the given
44// address within the receiving module state, or nil if the requested instance
45// is not tracked in the state.
46func (ms *Module) ResourceInstance(addr addrs.ResourceInstance) *ResourceInstance {
47 rs := ms.Resource(addr.Resource)
48 if rs == nil {
49 return nil
50 }
51 return rs.Instance(addr.Key)
52}
53
54// SetResourceMeta updates the resource-level metadata for the resource
55// with the given address, creating the resource state for it if it doesn't
56// already exist.
57func (ms *Module) SetResourceMeta(addr addrs.Resource, eachMode EachMode, provider addrs.AbsProviderConfig) {
58 rs := ms.Resource(addr)
59 if rs == nil {
60 rs = &Resource{
61 Addr: addr,
62 Instances: map[addrs.InstanceKey]*ResourceInstance{},
63 }
64 ms.Resources[addr.String()] = rs
65 }
66
67 rs.EachMode = eachMode
68 rs.ProviderConfig = provider
69}
70
71// RemoveResource removes the entire state for the given resource, taking with
72// it any instances associated with the resource. This should generally be
73// called only for resource objects whose instances have all been destroyed.
74func (ms *Module) RemoveResource(addr addrs.Resource) {
75 delete(ms.Resources, addr.String())
76}
77
78// SetResourceInstanceCurrent saves the given instance object as the current
79// generation of the resource instance with the given address, simulataneously
80// updating the recorded provider configuration address, dependencies, and
81// resource EachMode.
82//
83// Any existing current instance object for the given resource is overwritten.
84// Set obj to nil to remove the primary generation object altogether. If there
85// are no deposed objects then the instance will be removed altogether.
86//
87// The provider address and "each mode" are resource-wide settings and so they
88// are updated for all other instances of the same resource as a side-effect of
89// this call.
90func (ms *Module) SetResourceInstanceCurrent(addr addrs.ResourceInstance, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) {
91 ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider)
92
93 rs := ms.Resource(addr.Resource)
94 is := rs.EnsureInstance(addr.Key)
95
96 is.Current = obj
97
98 if !is.HasObjects() {
99 // If we have no objects at all then we'll clean up.
100 delete(rs.Instances, addr.Key)
101 }
102 if rs.EachMode == NoEach && len(rs.Instances) == 0 {
103 // Also clean up if we only expect to have one instance anyway
104 // and there are none. We leave the resource behind if an each mode
105 // is active because an empty list or map of instances is a valid state.
106 delete(ms.Resources, addr.Resource.String())
107 }
108}
109
110// SetResourceInstanceDeposed saves the given instance object as a deposed
111// generation of the resource instance with the given address and deposed key.
112//
113// Call this method only for pre-existing deposed objects that already have
114// a known DeposedKey. For example, this method is useful if reloading objects
115// that were persisted to a state file. To mark the current object as deposed,
116// use DeposeResourceInstanceObject instead.
117//
118// The resource that contains the given instance must already exist in the
119// state, or this method will panic. Use Resource to check first if its
120// presence is not already guaranteed.
121//
122// Any existing current instance object for the given resource and deposed key
123// is overwritten. Set obj to nil to remove the deposed object altogether. If
124// the instance is left with no objects after this operation then it will
125// be removed from its containing resource altogether.
126func (ms *Module) SetResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) {
127 ms.SetResourceMeta(addr.Resource, eachModeForInstanceKey(addr.Key), provider)
128
129 rs := ms.Resource(addr.Resource)
130 is := rs.EnsureInstance(addr.Key)
131 if obj != nil {
132 is.Deposed[key] = obj
133 } else {
134 delete(is.Deposed, key)
135 }
136
137 if !is.HasObjects() {
138 // If we have no objects at all then we'll clean up.
139 delete(rs.Instances, addr.Key)
140 }
141 if rs.EachMode == NoEach && len(rs.Instances) == 0 {
142 // Also clean up if we only expect to have one instance anyway
143 // and there are none. We leave the resource behind if an each mode
144 // is active because an empty list or map of instances is a valid state.
145 delete(ms.Resources, addr.Resource.String())
146 }
147}
148
149// ForgetResourceInstanceAll removes the record of all objects associated with
150// the specified resource instance, if present. If not present, this is a no-op.
151func (ms *Module) ForgetResourceInstanceAll(addr addrs.ResourceInstance) {
152 rs := ms.Resource(addr.Resource)
153 if rs == nil {
154 return
155 }
156 delete(rs.Instances, addr.Key)
157
158 if rs.EachMode == NoEach && len(rs.Instances) == 0 {
159 // Also clean up if we only expect to have one instance anyway
160 // and there are none. We leave the resource behind if an each mode
161 // is active because an empty list or map of instances is a valid state.
162 delete(ms.Resources, addr.Resource.String())
163 }
164}
165
166// ForgetResourceInstanceDeposed removes the record of the deposed object with
167// the given address and key, if present. If not present, this is a no-op.
168func (ms *Module) ForgetResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey) {
169 rs := ms.Resource(addr.Resource)
170 if rs == nil {
171 return
172 }
173 is := rs.Instance(addr.Key)
174 if is == nil {
175 return
176 }
177 delete(is.Deposed, key)
178
179 if !is.HasObjects() {
180 // If we have no objects at all then we'll clean up.
181 delete(rs.Instances, addr.Key)
182 }
183 if rs.EachMode == NoEach && len(rs.Instances) == 0 {
184 // Also clean up if we only expect to have one instance anyway
185 // and there are none. We leave the resource behind if an each mode
186 // is active because an empty list or map of instances is a valid state.
187 delete(ms.Resources, addr.Resource.String())
188 }
189}
190
191// deposeResourceInstanceObject is the real implementation of
192// SyncState.DeposeResourceInstanceObject.
193func (ms *Module) deposeResourceInstanceObject(addr addrs.ResourceInstance, forceKey DeposedKey) DeposedKey {
194 is := ms.ResourceInstance(addr)
195 if is == nil {
196 return NotDeposed
197 }
198 return is.deposeCurrentObject(forceKey)
199}
200
201// maybeRestoreResourceInstanceDeposed is the real implementation of
202// SyncState.MaybeRestoreResourceInstanceDeposed.
203func (ms *Module) maybeRestoreResourceInstanceDeposed(addr addrs.ResourceInstance, key DeposedKey) bool {
204 rs := ms.Resource(addr.Resource)
205 if rs == nil {
206 return false
207 }
208 is := rs.Instance(addr.Key)
209 if is == nil {
210 return false
211 }
212 if is.Current != nil {
213 return false
214 }
215 if len(is.Deposed) == 0 {
216 return false
217 }
218 is.Current = is.Deposed[key]
219 delete(is.Deposed, key)
220 return true
221}
222
223// SetOutputValue writes an output value into the state, overwriting any
224// existing value of the same name.
225func (ms *Module) SetOutputValue(name string, value cty.Value, sensitive bool) *OutputValue {
226 os := &OutputValue{
227 Value: value,
228 Sensitive: sensitive,
229 }
230 ms.OutputValues[name] = os
231 return os
232}
233
234// RemoveOutputValue removes the output value of the given name from the state,
235// if it exists. This method is a no-op if there is no value of the given
236// name.
237func (ms *Module) RemoveOutputValue(name string) {
238 delete(ms.OutputValues, name)
239}
240
241// SetLocalValue writes a local value into the state, overwriting any
242// existing value of the same name.
243func (ms *Module) SetLocalValue(name string, value cty.Value) {
244 ms.LocalValues[name] = value
245}
246
247// RemoveLocalValue removes the local value of the given name from the state,
248// if it exists. This method is a no-op if there is no value of the given
249// name.
250func (ms *Module) RemoveLocalValue(name string) {
251 delete(ms.LocalValues, name)
252}
253
254// PruneResourceHusks is a specialized method that will remove any Resource
255// objects that do not contain any instances, even if they have an EachMode.
256//
257// You probably shouldn't call this! See the method of the same name on
258// type State for more information on what this is for and the rare situations
259// where it is safe to use.
260func (ms *Module) PruneResourceHusks() {
261 for _, rs := range ms.Resources {
262 if len(rs.Instances) == 0 {
263 ms.RemoveResource(rs.Addr)
264 }
265 }
266}
267
268// empty returns true if the receving module state is contributing nothing
269// to the state. In other words, it returns true if the module could be
270// removed from the state altogether without changing the meaning of the state.
271//
272// In practice a module containing no objects is the same as a non-existent
273// module, and so we can opportunistically clean up once a module becomes
274// empty on the assumption that it will be re-added if needed later.
275func (ms *Module) empty() bool {
276 if ms == nil {
277 return true
278 }
279
280 // This must be updated to cover any new collections added to Module
281 // in future.
282 return (len(ms.Resources) == 0 &&
283 len(ms.OutputValues) == 0 &&
284 len(ms.LocalValues) == 0)
285}
diff --git a/vendor/github.com/hashicorp/terraform/states/objectstatus_string.go b/vendor/github.com/hashicorp/terraform/states/objectstatus_string.go
new file mode 100644
index 0000000..96a6db2
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/objectstatus_string.go
@@ -0,0 +1,33 @@
1// Code generated by "stringer -type ObjectStatus"; DO NOT EDIT.
2
3package states
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[ObjectReady-82]
12 _ = x[ObjectTainted-84]
13 _ = x[ObjectPlanned-80]
14}
15
16const (
17 _ObjectStatus_name_0 = "ObjectPlanned"
18 _ObjectStatus_name_1 = "ObjectReady"
19 _ObjectStatus_name_2 = "ObjectTainted"
20)
21
22func (i ObjectStatus) String() string {
23 switch {
24 case i == 80:
25 return _ObjectStatus_name_0
26 case i == 82:
27 return _ObjectStatus_name_1
28 case i == 84:
29 return _ObjectStatus_name_2
30 default:
31 return "ObjectStatus(" + strconv.FormatInt(int64(i), 10) + ")"
32 }
33}
diff --git a/vendor/github.com/hashicorp/terraform/states/output_value.go b/vendor/github.com/hashicorp/terraform/states/output_value.go
new file mode 100644
index 0000000..d232b76
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/output_value.go
@@ -0,0 +1,14 @@
1package states
2
3import (
4 "github.com/zclconf/go-cty/cty"
5)
6
7// OutputValue represents the state of a particular output value.
8//
9// It is not valid to mutate an OutputValue object once it has been created.
10// Instead, create an entirely new OutputValue to replace the previous one.
11type OutputValue struct {
12 Value cty.Value
13 Sensitive bool
14}
diff --git a/vendor/github.com/hashicorp/terraform/states/resource.go b/vendor/github.com/hashicorp/terraform/states/resource.go
new file mode 100644
index 0000000..e2a2b85
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/resource.go
@@ -0,0 +1,239 @@
1package states
2
3import (
4 "fmt"
5 "math/rand"
6 "time"
7
8 "github.com/hashicorp/terraform/addrs"
9)
10
11// Resource represents the state of a resource.
12type Resource struct {
13 // Addr is the module-relative address for the resource this state object
14 // belongs to.
15 Addr addrs.Resource
16
17 // EachMode is the multi-instance mode currently in use for this resource,
18 // or NoEach if this is a single-instance resource. This dictates what
19 // type of value is returned when accessing this resource via expressions
20 // in the Terraform language.
21 EachMode EachMode
22
23 // Instances contains the potentially-multiple instances associated with
24 // this resource. This map can contain a mixture of different key types,
25 // but only the ones of InstanceKeyType are considered current.
26 Instances map[addrs.InstanceKey]*ResourceInstance
27
28 // ProviderConfig is the absolute address for the provider configuration that
29 // most recently managed this resource. This is used to connect a resource
30 // with a provider configuration when the resource configuration block is
31 // not available, such as if it has been removed from configuration
32 // altogether.
33 ProviderConfig addrs.AbsProviderConfig
34}
35
36// Instance returns the state for the instance with the given key, or nil
37// if no such instance is tracked within the state.
38func (rs *Resource) Instance(key addrs.InstanceKey) *ResourceInstance {
39 return rs.Instances[key]
40}
41
42// EnsureInstance returns the state for the instance with the given key,
43// creating a new empty state for it if one doesn't already exist.
44//
45// Because this may create and save a new state, it is considered to be
46// a write operation.
47func (rs *Resource) EnsureInstance(key addrs.InstanceKey) *ResourceInstance {
48 ret := rs.Instance(key)
49 if ret == nil {
50 ret = NewResourceInstance()
51 rs.Instances[key] = ret
52 }
53 return ret
54}
55
56// ResourceInstance represents the state of a particular instance of a resource.
57type ResourceInstance struct {
58 // Current, if non-nil, is the remote object that is currently represented
59 // by the corresponding resource instance.
60 Current *ResourceInstanceObjectSrc
61
62 // Deposed, if len > 0, contains any remote objects that were previously
63 // represented by the corresponding resource instance but have been
64 // replaced and are pending destruction due to the create_before_destroy
65 // lifecycle mode.
66 Deposed map[DeposedKey]*ResourceInstanceObjectSrc
67}
68
69// NewResourceInstance constructs and returns a new ResourceInstance, ready to
70// use.
71func NewResourceInstance() *ResourceInstance {
72 return &ResourceInstance{
73 Deposed: map[DeposedKey]*ResourceInstanceObjectSrc{},
74 }
75}
76
77// HasCurrent returns true if this resource instance has a "current"-generation
78// object. Most instances do, but this can briefly be false during a
79// create-before-destroy replace operation when the current has been deposed
80// but its replacement has not yet been created.
81func (i *ResourceInstance) HasCurrent() bool {
82 return i != nil && i.Current != nil
83}
84
85// HasDeposed returns true if this resource instance has a deposed object
86// with the given key.
87func (i *ResourceInstance) HasDeposed(key DeposedKey) bool {
88 return i != nil && i.Deposed[key] != nil
89}
90
91// HasAnyDeposed returns true if this resource instance has one or more
92// deposed objects.
93func (i *ResourceInstance) HasAnyDeposed() bool {
94 return i != nil && len(i.Deposed) > 0
95}
96
97// HasObjects returns true if this resource has any objects at all, whether
98// current or deposed.
99func (i *ResourceInstance) HasObjects() bool {
100 return i.Current != nil || len(i.Deposed) != 0
101}
102
103// deposeCurrentObject is part of the real implementation of
104// SyncState.DeposeResourceInstanceObject. The exported method uses a lock
105// to ensure that we can safely allocate an unused deposed key without
106// collision.
107func (i *ResourceInstance) deposeCurrentObject(forceKey DeposedKey) DeposedKey {
108 if !i.HasCurrent() {
109 return NotDeposed
110 }
111
112 key := forceKey
113 if key == NotDeposed {
114 key = i.findUnusedDeposedKey()
115 } else {
116 if _, exists := i.Deposed[key]; exists {
117 panic(fmt.Sprintf("forced key %s is already in use", forceKey))
118 }
119 }
120 i.Deposed[key] = i.Current
121 i.Current = nil
122 return key
123}
124
125// GetGeneration retrieves the object of the given generation from the
126// ResourceInstance, or returns nil if there is no such object.
127//
128// If the given generation is nil or invalid, this method will panic.
129func (i *ResourceInstance) GetGeneration(gen Generation) *ResourceInstanceObjectSrc {
130 if gen == CurrentGen {
131 return i.Current
132 }
133 if dk, ok := gen.(DeposedKey); ok {
134 return i.Deposed[dk]
135 }
136 if gen == nil {
137 panic(fmt.Sprintf("get with nil Generation"))
138 }
139 // Should never fall out here, since the above covers all possible
140 // Generation values.
141 panic(fmt.Sprintf("get invalid Generation %#v", gen))
142}
143
144// FindUnusedDeposedKey generates a unique DeposedKey that is guaranteed not to
145// already be in use for this instance at the time of the call.
146//
147// Note that the validity of this result may change if new deposed keys are
148// allocated before it is used. To avoid this risk, instead use the
149// DeposeResourceInstanceObject method on the SyncState wrapper type, which
150// allocates a key and uses it atomically.
151func (i *ResourceInstance) FindUnusedDeposedKey() DeposedKey {
152 return i.findUnusedDeposedKey()
153}
154
155// findUnusedDeposedKey generates a unique DeposedKey that is guaranteed not to
156// already be in use for this instance.
157func (i *ResourceInstance) findUnusedDeposedKey() DeposedKey {
158 for {
159 key := NewDeposedKey()
160 if _, exists := i.Deposed[key]; !exists {
161 return key
162 }
163 // Spin until we find a unique one. This shouldn't take long, because
164 // we have a 32-bit keyspace and there's rarely more than one deposed
165 // instance.
166 }
167}
168
169// EachMode specifies the multi-instance mode for a resource.
170type EachMode rune
171
172const (
173 NoEach EachMode = 0
174 EachList EachMode = 'L'
175 EachMap EachMode = 'M'
176)
177
178//go:generate stringer -type EachMode
179
180func eachModeForInstanceKey(key addrs.InstanceKey) EachMode {
181 switch key.(type) {
182 case addrs.IntKey:
183 return EachList
184 case addrs.StringKey:
185 return EachMap
186 default:
187 if key == addrs.NoKey {
188 return NoEach
189 }
190 panic(fmt.Sprintf("don't know an each mode for instance key %#v", key))
191 }
192}
193
194// DeposedKey is a 8-character hex string used to uniquely identify deposed
195// instance objects in the state.
196type DeposedKey string
197
198// NotDeposed is a special invalid value of DeposedKey that is used to represent
199// the absense of a deposed key. It must not be used as an actual deposed key.
200const NotDeposed = DeposedKey("")
201
202var deposedKeyRand = rand.New(rand.NewSource(time.Now().UnixNano()))
203
204// NewDeposedKey generates a pseudo-random deposed key. Because of the short
205// length of these keys, uniqueness is not a natural consequence and so the
206// caller should test to see if the generated key is already in use and generate
207// another if so, until a unique key is found.
208func NewDeposedKey() DeposedKey {
209 v := deposedKeyRand.Uint32()
210 return DeposedKey(fmt.Sprintf("%08x", v))
211}
212
213func (k DeposedKey) String() string {
214 return string(k)
215}
216
217func (k DeposedKey) GoString() string {
218 ks := string(k)
219 switch {
220 case ks == "":
221 return "states.NotDeposed"
222 default:
223 return fmt.Sprintf("states.DeposedKey(%s)", ks)
224 }
225}
226
227// Generation is a helper method to convert a DeposedKey into a Generation.
228// If the reciever is anything other than NotDeposed then the result is
229// just the same value as a Generation. If the receiver is NotDeposed then
230// the result is CurrentGen.
231func (k DeposedKey) Generation() Generation {
232 if k == NotDeposed {
233 return CurrentGen
234 }
235 return k
236}
237
238// generation is an implementation of Generation.
239func (k DeposedKey) generation() {}
diff --git a/vendor/github.com/hashicorp/terraform/states/state.go b/vendor/github.com/hashicorp/terraform/states/state.go
new file mode 100644
index 0000000..1f84235
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/state.go
@@ -0,0 +1,229 @@
1package states
2
3import (
4 "sort"
5
6 "github.com/zclconf/go-cty/cty"
7
8 "github.com/hashicorp/terraform/addrs"
9)
10
11// State is the top-level type of a Terraform state.
12//
13// A state should be mutated only via its accessor methods, to ensure that
14// invariants are preserved.
15//
16// Access to State and the nested values within it is not concurrency-safe,
17// so when accessing a State object concurrently it is the caller's
18// responsibility to ensure that only one write is in progress at a time
19// and that reads only occur when no write is in progress. The most common
20// way to acheive this is to wrap the State in a SyncState and use the
21// higher-level atomic operations supported by that type.
22type State struct {
23 // Modules contains the state for each module. The keys in this map are
24 // an implementation detail and must not be used by outside callers.
25 Modules map[string]*Module
26}
27
28// NewState constructs a minimal empty state, containing an empty root module.
29func NewState() *State {
30 modules := map[string]*Module{}
31 modules[addrs.RootModuleInstance.String()] = NewModule(addrs.RootModuleInstance)
32 return &State{
33 Modules: modules,
34 }
35}
36
37// BuildState is a helper -- primarily intended for tests -- to build a state
38// using imperative code against the StateSync type while still acting as
39// an expression of type *State to assign into a containing struct.
40func BuildState(cb func(*SyncState)) *State {
41 s := NewState()
42 cb(s.SyncWrapper())
43 return s
44}
45
46// Empty returns true if there are no resources or populated output values
47// in the receiver. In other words, if this state could be safely replaced
48// with the return value of NewState and be functionally equivalent.
49func (s *State) Empty() bool {
50 if s == nil {
51 return true
52 }
53 for _, ms := range s.Modules {
54 if len(ms.Resources) != 0 {
55 return false
56 }
57 if len(ms.OutputValues) != 0 {
58 return false
59 }
60 }
61 return true
62}
63
64// Module returns the state for the module with the given address, or nil if
65// the requested module is not tracked in the state.
66func (s *State) Module(addr addrs.ModuleInstance) *Module {
67 if s == nil {
68 panic("State.Module on nil *State")
69 }
70 return s.Modules[addr.String()]
71}
72
73// RemoveModule removes the module with the given address from the state,
74// unless it is the root module. The root module cannot be deleted, and so
75// this method will panic if that is attempted.
76//
77// Removing a module implicitly discards all of the resources, outputs and
78// local values within it, and so this should usually be done only for empty
79// modules. For callers accessing the state through a SyncState wrapper, modules
80// are automatically pruned if they are empty after one of their contained
81// elements is removed.
82func (s *State) RemoveModule(addr addrs.ModuleInstance) {
83 if addr.IsRoot() {
84 panic("attempted to remove root module")
85 }
86
87 delete(s.Modules, addr.String())
88}
89
90// RootModule is a convenient alias for Module(addrs.RootModuleInstance).
91func (s *State) RootModule() *Module {
92 if s == nil {
93 panic("RootModule called on nil State")
94 }
95 return s.Modules[addrs.RootModuleInstance.String()]
96}
97
98// EnsureModule returns the state for the module with the given address,
99// creating and adding a new one if necessary.
100//
101// Since this might modify the state to add a new instance, it is considered
102// to be a write operation.
103func (s *State) EnsureModule(addr addrs.ModuleInstance) *Module {
104 ms := s.Module(addr)
105 if ms == nil {
106 ms = NewModule(addr)
107 s.Modules[addr.String()] = ms
108 }
109 return ms
110}
111
112// HasResources returns true if there is at least one resource (of any mode)
113// present in the receiving state.
114func (s *State) HasResources() bool {
115 if s == nil {
116 return false
117 }
118 for _, ms := range s.Modules {
119 if len(ms.Resources) > 0 {
120 return true
121 }
122 }
123 return false
124}
125
126// Resource returns the state for the resource with the given address, or nil
127// if no such resource is tracked in the state.
128func (s *State) Resource(addr addrs.AbsResource) *Resource {
129 ms := s.Module(addr.Module)
130 if ms == nil {
131 return nil
132 }
133 return ms.Resource(addr.Resource)
134}
135
136// ResourceInstance returns the state for the resource instance with the given
137// address, or nil if no such resource is tracked in the state.
138func (s *State) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstance {
139 if s == nil {
140 panic("State.ResourceInstance on nil *State")
141 }
142 ms := s.Module(addr.Module)
143 if ms == nil {
144 return nil
145 }
146 return ms.ResourceInstance(addr.Resource)
147}
148
149// OutputValue returns the state for the output value with the given address,
150// or nil if no such output value is tracked in the state.
151func (s *State) OutputValue(addr addrs.AbsOutputValue) *OutputValue {
152 ms := s.Module(addr.Module)
153 if ms == nil {
154 return nil
155 }
156 return ms.OutputValues[addr.OutputValue.Name]
157}
158
159// LocalValue returns the value of the named local value with the given address,
160// or cty.NilVal if no such value is tracked in the state.
161func (s *State) LocalValue(addr addrs.AbsLocalValue) cty.Value {
162 ms := s.Module(addr.Module)
163 if ms == nil {
164 return cty.NilVal
165 }
166 return ms.LocalValues[addr.LocalValue.Name]
167}
168
169// ProviderAddrs returns a list of all of the provider configuration addresses
170// referenced throughout the receiving state.
171//
172// The result is de-duplicated so that each distinct address appears only once.
173func (s *State) ProviderAddrs() []addrs.AbsProviderConfig {
174 if s == nil {
175 return nil
176 }
177
178 m := map[string]addrs.AbsProviderConfig{}
179 for _, ms := range s.Modules {
180 for _, rc := range ms.Resources {
181 m[rc.ProviderConfig.String()] = rc.ProviderConfig
182 }
183 }
184 if len(m) == 0 {
185 return nil
186 }
187
188 // This is mainly just so we'll get stable results for testing purposes.
189 keys := make([]string, 0, len(m))
190 for k := range m {
191 keys = append(keys, k)
192 }
193 sort.Strings(keys)
194
195 ret := make([]addrs.AbsProviderConfig, len(keys))
196 for i, key := range keys {
197 ret[i] = m[key]
198 }
199
200 return ret
201}
202
203// PruneResourceHusks is a specialized method that will remove any Resource
204// objects that do not contain any instances, even if they have an EachMode.
205//
206// This should generally be used only after a "terraform destroy" operation,
207// to finalize the cleanup of the state. It is not correct to use this after
208// other operations because if a resource has "count = 0" or "for_each" over
209// an empty collection then we want to retain it in the state so that references
210// to it, particularly in "strange" contexts like "terraform console", can be
211// properly resolved.
212//
213// This method MUST NOT be called concurrently with other readers and writers
214// of the receiving state.
215func (s *State) PruneResourceHusks() {
216 for _, m := range s.Modules {
217 m.PruneResourceHusks()
218 if len(m.Resources) == 0 && !m.Addr.IsRoot() {
219 s.RemoveModule(m.Addr)
220 }
221 }
222}
223
224// SyncWrapper returns a SyncState object wrapping the receiver.
225func (s *State) SyncWrapper() *SyncState {
226 return &SyncState{
227 state: s,
228 }
229}
diff --git a/vendor/github.com/hashicorp/terraform/states/state_deepcopy.go b/vendor/github.com/hashicorp/terraform/states/state_deepcopy.go
new file mode 100644
index 0000000..ea717d0
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/state_deepcopy.go
@@ -0,0 +1,218 @@
1package states
2
3import (
4 "github.com/hashicorp/terraform/addrs"
5 "github.com/zclconf/go-cty/cty"
6)
7
8// Taking deep copies of states is an important operation because state is
9// otherwise a mutable data structure that is challenging to share across
10// many separate callers. It is important that the DeepCopy implementations
11// in this file comprehensively copy all parts of the state data structure
12// that could be mutated via pointers.
13
14// DeepCopy returns a new state that contains equivalent data to the reciever
15// but shares no backing memory in common.
16//
17// As with all methods on State, this method is not safe to use concurrently
18// with writing to any portion of the recieving data structure. It is the
19// caller's responsibility to ensure mutual exclusion for the duration of the
20// operation, but may then freely modify the receiver and the returned copy
21// independently once this method returns.
22func (s *State) DeepCopy() *State {
23 if s == nil {
24 return nil
25 }
26
27 modules := make(map[string]*Module, len(s.Modules))
28 for k, m := range s.Modules {
29 modules[k] = m.DeepCopy()
30 }
31 return &State{
32 Modules: modules,
33 }
34}
35
36// DeepCopy returns a new module state that contains equivalent data to the
37// receiver but shares no backing memory in common.
38//
39// As with all methods on Module, this method is not safe to use concurrently
40// with writing to any portion of the recieving data structure. It is the
41// caller's responsibility to ensure mutual exclusion for the duration of the
42// operation, but may then freely modify the receiver and the returned copy
43// independently once this method returns.
44func (ms *Module) DeepCopy() *Module {
45 if ms == nil {
46 return nil
47 }
48
49 resources := make(map[string]*Resource, len(ms.Resources))
50 for k, r := range ms.Resources {
51 resources[k] = r.DeepCopy()
52 }
53 outputValues := make(map[string]*OutputValue, len(ms.OutputValues))
54 for k, v := range ms.OutputValues {
55 outputValues[k] = v.DeepCopy()
56 }
57 localValues := make(map[string]cty.Value, len(ms.LocalValues))
58 for k, v := range ms.LocalValues {
59 // cty.Value is immutable, so we don't need to copy these.
60 localValues[k] = v
61 }
62
63 return &Module{
64 Addr: ms.Addr, // technically mutable, but immutable by convention
65 Resources: resources,
66 OutputValues: outputValues,
67 LocalValues: localValues,
68 }
69}
70
71// DeepCopy returns a new resource state that contains equivalent data to the
72// receiver but shares no backing memory in common.
73//
74// As with all methods on Resource, this method is not safe to use concurrently
75// with writing to any portion of the recieving data structure. It is the
76// caller's responsibility to ensure mutual exclusion for the duration of the
77// operation, but may then freely modify the receiver and the returned copy
78// independently once this method returns.
79func (rs *Resource) DeepCopy() *Resource {
80 if rs == nil {
81 return nil
82 }
83
84 instances := make(map[addrs.InstanceKey]*ResourceInstance, len(rs.Instances))
85 for k, i := range rs.Instances {
86 instances[k] = i.DeepCopy()
87 }
88
89 return &Resource{
90 Addr: rs.Addr,
91 EachMode: rs.EachMode,
92 Instances: instances,
93 ProviderConfig: rs.ProviderConfig, // technically mutable, but immutable by convention
94 }
95}
96
97// DeepCopy returns a new resource instance state that contains equivalent data
98// to the receiver but shares no backing memory in common.
99//
100// As with all methods on ResourceInstance, this method is not safe to use
101// concurrently with writing to any portion of the recieving data structure. It
102// is the caller's responsibility to ensure mutual exclusion for the duration
103// of the operation, but may then freely modify the receiver and the returned
104// copy independently once this method returns.
105func (is *ResourceInstance) DeepCopy() *ResourceInstance {
106 if is == nil {
107 return nil
108 }
109
110 deposed := make(map[DeposedKey]*ResourceInstanceObjectSrc, len(is.Deposed))
111 for k, obj := range is.Deposed {
112 deposed[k] = obj.DeepCopy()
113 }
114
115 return &ResourceInstance{
116 Current: is.Current.DeepCopy(),
117 Deposed: deposed,
118 }
119}
120
121// DeepCopy returns a new resource instance object that contains equivalent data
122// to the receiver but shares no backing memory in common.
123//
124// As with all methods on ResourceInstanceObjectSrc, this method is not safe to
125// use concurrently with writing to any portion of the recieving data structure.
126// It is the caller's responsibility to ensure mutual exclusion for the duration
127// of the operation, but may then freely modify the receiver and the returned
128// copy independently once this method returns.
129func (obj *ResourceInstanceObjectSrc) DeepCopy() *ResourceInstanceObjectSrc {
130 if obj == nil {
131 return nil
132 }
133
134 var attrsFlat map[string]string
135 if obj.AttrsFlat != nil {
136 attrsFlat = make(map[string]string, len(obj.AttrsFlat))
137 for k, v := range obj.AttrsFlat {
138 attrsFlat[k] = v
139 }
140 }
141
142 var attrsJSON []byte
143 if obj.AttrsJSON != nil {
144 attrsJSON = make([]byte, len(obj.AttrsJSON))
145 copy(attrsJSON, obj.AttrsJSON)
146 }
147
148 var private []byte
149 if obj.Private != nil {
150 private := make([]byte, len(obj.Private))
151 copy(private, obj.Private)
152 }
153
154 // Some addrs.Referencable implementations are technically mutable, but
155 // we treat them as immutable by convention and so we don't deep-copy here.
156 dependencies := make([]addrs.Referenceable, len(obj.Dependencies))
157 copy(dependencies, obj.Dependencies)
158
159 return &ResourceInstanceObjectSrc{
160 Status: obj.Status,
161 SchemaVersion: obj.SchemaVersion,
162 Private: private,
163 AttrsFlat: attrsFlat,
164 AttrsJSON: attrsJSON,
165 Dependencies: dependencies,
166 }
167}
168
169// DeepCopy returns a new resource instance object that contains equivalent data
170// to the receiver but shares no backing memory in common.
171//
172// As with all methods on ResourceInstanceObject, this method is not safe to use
173// concurrently with writing to any portion of the recieving data structure. It
174// is the caller's responsibility to ensure mutual exclusion for the duration
175// of the operation, but may then freely modify the receiver and the returned
176// copy independently once this method returns.
177func (obj *ResourceInstanceObject) DeepCopy() *ResourceInstanceObject {
178 if obj == nil {
179 return nil
180 }
181
182 var private []byte
183 if obj.Private != nil {
184 private := make([]byte, len(obj.Private))
185 copy(private, obj.Private)
186 }
187
188 // Some addrs.Referencable implementations are technically mutable, but
189 // we treat them as immutable by convention and so we don't deep-copy here.
190 dependencies := make([]addrs.Referenceable, len(obj.Dependencies))
191 copy(dependencies, obj.Dependencies)
192
193 return &ResourceInstanceObject{
194 Value: obj.Value,
195 Status: obj.Status,
196 Private: private,
197 Dependencies: dependencies,
198 }
199}
200
201// DeepCopy returns a new output value state that contains equivalent data
202// to the receiver but shares no backing memory in common.
203//
204// As with all methods on OutputValue, this method is not safe to use
205// concurrently with writing to any portion of the recieving data structure. It
206// is the caller's responsibility to ensure mutual exclusion for the duration
207// of the operation, but may then freely modify the receiver and the returned
208// copy independently once this method returns.
209func (os *OutputValue) DeepCopy() *OutputValue {
210 if os == nil {
211 return nil
212 }
213
214 return &OutputValue{
215 Value: os.Value,
216 Sensitive: os.Sensitive,
217 }
218}
diff --git a/vendor/github.com/hashicorp/terraform/states/state_equal.go b/vendor/github.com/hashicorp/terraform/states/state_equal.go
new file mode 100644
index 0000000..ea20967
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/state_equal.go
@@ -0,0 +1,18 @@
1package states
2
3import (
4 "reflect"
5)
6
7// Equal returns true if the receiver is functionally equivalent to other,
8// including any ephemeral portions of the state that would not be included
9// if the state were saved to files.
10//
11// To test only the persistent portions of two states for equality, instead
12// use statefile.StatesMarshalEqual.
13func (s *State) Equal(other *State) bool {
14 // For the moment this is sufficient, but we may need to do something
15 // more elaborate in future if we have any portions of state that require
16 // more sophisticated comparisons.
17 return reflect.DeepEqual(s, other)
18}
diff --git a/vendor/github.com/hashicorp/terraform/states/state_string.go b/vendor/github.com/hashicorp/terraform/states/state_string.go
new file mode 100644
index 0000000..bca4581
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/state_string.go
@@ -0,0 +1,279 @@
1package states
2
3import (
4 "bufio"
5 "bytes"
6 "encoding/json"
7 "fmt"
8 "sort"
9 "strings"
10
11 ctyjson "github.com/zclconf/go-cty/cty/json"
12
13 "github.com/hashicorp/terraform/addrs"
14 "github.com/hashicorp/terraform/config/hcl2shim"
15)
16
17// String returns a rather-odd string representation of the entire state.
18//
19// This is intended to match the behavior of the older terraform.State.String
20// method that is used in lots of existing tests. It should not be used in
21// new tests: instead, use "cmp" to directly compare the state data structures
22// and print out a diff if they do not match.
23//
24// This method should never be used in non-test code, whether directly by call
25// or indirectly via a %s or %q verb in package fmt.
26func (s *State) String() string {
27 if s == nil {
28 return "<nil>"
29 }
30
31 // sort the modules by name for consistent output
32 modules := make([]string, 0, len(s.Modules))
33 for m := range s.Modules {
34 modules = append(modules, m)
35 }
36 sort.Strings(modules)
37
38 var buf bytes.Buffer
39 for _, name := range modules {
40 m := s.Modules[name]
41 mStr := m.testString()
42
43 // If we're the root module, we just write the output directly.
44 if m.Addr.IsRoot() {
45 buf.WriteString(mStr + "\n")
46 continue
47 }
48
49 // We need to build out a string that resembles the not-quite-standard
50 // format that terraform.State.String used to use, where there's a
51 // "module." prefix but then just a chain of all of the module names
52 // without any further "module." portions.
53 buf.WriteString("module")
54 for _, step := range m.Addr {
55 buf.WriteByte('.')
56 buf.WriteString(step.Name)
57 if step.InstanceKey != addrs.NoKey {
58 buf.WriteByte('[')
59 buf.WriteString(step.InstanceKey.String())
60 buf.WriteByte(']')
61 }
62 }
63 buf.WriteString(":\n")
64
65 s := bufio.NewScanner(strings.NewReader(mStr))
66 for s.Scan() {
67 text := s.Text()
68 if text != "" {
69 text = " " + text
70 }
71
72 buf.WriteString(fmt.Sprintf("%s\n", text))
73 }
74 }
75
76 return strings.TrimSpace(buf.String())
77}
78
79// testString is used to produce part of the output of State.String. It should
80// never be used directly.
81func (m *Module) testString() string {
82 var buf bytes.Buffer
83
84 if len(m.Resources) == 0 {
85 buf.WriteString("<no state>")
86 }
87
88 // We use AbsResourceInstance here, even though everything belongs to
89 // the same module, just because we have a sorting behavior defined
90 // for those but not for just ResourceInstance.
91 addrsOrder := make([]addrs.AbsResourceInstance, 0, len(m.Resources))
92 for _, rs := range m.Resources {
93 for ik := range rs.Instances {
94 addrsOrder = append(addrsOrder, rs.Addr.Instance(ik).Absolute(addrs.RootModuleInstance))
95 }
96 }
97
98 sort.Slice(addrsOrder, func(i, j int) bool {
99 return addrsOrder[i].Less(addrsOrder[j])
100 })
101
102 for _, fakeAbsAddr := range addrsOrder {
103 addr := fakeAbsAddr.Resource
104 rs := m.Resource(addr.ContainingResource())
105 is := m.ResourceInstance(addr)
106
107 // Here we need to fake up a legacy-style address as the old state
108 // types would've used, since that's what our tests against those
109 // old types expect. The significant difference is that instancekey
110 // is dot-separated rather than using index brackets.
111 k := addr.ContainingResource().String()
112 if addr.Key != addrs.NoKey {
113 switch tk := addr.Key.(type) {
114 case addrs.IntKey:
115 k = fmt.Sprintf("%s.%d", k, tk)
116 default:
117 // No other key types existed for the legacy types, so we
118 // can do whatever we want here. We'll just use our standard
119 // syntax for these.
120 k = k + tk.String()
121 }
122 }
123
124 id := LegacyInstanceObjectID(is.Current)
125
126 taintStr := ""
127 if is.Current != nil && is.Current.Status == ObjectTainted {
128 taintStr = " (tainted)"
129 }
130
131 deposedStr := ""
132 if len(is.Deposed) > 0 {
133 deposedStr = fmt.Sprintf(" (%d deposed)", len(is.Deposed))
134 }
135
136 buf.WriteString(fmt.Sprintf("%s:%s%s\n", k, taintStr, deposedStr))
137 buf.WriteString(fmt.Sprintf(" ID = %s\n", id))
138 buf.WriteString(fmt.Sprintf(" provider = %s\n", rs.ProviderConfig.String()))
139
140 // Attributes were a flatmap before, but are not anymore. To preserve
141 // our old output as closely as possible we need to do a conversion
142 // to flatmap. Normally we'd want to do this with schema for
143 // accuracy, but for our purposes here it only needs to be approximate.
144 // This should produce an identical result for most cases, though
145 // in particular will differ in a few cases:
146 // - The keys used for elements in a set will be different
147 // - Values for attributes of type cty.DynamicPseudoType will be
148 // misinterpreted (but these weren't possible in old world anyway)
149 var attributes map[string]string
150 if obj := is.Current; obj != nil {
151 switch {
152 case obj.AttrsFlat != nil:
153 // Easy (but increasingly unlikely) case: the state hasn't
154 // actually been upgraded to the new form yet.
155 attributes = obj.AttrsFlat
156 case obj.AttrsJSON != nil:
157 ty, err := ctyjson.ImpliedType(obj.AttrsJSON)
158 if err == nil {
159 val, err := ctyjson.Unmarshal(obj.AttrsJSON, ty)
160 if err == nil {
161 attributes = hcl2shim.FlatmapValueFromHCL2(val)
162 }
163 }
164 }
165 }
166 attrKeys := make([]string, 0, len(attributes))
167 for ak, val := range attributes {
168 if ak == "id" {
169 continue
170 }
171
172 // don't show empty containers in the output
173 if val == "0" && (strings.HasSuffix(ak, ".#") || strings.HasSuffix(ak, ".%")) {
174 continue
175 }
176
177 attrKeys = append(attrKeys, ak)
178 }
179
180 sort.Strings(attrKeys)
181
182 for _, ak := range attrKeys {
183 av := attributes[ak]
184 buf.WriteString(fmt.Sprintf(" %s = %s\n", ak, av))
185 }
186
187 // CAUTION: Since deposed keys are now random strings instead of
188 // incrementing integers, this result will not be deterministic
189 // if there is more than one deposed object.
190 i := 1
191 for _, t := range is.Deposed {
192 id := LegacyInstanceObjectID(t)
193 taintStr := ""
194 if t.Status == ObjectTainted {
195 taintStr = " (tainted)"
196 }
197 buf.WriteString(fmt.Sprintf(" Deposed ID %d = %s%s\n", i, id, taintStr))
198 i++
199 }
200
201 if obj := is.Current; obj != nil && len(obj.Dependencies) > 0 {
202 buf.WriteString(fmt.Sprintf("\n Dependencies:\n"))
203 for _, dep := range obj.Dependencies {
204 buf.WriteString(fmt.Sprintf(" %s\n", dep.String()))
205 }
206 }
207 }
208
209 if len(m.OutputValues) > 0 {
210 buf.WriteString("\nOutputs:\n\n")
211
212 ks := make([]string, 0, len(m.OutputValues))
213 for k := range m.OutputValues {
214 ks = append(ks, k)
215 }
216 sort.Strings(ks)
217
218 for _, k := range ks {
219 v := m.OutputValues[k]
220 lv := hcl2shim.ConfigValueFromHCL2(v.Value)
221 switch vTyped := lv.(type) {
222 case string:
223 buf.WriteString(fmt.Sprintf("%s = %s\n", k, vTyped))
224 case []interface{}:
225 buf.WriteString(fmt.Sprintf("%s = %s\n", k, vTyped))
226 case map[string]interface{}:
227 var mapKeys []string
228 for key := range vTyped {
229 mapKeys = append(mapKeys, key)
230 }
231 sort.Strings(mapKeys)
232
233 var mapBuf bytes.Buffer
234 mapBuf.WriteString("{")
235 for _, key := range mapKeys {
236 mapBuf.WriteString(fmt.Sprintf("%s:%s ", key, vTyped[key]))
237 }
238 mapBuf.WriteString("}")
239
240 buf.WriteString(fmt.Sprintf("%s = %s\n", k, mapBuf.String()))
241 default:
242 buf.WriteString(fmt.Sprintf("%s = %#v\n", k, lv))
243 }
244 }
245 }
246
247 return buf.String()
248}
249
250// LegacyInstanceObjectID is a helper for extracting an object id value from
251// an instance object in a way that approximates how we used to do this
252// for the old state types. ID is no longer first-class, so this is preserved
253// only for compatibility with old tests that include the id as part of their
254// expected value.
255func LegacyInstanceObjectID(obj *ResourceInstanceObjectSrc) string {
256 if obj == nil {
257 return "<not created>"
258 }
259
260 if obj.AttrsJSON != nil {
261 type WithID struct {
262 ID string `json:"id"`
263 }
264 var withID WithID
265 err := json.Unmarshal(obj.AttrsJSON, &withID)
266 if err == nil {
267 return withID.ID
268 }
269 } else if obj.AttrsFlat != nil {
270 if flatID, exists := obj.AttrsFlat["id"]; exists {
271 return flatID
272 }
273 }
274
275 // For resource types created after we removed id as special there may
276 // not actually be one at all. This is okay because older tests won't
277 // encounter this, and new tests shouldn't be using ids.
278 return "<none>"
279}
diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/diagnostics.go b/vendor/github.com/hashicorp/terraform/states/statefile/diagnostics.go
new file mode 100644
index 0000000..a6d88ec
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/statefile/diagnostics.go
@@ -0,0 +1,62 @@
1package statefile
2
3import (
4 "encoding/json"
5 "fmt"
6
7 "github.com/hashicorp/terraform/tfdiags"
8)
9
10const invalidFormat = "Invalid state file format"
11
12// jsonUnmarshalDiags is a helper that translates errors returned from
13// json.Unmarshal into hopefully-more-helpful diagnostics messages.
14func jsonUnmarshalDiags(err error) tfdiags.Diagnostics {
15 var diags tfdiags.Diagnostics
16 if err == nil {
17 return diags
18 }
19
20 switch tErr := err.(type) {
21 case *json.SyntaxError:
22 // We've usually already successfully parsed a source file as JSON at
23 // least once before we'd use jsonUnmarshalDiags with it (to sniff
24 // the version number) so this particular error should not appear much
25 // in practice.
26 diags = diags.Append(tfdiags.Sourceless(
27 tfdiags.Error,
28 invalidFormat,
29 fmt.Sprintf("The state file could not be parsed as JSON: syntax error at byte offset %d.", tErr.Offset),
30 ))
31 case *json.UnmarshalTypeError:
32 // This is likely to be the most common area, describing a
33 // non-conformance between the file and the expected file format
34 // at a semantic level.
35 if tErr.Field != "" {
36 diags = diags.Append(tfdiags.Sourceless(
37 tfdiags.Error,
38 invalidFormat,
39 fmt.Sprintf("The state file field %q has invalid value %s", tErr.Field, tErr.Value),
40 ))
41 break
42 } else {
43 // Without a field name, we can't really say anything helpful.
44 diags = diags.Append(tfdiags.Sourceless(
45 tfdiags.Error,
46 invalidFormat,
47 "The state file does not conform to the expected JSON data structure.",
48 ))
49 }
50 default:
51 // Fallback for all other types of errors. This can happen only for
52 // custom UnmarshalJSON implementations, so should be encountered
53 // only rarely.
54 diags = diags.Append(tfdiags.Sourceless(
55 tfdiags.Error,
56 invalidFormat,
57 fmt.Sprintf("The state file does not conform to the expected JSON data structure: %s.", err.Error()),
58 ))
59 }
60
61 return diags
62}
diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/doc.go b/vendor/github.com/hashicorp/terraform/states/statefile/doc.go
new file mode 100644
index 0000000..625d0cf
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/statefile/doc.go
@@ -0,0 +1,3 @@
1// Package statefile deals with the file format used to serialize states for
2// persistent storage and then deserialize them into memory again later.
3package statefile
diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/file.go b/vendor/github.com/hashicorp/terraform/states/statefile/file.go
new file mode 100644
index 0000000..6e20240
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/statefile/file.go
@@ -0,0 +1,62 @@
1package statefile
2
3import (
4 version "github.com/hashicorp/go-version"
5
6 "github.com/hashicorp/terraform/states"
7 tfversion "github.com/hashicorp/terraform/version"
8)
9
10// File is the in-memory representation of a state file. It includes the state
11// itself along with various metadata used to track changing state files for
12// the same configuration over time.
13type File struct {
14 // TerraformVersion is the version of Terraform that wrote this state file.
15 TerraformVersion *version.Version
16
17 // Serial is incremented on any operation that modifies
18 // the State file. It is used to detect potentially conflicting
19 // updates.
20 Serial uint64
21
22 // Lineage is set when a new, blank state file is created and then
23 // never updated. This allows us to determine whether the serials
24 // of two states can be meaningfully compared.
25 // Apart from the guarantee that collisions between two lineages
26 // are very unlikely, this value is opaque and external callers
27 // should only compare lineage strings byte-for-byte for equality.
28 Lineage string
29
30 // State is the actual state represented by this file.
31 State *states.State
32}
33
34func New(state *states.State, lineage string, serial uint64) *File {
35 // To make life easier on callers, we'll accept a nil state here and just
36 // allocate an empty one, which is required for this file to be successfully
37 // written out.
38 if state == nil {
39 state = states.NewState()
40 }
41
42 return &File{
43 TerraformVersion: tfversion.SemVer,
44 State: state,
45 Lineage: lineage,
46 Serial: serial,
47 }
48}
49
50// DeepCopy is a convenience method to create a new File object whose state
51// is a deep copy of the receiver's, as implemented by states.State.DeepCopy.
52func (f *File) DeepCopy() *File {
53 if f == nil {
54 return nil
55 }
56 return &File{
57 TerraformVersion: f.TerraformVersion,
58 Serial: f.Serial,
59 Lineage: f.Lineage,
60 State: f.State.DeepCopy(),
61 }
62}
diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/marshal_equal.go b/vendor/github.com/hashicorp/terraform/states/statefile/marshal_equal.go
new file mode 100644
index 0000000..4948b39
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/statefile/marshal_equal.go
@@ -0,0 +1,40 @@
1package statefile
2
3import (
4 "bytes"
5
6 "github.com/hashicorp/terraform/states"
7)
8
9// StatesMarshalEqual returns true if and only if the two given states have
10// an identical (byte-for-byte) statefile representation.
11//
12// This function compares only the portions of the state that are persisted
13// in state files, so for example it will not return false if the only
14// differences between the two states are local values or descendent module
15// outputs.
16func StatesMarshalEqual(a, b *states.State) bool {
17 var aBuf bytes.Buffer
18 var bBuf bytes.Buffer
19
20 // nil states are not valid states, and so they can never martial equal.
21 if a == nil || b == nil {
22 return false
23 }
24
25 // We write here some temporary files that have no header information
26 // populated, thus ensuring that we're only comparing the state itself
27 // and not any metadata.
28 err := Write(&File{State: a}, &aBuf)
29 if err != nil {
30 // Should never happen, because we're writing to an in-memory buffer
31 panic(err)
32 }
33 err = Write(&File{State: b}, &bBuf)
34 if err != nil {
35 // Should never happen, because we're writing to an in-memory buffer
36 panic(err)
37 }
38
39 return bytes.Equal(aBuf.Bytes(), bBuf.Bytes())
40}
diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/read.go b/vendor/github.com/hashicorp/terraform/states/statefile/read.go
new file mode 100644
index 0000000..d691c02
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/statefile/read.go
@@ -0,0 +1,209 @@
1package statefile
2
3import (
4 "encoding/json"
5 "errors"
6 "fmt"
7 "io"
8 "io/ioutil"
9 "os"
10
11 version "github.com/hashicorp/go-version"
12
13 "github.com/hashicorp/terraform/tfdiags"
14 tfversion "github.com/hashicorp/terraform/version"
15)
16
17// ErrNoState is returned by ReadState when the state file is empty.
18var ErrNoState = errors.New("no state")
19
20// Read reads a state from the given reader.
21//
22// Legacy state format versions 1 through 3 are supported, but the result will
23// contain object attributes in the deprecated "flatmap" format and so must
24// be upgraded by the caller before use.
25//
26// If the state file is empty, the special error value ErrNoState is returned.
27// Otherwise, the returned error might be a wrapper around tfdiags.Diagnostics
28// potentially describing multiple errors.
29func Read(r io.Reader) (*File, error) {
30 // Some callers provide us a "typed nil" *os.File here, which would
31 // cause us to panic below if we tried to use it.
32 if f, ok := r.(*os.File); ok && f == nil {
33 return nil, ErrNoState
34 }
35
36 var diags tfdiags.Diagnostics
37
38 // We actually just buffer the whole thing in memory, because states are
39 // generally not huge and we need to do be able to sniff for a version
40 // number before full parsing.
41 src, err := ioutil.ReadAll(r)
42 if err != nil {
43 diags = diags.Append(tfdiags.Sourceless(
44 tfdiags.Error,
45 "Failed to read state file",
46 fmt.Sprintf("The state file could not be read: %s", err),
47 ))
48 return nil, diags.Err()
49 }
50
51 if len(src) == 0 {
52 return nil, ErrNoState
53 }
54
55 state, diags := readState(src)
56 if diags.HasErrors() {
57 return nil, diags.Err()
58 }
59
60 if state == nil {
61 // Should never happen
62 panic("readState returned nil state with no errors")
63 }
64
65 if state.TerraformVersion != nil && state.TerraformVersion.GreaterThan(tfversion.SemVer) {
66 return state, fmt.Errorf(
67 "state snapshot was created by Terraform v%s, which is newer than current v%s; upgrade to Terraform v%s or greater to work with this state",
68 state.TerraformVersion,
69 tfversion.SemVer,
70 state.TerraformVersion,
71 )
72 }
73
74 return state, diags.Err()
75}
76
77func readState(src []byte) (*File, tfdiags.Diagnostics) {
78 var diags tfdiags.Diagnostics
79
80 if looksLikeVersion0(src) {
81 diags = diags.Append(tfdiags.Sourceless(
82 tfdiags.Error,
83 unsupportedFormat,
84 "The state is stored in a legacy binary format that is not supported since Terraform v0.7. To continue, first upgrade the state using Terraform 0.6.16 or earlier.",
85 ))
86 return nil, diags
87 }
88
89 version, versionDiags := sniffJSONStateVersion(src)
90 diags = diags.Append(versionDiags)
91 if versionDiags.HasErrors() {
92 return nil, diags
93 }
94
95 switch version {
96 case 0:
97 diags = diags.Append(tfdiags.Sourceless(
98 tfdiags.Error,
99 unsupportedFormat,
100 "The state file uses JSON syntax but has a version number of zero. There was never a JSON-based state format zero, so this state file is invalid and cannot be processed.",
101 ))
102 return nil, diags
103 case 1:
104 return readStateV1(src)
105 case 2:
106 return readStateV2(src)
107 case 3:
108 return readStateV3(src)
109 case 4:
110 return readStateV4(src)
111 default:
112 thisVersion := tfversion.SemVer.String()
113 creatingVersion := sniffJSONStateTerraformVersion(src)
114 switch {
115 case creatingVersion != "":
116 diags = diags.Append(tfdiags.Sourceless(
117 tfdiags.Error,
118 unsupportedFormat,
119 fmt.Sprintf("The state file uses format version %d, which is not supported by Terraform %s. This state file was created by Terraform %s.", version, thisVersion, creatingVersion),
120 ))
121 default:
122 diags = diags.Append(tfdiags.Sourceless(
123 tfdiags.Error,
124 unsupportedFormat,
125 fmt.Sprintf("The state file uses format version %d, which is not supported by Terraform %s. This state file may have been created by a newer version of Terraform.", version, thisVersion),
126 ))
127 }
128 return nil, diags
129 }
130}
131
132func sniffJSONStateVersion(src []byte) (uint64, tfdiags.Diagnostics) {
133 var diags tfdiags.Diagnostics
134
135 type VersionSniff struct {
136 Version *uint64 `json:"version"`
137 }
138 var sniff VersionSniff
139 err := json.Unmarshal(src, &sniff)
140 if err != nil {
141 switch tErr := err.(type) {
142 case *json.SyntaxError:
143 diags = diags.Append(tfdiags.Sourceless(
144 tfdiags.Error,
145 unsupportedFormat,
146 fmt.Sprintf("The state file could not be parsed as JSON: syntax error at byte offset %d.", tErr.Offset),
147 ))
148 case *json.UnmarshalTypeError:
149 diags = diags.Append(tfdiags.Sourceless(
150 tfdiags.Error,
151 unsupportedFormat,
152 fmt.Sprintf("The version in the state file is %s. A positive whole number is required.", tErr.Value),
153 ))
154 default:
155 diags = diags.Append(tfdiags.Sourceless(
156 tfdiags.Error,
157 unsupportedFormat,
158 "The state file could not be parsed as JSON.",
159 ))
160 }
161 }
162
163 if sniff.Version == nil {
164 diags = diags.Append(tfdiags.Sourceless(
165 tfdiags.Error,
166 unsupportedFormat,
167 "The state file does not have a \"version\" attribute, which is required to identify the format version.",
168 ))
169 return 0, diags
170 }
171
172 return *sniff.Version, diags
173}
174
175// sniffJSONStateTerraformVersion attempts to sniff the Terraform version
176// specification from the given state file source code. The result is either
177// a version string or an empty string if no version number could be extracted.
178//
179// This is a best-effort function intended to produce nicer error messages. It
180// should not be used for any real processing.
181func sniffJSONStateTerraformVersion(src []byte) string {
182 type VersionSniff struct {
183 Version string `json:"terraform_version"`
184 }
185 var sniff VersionSniff
186
187 err := json.Unmarshal(src, &sniff)
188 if err != nil {
189 return ""
190 }
191
192 // Attempt to parse the string as a version so we won't report garbage
193 // as a version number.
194 _, err = version.NewVersion(sniff.Version)
195 if err != nil {
196 return ""
197 }
198
199 return sniff.Version
200}
201
202// unsupportedFormat is a diagnostic summary message for when the state file
203// seems to not be a state file at all, or is not a supported version.
204//
205// Use invalidFormat instead for the subtly-different case of "this looks like
206// it's intended to be a state file but it's not structured correctly".
207const unsupportedFormat = "Unsupported state file format"
208
209const upgradeFailed = "State format upgrade failed"
diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/version0.go b/vendor/github.com/hashicorp/terraform/states/statefile/version0.go
new file mode 100644
index 0000000..9b53331
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/statefile/version0.go
@@ -0,0 +1,23 @@
1package statefile
2
3// looksLikeVersion0 sniffs for the signature indicating a version 0 state
4// file.
5//
6// Version 0 was the number retroactively assigned to Terraform's initial
7// (unversioned) binary state file format, which was later superseded by the
8// version 1 format in JSON.
9//
10// Version 0 is no longer supported, so this is used only to detect it and
11// return a nice error to the user.
12func looksLikeVersion0(src []byte) bool {
13 // Version 0 files begin with the magic prefix "tfstate".
14 const magic = "tfstate"
15 if len(src) < len(magic) {
16 // Not even long enough to have the magic prefix
17 return false
18 }
19 if string(src[0:len(magic)]) == magic {
20 return true
21 }
22 return false
23}
diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/version1.go b/vendor/github.com/hashicorp/terraform/states/statefile/version1.go
new file mode 100644
index 0000000..80d711b
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/statefile/version1.go
@@ -0,0 +1,174 @@
1package statefile
2
3import (
4 "encoding/json"
5 "fmt"
6
7 "github.com/hashicorp/terraform/tfdiags"
8)
9
10func readStateV1(src []byte) (*File, tfdiags.Diagnostics) {
11 var diags tfdiags.Diagnostics
12 sV1 := &stateV1{}
13 err := json.Unmarshal(src, sV1)
14 if err != nil {
15 diags = diags.Append(jsonUnmarshalDiags(err))
16 return nil, diags
17 }
18
19 file, prepDiags := prepareStateV1(sV1)
20 diags = diags.Append(prepDiags)
21 return file, diags
22}
23
24func prepareStateV1(sV1 *stateV1) (*File, tfdiags.Diagnostics) {
25 var diags tfdiags.Diagnostics
26 sV2, err := upgradeStateV1ToV2(sV1)
27 if err != nil {
28 diags = diags.Append(tfdiags.Sourceless(
29 tfdiags.Error,
30 upgradeFailed,
31 fmt.Sprintf("Error upgrading state file format from version 1 to version 2: %s.", err),
32 ))
33 return nil, diags
34 }
35
36 file, prepDiags := prepareStateV2(sV2)
37 diags = diags.Append(prepDiags)
38 return file, diags
39}
40
41// stateV1 is a representation of the legacy JSON state format version 1.
42//
43// It is only used to read version 1 JSON files prior to upgrading them to
44// the current format.
45type stateV1 struct {
46 // Version is the protocol version. "1" for a StateV1.
47 Version int `json:"version"`
48
49 // Serial is incremented on any operation that modifies
50 // the State file. It is used to detect potentially conflicting
51 // updates.
52 Serial int64 `json:"serial"`
53
54 // Remote is used to track the metadata required to
55 // pull and push state files from a remote storage endpoint.
56 Remote *remoteStateV1 `json:"remote,omitempty"`
57
58 // Modules contains all the modules in a breadth-first order
59 Modules []*moduleStateV1 `json:"modules"`
60}
61
62type remoteStateV1 struct {
63 // Type controls the client we use for the remote state
64 Type string `json:"type"`
65
66 // Config is used to store arbitrary configuration that
67 // is type specific
68 Config map[string]string `json:"config"`
69}
70
71type moduleStateV1 struct {
72 // Path is the import path from the root module. Modules imports are
73 // always disjoint, so the path represents amodule tree
74 Path []string `json:"path"`
75
76 // Outputs declared by the module and maintained for each module
77 // even though only the root module technically needs to be kept.
78 // This allows operators to inspect values at the boundaries.
79 Outputs map[string]string `json:"outputs"`
80
81 // Resources is a mapping of the logically named resource to
82 // the state of the resource. Each resource may actually have
83 // N instances underneath, although a user only needs to think
84 // about the 1:1 case.
85 Resources map[string]*resourceStateV1 `json:"resources"`
86
87 // Dependencies are a list of things that this module relies on
88 // existing to remain intact. For example: an module may depend
89 // on a VPC ID given by an aws_vpc resource.
90 //
91 // Terraform uses this information to build valid destruction
92 // orders and to warn the user if they're destroying a module that
93 // another resource depends on.
94 //
95 // Things can be put into this list that may not be managed by
96 // Terraform. If Terraform doesn't find a matching ID in the
97 // overall state, then it assumes it isn't managed and doesn't
98 // worry about it.
99 Dependencies []string `json:"depends_on,omitempty"`
100}
101
102type resourceStateV1 struct {
103 // This is filled in and managed by Terraform, and is the resource
104 // type itself such as "mycloud_instance". If a resource provider sets
105 // this value, it won't be persisted.
106 Type string `json:"type"`
107
108 // Dependencies are a list of things that this resource relies on
109 // existing to remain intact. For example: an AWS instance might
110 // depend on a subnet (which itself might depend on a VPC, and so
111 // on).
112 //
113 // Terraform uses this information to build valid destruction
114 // orders and to warn the user if they're destroying a resource that
115 // another resource depends on.
116 //
117 // Things can be put into this list that may not be managed by
118 // Terraform. If Terraform doesn't find a matching ID in the
119 // overall state, then it assumes it isn't managed and doesn't
120 // worry about it.
121 Dependencies []string `json:"depends_on,omitempty"`
122
123 // Primary is the current active instance for this resource.
124 // It can be replaced but only after a successful creation.
125 // This is the instances on which providers will act.
126 Primary *instanceStateV1 `json:"primary"`
127
128 // Tainted is used to track any underlying instances that
129 // have been created but are in a bad or unknown state and
130 // need to be cleaned up subsequently. In the
131 // standard case, there is only at most a single instance.
132 // However, in pathological cases, it is possible for the number
133 // of instances to accumulate.
134 Tainted []*instanceStateV1 `json:"tainted,omitempty"`
135
136 // Deposed is used in the mechanics of CreateBeforeDestroy: the existing
137 // Primary is Deposed to get it out of the way for the replacement Primary to
138 // be created by Apply. If the replacement Primary creates successfully, the
139 // Deposed instance is cleaned up. If there were problems creating the
140 // replacement, the instance remains in the Deposed list so it can be
141 // destroyed in a future run. Functionally, Deposed instances are very
142 // similar to Tainted instances in that Terraform is only tracking them in
143 // order to remember to destroy them.
144 Deposed []*instanceStateV1 `json:"deposed,omitempty"`
145
146 // Provider is used when a resource is connected to a provider with an alias.
147 // If this string is empty, the resource is connected to the default provider,
148 // e.g. "aws_instance" goes with the "aws" provider.
149 // If the resource block contained a "provider" key, that value will be set here.
150 Provider string `json:"provider,omitempty"`
151}
152
153type instanceStateV1 struct {
154 // A unique ID for this resource. This is opaque to Terraform
155 // and is only meant as a lookup mechanism for the providers.
156 ID string `json:"id"`
157
158 // Attributes are basic information about the resource. Any keys here
159 // are accessible in variable format within Terraform configurations:
160 // ${resourcetype.name.attribute}.
161 Attributes map[string]string `json:"attributes,omitempty"`
162
163 // Meta is a simple K/V map that is persisted to the State but otherwise
164 // ignored by Terraform core. It's meant to be used for accounting by
165 // external client code.
166 Meta map[string]string `json:"meta,omitempty"`
167}
168
169type ephemeralStateV1 struct {
170 // ConnInfo is used for the providers to export information which is
171 // used to connect to the resource for provisioning. For example,
172 // this could contain SSH or WinRM credentials.
173 ConnInfo map[string]string `json:"-"`
174}
diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/version1_upgrade.go b/vendor/github.com/hashicorp/terraform/states/statefile/version1_upgrade.go
new file mode 100644
index 0000000..0b417e1
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/statefile/version1_upgrade.go
@@ -0,0 +1,172 @@
1package statefile
2
3import (
4 "fmt"
5 "log"
6
7 "github.com/mitchellh/copystructure"
8)
9
10// upgradeStateV1ToV2 is used to upgrade a V1 state representation
11// into a V2 state representation
12func upgradeStateV1ToV2(old *stateV1) (*stateV2, error) {
13 log.Printf("[TRACE] statefile.Read: upgrading format from v1 to v2")
14 if old == nil {
15 return nil, nil
16 }
17
18 remote, err := old.Remote.upgradeToV2()
19 if err != nil {
20 return nil, fmt.Errorf("Error upgrading State V1: %v", err)
21 }
22
23 modules := make([]*moduleStateV2, len(old.Modules))
24 for i, module := range old.Modules {
25 upgraded, err := module.upgradeToV2()
26 if err != nil {
27 return nil, fmt.Errorf("Error upgrading State V1: %v", err)
28 }
29 modules[i] = upgraded
30 }
31 if len(modules) == 0 {
32 modules = nil
33 }
34
35 newState := &stateV2{
36 Version: 2,
37 Serial: old.Serial,
38 Remote: remote,
39 Modules: modules,
40 }
41
42 return newState, nil
43}
44
45func (old *remoteStateV1) upgradeToV2() (*remoteStateV2, error) {
46 if old == nil {
47 return nil, nil
48 }
49
50 config, err := copystructure.Copy(old.Config)
51 if err != nil {
52 return nil, fmt.Errorf("Error upgrading RemoteState V1: %v", err)
53 }
54
55 return &remoteStateV2{
56 Type: old.Type,
57 Config: config.(map[string]string),
58 }, nil
59}
60
61func (old *moduleStateV1) upgradeToV2() (*moduleStateV2, error) {
62 if old == nil {
63 return nil, nil
64 }
65
66 pathRaw, err := copystructure.Copy(old.Path)
67 if err != nil {
68 return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err)
69 }
70 path, ok := pathRaw.([]string)
71 if !ok {
72 return nil, fmt.Errorf("Error upgrading ModuleState V1: path is not a list of strings")
73 }
74 if len(path) == 0 {
75 // We found some V1 states with a nil path. Assume root.
76 path = []string{"root"}
77 }
78
79 // Outputs needs upgrading to use the new structure
80 outputs := make(map[string]*outputStateV2)
81 for key, output := range old.Outputs {
82 outputs[key] = &outputStateV2{
83 Type: "string",
84 Value: output,
85 Sensitive: false,
86 }
87 }
88
89 resources := make(map[string]*resourceStateV2)
90 for key, oldResource := range old.Resources {
91 upgraded, err := oldResource.upgradeToV2()
92 if err != nil {
93 return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err)
94 }
95 resources[key] = upgraded
96 }
97
98 dependencies, err := copystructure.Copy(old.Dependencies)
99 if err != nil {
100 return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err)
101 }
102
103 return &moduleStateV2{
104 Path: path,
105 Outputs: outputs,
106 Resources: resources,
107 Dependencies: dependencies.([]string),
108 }, nil
109}
110
111func (old *resourceStateV1) upgradeToV2() (*resourceStateV2, error) {
112 if old == nil {
113 return nil, nil
114 }
115
116 dependencies, err := copystructure.Copy(old.Dependencies)
117 if err != nil {
118 return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err)
119 }
120
121 primary, err := old.Primary.upgradeToV2()
122 if err != nil {
123 return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err)
124 }
125
126 deposed := make([]*instanceStateV2, len(old.Deposed))
127 for i, v := range old.Deposed {
128 upgraded, err := v.upgradeToV2()
129 if err != nil {
130 return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err)
131 }
132 deposed[i] = upgraded
133 }
134 if len(deposed) == 0 {
135 deposed = nil
136 }
137
138 return &resourceStateV2{
139 Type: old.Type,
140 Dependencies: dependencies.([]string),
141 Primary: primary,
142 Deposed: deposed,
143 Provider: old.Provider,
144 }, nil
145}
146
147func (old *instanceStateV1) upgradeToV2() (*instanceStateV2, error) {
148 if old == nil {
149 return nil, nil
150 }
151
152 attributes, err := copystructure.Copy(old.Attributes)
153 if err != nil {
154 return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err)
155 }
156
157 meta, err := copystructure.Copy(old.Meta)
158 if err != nil {
159 return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err)
160 }
161
162 newMeta := make(map[string]interface{})
163 for k, v := range meta.(map[string]string) {
164 newMeta[k] = v
165 }
166
167 return &instanceStateV2{
168 ID: old.ID,
169 Attributes: attributes.(map[string]string),
170 Meta: newMeta,
171 }, nil
172}
diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/version2.go b/vendor/github.com/hashicorp/terraform/states/statefile/version2.go
new file mode 100644
index 0000000..6fe2ab8
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/statefile/version2.go
@@ -0,0 +1,209 @@
1package statefile
2
3import (
4 "encoding/json"
5 "fmt"
6 "sync"
7
8 "github.com/hashicorp/terraform/tfdiags"
9)
10
11func readStateV2(src []byte) (*File, tfdiags.Diagnostics) {
12 var diags tfdiags.Diagnostics
13 sV2 := &stateV2{}
14 err := json.Unmarshal(src, sV2)
15 if err != nil {
16 diags = diags.Append(jsonUnmarshalDiags(err))
17 return nil, diags
18 }
19
20 file, prepDiags := prepareStateV2(sV2)
21 diags = diags.Append(prepDiags)
22 return file, diags
23}
24
25func prepareStateV2(sV2 *stateV2) (*File, tfdiags.Diagnostics) {
26 var diags tfdiags.Diagnostics
27 sV3, err := upgradeStateV2ToV3(sV2)
28 if err != nil {
29 diags = diags.Append(tfdiags.Sourceless(
30 tfdiags.Error,
31 upgradeFailed,
32 fmt.Sprintf("Error upgrading state file format from version 2 to version 3: %s.", err),
33 ))
34 return nil, diags
35 }
36
37 file, prepDiags := prepareStateV3(sV3)
38 diags = diags.Append(prepDiags)
39 return file, diags
40}
41
42// stateV2 is a representation of the legacy JSON state format version 2.
43//
44// It is only used to read version 2 JSON files prior to upgrading them to
45// the current format.
46type stateV2 struct {
47 // Version is the state file protocol version.
48 Version int `json:"version"`
49
50 // TFVersion is the version of Terraform that wrote this state.
51 TFVersion string `json:"terraform_version,omitempty"`
52
53 // Serial is incremented on any operation that modifies
54 // the State file. It is used to detect potentially conflicting
55 // updates.
56 Serial int64 `json:"serial"`
57
58 // Lineage is set when a new, blank state is created and then
59 // never updated. This allows us to determine whether the serials
60 // of two states can be meaningfully compared.
61 // Apart from the guarantee that collisions between two lineages
62 // are very unlikely, this value is opaque and external callers
63 // should only compare lineage strings byte-for-byte for equality.
64 Lineage string `json:"lineage"`
65
66 // Remote is used to track the metadata required to
67 // pull and push state files from a remote storage endpoint.
68 Remote *remoteStateV2 `json:"remote,omitempty"`
69
70 // Backend tracks the configuration for the backend in use with
71 // this state. This is used to track any changes in the backend
72 // configuration.
73 Backend *backendStateV2 `json:"backend,omitempty"`
74
75 // Modules contains all the modules in a breadth-first order
76 Modules []*moduleStateV2 `json:"modules"`
77}
78
79type remoteStateV2 struct {
80 // Type controls the client we use for the remote state
81 Type string `json:"type"`
82
83 // Config is used to store arbitrary configuration that
84 // is type specific
85 Config map[string]string `json:"config"`
86}
87
88type outputStateV2 struct {
89 // Sensitive describes whether the output is considered sensitive,
90 // which may lead to masking the value on screen in some cases.
91 Sensitive bool `json:"sensitive"`
92 // Type describes the structure of Value. Valid values are "string",
93 // "map" and "list"
94 Type string `json:"type"`
95 // Value contains the value of the output, in the structure described
96 // by the Type field.
97 Value interface{} `json:"value"`
98
99 mu sync.Mutex
100}
101
102type moduleStateV2 struct {
103 // Path is the import path from the root module. Modules imports are
104 // always disjoint, so the path represents amodule tree
105 Path []string `json:"path"`
106
107 // Locals are kept only transiently in-memory, because we can always
108 // re-compute them.
109 Locals map[string]interface{} `json:"-"`
110
111 // Outputs declared by the module and maintained for each module
112 // even though only the root module technically needs to be kept.
113 // This allows operators to inspect values at the boundaries.
114 Outputs map[string]*outputStateV2 `json:"outputs"`
115
116 // Resources is a mapping of the logically named resource to
117 // the state of the resource. Each resource may actually have
118 // N instances underneath, although a user only needs to think
119 // about the 1:1 case.
120 Resources map[string]*resourceStateV2 `json:"resources"`
121
122 // Dependencies are a list of things that this module relies on
123 // existing to remain intact. For example: an module may depend
124 // on a VPC ID given by an aws_vpc resource.
125 //
126 // Terraform uses this information to build valid destruction
127 // orders and to warn the user if they're destroying a module that
128 // another resource depends on.
129 //
130 // Things can be put into this list that may not be managed by
131 // Terraform. If Terraform doesn't find a matching ID in the
132 // overall state, then it assumes it isn't managed and doesn't
133 // worry about it.
134 Dependencies []string `json:"depends_on"`
135}
136
137type resourceStateV2 struct {
138 // This is filled in and managed by Terraform, and is the resource
139 // type itself such as "mycloud_instance". If a resource provider sets
140 // this value, it won't be persisted.
141 Type string `json:"type"`
142
143 // Dependencies are a list of things that this resource relies on
144 // existing to remain intact. For example: an AWS instance might
145 // depend on a subnet (which itself might depend on a VPC, and so
146 // on).
147 //
148 // Terraform uses this information to build valid destruction
149 // orders and to warn the user if they're destroying a resource that
150 // another resource depends on.
151 //
152 // Things can be put into this list that may not be managed by
153 // Terraform. If Terraform doesn't find a matching ID in the
154 // overall state, then it assumes it isn't managed and doesn't
155 // worry about it.
156 Dependencies []string `json:"depends_on"`
157
158 // Primary is the current active instance for this resource.
159 // It can be replaced but only after a successful creation.
160 // This is the instances on which providers will act.
161 Primary *instanceStateV2 `json:"primary"`
162
163 // Deposed is used in the mechanics of CreateBeforeDestroy: the existing
164 // Primary is Deposed to get it out of the way for the replacement Primary to
165 // be created by Apply. If the replacement Primary creates successfully, the
166 // Deposed instance is cleaned up.
167 //
168 // If there were problems creating the replacement Primary, the Deposed
169 // instance and the (now tainted) replacement Primary will be swapped so the
170 // tainted replacement will be cleaned up instead.
171 //
172 // An instance will remain in the Deposed list until it is successfully
173 // destroyed and purged.
174 Deposed []*instanceStateV2 `json:"deposed"`
175
176 // Provider is used when a resource is connected to a provider with an alias.
177 // If this string is empty, the resource is connected to the default provider,
178 // e.g. "aws_instance" goes with the "aws" provider.
179 // If the resource block contained a "provider" key, that value will be set here.
180 Provider string `json:"provider"`
181
182 mu sync.Mutex
183}
184
185type instanceStateV2 struct {
186 // A unique ID for this resource. This is opaque to Terraform
187 // and is only meant as a lookup mechanism for the providers.
188 ID string `json:"id"`
189
190 // Attributes are basic information about the resource. Any keys here
191 // are accessible in variable format within Terraform configurations:
192 // ${resourcetype.name.attribute}.
193 Attributes map[string]string `json:"attributes"`
194
195 // Meta is a simple K/V map that is persisted to the State but otherwise
196 // ignored by Terraform core. It's meant to be used for accounting by
197 // external client code. The value here must only contain Go primitives
198 // and collections.
199 Meta map[string]interface{} `json:"meta"`
200
201 // Tainted is used to mark a resource for recreation.
202 Tainted bool `json:"tainted"`
203}
204
205type backendStateV2 struct {
206 Type string `json:"type"` // Backend type
207 ConfigRaw json.RawMessage `json:"config"` // Backend raw config
208 Hash int `json:"hash"` // Hash of portion of configuration from config files
209}
diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/version2_upgrade.go b/vendor/github.com/hashicorp/terraform/states/statefile/version2_upgrade.go
new file mode 100644
index 0000000..2d03c07
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/statefile/version2_upgrade.go
@@ -0,0 +1,145 @@
1package statefile
2
3import (
4 "fmt"
5 "log"
6 "regexp"
7 "sort"
8 "strconv"
9 "strings"
10
11 "github.com/mitchellh/copystructure"
12)
13
14func upgradeStateV2ToV3(old *stateV2) (*stateV3, error) {
15 if old == nil {
16 return (*stateV3)(nil), nil
17 }
18
19 var new *stateV3
20 {
21 copy, err := copystructure.Config{Lock: true}.Copy(old)
22 if err != nil {
23 panic(err)
24 }
25 newWrongType := copy.(*stateV2)
26 newRightType := (stateV3)(*newWrongType)
27 new = &newRightType
28 }
29
30 // Set the new version number
31 new.Version = 3
32
33 // Change the counts for things which look like maps to use the %
34 // syntax. Remove counts for empty collections - they will be added
35 // back in later.
36 for _, module := range new.Modules {
37 for _, resource := range module.Resources {
38 // Upgrade Primary
39 if resource.Primary != nil {
40 upgradeAttributesV2ToV3(resource.Primary)
41 }
42
43 // Upgrade Deposed
44 for _, deposed := range resource.Deposed {
45 upgradeAttributesV2ToV3(deposed)
46 }
47 }
48 }
49
50 return new, nil
51}
52
53func upgradeAttributesV2ToV3(instanceState *instanceStateV2) error {
54 collectionKeyRegexp := regexp.MustCompile(`^(.*\.)#$`)
55 collectionSubkeyRegexp := regexp.MustCompile(`^([^\.]+)\..*`)
56
57 // Identify the key prefix of anything which is a collection
58 var collectionKeyPrefixes []string
59 for key := range instanceState.Attributes {
60 if submatches := collectionKeyRegexp.FindAllStringSubmatch(key, -1); len(submatches) > 0 {
61 collectionKeyPrefixes = append(collectionKeyPrefixes, submatches[0][1])
62 }
63 }
64 sort.Strings(collectionKeyPrefixes)
65
66 log.Printf("[STATE UPGRADE] Detected the following collections in state: %v", collectionKeyPrefixes)
67
68 // This could be rolled into fewer loops, but it is somewhat clearer this way, and will not
69 // run very often.
70 for _, prefix := range collectionKeyPrefixes {
71 // First get the actual keys that belong to this prefix
72 var potentialKeysMatching []string
73 for key := range instanceState.Attributes {
74 if strings.HasPrefix(key, prefix) {
75 potentialKeysMatching = append(potentialKeysMatching, strings.TrimPrefix(key, prefix))
76 }
77 }
78 sort.Strings(potentialKeysMatching)
79
80 var actualKeysMatching []string
81 for _, key := range potentialKeysMatching {
82 if submatches := collectionSubkeyRegexp.FindAllStringSubmatch(key, -1); len(submatches) > 0 {
83 actualKeysMatching = append(actualKeysMatching, submatches[0][1])
84 } else {
85 if key != "#" {
86 actualKeysMatching = append(actualKeysMatching, key)
87 }
88 }
89 }
90 actualKeysMatching = uniqueSortedStrings(actualKeysMatching)
91
92 // Now inspect the keys in order to determine whether this is most likely to be
93 // a map, list or set. There is room for error here, so we log in each case. If
94 // there is no method of telling, we remove the key from the InstanceState in
95 // order that it will be recreated. Again, this could be rolled into fewer loops
96 // but we prefer clarity.
97
98 oldCountKey := fmt.Sprintf("%s#", prefix)
99
100 // First, detect "obvious" maps - which have non-numeric keys (mostly).
101 hasNonNumericKeys := false
102 for _, key := range actualKeysMatching {
103 if _, err := strconv.Atoi(key); err != nil {
104 hasNonNumericKeys = true
105 }
106 }
107 if hasNonNumericKeys {
108 newCountKey := fmt.Sprintf("%s%%", prefix)
109
110 instanceState.Attributes[newCountKey] = instanceState.Attributes[oldCountKey]
111 delete(instanceState.Attributes, oldCountKey)
112 log.Printf("[STATE UPGRADE] Detected %s as a map. Replaced count = %s",
113 strings.TrimSuffix(prefix, "."), instanceState.Attributes[newCountKey])
114 }
115
116 // Now detect empty collections and remove them from state.
117 if len(actualKeysMatching) == 0 {
118 delete(instanceState.Attributes, oldCountKey)
119 log.Printf("[STATE UPGRADE] Detected %s as an empty collection. Removed from state.",
120 strings.TrimSuffix(prefix, "."))
121 }
122 }
123
124 return nil
125}
126
127// uniqueSortedStrings removes duplicates from a slice of strings and returns
128// a sorted slice of the unique strings.
129func uniqueSortedStrings(input []string) []string {
130 uniquemap := make(map[string]struct{})
131 for _, str := range input {
132 uniquemap[str] = struct{}{}
133 }
134
135 output := make([]string, len(uniquemap))
136
137 i := 0
138 for key := range uniquemap {
139 output[i] = key
140 i = i + 1
141 }
142
143 sort.Strings(output)
144 return output
145}
diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/version3.go b/vendor/github.com/hashicorp/terraform/states/statefile/version3.go
new file mode 100644
index 0000000..ab6414b
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/statefile/version3.go
@@ -0,0 +1,50 @@
1package statefile
2
3import (
4 "encoding/json"
5 "fmt"
6
7 "github.com/hashicorp/terraform/tfdiags"
8)
9
10func readStateV3(src []byte) (*File, tfdiags.Diagnostics) {
11 var diags tfdiags.Diagnostics
12 sV3 := &stateV3{}
13 err := json.Unmarshal(src, sV3)
14 if err != nil {
15 diags = diags.Append(jsonUnmarshalDiags(err))
16 return nil, diags
17 }
18
19 file, prepDiags := prepareStateV3(sV3)
20 diags = diags.Append(prepDiags)
21 return file, diags
22}
23
24func prepareStateV3(sV3 *stateV3) (*File, tfdiags.Diagnostics) {
25 var diags tfdiags.Diagnostics
26 sV4, err := upgradeStateV3ToV4(sV3)
27 if err != nil {
28 diags = diags.Append(tfdiags.Sourceless(
29 tfdiags.Error,
30 upgradeFailed,
31 fmt.Sprintf("Error upgrading state file format from version 3 to version 4: %s.", err),
32 ))
33 return nil, diags
34 }
35
36 file, prepDiags := prepareStateV4(sV4)
37 diags = diags.Append(prepDiags)
38 return file, diags
39}
40
41// stateV2 is a representation of the legacy JSON state format version 3.
42//
43// It is only used to read version 3 JSON files prior to upgrading them to
44// the current format.
45//
46// The differences between version 2 and version 3 are only in the data and
47// not in the structure, so stateV3 actually shares the same structs as
48// stateV2. Type stateV3 represents that the data within is formatted as
49// expected by the V3 format, rather than the V2 format.
50type stateV3 stateV2
diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/version3_upgrade.go b/vendor/github.com/hashicorp/terraform/states/statefile/version3_upgrade.go
new file mode 100644
index 0000000..2cbe8a5
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/statefile/version3_upgrade.go
@@ -0,0 +1,431 @@
1package statefile
2
3import (
4 "encoding/json"
5 "fmt"
6 "strconv"
7 "strings"
8
9 "github.com/zclconf/go-cty/cty"
10 ctyjson "github.com/zclconf/go-cty/cty/json"
11
12 "github.com/hashicorp/terraform/addrs"
13 "github.com/hashicorp/terraform/states"
14 "github.com/hashicorp/terraform/tfdiags"
15)
16
17func upgradeStateV3ToV4(old *stateV3) (*stateV4, error) {
18
19 if old.Serial < 0 {
20 // The new format is using uint64 here, which should be fine for any
21 // real state (we only used positive integers in practice) but we'll
22 // catch this explicitly here to avoid weird behavior if a state file
23 // has been tampered with in some way.
24 return nil, fmt.Errorf("state has serial less than zero, which is invalid")
25 }
26
27 new := &stateV4{
28 TerraformVersion: old.TFVersion,
29 Serial: uint64(old.Serial),
30 Lineage: old.Lineage,
31 RootOutputs: map[string]outputStateV4{},
32 Resources: []resourceStateV4{},
33 }
34
35 if new.TerraformVersion == "" {
36 // Older formats considered this to be optional, but now it's required
37 // and so we'll stub it out with something that's definitely older
38 // than the version that really created this state.
39 new.TerraformVersion = "0.0.0"
40 }
41
42 for _, msOld := range old.Modules {
43 if len(msOld.Path) < 1 || msOld.Path[0] != "root" {
44 return nil, fmt.Errorf("state contains invalid module path %#v", msOld.Path)
45 }
46
47 // Convert legacy-style module address into our newer address type.
48 // Since these old formats are only generated by versions of Terraform
49 // that don't support count and for_each on modules, we can just assume
50 // all of the modules are unkeyed.
51 moduleAddr := make(addrs.ModuleInstance, len(msOld.Path)-1)
52 for i, name := range msOld.Path[1:] {
53 moduleAddr[i] = addrs.ModuleInstanceStep{
54 Name: name,
55 InstanceKey: addrs.NoKey,
56 }
57 }
58
59 // In a v3 state file, a "resource state" is actually an instance
60 // state, so we need to fill in a missing level of heirarchy here
61 // by lazily creating resource states as we encounter them.
62 // We'll track them in here, keyed on the string representation of
63 // the resource address.
64 resourceStates := map[string]*resourceStateV4{}
65
66 for legacyAddr, rsOld := range msOld.Resources {
67 instAddr, err := parseLegacyResourceAddress(legacyAddr)
68 if err != nil {
69 return nil, err
70 }
71
72 resAddr := instAddr.Resource
73 rs, exists := resourceStates[resAddr.String()]
74 if !exists {
75 var modeStr string
76 switch resAddr.Mode {
77 case addrs.ManagedResourceMode:
78 modeStr = "managed"
79 case addrs.DataResourceMode:
80 modeStr = "data"
81 default:
82 return nil, fmt.Errorf("state contains resource %s with an unsupported resource mode", resAddr)
83 }
84
85 // In state versions prior to 4 we allowed each instance of a
86 // resource to have its own provider configuration address,
87 // which makes no real sense in practice because providers
88 // are associated with resources in the configuration. We
89 // elevate that to the resource level during this upgrade,
90 // implicitly taking the provider address of the first instance
91 // we encounter for each resource. While this is lossy in
92 // theory, in practice there is no reason for these values to
93 // differ between instances.
94 var providerAddr addrs.AbsProviderConfig
95 oldProviderAddr := rsOld.Provider
96 if strings.Contains(oldProviderAddr, "provider.") {
97 // Smells like a new-style provider address, but we'll test it.
98 var diags tfdiags.Diagnostics
99 providerAddr, diags = addrs.ParseAbsProviderConfigStr(oldProviderAddr)
100 if diags.HasErrors() {
101 return nil, diags.Err()
102 }
103 } else {
104 // Smells like an old-style module-local provider address,
105 // which we'll need to migrate. We'll assume it's referring
106 // to the same module the resource is in, which might be
107 // incorrect but it'll get fixed up next time any updates
108 // are made to an instance.
109 if oldProviderAddr != "" {
110 localAddr, diags := addrs.ParseProviderConfigCompactStr(oldProviderAddr)
111 if diags.HasErrors() {
112 return nil, diags.Err()
113 }
114 providerAddr = localAddr.Absolute(moduleAddr)
115 } else {
116 providerAddr = resAddr.DefaultProviderConfig().Absolute(moduleAddr)
117 }
118 }
119
120 rs = &resourceStateV4{
121 Module: moduleAddr.String(),
122 Mode: modeStr,
123 Type: resAddr.Type,
124 Name: resAddr.Name,
125 Instances: []instanceObjectStateV4{},
126 ProviderConfig: providerAddr.String(),
127 }
128 resourceStates[resAddr.String()] = rs
129 }
130
131 // Now we'll deal with the instance itself, which may either be
132 // the first instance in a resource we just created or an additional
133 // instance for a resource added on a prior loop.
134 instKey := instAddr.Key
135 if isOld := rsOld.Primary; isOld != nil {
136 isNew, err := upgradeInstanceObjectV3ToV4(rsOld, isOld, instKey, states.NotDeposed)
137 if err != nil {
138 return nil, fmt.Errorf("failed to migrate primary generation of %s: %s", instAddr, err)
139 }
140 rs.Instances = append(rs.Instances, *isNew)
141 }
142 for i, isOld := range rsOld.Deposed {
143 // When we migrate old instances we'll use sequential deposed
144 // keys just so that the upgrade result is deterministic. New
145 // deposed keys allocated moving forward will be pseudorandomly
146 // selected, but we check for collisions and so these
147 // non-random ones won't hurt.
148 deposedKey := states.DeposedKey(fmt.Sprintf("%08x", i+1))
149 isNew, err := upgradeInstanceObjectV3ToV4(rsOld, isOld, instKey, deposedKey)
150 if err != nil {
151 return nil, fmt.Errorf("failed to migrate deposed generation index %d of %s: %s", i, instAddr, err)
152 }
153 rs.Instances = append(rs.Instances, *isNew)
154 }
155
156 if instKey != addrs.NoKey && rs.EachMode == "" {
157 rs.EachMode = "list"
158 }
159 }
160
161 for _, rs := range resourceStates {
162 new.Resources = append(new.Resources, *rs)
163 }
164
165 if len(msOld.Path) == 1 && msOld.Path[0] == "root" {
166 // We'll migrate the outputs for this module too, then.
167 for name, oldOS := range msOld.Outputs {
168 newOS := outputStateV4{
169 Sensitive: oldOS.Sensitive,
170 }
171
172 valRaw := oldOS.Value
173 valSrc, err := json.Marshal(valRaw)
174 if err != nil {
175 // Should never happen, because this value came from JSON
176 // in the first place and so we're just round-tripping here.
177 return nil, fmt.Errorf("failed to serialize output %q value as JSON: %s", name, err)
178 }
179
180 // The "type" field in state V2 wasn't really that useful
181 // since it was only able to capture string vs. list vs. map.
182 // For this reason, during upgrade we'll just discard it
183 // altogether and use cty's idea of the implied type of
184 // turning our old value into JSON.
185 ty, err := ctyjson.ImpliedType(valSrc)
186 if err != nil {
187 // REALLY should never happen, because we literally just
188 // encoded this as JSON above!
189 return nil, fmt.Errorf("failed to parse output %q value from JSON: %s", name, err)
190 }
191
192 // ImpliedType tends to produce structural types, but since older
193 // version of Terraform didn't support those a collection type
194 // is probably what was intended, so we'll see if we can
195 // interpret our value as one.
196 ty = simplifyImpliedValueType(ty)
197
198 tySrc, err := ctyjson.MarshalType(ty)
199 if err != nil {
200 return nil, fmt.Errorf("failed to serialize output %q type as JSON: %s", name, err)
201 }
202
203 newOS.ValueRaw = json.RawMessage(valSrc)
204 newOS.ValueTypeRaw = json.RawMessage(tySrc)
205
206 new.RootOutputs[name] = newOS
207 }
208 }
209 }
210
211 new.normalize()
212
213 return new, nil
214}
215
216func upgradeInstanceObjectV3ToV4(rsOld *resourceStateV2, isOld *instanceStateV2, instKey addrs.InstanceKey, deposedKey states.DeposedKey) (*instanceObjectStateV4, error) {
217
218 // Schema versions were, in prior formats, a private concern of the provider
219 // SDK, and not a first-class concept in the state format. Here we're
220 // sniffing for the pre-0.12 SDK's way of representing schema versions
221 // and promoting it to our first-class field if we find it. We'll ignore
222 // it if it doesn't look like what the SDK would've written. If this
223 // sniffing fails then we'll assume schema version 0.
224 var schemaVersion uint64
225 migratedSchemaVersion := false
226 if raw, exists := isOld.Meta["schema_version"]; exists {
227 switch tv := raw.(type) {
228 case string:
229 v, err := strconv.ParseUint(tv, 10, 64)
230 if err == nil {
231 schemaVersion = v
232 migratedSchemaVersion = true
233 }
234 case int:
235 schemaVersion = uint64(tv)
236 migratedSchemaVersion = true
237 case float64:
238 schemaVersion = uint64(tv)
239 migratedSchemaVersion = true
240 }
241 }
242
243 private := map[string]interface{}{}
244 for k, v := range isOld.Meta {
245 if k == "schema_version" && migratedSchemaVersion {
246 // We're gonna promote this into our first-class schema version field
247 continue
248 }
249 private[k] = v
250 }
251 var privateJSON []byte
252 if len(private) != 0 {
253 var err error
254 privateJSON, err = json.Marshal(private)
255 if err != nil {
256 // This shouldn't happen, because the Meta values all came from JSON
257 // originally anyway.
258 return nil, fmt.Errorf("cannot serialize private instance object data: %s", err)
259 }
260 }
261
262 var status string
263 if isOld.Tainted {
264 status = "tainted"
265 }
266
267 var instKeyRaw interface{}
268 switch tk := instKey.(type) {
269 case addrs.IntKey:
270 instKeyRaw = int(tk)
271 case addrs.StringKey:
272 instKeyRaw = string(tk)
273 default:
274 if instKeyRaw != nil {
275 return nil, fmt.Errorf("insupported instance key: %#v", instKey)
276 }
277 }
278
279 var attributes map[string]string
280 if isOld.Attributes != nil {
281 attributes = make(map[string]string, len(isOld.Attributes))
282 for k, v := range isOld.Attributes {
283 attributes[k] = v
284 }
285 }
286 if isOld.ID != "" {
287 // As a special case, if we don't already have an "id" attribute and
288 // yet there's a non-empty first-class ID on the old object then we'll
289 // create a synthetic id attribute to avoid losing that first-class id.
290 // In practice this generally arises only in tests where state literals
291 // are hand-written in a non-standard way; real code prior to 0.12
292 // would always force the first-class ID to be copied into the
293 // id attribute before storing.
294 if attributes == nil {
295 attributes = make(map[string]string, len(isOld.Attributes))
296 }
297 if idVal := attributes["id"]; idVal == "" {
298 attributes["id"] = isOld.ID
299 }
300 }
301
302 dependencies := make([]string, len(rsOld.Dependencies))
303 for i, v := range rsOld.Dependencies {
304 dependencies[i] = parseLegacyDependency(v)
305 }
306
307 return &instanceObjectStateV4{
308 IndexKey: instKeyRaw,
309 Status: status,
310 Deposed: string(deposedKey),
311 AttributesFlat: attributes,
312 Dependencies: dependencies,
313 SchemaVersion: schemaVersion,
314 PrivateRaw: privateJSON,
315 }, nil
316}
317
318// parseLegacyResourceAddress parses the different identifier format used
319// state formats before version 4, like "instance.name.0".
320func parseLegacyResourceAddress(s string) (addrs.ResourceInstance, error) {
321 var ret addrs.ResourceInstance
322
323 // Split based on ".". Every resource address should have at least two
324 // elements (type and name).
325 parts := strings.Split(s, ".")
326 if len(parts) < 2 || len(parts) > 4 {
327 return ret, fmt.Errorf("invalid internal resource address format: %s", s)
328 }
329
330 // Data resource if we have at least 3 parts and the first one is data
331 ret.Resource.Mode = addrs.ManagedResourceMode
332 if len(parts) > 2 && parts[0] == "data" {
333 ret.Resource.Mode = addrs.DataResourceMode
334 parts = parts[1:]
335 }
336
337 // If we're not a data resource and we have more than 3, then it is an error
338 if len(parts) > 3 && ret.Resource.Mode != addrs.DataResourceMode {
339 return ret, fmt.Errorf("invalid internal resource address format: %s", s)
340 }
341
342 // Build the parts of the resource address that are guaranteed to exist
343 ret.Resource.Type = parts[0]
344 ret.Resource.Name = parts[1]
345 ret.Key = addrs.NoKey
346
347 // If we have more parts, then we have an index. Parse that.
348 if len(parts) > 2 {
349 idx, err := strconv.ParseInt(parts[2], 0, 0)
350 if err != nil {
351 return ret, fmt.Errorf("error parsing resource address %q: %s", s, err)
352 }
353
354 ret.Key = addrs.IntKey(idx)
355 }
356
357 return ret, nil
358}
359
360// simplifyImpliedValueType attempts to heuristically simplify a value type
361// derived from a legacy stored output value into something simpler that
362// is closer to what would've fitted into the pre-v0.12 value type system.
363func simplifyImpliedValueType(ty cty.Type) cty.Type {
364 switch {
365 case ty.IsTupleType():
366 // If all of the element types are the same then we'll make this
367 // a list instead. This is very likely to be true, since prior versions
368 // of Terraform did not officially support mixed-type collections.
369
370 if ty.Equals(cty.EmptyTuple) {
371 // Don't know what the element type would be, then.
372 return ty
373 }
374
375 etys := ty.TupleElementTypes()
376 ety := etys[0]
377 for _, other := range etys[1:] {
378 if !other.Equals(ety) {
379 // inconsistent types
380 return ty
381 }
382 }
383 ety = simplifyImpliedValueType(ety)
384 return cty.List(ety)
385
386 case ty.IsObjectType():
387 // If all of the attribute types are the same then we'll make this
388 // a map instead. This is very likely to be true, since prior versions
389 // of Terraform did not officially support mixed-type collections.
390
391 if ty.Equals(cty.EmptyObject) {
392 // Don't know what the element type would be, then.
393 return ty
394 }
395
396 atys := ty.AttributeTypes()
397 var ety cty.Type
398 for _, other := range atys {
399 if ety == cty.NilType {
400 ety = other
401 continue
402 }
403 if !other.Equals(ety) {
404 // inconsistent types
405 return ty
406 }
407 }
408 ety = simplifyImpliedValueType(ety)
409 return cty.Map(ety)
410
411 default:
412 // No other normalizations are possible
413 return ty
414 }
415}
416
417func parseLegacyDependency(s string) string {
418 parts := strings.Split(s, ".")
419 ret := parts[0]
420 for _, part := range parts[1:] {
421 if part == "*" {
422 break
423 }
424 if i, err := strconv.Atoi(part); err == nil {
425 ret = ret + fmt.Sprintf("[%d]", i)
426 break
427 }
428 ret = ret + "." + part
429 }
430 return ret
431}
diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/version4.go b/vendor/github.com/hashicorp/terraform/states/statefile/version4.go
new file mode 100644
index 0000000..ee8b652
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/statefile/version4.go
@@ -0,0 +1,604 @@
1package statefile
2
3import (
4 "encoding/json"
5 "fmt"
6 "io"
7 "sort"
8
9 version "github.com/hashicorp/go-version"
10 ctyjson "github.com/zclconf/go-cty/cty/json"
11
12 "github.com/hashicorp/terraform/addrs"
13 "github.com/hashicorp/terraform/states"
14 "github.com/hashicorp/terraform/tfdiags"
15)
16
17func readStateV4(src []byte) (*File, tfdiags.Diagnostics) {
18 var diags tfdiags.Diagnostics
19 sV4 := &stateV4{}
20 err := json.Unmarshal(src, sV4)
21 if err != nil {
22 diags = diags.Append(jsonUnmarshalDiags(err))
23 return nil, diags
24 }
25
26 file, prepDiags := prepareStateV4(sV4)
27 diags = diags.Append(prepDiags)
28 return file, diags
29}
30
31func prepareStateV4(sV4 *stateV4) (*File, tfdiags.Diagnostics) {
32 var diags tfdiags.Diagnostics
33
34 var tfVersion *version.Version
35 if sV4.TerraformVersion != "" {
36 var err error
37 tfVersion, err = version.NewVersion(sV4.TerraformVersion)
38 if err != nil {
39 diags = diags.Append(tfdiags.Sourceless(
40 tfdiags.Error,
41 "Invalid Terraform version string",
42 fmt.Sprintf("State file claims to have been written by Terraform version %q, which is not a valid version string.", sV4.TerraformVersion),
43 ))
44 }
45 }
46
47 file := &File{
48 TerraformVersion: tfVersion,
49 Serial: sV4.Serial,
50 Lineage: sV4.Lineage,
51 }
52
53 state := states.NewState()
54
55 for _, rsV4 := range sV4.Resources {
56 rAddr := addrs.Resource{
57 Type: rsV4.Type,
58 Name: rsV4.Name,
59 }
60 switch rsV4.Mode {
61 case "managed":
62 rAddr.Mode = addrs.ManagedResourceMode
63 case "data":
64 rAddr.Mode = addrs.DataResourceMode
65 default:
66 diags = diags.Append(tfdiags.Sourceless(
67 tfdiags.Error,
68 "Invalid resource mode in state",
69 fmt.Sprintf("State contains a resource with mode %q (%q %q) which is not supported.", rsV4.Mode, rAddr.Type, rAddr.Name),
70 ))
71 continue
72 }
73
74 moduleAddr := addrs.RootModuleInstance
75 if rsV4.Module != "" {
76 var addrDiags tfdiags.Diagnostics
77 moduleAddr, addrDiags = addrs.ParseModuleInstanceStr(rsV4.Module)
78 diags = diags.Append(addrDiags)
79 if addrDiags.HasErrors() {
80 continue
81 }
82 }
83
84 providerAddr, addrDiags := addrs.ParseAbsProviderConfigStr(rsV4.ProviderConfig)
85 diags.Append(addrDiags)
86 if addrDiags.HasErrors() {
87 continue
88 }
89
90 var eachMode states.EachMode
91 switch rsV4.EachMode {
92 case "":
93 eachMode = states.NoEach
94 case "list":
95 eachMode = states.EachList
96 case "map":
97 eachMode = states.EachMap
98 default:
99 diags = diags.Append(tfdiags.Sourceless(
100 tfdiags.Error,
101 "Invalid resource metadata in state",
102 fmt.Sprintf("Resource %s has invalid \"each\" value %q in state.", rAddr.Absolute(moduleAddr), eachMode),
103 ))
104 continue
105 }
106
107 ms := state.EnsureModule(moduleAddr)
108
109 // Ensure the resource container object is present in the state.
110 ms.SetResourceMeta(rAddr, eachMode, providerAddr)
111
112 for _, isV4 := range rsV4.Instances {
113 keyRaw := isV4.IndexKey
114 var key addrs.InstanceKey
115 switch tk := keyRaw.(type) {
116 case int:
117 key = addrs.IntKey(tk)
118 case float64:
119 // Since JSON only has one number type, reading from encoding/json
120 // gives us a float64 here even if the number is whole.
121 // float64 has a smaller integer range than int, but in practice
122 // we rarely have more than a few tens of instances and so
123 // it's unlikely that we'll exhaust the 52 bits in a float64.
124 key = addrs.IntKey(int(tk))
125 case string:
126 key = addrs.StringKey(tk)
127 default:
128 if keyRaw != nil {
129 diags = diags.Append(tfdiags.Sourceless(
130 tfdiags.Error,
131 "Invalid resource instance metadata in state",
132 fmt.Sprintf("Resource %s has an instance with the invalid instance key %#v.", rAddr.Absolute(moduleAddr), keyRaw),
133 ))
134 continue
135 }
136 key = addrs.NoKey
137 }
138
139 instAddr := rAddr.Instance(key)
140
141 obj := &states.ResourceInstanceObjectSrc{
142 SchemaVersion: isV4.SchemaVersion,
143 }
144
145 {
146 // Instance attributes
147 switch {
148 case isV4.AttributesRaw != nil:
149 obj.AttrsJSON = isV4.AttributesRaw
150 case isV4.AttributesFlat != nil:
151 obj.AttrsFlat = isV4.AttributesFlat
152 default:
153 // This is odd, but we'll accept it and just treat the
154 // object has being empty. In practice this should arise
155 // only from the contrived sort of state objects we tend
156 // to hand-write inline in tests.
157 obj.AttrsJSON = []byte{'{', '}'}
158 }
159 }
160
161 {
162 // Status
163 raw := isV4.Status
164 switch raw {
165 case "":
166 obj.Status = states.ObjectReady
167 case "tainted":
168 obj.Status = states.ObjectTainted
169 default:
170 diags = diags.Append(tfdiags.Sourceless(
171 tfdiags.Error,
172 "Invalid resource instance metadata in state",
173 fmt.Sprintf("Instance %s has invalid status %q.", instAddr.Absolute(moduleAddr), raw),
174 ))
175 continue
176 }
177 }
178
179 if raw := isV4.PrivateRaw; len(raw) > 0 {
180 obj.Private = raw
181 }
182
183 {
184 depsRaw := isV4.Dependencies
185 deps := make([]addrs.Referenceable, 0, len(depsRaw))
186 for _, depRaw := range depsRaw {
187 ref, refDiags := addrs.ParseRefStr(depRaw)
188 diags = diags.Append(refDiags)
189 if refDiags.HasErrors() {
190 continue
191 }
192 if len(ref.Remaining) != 0 {
193 diags = diags.Append(tfdiags.Sourceless(
194 tfdiags.Error,
195 "Invalid resource instance metadata in state",
196 fmt.Sprintf("Instance %s declares dependency on %q, which is not a reference to a dependable object.", instAddr.Absolute(moduleAddr), depRaw),
197 ))
198 }
199 if ref.Subject == nil {
200 // Should never happen
201 panic(fmt.Sprintf("parsing dependency %q for instance %s returned a nil address", depRaw, instAddr.Absolute(moduleAddr)))
202 }
203 deps = append(deps, ref.Subject)
204 }
205 obj.Dependencies = deps
206 }
207
208 switch {
209 case isV4.Deposed != "":
210 dk := states.DeposedKey(isV4.Deposed)
211 if len(dk) != 8 {
212 diags = diags.Append(tfdiags.Sourceless(
213 tfdiags.Error,
214 "Invalid resource instance metadata in state",
215 fmt.Sprintf("Instance %s has an object with deposed key %q, which is not correctly formatted.", instAddr.Absolute(moduleAddr), isV4.Deposed),
216 ))
217 continue
218 }
219 is := ms.ResourceInstance(instAddr)
220 if is.HasDeposed(dk) {
221 diags = diags.Append(tfdiags.Sourceless(
222 tfdiags.Error,
223 "Duplicate resource instance in state",
224 fmt.Sprintf("Instance %s deposed object %q appears multiple times in the state file.", instAddr.Absolute(moduleAddr), dk),
225 ))
226 continue
227 }
228
229 ms.SetResourceInstanceDeposed(instAddr, dk, obj, providerAddr)
230 default:
231 is := ms.ResourceInstance(instAddr)
232 if is.HasCurrent() {
233 diags = diags.Append(tfdiags.Sourceless(
234 tfdiags.Error,
235 "Duplicate resource instance in state",
236 fmt.Sprintf("Instance %s appears multiple times in the state file.", instAddr.Absolute(moduleAddr)),
237 ))
238 continue
239 }
240
241 ms.SetResourceInstanceCurrent(instAddr, obj, providerAddr)
242 }
243 }
244
245 // We repeat this after creating the instances because
246 // SetResourceInstanceCurrent automatically resets this metadata based
247 // on the incoming objects. That behavior is useful when we're making
248 // piecemeal updates to the state during an apply, but when we're
249 // reading the state file we want to reflect its contents exactly.
250 ms.SetResourceMeta(rAddr, eachMode, providerAddr)
251 }
252
253 // The root module is special in that we persist its attributes and thus
254 // need to reload them now. (For descendent modules we just re-calculate
255 // them based on the latest configuration on each run.)
256 {
257 rootModule := state.RootModule()
258 for name, fos := range sV4.RootOutputs {
259 os := &states.OutputValue{}
260 os.Sensitive = fos.Sensitive
261
262 ty, err := ctyjson.UnmarshalType([]byte(fos.ValueTypeRaw))
263 if err != nil {
264 diags = diags.Append(tfdiags.Sourceless(
265 tfdiags.Error,
266 "Invalid output value type in state",
267 fmt.Sprintf("The state file has an invalid type specification for output %q: %s.", name, err),
268 ))
269 continue
270 }
271
272 val, err := ctyjson.Unmarshal([]byte(fos.ValueRaw), ty)
273 if err != nil {
274 diags = diags.Append(tfdiags.Sourceless(
275 tfdiags.Error,
276 "Invalid output value saved in state",
277 fmt.Sprintf("The state file has an invalid value for output %q: %s.", name, err),
278 ))
279 continue
280 }
281
282 os.Value = val
283 rootModule.OutputValues[name] = os
284 }
285 }
286
287 file.State = state
288 return file, diags
289}
290
291func writeStateV4(file *File, w io.Writer) tfdiags.Diagnostics {
292 // Here we'll convert back from the "File" representation to our
293 // stateV4 struct representation and write that.
294 //
295 // While we support legacy state formats for reading, we only support the
296 // latest for writing and so if a V5 is added in future then this function
297 // should be deleted and replaced with a writeStateV5, even though the
298 // read/prepare V4 functions above would stick around.
299
300 var diags tfdiags.Diagnostics
301 if file == nil || file.State == nil {
302 panic("attempt to write nil state to file")
303 }
304
305 var terraformVersion string
306 if file.TerraformVersion != nil {
307 terraformVersion = file.TerraformVersion.String()
308 }
309
310 sV4 := &stateV4{
311 TerraformVersion: terraformVersion,
312 Serial: file.Serial,
313 Lineage: file.Lineage,
314 RootOutputs: map[string]outputStateV4{},
315 Resources: []resourceStateV4{},
316 }
317
318 for name, os := range file.State.RootModule().OutputValues {
319 src, err := ctyjson.Marshal(os.Value, os.Value.Type())
320 if err != nil {
321 diags = diags.Append(tfdiags.Sourceless(
322 tfdiags.Error,
323 "Failed to serialize output value in state",
324 fmt.Sprintf("An error occured while serializing output value %q: %s.", name, err),
325 ))
326 continue
327 }
328
329 typeSrc, err := ctyjson.MarshalType(os.Value.Type())
330 if err != nil {
331 diags = diags.Append(tfdiags.Sourceless(
332 tfdiags.Error,
333 "Failed to serialize output value in state",
334 fmt.Sprintf("An error occured while serializing the type of output value %q: %s.", name, err),
335 ))
336 continue
337 }
338
339 sV4.RootOutputs[name] = outputStateV4{
340 Sensitive: os.Sensitive,
341 ValueRaw: json.RawMessage(src),
342 ValueTypeRaw: json.RawMessage(typeSrc),
343 }
344 }
345
346 for _, ms := range file.State.Modules {
347 moduleAddr := ms.Addr
348 for _, rs := range ms.Resources {
349 resourceAddr := rs.Addr
350
351 var mode string
352 switch resourceAddr.Mode {
353 case addrs.ManagedResourceMode:
354 mode = "managed"
355 case addrs.DataResourceMode:
356 mode = "data"
357 default:
358 diags = diags.Append(tfdiags.Sourceless(
359 tfdiags.Error,
360 "Failed to serialize resource in state",
361 fmt.Sprintf("Resource %s has mode %s, which cannot be serialized in state", resourceAddr.Absolute(moduleAddr), resourceAddr.Mode),
362 ))
363 continue
364 }
365
366 var eachMode string
367 switch rs.EachMode {
368 case states.NoEach:
369 eachMode = ""
370 case states.EachList:
371 eachMode = "list"
372 case states.EachMap:
373 eachMode = "map"
374 default:
375 diags = diags.Append(tfdiags.Sourceless(
376 tfdiags.Error,
377 "Failed to serialize resource in state",
378 fmt.Sprintf("Resource %s has \"each\" mode %s, which cannot be serialized in state", resourceAddr.Absolute(moduleAddr), rs.EachMode),
379 ))
380 continue
381 }
382
383 sV4.Resources = append(sV4.Resources, resourceStateV4{
384 Module: moduleAddr.String(),
385 Mode: mode,
386 Type: resourceAddr.Type,
387 Name: resourceAddr.Name,
388 EachMode: eachMode,
389 ProviderConfig: rs.ProviderConfig.String(),
390 Instances: []instanceObjectStateV4{},
391 })
392 rsV4 := &(sV4.Resources[len(sV4.Resources)-1])
393
394 for key, is := range rs.Instances {
395 if is.HasCurrent() {
396 var objDiags tfdiags.Diagnostics
397 rsV4.Instances, objDiags = appendInstanceObjectStateV4(
398 rs, is, key, is.Current, states.NotDeposed,
399 rsV4.Instances,
400 )
401 diags = diags.Append(objDiags)
402 }
403 for dk, obj := range is.Deposed {
404 var objDiags tfdiags.Diagnostics
405 rsV4.Instances, objDiags = appendInstanceObjectStateV4(
406 rs, is, key, obj, dk,
407 rsV4.Instances,
408 )
409 diags = diags.Append(objDiags)
410 }
411 }
412 }
413 }
414
415 sV4.normalize()
416
417 src, err := json.MarshalIndent(sV4, "", " ")
418 if err != nil {
419 // Shouldn't happen if we do our conversion to *stateV4 correctly above.
420 diags = diags.Append(tfdiags.Sourceless(
421 tfdiags.Error,
422 "Failed to serialize state",
423 fmt.Sprintf("An error occured while serializing the state to save it. This is a bug in Terraform and should be reported: %s.", err),
424 ))
425 return diags
426 }
427 src = append(src, '\n')
428
429 _, err = w.Write(src)
430 if err != nil {
431 diags = diags.Append(tfdiags.Sourceless(
432 tfdiags.Error,
433 "Failed to write state",
434 fmt.Sprintf("An error occured while writing the serialized state: %s.", err),
435 ))
436 return diags
437 }
438
439 return diags
440}
441
442func appendInstanceObjectStateV4(rs *states.Resource, is *states.ResourceInstance, key addrs.InstanceKey, obj *states.ResourceInstanceObjectSrc, deposed states.DeposedKey, isV4s []instanceObjectStateV4) ([]instanceObjectStateV4, tfdiags.Diagnostics) {
443 var diags tfdiags.Diagnostics
444
445 var status string
446 switch obj.Status {
447 case states.ObjectReady:
448 status = ""
449 case states.ObjectTainted:
450 status = "tainted"
451 default:
452 diags = diags.Append(tfdiags.Sourceless(
453 tfdiags.Error,
454 "Failed to serialize resource instance in state",
455 fmt.Sprintf("Instance %s has status %s, which cannot be saved in state.", rs.Addr.Instance(key), obj.Status),
456 ))
457 }
458
459 var privateRaw []byte
460 if len(obj.Private) > 0 {
461 privateRaw = obj.Private
462 }
463
464 deps := make([]string, len(obj.Dependencies))
465 for i, depAddr := range obj.Dependencies {
466 deps[i] = depAddr.String()
467 }
468
469 var rawKey interface{}
470 switch tk := key.(type) {
471 case addrs.IntKey:
472 rawKey = int(tk)
473 case addrs.StringKey:
474 rawKey = string(tk)
475 default:
476 if key != addrs.NoKey {
477 diags = diags.Append(tfdiags.Sourceless(
478 tfdiags.Error,
479 "Failed to serialize resource instance in state",
480 fmt.Sprintf("Instance %s has an unsupported instance key: %#v.", rs.Addr.Instance(key), key),
481 ))
482 }
483 }
484
485 return append(isV4s, instanceObjectStateV4{
486 IndexKey: rawKey,
487 Deposed: string(deposed),
488 Status: status,
489 SchemaVersion: obj.SchemaVersion,
490 AttributesFlat: obj.AttrsFlat,
491 AttributesRaw: obj.AttrsJSON,
492 PrivateRaw: privateRaw,
493 Dependencies: deps,
494 }), diags
495}
496
497type stateV4 struct {
498 Version stateVersionV4 `json:"version"`
499 TerraformVersion string `json:"terraform_version"`
500 Serial uint64 `json:"serial"`
501 Lineage string `json:"lineage"`
502 RootOutputs map[string]outputStateV4 `json:"outputs"`
503 Resources []resourceStateV4 `json:"resources"`
504}
505
506// normalize makes some in-place changes to normalize the way items are
507// stored to ensure that two functionally-equivalent states will be stored
508// identically.
509func (s *stateV4) normalize() {
510 sort.Stable(sortResourcesV4(s.Resources))
511 for _, rs := range s.Resources {
512 sort.Stable(sortInstancesV4(rs.Instances))
513 }
514}
515
516type outputStateV4 struct {
517 ValueRaw json.RawMessage `json:"value"`
518 ValueTypeRaw json.RawMessage `json:"type"`
519 Sensitive bool `json:"sensitive,omitempty"`
520}
521
522type resourceStateV4 struct {
523 Module string `json:"module,omitempty"`
524 Mode string `json:"mode"`
525 Type string `json:"type"`
526 Name string `json:"name"`
527 EachMode string `json:"each,omitempty"`
528 ProviderConfig string `json:"provider"`
529 Instances []instanceObjectStateV4 `json:"instances"`
530}
531
532type instanceObjectStateV4 struct {
533 IndexKey interface{} `json:"index_key,omitempty"`
534 Status string `json:"status,omitempty"`
535 Deposed string `json:"deposed,omitempty"`
536
537 SchemaVersion uint64 `json:"schema_version"`
538 AttributesRaw json.RawMessage `json:"attributes,omitempty"`
539 AttributesFlat map[string]string `json:"attributes_flat,omitempty"`
540
541 PrivateRaw []byte `json:"private,omitempty"`
542
543 Dependencies []string `json:"depends_on,omitempty"`
544}
545
546// stateVersionV4 is a weird special type we use to produce our hard-coded
547// "version": 4 in the JSON serialization.
548type stateVersionV4 struct{}
549
550func (sv stateVersionV4) MarshalJSON() ([]byte, error) {
551 return []byte{'4'}, nil
552}
553
554func (sv stateVersionV4) UnmarshalJSON([]byte) error {
555 // Nothing to do: we already know we're version 4
556 return nil
557}
558
559type sortResourcesV4 []resourceStateV4
560
561func (sr sortResourcesV4) Len() int { return len(sr) }
562func (sr sortResourcesV4) Swap(i, j int) { sr[i], sr[j] = sr[j], sr[i] }
563func (sr sortResourcesV4) Less(i, j int) bool {
564 switch {
565 case sr[i].Mode != sr[j].Mode:
566 return sr[i].Mode < sr[j].Mode
567 case sr[i].Type != sr[j].Type:
568 return sr[i].Type < sr[j].Type
569 case sr[i].Name != sr[j].Name:
570 return sr[i].Name < sr[j].Name
571 default:
572 return false
573 }
574}
575
576type sortInstancesV4 []instanceObjectStateV4
577
578func (si sortInstancesV4) Len() int { return len(si) }
579func (si sortInstancesV4) Swap(i, j int) { si[i], si[j] = si[j], si[i] }
580func (si sortInstancesV4) Less(i, j int) bool {
581 ki := si[i].IndexKey
582 kj := si[j].IndexKey
583 if ki != kj {
584 if (ki == nil) != (kj == nil) {
585 return ki == nil
586 }
587 if kii, isInt := ki.(int); isInt {
588 if kji, isInt := kj.(int); isInt {
589 return kii < kji
590 }
591 return true
592 }
593 if kis, isStr := ki.(string); isStr {
594 if kjs, isStr := kj.(string); isStr {
595 return kis < kjs
596 }
597 return true
598 }
599 }
600 if si[i].Deposed != si[j].Deposed {
601 return si[i].Deposed < si[j].Deposed
602 }
603 return false
604}
diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/write.go b/vendor/github.com/hashicorp/terraform/states/statefile/write.go
new file mode 100644
index 0000000..548ba8a
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/statefile/write.go
@@ -0,0 +1,17 @@
1package statefile
2
3import (
4 "io"
5
6 tfversion "github.com/hashicorp/terraform/version"
7)
8
9// Write writes the given state to the given writer in the current state
10// serialization format.
11func Write(s *File, w io.Writer) error {
12 // Always record the current terraform version in the state.
13 s.TerraformVersion = tfversion.SemVer
14
15 diags := writeStateV4(s, w)
16 return diags.Err()
17}
diff --git a/vendor/github.com/hashicorp/terraform/states/sync.go b/vendor/github.com/hashicorp/terraform/states/sync.go
new file mode 100644
index 0000000..a377446
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/sync.go
@@ -0,0 +1,537 @@
1package states
2
3import (
4 "log"
5 "sync"
6
7 "github.com/hashicorp/terraform/addrs"
8 "github.com/zclconf/go-cty/cty"
9)
10
11// SyncState is a wrapper around State that provides concurrency-safe access to
12// various common operations that occur during a Terraform graph walk, or other
13// similar concurrent contexts.
14//
15// When a SyncState wrapper is in use, no concurrent direct access to the
16// underlying objects is permitted unless the caller first acquires an explicit
17// lock, using the Lock and Unlock methods. Most callers should _not_
18// explicitly lock, and should instead use the other methods of this type that
19// handle locking automatically.
20//
21// Since SyncState is able to safely consolidate multiple updates into a single
22// atomic operation, many of its methods are at a higher level than those
23// of the underlying types, and operate on the state as a whole rather than
24// on individual sub-structures of the state.
25//
26// SyncState can only protect against races within its own methods. It cannot
27// provide any guarantees about the order in which concurrent operations will
28// be processed, so callers may still need to employ higher-level techniques
29// for ensuring correct operation sequencing, such as building and walking
30// a dependency graph.
31type SyncState struct {
32 state *State
33 lock sync.RWMutex
34}
35
36// Module returns a snapshot of the state of the module instance with the given
37// address, or nil if no such module is tracked.
38//
39// The return value is a pointer to a copy of the module state, which the
40// caller may then freely access and mutate. However, since the module state
41// tends to be a large data structure with many child objects, where possible
42// callers should prefer to use a more granular accessor to access a child
43// module directly, and thus reduce the amount of copying required.
44func (s *SyncState) Module(addr addrs.ModuleInstance) *Module {
45 s.lock.RLock()
46 ret := s.state.Module(addr).DeepCopy()
47 s.lock.RUnlock()
48 return ret
49}
50
51// RemoveModule removes the entire state for the given module, taking with
52// it any resources associated with the module. This should generally be
53// called only for modules whose resources have all been destroyed, but
54// that is not enforced by this method.
55func (s *SyncState) RemoveModule(addr addrs.ModuleInstance) {
56 s.lock.Lock()
57 defer s.lock.Unlock()
58
59 s.state.RemoveModule(addr)
60}
61
62// OutputValue returns a snapshot of the state of the output value with the
63// given address, or nil if no such output value is tracked.
64//
65// The return value is a pointer to a copy of the output value state, which the
66// caller may then freely access and mutate.
67func (s *SyncState) OutputValue(addr addrs.AbsOutputValue) *OutputValue {
68 s.lock.RLock()
69 ret := s.state.OutputValue(addr).DeepCopy()
70 s.lock.RUnlock()
71 return ret
72}
73
74// SetOutputValue writes a given output value into the state, overwriting
75// any existing value of the same name.
76//
77// If the module containing the output is not yet tracked in state then it
78// be added as a side-effect.
79func (s *SyncState) SetOutputValue(addr addrs.AbsOutputValue, value cty.Value, sensitive bool) {
80 s.lock.Lock()
81 defer s.lock.Unlock()
82
83 ms := s.state.EnsureModule(addr.Module)
84 ms.SetOutputValue(addr.OutputValue.Name, value, sensitive)
85}
86
87// RemoveOutputValue removes the stored value for the output value with the
88// given address.
89//
90// If this results in its containing module being empty, the module will be
91// pruned from the state as a side-effect.
92func (s *SyncState) RemoveOutputValue(addr addrs.AbsOutputValue) {
93 s.lock.Lock()
94 defer s.lock.Unlock()
95
96 ms := s.state.Module(addr.Module)
97 if ms == nil {
98 return
99 }
100 ms.RemoveOutputValue(addr.OutputValue.Name)
101 s.maybePruneModule(addr.Module)
102}
103
104// LocalValue returns the current value associated with the given local value
105// address.
106func (s *SyncState) LocalValue(addr addrs.AbsLocalValue) cty.Value {
107 s.lock.RLock()
108 // cty.Value is immutable, so we don't need any extra copying here.
109 ret := s.state.LocalValue(addr)
110 s.lock.RUnlock()
111 return ret
112}
113
114// SetLocalValue writes a given output value into the state, overwriting
115// any existing value of the same name.
116//
117// If the module containing the local value is not yet tracked in state then it
118// will be added as a side-effect.
119func (s *SyncState) SetLocalValue(addr addrs.AbsLocalValue, value cty.Value) {
120 s.lock.Lock()
121 defer s.lock.Unlock()
122
123 ms := s.state.EnsureModule(addr.Module)
124 ms.SetLocalValue(addr.LocalValue.Name, value)
125}
126
127// RemoveLocalValue removes the stored value for the local value with the
128// given address.
129//
130// If this results in its containing module being empty, the module will be
131// pruned from the state as a side-effect.
132func (s *SyncState) RemoveLocalValue(addr addrs.AbsLocalValue) {
133 s.lock.Lock()
134 defer s.lock.Unlock()
135
136 ms := s.state.Module(addr.Module)
137 if ms == nil {
138 return
139 }
140 ms.RemoveLocalValue(addr.LocalValue.Name)
141 s.maybePruneModule(addr.Module)
142}
143
144// Resource returns a snapshot of the state of the resource with the given
145// address, or nil if no such resource is tracked.
146//
147// The return value is a pointer to a copy of the resource state, which the
148// caller may then freely access and mutate.
149func (s *SyncState) Resource(addr addrs.AbsResource) *Resource {
150 s.lock.RLock()
151 ret := s.state.Resource(addr).DeepCopy()
152 s.lock.RUnlock()
153 return ret
154}
155
156// ResourceInstance returns a snapshot of the state the resource instance with
157// the given address, or nil if no such instance is tracked.
158//
159// The return value is a pointer to a copy of the instance state, which the
160// caller may then freely access and mutate.
161func (s *SyncState) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstance {
162 s.lock.RLock()
163 ret := s.state.ResourceInstance(addr).DeepCopy()
164 s.lock.RUnlock()
165 return ret
166}
167
168// ResourceInstanceObject returns a snapshot of the current instance object
169// of the given generation belonging to the instance with the given address,
170// or nil if no such object is tracked..
171//
172// The return value is a pointer to a copy of the object, which the caller may
173// then freely access and mutate.
174func (s *SyncState) ResourceInstanceObject(addr addrs.AbsResourceInstance, gen Generation) *ResourceInstanceObjectSrc {
175 s.lock.RLock()
176 defer s.lock.RUnlock()
177
178 inst := s.state.ResourceInstance(addr)
179 if inst == nil {
180 return nil
181 }
182 return inst.GetGeneration(gen).DeepCopy()
183}
184
185// SetResourceMeta updates the resource-level metadata for the resource at
186// the given address, creating the containing module state and resource state
187// as a side-effect if not already present.
188func (s *SyncState) SetResourceMeta(addr addrs.AbsResource, eachMode EachMode, provider addrs.AbsProviderConfig) {
189 s.lock.Lock()
190 defer s.lock.Unlock()
191
192 ms := s.state.EnsureModule(addr.Module)
193 ms.SetResourceMeta(addr.Resource, eachMode, provider)
194}
195
196// RemoveResource removes the entire state for the given resource, taking with
197// it any instances associated with the resource. This should generally be
198// called only for resource objects whose instances have all been destroyed,
199// but that is not enforced by this method. (Use RemoveResourceIfEmpty instead
200// to safely check first.)
201func (s *SyncState) RemoveResource(addr addrs.AbsResource) {
202 s.lock.Lock()
203 defer s.lock.Unlock()
204
205 ms := s.state.EnsureModule(addr.Module)
206 ms.RemoveResource(addr.Resource)
207 s.maybePruneModule(addr.Module)
208}
209
210// RemoveResourceIfEmpty is similar to RemoveResource but first checks to
211// make sure there are no instances or objects left in the resource.
212//
213// Returns true if the resource was removed, or false if remaining child
214// objects prevented its removal. Returns true also if the resource was
215// already absent, and thus no action needed to be taken.
216func (s *SyncState) RemoveResourceIfEmpty(addr addrs.AbsResource) bool {
217 s.lock.Lock()
218 defer s.lock.Unlock()
219
220 ms := s.state.Module(addr.Module)
221 if ms == nil {
222 return true // nothing to do
223 }
224 rs := ms.Resource(addr.Resource)
225 if rs == nil {
226 return true // nothing to do
227 }
228 if len(rs.Instances) != 0 {
229 // We don't check here for the possibility of instances that exist
230 // but don't have any objects because it's the responsibility of the
231 // instance-mutation methods to prune those away automatically.
232 return false
233 }
234 ms.RemoveResource(addr.Resource)
235 s.maybePruneModule(addr.Module)
236 return true
237}
238
239// MaybeFixUpResourceInstanceAddressForCount deals with the situation where a
240// resource has changed from having "count" set to not set, or vice-versa, and
241// so we need to rename the zeroth instance key to no key at all, or vice-versa.
242//
243// Set countEnabled to true if the resource has count set in its new
244// configuration, or false if it does not.
245//
246// The state is modified in-place if necessary, moving a resource instance
247// between the two addresses. The return value is true if a change was made,
248// and false otherwise.
249func (s *SyncState) MaybeFixUpResourceInstanceAddressForCount(addr addrs.AbsResource, countEnabled bool) bool {
250 s.lock.Lock()
251 defer s.lock.Unlock()
252
253 ms := s.state.Module(addr.Module)
254 if ms == nil {
255 return false
256 }
257
258 relAddr := addr.Resource
259 rs := ms.Resource(relAddr)
260 if rs == nil {
261 return false
262 }
263 huntKey := addrs.NoKey
264 replaceKey := addrs.InstanceKey(addrs.IntKey(0))
265 if !countEnabled {
266 huntKey, replaceKey = replaceKey, huntKey
267 }
268
269 is, exists := rs.Instances[huntKey]
270 if !exists {
271 return false
272 }
273
274 if _, exists := rs.Instances[replaceKey]; exists {
275 // If the replacement key also exists then we'll do nothing and keep both.
276 return false
277 }
278
279 // If we get here then we need to "rename" from hunt to replace
280 rs.Instances[replaceKey] = is
281 delete(rs.Instances, huntKey)
282 return true
283}
284
285// SetResourceInstanceCurrent saves the given instance object as the current
286// generation of the resource instance with the given address, simulataneously
287// updating the recorded provider configuration address, dependencies, and
288// resource EachMode.
289//
290// Any existing current instance object for the given resource is overwritten.
291// Set obj to nil to remove the primary generation object altogether. If there
292// are no deposed objects then the instance as a whole will be removed, which
293// may in turn also remove the containing module if it becomes empty.
294//
295// The caller must ensure that the given ResourceInstanceObject is not
296// concurrently mutated during this call, but may be freely used again once
297// this function returns.
298//
299// The provider address and "each mode" are resource-wide settings and so they
300// are updated for all other instances of the same resource as a side-effect of
301// this call.
302//
303// If the containing module for this resource or the resource itself are not
304// already tracked in state then they will be added as a side-effect.
305func (s *SyncState) SetResourceInstanceCurrent(addr addrs.AbsResourceInstance, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) {
306 s.lock.Lock()
307 defer s.lock.Unlock()
308
309 ms := s.state.EnsureModule(addr.Module)
310 ms.SetResourceInstanceCurrent(addr.Resource, obj.DeepCopy(), provider)
311 s.maybePruneModule(addr.Module)
312}
313
314// SetResourceInstanceDeposed saves the given instance object as a deposed
315// generation of the resource instance with the given address and deposed key.
316//
317// Call this method only for pre-existing deposed objects that already have
318// a known DeposedKey. For example, this method is useful if reloading objects
319// that were persisted to a state file. To mark the current object as deposed,
320// use DeposeResourceInstanceObject instead.
321//
322// The caller must ensure that the given ResourceInstanceObject is not
323// concurrently mutated during this call, but may be freely used again once
324// this function returns.
325//
326// The resource that contains the given instance must already exist in the
327// state, or this method will panic. Use Resource to check first if its
328// presence is not already guaranteed.
329//
330// Any existing current instance object for the given resource and deposed key
331// is overwritten. Set obj to nil to remove the deposed object altogether. If
332// the instance is left with no objects after this operation then it will
333// be removed from its containing resource altogether.
334//
335// If the containing module for this resource or the resource itself are not
336// already tracked in state then they will be added as a side-effect.
337func (s *SyncState) SetResourceInstanceDeposed(addr addrs.AbsResourceInstance, key DeposedKey, obj *ResourceInstanceObjectSrc, provider addrs.AbsProviderConfig) {
338 s.lock.Lock()
339 defer s.lock.Unlock()
340
341 ms := s.state.EnsureModule(addr.Module)
342 ms.SetResourceInstanceDeposed(addr.Resource, key, obj.DeepCopy(), provider)
343 s.maybePruneModule(addr.Module)
344}
345
346// DeposeResourceInstanceObject moves the current instance object for the
347// given resource instance address into the deposed set, leaving the instance
348// without a current object.
349//
350// The return value is the newly-allocated deposed key, or NotDeposed if the
351// given instance is already lacking a current object.
352//
353// If the containing module for this resource or the resource itself are not
354// already tracked in state then there cannot be a current object for the
355// given instance, and so NotDeposed will be returned without modifying the
356// state at all.
357func (s *SyncState) DeposeResourceInstanceObject(addr addrs.AbsResourceInstance) DeposedKey {
358 s.lock.Lock()
359 defer s.lock.Unlock()
360
361 ms := s.state.Module(addr.Module)
362 if ms == nil {
363 return NotDeposed
364 }
365
366 return ms.deposeResourceInstanceObject(addr.Resource, NotDeposed)
367}
368
369// DeposeResourceInstanceObjectForceKey is like DeposeResourceInstanceObject
370// but uses a pre-allocated key. It's the caller's responsibility to ensure
371// that there aren't any races to use a particular key; this method will panic
372// if the given key is already in use.
373func (s *SyncState) DeposeResourceInstanceObjectForceKey(addr addrs.AbsResourceInstance, forcedKey DeposedKey) {
374 s.lock.Lock()
375 defer s.lock.Unlock()
376
377 if forcedKey == NotDeposed {
378 // Usage error: should use DeposeResourceInstanceObject in this case
379 panic("DeposeResourceInstanceObjectForceKey called without forced key")
380 }
381
382 ms := s.state.Module(addr.Module)
383 if ms == nil {
384 return // Nothing to do, since there can't be any current object either.
385 }
386
387 ms.deposeResourceInstanceObject(addr.Resource, forcedKey)
388}
389
390// ForgetResourceInstanceAll removes the record of all objects associated with
391// the specified resource instance, if present. If not present, this is a no-op.
392func (s *SyncState) ForgetResourceInstanceAll(addr addrs.AbsResourceInstance) {
393 s.lock.Lock()
394 defer s.lock.Unlock()
395
396 ms := s.state.Module(addr.Module)
397 if ms == nil {
398 return
399 }
400 ms.ForgetResourceInstanceAll(addr.Resource)
401 s.maybePruneModule(addr.Module)
402}
403
404// ForgetResourceInstanceDeposed removes the record of the deposed object with
405// the given address and key, if present. If not present, this is a no-op.
406func (s *SyncState) ForgetResourceInstanceDeposed(addr addrs.AbsResourceInstance, key DeposedKey) {
407 s.lock.Lock()
408 defer s.lock.Unlock()
409
410 ms := s.state.Module(addr.Module)
411 if ms == nil {
412 return
413 }
414 ms.ForgetResourceInstanceDeposed(addr.Resource, key)
415 s.maybePruneModule(addr.Module)
416}
417
418// MaybeRestoreResourceInstanceDeposed will restore the deposed object with the
419// given key on the specified resource as the current object for that instance
420// if and only if that would not cause us to forget an existing current
421// object for that instance.
422//
423// Returns true if the object was restored to current, or false if no change
424// was made at all.
425func (s *SyncState) MaybeRestoreResourceInstanceDeposed(addr addrs.AbsResourceInstance, key DeposedKey) bool {
426 s.lock.Lock()
427 defer s.lock.Unlock()
428
429 if key == NotDeposed {
430 panic("MaybeRestoreResourceInstanceDeposed called without DeposedKey")
431 }
432
433 ms := s.state.Module(addr.Module)
434 if ms == nil {
435 // Nothing to do, since the specified deposed object cannot exist.
436 return false
437 }
438
439 return ms.maybeRestoreResourceInstanceDeposed(addr.Resource, key)
440}
441
442// RemovePlannedResourceInstanceObjects removes from the state any resource
443// instance objects that have the status ObjectPlanned, indiciating that they
444// are just transient placeholders created during planning.
445//
446// Note that this does not restore any "ready" or "tainted" object that might
447// have been present before the planned object was written. The only real use
448// for this method is in preparing the state created during a refresh walk,
449// where we run the planning step for certain instances just to create enough
450// information to allow correct expression evaluation within provider and
451// data resource blocks. Discarding planned instances in that case is okay
452// because the refresh phase only creates planned objects to stand in for
453// objects that don't exist yet, and thus the planned object must have been
454// absent before by definition.
455func (s *SyncState) RemovePlannedResourceInstanceObjects() {
456 // TODO: Merge together the refresh and plan phases into a single walk,
457 // so we can remove the need to create this "partial plan" during refresh
458 // that we then need to clean up before proceeding.
459
460 s.lock.Lock()
461 defer s.lock.Unlock()
462
463 for _, ms := range s.state.Modules {
464 moduleAddr := ms.Addr
465
466 for _, rs := range ms.Resources {
467 resAddr := rs.Addr
468
469 for ik, is := range rs.Instances {
470 instAddr := resAddr.Instance(ik)
471
472 if is.Current != nil && is.Current.Status == ObjectPlanned {
473 // Setting the current instance to nil removes it from the
474 // state altogether if there are not also deposed instances.
475 ms.SetResourceInstanceCurrent(instAddr, nil, rs.ProviderConfig)
476 }
477
478 for dk, obj := range is.Deposed {
479 // Deposed objects should never be "planned", but we'll
480 // do this anyway for the sake of completeness.
481 if obj.Status == ObjectPlanned {
482 ms.ForgetResourceInstanceDeposed(instAddr, dk)
483 }
484 }
485 }
486 }
487
488 // We may have deleted some objects, which means that we may have
489 // left a module empty, and so we must prune to preserve the invariant
490 // that only the root module is allowed to be empty.
491 s.maybePruneModule(moduleAddr)
492 }
493}
494
495// Lock acquires an explicit lock on the state, allowing direct read and write
496// access to the returned state object. The caller must call Unlock once
497// access is no longer needed, and then immediately discard the state pointer
498// pointer.
499//
500// Most callers should not use this. Instead, use the concurrency-safe
501// accessors and mutators provided directly on SyncState.
502func (s *SyncState) Lock() *State {
503 s.lock.Lock()
504 return s.state
505}
506
507// Unlock releases a lock previously acquired by Lock, at which point the
508// caller must cease all use of the state pointer that was returned.
509//
510// Do not call this method except to end an explicit lock acquired by
511// Lock. If a caller calls Unlock without first holding the lock, behavior
512// is undefined.
513func (s *SyncState) Unlock() {
514 s.lock.Unlock()
515}
516
517// maybePruneModule will remove a module from the state altogether if it is
518// empty, unless it's the root module which must always be present.
519//
520// This helper method is not concurrency-safe on its own, so must only be
521// called while the caller is already holding the lock for writing.
522func (s *SyncState) maybePruneModule(addr addrs.ModuleInstance) {
523 if addr.IsRoot() {
524 // We never prune the root.
525 return
526 }
527
528 ms := s.state.Module(addr)
529 if ms == nil {
530 return
531 }
532
533 if ms.empty() {
534 log.Printf("[TRACE] states.SyncState: pruning %s because it is empty", addr)
535 s.state.RemoveModule(addr)
536 }
537}