better logic mgmt in output plugins, go fmt

This commit is contained in:
Yann Hamon 2020-05-31 02:10:19 +02:00
parent 224e9ca17d
commit 8eb297d4c4
11 changed files with 93 additions and 79 deletions

11
main.go
View file

@ -26,7 +26,7 @@ type validationResult struct {
// filter returns true if the file should be skipped // filter returns true if the file should be skipped
// Returning an array, this Reader might container multiple resources // Returning an array, this Reader might container multiple resources
func validateFile(f io.Reader, regs []registry.Registry, k8sVersion string, skip func(signature resource.Signature)bool) []validationResult { func validateFile(f io.Reader, regs []registry.Registry, k8sVersion string, skip func(signature resource.Signature) bool) []validationResult {
rawResource, err := ioutil.ReadAll(f) rawResource, err := ioutil.ReadAll(f)
if err != nil { if err != nil {
return []validationResult{{err: fmt.Errorf("failed reading file: %s", err)}} return []validationResult{{err: fmt.Errorf("failed reading file: %s", err)}}
@ -99,13 +99,13 @@ func realMain() int {
} }
splitKinds := strings.Split(skipKinds, ",") splitKinds := strings.Split(skipKinds, ",")
kinds := map[string]bool {} kinds := map[string]bool{}
for _, kind := range splitKinds { for _, kind := range splitKinds {
kinds[kind] = true kinds[kind] = true
} }
filter := func(signature resource.Signature) bool { filter := func(signature resource.Signature) bool {
isSkipKind, ok := kinds[signature.Kind] isSkipKind, ok := kinds[signature.Kind]
return ok && isSkipKind return ok && isSkipKind
} }
registries := []registry.Registry{} registries := []registry.Registry{}
@ -135,7 +135,7 @@ func realMain() int {
}() }()
var wg sync.WaitGroup var wg sync.WaitGroup
for i:=0; i<nWorkers; i++ { for i := 0; i < nWorkers; i++ {
wg.Add(1) wg.Add(1)
go func() { go func() {
defer wg.Done() defer wg.Done()
@ -151,7 +151,6 @@ func realMain() int {
res := validateFile(f, registries, k8sVersion, filter) res := validateFile(f, registries, k8sVersion, filter)
f.Close() f.Close()
for _, resourceValidation := range res { for _, resourceValidation := range res {
o.Write(filename, resourceValidation.err, resourceValidation.skipped) o.Write(filename, resourceValidation.err, resourceValidation.skipped)
} }

6
pkg/cache/main.go vendored
View file

@ -10,15 +10,15 @@ import (
var mu sync.Mutex var mu sync.Mutex
var schemas map[string]*gojsonschema.Schema var schemas map[string]*gojsonschema.Schema
func init () { func init() {
schemas = map[string]*gojsonschema.Schema{} schemas = map[string]*gojsonschema.Schema{}
} }
func WithCache(downloadSchema func(string, string, string) (*gojsonschema.Schema, error)) (func (string, string, string) (*gojsonschema.Schema, error)) { func WithCache(downloadSchema func(string, string, string) (*gojsonschema.Schema, error)) func(string, string, string) (*gojsonschema.Schema, error) {
return func(resourceKind, resourceAPIVersion, k8sVersion string) (*gojsonschema.Schema, error) { return func(resourceKind, resourceAPIVersion, k8sVersion string) (*gojsonschema.Schema, error) {
cacheKey := fmt.Sprintf("%s-%s-%s", resourceKind, resourceAPIVersion, k8sVersion) cacheKey := fmt.Sprintf("%s-%s-%s", resourceKind, resourceAPIVersion, k8sVersion)
mu.Lock() mu.Lock()
cachedSchema, ok := schemas[cacheKey]; cachedSchema, ok := schemas[cacheKey]
mu.Unlock() mu.Unlock()
if ok { if ok {
return cachedSchema, nil return cachedSchema, nil

View file

@ -3,45 +3,44 @@ package output
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/yannh/kubeconform/pkg/validator"
) )
type result struct { type result struct {
Filename string `json:"filename"` Filename string `json:"filename"`
Status string `json:"status"` Status string `json:"status"`
Msg string `json:"msg"` Msg string `json:"msg"`
} }
type JSONOutput struct { type JSONOutput struct {
withSummary bool withSummary bool
results []result results []result
} }
func NewJSONOutput(withSummary bool) Output{ func NewJSONOutput(withSummary bool) Output {
return &JSONOutput{ return &JSONOutput{
withSummary: withSummary, withSummary: withSummary,
results: []result{}, results: []result{},
} }
} }
func (o *JSONOutput) Write(filename string,err error, skipped bool) { func (o *JSONOutput) Write(filename string, err error, skipped bool) {
status := "VALID" msg, st := "", ""
msg := ""
if err != nil { s := status(err, skipped)
switch {
case s == VALID:
st = "VALID"
case s == INVALID:
st = "INVALID"
msg = err.Error() msg = err.Error()
if _, ok := err.(validator.InvalidResourceError); ok { case s == ERROR:
status = "INVALID" st = "ERROR"
} else { msg = err.Error()
status = "ERROR" case s == SKIPPED:
} st = "SKIPPED"
} }
if skipped { o.results = append(o.results, result{Filename: filename, Status: st, Msg: msg})
status = "SKIPPED"
}
o.results = append(o.results, result{Filename: filename, Status: status, Msg: msg})
} }
func (o *JSONOutput) Flush() { func (o *JSONOutput) Flush() {
@ -51,13 +50,13 @@ func (o *JSONOutput) Flush() {
if o.withSummary { if o.withSummary {
jsonObj := struct { jsonObj := struct {
Resources []result `json:"resources"` Resources []result `json:"resources"`
Summary struct { Summary struct {
Valid int `json:"valid"` Valid int `json:"valid"`
Invalid int `json:"invalid"` Invalid int `json:"invalid"`
Errors int `json:"errors"` Errors int `json:"errors"`
Skipped int `json:"skipped"` Skipped int `json:"skipped"`
} `json:"summary"` } `json:"summary"`
} { }{
Resources: o.results, Resources: o.results,
} }
@ -74,15 +73,15 @@ func (o *JSONOutput) Flush() {
} }
} }
res, err = json.MarshalIndent(jsonObj,"", " ") res, err = json.MarshalIndent(jsonObj, "", " ")
} else { } else {
jsonObj := struct { jsonObj := struct {
Resources []result Resources []result
} { }{
Resources: o.results, Resources: o.results,
} }
res, err = json.MarshalIndent(jsonObj,"", " ") res, err = json.MarshalIndent(jsonObj, "", " ")
} }
if err != nil { if err != nil {

View file

@ -1,7 +1,31 @@
package output package output
import "github.com/yannh/kubeconform/pkg/validator"
const (
VALID = iota
INVALID = iota
ERROR = iota
SKIPPED = iota
)
type Output interface { type Output interface {
Write (filename string, err error, skipped bool) Write(filename string, err error, skipped bool)
Flush () Flush()
} }
func status(err error, skipped bool) int {
if err != nil {
if _, ok := err.(validator.InvalidResourceError); ok {
return INVALID
} else {
return ERROR
}
}
if skipped {
return SKIPPED
}
return VALID
}

View file

@ -2,39 +2,32 @@ package output
import ( import (
"fmt" "fmt"
"github.com/yannh/kubeconform/pkg/validator"
) )
type TextOutput struct { type TextOutput struct {
withSummary bool withSummary bool
nValid, nInvalid, nErrors, nSkipped int nValid, nInvalid, nErrors, nSkipped int
} }
func NewTextOutput(withSummary bool) Output { func NewTextOutput(withSummary bool) Output {
return &TextOutput{withSummary, 0,0,0, 0} return &TextOutput{withSummary, 0, 0, 0, 0}
} }
func (o *TextOutput) Write(filename string,err error, skipped bool) { func (o *TextOutput) Write(filename string, err error, skipped bool) {
if skipped { s := status(err, skipped)
fmt.Printf("skipping resource\n") switch {
o.nSkipped++ case s == VALID:
return
}
if err != nil {
if _, ok := err.(validator.InvalidResourceError); ok {
fmt.Printf("invalid resource: %s\n", err)
o.nInvalid++
} else {
fmt.Printf("failed validating resource in file %s: %s\n", filename, err)
o.nErrors++
}
return
}
if !skipped{
fmt.Printf("file %s is valid\n", filename) fmt.Printf("file %s is valid\n", filename)
o.nValid++ o.nValid++
case s == INVALID:
fmt.Printf("invalid resource: %s\n", err)
o.nInvalid++
case s == ERROR:
fmt.Printf("failed validating resource in file %s: %s\n", filename, err)
o.nErrors++
case s == SKIPPED:
fmt.Printf("skipping resource\n")
o.nSkipped++
} }
} }
@ -43,4 +36,3 @@ func (o *TextOutput) Flush() {
fmt.Printf("Run summary - Valid: %d, Invalid: %d, Errors: %d Skipped: %d\n", o.nValid, o.nInvalid, o.nErrors, o.nSkipped) fmt.Printf("Run summary - Valid: %d, Invalid: %d, Errors: %d Skipped: %d\n", o.nValid, o.nInvalid, o.nErrors, o.nSkipped)
} }
} }

View file

@ -17,11 +17,12 @@ type downloadError struct {
err error err error
isRetryable bool isRetryable bool
} }
func newDownloadError(err error, isRetryable bool) *downloadError{
func newDownloadError(err error, isRetryable bool) *downloadError {
return &downloadError{err, isRetryable} return &downloadError{err, isRetryable}
} }
func (e *downloadError) IsRetryable() bool { return e.isRetryable } func (e *downloadError) IsRetryable() bool { return e.isRetryable }
func (e *downloadError) Error() string { return e.err.Error() } func (e *downloadError) Error() string { return e.err.Error() }
func NewKubernetesRegistry(strict bool) *KubernetesRegistry { func NewKubernetesRegistry(strict bool) *KubernetesRegistry {
return &KubernetesRegistry{ return &KubernetesRegistry{
@ -61,7 +62,7 @@ func (r KubernetesRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8s
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound{ if resp.StatusCode == http.StatusNotFound {
return nil, newDownloadError(fmt.Errorf("no schema found"), false) return nil, newDownloadError(fmt.Errorf("no schema found"), false)
} }

View file

@ -3,9 +3,9 @@ package registry
import ( import (
"fmt" "fmt"
"github.com/xeipuuv/gojsonschema" "github.com/xeipuuv/gojsonschema"
"sigs.k8s.io/yaml"
"io/ioutil" "io/ioutil"
"os" "os"
"sigs.k8s.io/yaml"
"strings" "strings"
) )
@ -13,7 +13,6 @@ type LocalSchemas struct {
schemas map[string]*gojsonschema.Schema schemas map[string]*gojsonschema.Schema
} }
func NewLocalSchemas(schemaFiles []string) (*LocalSchemas, error) { func NewLocalSchemas(schemaFiles []string) (*LocalSchemas, error) {
schemas := &LocalSchemas{ schemas := &LocalSchemas{
schemas: map[string]*gojsonschema.Schema{}, schemas: map[string]*gojsonschema.Schema{},
@ -30,7 +29,7 @@ func NewLocalSchemas(schemaFiles []string) (*LocalSchemas, error) {
return nil, fmt.Errorf("failed to read schema %s", schemaFile) return nil, fmt.Errorf("failed to read schema %s", schemaFile)
} }
var parsedSchema struct{ var parsedSchema struct {
Spec struct { Spec struct {
Names struct { Names struct {
Kind string `json:"Kind"` Kind string `json:"Kind"`
@ -42,7 +41,7 @@ func NewLocalSchemas(schemaFiles []string) (*LocalSchemas, error) {
return nil, fmt.Errorf("failed parsing schema %s", schemaFile) return nil, fmt.Errorf("failed parsing schema %s", schemaFile)
} }
if strings.HasSuffix(schemaFile, ".yml") || strings.HasSuffix(schemaFile, ".yaml") { if strings.HasSuffix(schemaFile, ".yml") || strings.HasSuffix(schemaFile, ".yaml") {
asJSON, err := yaml.YAMLToJSON(content) asJSON, err := yaml.YAMLToJSON(content)
if err != nil { if err != nil {
return nil, fmt.Errorf("error converting manifest %s to JSON: %s", schemaFile, err) return nil, fmt.Errorf("error converting manifest %s to JSON: %s", schemaFile, err)

View file

@ -21,4 +21,3 @@ func SignatureFromBytes(s []byte) (Signature, error) {
return Signature{Kind: resource.Kind, Version: resource.APIVersion, Namespace: resource.Metadata.Namespace}, err return Signature{Kind: resource.Kind, Version: resource.APIVersion, Namespace: resource.Metadata.Namespace}, err
} }

View file

@ -6,14 +6,16 @@ import (
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
type InvalidResourceError struct { err string } type InvalidResourceError struct{ err string }
func (r InvalidResourceError) Error() string{
func (r InvalidResourceError) Error() string {
return r.err return r.err
} }
// ValidFormat is a type for quickly forcing // ValidFormat is a type for quickly forcing
// new formats on the gojsonschema loader // new formats on the gojsonschema loader
type ValidFormat struct{} type ValidFormat struct{}
func (f ValidFormat) IsFormat(input interface{}) bool { func (f ValidFormat) IsFormat(input interface{}) bool {
return true return true
} }
@ -40,13 +42,12 @@ func Validate(rawResource []byte, schema *gojsonschema.Schema) error {
results, err := schema.Validate(resourceLoader) results, err := schema.Validate(resourceLoader)
if err != nil { if err != nil {
// This error can only happen if the Object to validate is poorly formed. There's no hope of saving this one // This error can only happen if the Object to validate is poorly formed. There's no hope of saving this one
return fmt.Errorf("problem validating schema. Check JSON formatting: %s", err) return fmt.Errorf("problem validating schema. Check JSON formatting: %s", err)
} }
if !results.Valid() { if !results.Valid() {
return InvalidResourceError{err: fmt.Sprintf("resource does not conform to schema: %+v", results) } return InvalidResourceError{err: fmt.Sprintf("resource does not conform to schema: %+v", results)}
} }
return nil return nil
} }