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 }