package statefile import ( "encoding/json" "fmt" "sync" "github.com/hashicorp/terraform/tfdiags" ) func readStateV2(src []byte) (*File, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics sV2 := &stateV2{} err := json.Unmarshal(src, sV2) if err != nil { diags = diags.Append(jsonUnmarshalDiags(err)) return nil, diags } file, prepDiags := prepareStateV2(sV2) diags = diags.Append(prepDiags) return file, diags } func prepareStateV2(sV2 *stateV2) (*File, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics sV3, err := upgradeStateV2ToV3(sV2) if err != nil { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, upgradeFailed, fmt.Sprintf("Error upgrading state file format from version 2 to version 3: %s.", err), )) return nil, diags } file, prepDiags := prepareStateV3(sV3) diags = diags.Append(prepDiags) return file, diags } // stateV2 is a representation of the legacy JSON state format version 2. // // It is only used to read version 2 JSON files prior to upgrading them to // the current format. type stateV2 struct { // Version is the state file protocol version. Version int `json:"version"` // TFVersion is the version of Terraform that wrote this state. TFVersion string `json:"terraform_version,omitempty"` // Serial is incremented on any operation that modifies // the State file. It is used to detect potentially conflicting // updates. Serial int64 `json:"serial"` // Lineage is set when a new, blank state is created and then // never updated. This allows us to determine whether the serials // of two states can be meaningfully compared. // Apart from the guarantee that collisions between two lineages // are very unlikely, this value is opaque and external callers // should only compare lineage strings byte-for-byte for equality. Lineage string `json:"lineage"` // Remote is used to track the metadata required to // pull and push state files from a remote storage endpoint. Remote *remoteStateV2 `json:"remote,omitempty"` // Backend tracks the configuration for the backend in use with // this state. This is used to track any changes in the backend // configuration. Backend *backendStateV2 `json:"backend,omitempty"` // Modules contains all the modules in a breadth-first order Modules []*moduleStateV2 `json:"modules"` } type remoteStateV2 struct { // Type controls the client we use for the remote state Type string `json:"type"` // Config is used to store arbitrary configuration that // is type specific Config map[string]string `json:"config"` } type outputStateV2 struct { // Sensitive describes whether the output is considered sensitive, // which may lead to masking the value on screen in some cases. Sensitive bool `json:"sensitive"` // Type describes the structure of Value. Valid values are "string", // "map" and "list" Type string `json:"type"` // Value contains the value of the output, in the structure described // by the Type field. Value interface{} `json:"value"` mu sync.Mutex } type moduleStateV2 struct { // Path is the import path from the root module. Modules imports are // always disjoint, so the path represents amodule tree Path []string `json:"path"` // Locals are kept only transiently in-memory, because we can always // re-compute them. Locals map[string]interface{} `json:"-"` // Outputs declared by the module and maintained for each module // even though only the root module technically needs to be kept. // This allows operators to inspect values at the boundaries. Outputs map[string]*outputStateV2 `json:"outputs"` // Resources is a mapping of the logically named resource to // the state of the resource. Each resource may actually have // N instances underneath, although a user only needs to think // about the 1:1 case. Resources map[string]*resourceStateV2 `json:"resources"` // Dependencies are a list of things that this module relies on // existing to remain intact. For example: an module may depend // on a VPC ID given by an aws_vpc resource. // // Terraform uses this information to build valid destruction // orders and to warn the user if they're destroying a module that // another resource depends on. // // Things can be put into this list that may not be managed by // Terraform. If Terraform doesn't find a matching ID in the // overall state, then it assumes it isn't managed and doesn't // worry about it. Dependencies []string `json:"depends_on"` } type resourceStateV2 struct { // This is filled in and managed by Terraform, and is the resource // type itself such as "mycloud_instance". If a resource provider sets // this value, it won't be persisted. Type string `json:"type"` // Dependencies are a list of things that this resource relies on // existing to remain intact. For example: an AWS instance might // depend on a subnet (which itself might depend on a VPC, and so // on). // // Terraform uses this information to build valid destruction // orders and to warn the user if they're destroying a resource that // another resource depends on. // // Things can be put into this list that may not be managed by // Terraform. If Terraform doesn't find a matching ID in the // overall state, then it assumes it isn't managed and doesn't // worry about it. Dependencies []string `json:"depends_on"` // Primary is the current active instance for this resource. // It can be replaced but only after a successful creation. // This is the instances on which providers will act. Primary *instanceStateV2 `json:"primary"` // Deposed is used in the mechanics of CreateBeforeDestroy: the existing // Primary is Deposed to get it out of the way for the replacement Primary to // be created by Apply. If the replacement Primary creates successfully, the // Deposed instance is cleaned up. // // If there were problems creating the replacement Primary, the Deposed // instance and the (now tainted) replacement Primary will be swapped so the // tainted replacement will be cleaned up instead. // // An instance will remain in the Deposed list until it is successfully // destroyed and purged. Deposed []*instanceStateV2 `json:"deposed"` // Provider is used when a resource is connected to a provider with an alias. // If this string is empty, the resource is connected to the default provider, // e.g. "aws_instance" goes with the "aws" provider. // If the resource block contained a "provider" key, that value will be set here. Provider string `json:"provider"` mu sync.Mutex } type instanceStateV2 struct { // A unique ID for this resource. This is opaque to Terraform // and is only meant as a lookup mechanism for the providers. ID string `json:"id"` // Attributes are basic information about the resource. Any keys here // are accessible in variable format within Terraform configurations: // ${resourcetype.name.attribute}. Attributes map[string]string `json:"attributes"` // Meta is a simple K/V map that is persisted to the State but otherwise // ignored by Terraform core. It's meant to be used for accounting by // external client code. The value here must only contain Go primitives // and collections. Meta map[string]interface{} `json:"meta"` // Tainted is used to mark a resource for recreation. Tainted bool `json:"tainted"` } type backendStateV2 struct { Type string `json:"type"` // Backend type ConfigRaw json.RawMessage `json:"config"` // Backend raw config Hash uint64 `json:"hash"` // Hash of portion of configuration from config files }