Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions internal/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,9 @@ type RunningOperation struct {
// operation has completed.
Result OperationResult

// PlanEmpty is populated after a Plan operation completes without error
// to note whether a plan is empty or has changes.
// PlanEmpty is populated after a Plan operation completes to note whether
// a plan is empty or has changes. This is only used in the CLI to determine
// the exit status because the plan value is not available at that point.
PlanEmpty bool

// State is the final state after the operation completed. Persisting
Expand Down
24 changes: 15 additions & 9 deletions internal/backend/local/backend_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,19 @@ func (b *Local) opPlan(
// generate a partial saved plan file for external analysis.
diags = diags.Append(planDiags)

// Even if there are errors we need to handle anything that may be
// contained within the plan, so only exit if there is no data at all.
if plan == nil {
runningOp.PlanEmpty = true
op.ReportResult(runningOp, diags)
return
}

// Record whether this plan includes any side-effects that could be applied.
runningOp.PlanEmpty = !plan.CanApply()

// Save the plan to disk
if path := op.PlanOutPath; path != "" && plan != nil {
if path := op.PlanOutPath; path != "" {
if op.PlanOutBackend == nil {
// This is always a bug in the operation caller; it's not valid
// to set PlanOutPath without also setting PlanOutBackend.
Expand Down Expand Up @@ -154,15 +162,13 @@ func (b *Local) opPlan(

// Render the plan, if we produced one.
// (This might potentially be a partial plan with Errored set to true)
if plan != nil {
schemas, moreDiags := lr.Core.Schemas(lr.Config, lr.InputState)
diags = diags.Append(moreDiags)
if moreDiags.HasErrors() {
op.ReportResult(runningOp, diags)
return
}
op.View.Plan(plan, schemas)
schemas, moreDiags := lr.Core.Schemas(lr.Config, lr.InputState)
diags = diags.Append(moreDiags)
if moreDiags.HasErrors() {
op.ReportResult(runningOp, diags)
return
}
op.View.Plan(plan, schemas)

// If we've accumulated any diagnostics along the way then we'll show them
// here just before we show the summary and next steps. This can potentially
Expand Down
24 changes: 24 additions & 0 deletions internal/backend/local/backend_plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -880,3 +880,27 @@ func planFixtureSchema() *terraform.ProviderSchema {
},
}
}

func TestLocal_invalidOptions(t *testing.T) {
b := TestLocal(t)
TestLocalProvider(t, b, "test", planFixtureSchema())

op, configCleanup, done := testOperationPlan(t, "./testdata/plan")
defer configCleanup()
op.PlanRefresh = true
op.PlanMode = plans.RefreshOnlyMode
op.ForceReplace = []addrs.AbsResourceInstance{mustResourceInstanceAddr("test_instance.foo")}

run, err := b.Operation(context.Background(), op)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
<-run.Done()
if run.Result == backend.OperationSuccess {
t.Fatalf("plan operation failed")
}

if errOutput := done(t).Stderr(); errOutput == "" {
t.Fatal("expected error output")
}
}