Use -state-out option when applying from a plan

When working from an existing plan, we weren't setting the PathOut field
for a LocalState. This required adding an outPath argument to the
StateFromPlan function to avoid having to introspect the returned
state.State interface to find the appropriate field.

To test we run a plan first and provide the new plan to apply with
`-state-out` set.
This commit is contained in:
James Bardin 2016-06-30 16:50:56 -04:00
parent a84aa5e914
commit 6b5ee73e86
3 changed files with 65 additions and 4 deletions

View File

@ -1326,6 +1326,59 @@ func TestApply_disableBackup(t *testing.T) {
} }
} }
// -state-out wasn't taking effect when a plan is supplied. GH-7264
func TestApply_stateOutWithPlan(t *testing.T) {
p := testProvider()
ui := new(cli.MockUi)
tmpDir := testTempDir(t)
defer os.RemoveAll(tmpDir)
statePath := filepath.Join(tmpDir, "state.tfstate")
planPath := filepath.Join(tmpDir, "terraform.tfplan")
args := []string{
"-state", statePath,
"-out", planPath,
testFixturePath("plan"),
}
// Run plan first to get a current plan file
pc := &PlanCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
}
if code := pc.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
// now run apply with the generated plan
stateOutPath := filepath.Join(tmpDir, "state-new.tfstate")
args = []string{
"-state", statePath,
"-state-out", stateOutPath,
planPath,
}
ac := &ApplyCommand{
Meta: Meta{
ContextOpts: testCtxConfig(p),
Ui: ui,
},
}
if code := ac.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
// now make sure we wrote out our new state
if _, err := os.Stat(stateOutPath); err != nil {
t.Fatalf("missing new state file: %s", err)
}
}
func testHttpServer(t *testing.T) net.Listener { func testHttpServer(t *testing.T) net.Listener {
ln, err := net.Listen("tcp", "127.0.0.1:0") ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil { if err != nil {

View File

@ -109,14 +109,18 @@ func (m *Meta) Context(copts contextOpts) (*terraform.Context, bool, error) {
f.Close() f.Close()
if err == nil { if err == nil {
// Setup our state // Setup our state
state, statePath, err := StateFromPlan(m.statePath, plan) state, statePath, err := StateFromPlan(m.statePath, m.stateOutPath, plan)
if err != nil { if err != nil {
return nil, false, fmt.Errorf("Error loading plan: %s", err) return nil, false, fmt.Errorf("Error loading plan: %s", err)
} }
// Set our state // Set our state
m.state = state m.state = state
m.stateOutPath = statePath
// this is used for printing the saved location later
if m.stateOutPath == "" {
m.stateOutPath = statePath
}
if len(m.variables) > 0 { if len(m.variables) > 0 {
return nil, false, fmt.Errorf( return nil, false, fmt.Errorf(

View File

@ -169,7 +169,8 @@ func State(opts *StateOpts) (*StateResult, error) {
// StateFromPlan gets our state from the plan. // StateFromPlan gets our state from the plan.
func StateFromPlan( func StateFromPlan(
localPath string, plan *terraform.Plan) (state.State, string, error) { localPath, outPath string,
plan *terraform.Plan) (state.State, string, error) {
var result state.State var result state.State
resultPath := localPath resultPath := localPath
if plan != nil && plan.State != nil && if plan != nil && plan.State != nil &&
@ -186,7 +187,10 @@ func StateFromPlan(
} }
if result == nil { if result == nil {
local := &state.LocalState{Path: resultPath} local := &state.LocalState{
Path: resultPath,
PathOut: outPath,
}
local.SetState(plan.State) local.SetState(plan.State)
result = local result = local
} }