]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
1 | package terraform |
2 | ||
3 | import ( | |
4 | "bufio" | |
5 | "bytes" | |
6 | "encoding/json" | |
7 | "errors" | |
8 | "fmt" | |
9 | "io" | |
10 | "io/ioutil" | |
11 | "log" | |
12 | "reflect" | |
13 | "sort" | |
14 | "strconv" | |
15 | "strings" | |
16 | "sync" | |
17 | ||
18 | "github.com/hashicorp/go-multierror" | |
19 | "github.com/hashicorp/go-version" | |
20 | "github.com/hashicorp/terraform/config" | |
21 | "github.com/mitchellh/copystructure" | |
22 | "github.com/satori/go.uuid" | |
23 | ) | |
24 | ||
25 | const ( | |
26 | // StateVersion is the current version for our state file | |
27 | StateVersion = 3 | |
28 | ) | |
29 | ||
30 | // rootModulePath is the path of the root module | |
31 | var rootModulePath = []string{"root"} | |
32 | ||
33 | // normalizeModulePath takes a raw module path and returns a path that | |
34 | // has the rootModulePath prepended to it. If I could go back in time I | |
35 | // would've never had a rootModulePath (empty path would be root). We can | |
36 | // still fix this but thats a big refactor that my branch doesn't make sense | |
37 | // for. Instead, this function normalizes paths. | |
38 | func normalizeModulePath(p []string) []string { | |
39 | k := len(rootModulePath) | |
40 | ||
41 | // If we already have a root module prefix, we're done | |
42 | if len(p) >= len(rootModulePath) { | |
43 | if reflect.DeepEqual(p[:k], rootModulePath) { | |
44 | return p | |
45 | } | |
46 | } | |
47 | ||
48 | // None? Prefix it | |
49 | result := make([]string, len(rootModulePath)+len(p)) | |
50 | copy(result, rootModulePath) | |
51 | copy(result[k:], p) | |
52 | return result | |
53 | } | |
54 | ||
55 | // State keeps track of a snapshot state-of-the-world that Terraform | |
56 | // can use to keep track of what real world resources it is actually | |
57 | // managing. | |
58 | type State struct { | |
59 | // Version is the state file protocol version. | |
60 | Version int `json:"version"` | |
61 | ||
62 | // TFVersion is the version of Terraform that wrote this state. | |
63 | TFVersion string `json:"terraform_version,omitempty"` | |
64 | ||
65 | // Serial is incremented on any operation that modifies | |
66 | // the State file. It is used to detect potentially conflicting | |
67 | // updates. | |
68 | Serial int64 `json:"serial"` | |
69 | ||
70 | // Lineage is set when a new, blank state is created and then | |
71 | // never updated. This allows us to determine whether the serials | |
72 | // of two states can be meaningfully compared. | |
73 | // Apart from the guarantee that collisions between two lineages | |
74 | // are very unlikely, this value is opaque and external callers | |
75 | // should only compare lineage strings byte-for-byte for equality. | |
76 | Lineage string `json:"lineage"` | |
77 | ||
78 | // Remote is used to track the metadata required to | |
79 | // pull and push state files from a remote storage endpoint. | |
80 | Remote *RemoteState `json:"remote,omitempty"` | |
81 | ||
82 | // Backend tracks the configuration for the backend in use with | |
83 | // this state. This is used to track any changes in the backend | |
84 | // configuration. | |
85 | Backend *BackendState `json:"backend,omitempty"` | |
86 | ||
87 | // Modules contains all the modules in a breadth-first order | |
88 | Modules []*ModuleState `json:"modules"` | |
89 | ||
90 | mu sync.Mutex | |
91 | } | |
92 | ||
93 | func (s *State) Lock() { s.mu.Lock() } | |
94 | func (s *State) Unlock() { s.mu.Unlock() } | |
95 | ||
96 | // NewState is used to initialize a blank state | |
97 | func NewState() *State { | |
98 | s := &State{} | |
99 | s.init() | |
100 | return s | |
101 | } | |
102 | ||
103 | // Children returns the ModuleStates that are direct children of | |
104 | // the given path. If the path is "root", for example, then children | |
105 | // returned might be "root.child", but not "root.child.grandchild". | |
106 | func (s *State) Children(path []string) []*ModuleState { | |
107 | s.Lock() | |
108 | defer s.Unlock() | |
109 | // TODO: test | |
110 | ||
111 | return s.children(path) | |
112 | } | |
113 | ||
114 | func (s *State) children(path []string) []*ModuleState { | |
115 | result := make([]*ModuleState, 0) | |
116 | for _, m := range s.Modules { | |
117 | if m == nil { | |
118 | continue | |
119 | } | |
120 | ||
121 | if len(m.Path) != len(path)+1 { | |
122 | continue | |
123 | } | |
124 | if !reflect.DeepEqual(path, m.Path[:len(path)]) { | |
125 | continue | |
126 | } | |
127 | ||
128 | result = append(result, m) | |
129 | } | |
130 | ||
131 | return result | |
132 | } | |
133 | ||
134 | // AddModule adds the module with the given path to the state. | |
135 | // | |
136 | // This should be the preferred method to add module states since it | |
137 | // allows us to optimize lookups later as well as control sorting. | |
138 | func (s *State) AddModule(path []string) *ModuleState { | |
139 | s.Lock() | |
140 | defer s.Unlock() | |
141 | ||
142 | return s.addModule(path) | |
143 | } | |
144 | ||
145 | func (s *State) addModule(path []string) *ModuleState { | |
146 | // check if the module exists first | |
147 | m := s.moduleByPath(path) | |
148 | if m != nil { | |
149 | return m | |
150 | } | |
151 | ||
152 | m = &ModuleState{Path: path} | |
153 | m.init() | |
154 | s.Modules = append(s.Modules, m) | |
155 | s.sort() | |
156 | return m | |
157 | } | |
158 | ||
159 | // ModuleByPath is used to lookup the module state for the given path. | |
160 | // This should be the preferred lookup mechanism as it allows for future | |
161 | // lookup optimizations. | |
162 | func (s *State) ModuleByPath(path []string) *ModuleState { | |
163 | if s == nil { | |
164 | return nil | |
165 | } | |
166 | s.Lock() | |
167 | defer s.Unlock() | |
168 | ||
169 | return s.moduleByPath(path) | |
170 | } | |
171 | ||
172 | func (s *State) moduleByPath(path []string) *ModuleState { | |
173 | for _, mod := range s.Modules { | |
174 | if mod == nil { | |
175 | continue | |
176 | } | |
177 | if mod.Path == nil { | |
178 | panic("missing module path") | |
179 | } | |
180 | if reflect.DeepEqual(mod.Path, path) { | |
181 | return mod | |
182 | } | |
183 | } | |
184 | return nil | |
185 | } | |
186 | ||
187 | // ModuleOrphans returns all the module orphans in this state by | |
188 | // returning their full paths. These paths can be used with ModuleByPath | |
189 | // to return the actual state. | |
190 | func (s *State) ModuleOrphans(path []string, c *config.Config) [][]string { | |
191 | s.Lock() | |
192 | defer s.Unlock() | |
193 | ||
194 | return s.moduleOrphans(path, c) | |
195 | ||
196 | } | |
197 | ||
198 | func (s *State) moduleOrphans(path []string, c *config.Config) [][]string { | |
199 | // direct keeps track of what direct children we have both in our config | |
200 | // and in our state. childrenKeys keeps track of what isn't an orphan. | |
201 | direct := make(map[string]struct{}) | |
202 | childrenKeys := make(map[string]struct{}) | |
203 | if c != nil { | |
204 | for _, m := range c.Modules { | |
205 | childrenKeys[m.Name] = struct{}{} | |
206 | direct[m.Name] = struct{}{} | |
207 | } | |
208 | } | |
209 | ||
210 | // Go over the direct children and find any that aren't in our keys. | |
211 | var orphans [][]string | |
212 | for _, m := range s.children(path) { | |
213 | key := m.Path[len(m.Path)-1] | |
214 | ||
215 | // Record that we found this key as a direct child. We use this | |
216 | // later to find orphan nested modules. | |
217 | direct[key] = struct{}{} | |
218 | ||
219 | // If we have a direct child still in our config, it is not an orphan | |
220 | if _, ok := childrenKeys[key]; ok { | |
221 | continue | |
222 | } | |
223 | ||
224 | orphans = append(orphans, m.Path) | |
225 | } | |
226 | ||
227 | // Find the orphans that are nested... | |
228 | for _, m := range s.Modules { | |
229 | if m == nil { | |
230 | continue | |
231 | } | |
232 | ||
233 | // We only want modules that are at least grandchildren | |
234 | if len(m.Path) < len(path)+2 { | |
235 | continue | |
236 | } | |
237 | ||
238 | // If it isn't part of our tree, continue | |
239 | if !reflect.DeepEqual(path, m.Path[:len(path)]) { | |
240 | continue | |
241 | } | |
242 | ||
243 | // If we have the direct child, then just skip it. | |
244 | key := m.Path[len(path)] | |
245 | if _, ok := direct[key]; ok { | |
246 | continue | |
247 | } | |
248 | ||
249 | orphanPath := m.Path[:len(path)+1] | |
250 | ||
251 | // Don't double-add if we've already added this orphan (which can happen if | |
252 | // there are multiple nested sub-modules that get orphaned together). | |
253 | alreadyAdded := false | |
254 | for _, o := range orphans { | |
255 | if reflect.DeepEqual(o, orphanPath) { | |
256 | alreadyAdded = true | |
257 | break | |
258 | } | |
259 | } | |
260 | if alreadyAdded { | |
261 | continue | |
262 | } | |
263 | ||
264 | // Add this orphan | |
265 | orphans = append(orphans, orphanPath) | |
266 | } | |
267 | ||
268 | return orphans | |
269 | } | |
270 | ||
271 | // Empty returns true if the state is empty. | |
272 | func (s *State) Empty() bool { | |
273 | if s == nil { | |
274 | return true | |
275 | } | |
276 | s.Lock() | |
277 | defer s.Unlock() | |
278 | ||
279 | return len(s.Modules) == 0 | |
280 | } | |
281 | ||
282 | // HasResources returns true if the state contains any resources. | |
283 | // | |
284 | // This is similar to !s.Empty, but returns true also in the case where the | |
285 | // state has modules but all of them are devoid of resources. | |
286 | func (s *State) HasResources() bool { | |
287 | if s.Empty() { | |
288 | return false | |
289 | } | |
290 | ||
291 | for _, mod := range s.Modules { | |
292 | if len(mod.Resources) > 0 { | |
293 | return true | |
294 | } | |
295 | } | |
296 | ||
297 | return false | |
298 | } | |
299 | ||
300 | // IsRemote returns true if State represents a state that exists and is | |
301 | // remote. | |
302 | func (s *State) IsRemote() bool { | |
303 | if s == nil { | |
304 | return false | |
305 | } | |
306 | s.Lock() | |
307 | defer s.Unlock() | |
308 | ||
309 | if s.Remote == nil { | |
310 | return false | |
311 | } | |
312 | if s.Remote.Type == "" { | |
313 | return false | |
314 | } | |
315 | ||
316 | return true | |
317 | } | |
318 | ||
319 | // Validate validates the integrity of this state file. | |
320 | // | |
321 | // Certain properties of the statefile are expected by Terraform in order | |
322 | // to behave properly. The core of Terraform will assume that once it | |
323 | // receives a State structure that it has been validated. This validation | |
324 | // check should be called to ensure that. | |
325 | // | |
326 | // If this returns an error, then the user should be notified. The error | |
327 | // response will include detailed information on the nature of the error. | |
328 | func (s *State) Validate() error { | |
329 | s.Lock() | |
330 | defer s.Unlock() | |
331 | ||
332 | var result error | |
333 | ||
334 | // !!!! FOR DEVELOPERS !!!! | |
335 | // | |
336 | // Any errors returned from this Validate function will BLOCK TERRAFORM | |
337 | // from loading a state file. Therefore, this should only contain checks | |
338 | // that are only resolvable through manual intervention. | |
339 | // | |
340 | // !!!! FOR DEVELOPERS !!!! | |
341 | ||
342 | // Make sure there are no duplicate module states. We open a new | |
343 | // block here so we can use basic variable names and future validations | |
344 | // can do the same. | |
345 | { | |
346 | found := make(map[string]struct{}) | |
347 | for _, ms := range s.Modules { | |
348 | if ms == nil { | |
349 | continue | |
350 | } | |
351 | ||
352 | key := strings.Join(ms.Path, ".") | |
353 | if _, ok := found[key]; ok { | |
354 | result = multierror.Append(result, fmt.Errorf( | |
355 | strings.TrimSpace(stateValidateErrMultiModule), key)) | |
356 | continue | |
357 | } | |
358 | ||
359 | found[key] = struct{}{} | |
360 | } | |
361 | } | |
362 | ||
363 | return result | |
364 | } | |
365 | ||
366 | // Remove removes the item in the state at the given address, returning | |
367 | // any errors that may have occurred. | |
368 | // | |
369 | // If the address references a module state or resource, it will delete | |
370 | // all children as well. To check what will be deleted, use a StateFilter | |
371 | // first. | |
372 | func (s *State) Remove(addr ...string) error { | |
373 | s.Lock() | |
374 | defer s.Unlock() | |
375 | ||
376 | // Filter out what we need to delete | |
377 | filter := &StateFilter{State: s} | |
378 | results, err := filter.Filter(addr...) | |
379 | if err != nil { | |
380 | return err | |
381 | } | |
382 | ||
383 | // If we have no results, just exit early, we're not going to do anything. | |
384 | // While what happens below is fairly fast, this is an important early | |
385 | // exit since the prune below might modify the state more and we don't | |
386 | // want to modify the state if we don't have to. | |
387 | if len(results) == 0 { | |
388 | return nil | |
389 | } | |
390 | ||
391 | // Go through each result and grab what we need | |
392 | removed := make(map[interface{}]struct{}) | |
393 | for _, r := range results { | |
394 | // Convert the path to our own type | |
395 | path := append([]string{"root"}, r.Path...) | |
396 | ||
397 | // If we removed this already, then ignore | |
398 | if _, ok := removed[r.Value]; ok { | |
399 | continue | |
400 | } | |
401 | ||
402 | // If we removed the parent already, then ignore | |
403 | if r.Parent != nil { | |
404 | if _, ok := removed[r.Parent.Value]; ok { | |
405 | continue | |
406 | } | |
407 | } | |
408 | ||
409 | // Add this to the removed list | |
410 | removed[r.Value] = struct{}{} | |
411 | ||
412 | switch v := r.Value.(type) { | |
413 | case *ModuleState: | |
414 | s.removeModule(path, v) | |
415 | case *ResourceState: | |
416 | s.removeResource(path, v) | |
417 | case *InstanceState: | |
418 | s.removeInstance(path, r.Parent.Value.(*ResourceState), v) | |
419 | default: | |
420 | return fmt.Errorf("unknown type to delete: %T", r.Value) | |
421 | } | |
422 | } | |
423 | ||
424 | // Prune since the removal functions often do the bare minimum to | |
425 | // remove a thing and may leave around dangling empty modules, resources, | |
426 | // etc. Prune will clean that all up. | |
427 | s.prune() | |
428 | ||
429 | return nil | |
430 | } | |
431 | ||
432 | func (s *State) removeModule(path []string, v *ModuleState) { | |
433 | for i, m := range s.Modules { | |
434 | if m == v { | |
435 | s.Modules, s.Modules[len(s.Modules)-1] = append(s.Modules[:i], s.Modules[i+1:]...), nil | |
436 | return | |
437 | } | |
438 | } | |
439 | } | |
440 | ||
441 | func (s *State) removeResource(path []string, v *ResourceState) { | |
442 | // Get the module this resource lives in. If it doesn't exist, we're done. | |
443 | mod := s.moduleByPath(path) | |
444 | if mod == nil { | |
445 | return | |
446 | } | |
447 | ||
448 | // Find this resource. This is a O(N) lookup when if we had the key | |
449 | // it could be O(1) but even with thousands of resources this shouldn't | |
450 | // matter right now. We can easily up performance here when the time comes. | |
451 | for k, r := range mod.Resources { | |
452 | if r == v { | |
453 | // Found it | |
454 | delete(mod.Resources, k) | |
455 | return | |
456 | } | |
457 | } | |
458 | } | |
459 | ||
460 | func (s *State) removeInstance(path []string, r *ResourceState, v *InstanceState) { | |
461 | // Go through the resource and find the instance that matches this | |
462 | // (if any) and remove it. | |
463 | ||
464 | // Check primary | |
465 | if r.Primary == v { | |
466 | r.Primary = nil | |
467 | return | |
468 | } | |
469 | ||
470 | // Check lists | |
471 | lists := [][]*InstanceState{r.Deposed} | |
472 | for _, is := range lists { | |
473 | for i, instance := range is { | |
474 | if instance == v { | |
475 | // Found it, remove it | |
476 | is, is[len(is)-1] = append(is[:i], is[i+1:]...), nil | |
477 | ||
478 | // Done | |
479 | return | |
480 | } | |
481 | } | |
482 | } | |
483 | } | |
484 | ||
485 | // RootModule returns the ModuleState for the root module | |
486 | func (s *State) RootModule() *ModuleState { | |
487 | root := s.ModuleByPath(rootModulePath) | |
488 | if root == nil { | |
489 | panic("missing root module") | |
490 | } | |
491 | return root | |
492 | } | |
493 | ||
494 | // Equal tests if one state is equal to another. | |
495 | func (s *State) Equal(other *State) bool { | |
496 | // If one is nil, we do a direct check | |
497 | if s == nil || other == nil { | |
498 | return s == other | |
499 | } | |
500 | ||
501 | s.Lock() | |
502 | defer s.Unlock() | |
503 | return s.equal(other) | |
504 | } | |
505 | ||
506 | func (s *State) equal(other *State) bool { | |
507 | if s == nil || other == nil { | |
508 | return s == other | |
509 | } | |
510 | ||
511 | // If the versions are different, they're certainly not equal | |
512 | if s.Version != other.Version { | |
513 | return false | |
514 | } | |
515 | ||
516 | // If any of the modules are not equal, then this state isn't equal | |
517 | if len(s.Modules) != len(other.Modules) { | |
518 | return false | |
519 | } | |
520 | for _, m := range s.Modules { | |
521 | // This isn't very optimal currently but works. | |
522 | otherM := other.moduleByPath(m.Path) | |
523 | if otherM == nil { | |
524 | return false | |
525 | } | |
526 | ||
527 | // If they're not equal, then we're not equal! | |
528 | if !m.Equal(otherM) { | |
529 | return false | |
530 | } | |
531 | } | |
532 | ||
533 | return true | |
534 | } | |
535 | ||
536 | type StateAgeComparison int | |
537 | ||
538 | const ( | |
539 | StateAgeEqual StateAgeComparison = 0 | |
540 | StateAgeReceiverNewer StateAgeComparison = 1 | |
541 | StateAgeReceiverOlder StateAgeComparison = -1 | |
542 | ) | |
543 | ||
544 | // CompareAges compares one state with another for which is "older". | |
545 | // | |
546 | // This is a simple check using the state's serial, and is thus only as | |
547 | // reliable as the serial itself. In the normal case, only one state | |
548 | // exists for a given combination of lineage/serial, but Terraform | |
549 | // does not guarantee this and so the result of this method should be | |
550 | // used with care. | |
551 | // | |
552 | // Returns an integer that is negative if the receiver is older than | |
553 | // the argument, positive if the converse, and zero if they are equal. | |
554 | // An error is returned if the two states are not of the same lineage, | |
555 | // in which case the integer returned has no meaning. | |
556 | func (s *State) CompareAges(other *State) (StateAgeComparison, error) { | |
557 | // nil states are "older" than actual states | |
558 | switch { | |
559 | case s != nil && other == nil: | |
560 | return StateAgeReceiverNewer, nil | |
561 | case s == nil && other != nil: | |
562 | return StateAgeReceiverOlder, nil | |
563 | case s == nil && other == nil: | |
564 | return StateAgeEqual, nil | |
565 | } | |
566 | ||
567 | if !s.SameLineage(other) { | |
568 | return StateAgeEqual, fmt.Errorf( | |
569 | "can't compare two states of differing lineage", | |
570 | ) | |
571 | } | |
572 | ||
573 | s.Lock() | |
574 | defer s.Unlock() | |
575 | ||
576 | switch { | |
577 | case s.Serial < other.Serial: | |
578 | return StateAgeReceiverOlder, nil | |
579 | case s.Serial > other.Serial: | |
580 | return StateAgeReceiverNewer, nil | |
581 | default: | |
582 | return StateAgeEqual, nil | |
583 | } | |
584 | } | |
585 | ||
586 | // SameLineage returns true only if the state given in argument belongs | |
587 | // to the same "lineage" of states as the receiver. | |
588 | func (s *State) SameLineage(other *State) bool { | |
589 | s.Lock() | |
590 | defer s.Unlock() | |
591 | ||
592 | // If one of the states has no lineage then it is assumed to predate | |
593 | // this concept, and so we'll accept it as belonging to any lineage | |
594 | // so that a lineage string can be assigned to newer versions | |
595 | // without breaking compatibility with older versions. | |
596 | if s.Lineage == "" || other.Lineage == "" { | |
597 | return true | |
598 | } | |
599 | ||
600 | return s.Lineage == other.Lineage | |
601 | } | |
602 | ||
603 | // DeepCopy performs a deep copy of the state structure and returns | |
604 | // a new structure. | |
605 | func (s *State) DeepCopy() *State { | |
606 | copy, err := copystructure.Config{Lock: true}.Copy(s) | |
607 | if err != nil { | |
608 | panic(err) | |
609 | } | |
610 | ||
611 | return copy.(*State) | |
612 | } | |
613 | ||
614 | // IncrementSerialMaybe increments the serial number of this state | |
615 | // if it different from the other state. | |
616 | func (s *State) IncrementSerialMaybe(other *State) { | |
617 | if s == nil { | |
618 | return | |
619 | } | |
620 | if other == nil { | |
621 | return | |
622 | } | |
623 | s.Lock() | |
624 | defer s.Unlock() | |
625 | ||
626 | if s.Serial > other.Serial { | |
627 | return | |
628 | } | |
629 | if other.TFVersion != s.TFVersion || !s.equal(other) { | |
630 | if other.Serial > s.Serial { | |
631 | s.Serial = other.Serial | |
632 | } | |
633 | ||
634 | s.Serial++ | |
635 | } | |
636 | } | |
637 | ||
638 | // FromFutureTerraform checks if this state was written by a Terraform | |
639 | // version from the future. | |
640 | func (s *State) FromFutureTerraform() bool { | |
641 | s.Lock() | |
642 | defer s.Unlock() | |
643 | ||
644 | // No TF version means it is certainly from the past | |
645 | if s.TFVersion == "" { | |
646 | return false | |
647 | } | |
648 | ||
649 | v := version.Must(version.NewVersion(s.TFVersion)) | |
650 | return SemVersion.LessThan(v) | |
651 | } | |
652 | ||
653 | func (s *State) Init() { | |
654 | s.Lock() | |
655 | defer s.Unlock() | |
656 | s.init() | |
657 | } | |
658 | ||
659 | func (s *State) init() { | |
660 | if s.Version == 0 { | |
661 | s.Version = StateVersion | |
662 | } | |
663 | if s.moduleByPath(rootModulePath) == nil { | |
664 | s.addModule(rootModulePath) | |
665 | } | |
666 | s.ensureHasLineage() | |
667 | ||
668 | for _, mod := range s.Modules { | |
669 | if mod != nil { | |
670 | mod.init() | |
671 | } | |
672 | } | |
673 | ||
674 | if s.Remote != nil { | |
675 | s.Remote.init() | |
676 | } | |
677 | ||
678 | } | |
679 | ||
680 | func (s *State) EnsureHasLineage() { | |
681 | s.Lock() | |
682 | defer s.Unlock() | |
683 | ||
684 | s.ensureHasLineage() | |
685 | } | |
686 | ||
687 | func (s *State) ensureHasLineage() { | |
688 | if s.Lineage == "" { | |
689 | s.Lineage = uuid.NewV4().String() | |
690 | log.Printf("[DEBUG] New state was assigned lineage %q\n", s.Lineage) | |
691 | } else { | |
692 | log.Printf("[TRACE] Preserving existing state lineage %q\n", s.Lineage) | |
693 | } | |
694 | } | |
695 | ||
696 | // AddModuleState insert this module state and override any existing ModuleState | |
697 | func (s *State) AddModuleState(mod *ModuleState) { | |
698 | mod.init() | |
699 | s.Lock() | |
700 | defer s.Unlock() | |
701 | ||
702 | s.addModuleState(mod) | |
703 | } | |
704 | ||
705 | func (s *State) addModuleState(mod *ModuleState) { | |
706 | for i, m := range s.Modules { | |
707 | if reflect.DeepEqual(m.Path, mod.Path) { | |
708 | s.Modules[i] = mod | |
709 | return | |
710 | } | |
711 | } | |
712 | ||
713 | s.Modules = append(s.Modules, mod) | |
714 | s.sort() | |
715 | } | |
716 | ||
717 | // prune is used to remove any resources that are no longer required | |
718 | func (s *State) prune() { | |
719 | if s == nil { | |
720 | return | |
721 | } | |
722 | ||
723 | // Filter out empty modules. | |
724 | // A module is always assumed to have a path, and it's length isn't always | |
725 | // bounds checked later on. Modules may be "emptied" during destroy, but we | |
726 | // never want to store those in the state. | |
727 | for i := 0; i < len(s.Modules); i++ { | |
728 | if s.Modules[i] == nil || len(s.Modules[i].Path) == 0 { | |
729 | s.Modules = append(s.Modules[:i], s.Modules[i+1:]...) | |
730 | i-- | |
731 | } | |
732 | } | |
733 | ||
734 | for _, mod := range s.Modules { | |
735 | mod.prune() | |
736 | } | |
737 | if s.Remote != nil && s.Remote.Empty() { | |
738 | s.Remote = nil | |
739 | } | |
740 | } | |
741 | ||
742 | // sort sorts the modules | |
743 | func (s *State) sort() { | |
744 | sort.Sort(moduleStateSort(s.Modules)) | |
745 | ||
746 | // Allow modules to be sorted | |
747 | for _, m := range s.Modules { | |
748 | if m != nil { | |
749 | m.sort() | |
750 | } | |
751 | } | |
752 | } | |
753 | ||
754 | func (s *State) String() string { | |
755 | if s == nil { | |
756 | return "<nil>" | |
757 | } | |
758 | s.Lock() | |
759 | defer s.Unlock() | |
760 | ||
761 | var buf bytes.Buffer | |
762 | for _, m := range s.Modules { | |
763 | mStr := m.String() | |
764 | ||
765 | // If we're the root module, we just write the output directly. | |
766 | if reflect.DeepEqual(m.Path, rootModulePath) { | |
767 | buf.WriteString(mStr + "\n") | |
768 | continue | |
769 | } | |
770 | ||
771 | buf.WriteString(fmt.Sprintf("module.%s:\n", strings.Join(m.Path[1:], "."))) | |
772 | ||
773 | s := bufio.NewScanner(strings.NewReader(mStr)) | |
774 | for s.Scan() { | |
775 | text := s.Text() | |
776 | if text != "" { | |
777 | text = " " + text | |
778 | } | |
779 | ||
780 | buf.WriteString(fmt.Sprintf("%s\n", text)) | |
781 | } | |
782 | } | |
783 | ||
784 | return strings.TrimSpace(buf.String()) | |
785 | } | |
786 | ||
787 | // BackendState stores the configuration to connect to a remote backend. | |
788 | type BackendState struct { | |
789 | Type string `json:"type"` // Backend type | |
790 | Config map[string]interface{} `json:"config"` // Backend raw config | |
791 | ||
792 | // Hash is the hash code to uniquely identify the original source | |
793 | // configuration. We use this to detect when there is a change in | |
794 | // configuration even when "type" isn't changed. | |
795 | Hash uint64 `json:"hash"` | |
796 | } | |
797 | ||
798 | // Empty returns true if BackendState has no state. | |
799 | func (s *BackendState) Empty() bool { | |
800 | return s == nil || s.Type == "" | |
801 | } | |
802 | ||
803 | // Rehash returns a unique content hash for this backend's configuration | |
804 | // as a uint64 value. | |
805 | // The Hash stored in the backend state needs to match the config itself, but | |
806 | // we need to compare the backend config after it has been combined with all | |
807 | // options. | |
808 | // This function must match the implementation used by config.Backend. | |
809 | func (s *BackendState) Rehash() uint64 { | |
810 | if s == nil { | |
811 | return 0 | |
812 | } | |
813 | ||
814 | cfg := config.Backend{ | |
815 | Type: s.Type, | |
816 | RawConfig: &config.RawConfig{ | |
817 | Raw: s.Config, | |
818 | }, | |
819 | } | |
820 | ||
821 | return cfg.Rehash() | |
822 | } | |
823 | ||
824 | // RemoteState is used to track the information about a remote | |
825 | // state store that we push/pull state to. | |
826 | type RemoteState struct { | |
827 | // Type controls the client we use for the remote state | |
828 | Type string `json:"type"` | |
829 | ||
830 | // Config is used to store arbitrary configuration that | |
831 | // is type specific | |
832 | Config map[string]string `json:"config"` | |
833 | ||
834 | mu sync.Mutex | |
835 | } | |
836 | ||
837 | func (s *RemoteState) Lock() { s.mu.Lock() } | |
838 | func (s *RemoteState) Unlock() { s.mu.Unlock() } | |
839 | ||
840 | func (r *RemoteState) init() { | |
841 | r.Lock() | |
842 | defer r.Unlock() | |
843 | ||
844 | if r.Config == nil { | |
845 | r.Config = make(map[string]string) | |
846 | } | |
847 | } | |
848 | ||
849 | func (r *RemoteState) deepcopy() *RemoteState { | |
850 | r.Lock() | |
851 | defer r.Unlock() | |
852 | ||
853 | confCopy := make(map[string]string, len(r.Config)) | |
854 | for k, v := range r.Config { | |
855 | confCopy[k] = v | |
856 | } | |
857 | return &RemoteState{ | |
858 | Type: r.Type, | |
859 | Config: confCopy, | |
860 | } | |
861 | } | |
862 | ||
863 | func (r *RemoteState) Empty() bool { | |
864 | if r == nil { | |
865 | return true | |
866 | } | |
867 | r.Lock() | |
868 | defer r.Unlock() | |
869 | ||
870 | return r.Type == "" | |
871 | } | |
872 | ||
873 | func (r *RemoteState) Equals(other *RemoteState) bool { | |
874 | r.Lock() | |
875 | defer r.Unlock() | |
876 | ||
877 | if r.Type != other.Type { | |
878 | return false | |
879 | } | |
880 | if len(r.Config) != len(other.Config) { | |
881 | return false | |
882 | } | |
883 | for k, v := range r.Config { | |
884 | if other.Config[k] != v { | |
885 | return false | |
886 | } | |
887 | } | |
888 | return true | |
889 | } | |
890 | ||
891 | // OutputState is used to track the state relevant to a single output. | |
892 | type OutputState struct { | |
893 | // Sensitive describes whether the output is considered sensitive, | |
894 | // which may lead to masking the value on screen in some cases. | |
895 | Sensitive bool `json:"sensitive"` | |
896 | // Type describes the structure of Value. Valid values are "string", | |
897 | // "map" and "list" | |
898 | Type string `json:"type"` | |
899 | // Value contains the value of the output, in the structure described | |
900 | // by the Type field. | |
901 | Value interface{} `json:"value"` | |
902 | ||
903 | mu sync.Mutex | |
904 | } | |
905 | ||
906 | func (s *OutputState) Lock() { s.mu.Lock() } | |
907 | func (s *OutputState) Unlock() { s.mu.Unlock() } | |
908 | ||
909 | func (s *OutputState) String() string { | |
910 | return fmt.Sprintf("%#v", s.Value) | |
911 | } | |
912 | ||
913 | // Equal compares two OutputState structures for equality. nil values are | |
914 | // considered equal. | |
915 | func (s *OutputState) Equal(other *OutputState) bool { | |
916 | if s == nil && other == nil { | |
917 | return true | |
918 | } | |
919 | ||
920 | if s == nil || other == nil { | |
921 | return false | |
922 | } | |
923 | s.Lock() | |
924 | defer s.Unlock() | |
925 | ||
926 | if s.Type != other.Type { | |
927 | return false | |
928 | } | |
929 | ||
930 | if s.Sensitive != other.Sensitive { | |
931 | return false | |
932 | } | |
933 | ||
934 | if !reflect.DeepEqual(s.Value, other.Value) { | |
935 | return false | |
936 | } | |
937 | ||
938 | return true | |
939 | } | |
940 | ||
941 | func (s *OutputState) deepcopy() *OutputState { | |
942 | if s == nil { | |
943 | return nil | |
944 | } | |
945 | ||
946 | stateCopy, err := copystructure.Config{Lock: true}.Copy(s) | |
947 | if err != nil { | |
948 | panic(fmt.Errorf("Error copying output value: %s", err)) | |
949 | } | |
950 | ||
951 | return stateCopy.(*OutputState) | |
952 | } | |
953 | ||
954 | // ModuleState is used to track all the state relevant to a single | |
955 | // module. Previous to Terraform 0.3, all state belonged to the "root" | |
956 | // module. | |
957 | type ModuleState struct { | |
958 | // Path is the import path from the root module. Modules imports are | |
959 | // always disjoint, so the path represents amodule tree | |
960 | Path []string `json:"path"` | |
961 | ||
962 | // Outputs declared by the module and maintained for each module | |
963 | // even though only the root module technically needs to be kept. | |
964 | // This allows operators to inspect values at the boundaries. | |
965 | Outputs map[string]*OutputState `json:"outputs"` | |
966 | ||
967 | // Resources is a mapping of the logically named resource to | |
968 | // the state of the resource. Each resource may actually have | |
969 | // N instances underneath, although a user only needs to think | |
970 | // about the 1:1 case. | |
971 | Resources map[string]*ResourceState `json:"resources"` | |
972 | ||
973 | // Dependencies are a list of things that this module relies on | |
974 | // existing to remain intact. For example: an module may depend | |
975 | // on a VPC ID given by an aws_vpc resource. | |
976 | // | |
977 | // Terraform uses this information to build valid destruction | |
978 | // orders and to warn the user if they're destroying a module that | |
979 | // another resource depends on. | |
980 | // | |
981 | // Things can be put into this list that may not be managed by | |
982 | // Terraform. If Terraform doesn't find a matching ID in the | |
983 | // overall state, then it assumes it isn't managed and doesn't | |
984 | // worry about it. | |
985 | Dependencies []string `json:"depends_on"` | |
986 | ||
987 | mu sync.Mutex | |
988 | } | |
989 | ||
990 | func (s *ModuleState) Lock() { s.mu.Lock() } | |
991 | func (s *ModuleState) Unlock() { s.mu.Unlock() } | |
992 | ||
993 | // Equal tests whether one module state is equal to another. | |
994 | func (m *ModuleState) Equal(other *ModuleState) bool { | |
995 | m.Lock() | |
996 | defer m.Unlock() | |
997 | ||
998 | // Paths must be equal | |
999 | if !reflect.DeepEqual(m.Path, other.Path) { | |
1000 | return false | |
1001 | } | |
1002 | ||
1003 | // Outputs must be equal | |
1004 | if len(m.Outputs) != len(other.Outputs) { | |
1005 | return false | |
1006 | } | |
1007 | for k, v := range m.Outputs { | |
1008 | if !other.Outputs[k].Equal(v) { | |
1009 | return false | |
1010 | } | |
1011 | } | |
1012 | ||
1013 | // Dependencies must be equal. This sorts these in place but | |
1014 | // this shouldn't cause any problems. | |
1015 | sort.Strings(m.Dependencies) | |
1016 | sort.Strings(other.Dependencies) | |
1017 | if len(m.Dependencies) != len(other.Dependencies) { | |
1018 | return false | |
1019 | } | |
1020 | for i, d := range m.Dependencies { | |
1021 | if other.Dependencies[i] != d { | |
1022 | return false | |
1023 | } | |
1024 | } | |
1025 | ||
1026 | // Resources must be equal | |
1027 | if len(m.Resources) != len(other.Resources) { | |
1028 | return false | |
1029 | } | |
1030 | for k, r := range m.Resources { | |
1031 | otherR, ok := other.Resources[k] | |
1032 | if !ok { | |
1033 | return false | |
1034 | } | |
1035 | ||
1036 | if !r.Equal(otherR) { | |
1037 | return false | |
1038 | } | |
1039 | } | |
1040 | ||
1041 | return true | |
1042 | } | |
1043 | ||
1044 | // IsRoot says whether or not this module diff is for the root module. | |
1045 | func (m *ModuleState) IsRoot() bool { | |
1046 | m.Lock() | |
1047 | defer m.Unlock() | |
1048 | return reflect.DeepEqual(m.Path, rootModulePath) | |
1049 | } | |
1050 | ||
1051 | // IsDescendent returns true if other is a descendent of this module. | |
1052 | func (m *ModuleState) IsDescendent(other *ModuleState) bool { | |
1053 | m.Lock() | |
1054 | defer m.Unlock() | |
1055 | ||
1056 | i := len(m.Path) | |
1057 | return len(other.Path) > i && reflect.DeepEqual(other.Path[:i], m.Path) | |
1058 | } | |
1059 | ||
1060 | // Orphans returns a list of keys of resources that are in the State | |
1061 | // but aren't present in the configuration itself. Hence, these keys | |
1062 | // represent the state of resources that are orphans. | |
1063 | func (m *ModuleState) Orphans(c *config.Config) []string { | |
1064 | m.Lock() | |
1065 | defer m.Unlock() | |
1066 | ||
1067 | keys := make(map[string]struct{}) | |
1068 | for k, _ := range m.Resources { | |
1069 | keys[k] = struct{}{} | |
1070 | } | |
1071 | ||
1072 | if c != nil { | |
1073 | for _, r := range c.Resources { | |
1074 | delete(keys, r.Id()) | |
1075 | ||
1076 | for k, _ := range keys { | |
1077 | if strings.HasPrefix(k, r.Id()+".") { | |
1078 | delete(keys, k) | |
1079 | } | |
1080 | } | |
1081 | } | |
1082 | } | |
1083 | ||
1084 | result := make([]string, 0, len(keys)) | |
1085 | for k, _ := range keys { | |
1086 | result = append(result, k) | |
1087 | } | |
1088 | ||
1089 | return result | |
1090 | } | |
1091 | ||
1092 | // View returns a view with the given resource prefix. | |
1093 | func (m *ModuleState) View(id string) *ModuleState { | |
1094 | if m == nil { | |
1095 | return m | |
1096 | } | |
1097 | ||
1098 | r := m.deepcopy() | |
1099 | for k, _ := range r.Resources { | |
1100 | if id == k || strings.HasPrefix(k, id+".") { | |
1101 | continue | |
1102 | } | |
1103 | ||
1104 | delete(r.Resources, k) | |
1105 | } | |
1106 | ||
1107 | return r | |
1108 | } | |
1109 | ||
1110 | func (m *ModuleState) init() { | |
1111 | m.Lock() | |
1112 | defer m.Unlock() | |
1113 | ||
1114 | if m.Path == nil { | |
1115 | m.Path = []string{} | |
1116 | } | |
1117 | if m.Outputs == nil { | |
1118 | m.Outputs = make(map[string]*OutputState) | |
1119 | } | |
1120 | if m.Resources == nil { | |
1121 | m.Resources = make(map[string]*ResourceState) | |
1122 | } | |
1123 | ||
1124 | if m.Dependencies == nil { | |
1125 | m.Dependencies = make([]string, 0) | |
1126 | } | |
1127 | ||
1128 | for _, rs := range m.Resources { | |
1129 | rs.init() | |
1130 | } | |
1131 | } | |
1132 | ||
1133 | func (m *ModuleState) deepcopy() *ModuleState { | |
1134 | if m == nil { | |
1135 | return nil | |
1136 | } | |
1137 | ||
1138 | stateCopy, err := copystructure.Config{Lock: true}.Copy(m) | |
1139 | if err != nil { | |
1140 | panic(err) | |
1141 | } | |
1142 | ||
1143 | return stateCopy.(*ModuleState) | |
1144 | } | |
1145 | ||
1146 | // prune is used to remove any resources that are no longer required | |
1147 | func (m *ModuleState) prune() { | |
1148 | m.Lock() | |
1149 | defer m.Unlock() | |
1150 | ||
1151 | for k, v := range m.Resources { | |
1152 | if v == nil || (v.Primary == nil || v.Primary.ID == "") && len(v.Deposed) == 0 { | |
1153 | delete(m.Resources, k) | |
1154 | continue | |
1155 | } | |
1156 | ||
1157 | v.prune() | |
1158 | } | |
1159 | ||
1160 | for k, v := range m.Outputs { | |
1161 | if v.Value == config.UnknownVariableValue { | |
1162 | delete(m.Outputs, k) | |
1163 | } | |
1164 | } | |
1165 | ||
1166 | m.Dependencies = uniqueStrings(m.Dependencies) | |
1167 | } | |
1168 | ||
1169 | func (m *ModuleState) sort() { | |
1170 | for _, v := range m.Resources { | |
1171 | v.sort() | |
1172 | } | |
1173 | } | |
1174 | ||
1175 | func (m *ModuleState) String() string { | |
1176 | m.Lock() | |
1177 | defer m.Unlock() | |
1178 | ||
1179 | var buf bytes.Buffer | |
1180 | ||
1181 | if len(m.Resources) == 0 { | |
1182 | buf.WriteString("<no state>") | |
1183 | } | |
1184 | ||
1185 | names := make([]string, 0, len(m.Resources)) | |
1186 | for name, _ := range m.Resources { | |
1187 | names = append(names, name) | |
1188 | } | |
1189 | ||
1190 | sort.Sort(resourceNameSort(names)) | |
1191 | ||
1192 | for _, k := range names { | |
1193 | rs := m.Resources[k] | |
1194 | var id string | |
1195 | if rs.Primary != nil { | |
1196 | id = rs.Primary.ID | |
1197 | } | |
1198 | if id == "" { | |
1199 | id = "<not created>" | |
1200 | } | |
1201 | ||
1202 | taintStr := "" | |
1203 | if rs.Primary.Tainted { | |
1204 | taintStr = " (tainted)" | |
1205 | } | |
1206 | ||
1207 | deposedStr := "" | |
1208 | if len(rs.Deposed) > 0 { | |
1209 | deposedStr = fmt.Sprintf(" (%d deposed)", len(rs.Deposed)) | |
1210 | } | |
1211 | ||
1212 | buf.WriteString(fmt.Sprintf("%s:%s%s\n", k, taintStr, deposedStr)) | |
1213 | buf.WriteString(fmt.Sprintf(" ID = %s\n", id)) | |
1214 | if rs.Provider != "" { | |
1215 | buf.WriteString(fmt.Sprintf(" provider = %s\n", rs.Provider)) | |
1216 | } | |
1217 | ||
1218 | var attributes map[string]string | |
1219 | if rs.Primary != nil { | |
1220 | attributes = rs.Primary.Attributes | |
1221 | } | |
1222 | attrKeys := make([]string, 0, len(attributes)) | |
1223 | for ak, _ := range attributes { | |
1224 | if ak == "id" { | |
1225 | continue | |
1226 | } | |
1227 | ||
1228 | attrKeys = append(attrKeys, ak) | |
1229 | } | |
1230 | ||
1231 | sort.Strings(attrKeys) | |
1232 | ||
1233 | for _, ak := range attrKeys { | |
1234 | av := attributes[ak] | |
1235 | buf.WriteString(fmt.Sprintf(" %s = %s\n", ak, av)) | |
1236 | } | |
1237 | ||
1238 | for idx, t := range rs.Deposed { | |
1239 | taintStr := "" | |
1240 | if t.Tainted { | |
1241 | taintStr = " (tainted)" | |
1242 | } | |
1243 | buf.WriteString(fmt.Sprintf(" Deposed ID %d = %s%s\n", idx+1, t.ID, taintStr)) | |
1244 | } | |
1245 | ||
1246 | if len(rs.Dependencies) > 0 { | |
1247 | buf.WriteString(fmt.Sprintf("\n Dependencies:\n")) | |
1248 | for _, dep := range rs.Dependencies { | |
1249 | buf.WriteString(fmt.Sprintf(" %s\n", dep)) | |
1250 | } | |
1251 | } | |
1252 | } | |
1253 | ||
1254 | if len(m.Outputs) > 0 { | |
1255 | buf.WriteString("\nOutputs:\n\n") | |
1256 | ||
1257 | ks := make([]string, 0, len(m.Outputs)) | |
1258 | for k, _ := range m.Outputs { | |
1259 | ks = append(ks, k) | |
1260 | } | |
1261 | ||
1262 | sort.Strings(ks) | |
1263 | ||
1264 | for _, k := range ks { | |
1265 | v := m.Outputs[k] | |
1266 | switch vTyped := v.Value.(type) { | |
1267 | case string: | |
1268 | buf.WriteString(fmt.Sprintf("%s = %s\n", k, vTyped)) | |
1269 | case []interface{}: | |
1270 | buf.WriteString(fmt.Sprintf("%s = %s\n", k, vTyped)) | |
1271 | case map[string]interface{}: | |
1272 | var mapKeys []string | |
1273 | for key, _ := range vTyped { | |
1274 | mapKeys = append(mapKeys, key) | |
1275 | } | |
1276 | sort.Strings(mapKeys) | |
1277 | ||
1278 | var mapBuf bytes.Buffer | |
1279 | mapBuf.WriteString("{") | |
1280 | for _, key := range mapKeys { | |
1281 | mapBuf.WriteString(fmt.Sprintf("%s:%s ", key, vTyped[key])) | |
1282 | } | |
1283 | mapBuf.WriteString("}") | |
1284 | ||
1285 | buf.WriteString(fmt.Sprintf("%s = %s\n", k, mapBuf.String())) | |
1286 | } | |
1287 | } | |
1288 | } | |
1289 | ||
1290 | return buf.String() | |
1291 | } | |
1292 | ||
1293 | // ResourceStateKey is a structured representation of the key used for the | |
1294 | // ModuleState.Resources mapping | |
1295 | type ResourceStateKey struct { | |
1296 | Name string | |
1297 | Type string | |
1298 | Mode config.ResourceMode | |
1299 | Index int | |
1300 | } | |
1301 | ||
1302 | // Equal determines whether two ResourceStateKeys are the same | |
1303 | func (rsk *ResourceStateKey) Equal(other *ResourceStateKey) bool { | |
1304 | if rsk == nil || other == nil { | |
1305 | return false | |
1306 | } | |
1307 | if rsk.Mode != other.Mode { | |
1308 | return false | |
1309 | } | |
1310 | if rsk.Type != other.Type { | |
1311 | return false | |
1312 | } | |
1313 | if rsk.Name != other.Name { | |
1314 | return false | |
1315 | } | |
1316 | if rsk.Index != other.Index { | |
1317 | return false | |
1318 | } | |
1319 | return true | |
1320 | } | |
1321 | ||
1322 | func (rsk *ResourceStateKey) String() string { | |
1323 | if rsk == nil { | |
1324 | return "" | |
1325 | } | |
1326 | var prefix string | |
1327 | switch rsk.Mode { | |
1328 | case config.ManagedResourceMode: | |
1329 | prefix = "" | |
1330 | case config.DataResourceMode: | |
1331 | prefix = "data." | |
1332 | default: | |
1333 | panic(fmt.Errorf("unknown resource mode %s", rsk.Mode)) | |
1334 | } | |
1335 | if rsk.Index == -1 { | |
1336 | return fmt.Sprintf("%s%s.%s", prefix, rsk.Type, rsk.Name) | |
1337 | } | |
1338 | return fmt.Sprintf("%s%s.%s.%d", prefix, rsk.Type, rsk.Name, rsk.Index) | |
1339 | } | |
1340 | ||
1341 | // ParseResourceStateKey accepts a key in the format used by | |
1342 | // ModuleState.Resources and returns a resource name and resource index. In the | |
1343 | // state, a resource has the format "type.name.index" or "type.name". In the | |
1344 | // latter case, the index is returned as -1. | |
1345 | func ParseResourceStateKey(k string) (*ResourceStateKey, error) { | |
1346 | parts := strings.Split(k, ".") | |
1347 | mode := config.ManagedResourceMode | |
1348 | if len(parts) > 0 && parts[0] == "data" { | |
1349 | mode = config.DataResourceMode | |
1350 | // Don't need the constant "data" prefix for parsing | |
1351 | // now that we've figured out the mode. | |
1352 | parts = parts[1:] | |
1353 | } | |
1354 | if len(parts) < 2 || len(parts) > 3 { | |
1355 | return nil, fmt.Errorf("Malformed resource state key: %s", k) | |
1356 | } | |
1357 | rsk := &ResourceStateKey{ | |
1358 | Mode: mode, | |
1359 | Type: parts[0], | |
1360 | Name: parts[1], | |
1361 | Index: -1, | |
1362 | } | |
1363 | if len(parts) == 3 { | |
1364 | index, err := strconv.Atoi(parts[2]) | |
1365 | if err != nil { | |
1366 | return nil, fmt.Errorf("Malformed resource state key index: %s", k) | |
1367 | } | |
1368 | rsk.Index = index | |
1369 | } | |
1370 | return rsk, nil | |
1371 | } | |
1372 | ||
1373 | // ResourceState holds the state of a resource that is used so that | |
1374 | // a provider can find and manage an existing resource as well as for | |
1375 | // storing attributes that are used to populate variables of child | |
1376 | // resources. | |
1377 | // | |
1378 | // Attributes has attributes about the created resource that are | |
1379 | // queryable in interpolation: "${type.id.attr}" | |
1380 | // | |
1381 | // Extra is just extra data that a provider can return that we store | |
1382 | // for later, but is not exposed in any way to the user. | |
1383 | // | |
1384 | type ResourceState struct { | |
1385 | // This is filled in and managed by Terraform, and is the resource | |
1386 | // type itself such as "mycloud_instance". If a resource provider sets | |
1387 | // this value, it won't be persisted. | |
1388 | Type string `json:"type"` | |
1389 | ||
1390 | // Dependencies are a list of things that this resource relies on | |
1391 | // existing to remain intact. For example: an AWS instance might | |
1392 | // depend on a subnet (which itself might depend on a VPC, and so | |
1393 | // on). | |
1394 | // | |
1395 | // Terraform uses this information to build valid destruction | |
1396 | // orders and to warn the user if they're destroying a resource that | |
1397 | // another resource depends on. | |
1398 | // | |
1399 | // Things can be put into this list that may not be managed by | |
1400 | // Terraform. If Terraform doesn't find a matching ID in the | |
1401 | // overall state, then it assumes it isn't managed and doesn't | |
1402 | // worry about it. | |
1403 | Dependencies []string `json:"depends_on"` | |
1404 | ||
1405 | // Primary is the current active instance for this resource. | |
1406 | // It can be replaced but only after a successful creation. | |
1407 | // This is the instances on which providers will act. | |
1408 | Primary *InstanceState `json:"primary"` | |
1409 | ||
1410 | // Deposed is used in the mechanics of CreateBeforeDestroy: the existing | |
1411 | // Primary is Deposed to get it out of the way for the replacement Primary to | |
1412 | // be created by Apply. If the replacement Primary creates successfully, the | |
1413 | // Deposed instance is cleaned up. | |
1414 | // | |
1415 | // If there were problems creating the replacement Primary, the Deposed | |
1416 | // instance and the (now tainted) replacement Primary will be swapped so the | |
1417 | // tainted replacement will be cleaned up instead. | |
1418 | // | |
1419 | // An instance will remain in the Deposed list until it is successfully | |
1420 | // destroyed and purged. | |
1421 | Deposed []*InstanceState `json:"deposed"` | |
1422 | ||
1423 | // Provider is used when a resource is connected to a provider with an alias. | |
1424 | // If this string is empty, the resource is connected to the default provider, | |
1425 | // e.g. "aws_instance" goes with the "aws" provider. | |
1426 | // If the resource block contained a "provider" key, that value will be set here. | |
1427 | Provider string `json:"provider"` | |
1428 | ||
1429 | mu sync.Mutex | |
1430 | } | |
1431 | ||
1432 | func (s *ResourceState) Lock() { s.mu.Lock() } | |
1433 | func (s *ResourceState) Unlock() { s.mu.Unlock() } | |
1434 | ||
1435 | // Equal tests whether two ResourceStates are equal. | |
1436 | func (s *ResourceState) Equal(other *ResourceState) bool { | |
1437 | s.Lock() | |
1438 | defer s.Unlock() | |
1439 | ||
1440 | if s.Type != other.Type { | |
1441 | return false | |
1442 | } | |
1443 | ||
1444 | if s.Provider != other.Provider { | |
1445 | return false | |
1446 | } | |
1447 | ||
1448 | // Dependencies must be equal | |
1449 | sort.Strings(s.Dependencies) | |
1450 | sort.Strings(other.Dependencies) | |
1451 | if len(s.Dependencies) != len(other.Dependencies) { | |
1452 | return false | |
1453 | } | |
1454 | for i, d := range s.Dependencies { | |
1455 | if other.Dependencies[i] != d { | |
1456 | return false | |
1457 | } | |
1458 | } | |
1459 | ||
1460 | // States must be equal | |
1461 | if !s.Primary.Equal(other.Primary) { | |
1462 | return false | |
1463 | } | |
1464 | ||
1465 | return true | |
1466 | } | |
1467 | ||
1468 | // Taint marks a resource as tainted. | |
1469 | func (s *ResourceState) Taint() { | |
1470 | s.Lock() | |
1471 | defer s.Unlock() | |
1472 | ||
1473 | if s.Primary != nil { | |
1474 | s.Primary.Tainted = true | |
1475 | } | |
1476 | } | |
1477 | ||
1478 | // Untaint unmarks a resource as tainted. | |
1479 | func (s *ResourceState) Untaint() { | |
1480 | s.Lock() | |
1481 | defer s.Unlock() | |
1482 | ||
1483 | if s.Primary != nil { | |
1484 | s.Primary.Tainted = false | |
1485 | } | |
1486 | } | |
1487 | ||
1488 | func (s *ResourceState) init() { | |
1489 | s.Lock() | |
1490 | defer s.Unlock() | |
1491 | ||
1492 | if s.Primary == nil { | |
1493 | s.Primary = &InstanceState{} | |
1494 | } | |
1495 | s.Primary.init() | |
1496 | ||
1497 | if s.Dependencies == nil { | |
1498 | s.Dependencies = []string{} | |
1499 | } | |
1500 | ||
1501 | if s.Deposed == nil { | |
1502 | s.Deposed = make([]*InstanceState, 0) | |
1503 | } | |
1504 | } | |
1505 | ||
1506 | func (s *ResourceState) deepcopy() *ResourceState { | |
1507 | copy, err := copystructure.Config{Lock: true}.Copy(s) | |
1508 | if err != nil { | |
1509 | panic(err) | |
1510 | } | |
1511 | ||
1512 | return copy.(*ResourceState) | |
1513 | } | |
1514 | ||
1515 | // prune is used to remove any instances that are no longer required | |
1516 | func (s *ResourceState) prune() { | |
1517 | s.Lock() | |
1518 | defer s.Unlock() | |
1519 | ||
1520 | n := len(s.Deposed) | |
1521 | for i := 0; i < n; i++ { | |
1522 | inst := s.Deposed[i] | |
1523 | if inst == nil || inst.ID == "" { | |
1524 | copy(s.Deposed[i:], s.Deposed[i+1:]) | |
1525 | s.Deposed[n-1] = nil | |
1526 | n-- | |
1527 | i-- | |
1528 | } | |
1529 | } | |
1530 | s.Deposed = s.Deposed[:n] | |
1531 | ||
1532 | s.Dependencies = uniqueStrings(s.Dependencies) | |
1533 | } | |
1534 | ||
1535 | func (s *ResourceState) sort() { | |
1536 | s.Lock() | |
1537 | defer s.Unlock() | |
1538 | ||
1539 | sort.Strings(s.Dependencies) | |
1540 | } | |
1541 | ||
1542 | func (s *ResourceState) String() string { | |
1543 | s.Lock() | |
1544 | defer s.Unlock() | |
1545 | ||
1546 | var buf bytes.Buffer | |
1547 | buf.WriteString(fmt.Sprintf("Type = %s", s.Type)) | |
1548 | return buf.String() | |
1549 | } | |
1550 | ||
1551 | // InstanceState is used to track the unique state information belonging | |
1552 | // to a given instance. | |
1553 | type InstanceState struct { | |
1554 | // A unique ID for this resource. This is opaque to Terraform | |
1555 | // and is only meant as a lookup mechanism for the providers. | |
1556 | ID string `json:"id"` | |
1557 | ||
1558 | // Attributes are basic information about the resource. Any keys here | |
1559 | // are accessible in variable format within Terraform configurations: | |
1560 | // ${resourcetype.name.attribute}. | |
1561 | Attributes map[string]string `json:"attributes"` | |
1562 | ||
1563 | // Ephemeral is used to store any state associated with this instance | |
1564 | // that is necessary for the Terraform run to complete, but is not | |
1565 | // persisted to a state file. | |
1566 | Ephemeral EphemeralState `json:"-"` | |
1567 | ||
1568 | // Meta is a simple K/V map that is persisted to the State but otherwise | |
1569 | // ignored by Terraform core. It's meant to be used for accounting by | |
1570 | // external client code. The value here must only contain Go primitives | |
1571 | // and collections. | |
1572 | Meta map[string]interface{} `json:"meta"` | |
1573 | ||
1574 | // Tainted is used to mark a resource for recreation. | |
1575 | Tainted bool `json:"tainted"` | |
1576 | ||
1577 | mu sync.Mutex | |
1578 | } | |
1579 | ||
1580 | func (s *InstanceState) Lock() { s.mu.Lock() } | |
1581 | func (s *InstanceState) Unlock() { s.mu.Unlock() } | |
1582 | ||
1583 | func (s *InstanceState) init() { | |
1584 | s.Lock() | |
1585 | defer s.Unlock() | |
1586 | ||
1587 | if s.Attributes == nil { | |
1588 | s.Attributes = make(map[string]string) | |
1589 | } | |
1590 | if s.Meta == nil { | |
1591 | s.Meta = make(map[string]interface{}) | |
1592 | } | |
1593 | s.Ephemeral.init() | |
1594 | } | |
1595 | ||
1596 | // Copy all the Fields from another InstanceState | |
1597 | func (s *InstanceState) Set(from *InstanceState) { | |
1598 | s.Lock() | |
1599 | defer s.Unlock() | |
1600 | ||
1601 | from.Lock() | |
1602 | defer from.Unlock() | |
1603 | ||
1604 | s.ID = from.ID | |
1605 | s.Attributes = from.Attributes | |
1606 | s.Ephemeral = from.Ephemeral | |
1607 | s.Meta = from.Meta | |
1608 | s.Tainted = from.Tainted | |
1609 | } | |
1610 | ||
1611 | func (s *InstanceState) DeepCopy() *InstanceState { | |
1612 | copy, err := copystructure.Config{Lock: true}.Copy(s) | |
1613 | if err != nil { | |
1614 | panic(err) | |
1615 | } | |
1616 | ||
1617 | return copy.(*InstanceState) | |
1618 | } | |
1619 | ||
1620 | func (s *InstanceState) Empty() bool { | |
1621 | if s == nil { | |
1622 | return true | |
1623 | } | |
1624 | s.Lock() | |
1625 | defer s.Unlock() | |
1626 | ||
1627 | return s.ID == "" | |
1628 | } | |
1629 | ||
1630 | func (s *InstanceState) Equal(other *InstanceState) bool { | |
1631 | // Short circuit some nil checks | |
1632 | if s == nil || other == nil { | |
1633 | return s == other | |
1634 | } | |
1635 | s.Lock() | |
1636 | defer s.Unlock() | |
1637 | ||
1638 | // IDs must be equal | |
1639 | if s.ID != other.ID { | |
1640 | return false | |
1641 | } | |
1642 | ||
1643 | // Attributes must be equal | |
1644 | if len(s.Attributes) != len(other.Attributes) { | |
1645 | return false | |
1646 | } | |
1647 | for k, v := range s.Attributes { | |
1648 | otherV, ok := other.Attributes[k] | |
1649 | if !ok { | |
1650 | return false | |
1651 | } | |
1652 | ||
1653 | if v != otherV { | |
1654 | return false | |
1655 | } | |
1656 | } | |
1657 | ||
1658 | // Meta must be equal | |
1659 | if len(s.Meta) != len(other.Meta) { | |
1660 | return false | |
1661 | } | |
1662 | if s.Meta != nil && other.Meta != nil { | |
1663 | // We only do the deep check if both are non-nil. If one is nil | |
1664 | // we treat it as equal since their lengths are both zero (check | |
1665 | // above). | |
1666 | if !reflect.DeepEqual(s.Meta, other.Meta) { | |
1667 | return false | |
1668 | } | |
1669 | } | |
1670 | ||
1671 | if s.Tainted != other.Tainted { | |
1672 | return false | |
1673 | } | |
1674 | ||
1675 | return true | |
1676 | } | |
1677 | ||
1678 | // MergeDiff takes a ResourceDiff and merges the attributes into | |
1679 | // this resource state in order to generate a new state. This new | |
1680 | // state can be used to provide updated attribute lookups for | |
1681 | // variable interpolation. | |
1682 | // | |
1683 | // If the diff attribute requires computing the value, and hence | |
1684 | // won't be available until apply, the value is replaced with the | |
1685 | // computeID. | |
1686 | func (s *InstanceState) MergeDiff(d *InstanceDiff) *InstanceState { | |
1687 | result := s.DeepCopy() | |
1688 | if result == nil { | |
1689 | result = new(InstanceState) | |
1690 | } | |
1691 | result.init() | |
1692 | ||
1693 | if s != nil { | |
1694 | s.Lock() | |
1695 | defer s.Unlock() | |
1696 | for k, v := range s.Attributes { | |
1697 | result.Attributes[k] = v | |
1698 | } | |
1699 | } | |
1700 | if d != nil { | |
1701 | for k, diff := range d.CopyAttributes() { | |
1702 | if diff.NewRemoved { | |
1703 | delete(result.Attributes, k) | |
1704 | continue | |
1705 | } | |
1706 | if diff.NewComputed { | |
1707 | result.Attributes[k] = config.UnknownVariableValue | |
1708 | continue | |
1709 | } | |
1710 | ||
1711 | result.Attributes[k] = diff.New | |
1712 | } | |
1713 | } | |
1714 | ||
1715 | return result | |
1716 | } | |
1717 | ||
1718 | func (s *InstanceState) String() string { | |
1719 | s.Lock() | |
1720 | defer s.Unlock() | |
1721 | ||
1722 | var buf bytes.Buffer | |
1723 | ||
1724 | if s == nil || s.ID == "" { | |
1725 | return "<not created>" | |
1726 | } | |
1727 | ||
1728 | buf.WriteString(fmt.Sprintf("ID = %s\n", s.ID)) | |
1729 | ||
1730 | attributes := s.Attributes | |
1731 | attrKeys := make([]string, 0, len(attributes)) | |
1732 | for ak, _ := range attributes { | |
1733 | if ak == "id" { | |
1734 | continue | |
1735 | } | |
1736 | ||
1737 | attrKeys = append(attrKeys, ak) | |
1738 | } | |
1739 | sort.Strings(attrKeys) | |
1740 | ||
1741 | for _, ak := range attrKeys { | |
1742 | av := attributes[ak] | |
1743 | buf.WriteString(fmt.Sprintf("%s = %s\n", ak, av)) | |
1744 | } | |
1745 | ||
1746 | buf.WriteString(fmt.Sprintf("Tainted = %t\n", s.Tainted)) | |
1747 | ||
1748 | return buf.String() | |
1749 | } | |
1750 | ||
1751 | // EphemeralState is used for transient state that is only kept in-memory | |
1752 | type EphemeralState struct { | |
1753 | // ConnInfo is used for the providers to export information which is | |
1754 | // used to connect to the resource for provisioning. For example, | |
1755 | // this could contain SSH or WinRM credentials. | |
1756 | ConnInfo map[string]string `json:"-"` | |
1757 | ||
1758 | // Type is used to specify the resource type for this instance. This is only | |
1759 | // required for import operations (as documented). If the documentation | |
1760 | // doesn't state that you need to set this, then don't worry about | |
1761 | // setting it. | |
1762 | Type string `json:"-"` | |
1763 | } | |
1764 | ||
1765 | func (e *EphemeralState) init() { | |
1766 | if e.ConnInfo == nil { | |
1767 | e.ConnInfo = make(map[string]string) | |
1768 | } | |
1769 | } | |
1770 | ||
1771 | func (e *EphemeralState) DeepCopy() *EphemeralState { | |
1772 | copy, err := copystructure.Config{Lock: true}.Copy(e) | |
1773 | if err != nil { | |
1774 | panic(err) | |
1775 | } | |
1776 | ||
1777 | return copy.(*EphemeralState) | |
1778 | } | |
1779 | ||
1780 | type jsonStateVersionIdentifier struct { | |
1781 | Version int `json:"version"` | |
1782 | } | |
1783 | ||
1784 | // Check if this is a V0 format - the magic bytes at the start of the file | |
1785 | // should be "tfstate" if so. We no longer support upgrading this type of | |
1786 | // state but return an error message explaining to a user how they can | |
1787 | // upgrade via the 0.6.x series. | |
1788 | func testForV0State(buf *bufio.Reader) error { | |
1789 | start, err := buf.Peek(len("tfstate")) | |
1790 | if err != nil { | |
1791 | return fmt.Errorf("Failed to check for magic bytes: %v", err) | |
1792 | } | |
1793 | if string(start) == "tfstate" { | |
1794 | return fmt.Errorf("Terraform 0.7 no longer supports upgrading the binary state\n" + | |
1795 | "format which was used prior to Terraform 0.3. Please upgrade\n" + | |
1796 | "this state file using Terraform 0.6.16 prior to using it with\n" + | |
1797 | "Terraform 0.7.") | |
1798 | } | |
1799 | ||
1800 | return nil | |
1801 | } | |
1802 | ||
1803 | // ErrNoState is returned by ReadState when the io.Reader contains no data | |
1804 | var ErrNoState = errors.New("no state") | |
1805 | ||
1806 | // ReadState reads a state structure out of a reader in the format that | |
1807 | // was written by WriteState. | |
1808 | func ReadState(src io.Reader) (*State, error) { | |
1809 | buf := bufio.NewReader(src) | |
1810 | if _, err := buf.Peek(1); err != nil { | |
1811 | // the error is either io.EOF or "invalid argument", and both are from | |
1812 | // an empty state. | |
1813 | return nil, ErrNoState | |
1814 | } | |
1815 | ||
1816 | if err := testForV0State(buf); err != nil { | |
1817 | return nil, err | |
1818 | } | |
1819 | ||
1820 | // If we are JSON we buffer the whole thing in memory so we can read it twice. | |
1821 | // This is suboptimal, but will work for now. | |
1822 | jsonBytes, err := ioutil.ReadAll(buf) | |
1823 | if err != nil { | |
1824 | return nil, fmt.Errorf("Reading state file failed: %v", err) | |
1825 | } | |
1826 | ||
1827 | versionIdentifier := &jsonStateVersionIdentifier{} | |
1828 | if err := json.Unmarshal(jsonBytes, versionIdentifier); err != nil { | |
1829 | return nil, fmt.Errorf("Decoding state file version failed: %v", err) | |
1830 | } | |
1831 | ||
1832 | var result *State | |
1833 | switch versionIdentifier.Version { | |
1834 | case 0: | |
1835 | return nil, fmt.Errorf("State version 0 is not supported as JSON.") | |
1836 | case 1: | |
1837 | v1State, err := ReadStateV1(jsonBytes) | |
1838 | if err != nil { | |
1839 | return nil, err | |
1840 | } | |
1841 | ||
1842 | v2State, err := upgradeStateV1ToV2(v1State) | |
1843 | if err != nil { | |
1844 | return nil, err | |
1845 | } | |
1846 | ||
1847 | v3State, err := upgradeStateV2ToV3(v2State) | |
1848 | if err != nil { | |
1849 | return nil, err | |
1850 | } | |
1851 | ||
1852 | // increment the Serial whenever we upgrade state | |
1853 | v3State.Serial++ | |
1854 | result = v3State | |
1855 | case 2: | |
1856 | v2State, err := ReadStateV2(jsonBytes) | |
1857 | if err != nil { | |
1858 | return nil, err | |
1859 | } | |
1860 | v3State, err := upgradeStateV2ToV3(v2State) | |
1861 | if err != nil { | |
1862 | return nil, err | |
1863 | } | |
1864 | ||
1865 | v3State.Serial++ | |
1866 | result = v3State | |
1867 | case 3: | |
1868 | v3State, err := ReadStateV3(jsonBytes) | |
1869 | if err != nil { | |
1870 | return nil, err | |
1871 | } | |
1872 | ||
1873 | result = v3State | |
1874 | default: | |
1875 | return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", | |
1876 | SemVersion.String(), versionIdentifier.Version) | |
1877 | } | |
1878 | ||
1879 | // If we reached this place we must have a result set | |
1880 | if result == nil { | |
1881 | panic("resulting state in load not set, assertion failed") | |
1882 | } | |
1883 | ||
1884 | // Prune the state when read it. Its possible to write unpruned states or | |
1885 | // for a user to make a state unpruned (nil-ing a module state for example). | |
1886 | result.prune() | |
1887 | ||
1888 | // Validate the state file is valid | |
1889 | if err := result.Validate(); err != nil { | |
1890 | return nil, err | |
1891 | } | |
1892 | ||
1893 | return result, nil | |
1894 | } | |
1895 | ||
1896 | func ReadStateV1(jsonBytes []byte) (*stateV1, error) { | |
1897 | v1State := &stateV1{} | |
1898 | if err := json.Unmarshal(jsonBytes, v1State); err != nil { | |
1899 | return nil, fmt.Errorf("Decoding state file failed: %v", err) | |
1900 | } | |
1901 | ||
1902 | if v1State.Version != 1 { | |
1903 | return nil, fmt.Errorf("Decoded state version did not match the decoder selection: "+ | |
1904 | "read %d, expected 1", v1State.Version) | |
1905 | } | |
1906 | ||
1907 | return v1State, nil | |
1908 | } | |
1909 | ||
1910 | func ReadStateV2(jsonBytes []byte) (*State, error) { | |
1911 | state := &State{} | |
1912 | if err := json.Unmarshal(jsonBytes, state); err != nil { | |
1913 | return nil, fmt.Errorf("Decoding state file failed: %v", err) | |
1914 | } | |
1915 | ||
1916 | // Check the version, this to ensure we don't read a future | |
1917 | // version that we don't understand | |
1918 | if state.Version > StateVersion { | |
1919 | return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", | |
1920 | SemVersion.String(), state.Version) | |
1921 | } | |
1922 | ||
1923 | // Make sure the version is semantic | |
1924 | if state.TFVersion != "" { | |
1925 | if _, err := version.NewVersion(state.TFVersion); err != nil { | |
1926 | return nil, fmt.Errorf( | |
1927 | "State contains invalid version: %s\n\n"+ | |
1928 | "Terraform validates the version format prior to writing it. This\n"+ | |
1929 | "means that this is invalid of the state becoming corrupted through\n"+ | |
1930 | "some external means. Please manually modify the Terraform version\n"+ | |
1931 | "field to be a proper semantic version.", | |
1932 | state.TFVersion) | |
1933 | } | |
1934 | } | |
1935 | ||
1936 | // catch any unitialized fields in the state | |
1937 | state.init() | |
1938 | ||
1939 | // Sort it | |
1940 | state.sort() | |
1941 | ||
1942 | return state, nil | |
1943 | } | |
1944 | ||
1945 | func ReadStateV3(jsonBytes []byte) (*State, error) { | |
1946 | state := &State{} | |
1947 | if err := json.Unmarshal(jsonBytes, state); err != nil { | |
1948 | return nil, fmt.Errorf("Decoding state file failed: %v", err) | |
1949 | } | |
1950 | ||
1951 | // Check the version, this to ensure we don't read a future | |
1952 | // version that we don't understand | |
1953 | if state.Version > StateVersion { | |
1954 | return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", | |
1955 | SemVersion.String(), state.Version) | |
1956 | } | |
1957 | ||
1958 | // Make sure the version is semantic | |
1959 | if state.TFVersion != "" { | |
1960 | if _, err := version.NewVersion(state.TFVersion); err != nil { | |
1961 | return nil, fmt.Errorf( | |
1962 | "State contains invalid version: %s\n\n"+ | |
1963 | "Terraform validates the version format prior to writing it. This\n"+ | |
1964 | "means that this is invalid of the state becoming corrupted through\n"+ | |
1965 | "some external means. Please manually modify the Terraform version\n"+ | |
1966 | "field to be a proper semantic version.", | |
1967 | state.TFVersion) | |
1968 | } | |
1969 | } | |
1970 | ||
1971 | // catch any unitialized fields in the state | |
1972 | state.init() | |
1973 | ||
1974 | // Sort it | |
1975 | state.sort() | |
1976 | ||
1977 | // Now we write the state back out to detect any changes in normaliztion. | |
1978 | // If our state is now written out differently, bump the serial number to | |
1979 | // prevent conflicts. | |
1980 | var buf bytes.Buffer | |
1981 | err := WriteState(state, &buf) | |
1982 | if err != nil { | |
1983 | return nil, err | |
1984 | } | |
1985 | ||
1986 | if !bytes.Equal(jsonBytes, buf.Bytes()) { | |
1987 | log.Println("[INFO] state modified during read or write. incrementing serial number") | |
1988 | state.Serial++ | |
1989 | } | |
1990 | ||
1991 | return state, nil | |
1992 | } | |
1993 | ||
1994 | // WriteState writes a state somewhere in a binary format. | |
1995 | func WriteState(d *State, dst io.Writer) error { | |
1996 | // writing a nil state is a noop. | |
1997 | if d == nil { | |
1998 | return nil | |
1999 | } | |
2000 | ||
2001 | // make sure we have no uninitialized fields | |
2002 | d.init() | |
2003 | ||
2004 | // Make sure it is sorted | |
2005 | d.sort() | |
2006 | ||
2007 | // Ensure the version is set | |
2008 | d.Version = StateVersion | |
2009 | ||
2010 | // If the TFVersion is set, verify it. We used to just set the version | |
2011 | // here, but this isn't safe since it changes the MD5 sum on some remote | |
2012 | // state storage backends such as Atlas. We now leave it be if needed. | |
2013 | if d.TFVersion != "" { | |
2014 | if _, err := version.NewVersion(d.TFVersion); err != nil { | |
2015 | return fmt.Errorf( | |
2016 | "Error writing state, invalid version: %s\n\n"+ | |
2017 | "The Terraform version when writing the state must be a semantic\n"+ | |
2018 | "version.", | |
2019 | d.TFVersion) | |
2020 | } | |
2021 | } | |
2022 | ||
2023 | // Encode the data in a human-friendly way | |
2024 | data, err := json.MarshalIndent(d, "", " ") | |
2025 | if err != nil { | |
2026 | return fmt.Errorf("Failed to encode state: %s", err) | |
2027 | } | |
2028 | ||
2029 | // We append a newline to the data because MarshalIndent doesn't | |
2030 | data = append(data, '\n') | |
2031 | ||
2032 | // Write the data out to the dst | |
2033 | if _, err := io.Copy(dst, bytes.NewReader(data)); err != nil { | |
2034 | return fmt.Errorf("Failed to write state: %v", err) | |
2035 | } | |
2036 | ||
2037 | return nil | |
2038 | } | |
2039 | ||
2040 | // resourceNameSort implements the sort.Interface to sort name parts lexically for | |
2041 | // strings and numerically for integer indexes. | |
2042 | type resourceNameSort []string | |
2043 | ||
2044 | func (r resourceNameSort) Len() int { return len(r) } | |
2045 | func (r resourceNameSort) Swap(i, j int) { r[i], r[j] = r[j], r[i] } | |
2046 | ||
2047 | func (r resourceNameSort) Less(i, j int) bool { | |
2048 | iParts := strings.Split(r[i], ".") | |
2049 | jParts := strings.Split(r[j], ".") | |
2050 | ||
2051 | end := len(iParts) | |
2052 | if len(jParts) < end { | |
2053 | end = len(jParts) | |
2054 | } | |
2055 | ||
2056 | for idx := 0; idx < end; idx++ { | |
2057 | if iParts[idx] == jParts[idx] { | |
2058 | continue | |
2059 | } | |
2060 | ||
2061 | // sort on the first non-matching part | |
2062 | iInt, iIntErr := strconv.Atoi(iParts[idx]) | |
2063 | jInt, jIntErr := strconv.Atoi(jParts[idx]) | |
2064 | ||
2065 | switch { | |
2066 | case iIntErr == nil && jIntErr == nil: | |
2067 | // sort numerically if both parts are integers | |
2068 | return iInt < jInt | |
2069 | case iIntErr == nil: | |
2070 | // numbers sort before strings | |
2071 | return true | |
2072 | case jIntErr == nil: | |
2073 | return false | |
2074 | default: | |
2075 | return iParts[idx] < jParts[idx] | |
2076 | } | |
2077 | } | |
2078 | ||
2079 | return r[i] < r[j] | |
2080 | } | |
2081 | ||
2082 | // moduleStateSort implements sort.Interface to sort module states | |
2083 | type moduleStateSort []*ModuleState | |
2084 | ||
2085 | func (s moduleStateSort) Len() int { | |
2086 | return len(s) | |
2087 | } | |
2088 | ||
2089 | func (s moduleStateSort) Less(i, j int) bool { | |
2090 | a := s[i] | |
2091 | b := s[j] | |
2092 | ||
2093 | // If either is nil, then the nil one is "less" than | |
2094 | if a == nil || b == nil { | |
2095 | return a == nil | |
2096 | } | |
2097 | ||
2098 | // If the lengths are different, then the shorter one always wins | |
2099 | if len(a.Path) != len(b.Path) { | |
2100 | return len(a.Path) < len(b.Path) | |
2101 | } | |
2102 | ||
2103 | // Otherwise, compare lexically | |
2104 | return strings.Join(a.Path, ".") < strings.Join(b.Path, ".") | |
2105 | } | |
2106 | ||
2107 | func (s moduleStateSort) Swap(i, j int) { | |
2108 | s[i], s[j] = s[j], s[i] | |
2109 | } | |
2110 | ||
2111 | const stateValidateErrMultiModule = ` | |
2112 | Multiple modules with the same path: %s | |
2113 | ||
2114 | This means that there are multiple entries in the "modules" field | |
2115 | in your state file that point to the same module. This will cause Terraform | |
2116 | to behave in unexpected and error prone ways and is invalid. Please back up | |
2117 | and modify your state file manually to resolve this. | |
2118 | ` |