Skip to content

Commit

Permalink
Merge pull request #27681 from hashicorp/alisdair/remove-deprecated-d…
Browse files Browse the repository at this point in the history
…estroy-force

cli: Remove deprecated destroy -force flag
  • Loading branch information
alisdair authored Feb 4, 2021
2 parents 030632e + b2ba650 commit 294bbd8
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 16 deletions.
11 changes: 5 additions & 6 deletions backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,11 @@ type Operation struct {

// The options below are more self-explanatory and affect the runtime
// behavior of the operation.
AutoApprove bool
Destroy bool
DestroyForce bool
Parallelism int
Targets []addrs.Targetable
Variables map[string]UnparsedVariableValue
AutoApprove bool
Destroy bool
Parallelism int
Targets []addrs.Targetable
Variables map[string]UnparsedVariableValue

// Some operations use root module variables only opportunistically or
// don't need them at all. If this flag is set, the backend must treat
Expand Down
2 changes: 1 addition & 1 deletion backend/local/backend_apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (b *Local) opApply(

trivialPlan := plan.Changes.Empty()
hasUI := op.UIOut != nil && op.UIIn != nil
mustConfirm := hasUI && ((op.Destroy && (!op.DestroyForce && !op.AutoApprove)) || (!op.Destroy && !op.AutoApprove && !trivialPlan))
mustConfirm := hasUI && !op.AutoApprove && !trivialPlan
if mustConfirm {
var desc, query string
if op.Destroy {
Expand Down
3 changes: 1 addition & 2 deletions backend/remote/backend_apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,7 @@ func (b *Remote) opApply(stopCtx, cancelCtx context.Context, op *backend.Operati
return r, diags.Err()
}

mustConfirm := (op.UIIn != nil && op.UIOut != nil) &&
((op.Destroy && (!op.DestroyForce && !op.AutoApprove)) || (!op.Destroy && !op.AutoApprove))
mustConfirm := (op.UIIn != nil && op.UIOut != nil) && !op.AutoApprove

if !w.AutoApply {
if mustConfirm {
Expand Down
8 changes: 1 addition & 7 deletions command/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type ApplyCommand struct {
}

func (c *ApplyCommand) Run(args []string) int {
var destroyForce, refresh, autoApprove bool
var refresh, autoApprove bool
args = c.Meta.process(args)
cmdName := "apply"
if c.Destroy {
Expand All @@ -33,9 +33,6 @@ func (c *ApplyCommand) Run(args []string) int {

cmdFlags := c.Meta.extendedFlagSet(cmdName)
cmdFlags.BoolVar(&autoApprove, "auto-approve", false, "skip interactive approval of plan before applying")
if c.Destroy {
cmdFlags.BoolVar(&destroyForce, "force", false, "deprecated: same as auto-approve")
}
cmdFlags.BoolVar(&refresh, "refresh", true, "refresh")
cmdFlags.IntVar(&c.Meta.parallelism, "parallelism", DefaultParallelism, "parallelism")
cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path")
Expand Down Expand Up @@ -160,7 +157,6 @@ func (c *ApplyCommand) Run(args []string) int {
opReq.AutoApprove = autoApprove
opReq.ConfigDir = configPath
opReq.Destroy = c.Destroy
opReq.DestroyForce = destroyForce
opReq.PlanFile = planFile
opReq.PlanRefresh = refresh
opReq.Type = backend.OperationTypeApply
Expand Down Expand Up @@ -319,8 +315,6 @@ Options:
-auto-approve Skip interactive approval before destroying.
-force Deprecated: same as auto-approve.
-lock=true Lock the state file when locking is supported.
-lock-timeout=0s Duration to retry a state lock.
Expand Down
118 changes: 118 additions & 0 deletions command/apply_destroy_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package command

import (
"bytes"
"os"
"strings"
"testing"
Expand Down Expand Up @@ -114,6 +115,123 @@ func TestApply_destroy(t *testing.T) {
}
}

func TestApply_destroyApproveNo(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply"), td)
defer os.RemoveAll(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)

// Disable test mode so input would be asked
test = false
defer func() { test = true }()

// Answer approval request with "no"
defaultInputReader = bytes.NewBufferString("no\n")
defaultInputWriter = new(bytes.Buffer)

p := applyFixtureProvider()
ui := new(cli.MockUi)
c := &ApplyCommand{
Destroy: true,
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}

args := []string{
"-state", statePath,
}
if code := c.Run(args); code != 1 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
if got, want := ui.OutputWriter.String(), "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 := tempDir(t)
testCopyDir(t, testFixturePath("apply"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()

statePath := testTempFile(t)

p := applyFixtureProvider()

// Disable test mode so input would be asked
test = false
defer func() { test = true }()

// Answer approval request with "yes"
defaultInputReader = bytes.NewBufferString("yes\n")
defaultInputWriter = new(bytes.Buffer)

ui := new(cli.MockUi)
c := &ApplyCommand{
Destroy: true,
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}

args := []string{
"-state", statePath,
}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}

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) {
originalState := states.BuildState(func(s *states.SyncState) {
s.SetResourceInstanceCurrent(
Expand Down
85 changes: 85 additions & 0 deletions command/apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,91 @@ func TestApply(t *testing.T) {
}
}

func TestApply_approveNo(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()

statePath := testTempFile(t)

// Disable test mode so input would be asked
test = false
defer func() { test = true }()

// Answer approval request with "no"
defaultInputReader = bytes.NewBufferString("no\n")
defaultInputWriter = new(bytes.Buffer)

p := applyFixtureProvider()
ui := new(cli.MockUi)
c := &ApplyCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}

args := []string{
"-state", statePath,
}
if code := c.Run(args); code != 1 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}
if got, want := ui.OutputWriter.String(), "Apply cancelled"; !strings.Contains(got, want) {
t.Fatalf("expected output to include %q, but was:\n%s", want, got)
}

if _, err := os.Stat(statePath); err == nil || !os.IsNotExist(err) {
t.Fatalf("state file should not exist")
}
}

func TestApply_approveYes(t *testing.T) {
// Create a temporary working directory that is empty
td := tempDir(t)
testCopyDir(t, testFixturePath("apply"), td)
defer os.RemoveAll(td)
defer testChdir(t, td)()

statePath := testTempFile(t)

p := applyFixtureProvider()

// Disable test mode so input would be asked
test = false
defer func() { test = true }()

// Answer approval request with "yes"
defaultInputReader = bytes.NewBufferString("yes\n")
defaultInputWriter = new(bytes.Buffer)

ui := new(cli.MockUi)
c := &ApplyCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
},
}

args := []string{
"-state", statePath,
}
if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}

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")
}
}

// test apply with locked state
func TestApply_lockedState(t *testing.T) {
// Create a temporary working directory that is empty
Expand Down

0 comments on commit 294bbd8

Please sign in to comment.