mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-26 17:01:04 -06:00
command/destroy: first steps
This commit is contained in:
parent
3ae6f6ba76
commit
ab9dd71bcb
@ -17,6 +17,11 @@ import (
|
|||||||
type ApplyCommand struct {
|
type ApplyCommand struct {
|
||||||
Meta
|
Meta
|
||||||
|
|
||||||
|
// If true, then this apply command will become the "destroy"
|
||||||
|
// command. It is just like apply but only processes a destroy.
|
||||||
|
Destroy bool
|
||||||
|
|
||||||
|
// When this channel is closed, the apply will be cancelled.
|
||||||
ShutdownCh <-chan struct{}
|
ShutdownCh <-chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +31,12 @@ func (c *ApplyCommand) Run(args []string) int {
|
|||||||
|
|
||||||
args = c.Meta.process(args, true)
|
args = c.Meta.process(args, true)
|
||||||
|
|
||||||
cmdFlags := c.Meta.flagSet("apply")
|
cmdName := "apply"
|
||||||
|
if c.Destroy {
|
||||||
|
cmdName = "destroy"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdFlags := c.Meta.flagSet(cmdName)
|
||||||
cmdFlags.BoolVar(&refresh, "refresh", true, "refresh")
|
cmdFlags.BoolVar(&refresh, "refresh", true, "refresh")
|
||||||
cmdFlags.StringVar(&statePath, "state", DefaultStateFilename, "path")
|
cmdFlags.StringVar(&statePath, "state", DefaultStateFilename, "path")
|
||||||
cmdFlags.StringVar(&stateOutPath, "state-out", "", "path")
|
cmdFlags.StringVar(&stateOutPath, "state-out", "", "path")
|
||||||
@ -70,22 +80,24 @@ func (c *ApplyCommand) Run(args []string) int {
|
|||||||
backupPath = stateOutPath + DefaultBackupExtention
|
backupPath = stateOutPath + DefaultBackupExtention
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do a detect to determine if we need to do an init + apply.
|
if !c.Destroy {
|
||||||
if detected, err := module.Detect(configPath, pwd); err != nil {
|
// Do a detect to determine if we need to do an init + apply.
|
||||||
c.Ui.Error(fmt.Sprintf(
|
if detected, err := module.Detect(configPath, pwd); err != nil {
|
||||||
"Invalid path: %s", err))
|
c.Ui.Error(fmt.Sprintf(
|
||||||
return 1
|
"Invalid path: %s", err))
|
||||||
} else if !strings.HasPrefix(detected, "file") {
|
return 1
|
||||||
// If this isn't a file URL then we're doing an init +
|
} else if !strings.HasPrefix(detected, "file") {
|
||||||
// apply.
|
// If this isn't a file URL then we're doing an init +
|
||||||
var init InitCommand
|
// apply.
|
||||||
init.Meta = c.Meta
|
var init InitCommand
|
||||||
if code := init.Run([]string{detected}); code != 0 {
|
init.Meta = c.Meta
|
||||||
return code
|
if code := init.Run([]string{detected}); code != 0 {
|
||||||
}
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
// Change the config path to be the cwd
|
// Change the config path to be the cwd
|
||||||
configPath = pwd
|
configPath = pwd
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the context based on the arguments given
|
// Build the context based on the arguments given
|
||||||
@ -97,6 +109,11 @@ func (c *ApplyCommand) Run(args []string) int {
|
|||||||
c.Ui.Error(err.Error())
|
c.Ui.Error(err.Error())
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
if planned {
|
||||||
|
c.Ui.Error(fmt.Sprintf(
|
||||||
|
"Destroy can't be called with a plan file."))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
if c.Input() {
|
if c.Input() {
|
||||||
if err := ctx.Input(); err != nil {
|
if err := ctx.Input(); err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf("Error configuring: %s", err))
|
c.Ui.Error(fmt.Sprintf("Error configuring: %s", err))
|
||||||
@ -130,7 +147,12 @@ func (c *ApplyCommand) Run(args []string) int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ctx.Plan(nil); err != nil {
|
var opts terraform.PlanOpts
|
||||||
|
if c.Destroy {
|
||||||
|
opts.Destroy = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := ctx.Plan(&opts); err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf(
|
c.Ui.Error(fmt.Sprintf(
|
||||||
"Error creating plan: %s", err))
|
"Error creating plan: %s", err))
|
||||||
return 1
|
return 1
|
||||||
|
120
command/apply_destroy_test.go
Normal file
120
command/apply_destroy_test.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestApply_destroy(t *testing.T) {
|
||||||
|
originalState := &terraform.State{
|
||||||
|
Modules: []*terraform.ModuleState{
|
||||||
|
&terraform.ModuleState{
|
||||||
|
Path: []string{"root"},
|
||||||
|
Resources: map[string]*terraform.ResourceState{
|
||||||
|
"test_instance.foo": &terraform.ResourceState{
|
||||||
|
Type: "test_instance",
|
||||||
|
Primary: &terraform.InstanceState{
|
||||||
|
ID: "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
statePath := testStateFile(t, originalState)
|
||||||
|
|
||||||
|
p := testProvider()
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
c := &ApplyCommand{
|
||||||
|
Destroy: true,
|
||||||
|
Meta: Meta{
|
||||||
|
ContextOpts: testCtxConfig(p),
|
||||||
|
Ui: ui,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the apply command pointing to our existing state
|
||||||
|
args := []string{
|
||||||
|
"-state", statePath,
|
||||||
|
testFixturePath("apply"),
|
||||||
|
}
|
||||||
|
if code := c.Run(args); code != 0 {
|
||||||
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
|
||||||
|
state, err := terraform.ReadState(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should have a backup file
|
||||||
|
f, err = os.Open(statePath + DefaultBackupExtention)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
backupState, err := terraform.ReadState(f)
|
||||||
|
f.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
actualStr = strings.TrimSpace(backupState.String())
|
||||||
|
expectedStr = strings.TrimSpace(originalState.String())
|
||||||
|
if actualStr != expectedStr {
|
||||||
|
t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApply_destroyPlan(t *testing.T) {
|
||||||
|
planPath := testPlanFile(t, &terraform.Plan{
|
||||||
|
Module: testModule(t, "apply"),
|
||||||
|
})
|
||||||
|
|
||||||
|
p := testProvider()
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
c := &ApplyCommand{
|
||||||
|
Destroy: true,
|
||||||
|
Meta: Meta{
|
||||||
|
ContextOpts: testCtxConfig(p),
|
||||||
|
Ui: ui,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the apply command pointing to our existing state
|
||||||
|
args := []string{
|
||||||
|
planPath,
|
||||||
|
}
|
||||||
|
if code := c.Run(args); code != 1 {
|
||||||
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const testApplyDestroyStr = `
|
||||||
|
<no state>
|
||||||
|
`
|
Loading…
Reference in New Issue
Block a user