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