github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/states/remote/state.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package remote 5 6 import ( 7 "bytes" 8 "fmt" 9 "log" 10 "sync" 11 12 uuid "github.com/hashicorp/go-uuid" 13 14 "github.com/terramate-io/tf/backend/local" 15 "github.com/terramate-io/tf/states" 16 "github.com/terramate-io/tf/states/statefile" 17 "github.com/terramate-io/tf/states/statemgr" 18 "github.com/terramate-io/tf/terraform" 19 ) 20 21 // State implements the State interfaces in the state package to handle 22 // reading and writing the remote state. This State on its own does no 23 // local caching so every persist will go to the remote storage and local 24 // writes will go to memory. 25 type State struct { 26 mu sync.Mutex 27 28 Client Client 29 30 // We track two pieces of meta data in addition to the state itself: 31 // 32 // lineage - the state's unique ID 33 // serial - the monotonic counter of "versions" of the state 34 // 35 // Both of these (along with state) have a sister field 36 // that represents the values read in from an existing source. 37 // All three of these values are used to determine if the new 38 // state has changed from an existing state we read in. 39 lineage, readLineage string 40 serial, readSerial uint64 41 state, readState *states.State 42 disableLocks bool 43 44 // If this is set then the state manager will decline to store intermediate 45 // state snapshots created while a Terraform Core apply operation is in 46 // progress. Otherwise (by default) it will accept persistent snapshots 47 // using the default rules defined in the local backend. 48 DisableIntermediateSnapshots bool 49 } 50 51 var _ statemgr.Full = (*State)(nil) 52 var _ statemgr.Migrator = (*State)(nil) 53 var _ local.IntermediateStateConditionalPersister = (*State)(nil) 54 55 // statemgr.Reader impl. 56 func (s *State) State() *states.State { 57 s.mu.Lock() 58 defer s.mu.Unlock() 59 60 return s.state.DeepCopy() 61 } 62 63 func (s *State) GetRootOutputValues() (map[string]*states.OutputValue, error) { 64 if err := s.RefreshState(); err != nil { 65 return nil, fmt.Errorf("Failed to load state: %s", err) 66 } 67 68 state := s.State() 69 if state == nil { 70 state = states.NewState() 71 } 72 73 return state.RootModule().OutputValues, nil 74 } 75 76 // StateForMigration is part of our implementation of statemgr.Migrator. 77 func (s *State) StateForMigration() *statefile.File { 78 s.mu.Lock() 79 defer s.mu.Unlock() 80 81 return statefile.New(s.state.DeepCopy(), s.lineage, s.serial) 82 } 83 84 // statemgr.Writer impl. 85 func (s *State) WriteState(state *states.State) error { 86 s.mu.Lock() 87 defer s.mu.Unlock() 88 89 // We create a deep copy of the state here, because the caller also has 90 // a reference to the given object and can potentially go on to mutate 91 // it after we return, but we want the snapshot at this point in time. 92 s.state = state.DeepCopy() 93 94 return nil 95 } 96 97 // WriteStateForMigration is part of our implementation of statemgr.Migrator. 98 func (s *State) WriteStateForMigration(f *statefile.File, force bool) error { 99 s.mu.Lock() 100 defer s.mu.Unlock() 101 102 if !force { 103 checkFile := statefile.New(s.state, s.lineage, s.serial) 104 if err := statemgr.CheckValidImport(f, checkFile); err != nil { 105 return err 106 } 107 } 108 109 // The remote backend needs to pass the `force` flag through to its client. 110 // For backends that support such operations, inform the client 111 // that a force push has been requested 112 c, isForcePusher := s.Client.(ClientForcePusher) 113 if force && isForcePusher { 114 c.EnableForcePush() 115 } 116 117 // We create a deep copy of the state here, because the caller also has 118 // a reference to the given object and can potentially go on to mutate 119 // it after we return, but we want the snapshot at this point in time. 120 s.state = f.State.DeepCopy() 121 s.lineage = f.Lineage 122 s.serial = f.Serial 123 124 return nil 125 } 126 127 // statemgr.Refresher impl. 128 func (s *State) RefreshState() error { 129 s.mu.Lock() 130 defer s.mu.Unlock() 131 return s.refreshState() 132 } 133 134 // refreshState is the main implementation of RefreshState, but split out so 135 // that we can make internal calls to it from methods that are already holding 136 // the s.mu lock. 137 func (s *State) refreshState() error { 138 payload, err := s.Client.Get() 139 if err != nil { 140 return err 141 } 142 143 // no remote state is OK 144 if payload == nil { 145 s.readState = nil 146 s.lineage = "" 147 s.serial = 0 148 return nil 149 } 150 151 stateFile, err := statefile.Read(bytes.NewReader(payload.Data)) 152 if err != nil { 153 return err 154 } 155 156 s.lineage = stateFile.Lineage 157 s.serial = stateFile.Serial 158 s.state = stateFile.State 159 160 // Properties from the remote must be separate so we can 161 // track changes as lineage, serial and/or state are mutated 162 s.readLineage = stateFile.Lineage 163 s.readSerial = stateFile.Serial 164 s.readState = s.state.DeepCopy() 165 return nil 166 } 167 168 // statemgr.Persister impl. 169 func (s *State) PersistState(schemas *terraform.Schemas) error { 170 s.mu.Lock() 171 defer s.mu.Unlock() 172 173 log.Printf("[DEBUG] states/remote: state read serial is: %d; serial is: %d", s.readSerial, s.serial) 174 log.Printf("[DEBUG] states/remote: state read lineage is: %s; lineage is: %s", s.readLineage, s.lineage) 175 176 if s.readState != nil { 177 lineageUnchanged := s.readLineage != "" && s.lineage == s.readLineage 178 serialUnchanged := s.readSerial != 0 && s.serial == s.readSerial 179 stateUnchanged := statefile.StatesMarshalEqual(s.state, s.readState) 180 if stateUnchanged && lineageUnchanged && serialUnchanged { 181 // If the state, lineage or serial haven't changed at all then we have nothing to do. 182 return nil 183 } 184 s.serial++ 185 } else { 186 // We might be writing a new state altogether, but before we do that 187 // we'll check to make sure there isn't already a snapshot present 188 // that we ought to be updating. 189 err := s.refreshState() 190 if err != nil { 191 return fmt.Errorf("failed checking for existing remote state: %s", err) 192 } 193 log.Printf("[DEBUG] states/remote: after refresh, state read serial is: %d; serial is: %d", s.readSerial, s.serial) 194 log.Printf("[DEBUG] states/remote: after refresh, state read lineage is: %s; lineage is: %s", s.readLineage, s.lineage) 195 if s.lineage == "" { // indicates that no state snapshot is present yet 196 lineage, err := uuid.GenerateUUID() 197 if err != nil { 198 return fmt.Errorf("failed to generate initial lineage: %v", err) 199 } 200 s.lineage = lineage 201 s.serial++ 202 } 203 } 204 205 f := statefile.New(s.state, s.lineage, s.serial) 206 207 var buf bytes.Buffer 208 err := statefile.Write(f, &buf) 209 if err != nil { 210 return err 211 } 212 213 err = s.Client.Put(buf.Bytes()) 214 if err != nil { 215 return err 216 } 217 218 // After we've successfully persisted, what we just wrote is our new 219 // reference state until someone calls RefreshState again. 220 // We've potentially overwritten (via force) the state, lineage 221 // and / or serial (and serial was incremented) so we copy over all 222 // three fields so everything matches the new state and a subsequent 223 // operation would correctly detect no changes to the lineage, serial or state. 224 s.readState = s.state.DeepCopy() 225 s.readLineage = s.lineage 226 s.readSerial = s.serial 227 return nil 228 } 229 230 // ShouldPersistIntermediateState implements local.IntermediateStateConditionalPersister 231 func (s *State) ShouldPersistIntermediateState(info *local.IntermediateStatePersistInfo) bool { 232 if s.DisableIntermediateSnapshots { 233 return false 234 } 235 return local.DefaultIntermediateStatePersistRule(info) 236 } 237 238 // Lock calls the Client's Lock method if it's implemented. 239 func (s *State) Lock(info *statemgr.LockInfo) (string, error) { 240 s.mu.Lock() 241 defer s.mu.Unlock() 242 243 if s.disableLocks { 244 return "", nil 245 } 246 247 if c, ok := s.Client.(ClientLocker); ok { 248 return c.Lock(info) 249 } 250 return "", nil 251 } 252 253 // Unlock calls the Client's Unlock method if it's implemented. 254 func (s *State) Unlock(id string) error { 255 s.mu.Lock() 256 defer s.mu.Unlock() 257 258 if s.disableLocks { 259 return nil 260 } 261 262 if c, ok := s.Client.(ClientLocker); ok { 263 return c.Unlock(id) 264 } 265 return nil 266 } 267 268 // DisableLocks turns the Lock and Unlock methods into no-ops. This is intended 269 // to be called during initialization of a state manager and should not be 270 // called after any of the statemgr.Full interface methods have been called. 271 func (s *State) DisableLocks() { 272 s.disableLocks = true 273 } 274 275 // StateSnapshotMeta returns the metadata from the most recently persisted 276 // or refreshed persistent state snapshot. 277 // 278 // This is an implementation of statemgr.PersistentMeta. 279 func (s *State) StateSnapshotMeta() statemgr.SnapshotMeta { 280 return statemgr.SnapshotMeta{ 281 Lineage: s.lineage, 282 Serial: s.serial, 283 } 284 }