Skip to content

Commit

Permalink
applying a plan should use the refreshed state
Browse files Browse the repository at this point in the history
When applying a plan, a copy of the plan's state is initially persisted
to state storage, which may in turn increment the serial number. The
apply operation stil uses the state stored in the plan, which may have a
lower serial number.

Now we no longer use the plan state when building the apply context, but
still perform a sanity check to ensure that the state be used is
equivalent.
  • Loading branch information
jbardin authored and apparentlymart committed Jun 29, 2017
1 parent 91a055e commit 726ccd8
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 2 deletions.
4 changes: 4 additions & 0 deletions backend/local/backend_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ func (b *Local) context(op *backend.Operation) (*terraform.Context, state.State,
}

// Load our state
// By the time we get here, the backend creation code in "command" took
// care of making s.State() return a state compatible with our plan,
// if any, so we can safely pass this value in both the plan context
// and new context cases below.
opts.State = s.State()

// Build the context
Expand Down
21 changes: 19 additions & 2 deletions terraform/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,30 @@ type Plan struct {
// Context returns a Context with the data encapsulated in this plan.
//
// The following fields in opts are overridden by the plan: Config,
// Diff, State, Variables.
// Diff, Variables.
//
// If State is not provided, it is set from the plan. If it _is_ provided,
// it must be Equal to the state stored in plan, but may have a newer
// serial.
func (p *Plan) Context(opts *ContextOpts) (*Context, error) {
opts.Diff = p.Diff
opts.Module = p.Module
opts.State = p.State
opts.Targets = p.Targets

// If we are given a state in "base" then we'll use it, and otherwise
// we'll fall back on the state stashed in the plan. We do this because
// sometimes the caller has already persisted the plan's state by the
// time we get here, and we don't want to regress to the plan-time state
// serial if so.
if opts.State == nil {
opts.State = p.State
} else if !opts.State.Equal(p.State) {
// Even if we're overriding the state, it should be logically equal
// to what's in plan. The only valid change to have made by the time
// we get here is to have incremented the serial.
return nil, errors.New("plan state and ContextOpts state are not equal")
}

opts.Variables = make(map[string]interface{})
for k, v := range p.Vars {
opts.Variables[k] = v
Expand Down

0 comments on commit 726ccd8

Please sign in to comment.