backend/local: validate module exists for plan
Fixes #11504 The local backend should error if `terraform plan` is called in a directory with no Terraform config files (same behavior as 0.8.x). **New behavior:** We now allow `terraform plan -destroy` with no configuration files since that seems reasonable.
This commit is contained in:
parent
9183be4c83
commit
a424203ea3
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/command/format"
|
||||
"github.com/hashicorp/terraform/config/module"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
|
@ -28,6 +29,18 @@ func (b *Local) opPlan(
|
|||
"directory as an argument.\n\n"))
|
||||
}
|
||||
|
||||
// A local plan requires either a plan or a module
|
||||
if op.Plan == nil && op.Module == nil && !op.Destroy {
|
||||
runningOp.Err = fmt.Errorf(strings.TrimSpace(planErrNoConfig))
|
||||
return
|
||||
}
|
||||
|
||||
// If we have a nil module at this point, then set it to an empty tree
|
||||
// to avoid any potential crashes.
|
||||
if op.Module == nil {
|
||||
op.Module = module.NewEmptyTree()
|
||||
}
|
||||
|
||||
// Setup our count hook that keeps track of resource changes
|
||||
countHook := new(CountHook)
|
||||
if b.ContextOpts == nil {
|
||||
|
@ -120,6 +133,16 @@ func (b *Local) opPlan(
|
|||
}
|
||||
}
|
||||
|
||||
const planErrNoConfig = `
|
||||
No configuration files found!
|
||||
|
||||
Plan requires configuration to be present. Planning without a configuration
|
||||
would mark everything for destruction, which is normally not what is desired.
|
||||
If you would like to destroy everything, please run plan with the "-destroy"
|
||||
flag or create a single empty configuration file. Otherwise, please create
|
||||
a Terraform configuration file in the path being executed and try again.
|
||||
`
|
||||
|
||||
const planHeaderNoOutput = `
|
||||
The Terraform execution plan has been generated and is shown below.
|
||||
Resources are shown in alphabetical order for quick scanning. Green resources
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
|
@ -36,6 +37,29 @@ func TestLocal_planBasic(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestLocal_planNoConfig(t *testing.T) {
|
||||
b := TestLocal(t)
|
||||
TestLocalProvider(t, b, "test")
|
||||
|
||||
op := testOperationPlan()
|
||||
op.Module = nil
|
||||
op.PlanRefresh = true
|
||||
|
||||
run, err := b.Operation(context.Background(), op)
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
<-run.Done()
|
||||
|
||||
err = run.Err
|
||||
if err == nil {
|
||||
t.Fatal("should error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "configuration") {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocal_planRefreshFalse(t *testing.T) {
|
||||
b := TestLocal(t)
|
||||
p := TestLocalProvider(t, b, "test")
|
||||
|
@ -110,6 +134,48 @@ func TestLocal_planDestroy(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestLocal_planDestroyNoConfig(t *testing.T) {
|
||||
b := TestLocal(t)
|
||||
p := TestLocalProvider(t, b, "test")
|
||||
terraform.TestStateFile(t, b.StatePath, testPlanState())
|
||||
|
||||
outDir := testTempDir(t)
|
||||
defer os.RemoveAll(outDir)
|
||||
planPath := filepath.Join(outDir, "plan.tfplan")
|
||||
|
||||
op := testOperationPlan()
|
||||
op.Destroy = true
|
||||
op.PlanRefresh = true
|
||||
op.Module = nil
|
||||
op.PlanOutPath = planPath
|
||||
|
||||
run, err := b.Operation(context.Background(), op)
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
<-run.Done()
|
||||
if run.Err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if !p.RefreshCalled {
|
||||
t.Fatal("refresh should be called")
|
||||
}
|
||||
|
||||
if run.PlanEmpty {
|
||||
t.Fatal("plan should not be empty")
|
||||
}
|
||||
|
||||
plan := testReadPlan(t, planPath)
|
||||
for _, m := range plan.Diff.Modules {
|
||||
for _, r := range m.Resources {
|
||||
if !r.Destroy {
|
||||
t.Fatalf("bad: %#v", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocal_planOutPathNoChange(t *testing.T) {
|
||||
b := TestLocal(t)
|
||||
TestLocalProvider(t, b, "test")
|
||||
|
|
Loading…
Reference in New Issue