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