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