diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/states')
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. | ||
3 | package 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 | |||
3 | package states | ||
4 | |||
5 | import "strconv" | ||
6 | |||
7 | func _() { | ||
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 | |||
16 | const ( | ||
17 | _EachMode_name_0 = "NoEach" | ||
18 | _EachMode_name_1 = "EachListEachMap" | ||
19 | ) | ||
20 | |||
21 | var ( | ||
22 | _EachMode_index_1 = [...]uint8{0, 8, 15} | ||
23 | ) | ||
24 | |||
25 | func (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 @@ | |||
1 | package 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. | ||
14 | type Generation interface { | ||
15 | generation() | ||
16 | } | ||
17 | |||
18 | // CurrentGen is the Generation representing the currently-active object for | ||
19 | // a resource instance. | ||
20 | var CurrentGen Generation | ||
21 | |||
22 | type currentGen struct{} | ||
23 | |||
24 | func (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 @@ | |||
1 | package states | ||
2 | |||
3 | import ( | ||
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. | ||
17 | type 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. | ||
41 | type ObjectStatus rune | ||
42 | |||
43 | //go:generate stringer -type ObjectStatus | ||
44 | |||
45 | const ( | ||
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. | ||
82 | func (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. | ||
110 | func (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 @@ | |||
1 | package states | ||
2 | |||
3 | import ( | ||
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. | ||
15 | type 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. | ||
69 | func (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. | ||
97 | func (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 @@ | |||
1 | package states | ||
2 | |||
3 | import ( | ||
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. | ||
10 | type 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. | ||
27 | func 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. | ||
39 | func (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. | ||
46 | func (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. | ||
57 | func (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. | ||
74 | func (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. | ||
90 | func (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. | ||
126 | func (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. | ||
151 | func (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. | ||
168 | func (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. | ||
193 | func (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. | ||
203 | func (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. | ||
225 | func (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. | ||
237 | func (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. | ||
243 | func (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. | ||
250 | func (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. | ||
260 | func (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. | ||
275 | func (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 | |||
3 | package states | ||
4 | |||
5 | import "strconv" | ||
6 | |||
7 | func _() { | ||
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 | |||
16 | const ( | ||
17 | _ObjectStatus_name_0 = "ObjectPlanned" | ||
18 | _ObjectStatus_name_1 = "ObjectReady" | ||
19 | _ObjectStatus_name_2 = "ObjectTainted" | ||
20 | ) | ||
21 | |||
22 | func (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 @@ | |||
1 | package states | ||
2 | |||
3 | import ( | ||
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. | ||
11 | type 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 @@ | |||
1 | package states | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "math/rand" | ||
6 | "time" | ||
7 | |||
8 | "github.com/hashicorp/terraform/addrs" | ||
9 | ) | ||
10 | |||
11 | // Resource represents the state of a resource. | ||
12 | type 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. | ||
38 | func (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. | ||
47 | func (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. | ||
57 | type 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. | ||
71 | func 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. | ||
81 | func (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. | ||
87 | func (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. | ||
93 | func (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. | ||
99 | func (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. | ||
107 | func (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. | ||
129 | func (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. | ||
151 | func (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. | ||
157 | func (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. | ||
170 | type EachMode rune | ||
171 | |||
172 | const ( | ||
173 | NoEach EachMode = 0 | ||
174 | EachList EachMode = 'L' | ||
175 | EachMap EachMode = 'M' | ||
176 | ) | ||
177 | |||
178 | //go:generate stringer -type EachMode | ||
179 | |||
180 | func 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. | ||
196 | type 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. | ||
200 | const NotDeposed = DeposedKey("") | ||
201 | |||
202 | var 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. | ||
208 | func NewDeposedKey() DeposedKey { | ||
209 | v := deposedKeyRand.Uint32() | ||
210 | return DeposedKey(fmt.Sprintf("%08x", v)) | ||
211 | } | ||
212 | |||
213 | func (k DeposedKey) String() string { | ||
214 | return string(k) | ||
215 | } | ||
216 | |||
217 | func (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. | ||
231 | func (k DeposedKey) Generation() Generation { | ||
232 | if k == NotDeposed { | ||
233 | return CurrentGen | ||
234 | } | ||
235 | return k | ||
236 | } | ||
237 | |||
238 | // generation is an implementation of Generation. | ||
239 | func (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 @@ | |||
1 | package states | ||
2 | |||
3 | import ( | ||
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. | ||
22 | type 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. | ||
29 | func 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. | ||
40 | func 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. | ||
49 | func (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. | ||
66 | func (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. | ||
82 | func (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). | ||
91 | func (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. | ||
103 | func (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. | ||
114 | func (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. | ||
128 | func (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. | ||
138 | func (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. | ||
151 | func (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. | ||
161 | func (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. | ||
173 | func (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. | ||
215 | func (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. | ||
225 | func (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 @@ | |||
1 | package states | ||
2 | |||
3 | import ( | ||
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. | ||
22 | func (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. | ||
44 | func (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. | ||
79 | func (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. | ||
105 | func (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. | ||
129 | func (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. | ||
177 | func (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. | ||
209 | func (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 @@ | |||
1 | package states | ||
2 | |||
3 | import ( | ||
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. | ||
13 | func (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 @@ | |||
1 | package states | ||
2 | |||
3 | import ( | ||
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. | ||
26 | func (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. | ||
81 | func (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. | ||
255 | func 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 @@ | |||
1 | package statefile | ||
2 | |||
3 | import ( | ||
4 | "encoding/json" | ||
5 | "fmt" | ||
6 | |||
7 | "github.com/hashicorp/terraform/tfdiags" | ||
8 | ) | ||
9 | |||
10 | const 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. | ||
14 | func 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. | ||
3 | package 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 @@ | |||
1 | package statefile | ||
2 | |||
3 | import ( | ||
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. | ||
13 | type 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 | |||
34 | func 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. | ||
52 | func (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 @@ | |||
1 | package statefile | ||
2 | |||
3 | import ( | ||
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. | ||
16 | func 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 @@ | |||
1 | package statefile | ||
2 | |||
3 | import ( | ||
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. | ||
18 | var 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. | ||
29 | func 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 | |||
77 | func 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 | |||
132 | func 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. | ||
181 | func 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". | ||
207 | const unsupportedFormat = "Unsupported state file format" | ||
208 | |||
209 | const 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 @@ | |||
1 | package 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. | ||
12 | func 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 @@ | |||
1 | package statefile | ||
2 | |||
3 | import ( | ||
4 | "encoding/json" | ||
5 | "fmt" | ||
6 | |||
7 | "github.com/hashicorp/terraform/tfdiags" | ||
8 | ) | ||
9 | |||
10 | func 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 | |||
24 | func 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. | ||
45 | type 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 | |||
62 | type 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 | |||
71 | type 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 | |||
102 | type 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 | |||
153 | type 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 | |||
169 | type 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 @@ | |||
1 | package statefile | ||
2 | |||
3 | import ( | ||
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 | ||
12 | func 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 | |||
45 | func (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 | |||
61 | func (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 | |||
111 | func (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 | |||
147 | func (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 @@ | |||
1 | package statefile | ||
2 | |||
3 | import ( | ||
4 | "encoding/json" | ||
5 | "fmt" | ||
6 | "sync" | ||
7 | |||
8 | "github.com/hashicorp/terraform/tfdiags" | ||
9 | ) | ||
10 | |||
11 | func 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 | |||
25 | func 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. | ||
46 | type 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 | |||
79 | type 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 | |||
88 | type 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 | |||
102 | type 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 | |||
137 | type 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 | |||
185 | type 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 | |||
205 | type 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 @@ | |||
1 | package statefile | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "log" | ||
6 | "regexp" | ||
7 | "sort" | ||
8 | "strconv" | ||
9 | "strings" | ||
10 | |||
11 | "github.com/mitchellh/copystructure" | ||
12 | ) | ||
13 | |||
14 | func 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 | |||
53 | func 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. | ||
129 | func 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 @@ | |||
1 | package statefile | ||
2 | |||
3 | import ( | ||
4 | "encoding/json" | ||
5 | "fmt" | ||
6 | |||
7 | "github.com/hashicorp/terraform/tfdiags" | ||
8 | ) | ||
9 | |||
10 | func 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 | |||
24 | func 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. | ||
50 | type 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 @@ | |||
1 | package statefile | ||
2 | |||
3 | import ( | ||
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 | |||
17 | func 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 | |||
216 | func 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". | ||
320 | func 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. | ||
363 | func 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 | |||
417 | func 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 @@ | |||
1 | package statefile | ||
2 | |||
3 | import ( | ||
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 | |||
17 | func 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 | |||
31 | func 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 | |||
291 | func 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 | |||
442 | func 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 | |||
497 | type 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. | ||
509 | func (s *stateV4) normalize() { | ||
510 | sort.Stable(sortResourcesV4(s.Resources)) | ||
511 | for _, rs := range s.Resources { | ||
512 | sort.Stable(sortInstancesV4(rs.Instances)) | ||
513 | } | ||
514 | } | ||
515 | |||
516 | type outputStateV4 struct { | ||
517 | ValueRaw json.RawMessage `json:"value"` | ||
518 | ValueTypeRaw json.RawMessage `json:"type"` | ||
519 | Sensitive bool `json:"sensitive,omitempty"` | ||
520 | } | ||
521 | |||
522 | type 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 | |||
532 | type 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. | ||
548 | type stateVersionV4 struct{} | ||
549 | |||
550 | func (sv stateVersionV4) MarshalJSON() ([]byte, error) { | ||
551 | return []byte{'4'}, nil | ||
552 | } | ||
553 | |||
554 | func (sv stateVersionV4) UnmarshalJSON([]byte) error { | ||
555 | // Nothing to do: we already know we're version 4 | ||
556 | return nil | ||
557 | } | ||
558 | |||
559 | type sortResourcesV4 []resourceStateV4 | ||
560 | |||
561 | func (sr sortResourcesV4) Len() int { return len(sr) } | ||
562 | func (sr sortResourcesV4) Swap(i, j int) { sr[i], sr[j] = sr[j], sr[i] } | ||
563 | func (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 | |||
576 | type sortInstancesV4 []instanceObjectStateV4 | ||
577 | |||
578 | func (si sortInstancesV4) Len() int { return len(si) } | ||
579 | func (si sortInstancesV4) Swap(i, j int) { si[i], si[j] = si[j], si[i] } | ||
580 | func (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 @@ | |||
1 | package statefile | ||
2 | |||
3 | import ( | ||
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. | ||
11 | func 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 @@ | |||
1 | package states | ||
2 | |||
3 | import ( | ||
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. | ||
31 | type 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. | ||
44 | func (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. | ||
55 | func (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. | ||
67 | func (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. | ||
79 | func (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. | ||
92 | func (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. | ||
106 | func (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. | ||
119 | func (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. | ||
132 | func (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. | ||
149 | func (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. | ||
161 | func (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. | ||
174 | func (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. | ||
188 | func (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.) | ||
201 | func (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. | ||
216 | func (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. | ||
249 | func (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. | ||
305 | func (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. | ||
337 | func (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. | ||
357 | func (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. | ||
373 | func (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. | ||
392 | func (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. | ||
406 | func (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. | ||
425 | func (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. | ||
455 | func (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. | ||
502 | func (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. | ||
513 | func (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. | ||
522 | func (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 | } | ||