command: Even more fixes for "apply" command tests

This commit is contained in:
Martin Atkins 2018-10-12 18:04:26 -07:00
parent 34a29315f7
commit 741d334ee4
4 changed files with 113 additions and 24 deletions

View File

@ -192,13 +192,14 @@ func (b *Local) contextFromPlanFile(pf *planfile.Reader, opts terraform.ContextO
// has changed since the plan was created. (All of the "real-world" // has changed since the plan was created. (All of the "real-world"
// state manager implementstions support this, but simpler test backends // state manager implementstions support this, but simpler test backends
// may not.) // may not.)
lineageOk := currentStateMeta.Lineage == "" || priorStateFile.Lineage == currentStateMeta.Lineage if currentStateMeta.Lineage != "" && priorStateFile.Lineage != "" {
if priorStateFile.Serial != currentStateMeta.Serial || !lineageOk { if priorStateFile.Serial != currentStateMeta.Serial || priorStateFile.Lineage != currentStateMeta.Lineage {
diags = diags.Append(tfdiags.Sourceless( diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error, tfdiags.Error,
"Saved plan is stale", "Saved plan is stale",
"The given plan file can no longer be applied because the state was changed by another operation after the plan was created.", "The given plan file can no longer be applied because the state was changed by another operation after the plan was created.",
)) ))
}
} }
} }
// The caller already wrote the "current state" here, but we're overriding // The caller already wrote the "current state" here, but we're overriding

View File

