Skip to content

Commit

Permalink
Merge pull request hashicorp#15566 from hashicorp/jbardin/state-serial
Browse files Browse the repository at this point in the history
Add warning to mismatched plan state
  • Loading branch information
jbardin authored Jul 18, 2017
2 parents dd9a54c + a1727ec commit 9a7ffbf
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 3 deletions.
4 changes: 4 additions & 0 deletions backend/local/backend_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,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
23 changes: 20 additions & 3 deletions terraform/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"io"
"log"
"sync"

"github.com/hashicorp/terraform/config/module"
Expand Down Expand Up @@ -43,7 +44,11 @@ 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) {
var err error
opts, err = p.contextOpts(opts)
Expand All @@ -60,11 +65,23 @@ func (p *Plan) contextOpts(base *ContextOpts) (*ContextOpts, error) {

opts.Diff = p.Diff
opts.Module = p.Module
opts.State = p.State
opts.Targets = p.Targets

opts.ProviderSHA256s = p.ProviderSHA256s

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.
//
// Due to the fact that serialization may change the representation of
// the state, there is little chance that these aren't actually equal.
// Log the error condition for reference, but continue with the state
// we have.
log.Println("[WARNING] Plan state and ContextOpts state are not equal")
}

thisVersion := VersionString()
if p.TerraformVersion != "" && p.TerraformVersion != thisVersion {
return nil, fmt.Errorf(
Expand Down
49 changes: 49 additions & 0 deletions terraform/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,52 @@ func TestReadWritePlan(t *testing.T) {
t.Fatalf("bad:\n\n%s\n\nexpected:\n\n%s", actualStr, expectedStr)
}
}

func TestPlanContextOptsOverrideStateGood(t *testing.T) {
plan := &Plan{
Diff: &Diff{
Modules: []*ModuleDiff{
{
Path: []string{"test"},
},
},
},
Module: module.NewTree("test", nil),
State: &State{
TFVersion: "sigil",
Serial: 1,
},
Vars: map[string]interface{}{"foo": "bar"},
Targets: []string{"baz"},

TerraformVersion: VersionString(),
ProviderSHA256s: map[string][]byte{
"test": []byte("placeholder"),
},
}

base := &ContextOpts{
State: &State{
TFVersion: "sigil",
Serial: 2,
},
}

got, err := plan.contextOpts(base)
if err != nil {
t.Fatalf("error creating context: %s", err)
}

want := &ContextOpts{
Diff: plan.Diff,
Module: plan.Module,
State: base.State,
Variables: plan.Vars,
Targets: plan.Targets,
ProviderSHA256s: plan.ProviderSHA256s,
}

if !reflect.DeepEqual(got, want) {
t.Errorf("wrong result\ngot: %#v\nwant %#v", got, want)
}
}

0 comments on commit 9a7ffbf

Please sign in to comment.