diff --git a/cmd/coconut.go b/cmd/coconut.go index 296ef3c7b..91c8385fa 100644 --- a/cmd/coconut.go +++ b/cmd/coconut.go @@ -4,10 +4,15 @@ package cmd import ( "flag" + "fmt" + "os" "strconv" "github.com/golang/glog" "github.com/spf13/cobra" + + "github.com/pulumi/coconut/pkg/compiler/core" + "github.com/pulumi/coconut/pkg/diag" ) func NewCoconutCmd() *cobra.Command { @@ -45,3 +50,27 @@ func NewCoconutCmd() *cobra.Command { return cmd } + +var snk diag.Sink + +// sink lazily allocates a sink to be used if we can't create a compiler. +func sink() diag.Sink { + if snk == nil { + snk = core.DefaultSink("") + } + return snk +} + +// exitErrorPrefix is auto-appended to any abrupt command exit. +const exitErrorPrefix = "fatal: " + +// exitError issues an error and exits with a standard error exit code. +func exitError(msg string, args ...interface{}) { + exitErrorCode(-1, msg, args...) +} + +// exitErrorCode issues an error and exists with the given error exit code. +func exitErrorCode(code int, msg string, args ...interface{}) { + sink().Errorf(diag.Message(exitErrorPrefix + fmt.Sprintf(msg, args...))) + os.Exit(code) +} diff --git a/cmd/describe.go b/cmd/describe.go index 0ae3d6313..713ccd3f6 100644 --- a/cmd/describe.go +++ b/cmd/describe.go @@ -42,8 +42,7 @@ func newDescribeCmd() *cobra.Command { pwd, _ := os.Getwd() pkgpath, err := workspace.DetectPackage(pwd, sink()) if err != nil { - fmt.Fprintf(os.Stderr, "fatal: could not find a nut: %v", err) - os.Exit(-1) + exitError("could not locate a nut to load: %v", err) } if pkg := cmdutil.ReadPackage(pkgpath); pkg != nil { diff --git a/cmd/eval.go b/cmd/eval.go index d3a7bc802..ce4b354d7 100644 --- a/cmd/eval.go +++ b/cmd/eval.go @@ -38,8 +38,7 @@ func newEvalCmd() *cobra.Command { if dotOutput { // Convert the output to a DOT file. if err := dotconv.Print(g, os.Stdout); err != nil { - fmt.Fprintf(os.Stderr, "error: failed to write DOT file to output: %v\n", err) - os.Exit(-1) + exitError("failed to write DOT file to output: %v", err) } } else { // Just print a very basic, yet (hopefully) aesthetically pleasinge, ascii-ization of the graph. diff --git a/cmd/husk.go b/cmd/husk.go index 28edf2dd7..e50942c64 100644 --- a/cmd/husk.go +++ b/cmd/husk.go @@ -53,23 +53,13 @@ func newHuskCmd() *cobra.Command { return cmd } -var snk diag.Sink - -// sink lazily allocates a sink to be used if we can't create a compiler. -func sink() diag.Sink { - if snk == nil { - snk = core.DefaultSink("") - } - return snk -} - -func initHuskCmd(cmd *cobra.Command, args []string) (*huskCmdInfo, error) { +func initHuskCmd(cmd *cobra.Command, args []string) *huskCmdInfo { // Create a new context for the plan operations. ctx := resource.NewContext(sink()) // Read in the name of the husk to use. if len(args) == 0 { - return nil, fmt.Errorf("missing required husk name") + exitError("missing required husk name") } // Read in the deployment information, bailing if an IO error occurs. @@ -77,7 +67,7 @@ func initHuskCmd(cmd *cobra.Command, args []string) (*huskCmdInfo, error) { huskfile, husk, old := readHusk(ctx, name) if husk == nil { contract.Assert(!ctx.Diag.Success()) - return nil, fmt.Errorf("failed to read huskfile") // failure reading the husk information. + exitError("could not read huskfile required to proceed") // failure reading the husk information. } return &huskCmdInfo{ Ctx: ctx, @@ -86,7 +76,7 @@ func initHuskCmd(cmd *cobra.Command, args []string) (*huskCmdInfo, error) { Old: old, Args: args[1:], Orig: args, - }, nil + } } type huskCmdInfo struct { diff --git a/cmd/husk_deploy.go b/cmd/husk_deploy.go index 09f67483c..d3bcd8e1e 100644 --- a/cmd/husk_deploy.go +++ b/cmd/husk_deploy.go @@ -26,11 +26,8 @@ func newHuskDeployCmd() *cobra.Command { "\n" + "By default, the Nut to execute is loaded from the current directory. Optionally, an\n" + "explicit path can be provided using the [nut-file] argument.", - RunE: func(cmd *cobra.Command, args []string) error { - info, err := initHuskCmd(cmd, args) - if err != nil { - return err - } + Run: func(cmd *cobra.Command, args []string) { + info := initHuskCmd(cmd, args) apply(cmd, info, applyOptions{ Delete: false, DryRun: dryRun, @@ -39,7 +36,6 @@ func newHuskDeployCmd() *cobra.Command { Summary: summary, Output: output, }) - return nil }, } diff --git a/cmd/husk_destroy.go b/cmd/husk_destroy.go index cc67c7784..056c39791 100644 --- a/cmd/husk_destroy.go +++ b/cmd/husk_destroy.go @@ -21,11 +21,8 @@ func newHuskDestroyCmd() *cobra.Command { "\n" + "Warning: although old snapshots can be used to recreate an environment, this command\n" + "is generally irreversable and should be used with great care.", - RunE: func(cmd *cobra.Command, args []string) error { - info, err := initHuskCmd(cmd, args) - if err != nil { - return err - } + Run: func(cmd *cobra.Command, args []string) { + info := initHuskCmd(cmd, args) if dryRun || yes || confirmPrompt("This will permanently destroy all resources in the '%v' husk!", info.Husk.Name) { apply(cmd, info, applyOptions{ @@ -34,7 +31,6 @@ func newHuskDestroyCmd() *cobra.Command { Summary: summary, }) } - return nil }, } diff --git a/cmd/husk_init.go b/cmd/husk_init.go index e62b01d86..9b621a37a 100644 --- a/cmd/husk_init.go +++ b/cmd/husk_init.go @@ -3,9 +3,6 @@ package cmd import ( - "fmt" - "os" - "github.com/spf13/cobra" "github.com/pulumi/coconut/pkg/tokens" @@ -22,8 +19,7 @@ func newHuskInitCmd() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { // Read in the name of the husk to use. if len(args) == 0 { - fmt.Fprintf(os.Stderr, "fatal: missing required husk name\n") - os.Exit(-1) + exitError("missing required husk name") } husk := tokens.QName(args[0]) diff --git a/cmd/husk_ls.go b/cmd/husk_ls.go index 8fc227ad2..12fa22148 100644 --- a/cmd/husk_ls.go +++ b/cmd/husk_ls.go @@ -27,8 +27,7 @@ func newHuskLsCmd() *cobra.Command { path := workspace.HuskPath("") files, err := ioutil.ReadDir(path) if err != nil && !os.IsNotExist(err) { - fmt.Fprintf(os.Stderr, "fatal: could not read husks: %v\n", err) - os.Exit(-1) + exitError("could not read husks: %v", err) } fmt.Printf("%-20s %-48s %-12s\n", "NAME", "LAST DEPLOYMENT", "RESOURCE COUNT") diff --git a/cmd/husk_rm.go b/cmd/husk_rm.go index cb521c3be..c0fedcc85 100644 --- a/cmd/husk_rm.go +++ b/cmd/husk_rm.go @@ -3,8 +3,6 @@ package cmd import ( - "fmt" - "github.com/spf13/cobra" ) @@ -20,20 +18,16 @@ func newHuskRmCmd() *cobra.Command { "`destroy` command for removing a husk's resources, as it is a distinct operation.\n" + "\n" + "After this command completes, the husk will no longer be available for deployments.", - RunE: func(cmd *cobra.Command, args []string) error { - info, err := initHuskCmd(cmd, args) - if err != nil { - return err - } + Run: func(cmd *cobra.Command, args []string) { + info := initHuskCmd(cmd, args) if !force && info.Old != nil && len(info.Old.Resources()) > 0 { - return fmt.Errorf( - "Husk '%v' still has resources; removal rejected; pass --force to override\n", info.Husk.Name) + exitError( + "'%v' still has resources; removal rejected; pass --force to override", info.Husk.Name) } if yes || confirmPrompt("This will permanently remove the '%v' husk!", info.Husk.Name) { remove(info.Husk) } - return nil }, } diff --git a/cmd/verify.go b/cmd/verify.go index 9dba757eb..9048a4815 100644 --- a/cmd/verify.go +++ b/cmd/verify.go @@ -3,9 +3,6 @@ package cmd import ( - "fmt" - "os" - "github.com/spf13/cobra" ) @@ -25,8 +22,7 @@ func newVerifyCmd() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { // Create a compiler object and perform the verification. if !verify(cmd, args) { - fmt.Printf("fatal: Nut verification failed\n") - os.Exit(-1) + exitError("Nut verification failed") } }, }