github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/states/statefile/version2.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package statefile 5 6 import ( 7 "encoding/json" 8 "fmt" 9 10 "github.com/terramate-io/tf/tfdiags" 11 ) 12 13 func readStateV2(src []byte) (*File, tfdiags.Diagnostics) { 14 var diags tfdiags.Diagnostics 15 sV2 := &stateV2{} 16 err := json.Unmarshal(src, sV2) 17 if err != nil { 18 diags = diags.Append(jsonUnmarshalDiags(err)) 19 return nil, diags 20 } 21 22 file, prepDiags := prepareStateV2(sV2) 23 diags = diags.Append(prepDiags) 24 return file, diags 25 } 26 27 func prepareStateV2(sV2 *stateV2) (*File, tfdiags.Diagnostics) { 28 var diags tfdiags.Diagnostics 29 sV3, err := upgradeStateV2ToV3(sV2) 30 if err != nil { 31 diags = diags.Append(tfdiags.Sourceless( 32 tfdiags.Error, 33 upgradeFailed, 34 fmt.Sprintf("Error upgrading state file format from version 2 to version 3: %s.", err), 35 )) 36 return nil, diags 37 } 38 39 file, prepDiags := prepareStateV3(sV3) 40 diags = diags.Append(prepDiags) 41 return file, diags 42 } 43 44 // stateV2 is a representation of the legacy JSON state format version 2. 45 // 46 // It is only used to read version 2 JSON files prior to upgrading them to 47 // the current format. 48 type stateV2 struct { 49 // Version is the state file protocol version. 50 Version int `json:"version"` 51 52 // TFVersion is the version of Terraform that wrote this state. 53 TFVersion string `json:"terraform_version,omitempty"` 54 55 // Serial is incremented on any operation that modifies 56 // the State file. It is used to detect potentially conflicting 57 // updates. 58 Serial int64 `json:"serial"` 59 60 // Lineage is set when a new, blank state is created and then 61 // never updated. This allows us to determine whether the serials 62 // of two states can be meaningfully compared. 63 // Apart from the guarantee that collisions between two lineages 64 // are very unlikely, this value is opaque and external callers 65 // should only compare lineage strings byte-for-byte for equality. 66 Lineage string `json:"lineage"` 67 68 // Remote is used to track the metadata required to 69 // pull and push state files from a remote storage endpoint. 70 Remote *remoteStateV2 `json:"remote,omitempty"` 71 72 // Backend tracks the configuration for the backend in use with 73 // this state. This is used to track any changes in the backend 74 // configuration. 75 Backend *backendStateV2 `json:"backend,omitempty"` 76 77 // Modules contains all the modules in a breadth-first order 78 Modules []*moduleStateV2 `json:"modules"` 79 } 80 81 type remoteStateV2 struct { 82 // Type controls the client we use for the remote state 83 Type string `json:"type"` 84 85 // Config is used to store arbitrary configuration that 86 // is type specific 87 Config map[string]string `json:"config"` 88 } 89 90 type outputStateV2 struct { 91 // Sensitive describes whether the output is considered sensitive, 92 // which may lead to masking the value on screen in some cases. 93 Sensitive bool `json:"sensitive"` 94 // Type describes the structure of Value. Valid values are "string", 95 // "map" and "list" 96 Type string `json:"type"` 97 // Value contains the value of the output, in the structure described 98 // by the Type field. 99 Value interface{} `json:"value"` 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 183 type instanceStateV2 struct { 184 // A unique ID for this resource. This is opaque to Terraform 185 // and is only meant as a lookup mechanism for the providers. 186 ID string `json:"id"` 187 188 // Attributes are basic information about the resource. Any keys here 189 // are accessible in variable format within Terraform configurations: 190 // ${resourcetype.name.attribute}. 191 Attributes map[string]string `json:"attributes"` 192 193 // Meta is a simple K/V map that is persisted to the State but otherwise 194 // ignored by Terraform core. It's meant to be used for accounting by 195 // external client code. The value here must only contain Go primitives 196 // and collections. 197 Meta map[string]interface{} `json:"meta"` 198 199 // Tainted is used to mark a resource for recreation. 200 Tainted bool `json:"tainted"` 201 } 202 203 type backendStateV2 struct { 204 Type string `json:"type"` // Backend type 205 ConfigRaw json.RawMessage `json:"config"` // Backend raw config 206 Hash uint64 `json:"hash"` // Hash of portion of configuration from config files 207 }