diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/states/state_string.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/states/state_string.go | 279 |
1 files changed, 279 insertions, 0 deletions
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 | } | ||