Skip to content

Commit

Permalink
Merge pull request #8796 from priyawadhwa/error
Browse files Browse the repository at this point in the history
Add support for Error type to JSON output
  • Loading branch information
priyawadhwa authored Jul 22, 2020
2 parents f2100e3 + 3f18ecb commit 25566c9
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 27 deletions.
11 changes: 7 additions & 4 deletions pkg/minikube/exit/exit.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,26 @@ const (

// UsageT outputs a templated usage error and exits with error code 64
func UsageT(format string, a ...out.V) {
out.ErrT(out.Usage, format, a...)
out.ErrWithExitCode(out.Usage, format, BadUsage, a...)
os.Exit(BadUsage)
}

// WithCodeT outputs a templated fatal error message and exits with the supplied error code.
func WithCodeT(code int, format string, a ...out.V) {
out.FatalT(format, a...)
out.ErrWithExitCode(out.FatalType, format, code, a...)
os.Exit(code)
}

// WithError outputs an error and exits.
func WithError(msg string, err error) {
glog.Infof("WithError(%s)=%v called from:\n%s", msg, err, debug.Stack())
p := problem.FromError(err, runtime.GOOS)
if p != nil {
if p != nil && out.JSON {
p.DisplayJSON(Config)
os.Exit(Config)
} else {
WithProblem(msg, err, p)
os.Exit(Config)
}
out.DisplayError(msg, err)
os.Exit(Software)
Expand All @@ -74,5 +78,4 @@ func WithProblem(msg string, err error, p *problem.Problem) {
out.ErrT(out.Sad, "If the above advice does not help, please let us know: ")
out.ErrT(out.URL, "https://github.com/kubernetes/minikube/issues/new/choose")
}
os.Exit(Config)
}
20 changes: 19 additions & 1 deletion pkg/minikube/out/out.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ func Ln(format string, a ...interface{}) {
String(format+"\n", a...)
}

// ErrWithExitCode includes the exit code in JSON output
func ErrWithExitCode(style StyleEnum, format string, exitcode int, a ...V) {
if JSON {
errStyled := ApplyTemplateFormatting(style, useColor, format, a...)
register.PrintErrorExitCode(errStyled, exitcode)
return
}
ErrT(style, format, a...)
}

// ErrT writes a stylized and templated error message to stderr
func ErrT(style StyleEnum, format string, a ...V) {
errStyled := ApplyTemplateFormatting(style, useColor, format, a...)
Expand All @@ -123,6 +133,10 @@ func ErrT(style StyleEnum, format string, a ...V) {

// Err writes a basic formatted string to stderr
func Err(format string, a ...interface{}) {
if JSON {
register.PrintError(format)
return
}
if errFile == nil {
glog.Errorf("[unset errFile]: %s", fmt.Sprintf(format, a...))
return
Expand Down Expand Up @@ -232,8 +246,12 @@ func LogEntries(msg string, err error, entries map[string][]string) {

// DisplayError prints the error and displays the standard minikube error messaging
func DisplayError(msg string, err error) {
// use Warning because Error will display a duplicate message to stderr
glog.Warningf(fmt.Sprintf("%s: %v", msg, err))
if JSON {
FatalT("{{.msg}}: {{.err}}", V{"msg": translate.T(msg), "err": err})
return
}
// use Warning because Error will display a duplicate message to stderr
ErrT(Empty, "")
FatalT("{{.msg}}: {{.err}}", V{"msg": translate.T(msg), "err": err})
ErrT(Empty, "")
Expand Down
8 changes: 4 additions & 4 deletions pkg/minikube/out/register/cloud_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ const (
)

var (
outputFile io.Writer = os.Stdout
getUUID = randomID
OutputFile io.Writer = os.Stdout
GetUUID = randomID
)

func printAsCloudEvent(log Log, data map[string]string) {
Expand All @@ -43,12 +43,12 @@ func printAsCloudEvent(log Log, data map[string]string) {
if err := event.SetData(cloudevents.ApplicationJSON, data); err != nil {
glog.Warningf("error setting data: %v", err)
}
event.SetID(getUUID())
event.SetID(GetUUID())
json, err := event.MarshalJSON()
if err != nil {
glog.Warningf("error marashalling event: %v", err)
}
fmt.Fprintln(outputFile, string(json))
fmt.Fprintln(OutputFile, string(json))
}

func randomID() string {
Expand Down
12 changes: 12 additions & 0 deletions pkg/minikube/out/register/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ func PrintDownloadProgress(artifact, progress string) {
printAsCloudEvent(s, s.data)
}

// PrintError prints an Error type in JSON format
func PrintError(err string) {
e := NewError(err)
printAsCloudEvent(e, e.data)
}

// PrintErrorExitCode prints an error in JSON format and includes an exit code
func PrintErrorExitCode(err string, exitcode int, additionalArgs ...map[string]string) {
e := NewErrorExitCode(err, exitcode, additionalArgs...)
printAsCloudEvent(e, e.data)
}

// PrintWarning prints a Warning type in JSON format
func PrintWarning(warning string) {
w := NewWarning(warning)
Expand Down
56 changes: 47 additions & 9 deletions pkg/minikube/out/register/json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ func TestPrintStep(t *testing.T) {
expected += "\n"

buf := bytes.NewBuffer([]byte{})
outputFile = buf
defer func() { outputFile = os.Stdout }()
OutputFile = buf
defer func() { OutputFile = os.Stdout }()

getUUID = func() string {
GetUUID = func() string {
return "random-id"
}

Expand All @@ -49,10 +49,10 @@ func TestPrintInfo(t *testing.T) {
expected += "\n"

buf := bytes.NewBuffer([]byte{})
outputFile = buf
defer func() { outputFile = os.Stdout }()
OutputFile = buf
defer func() { OutputFile = os.Stdout }()

getUUID = func() string {
GetUUID = func() string {
return "random-id"
}

Expand All @@ -64,15 +64,53 @@ func TestPrintInfo(t *testing.T) {
}
}

func TestError(t *testing.T) {
expected := `{"data":{"message":"error"},"datacontenttype":"application/json","id":"random-id","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.error"}`
expected += "\n"

buf := bytes.NewBuffer([]byte{})
OutputFile = buf
defer func() { OutputFile = os.Stdout }()

GetUUID = func() string {
return "random-id"
}

PrintError("error")
actual := buf.String()

if actual != expected {
t.Fatalf("expected didn't match actual:\nExpected:\n%v\n\nActual:\n%v", expected, actual)
}
}

func TestErrorExitCode(t *testing.T) {
expected := `{"data":{"a":"b","c":"d","exitcode":"5","message":"error"},"datacontenttype":"application/json","id":"random-id","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.error"}`
expected += "\n"

buf := bytes.NewBuffer([]byte{})
OutputFile = buf
defer func() { OutputFile = os.Stdout }()

GetUUID = func() string {
return "random-id"
}

PrintErrorExitCode("error", 5, map[string]string{"a": "b"}, map[string]string{"c": "d"})
actual := buf.String()
if actual != expected {
t.Fatalf("expected didn't match actual:\nExpected:\n%v\n\nActual:\n%v", expected, actual)
}
}
func TestWarning(t *testing.T) {
expected := `{"data":{"message":"warning"},"datacontenttype":"application/json","id":"random-id","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.warning"}`
expected += "\n"

buf := bytes.NewBuffer([]byte{})
outputFile = buf
defer func() { outputFile = os.Stdout }()
OutputFile = buf
defer func() { OutputFile = os.Stdout }()

getUUID = func() string {
GetUUID = func() string {
return "random-id"
}

Expand Down
23 changes: 23 additions & 0 deletions pkg/minikube/out/register/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ limitations under the License.

package register

import "fmt"

// Log represents the different types of logs that can be output as JSON
// This includes: Step, Download, DownloadProgress, Warning, Info, Error
type Log interface {
Expand Down Expand Up @@ -120,6 +122,27 @@ func NewInfo(message string) *Info {

// Error will be used to notify the user of errors
type Error struct {
data map[string]string
}

func NewError(err string) *Error {
return &Error{
map[string]string{
"message": err,
},
}
}

// NewErrorExitCode returns an error that has an associated exit code
func NewErrorExitCode(err string, exitcode int, additionalData ...map[string]string) *Error {
e := NewError(err)
e.data["exitcode"] = fmt.Sprintf("%v", exitcode)
for _, a := range additionalData {
for k, v := range a {
e.data[k] = v
}
}
return e
}

func (s *Error) Type() string {
Expand Down
6 changes: 3 additions & 3 deletions pkg/minikube/out/register/register_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ func TestSetCurrentStep(t *testing.T) {
expected += "\n"

buf := bytes.NewBuffer([]byte{})
outputFile = buf
defer func() { outputFile = os.Stdout }()
OutputFile = buf
defer func() { OutputFile = os.Stdout }()

getUUID = func() string {
GetUUID = func() string {
return "random-id"
}

Expand Down
16 changes: 16 additions & 0 deletions pkg/minikube/problem/problem.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"regexp"

"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
"k8s.io/minikube/pkg/minikube/translate"
)

Expand Down Expand Up @@ -80,6 +81,21 @@ func (p *Problem) Display() {
}
}

// DisplayJSON displays problem metadata in JSON format
func (p *Problem) DisplayJSON(exitcode int) {
var issues string
for _, i := range p.Issues {
issues += fmt.Sprintf("https://github.com/kubernetes/minikube/issues/%v,", i)
}
extraArgs := map[string]string{
"name": p.ID,
"advice": p.Advice,
"url": p.URL,
"issues": issues,
}
register.PrintErrorExitCode(p.Err.Error(), exitcode, extraArgs)
}

// FromError returns a known problem from an error on an OS
func FromError(err error, goos string) *Problem {
maps := []map[string]match{
Expand Down
41 changes: 41 additions & 0 deletions pkg/minikube/problem/problem_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ package problem
import (
"bytes"
"fmt"
"os"
"strings"
"testing"

"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/out/register"
)

type buffFd struct {
Expand Down Expand Up @@ -96,6 +98,45 @@ func TestDisplay(t *testing.T) {
}
}

func TestDisplayJSON(t *testing.T) {
defer out.SetJSON(false)
out.SetJSON(true)

tcs := []struct {
p *Problem
expected string
}{
{
p: &Problem{
Err: fmt.Errorf("my error"),
Advice: "fix me!",
Issues: []int{1, 2},
URL: "url",
ID: "BUG",
},
expected: `{"data":{"advice":"fix me!","exitcode":"4","issues":"https://github.com/kubernetes/minikube/issues/1,https://github.com/kubernetes/minikube/issues/2,","message":"my error","name":"BUG","url":"url"},"datacontenttype":"application/json","id":"random-id","source":"https://minikube.sigs.k8s.io/","specversion":"1.0","type":"io.k8s.sigs.minikube.error"}
`,
},
}
for _, tc := range tcs {
t.Run(tc.p.ID, func(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
register.OutputFile = buf
defer func() { register.OutputFile = os.Stdout }()

register.GetUUID = func() string {
return "random-id"
}

tc.p.DisplayJSON(4)
actual := buf.String()
if actual != tc.expected {
t.Fatalf("expected didn't match actual:\nExpected:\n%v\n\nActual:\n%v", tc.expected, actual)
}
})
}
}

func TestFromError(t *testing.T) {
var tests = []struct {
issue int
Expand Down
Loading

0 comments on commit 25566c9

Please sign in to comment.