mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 01:41:48 -06:00
ebcf7455eb
* Rename module name from "github.com/hashicorp/terraform" to "github.com/placeholderplaceholderplaceholder/opentf". Signed-off-by: Jakub Martin <kubam@spacelift.io> * Gofmt. Signed-off-by: Jakub Martin <kubam@spacelift.io> * Regenerate protobuf. Signed-off-by: Jakub Martin <kubam@spacelift.io> * Fix comments. Signed-off-by: Jakub Martin <kubam@spacelift.io> * Undo issue and pull request link changes. Signed-off-by: Jakub Martin <kubam@spacelift.io> * Undo comment changes. Signed-off-by: Jakub Martin <kubam@spacelift.io> * Fix comment. Signed-off-by: Jakub Martin <kubam@spacelift.io> * Undo some link changes. Signed-off-by: Jakub Martin <kubam@spacelift.io> * make generate && make protobuf Signed-off-by: Jakub Martin <kubam@spacelift.io> --------- Signed-off-by: Jakub Martin <kubam@spacelift.io>
676 lines
17 KiB
Go
676 lines
17 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package command
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/davecgh/go-spew/spew"
|
|
"github.com/mitchellh/cli"
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
"github.com/placeholderplaceholderplaceholder/opentf/internal/addrs"
|
|
"github.com/placeholderplaceholderplaceholder/opentf/internal/configs/configschema"
|
|
"github.com/placeholderplaceholderplaceholder/opentf/internal/providers"
|
|
"github.com/placeholderplaceholderplaceholder/opentf/internal/states"
|
|
"github.com/placeholderplaceholderplaceholder/opentf/internal/states/statefile"
|
|
)
|
|
|
|
func TestApply_destroy(t *testing.T) {
|
|
// Create a temporary working directory that is empty
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath("apply"), td)
|
|
defer testChdir(t, td)()
|
|
|
|
originalState := states.BuildState(func(s *states.SyncState) {
|
|
s.SetResourceInstanceCurrent(
|
|
addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test_instance",
|
|
Name: "foo",
|
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
|
&states.ResourceInstanceObjectSrc{
|
|
AttrsJSON: []byte(`{"id":"bar"}`),
|
|
Status: states.ObjectReady,
|
|
},
|
|
addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
)
|
|
})
|
|
statePath := testStateFile(t, originalState)
|
|
|
|
p := testProvider()
|
|
p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
|
|
ResourceTypes: map[string]providers.Schema{
|
|
"test_instance": {
|
|
Block: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"id": {Type: cty.String, Computed: true},
|
|
"ami": {Type: cty.String, Optional: true},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
view, done := testView(t)
|
|
c := &ApplyCommand{
|
|
Destroy: true,
|
|
Meta: Meta{
|
|
testingOverrides: metaOverridesForProvider(p),
|
|
View: view,
|
|
},
|
|
}
|
|
|
|
// Run the apply command pointing to our existing state
|
|
args := []string{
|
|
"-auto-approve",
|
|
"-state", statePath,
|
|
}
|
|
code := c.Run(args)
|
|
output := done(t)
|
|
if code != 0 {
|
|
t.Log(output.Stdout())
|
|
t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
|
|
}
|
|
|
|
// Verify a new state exists
|
|
if _, err := os.Stat(statePath); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
f, err := os.Open(statePath)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
stateFile, err := statefile.Read(f)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
if stateFile.State == nil {
|
|
t.Fatal("state should not be nil")
|
|
}
|
|
|
|
actualStr := strings.TrimSpace(stateFile.State.String())
|
|
expectedStr := strings.TrimSpace(testApplyDestroyStr)
|
|
if actualStr != expectedStr {
|
|
t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
|
|
}
|
|
|
|
// Should have a backup file
|
|
f, err = os.Open(statePath + DefaultBackupExtension)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
backupStateFile, err := statefile.Read(f)
|
|
f.Close()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actualStr = strings.TrimSpace(backupStateFile.State.String())
|
|
expectedStr = strings.TrimSpace(originalState.String())
|
|
if actualStr != expectedStr {
|
|
t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
|
|
}
|
|
}
|
|
|
|
func TestApply_destroyApproveNo(t *testing.T) {
|
|
// Create a temporary working directory that is empty
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath("apply"), td)
|
|
defer testChdir(t, td)()
|
|
|
|
// Create some existing state
|
|
originalState := states.BuildState(func(s *states.SyncState) {
|
|
s.SetResourceInstanceCurrent(
|
|
addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test_instance",
|
|
Name: "foo",
|
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
|
&states.ResourceInstanceObjectSrc{
|
|
AttrsJSON: []byte(`{"id":"bar"}`),
|
|
Status: states.ObjectReady,
|
|
},
|
|
addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
)
|
|
})
|
|
statePath := testStateFile(t, originalState)
|
|
|
|
p := applyFixtureProvider()
|
|
|
|
defer testInputMap(t, map[string]string{
|
|
"approve": "no",
|
|
})()
|
|
|
|
// Do not use the NewMockUi initializer here, as we want to delay
|
|
// the call to init until after setting up the input mocks
|
|
ui := new(cli.MockUi)
|
|
view, done := testView(t)
|
|
c := &ApplyCommand{
|
|
Destroy: true,
|
|
Meta: Meta{
|
|
testingOverrides: metaOverridesForProvider(p),
|
|
Ui: ui,
|
|
View: view,
|
|
},
|
|
}
|
|
|
|
args := []string{
|
|
"-state", statePath,
|
|
}
|
|
code := c.Run(args)
|
|
output := done(t)
|
|
if code != 1 {
|
|
t.Fatalf("bad: %d\n\n%s", code, output.Stdout())
|
|
}
|
|
if got, want := output.Stdout(), "Destroy cancelled"; !strings.Contains(got, want) {
|
|
t.Fatalf("expected output to include %q, but was:\n%s", want, got)
|
|
}
|
|
|
|
state := testStateRead(t, statePath)
|
|
if state == nil {
|
|
t.Fatal("state should not be nil")
|
|
}
|
|
actualStr := strings.TrimSpace(state.String())
|
|
expectedStr := strings.TrimSpace(originalState.String())
|
|
if actualStr != expectedStr {
|
|
t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
|
|
}
|
|
}
|
|
|
|
func TestApply_destroyApproveYes(t *testing.T) {
|
|
// Create a temporary working directory that is empty
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath("apply"), td)
|
|
defer testChdir(t, td)()
|
|
|
|
// Create some existing state
|
|
originalState := states.BuildState(func(s *states.SyncState) {
|
|
s.SetResourceInstanceCurrent(
|
|
addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test_instance",
|
|
Name: "foo",
|
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
|
&states.ResourceInstanceObjectSrc{
|
|
AttrsJSON: []byte(`{"id":"bar"}`),
|
|
Status: states.ObjectReady,
|
|
},
|
|
addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
)
|
|
})
|
|
statePath := testStateFile(t, originalState)
|
|
|
|
p := applyFixtureProvider()
|
|
|
|
defer testInputMap(t, map[string]string{
|
|
"approve": "yes",
|
|
})()
|
|
|
|
// Do not use the NewMockUi initializer here, as we want to delay
|
|
// the call to init until after setting up the input mocks
|
|
ui := new(cli.MockUi)
|
|
view, done := testView(t)
|
|
c := &ApplyCommand{
|
|
Destroy: true,
|
|
Meta: Meta{
|
|
testingOverrides: metaOverridesForProvider(p),
|
|
Ui: ui,
|
|
View: view,
|
|
},
|
|
}
|
|
|
|
args := []string{
|
|
"-state", statePath,
|
|
}
|
|
code := c.Run(args)
|
|
output := done(t)
|
|
if code != 0 {
|
|
t.Log(output.Stdout())
|
|
t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
|
|
}
|
|
|
|
if _, err := os.Stat(statePath); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state := testStateRead(t, statePath)
|
|
if state == nil {
|
|
t.Fatal("state should not be nil")
|
|
}
|
|
|
|
actualStr := strings.TrimSpace(state.String())
|
|
expectedStr := strings.TrimSpace(testApplyDestroyStr)
|
|
if actualStr != expectedStr {
|
|
t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
|
|
}
|
|
}
|
|
|
|
func TestApply_destroyLockedState(t *testing.T) {
|
|
// Create a temporary working directory that is empty
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath("apply"), td)
|
|
defer testChdir(t, td)()
|
|
|
|
originalState := states.BuildState(func(s *states.SyncState) {
|
|
s.SetResourceInstanceCurrent(
|
|
addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test_instance",
|
|
Name: "foo",
|
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
|
&states.ResourceInstanceObjectSrc{
|
|
AttrsJSON: []byte(`{"id":"bar"}`),
|
|
Status: states.ObjectReady,
|
|
},
|
|
addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
)
|
|
})
|
|
statePath := testStateFile(t, originalState)
|
|
|
|
unlock, err := testLockState(t, testDataDir, statePath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer unlock()
|
|
|
|
p := testProvider()
|
|
view, done := testView(t)
|
|
c := &ApplyCommand{
|
|
Destroy: true,
|
|
Meta: Meta{
|
|
testingOverrides: metaOverridesForProvider(p),
|
|
View: view,
|
|
},
|
|
}
|
|
|
|
// Run the apply command pointing to our existing state
|
|
args := []string{
|
|
"-auto-approve",
|
|
"-state", statePath,
|
|
}
|
|
|
|
code := c.Run(args)
|
|
output := done(t)
|
|
if code == 0 {
|
|
t.Fatalf("bad: %d\n\n%s", code, output.Stdout())
|
|
}
|
|
|
|
if !strings.Contains(output.Stderr(), "lock") {
|
|
t.Fatal("command output does not look like a lock error:", output.Stderr())
|
|
}
|
|
}
|
|
|
|
func TestApply_destroyPlan(t *testing.T) {
|
|
// Create a temporary working directory that is empty
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath("apply"), td)
|
|
defer testChdir(t, td)()
|
|
|
|
planPath := testPlanFileNoop(t)
|
|
|
|
p := testProvider()
|
|
view, done := testView(t)
|
|
c := &ApplyCommand{
|
|
Destroy: true,
|
|
Meta: Meta{
|
|
testingOverrides: metaOverridesForProvider(p),
|
|
View: view,
|
|
},
|
|
}
|
|
|
|
// Run the apply command pointing to our existing state
|
|
args := []string{
|
|
planPath,
|
|
}
|
|
code := c.Run(args)
|
|
output := done(t)
|
|
if code != 1 {
|
|
t.Fatalf("bad: %d\n\n%s", code, output.Stdout())
|
|
}
|
|
if !strings.Contains(output.Stderr(), "plan file") {
|
|
t.Fatal("expected command output to refer to plan file, but got:", output.Stderr())
|
|
}
|
|
}
|
|
|
|
func TestApply_destroyPath(t *testing.T) {
|
|
// Create a temporary working directory that is empty
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath("apply"), td)
|
|
defer testChdir(t, td)()
|
|
|
|
p := applyFixtureProvider()
|
|
|
|
view, done := testView(t)
|
|
c := &ApplyCommand{
|
|
Destroy: true,
|
|
Meta: Meta{
|
|
testingOverrides: metaOverridesForProvider(p),
|
|
View: view,
|
|
},
|
|
}
|
|
|
|
args := []string{
|
|
"-auto-approve",
|
|
testFixturePath("apply"),
|
|
}
|
|
code := c.Run(args)
|
|
output := done(t)
|
|
if code != 1 {
|
|
t.Fatalf("bad: %d\n\n%s", code, output.Stdout())
|
|
}
|
|
if !strings.Contains(output.Stderr(), "-chdir") {
|
|
t.Fatal("expected command output to refer to -chdir flag, but got:", output.Stderr())
|
|
}
|
|
}
|
|
|
|
// Config with multiple resources with dependencies, targeting destroy of a
|
|
// root node, expecting all other resources to be destroyed due to
|
|
// dependencies.
|
|
func TestApply_destroyTargetedDependencies(t *testing.T) {
|
|
// Create a temporary working directory that is empty
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath("apply-destroy-targeted"), td)
|
|
defer testChdir(t, td)()
|
|
|
|
originalState := states.BuildState(func(s *states.SyncState) {
|
|
s.SetResourceInstanceCurrent(
|
|
addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test_instance",
|
|
Name: "foo",
|
|
}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
|
|
&states.ResourceInstanceObjectSrc{
|
|
AttrsJSON: []byte(`{"id":"i-ab123"}`),
|
|
Status: states.ObjectReady,
|
|
},
|
|
addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
)
|
|
s.SetResourceInstanceCurrent(
|
|
addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test_load_balancer",
|
|
Name: "foo",
|
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
|
&states.ResourceInstanceObjectSrc{
|
|
AttrsJSON: []byte(`{"id":"i-abc123"}`),
|
|
Dependencies: []addrs.ConfigResource{mustResourceAddr("test_instance.foo")},
|
|
Status: states.ObjectReady,
|
|
},
|
|
addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
)
|
|
})
|
|
statePath := testStateFile(t, originalState)
|
|
|
|
p := testProvider()
|
|
p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
|
|
ResourceTypes: map[string]providers.Schema{
|
|
"test_instance": {
|
|
Block: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"id": {Type: cty.String, Computed: true},
|
|
},
|
|
},
|
|
},
|
|
"test_load_balancer": {
|
|
Block: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"id": {Type: cty.String, Computed: true},
|
|
"instances": {Type: cty.List(cty.String), Optional: true},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
|
|
return providers.PlanResourceChangeResponse{
|
|
PlannedState: req.ProposedNewState,
|
|
}
|
|
}
|
|
|
|
view, done := testView(t)
|
|
c := &ApplyCommand{
|
|
Destroy: true,
|
|
Meta: Meta{
|
|
testingOverrides: metaOverridesForProvider(p),
|
|
View: view,
|
|
},
|
|
}
|
|
|
|
// Run the apply command pointing to our existing state
|
|
args := []string{
|
|
"-auto-approve",
|
|
"-target", "test_instance.foo",
|
|
"-state", statePath,
|
|
}
|
|
code := c.Run(args)
|
|
output := done(t)
|
|
if code != 0 {
|
|
t.Log(output.Stdout())
|
|
t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
|
|
}
|
|
|
|
// Verify a new state exists
|
|
if _, err := os.Stat(statePath); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
f, err := os.Open(statePath)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
stateFile, err := statefile.Read(f)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
if stateFile == nil || stateFile.State == nil {
|
|
t.Fatal("state should not be nil")
|
|
}
|
|
|
|
spew.Config.DisableMethods = true
|
|
if !stateFile.State.Empty() {
|
|
t.Fatalf("unexpected final state\ngot: %s\nwant: empty state", spew.Sdump(stateFile.State))
|
|
}
|
|
|
|
// Should have a backup file
|
|
f, err = os.Open(statePath + DefaultBackupExtension)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
backupStateFile, err := statefile.Read(f)
|
|
f.Close()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actualStr := strings.TrimSpace(backupStateFile.State.String())
|
|
expectedStr := strings.TrimSpace(originalState.String())
|
|
if actualStr != expectedStr {
|
|
t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\nb%s", actualStr, expectedStr)
|
|
}
|
|
}
|
|
|
|
// Config with multiple resources with dependencies, targeting destroy of a
|
|
// leaf node, expecting the other resources to remain.
|
|
func TestApply_destroyTargeted(t *testing.T) {
|
|
// Create a temporary working directory that is empty
|
|
td := t.TempDir()
|
|
testCopyDir(t, testFixturePath("apply-destroy-targeted"), td)
|
|
defer testChdir(t, td)()
|
|
|
|
originalState := states.BuildState(func(s *states.SyncState) {
|
|
s.SetResourceInstanceCurrent(
|
|
addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test_instance",
|
|
Name: "foo",
|
|
}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
|
|
&states.ResourceInstanceObjectSrc{
|
|
AttrsJSON: []byte(`{"id":"i-ab123"}`),
|
|
Status: states.ObjectReady,
|
|
},
|
|
addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
)
|
|
s.SetResourceInstanceCurrent(
|
|
addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test_load_balancer",
|
|
Name: "foo",
|
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
|
&states.ResourceInstanceObjectSrc{
|
|
AttrsJSON: []byte(`{"id":"i-abc123"}`),
|
|
Dependencies: []addrs.ConfigResource{mustResourceAddr("test_instance.foo")},
|
|
Status: states.ObjectReady,
|
|
},
|
|
addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
)
|
|
})
|
|
wantState := states.BuildState(func(s *states.SyncState) {
|
|
s.SetResourceInstanceCurrent(
|
|
addrs.Resource{
|
|
Mode: addrs.ManagedResourceMode,
|
|
Type: "test_instance",
|
|
Name: "foo",
|
|
}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
|
|
&states.ResourceInstanceObjectSrc{
|
|
AttrsJSON: []byte(`{"id":"i-ab123"}`),
|
|
Status: states.ObjectReady,
|
|
},
|
|
addrs.AbsProviderConfig{
|
|
Provider: addrs.NewDefaultProvider("test"),
|
|
Module: addrs.RootModule,
|
|
},
|
|
)
|
|
})
|
|
statePath := testStateFile(t, originalState)
|
|
|
|
p := testProvider()
|
|
p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
|
|
ResourceTypes: map[string]providers.Schema{
|
|
"test_instance": {
|
|
Block: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"id": {Type: cty.String, Computed: true},
|
|
},
|
|
},
|
|
},
|
|
"test_load_balancer": {
|
|
Block: &configschema.Block{
|
|
Attributes: map[string]*configschema.Attribute{
|
|
"id": {Type: cty.String, Computed: true},
|
|
"instances": {Type: cty.List(cty.String), Optional: true},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
|
|
return providers.PlanResourceChangeResponse{
|
|
PlannedState: req.ProposedNewState,
|
|
}
|
|
}
|
|
|
|
view, done := testView(t)
|
|
c := &ApplyCommand{
|
|
Destroy: true,
|
|
Meta: Meta{
|
|
testingOverrides: metaOverridesForProvider(p),
|
|
View: view,
|
|
},
|
|
}
|
|
|
|
// Run the apply command pointing to our existing state
|
|
args := []string{
|
|
"-auto-approve",
|
|
"-target", "test_load_balancer.foo",
|
|
"-state", statePath,
|
|
}
|
|
code := c.Run(args)
|
|
output := done(t)
|
|
if code != 0 {
|
|
t.Log(output.Stdout())
|
|
t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
|
|
}
|
|
|
|
// Verify a new state exists
|
|
if _, err := os.Stat(statePath); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
f, err := os.Open(statePath)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
stateFile, err := statefile.Read(f)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
if stateFile == nil || stateFile.State == nil {
|
|
t.Fatal("state should not be nil")
|
|
}
|
|
|
|
actualStr := strings.TrimSpace(stateFile.State.String())
|
|
expectedStr := strings.TrimSpace(wantState.String())
|
|
if actualStr != expectedStr {
|
|
t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\nb%s", actualStr, expectedStr)
|
|
}
|
|
|
|
// Should have a backup file
|
|
f, err = os.Open(statePath + DefaultBackupExtension)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
backupStateFile, err := statefile.Read(f)
|
|
f.Close()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
backupActualStr := strings.TrimSpace(backupStateFile.State.String())
|
|
backupExpectedStr := strings.TrimSpace(originalState.String())
|
|
if backupActualStr != backupExpectedStr {
|
|
t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\nb%s", backupActualStr, backupExpectedStr)
|
|
}
|
|
}
|
|
|
|
const testApplyDestroyStr = `
|
|
<no state>
|
|
`
|