@ -331,7 +331,7 @@ func TestApply_error(t *testing.T) {
statePath := testTempFile(t) statePath := testTempFile(t)
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := cli.NewMockUi()
c := &ApplyCommand{ c := &ApplyCommand{
Meta: Meta{ Meta: Meta{
testingOverrides: metaOverridesForProvider(p), testingOverrides: metaOverridesForProvider(p),
@ -367,14 +367,29 @@ func TestApply_error(t *testing.T) {
}, },
}, nil }, nil
} }
p.GetSchemaReturn = &terraform.ProviderSchema{
ResourceTypes: map[string]*configschema.Block{
"test_instance": {
Attributes: map[string]*configschema.Attribute{
"id": {Type: cty.String, Optional: true, Computed: true},
"ami": {Type: cty.String, Optional: true},
"error": {Type: cty.Bool, Optional: true},
},
},
},
}
args := []string{ args := []string{
"-state", statePath, "-state", statePath,
"-auto-approve", "-auto-approve",
testFixturePath("apply-error"), testFixturePath("apply-error"),
} }
if ui.ErrorWriter != nil {
t.Logf("stdout:\n%s", ui.OutputWriter.String())
t.Logf("stderr:\n%s", ui.ErrorWriter.String())
}
if code := c.Run(args); code != 1 { if code := c.Run(args); code != 1 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) t.Fatalf("wrong exit code %d; want 1", code)
} }
if _, err := os.Stat(statePath); err != nil { if _, err := os.Stat(statePath); err != nil {
@ -521,10 +536,10 @@ func TestApply_plan(t *testing.T) {
defaultInputReader = new(bytes.Buffer) defaultInputReader = new(bytes.Buffer)
defaultInputWriter = new(bytes.Buffer) defaultInputWriter = new(bytes.Buffer)
planPath := testPlanFileNoop(t) planPath := applyFixturePlanFile(t)
statePath := testTempFile(t) statePath := testTempFile(t)
p := testProvider() p := applyFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &ApplyCommand{ c := &ApplyCommand{
Meta: Meta{ Meta: Meta{
@ -552,11 +567,11 @@ func TestApply_plan(t *testing.T) {
} }
func TestApply_plan_backup(t *testing.T) { func TestApply_plan_backup(t *testing.T) {
planPath := testPlanFileNoop(t) planPath := applyFixturePlanFile(t)
statePath := testTempFile(t) statePath := testTempFile(t)
backupPath := testTempFile(t) backupPath := testTempFile(t)
p := testProvider() p := applyFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &ApplyCommand{ c := &ApplyCommand{
Meta: Meta{ Meta: Meta{
@ -584,10 +599,10 @@ func TestApply_plan_backup(t *testing.T) {
} }
func TestApply_plan_noBackup(t *testing.T) { func TestApply_plan_noBackup(t *testing.T) {
planPath := testPlanFileNoop(t) planPath := applyFixturePlanFile(t)
statePath := testTempFile(t) statePath := testTempFile(t)
p := testProvider() p := applyFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &ApplyCommand{ c := &ApplyCommand{
Meta: Meta{ Meta: Meta{
@ -640,7 +655,28 @@ func TestApply_plan_remoteState(t *testing.T) {
testStateFileRemote(t, backendState) testStateFileRemote(t, backendState)
_, snap := testModuleWithSnapshot(t, "apply") _, snap := testModuleWithSnapshot(t, "apply")
planPath := testPlanFile(t, snap, state, &plans.Plan{}) backendConfig := cty.ObjectVal(map[string]cty.Value{
"address": cty.StringVal(srv.URL),
"update_method": cty.NullVal(cty.String),
"lock_address": cty.NullVal(cty.String),
"unlock_address": cty.NullVal(cty.String),
"lock_method": cty.NullVal(cty.String),
"unlock_method": cty.NullVal(cty.String),
"username": cty.NullVal(cty.String),
"password": cty.NullVal(cty.String),
"skip_cert_verification": cty.NullVal(cty.Bool),
})
backendConfigRaw, err := plans.NewDynamicValue(backendConfig, backendConfig.Type())
if err != nil {
t.Fatal(err)
}
planPath := testPlanFile(t, snap, state, &plans.Plan{
Backend: plans.Backend{
Type: "http",
Config: backendConfigRaw,
},
Changes: plans.NewChanges(),
})
p := testProvider() p := testProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
@ -677,7 +713,7 @@ func TestApply_planWithVarFile(t *testing.T) {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
planPath := testPlanFileNoop(t) planPath := applyFixturePlanFile(t)
statePath := testTempFile(t) statePath := testTempFile(t)
cwd, err := os.Getwd() cwd, err := os.Getwd()
@ -689,7 +725,7 @@ func TestApply_planWithVarFile(t *testing.T) {
} }
defer os.Chdir(cwd) defer os.Chdir(cwd)
p := testProvider() p := applyFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &ApplyCommand{ c := &ApplyCommand{
Meta: Meta{ Meta: Meta{
@ -717,10 +753,10 @@ func TestApply_planWithVarFile(t *testing.T) {
} }
func TestApply_planVars(t *testing.T) { func TestApply_planVars(t *testing.T) {
planPath := testPlanFileNoop(t) planPath := applyFixturePlanFile(t)
statePath := testTempFile(t) statePath := testTempFile(t)
p := testProvider() p := applyFixtureProvider()
ui := new(cli.MockUi) ui := new(cli.MockUi)
c := &ApplyCommand{ c := &ApplyCommand{
Meta: Meta{ Meta: Meta{
@ -747,8 +783,8 @@ func TestApply_planNoModuleFiles(t *testing.T) {
defer testChdir(t, td)() defer testChdir(t, td)()
p := testProvider() p := applyFixtureProvider()
planFile := testPlanFileNoop(t) planPath := applyFixturePlanFile(t)
apply := &ApplyCommand{ apply := &ApplyCommand{
Meta: Meta{ Meta: Meta{
@ -757,7 +793,7 @@ func TestApply_planNoModuleFiles(t *testing.T) {
}, },
} }
args := []string{ args := []string{
planFile, planPath,
} }
apply.Run(args) apply.Run(args)
if p.ValidateProviderConfigCalled { if p.ValidateProviderConfigCalled {
@ -1592,6 +1628,45 @@ func applyFixtureProvider() *terraform.MockProvider {
return p return p
} }
// applyFixturePlanFile creates a plan file at a temporary location containing
// a single change to create the test_instance.foo that is included in the
// "apply" test fixture, returning the location of that plan file.
func applyFixturePlanFile(t *testing.T) string {
_, snap := testModuleWithSnapshot(t, "apply")
plannedVal := cty.ObjectVal(map[string]cty.Value{
"id": cty.UnknownVal(cty.String),
"ami": cty.StringVal("bar"),
})
priorValRaw, err := plans.NewDynamicValue(cty.NullVal(plannedVal.Type()), plannedVal.Type())
if err != nil {
t.Fatal(err)
}
plannedValRaw, err := plans.NewDynamicValue(plannedVal, plannedVal.Type())
if err != nil {
t.Fatal(err)
}
plan := testPlan(t)
plan.Changes.SyncWrapper().AppendResourceInstanceChange(&plans.ResourceInstanceChangeSrc{
Addr: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_instance",
Name: "foo",
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
ProviderAddr: addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
ChangeSrc: plans.ChangeSrc{
Action: plans.Create,
Before: priorValRaw,
After: plannedValRaw,
},
})
return testPlanFile(
t,
snap,
states.NewState(),
plan,
)
}
const applyVarFile = ` const applyVarFile = `
foo = "bar" foo = "bar"
` `

View File

@ -182,7 +182,7 @@ func testPlanFile(t *testing.T, configSnap *configload.Snapshot, state *states.S
t.Helper() t.Helper()
stateFile := &statefile.File{ stateFile := &statefile.File{
Lineage: "command.testPlanFile", Lineage: "",
State: state, State: state,
TerraformVersion: version.SemVer, TerraformVersion: version.SemVer,
} }

View File

@ -12,6 +12,7 @@ import (
"github.com/hashicorp/hcl2/hcl" "github.com/hashicorp/hcl2/hcl"
"github.com/spf13/afero" "github.com/spf13/afero"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs"
) )
@ -77,6 +78,18 @@ type Snapshot struct {
Modules map[string]*SnapshotModule Modules map[string]*SnapshotModule
} }
// NewEmptySnapshot constructs and returns a snapshot containing only an empty
// root module. This is not useful for anything except placeholders in tests.
func NewEmptySnapshot() *Snapshot {
return &Snapshot{
Modules: map[string]*SnapshotModule{
manifestKey(addrs.RootModule): &SnapshotModule{
Files: map[string][]byte{},
},
},
}
}
// SnapshotModule represents a single module within a Snapshot. // SnapshotModule represents a single module within a Snapshot.
type SnapshotModule struct { type SnapshotModule struct {
// Dir is the path, relative to the root directory given when the // Dir is the path, relative to the root directory given when the