aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/states/statefile/version2_upgrade.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/states/statefile/version2_upgrade.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/states/statefile/version2_upgrade.go145
1 files changed, 145 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/states/statefile/version2_upgrade.go b/vendor/github.com/hashicorp/terraform/states/statefile/version2_upgrade.go
new file mode 100644
index 0000000..2d03c07
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/states/statefile/version2_upgrade.go
@@ -0,0 +1,145 @@
1package statefile
2
3import (
4 "fmt"
5 "log"
6 "regexp"
7 "sort"
8 "strconv"
9 "strings"
10
11 "github.com/mitchellh/copystructure"
12)
13
14func upgradeStateV2ToV3(old *stateV2) (*stateV3, error) {
15 if old == nil {
16 return (*stateV3)(nil), nil
17 }
18
19 var new *stateV3
20 {
21 copy, err := copystructure.Config{Lock: true}.Copy(old)
22 if err != nil {
23 panic(err)
24 }
25 newWrongType := copy.(*stateV2)
26 newRightType := (stateV3)(*newWrongType)
27 new = &newRightType
28 }
29
30 // Set the new version number
31 new.Version = 3
32
33 // Change the counts for things which look like maps to use the %
34 // syntax. Remove counts for empty collections - they will be added
35 // back in later.
36 for _, module := range new.Modules {
37 for _, resource := range module.Resources {
38 // Upgrade Primary
39 if resource.Primary != nil {
40 upgradeAttributesV2ToV3(resource.Primary)
41 }
42
43 // Upgrade Deposed
44 for _, deposed := range resource.Deposed {
45 upgradeAttributesV2ToV3(deposed)
46 }
47 }
48 }
49
50 return new, nil
51}
52
53func upgradeAttributesV2ToV3(instanceState *instanceStateV2) error {
54 collectionKeyRegexp := regexp.MustCompile(`^(.*\.)#$`)
55 collectionSubkeyRegexp := regexp.MustCompile(`^([^\.]+)\..*`)
56
57 // Identify the key prefix of anything which is a collection
58 var collectionKeyPrefixes []string
59 for key := range instanceState.Attributes {
60 if submatches := collectionKeyRegexp.FindAllStringSubmatch(key, -1); len(submatches) > 0 {
61 collectionKeyPrefixes = append(collectionKeyPrefixes, submatches[0][1])
62 }
63 }
64 sort.Strings(collectionKeyPrefixes)
65
66 log.Printf("[STATE UPGRADE] Detected the following collections in state: %v", collectionKeyPrefixes)
67
68 // This could be rolled into fewer loops, but it is somewhat clearer this way, and will not
69 // run very often.
70 for _, prefix := range collectionKeyPrefixes {
71 // First get the actual keys that belong to this prefix
72 var potentialKeysMatching []string
73 for key := range instanceState.Attributes {
74 if strings.HasPrefix(key, prefix) {
75 potentialKeysMatching = append(potentialKeysMatching, strings.TrimPrefix(key, prefix))
76 }
77 }
78 sort.Strings(potentialKeysMatching)
79
80 var actualKeysMatching []string
81 for _, key := range potentialKeysMatching {
82 if submatches := collectionSubkeyRegexp.FindAllStringSubmatch(key, -1); len(submatches) > 0 {
83 actualKeysMatching = append(actualKeysMatching, submatches[0][1])
84 } else {
85 if key != "#" {
86 actualKeysMatching = append(actualKeysMatching, key)
87 }
88 }
89 }
90 actualKeysMatching = uniqueSortedStrings(actualKeysMatching)
91
92 // Now inspect the keys in order to determine whether this is most likely to be
93 // a map, list or set. There is room for error here, so we log in each case. If
94 // there is no method of telling, we remove the key from the InstanceState in
95 // order that it will be recreated. Again, this could be rolled into fewer loops
96 // but we prefer clarity.
97
98 oldCountKey := fmt.Sprintf("%s#", prefix)
99
100 // First, detect "obvious" maps - which have non-numeric keys (mostly).
101 hasNonNumericKeys := false
102 for _, key := range actualKeysMatching {
103 if _, err := strconv.Atoi(key); err != nil {
104 hasNonNumericKeys = true
105 }
106 }
107 if hasNonNumericKeys {
108 newCountKey := fmt.Sprintf("%s%%", prefix)
109
110 instanceState.Attributes[newCountKey] = instanceState.Attributes[oldCountKey]
111 delete(instanceState.Attributes, oldCountKey)
112 log.Printf("[STATE UPGRADE] Detected %s as a map. Replaced count = %s",
113 strings.TrimSuffix(prefix, "."), instanceState.Attributes[newCountKey])
114 }
115
116 // Now detect empty collections and remove them from state.
117 if len(actualKeysMatching) == 0 {
118 delete(instanceState.Attributes, oldCountKey)
119 log.Printf("[STATE UPGRADE] Detected %s as an empty collection. Removed from state.",
120 strings.TrimSuffix(prefix, "."))
121 }
122 }
123
124 return nil
125}
126
127// uniqueSortedStrings removes duplicates from a slice of strings and returns
128// a sorted slice of the unique strings.
129func uniqueSortedStrings(input []string) []string {
130 uniquemap := make(map[string]struct{})
131 for _, str := range input {
132 uniquemap[str] = struct{}{}
133 }
134
135 output := make([]string, len(uniquemap))
136
137 i := 0
138 for key := range uniquemap {
139 output[i] = key
140 i = i + 1
141 }
142
143 sort.Strings(output)
144 return output
145}