command: Use statemgr.Import and statemgr.Export for state push and pull
We previously hacked around the import/export functionality being missing in the statemgr layer after refactoring, but now it's been reintroduced to fix functionality elsewhere we should use the centralized Import and Export functions to ensure consistent behavior. In particular, this pushes the logic for checking lineage and serial during push down into the state manager itself, which is better because all other details about lineage and serial are managed within the state managers.
This commit is contained in:
parent
985b414dca
commit
22c84c71a4
|
@ -35,7 +35,7 @@ func (c *StatePullCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the state
|
// Get the state manager for the current workspace
|
||||||
env := c.Workspace()
|
env := c.Workspace()
|
||||||
stateMgr, err := b.StateMgr(env)
|
stateMgr, err := b.StateMgr(env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -47,24 +47,20 @@ func (c *StatePullCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
state := stateMgr.State()
|
// Get a statefile object representing the latest snapshot
|
||||||
if state == nil {
|
stateFile := statemgr.Export(stateMgr)
|
||||||
// Output on "error" so it shows up on stderr
|
|
||||||
c.Ui.Error("Empty state (no state)")
|
if stateFile != nil { // we produce no output if the statefile is nil
|
||||||
return 0
|
var buf bytes.Buffer
|
||||||
|
err = statefile.Write(stateFile, &buf)
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf("Failed to write state: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Ui.Output(buf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the state file.
|
|
||||||
stateFile := statemgr.StateFile(stateMgr, state)
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err = statefile.Write(stateFile, &buf)
|
|
||||||
if err != nil {
|
|
||||||
c.Ui.Error(fmt.Sprintf("Failed to write state: %s", err))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Ui.Output(buf.String())
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ func TestStatePull_noState(t *testing.T) {
|
||||||
defer testFixCwd(t, tmp, cwd)
|
defer testFixCwd(t, tmp, cwd)
|
||||||
|
|
||||||
p := testProvider()
|
p := testProvider()
|
||||||
ui := new(cli.MockUi)
|
ui := cli.NewMockUi()
|
||||||
c := &StatePullCommand{
|
c := &StatePullCommand{
|
||||||
Meta: Meta{
|
Meta: Meta{
|
||||||
testingOverrides: metaOverridesForProvider(p),
|
testingOverrides: metaOverridesForProvider(p),
|
||||||
|
|
|
@ -70,7 +70,7 @@ func (c *StatePushCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the state
|
// Get the state manager for the currently-selected workspace
|
||||||
env := c.Workspace()
|
env := c.Workspace()
|
||||||
stateMgr, err := b.StateMgr(env)
|
stateMgr, err := b.StateMgr(env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -81,23 +81,17 @@ func (c *StatePushCommand) Run(args []string) int {
|
||||||
c.Ui.Error(fmt.Sprintf("Failed to refresh destination state: %s", err))
|
c.Ui.Error(fmt.Sprintf("Failed to refresh destination state: %s", err))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
dstState := stateMgr.State()
|
|
||||||
|
|
||||||
// If we're not forcing, then perform safety checks
|
if srcStateFile == nil {
|
||||||
if !flagForce && !dstState.Empty() {
|
// We'll push a new empty state instead
|
||||||
dstStateFile := statemgr.StateFile(stateMgr, dstState)
|
srcStateFile = statemgr.NewStateFile()
|
||||||
|
|
||||||
if dstStateFile.Lineage != srcStateFile.Lineage {
|
|
||||||
c.Ui.Error(strings.TrimSpace(errStatePushLineage))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
if dstStateFile.Serial > srcStateFile.Serial {
|
|
||||||
c.Ui.Error(strings.TrimSpace(errStatePushSerialNewer))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overwrite it
|
// Import it, forcing through the lineage/serial if requested and possible.
|
||||||
|
if err := statemgr.Import(srcStateFile, stateMgr, flagForce); err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf("Failed to write state: %s", err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
if err := stateMgr.WriteState(srcStateFile.State); err != nil {
|
if err := stateMgr.WriteState(srcStateFile.State); err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf("Failed to write state: %s", err))
|
c.Ui.Error(fmt.Sprintf("Failed to write state: %s", err))
|
||||||
return 1
|
return 1
|
||||||
|
|
|
@ -1,5 +1,17 @@
|
||||||
{
|
{
|
||||||
"version": 3,
|
"version": 3,
|
||||||
"serial": 1,
|
"serial": 1,
|
||||||
"lineage": "mismatch"
|
"lineage": "mismatch",
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"path": ["root"],
|
||||||
|
"outputs": {
|
||||||
|
"foo": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "bar"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resources": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,17 @@
|
||||||
{
|
{
|
||||||
"version": 3,
|
"version": 3,
|
||||||
"serial": 2,
|
"serial": 2,
|
||||||
"lineage": "hello"
|
"lineage": "hello",
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"path": ["root"],
|
||||||
|
"outputs": {
|
||||||
|
"foo": {
|
||||||
|
"type": "string",
|
||||||
|
"value": "baz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resources": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,28 +15,10 @@ func NewStateFile() *statefile.File {
|
||||||
return &statefile.File{
|
return &statefile.File{
|
||||||
Lineage: NewLineage(),
|
Lineage: NewLineage(),
|
||||||
TerraformVersion: version.SemVer,
|
TerraformVersion: version.SemVer,
|
||||||
|
State: states.NewState(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateFile is a special helper to obtain a statefile representation
|
|
||||||
// of a state snapshot that can be written later by a call
|
|
||||||
func StateFile(mgr Storage, state *states.State) *statefile.File {
|
|
||||||
ret := &statefile.File{
|
|
||||||
State: state.DeepCopy(),
|
|
||||||
TerraformVersion: version.SemVer,
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the given manager uses snapshot metadata then we'll save that
|
|
||||||
// in our file so we can check it again during WritePlannedStateUpdate.
|
|
||||||
if mr, ok := mgr.(PersistentMeta); ok {
|
|
||||||
m := mr.StateSnapshotMeta()
|
|
||||||
ret.Lineage = m.Lineage
|
|
||||||
ret.Serial = m.Serial
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// RefreshAndRead refreshes the persistent snapshot in the given state manager
|
// RefreshAndRead refreshes the persistent snapshot in the given state manager
|
||||||
// and then returns it.
|
// and then returns it.
|
||||||
//
|
//
|
||||||
|
|
Loading…
Reference in New Issue