command: show shadow errors to the user

This commit is contained in:
Mitchell Hashimoto 2016-11-03 14:46:26 -07:00
parent 7291376c2d
commit d429e82661
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
4 changed files with 98 additions and 0 deletions

View File

@ -136,6 +136,9 @@ func (c *ApplyCommand) Run(args []string) int {
}
}
// This is going to keep track of shadow errors
var shadowErr error
// Build the context based on the arguments given
ctx, planned, err := c.Context(contextOpts{
Destroy: c.Destroy,
@ -189,6 +192,12 @@ func (c *ApplyCommand) Run(args []string) int {
c.Ui.Error(fmt.Sprintf("Error configuring: %s", err))
return 1
}
// Record any shadow errors for later
if err := ctx.ShadowError(); err != nil {
shadowErr = multierror.Append(shadowErr, multierror.Prefix(
err, "input operation:"))
}
}
if !validateContext(ctx, c.Ui) {
return 1
@ -208,6 +217,12 @@ func (c *ApplyCommand) Run(args []string) int {
"Error creating plan: %s", err))
return 1
}
// Record any shadow errors for later
if err := ctx.ShadowError(); err != nil {
shadowErr = multierror.Append(shadowErr, multierror.Prefix(
err, "plan operation:"))
}
}
// Setup the state hook for continuous state updates
@ -229,6 +244,12 @@ func (c *ApplyCommand) Run(args []string) int {
go func() {
defer close(doneCh)
state, applyErr = ctx.Apply()
// Record any shadow errors for later
if err := ctx.ShadowError(); err != nil {
shadowErr = multierror.Append(shadowErr, multierror.Prefix(
err, "apply operation:"))
}
}()
// Wait for the apply to finish or for us to be interrupted so
@ -304,6 +325,9 @@ func (c *ApplyCommand) Run(args []string) int {
}
}
// If we have an error in the shadow graph, let the user know.
c.outputShadowError(shadowErr, applyErr == nil)
return 0
}

View File

@ -460,6 +460,42 @@ func (m *Meta) addModuleDepthFlag(flags *flag.FlagSet, moduleDepth *int) {
}
}
// outputShadowError outputs the error from ctx.ShadowError. If the
// error is nil then nothing happens. If output is false then it isn't
// outputted to the user (you can define logic to guard against outputting).
func (m *Meta) outputShadowError(err error, output bool) bool {
// Do nothing if no error
if err == nil {
return false
}
// If not outputting, do nothing
if !output {
return false
}
// Output!
m.Ui.Output(m.Colorize().Color(fmt.Sprintf(
"[reset][bold][yellow]\nExperimental feature failure! Please report a bug.\n\n"+
"This is not an error. Your Terraform operation completed successfully.\n"+
"Your real infrastructure is unaffected by this message.\n\n"+
"[reset][yellow]While running, Terraform sometimes tests experimental features in the\n"+
"background. These features cannot affect real state and never touch\n"+
"real infrastructure. If the features work properly, you see nothing.\n"+
"If the features fail, this message appears.\n\n"+
"The following failures happened while running experimental features.\n"+
"Please report a Terraform bug so that future Terraform versions that\n"+
"enable these features can be improved!\n\n"+
"You can report an issue at: https://github.com/hashicorp/terraform/issues\n\n"+
"%s\n\n"+
"This is not an error. Your terraform operation completed successfully\n"+
"and your real infrastructure is unaffected by this message.",
err,
)))
return true
}
// contextOpts are the options used to load a context from a command.
type contextOpts struct {
// Path to the directory where the root module is.

View File

@ -6,6 +6,7 @@ import (
"os"
"strings"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/terraform"
)
@ -57,6 +58,9 @@ func (c *PlanCommand) Run(args []string) int {
countHook := new(CountHook)
c.Meta.extraHooks = []terraform.Hook{countHook}
// This is going to keep track of shadow errors
var shadowErr error
ctx, _, err := c.Context(contextOpts{
Destroy: destroy,
Path: path,
@ -73,6 +77,12 @@ func (c *PlanCommand) Run(args []string) int {
return 1
}
// Record any shadow errors for later
if err := ctx.ShadowError(); err != nil {
shadowErr = multierror.Append(shadowErr, multierror.Prefix(
err, "input operation:"))
}
if !validateContext(ctx, c.Ui) {
return 1
}
@ -138,6 +148,15 @@ func (c *PlanCommand) Run(args []string) int {
countHook.ToChange,
countHook.ToRemove+countHook.ToRemoveAndAdd)))
// Record any shadow errors for later
if err := ctx.ShadowError(); err != nil {
shadowErr = multierror.Append(shadowErr, multierror.Prefix(
err, "plan operation:"))
}
// If we have an error in the shadow graph, let the user know.
c.outputShadowError(shadowErr, true)
if detailed {
return 2
}

View File

@ -6,6 +6,7 @@ import (
"os"
"strings"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/terraform"
)
@ -79,6 +80,9 @@ func (c *RefreshCommand) Run(args []string) int {
}
}
// This is going to keep track of shadow errors
var shadowErr error
// Build the context based on the arguments given
ctx, _, err := c.Context(contextOpts{
Path: configPath,
@ -95,6 +99,12 @@ func (c *RefreshCommand) Run(args []string) int {
return 1
}
// Record any shadow errors for later
if err := ctx.ShadowError(); err != nil {
shadowErr = multierror.Append(shadowErr, multierror.Prefix(
err, "input operation:"))
}
if !validateContext(ctx, c.Ui) {
return 1
}
@ -115,6 +125,15 @@ func (c *RefreshCommand) Run(args []string) int {
c.Ui.Output(c.Colorize().Color(outputs))
}
// Record any shadow errors for later
if err := ctx.ShadowError(); err != nil {
shadowErr = multierror.Append(shadowErr, multierror.Prefix(
err, "refresh operation:"))
}
// If we have an error in the shadow graph, let the user know.
c.outputShadowError(shadowErr, true)
return 0
}