core: Warn when creating and applying with -target

The documentation for the -target option warns that it's intended for
exceptional circumstances only and not for routine use, but that's not a
very prominent location for that warning and so some users miss it.

Here we make the warning more prominent by including it directly in the
Terraform output when -target is in use. We first warn during planning
that the plan might be incomplete, and then warn again after apply
concludes and direct the user to run "terraform plan" to make sure that
there are no further changes outstanding. The latter message is intended
to reinforce that -target should only be a one-off operation and that you
should always run without it soon after to ensure that the workspace is
left in a consistent, converged state.
This commit is contained in:
Martin Atkins 2019-09-12 12:11:54 -07:00
parent 3145171caf
commit 7e29b9b5d4
4 changed files with 68 additions and 12 deletions

View File

@ -76,9 +76,9 @@ func (b *Local) opApply(
// Perform the plan
log.Printf("[INFO] backend/local: apply calling Plan")
plan, err := tfCtx.Plan()
if err != nil {
diags = diags.Append(err)
plan, planDiags := tfCtx.Plan()
diags = diags.Append(planDiags)
if planDiags.HasErrors() {
b.ReportResult(runningOp, diags)
return
}
@ -112,6 +112,13 @@ func (b *Local) opApply(
b.CLI.Output("")
}
// We'll show any accumulated warnings before we display the prompt,
// so the user can consider them when deciding how to answer.
if len(diags) > 0 {
b.ShowDiagnostics(diags)
diags = nil // reset so we won't show the same diagnostics again later
}
v, err := op.UIIn.Input(stopCtx, &terraform.InputOpts{
Id: "approve",
Query: query,

View File

@ -467,6 +467,17 @@ func (c *Context) Apply() (*states.State, tfdiags.Diagnostics) {
c.state.PruneResourceHusks()
}
if len(c.targets) > 0 {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Warning,
"Applied changes may be incomplete",
`The plan was created with the -target option in effect, so some changes requested in the configuration may have been ignored and the output values may not be fully updated. Run the following command to verify that no other changes are pending:
terraform plan
Note that the -target option is not suitable for routine use, and is provided only for exceptional situations such as recovering from errors or mistakes, or when Terraform specifically suggests to use it as part of an error message.`,
))
}
return c.state, diags
}
@ -483,6 +494,16 @@ func (c *Context) Plan() (*plans.Plan, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
if len(c.targets) > 0 {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Warning,
"Resource targeting is in effect",
`You are creating a plan with the -target option, which means that the result of this plan may not represent all of the changes requested by the current configuration.
The -target option is not for routine use, and is provided only for exceptional situations such as recovering from errors or mistakes, or when Terraform specifically suggests to use it as part of an error message.`,
))
}
varVals := make(map[string]plans.DynamicValue, len(c.variables))
for k, iv := range c.variables {
// We use cty.DynamicPseudoType here so that we'll save both the

View File

@ -6824,15 +6824,34 @@ func TestContext2Apply_destroyTargetWithModuleVariableAndCount(t *testing.T) {
},
})
_, err := ctx.Plan()
if err != nil {
t.Fatalf("plan err: %s", err)
_, diags := ctx.Plan()
if diags.HasErrors() {
t.Fatalf("plan err: %s", diags)
}
if len(diags) != 1 {
// Should have one warning that -target is in effect.
t.Fatalf("got %d diagnostics in plan; want 1", len(diags))
}
if got, want := diags[0].Severity(), tfdiags.Warning; got != want {
t.Errorf("wrong diagnostic severity %#v; want %#v", got, want)
}
if got, want := diags[0].Description().Summary, "Resource targeting is in effect"; got != want {
t.Errorf("wrong diagnostic summary %#v; want %#v", got, want)
}
// Destroy, targeting the module explicitly
state, err = ctx.Apply()
if err != nil {
t.Fatalf("destroy apply err: %s", err)
state, diags = ctx.Apply()
if diags.HasErrors() {
t.Fatalf("destroy apply err: %s", diags)
}
if len(diags) != 1 {
t.Fatalf("got %d diagnostics; want 1", len(diags))
}
if got, want := diags[0].Severity(), tfdiags.Warning; got != want {
t.Errorf("wrong diagnostic severity %#v; want %#v", got, want)
}
if got, want := diags[0].Description().Summary, "Applied changes may be incomplete"; got != want {
t.Errorf("wrong diagnostic summary %#v; want %#v", got, want)
}
}

View File

@ -4690,9 +4690,18 @@ func TestContext2Plan_outputContainsTargetedResource(t *testing.T) {
},
})
_, err := ctx.Plan()
if err != nil {
t.Fatalf("err: %s", err)
_, diags := ctx.Plan()
if diags.HasErrors() {
t.Fatalf("err: %s", diags)
}
if len(diags) != 1 {
t.Fatalf("got %d diagnostics; want 1", diags)
}
if got, want := diags[0].Severity(), tfdiags.Warning; got != want {
t.Errorf("wrong diagnostic severity %#v; want %#v", got, want)
}
if got, want := diags[0].Description().Summary, "Resource targeting is in effect"; got != want {
t.Errorf("wrong diagnostic summary %#v; want %#v", got, want)
}
}