github.com/ctrox/terraform@v0.11.12-beta1/state/remote/state.go (about) 1 package remote 2 3 import ( 4 "bytes" 5 "log" 6 "sync" 7 8 "github.com/hashicorp/terraform/state" 9 "github.com/hashicorp/terraform/terraform" 10 ) 11 12 // State implements the State interfaces in the state package to handle 13 // reading and writing the remote state. This State on its own does no 14 // local caching so every persist will go to the remote storage and local 15 // writes will go to memory. 16 type State struct { 17 mu sync.Mutex 18 19 Client Client 20 21 state, readState *terraform.State 22 } 23 24 // StateReader impl. 25 func (s *State) State() *terraform.State { 26 s.mu.Lock() 27 defer s.mu.Unlock() 28 29 return s.state.DeepCopy() 30 } 31 32 // StateWriter impl. 33 func (s *State) WriteState(state *terraform.State) error { 34 s.mu.Lock() 35 defer s.mu.Unlock() 36 37 if s.readState != nil && !state.SameLineage(s.readState) { 38 // This can't error here, because we need to be able to overwrite the 39 // state in some cases, like `state push -force` or `workspace new 40 // -state=` 41 log.Printf("[WARN] incompatible state lineage; given %s but want %s", state.Lineage, s.readState.Lineage) 42 } 43 44 // We create a deep copy of the state here, because the caller also has 45 // a reference to the given object and can potentially go on to mutate 46 // it after we return, but we want the snapshot at this point in time. 47 s.state = state.DeepCopy() 48 49 // Force our new state to have the same serial as our read state. We'll 50 // update this if PersistState is called later. (We don't require nor trust 51 // the caller to properly maintain serial for transient state objects since 52 // the rest of Terraform treats state as an openly mutable object.) 53 // 54 // If we have no read state then we assume we're either writing a new 55 // state for the first time or we're migrating a state from elsewhere, 56 // and in both cases we wish to retain the lineage and serial from 57 // the given state. 58 if s.readState != nil { 59 s.state.Serial = s.readState.Serial 60 } 61 62 return nil 63 } 64 65 // StateRefresher impl. 66 func (s *State) RefreshState() error { 67 s.mu.Lock() 68 defer s.mu.Unlock() 69 70 payload, err := s.Client.Get() 71 if err != nil { 72 return err 73 } 74 75 // no remote state is OK 76 if payload == nil { 77 return nil 78 } 79 80 state, err := terraform.ReadState(bytes.NewReader(payload.Data)) 81 if err != nil { 82 return err 83 } 84 85 s.state = state 86 s.readState = s.state.DeepCopy() // our states must be separate instances so we can track changes 87 return nil 88 } 89 90 // StatePersister impl. 91 func (s *State) PersistState() error { 92 s.mu.Lock() 93 defer s.mu.Unlock() 94 95 if !s.state.MarshalEqual(s.readState) { 96 // Our new state does not marshal as byte-for-byte identical to 97 // the old, so we need to increment the serial. 98 // Note that in WriteState we force the serial to match that of 99 // s.readState, if we have a readState. 100 s.state.Serial++ 101 } 102 103 var buf bytes.Buffer 104 if err := terraform.WriteState(s.state, &buf); err != nil { 105 return err 106 } 107 108 err := s.Client.Put(buf.Bytes()) 109 if err != nil { 110 return err 111 } 112 113 // After we've successfully persisted, what we just wrote is our new 114 // reference state until someone calls RefreshState again. 115 s.readState = s.state.DeepCopy() 116 return nil 117 } 118 119 // Lock calls the Client's Lock method if it's implemented. 120 func (s *State) Lock(info *state.LockInfo) (string, error) { 121 s.mu.Lock() 122 defer s.mu.Unlock() 123 124 if c, ok := s.Client.(ClientLocker); ok { 125 return c.Lock(info) 126 } 127 return "", nil 128 } 129 130 // Unlock calls the Client's Unlock method if it's implemented. 131 func (s *State) Unlock(id string) error { 132 s.mu.Lock() 133 defer s.mu.Unlock() 134 135 if c, ok := s.Client.(ClientLocker); ok { 136 return c.Unlock(id) 137 } 138 return nil 139 }