205 lines
7.4 KiB
Go
205 lines
7.4 KiB
Go
package statefile
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"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"`
|
|
}
|
|
|
|
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"`
|
|
}
|
|
|
|
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
|
|
}
|