diff --git a/pkg/output/json.go b/pkg/output/json.go index 872d22c..5778065 100644 --- a/pkg/output/json.go +++ b/pkg/output/json.go @@ -9,12 +9,13 @@ import ( ) type oresult struct { - Filename string `json:"filename"` - Kind string `json:"kind"` - Name string `json:"name"` - Version string `json:"version"` - Status string `json:"status"` - Msg string `json:"msg"` + Filename string `json:"filename"` + Kind string `json:"kind"` + Name string `json:"name"` + Version string `json:"version"` + Status string `json:"status"` + Msg string `json:"msg"` + ValidationErrors []validator.ValidationError `json:"validationErrors,omitempty"` } type jsono struct { @@ -49,11 +50,15 @@ func (o *jsono) Write(result validator.Result) error { o.nValid++ case validator.Invalid: st = "statusInvalid" - msg = result.Err.Error() + if result.Err != nil { + msg = result.Err.Error() + } o.nInvalid++ case validator.Error: st = "statusError" - msg = result.Err.Error() + if result.Err != nil { + msg = result.Err.Error() + } o.nErrors++ case validator.Skipped: st = "statusSkipped" @@ -63,7 +68,15 @@ func (o *jsono) Write(result validator.Result) error { if o.verbose || (result.Status != validator.Valid && result.Status != validator.Skipped && result.Status != validator.Empty) { sig, _ := result.Resource.Signature() - o.results = append(o.results, oresult{Filename: result.Resource.Path, Kind: sig.Kind, Name: sig.Name, Version: sig.Version, Status: st, Msg: msg}) + o.results = append(o.results, oresult{ + Filename: result.Resource.Path, + Kind: sig.Kind, + Name: sig.Name, + Version: sig.Version, + Status: st, + Msg: msg, + ValidationErrors: result.ValidationErrors, + }) } return nil diff --git a/pkg/output/json_test.go b/pkg/output/json_test.go index e5ce27d..0ed4ad4 100644 --- a/pkg/output/json_test.go +++ b/pkg/output/json_test.go @@ -93,6 +93,60 @@ metadata: "skipped": 0 } } +`, + }, + { + "a single invalid deployment, verbose, with summary", + true, + false, + true, + []validator.Result{ + { + Resource: resource.Resource{ + Path: "deployment.yml", + Bytes: []byte(`apiVersion: apps/v1 +kind: Deployment +metadata: + name: "my-app" +`), + }, + Status: validator.Invalid, + Err: &validator.ValidationError{ + Path: "foo", + Msg: "bar", + }, + ValidationErrors: []validator.ValidationError{ + { + Path: "foo", + Msg: "bar", + }, + }, + }, + }, + `{ + "resources": [ + { + "filename": "deployment.yml", + "kind": "Deployment", + "name": "my-app", + "version": "apps/v1", + "status": "statusInvalid", + "msg": "bar", + "validationErrors": [ + { + "path": "foo", + "msg": "bar" + } + ] + } + ], + "summary": { + "valid": 0, + "invalid": 1, + "errors": 0, + "skipped": 0 + } +} `, }, } { diff --git a/pkg/validator/validator.go b/pkg/validator/validator.go index 34b9036..c7a430d 100644 --- a/pkg/validator/validator.go +++ b/pkg/validator/validator.go @@ -3,6 +3,7 @@ package validator import ( "context" + "errors" "fmt" "io" @@ -26,11 +27,21 @@ const ( Empty // resource is empty. Note: is triggered for files starting with a --- separator. ) +type ValidationError struct { + Path string `json:"path"` + Msg string `json:"msg"` +} + +func (ve *ValidationError) Error() string { + return ve.Msg +} + // Result contains the details of the result of a resource validation type Result struct { - Resource resource.Resource - Err error - Status Status + Resource resource.Resource + Err error + Status Status + ValidationErrors []ValidationError } // Validator exposes multiple methods to validate your Kubernetes resources. @@ -181,7 +192,23 @@ func (val *v) ValidateResource(res resource.Resource) Result { err = schema.Validate(r) if err != nil { - return Result{Resource: res, Status: Invalid, Err: fmt.Errorf("problem validating schema. Check JSON formatting: %s", err)} + validationErrors := []ValidationError{} + var e *jsonschema.ValidationError + if errors.As(err, &e) { + for _, ve := range e.Causes { + validationErrors = append(validationErrors, ValidationError{ + Path: ve.KeywordLocation, + Msg: ve.Message, + }) + } + + } + return Result{ + Resource: res, + Status: Invalid, + Err: fmt.Errorf("problem validating schema. Check JSON formatting: %s", err), + ValidationErrors: validationErrors, + } } return Result{Resource: res, Status: Valid}