github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/state/backup.go (about) 1 package state 2 3 import ( 4 "sync" 5 6 "github.com/hashicorp/terraform/states" 7 "github.com/hashicorp/terraform/states/statemgr" 8 ) 9 10 // BackupState wraps a State that backs up the state on the first time that 11 // a WriteState or PersistState is called. 12 // 13 // If Path exists, it will be overwritten. 14 type BackupState struct { 15 mu sync.Mutex 16 Real State 17 Path string 18 19 done bool 20 } 21 22 func (s *BackupState) State() *states.State { 23 return s.Real.State() 24 } 25 26 func (s *BackupState) RefreshState() error { 27 return s.Real.RefreshState() 28 } 29 30 func (s *BackupState) WriteState(state *states.State) error { 31 s.mu.Lock() 32 defer s.mu.Unlock() 33 34 if !s.done { 35 if err := s.backup(); err != nil { 36 return err 37 } 38 } 39 40 return s.Real.WriteState(state) 41 } 42 43 func (s *BackupState) PersistState() error { 44 s.mu.Lock() 45 defer s.mu.Unlock() 46 47 if !s.done { 48 if err := s.backup(); err != nil { 49 return err 50 } 51 } 52 53 return s.Real.PersistState() 54 } 55 56 func (s *BackupState) Lock(info *LockInfo) (string, error) { 57 return s.Real.Lock(info) 58 } 59 60 func (s *BackupState) Unlock(id string) error { 61 return s.Real.Unlock(id) 62 } 63 64 func (s *BackupState) backup() error { 65 state := s.Real.State() 66 if state == nil { 67 if err := s.Real.RefreshState(); err != nil { 68 return err 69 } 70 71 state = s.Real.State() 72 } 73 74 // LocalState.WriteState ensures that a file always exists for locking 75 // purposes, but we don't need a backup or lock if the state is empty, so 76 // skip this with a nil state. 77 if state != nil { 78 ls := statemgr.NewFilesystem(s.Path) 79 if err := ls.WriteState(state); err != nil { 80 return err 81 } 82 } 83 84 s.done = true 85 return nil 86 }