github.com/opentofu/opentofu@v1.7.1/internal/states/instance_object_src.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 states
     7  
     8  import (
     9  	"github.com/zclconf/go-cty/cty"
    10  	ctyjson "github.com/zclconf/go-cty/cty/json"
    11  
    12  	"github.com/opentofu/opentofu/internal/addrs"
    13  	"github.com/opentofu/opentofu/internal/configs/hcl2shim"
    14  )
    15  
    16  // ResourceInstanceObjectSrc is a not-fully-decoded version of
    17  // ResourceInstanceObject. Decoding of it can be completed by first handling
    18  // any schema migration steps to get to the latest schema version and then
    19  // calling method Decode with the implied type of the latest schema.
    20  type ResourceInstanceObjectSrc struct {
    21  	// SchemaVersion is the resource-type-specific schema version number that
    22  	// was current when either AttrsJSON or AttrsFlat was encoded. Migration
    23  	// steps are required if this is less than the current version number
    24  	// reported by the corresponding provider.
    25  	SchemaVersion uint64
    26  
    27  	// AttrsJSON is a JSON-encoded representation of the object attributes,
    28  	// encoding the value (of the object type implied by the associated resource
    29  	// type schema) that represents this remote object in OpenTofu Language
    30  	// expressions, and is compared with configuration when producing a diff.
    31  	//
    32  	// This is retained in JSON format here because it may require preprocessing
    33  	// before decoding if, for example, the stored attributes are for an older
    34  	// schema version which the provider must upgrade before use. If the
    35  	// version is current, it is valid to simply decode this using the
    36  	// type implied by the current schema, without the need for the provider
    37  	// to perform an upgrade first.
    38  	//
    39  	// When writing a ResourceInstanceObject into the state, AttrsJSON should
    40  	// always be conformant to the current schema version and the current
    41  	// schema version should be recorded in the SchemaVersion field.
    42  	AttrsJSON []byte
    43  
    44  	// AttrsFlat is a legacy form of attributes used in older state file
    45  	// formats, and in the new state format for objects that haven't yet been
    46  	// upgraded. This attribute is mutually exclusive with Attrs: for any
    47  	// ResourceInstanceObject, only one of these attributes may be populated
    48  	// and the other must be nil.
    49  	//
    50  	// An instance object with this field populated should be upgraded to use
    51  	// Attrs at the earliest opportunity, since this legacy flatmap-based
    52  	// format will be phased out over time. AttrsFlat should not be used when
    53  	// writing new or updated objects to state; instead, callers must follow
    54  	// the recommendations in the AttrsJSON documentation above.
    55  	AttrsFlat map[string]string
    56  
    57  	// AttrSensitivePaths is an array of paths to mark as sensitive coming out of
    58  	// state, or to save as sensitive paths when saving state
    59  	AttrSensitivePaths []cty.PathValueMarks
    60  
    61  	// These fields all correspond to the fields of the same name on
    62  	// ResourceInstanceObject.
    63  	Private             []byte
    64  	Status              ObjectStatus
    65  	Dependencies        []addrs.ConfigResource
    66  	CreateBeforeDestroy bool
    67  }
    68  
    69  // Decode unmarshals the raw representation of the object attributes. Pass the
    70  // implied type of the corresponding resource type schema for correct operation.
    71  //
    72  // Before calling Decode, the caller must check that the SchemaVersion field
    73  // exactly equals the version number of the schema whose implied type is being
    74  // passed, or else the result is undefined.
    75  //
    76  // The returned object may share internal references with the receiver and
    77  // so the caller must not mutate the receiver any further once once this
    78  // method is called.
    79  func (os *ResourceInstanceObjectSrc) Decode(ty cty.Type) (*ResourceInstanceObject, error) {
    80  	var val cty.Value
    81  	var err error
    82  	if os.AttrsFlat != nil {
    83  		// Legacy mode. We'll do our best to unpick this from the flatmap.
    84  		val, err = hcl2shim.HCL2ValueFromFlatmap(os.AttrsFlat, ty)
    85  		if err != nil {
    86  			return nil, err
    87  		}
    88  	} else {
    89  		val, err = ctyjson.Unmarshal(os.AttrsJSON, ty)
    90  		// Mark the value with paths if applicable
    91  		if os.AttrSensitivePaths != nil {
    92  			val = val.MarkWithPaths(os.AttrSensitivePaths)
    93  		}
    94  		if err != nil {
    95  			return nil, err
    96  		}
    97  	}
    98  
    99  	return &ResourceInstanceObject{
   100  		Value:               val,
   101  		Status:              os.Status,
   102  		Dependencies:        os.Dependencies,
   103  		Private:             os.Private,
   104  		CreateBeforeDestroy: os.CreateBeforeDestroy,
   105  	}, nil
   106  }
   107  
   108  // CompleteUpgrade creates a new ResourceInstanceObjectSrc by copying the
   109  // metadata from the receiver and writing in the given new schema version
   110  // and attribute value that are presumed to have resulted from upgrading
   111  // from an older schema version.
   112  func (os *ResourceInstanceObjectSrc) CompleteUpgrade(newAttrs cty.Value, newType cty.Type, newSchemaVersion uint64) (*ResourceInstanceObjectSrc, error) {
   113  	new := os.DeepCopy()
   114  	new.AttrsFlat = nil // We always use JSON after an upgrade, even if the source used flatmap
   115  
   116  	// This is the same principle as ResourceInstanceObject.Encode, but
   117  	// avoiding a decode/re-encode cycle because we don't have type info
   118  	// available for the "old" attributes.
   119  	newAttrs = cty.UnknownAsNull(newAttrs)
   120  	src, err := ctyjson.Marshal(newAttrs, newType)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	new.AttrsJSON = src
   126  	new.SchemaVersion = newSchemaVersion
   127  	return new, nil
   128  }