88 "github.com/hashicorp/errwrap"
99 "github.com/hashicorp/terraform/state"
1010 "github.com/hashicorp/terraform/state/remote"
11+ "github.com/hashicorp/terraform/terraform"
1112)
1213
1314// State returns the proper state.State implementation to represent the
@@ -24,7 +25,7 @@ func State(localPath string) (state.State, string, error) {
2425 remoteCachePath := filepath .Join (DefaultDataDir , DefaultStateFilename )
2526 if _ , err := os .Stat (remoteCachePath ); err == nil {
2627 // We have a remote state, initialize that.
27- result , err = remoteState (remoteCachePath )
28+ result , err = remoteStateFromPath (remoteCachePath )
2829 if err != nil {
2930 return nil , "" , err
3031 }
@@ -62,60 +63,112 @@ func State(localPath string) (state.State, string, error) {
6263 resultPath = localPath
6364 }
6465
66+ // If we have a result, make sure to back it up
67+ if result != nil {
68+ result = & state.BackupState {
69+ Real : result ,
70+ Path : resultPath + DefaultBackupExtention ,
71+ }
72+ }
73+
6574 // Return whatever state we have
6675 return result , resultPath , nil
6776}
6877
69- func remoteState (path string ) (state.State , error ) {
70- // First create the local state for the path
71- local := & state.LocalState {Path : path }
72- if err := local .RefreshState (); err != nil {
73- return nil , err
78+ // StateFromPlan gets our state from the plan.
79+ func StateFromPlan (
80+ localPath string , plan * terraform.Plan ) (state.State , string , error ) {
81+ var result state.State
82+ resultPath := localPath
83+ if plan != nil && plan .State != nil &&
84+ plan .State .Remote != nil && plan .State .Remote .Type != "" {
85+ var err error
86+
87+ // It looks like we have a remote state in the plan, so
88+ // we have to initialize that.
89+ resultPath = filepath .Join (DefaultDataDir , DefaultStateFilename )
90+ result , err = remoteState (plan .State , resultPath , false )
91+ if err != nil {
92+ return nil , "" , err
93+ }
7494 }
75- localState := local .State ()
7695
96+ if result == nil {
97+ local := & state.LocalState {Path : resultPath }
98+ local .SetState (plan .State )
99+ result = local
100+ }
101+
102+ // If we have a result, make sure to back it up
103+ result = & state.BackupState {
104+ Real : result ,
105+ Path : resultPath + DefaultBackupExtention ,
106+ }
107+
108+ return result , resultPath , nil
109+ }
110+
111+ func remoteState (
112+ local * terraform.State ,
113+ localPath string , refresh bool ) (state.State , error ) {
77114 // If there is no remote settings, it is an error
78- if localState .Remote == nil {
115+ if local .Remote == nil {
79116 return nil , fmt .Errorf ("Remote state cache has no remote info" )
80117 }
81118
82119 // Initialize the remote client based on the local state
83- client , err := remote .NewClient (localState .Remote .Type , localState .Remote .Config )
120+ client , err := remote .NewClient (local .Remote .Type , local .Remote .Config )
84121 if err != nil {
85122 return nil , errwrap .Wrapf (fmt .Sprintf (
86123 "Error initializing remote driver '%s': {{err}}" ,
87- localState .Remote .Type ), err )
124+ local .Remote .Type ), err )
88125 }
89126
90127 // Create the remote client
91128 durable := & remote.State {Client : client }
92129
93130 // Create the cached client
94131 cache := & state.CacheState {
95- Cache : local ,
132+ Cache : & state. LocalState { Path : localPath } ,
96133 Durable : durable ,
97134 }
98135
99- // Refresh the cache
100- if err := cache .RefreshState (); err != nil {
101- return nil , errwrap .Wrapf (
102- "Error reloading remote state: {{err}}" , err )
103- }
104- switch cache .RefreshResult () {
105- case state .CacheRefreshNoop :
106- case state .CacheRefreshInit :
107- case state .CacheRefreshLocalNewer :
108- case state .CacheRefreshUpdateLocal :
109- // Write our local state out to the durable storage to start.
110- if err := cache .WriteState (localState ); err != nil {
111- return nil , errwrap .Wrapf ("Error preparing remote state: {{err}}" , err )
136+ if refresh {
137+ // Refresh the cache
138+ if err := cache .RefreshState (); err != nil {
139+ return nil , errwrap .Wrapf (
140+ "Error reloading remote state: {{err}}" , err )
112141 }
113- if err := cache .PersistState (); err != nil {
114- return nil , errwrap .Wrapf ("Error preparing remote state: {{err}}" , err )
142+ switch cache .RefreshResult () {
143+ case state .CacheRefreshNoop :
144+ case state .CacheRefreshInit :
145+ case state .CacheRefreshLocalNewer :
146+ case state .CacheRefreshUpdateLocal :
147+ // Write our local state out to the durable storage to start.
148+ if err := cache .WriteState (local ); err != nil {
149+ return nil , errwrap .Wrapf (
150+ "Error preparing remote state: {{err}}" , err )
151+ }
152+ if err := cache .PersistState (); err != nil {
153+ return nil , errwrap .Wrapf (
154+ "Error preparing remote state: {{err}}" , err )
155+ }
156+ default :
157+ return nil , errwrap .Wrapf (
158+ "Error initilizing remote state: {{err}}" , err )
115159 }
116- default :
117- return nil , errwrap .Wrapf ("Error initilizing remote state: {{err}}" , err )
118160 }
119161
120162 return cache , nil
121163}
164+
165+ func remoteStateFromPath (path string ) (state.State , error ) {
166+ // First create the local state for the path
167+ local := & state.LocalState {Path : path }
168+ if err := local .RefreshState (); err != nil {
169+ return nil , err
170+ }
171+ localState := local .State ()
172+
173+ return remoteState (localState , path , true )
174+ }
0 commit comments