]>
Commit | Line | Data |
---|---|---|
1 | package statefile | |
2 | ||
3 | import ( | |
4 | "encoding/json" | |
5 | "fmt" | |
6 | "sync" | |
7 | ||
8 | "github.com/hashicorp/terraform/tfdiags" | |
9 | ) | |
10 | ||
11 | func readStateV2(src []byte) (*File, tfdiags.Diagnostics) { | |
12 | var diags tfdiags.Diagnostics | |
13 | sV2 := &stateV2{} | |
14 | err := json.Unmarshal(src, sV2) | |
15 | if err != nil { | |
16 | diags = diags.Append(jsonUnmarshalDiags(err)) | |
17 | return nil, diags | |
18 | } | |
19 | ||
20 | file, prepDiags := prepareStateV2(sV2) | |
21 | diags = diags.Append(prepDiags) | |
22 | return file, diags | |
23 | } | |
24 | ||
25 | func prepareStateV2(sV2 *stateV2) (*File, tfdiags.Diagnostics) { | |
26 | var diags tfdiags.Diagnostics | |
27 | sV3, err := upgradeStateV2ToV3(sV2) | |
28 | if err != nil { | |
29 | diags = diags.Append(tfdiags.Sourceless( | |
30 | tfdiags.Error, | |
31 | upgradeFailed, | |
32 | fmt.Sprintf("Error upgrading state file format from version 2 to version 3: %s.", err), | |
33 | )) | |
34 | return nil, diags | |
35 | } | |
36 | ||
37 | file, prepDiags := prepareStateV3(sV3) | |
38 | diags = diags.Append(prepDiags) | |
39 | return file, diags | |
40 | } | |
41 | ||
42 | // stateV2 is a representation of the legacy JSON state format version 2. | |
43 | // | |
44 | // It is only used to read version 2 JSON files prior to upgrading them to | |
45 | // the current format. | |
46 | type stateV2 struct { | |
47 | // Version is the state file protocol version. | |
48 | Version int `json:"version"` | |
49 | ||
50 | // TFVersion is the version of Terraform that wrote this state. | |
51 | TFVersion string `json:"terraform_version,omitempty"` | |
52 | ||
53 | // Serial is incremented on any operation that modifies | |
54 | // the State file. It is used to detect potentially conflicting | |
55 | // updates. | |
56 | Serial int64 `json:"serial"` | |
57 | ||
58 | // Lineage is set when a new, blank state is created and then | |
59 | // never updated. This allows us to determine whether the serials | |
60 | // of two states can be meaningfully compared. | |
61 | // Apart from the guarantee that collisions between two lineages | |
62 | // are very unlikely, this value is opaque and external callers | |
63 | // should only compare lineage strings byte-for-byte for equality. | |
64 | Lineage string `json:"lineage"` | |
65 | ||
66 | // Remote is used to track the metadata required to | |
67 | // pull and push state files from a remote storage endpoint. | |
68 | Remote *remoteStateV2 `json:"remote,omitempty"` | |
69 | ||
70 | // Backend tracks the configuration for the backend in use with | |
71 | // this state. This is used to track any changes in the backend | |
72 | // configuration. | |
73 | Backend *backendStateV2 `json:"backend,omitempty"` | |
74 | ||
75 | // Modules contains all the modules in a breadth-first order | |
76 | Modules []*moduleStateV2 `json:"modules"` | |
77 | } | |
78 | ||
79 | type remoteStateV2 struct { | |
80 | // Type controls the client we use for the remote state | |
81 | Type string `json:"type"` | |
82 | ||
83 | // Config is used to store arbitrary configuration that | |
84 | // is type specific | |
85 | Config map[string]string `json:"config"` | |
86 | } | |
87 | ||
88 | type outputStateV2 struct { | |
89 | // Sensitive describes whether the output is considered sensitive, | |
90 | // which may lead to masking the value on screen in some cases. | |
91 | Sensitive bool `json:"sensitive"` | |
92 | // Type describes the structure of Value. Valid values are "string", | |
93 | // "map" and "list" | |
94 | Type string `json:"type"` | |
95 | // Value contains the value of the output, in the structure described | |
96 | // by the Type field. | |
97 | Value interface{} `json:"value"` | |
98 | ||
99 | mu sync.Mutex | |
100 | } | |
101 | ||
102 | type moduleStateV2 struct { | |
103 | // Path is the import path from the root module. Modules imports are | |
104 | // always disjoint, so the path represents amodule tree | |
105 | Path []string `json:"path"` | |
106 | ||
107 | // Locals are kept only transiently in-memory, because we can always | |
108 | // re-compute them. | |
109 | Locals map[string]interface{} `json:"-"` | |
110 | ||
111 | // Outputs declared by the module and maintained for each module | |
112 | // even though only the root module technically needs to be kept. | |
113 | // This allows operators to inspect values at the boundaries. | |
114 | Outputs map[string]*outputStateV2 `json:"outputs"` | |
115 | ||
116 | // Resources is a mapping of the logically named resource to | |
117 | // the state of the resource. Each resource may actually have | |
118 | // N instances underneath, although a user only needs to think | |
119 | // about the 1:1 case. | |
120 | Resources map[string]*resourceStateV2 `json:"resources"` | |
121 | ||
122 | // Dependencies are a list of things that this module relies on | |
123 | // existing to remain intact. For example: an module may depend | |
124 | // on a VPC ID given by an aws_vpc resource. | |
125 | // | |
126 | // Terraform uses this information to build valid destruction | |
127 | // orders and to warn the user if they're destroying a module that | |
128 | // another resource depends on. | |
129 | // | |
130 | // Things can be put into this list that may not be managed by | |
131 | // Terraform. If Terraform doesn't find a matching ID in the | |
132 | // overall state, then it assumes it isn't managed and doesn't | |
133 | // worry about it. | |
134 | Dependencies []string `json:"depends_on"` | |
135 | } | |
136 | ||
137 | type resourceStateV2 struct { | |
138 | // This is filled in and managed by Terraform, and is the resource | |
139 | // type itself such as "mycloud_instance". If a resource provider sets | |
140 | // this value, it won't be persisted. | |
141 | Type string `json:"type"` | |
142 | ||
143 | // Dependencies are a list of things that this resource relies on | |
144 | // existing to remain intact. For example: an AWS instance might | |
145 | // depend on a subnet (which itself might depend on a VPC, and so | |
146 | // on). | |
147 | // | |
148 | // Terraform uses this information to build valid destruction | |
149 | // orders and to warn the user if they're destroying a resource that | |
150 | // another resource depends on. | |
151 | // | |
152 | // Things can be put into this list that may not be managed by | |
153 | // Terraform. If Terraform doesn't find a matching ID in the | |
154 | // overall state, then it assumes it isn't managed and doesn't | |
155 | // worry about it. | |
156 | Dependencies []string `json:"depends_on"` | |
157 | ||
158 | // Primary is the current active instance for this resource. | |
159 | // It can be replaced but only after a successful creation. | |
160 | // This is the instances on which providers will act. | |
161 | Primary *instanceStateV2 `json:"primary"` | |
162 | ||
163 | // Deposed is used in the mechanics of CreateBeforeDestroy: the existing | |
164 | // Primary is Deposed to get it out of the way for the replacement Primary to | |
165 | // be created by Apply. If the replacement Primary creates successfully, the | |
166 | // Deposed instance is cleaned up. | |
167 | // | |
168 | // If there were problems creating the replacement Primary, the Deposed | |
169 | // instance and the (now tainted) replacement Primary will be swapped so the | |
170 | // tainted replacement will be cleaned up instead. | |
171 | // | |
172 | // An instance will remain in the Deposed list until it is successfully | |
173 | // destroyed and purged. | |
174 | Deposed []*instanceStateV2 `json:"deposed"` | |
175 | ||
176 | // Provider is used when a resource is connected to a provider with an alias. | |
177 | // If this string is empty, the resource is connected to the default provider, | |
178 | // e.g. "aws_instance" goes with the "aws" provider. | |
179 | // If the resource block contained a "provider" key, that value will be set here. | |
180 | Provider string `json:"provider"` | |
181 | ||
182 | mu sync.Mutex | |
183 | } | |
184 | ||
185 | type instanceStateV2 struct { | |
186 | // A unique ID for this resource. This is opaque to Terraform | |
187 | // and is only meant as a lookup mechanism for the providers. | |
188 | ID string `json:"id"` | |
189 | ||
190 | // Attributes are basic information about the resource. Any keys here | |
191 | // are accessible in variable format within Terraform configurations: | |
192 | // ${resourcetype.name.attribute}. | |
193 | Attributes map[string]string `json:"attributes"` | |
194 | ||
195 | // Meta is a simple K/V map that is persisted to the State but otherwise | |
196 | // ignored by Terraform core. It's meant to be used for accounting by | |
197 | // external client code. The value here must only contain Go primitives | |
198 | // and collections. | |
199 | Meta map[string]interface{} `json:"meta"` | |
200 | ||
201 | // Tainted is used to mark a resource for recreation. | |
202 | Tainted bool `json:"tainted"` | |
203 | } | |
204 | ||
205 | type backendStateV2 struct { | |
206 | Type string `json:"type"` // Backend type | |
207 | ConfigRaw json.RawMessage `json:"config"` // Backend raw config | |
208 | Hash uint64 `json:"hash"` // Hash of portion of configuration from config files | |
209 | } |