From 4672ded0433e0610c247f7b06ef856f0453ccca7 Mon Sep 17 00:00:00 2001 From: Yann Hamon Date: Sat, 14 Nov 2020 16:23:33 +0100 Subject: [PATCH] only expose interfac --- cmd/kubeconform/main.go | 8 ++- pkg/validator/validator.go | 117 +++++++++++++------------------- pkg/validator/validator_test.go | 4 +- 3 files changed, 57 insertions(+), 72 deletions(-) diff --git a/cmd/kubeconform/main.go b/cmd/kubeconform/main.go index e9f1a71..0efa36d 100644 --- a/cmd/kubeconform/main.go +++ b/cmd/kubeconform/main.go @@ -90,7 +90,7 @@ func realMain() int { wg := sync.WaitGroup{} for i := 0; i < cfg.NumberOfWorkers; i++ { wg.Add(1) - go func(resources <-chan resource.Resource, validationResults chan<- validator.Result, v *validator.Validator) { + go func(resources <-chan resource.Resource, validationResults chan<- validator.Result, v validator.Validator) { for res := range resources { validationResults <- v.Validate(res) } @@ -106,7 +106,11 @@ func realMain() int { } if err, ok := err.(resource.DiscoveryError); ok { - validationResults <- validator.NewError(err.Path, err.Err) + validationResults <- validator.Result{ + Resource: resource.Resource{Path: err.Path}, + Err: err.Err, + Status: validator.Error, + } ctx.Done() } } diff --git a/pkg/validator/validator.go b/pkg/validator/validator.go index 12d451e..de84fba 100644 --- a/pkg/validator/validator.go +++ b/pkg/validator/validator.go @@ -21,11 +21,15 @@ const ( Empty ) -type Validator struct { - opts Opts - schemaCache *cache.SchemaCache - schemaDownload func(registries []registry.Registry, kind, version, k8sVersion string) (*gojsonschema.Schema, error) - regs []registry.Registry +// Result contains the details of the result of a resource validation +type Result struct { + Resource resource.Resource + Err error + Status Status +} + +type Validator interface { + Validate(res resource.Resource) Result } type Opts struct { @@ -37,28 +41,7 @@ type Opts struct { IgnoreMissingSchemas bool } -func downloadSchema(registries []registry.Registry, kind, version, k8sVersion string) (*gojsonschema.Schema, error) { - var err error - var schemaBytes []byte - - for _, reg := range registries { - schemaBytes, err = reg.DownloadSchema(kind, version, k8sVersion) - if err == nil { - return gojsonschema.NewSchema(gojsonschema.NewBytesLoader(schemaBytes)) - } - - // If we get a 404, we try the next registry, but we exit if we get a real failure - if _, notfound := err.(*registry.NotFoundError); notfound { - continue - } - - return nil, err - } - - return nil, nil // No schema found - we don't consider it an error, resource will be skipped -} - -func New(schemaLocations []string, opts Opts) *Validator { +func New(schemaLocations []string, opts Opts) Validator { registries := []registry.Registry{} for _, schemaLocation := range schemaLocations { registries = append(registries, registry.New(schemaLocation, opts.Strict, opts.SkipTLS)) @@ -71,7 +54,7 @@ func New(schemaLocations []string, opts Opts) *Validator { opts.RejectKinds = map[string]bool{} } - return &Validator{ + return &v{ opts: opts, schemaDownload: downloadSchema, schemaCache: cache.New(), @@ -79,14 +62,21 @@ func New(schemaLocations []string, opts Opts) *Validator { } } -func (v *Validator) Validate(res resource.Resource) Result { +type v struct { + opts Opts + schemaCache *cache.SchemaCache + schemaDownload func(registries []registry.Registry, kind, version, k8sVersion string) (*gojsonschema.Schema, error) + regs []registry.Registry +} + +func (val *v) Validate(res resource.Resource) Result { skip := func(signature resource.Signature) bool { - isSkipKind, ok := v.opts.SkipKinds[signature.Kind] + isSkipKind, ok := val.opts.SkipKinds[signature.Kind] return ok && isSkipKind } reject := func(signature resource.Signature) bool { - _, ok := v.opts.RejectKinds[signature.Kind] + _, ok := val.opts.RejectKinds[signature.Kind] return ok } @@ -111,38 +101,34 @@ func (v *Validator) Validate(res resource.Resource) Result { var schema *gojsonschema.Schema cacheKey := "" - if v.schemaCache != nil { - cacheKey = cache.Key(sig.Kind, sig.Version, v.opts.KubernetesVersion) - schema, cached = v.schemaCache.Get(cacheKey) + if val.schemaCache != nil { + cacheKey = cache.Key(sig.Kind, sig.Version, val.opts.KubernetesVersion) + schema, cached = val.schemaCache.Get(cacheKey) } if !cached { - if schema, err = v.schemaDownload(v.regs, sig.Kind, sig.Version, v.opts.KubernetesVersion); err != nil { + if schema, err = val.schemaDownload(val.regs, sig.Kind, sig.Version, val.opts.KubernetesVersion); err != nil { return Result{Resource: res, Err: err, Status: Error} } - if v.schemaCache != nil { - v.schemaCache.Set(cacheKey, schema) + if val.schemaCache != nil { + val.schemaCache.Set(cacheKey, schema) } } if schema == nil { - if v.opts.IgnoreMissingSchemas { + if val.opts.IgnoreMissingSchemas { return Result{Resource: res, Err: nil, Status: Skipped} } else { return Result{Resource: res, Err: fmt.Errorf("could not find schema for %s", sig.Kind), Status: Error} } } - if schema == nil { - return Result{Resource: res, Status: Skipped, Err: nil} - } - - var resource map[string]interface{} - if err := yaml.Unmarshal(res.Bytes, &resource); err != nil { + var r map[string]interface{} + if err := yaml.Unmarshal(res.Bytes, &r); err != nil { return Result{Resource: res, Status: Error, Err: fmt.Errorf("error unmarshalling resource: %s", err)} } - resourceLoader := gojsonschema.NewGoLoader(resource) + resourceLoader := gojsonschema.NewGoLoader(r) results, err := schema.Validate(resourceLoader) if err != nil { @@ -165,14 +151,25 @@ func (v *Validator) Validate(res resource.Resource) Result { return Result{Resource: res, Status: Invalid, Err: fmt.Errorf("%s", msg)} } -// ValidFormat is a type for quickly forcing -// new formats on the gojsonschema loader -type ValidFormat struct{} +func downloadSchema(registries []registry.Registry, kind, version, k8sVersion string) (*gojsonschema.Schema, error) { + var err error + var schemaBytes []byte -// ValidFormat is a type for quickly forcing -// new formats on the gojsonschema loader -func (f ValidFormat) IsFormat(input interface{}) bool { - return true + for _, reg := range registries { + schemaBytes, err = reg.DownloadSchema(kind, version, k8sVersion) + if err == nil { + return gojsonschema.NewSchema(gojsonschema.NewBytesLoader(schemaBytes)) + } + + // If we get a 404, we try the next registry, but we exit if we get a real failure + if _, notfound := err.(*registry.NotFoundError); notfound { + continue + } + + return nil, err + } + + return nil, nil // No schema found - we don't consider it an error, resource will be skipped } // From kubeval - let's see if absolutely necessary @@ -182,19 +179,3 @@ func (f ValidFormat) IsFormat(input interface{}) bool { // gojsonschema.FormatCheckers.Add("int32", ValidFormat{}) // gojsonschema.FormatCheckers.Add("int-or-string", ValidFormat{}) // } - -// Result contains the details of the result of a resource validation -type Result struct { - Resource resource.Resource - Err error - Status Status -} - -// NewError is a utility function to generate a validation error -func NewError(filename string, err error) Result { - return Result{ - Resource: resource.Resource{Path: filename}, - Err: err, - Status: Error, - } -} diff --git a/pkg/validator/validator_test.go b/pkg/validator/validator_test.go index 2872fec..c70a1af 100644 --- a/pkg/validator/validator_test.go +++ b/pkg/validator/validator_test.go @@ -134,7 +134,7 @@ lastName: bar Error, }, } { - v := Validator{ + val := v{ opts: Opts{ SkipKinds: map[string]bool{}, RejectKinds: map[string]bool{}, @@ -149,7 +149,7 @@ lastName: bar }, regs: nil, } - if got := v.Validate(resource.Resource{Bytes: testCase.rawResource}); got.Status != testCase.expect { + if got := val.Validate(resource.Resource{Bytes: testCase.rawResource}); got.Status != testCase.expect { t.Errorf("%d - expected %d, got %d", i, testCase.expect, got.Status) } }