diff --git a/command/plan.go b/command/plan.go index 5c884d632a..f23e1bb6e4 100644 --- a/command/plan.go +++ b/command/plan.go @@ -16,7 +16,7 @@ type PlanCommand struct { } func (c *PlanCommand) Run(args []string) int { - var destroy, refresh bool + var destroy, refresh, detailed bool var outPath string var moduleDepth int @@ -29,6 +29,7 @@ func (c *PlanCommand) Run(args []string) int { cmdFlags.StringVar(&outPath, "out", "", "path") cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path") cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path") + cmdFlags.BoolVar(&detailed, "detailed-exitcode", false, "detailed-exitcode") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { return 1 @@ -129,6 +130,9 @@ func (c *PlanCommand) Run(args []string) int { ModuleDepth: moduleDepth, })) + if detailed { + return 2 + } return 0 } @@ -152,6 +156,12 @@ Options: -destroy If set, a plan will be generated to destroy all resources managed by the given configuration and state. + -detailed-exitcode Return detailed exit codes when the command exits. This + will change the meaning of exit codes to: + 0 - Succeeded, diff is empty (no changes) + 1 - Errored + 2 - Succeeded, there is a diff + -input=true Ask for input for variables if not directly set. -module-depth=n Specifies the depth of modules to show in the output. diff --git a/command/plan_test.go b/command/plan_test.go index d981c2294e..3455fbbc61 100644 --- a/command/plan_test.go +++ b/command/plan_test.go @@ -567,6 +567,56 @@ func TestPlan_disableBackup(t *testing.T) { } } +func TestPlan_detailedExitcode(t *testing.T) { + cwd, err := os.Getwd() + if err != nil { + t.Fatalf("err: %s", err) + } + if err := os.Chdir(testFixturePath("plan")); err != nil { + t.Fatalf("err: %s", err) + } + defer os.Chdir(cwd) + + p := testProvider() + ui := new(cli.MockUi) + c := &PlanCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + args := []string{"-detailed-exitcode"} + if code := c.Run(args); code != 2 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } +} + +func TestPlan_detailedExitcode_emptyDiff(t *testing.T) { + cwd, err := os.Getwd() + if err != nil { + t.Fatalf("err: %s", err) + } + if err := os.Chdir(testFixturePath("plan-emptydiff")); err != nil { + t.Fatalf("err: %s", err) + } + defer os.Chdir(cwd) + + p := testProvider() + ui := new(cli.MockUi) + c := &PlanCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + args := []string{"-detailed-exitcode"} + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } +} + const planVarFile = ` foo = "bar" ` diff --git a/command/test-fixtures/plan-emptydiff/main.tf b/command/test-fixtures/plan-emptydiff/main.tf new file mode 100644 index 0000000000..e69de29bb2