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  }