diff --git a/libbeat/common/cli/cli.go b/libbeat/common/cli/cli.go index 0bf4650fc851..b608f95526bb 100644 --- a/libbeat/common/cli/cli.go +++ b/libbeat/common/cli/cli.go @@ -3,15 +3,25 @@ package cli import ( "fmt" "os" + "runtime/debug" "github.com/spf13/cobra" ) +func exitOnPanic() { + if r := recover(); r != nil { + fmt.Fprintf(os.Stderr, "panic: %s\n", r) + debug.PrintStack() + os.Exit(1) + } +} + // RunWith wrap cli function with an error handler instead of having the code exit early. func RunWith( fn func(cmd *cobra.Command, args []string) error, ) func(cmd *cobra.Command, args []string) { return func(cmd *cobra.Command, args []string) { + defer exitOnPanic() if err := fn(cmd, args); err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) diff --git a/libbeat/common/cli/cli_test.go b/libbeat/common/cli/cli_test.go index c39b95ae6e1f..fb4812d57fe2 100644 --- a/libbeat/common/cli/cli_test.go +++ b/libbeat/common/cli/cli_test.go @@ -11,51 +11,69 @@ import ( "github.com/stretchr/testify/assert" ) -func funcWithError() { - var cmd *cobra.Command - var args []string - RunWith(func(cmd *cobra.Command, args []string) error { - return fmt.Errorf("Something bad") - })(cmd, args) -} +func runCli(testName string) (*bytes.Buffer, error) { + cmd := exec.Command(os.Args[0], "-test.run="+testName) + cmd.Env = append(os.Environ(), "TEST_RUNWITH=1") + stderr := new(bytes.Buffer) + cmd.Stderr = stderr -func funcWithoutError() { - var cmd *cobra.Command - var args []string - RunWith(func(cmd *cobra.Command, args []string) error { - return nil - })(cmd, args) + err := cmd.Run() + return stderr, err } // Example taken from slides from Andrew Gerrand // https://talks.golang.org/2014/testing.slide#23 func TestExitWithError(t *testing.T) { if os.Getenv("TEST_RUNWITH") == "1" { - funcWithError() - return + func() { + var cmd *cobra.Command + var args []string + RunWith(func(cmd *cobra.Command, args []string) error { + return fmt.Errorf("Something bad") + })(cmd, args) + return + }() } - cmd := exec.Command(os.Args[0], "-test.run=TestExitWithError") - cmd.Env = append(os.Environ(), "TEST_RUNWITH=1") - bufError := new(bytes.Buffer) - cmd.Stderr = bufError - err := cmd.Run() + + stderr, err := runCli("TestExitWithError") if assert.Error(t, err) { assert.Equal(t, err.Error(), "exit status 1") } - assert.Equal(t, "Something bad\n", bufError.String()) + assert.Equal(t, "Something bad\n", stderr.String()) } func TestExitWithoutError(t *testing.T) { if os.Getenv("TEST_RUNWITH") == "1" { - funcWithoutError() + func() { + var cmd *cobra.Command + var args []string + RunWith(func(cmd *cobra.Command, args []string) error { + return nil + })(cmd, args) + }() return } - cmd := exec.Command(os.Args[0], "-test.run=TestExitWithoutError") - cmd.Env = append(os.Environ(), "TEST_RUNWITH=1") - bufError := new(bytes.Buffer) - cmd.Stderr = bufError - err := cmd.Run() + stderr, err := runCli("TestExitWithoutError") assert.NoError(t, err) - assert.Equal(t, "", bufError.String()) + assert.Equal(t, "", stderr.String()) +} + +func TestExitWithPanic(t *testing.T) { + if os.Getenv("TEST_RUNWITH") == "1" { + func() { + var cmd *cobra.Command + var args []string + RunWith(func(cmd *cobra.Command, args []string) error { + panic("something really bad happened") + })(cmd, args) + }() + return + } + + stderr, err := runCli("TestExitWithPanic") + if assert.Error(t, err) { + assert.Equal(t, err.Error(), "exit status 1") + } + assert.Contains(t, stderr.String(), "something really bad happened") }