Skip to content

Commit

Permalink
add json output formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
Brendan Ryan committed Aug 2, 2019
1 parent 22db812 commit 01f2f19
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 7 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ require (
github.com/spf13/jwalterweatherman v0.0.0-20180814060501-14d3d4c51834 // indirect
github.com/spf13/pflag v0.0.0-20180821114517-d929dcbb1086 // indirect
github.com/spf13/viper v1.1.0
github.com/stretchr/testify v1.3.0 // indirect
github.com/stretchr/testify v1.3.0
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v0.0.0-20180816142147-da425ebb7609
Expand Down
101 changes: 95 additions & 6 deletions kubeval/output.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package kubeval

import (
"github.com/instrumenta/kubeval/log"
"bytes"
"encoding/json"
"log"
"os"

kLog "github.com/instrumenta/kubeval/log"
)

// TODO (brendanryan) move these structs to `/log` once we have removed the potential
Expand All @@ -25,16 +30,16 @@ func NewSTDOutputManager() *STDOutputManager {

func (s *STDOutputManager) Put(result ValidationResult) error {
if len(result.Errors) > 0 {
log.Warn("The file", result.FileName, "contains an invalid", result.Kind)
kLog.Warn("The file", result.FileName, "contains an invalid", result.Kind)
for _, desc := range result.Errors {
log.Info("--->", desc)
kLog.Info("--->", desc)
}
} else if result.Kind == "" {
log.Success("The file", result.FileName, "contains an empty YAML document")
kLog.Success("The file", result.FileName, "contains an empty YAML document")
} else if !result.ValidatedAgainstSchema {
log.Warn("The file", result.FileName, "containing a", result.Kind, "was not validated against a schema")
kLog.Warn("The file", result.FileName, "containing a", result.Kind, "was not validated against a schema")
} else {
log.Success("The file", result.FileName, "contains a valid", result.Kind)
kLog.Success("The file", result.FileName, "contains a valid", result.Kind)
}

return nil
Expand All @@ -44,3 +49,87 @@ func (s *STDOutputManager) Flush() error {
// no op
return nil
}

type status string

const (
statusInvalid = "invalid"
statusValid = "valid"
statusSkipped = "skipped"
)

type jsonEvalResult struct {
Filename string `json:"filename"`
Kind string `json:"kind"`
Status status `json:"status"`
Errors []string `json:"errors"`
}

// jsonOutputManager reports `ccheck` results to `stdout` as a json array..
type jsonOutputManager struct {
logger *log.Logger

data []jsonEvalResult
}

func newDefaultJSONOutputManager() *jsonOutputManager {
return newJSONOutputManager(log.New(os.Stdout, "", 0))
}

func newJSONOutputManager(l *log.Logger) *jsonOutputManager {
return &jsonOutputManager{
logger: l,
}
}

func getStatus(r ValidationResult) status {
if r.Kind == "" {
return statusSkipped
}

if !r.ValidatedAgainstSchema {
return statusSkipped
}

if len(r.Errors) > 0 {
return statusInvalid
}

return statusValid
}

func (j *jsonOutputManager) put(r ValidationResult) error {

// stringify gojsonschema errors
// use a pre-allocated slice to ensure the json will have an
// empty array in the "zero" case
errs := make([]string, 0, len(r.Errors))
for _, e := range r.Errors {
errs = append(errs, e.String())
}

j.data = append(j.data, jsonEvalResult{
Filename: r.FileName,
Kind: r.Kind,
Status: getStatus(r),
Errors: errs,
})

return nil
}

func (j *jsonOutputManager) flush() error {
b, err := json.Marshal(j.data)
if err != nil {
return err
}

var out bytes.Buffer
err = json.Indent(&out, b, "", "\t")
if err != nil {
return err
}

j.logger.Print(out.String())
return nil
}
123 changes: 123 additions & 0 deletions kubeval/output_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package kubeval

import (
"bytes"
"log"
"testing"

"github.com/xeipuuv/gojsonschema"

"github.com/stretchr/testify/assert"
)

func newResultError(msg string) gojsonschema.ResultError {
r := &gojsonschema.ResultErrorFields{}

r.SetContext(gojsonschema.NewJsonContext("error", nil))
r.SetDescription(msg)

return r
}

func newResultErrors(msgs []string) []gojsonschema.ResultError {
var res []gojsonschema.ResultError
for _, m := range msgs {
res = append(res, newResultError(m))
}
return res
}

func Test_jsonOutputManager_put(t *testing.T) {
type args struct {
vr ValidationResult
}

tests := []struct {
msg string
args args
exp string
expErr error
}{
{
msg: "empty input",
args: args{
vr: ValidationResult{},
},
exp: `[
{
"filename": "",
"kind": "",
"status": "skipped",
"errors": []
}
]
`,
},
{
msg: "file with no errors",
args: args{
vr: ValidationResult{
FileName: "deployment.yaml",
Kind: "deployment",
ValidatedAgainstSchema: true,
Errors: nil,
},
},
exp: `[
{
"filename": "deployment.yaml",
"kind": "deployment",
"status": "valid",
"errors": []
}
]
`,
},
{
msg: "file with errors",
args: args{
vr: ValidationResult{
FileName: "service.yaml",
Kind: "service",
ValidatedAgainstSchema: true,
Errors: newResultErrors([]string{
"i am a error",
"i am another error",
}),
},
},
exp: `[
{
"filename": "service.yaml",
"kind": "service",
"status": "invalid",
"errors": [
"error: i am a error",
"error: i am another error"
]
}
]
`,
},
}
for _, tt := range tests {
t.Run(tt.msg, func(t *testing.T) {
buf := new(bytes.Buffer)
s := newJSONOutputManager(log.New(buf, "", 0))

// record results
err := s.put(tt.args.vr)
if err != nil {
assert.Equal(t, tt.expErr, err)
}

// flush final buffer
err = s.flush()
if err != nil {
assert.Equal(t, tt.expErr, err)
}

assert.Equal(t, tt.exp, buf.String())
})
}
}

0 comments on commit 01f2f19

Please sign in to comment.