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