Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Error type to JSON output #8796

Merged
merged 7 commits into from
Jul 22, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
22 changes: 22 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,26 @@ 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,
},
}
}

func NewErrorExitCode(err string, exitcode int, additionalData ...map[string]string) *Error {
priyawadhwa marked this conversation as resolved.
Show resolved Hide resolved
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