github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/states/statemgr/migrate.go (about) 1 package statemgr 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/terraform/states/statefile" 7 ) 8 9 // Migrator is an optional interface implemented by state managers that 10 // are capable of direct migration of state snapshots with their associated 11 // metadata unchanged. 12 // 13 // This interface is used when available by function Migrate. See that 14 // function for more information on how it is used. 15 type Migrator interface { 16 PersistentMeta 17 18 // StateForMigration returns a full statefile representing the latest 19 // snapshot (as would be returned by Reader.State) and the associated 20 // snapshot metadata (as would be returned by 21 // PersistentMeta.StateSnapshotMeta). 22 // 23 // Just as with Reader.State, this must not fail. 24 StateForMigration() *statefile.File 25 26 // WriteStateForMigration accepts a full statefile including associated 27 // snapshot metadata, and atomically updates the stored file (as with 28 // Writer.WriteState) and the metadata. 29 // 30 // If "force" is not set, the manager must call CheckValidImport with 31 // the given file and the current file and complete the update only if 32 // that function returns nil. If force is set this may override such 33 // checks, but some backends do not support forcing and so will act 34 // as if force is always false. 35 WriteStateForMigration(f *statefile.File, force bool) error 36 } 37 38 // Migrate writes the latest transient state snapshot from src into dest, 39 // preserving snapshot metadata (serial and lineage) where possible. 40 // 41 // If both managers implement the optional interface Migrator then it will 42 // be used to copy the snapshot and its associated metadata. Otherwise, 43 // the normal Reader and Writer interfaces will be used instead. 44 // 45 // If the destination manager refuses the new state or fails to write it then 46 // its error is returned directly. 47 // 48 // For state managers that also implement Persistent, it is the caller's 49 // responsibility to persist the newly-written state after a successful result, 50 // just as with calls to Writer.WriteState. 51 // 52 // This function doesn't do any locking of its own, so if the state managers 53 // also implement Locker the caller should hold a lock on both managers 54 // for the duration of this call. 55 func Migrate(dst, src Transient) error { 56 if dstM, ok := dst.(Migrator); ok { 57 if srcM, ok := src.(Migrator); ok { 58 // Full-fidelity migration, them. 59 s := srcM.StateForMigration() 60 return dstM.WriteStateForMigration(s, true) 61 } 62 } 63 64 // Managers to not support full-fidelity migration, so migration will not 65 // preserve serial/lineage. 66 s := src.State() 67 return dst.WriteState(s) 68 } 69 70 // Import loads the given state snapshot into the given manager, preserving 71 // its metadata (serial and lineage) if the target manager supports metadata. 72 // 73 // A state manager must implement the optional interface Migrator to get 74 // access to the full metadata. 75 // 76 // Unless "force" is true, Import will check first that the metadata given 77 // in the file matches the current snapshot metadata for the manager, if the 78 // manager supports metadata. Some managers do not support forcing, so a 79 // write with an unsuitable lineage or serial may still be rejected even if 80 // "force" is set. "force" has no effect for managers that do not support 81 // snapshot metadata. 82 // 83 // For state managers that also implement Persistent, it is the caller's 84 // responsibility to persist the newly-written state after a successful result, 85 // just as with calls to Writer.WriteState. 86 // 87 // This function doesn't do any locking of its own, so if the state manager 88 // also implements Locker the caller should hold a lock on it for the 89 // duration of this call. 90 func Import(f *statefile.File, mgr Transient, force bool) error { 91 if mgrM, ok := mgr.(Migrator); ok { 92 return mgrM.WriteStateForMigration(f, force) 93 } 94 95 // For managers that don't implement Migrator, this is just a normal write 96 // of the state contained in the given file. 97 return mgr.WriteState(f.State) 98 } 99 100 // Export retrieves the latest state snapshot from the given manager, including 101 // its metadata (serial and lineage) where possible. 102 // 103 // A state manager must also implement either Migrator or PersistentMeta 104 // for the metadata to be included. Otherwise, the relevant fields will have 105 // zero value in the returned object. 106 // 107 // For state managers that also implement Persistent, it is the caller's 108 // responsibility to refresh from persistent storage first if needed. 109 // 110 // This function doesn't do any locking of its own, so if the state manager 111 // also implements Locker the caller should hold a lock on it for the 112 // duration of this call. 113 func Export(mgr Reader) *statefile.File { 114 switch mgrT := mgr.(type) { 115 case Migrator: 116 return mgrT.StateForMigration() 117 case PersistentMeta: 118 s := mgr.State() 119 meta := mgrT.StateSnapshotMeta() 120 return statefile.New(s, meta.Lineage, meta.Serial) 121 default: 122 s := mgr.State() 123 return statefile.New(s, "", 0) 124 } 125 } 126 127 // SnapshotMetaRel describes a relationship between two SnapshotMeta values, 128 // returned from the SnapshotMeta.Compare method where the "first" value 129 // is the receiver of that method and the "second" is the given argument. 130 type SnapshotMetaRel rune 131 132 //go:generate go run golang.org/x/tools/cmd/stringer -type=SnapshotMetaRel 133 134 const ( 135 // SnapshotOlder indicates that two snapshots have a common lineage and 136 // that the first has a lower serial value. 137 SnapshotOlder SnapshotMetaRel = '<' 138 139 // SnapshotNewer indicates that two snapshots have a common lineage and 140 // that the first has a higher serial value. 141 SnapshotNewer SnapshotMetaRel = '>' 142 143 // SnapshotEqual indicates that two snapshots have a common lineage and 144 // the same serial value. 145 SnapshotEqual SnapshotMetaRel = '=' 146 147 // SnapshotUnrelated indicates that two snapshots have different lineage 148 // and thus cannot be meaningfully compared. 149 SnapshotUnrelated SnapshotMetaRel = '!' 150 151 // SnapshotLegacy indicates that one or both of the snapshots 152 // does not have a lineage at all, and thus no comparison is possible. 153 SnapshotLegacy SnapshotMetaRel = '?' 154 ) 155 156 // Compare determines the relationship, if any, between the given existing 157 // SnapshotMeta and the potential "new" SnapshotMeta that is the receiver. 158 func (m SnapshotMeta) Compare(existing SnapshotMeta) SnapshotMetaRel { 159 switch { 160 case m.Lineage == "" || existing.Lineage == "": 161 return SnapshotLegacy 162 case m.Lineage != existing.Lineage: 163 return SnapshotUnrelated 164 case m.Serial > existing.Serial: 165 return SnapshotNewer 166 case m.Serial < existing.Serial: 167 return SnapshotOlder 168 default: 169 // both serials are equal, by elimination 170 return SnapshotEqual 171 } 172 } 173 174 // CheckValidImport returns nil if the "new" snapshot can be imported as a 175 // successor of the "existing" snapshot without forcing. 176 // 177 // If not, an error is returned describing why. 178 func CheckValidImport(newFile, existingFile *statefile.File) error { 179 if existingFile == nil || existingFile.State.Empty() { 180 // It's always okay to overwrite an empty state, regardless of 181 // its lineage/serial. 182 return nil 183 } 184 new := SnapshotMeta{ 185 Lineage: newFile.Lineage, 186 Serial: newFile.Serial, 187 } 188 existing := SnapshotMeta{ 189 Lineage: existingFile.Lineage, 190 Serial: existingFile.Serial, 191 } 192 rel := new.Compare(existing) 193 switch rel { 194 case SnapshotNewer: 195 return nil // a newer snapshot is fine 196 case SnapshotLegacy: 197 return nil // anything goes for a legacy state 198 case SnapshotUnrelated: 199 return fmt.Errorf("cannot import state with lineage %q over unrelated state with lineage %q", new.Lineage, existing.Lineage) 200 case SnapshotEqual: 201 if statefile.StatesMarshalEqual(newFile.State, existingFile.State) { 202 // If lineage, serial, and state all match then this is fine. 203 return nil 204 } 205 return fmt.Errorf("cannot overwrite existing state with serial %d with a different state that has the same serial", new.Serial) 206 case SnapshotOlder: 207 return fmt.Errorf("cannot import state with serial %d over newer state with serial %d", new.Serial, existing.Serial) 208 default: 209 // Should never happen, but we'll check to make sure for safety 210 return fmt.Errorf("unsupported state snapshot relationship %s", rel) 211 } 212 }