mirror of
https://github.com/yannh/kubeconform.git
synced 2026-02-17 00:47:02 +00:00
refactor validator pkg so it can be usable in a third party app
This commit is contained in:
parent
94f8e9e631
commit
649c2ca4d6
3 changed files with 198 additions and 142 deletions
|
|
@ -3,94 +3,15 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/xeipuuv/gojsonschema"
|
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/yannh/kubeconform/pkg/cache"
|
|
||||||
"github.com/yannh/kubeconform/pkg/config"
|
"github.com/yannh/kubeconform/pkg/config"
|
||||||
"github.com/yannh/kubeconform/pkg/output"
|
"github.com/yannh/kubeconform/pkg/output"
|
||||||
"github.com/yannh/kubeconform/pkg/registry"
|
|
||||||
"github.com/yannh/kubeconform/pkg/resource"
|
"github.com/yannh/kubeconform/pkg/resource"
|
||||||
"github.com/yannh/kubeconform/pkg/validator"
|
"github.com/yannh/kubeconform/pkg/validator"
|
||||||
)
|
)
|
||||||
|
|
||||||
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 ValidateResources(resources <-chan resource.Resource, validationResults chan<- validator.Result, regs []registry.Registry, k8sVersion string, c *cache.SchemaCache, skip func(signature resource.Signature) bool, reject func(signature resource.Signature) bool, ignoreMissingSchemas bool) {
|
|
||||||
for res := range resources {
|
|
||||||
sig, err := res.Signature()
|
|
||||||
if err != nil {
|
|
||||||
validationResults <- validator.Result{Resource: res, Err: fmt.Errorf("error while parsing: %s", err), Status: validator.Error}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if sig.Kind == "" {
|
|
||||||
validationResults <- validator.Result{Resource: res, Err: nil, Status: validator.Empty}
|
|
||||||
continue // We skip resoures that don't have a Kind defined
|
|
||||||
}
|
|
||||||
|
|
||||||
if skip(*sig) {
|
|
||||||
validationResults <- validator.Result{Resource: res, Err: nil, Status: validator.Skipped}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if reject(*sig) {
|
|
||||||
validationResults <- validator.Result{Resource: res, Err: fmt.Errorf("prohibited resource kind %s", sig.Kind), Status: validator.Error}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
cached := false
|
|
||||||
var schema *gojsonschema.Schema
|
|
||||||
cacheKey := ""
|
|
||||||
|
|
||||||
if c != nil {
|
|
||||||
cacheKey = cache.Key(sig.Kind, sig.Version, k8sVersion)
|
|
||||||
schema, cached = c.Get(cacheKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !cached {
|
|
||||||
if schema, err = downloadSchema(regs, sig.Kind, sig.Version, k8sVersion); err != nil {
|
|
||||||
validationResults <- validator.Result{Resource: res, Err: err, Status: validator.Error}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if c != nil {
|
|
||||||
c.Set(cacheKey, schema)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if schema == nil {
|
|
||||||
if ignoreMissingSchemas {
|
|
||||||
validationResults <- validator.Result{Resource: res, Err: nil, Status: validator.Skipped}
|
|
||||||
} else {
|
|
||||||
validationResults <- validator.Result{Resource: res, Err: fmt.Errorf("could not find schema for %s", sig.Kind), Status: validator.Error}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
validationResults <- validator.Validate(res, schema)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func processResults(ctx context.Context, o output.Output, validationResults <-chan validator.Result, exitOnError bool) <-chan bool {
|
func processResults(ctx context.Context, o output.Output, validationResults <-chan validator.Result, exitOnError bool) <-chan bool {
|
||||||
success := true
|
success := true
|
||||||
result := make(chan bool)
|
result := make(chan bool)
|
||||||
|
|
@ -138,27 +59,21 @@ func realMain() int {
|
||||||
isStdin = true
|
isStdin = true
|
||||||
}
|
}
|
||||||
|
|
||||||
filter := func(signature resource.Signature) bool {
|
|
||||||
isSkipKind, ok := cfg.SkipKinds[signature.Kind]
|
|
||||||
return ok && isSkipKind
|
|
||||||
}
|
|
||||||
|
|
||||||
reject := func(signature resource.Signature) bool {
|
|
||||||
_, ok := cfg.RejectKinds[signature.Kind]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
registries := []registry.Registry{}
|
|
||||||
for _, schemaLocation := range cfg.SchemaLocations {
|
|
||||||
registries = append(registries, registry.New(schemaLocation, cfg.Strict, cfg.SkipTLS))
|
|
||||||
}
|
|
||||||
|
|
||||||
var o output.Output
|
var o output.Output
|
||||||
if o, err = output.New(cfg.OutputFormat, cfg.Summary, isStdin, cfg.Verbose); err != nil {
|
if o, err = output.New(cfg.OutputFormat, cfg.Summary, isStdin, cfg.Verbose); err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v := validator.New(cfg.SchemaLocations, &validator.Opts{
|
||||||
|
SkipTLS: cfg.SkipTLS,
|
||||||
|
SkipKinds: cfg.SkipKinds,
|
||||||
|
RejectKinds: cfg.RejectKinds,
|
||||||
|
KubernetesVersion: cfg.KubernetesVersion,
|
||||||
|
Strict: cfg.Strict,
|
||||||
|
IgnoreMissingSchemas: cfg.IgnoreMissingSchemas,
|
||||||
|
})
|
||||||
|
|
||||||
var resourcesChan <-chan resource.Resource
|
var resourcesChan <-chan resource.Resource
|
||||||
var errors <-chan error
|
var errors <-chan error
|
||||||
validationResults := make(chan validator.Result)
|
validationResults := make(chan validator.Result)
|
||||||
|
|
@ -172,14 +87,15 @@ func realMain() int {
|
||||||
resourcesChan, errors = resource.FromFiles(ctx, cfg.IgnoreFilenamePatterns, cfg.Files...)
|
resourcesChan, errors = resource.FromFiles(ctx, cfg.IgnoreFilenamePatterns, cfg.Files...)
|
||||||
}
|
}
|
||||||
|
|
||||||
c := cache.New()
|
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
for i := 0; i < cfg.NumberOfWorkers; i++ {
|
for i := 0; i < cfg.NumberOfWorkers; i++ {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func(resources <-chan resource.Resource, validationResults chan<- validator.Result, v *validator.Validator) {
|
||||||
ValidateResources(resourcesChan, validationResults, registries, cfg.KubernetesVersion, c, filter, reject, cfg.IgnoreMissingSchemas)
|
for res := range resources {
|
||||||
|
validationResults <- v.Validate(res)
|
||||||
|
}
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}()
|
}(resourcesChan, validationResults, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ package validator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/yannh/kubeconform/pkg/cache"
|
||||||
|
"github.com/yannh/kubeconform/pkg/registry"
|
||||||
"github.com/yannh/kubeconform/pkg/resource"
|
"github.com/yannh/kubeconform/pkg/resource"
|
||||||
|
|
||||||
"github.com/xeipuuv/gojsonschema"
|
"github.com/xeipuuv/gojsonschema"
|
||||||
|
|
@ -19,6 +21,150 @@ const (
|
||||||
Empty
|
Empty
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Validator struct {
|
||||||
|
opts *Opts
|
||||||
|
schemaCache *cache.SchemaCache
|
||||||
|
schemaDownload func(registries []registry.Registry, kind, version, k8sVersion string) (*gojsonschema.Schema, error)
|
||||||
|
regs []registry.Registry
|
||||||
|
}
|
||||||
|
|
||||||
|
type Opts struct {
|
||||||
|
SkipTLS bool
|
||||||
|
SkipKinds map[string]bool
|
||||||
|
RejectKinds map[string]bool
|
||||||
|
KubernetesVersion string
|
||||||
|
Strict bool
|
||||||
|
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 {
|
||||||
|
registries := []registry.Registry{}
|
||||||
|
for _, schemaLocation := range schemaLocations {
|
||||||
|
registries = append(registries, registry.New(schemaLocation, opts.Strict, opts.SkipTLS))
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.SkipKinds == nil {
|
||||||
|
opts.SkipKinds = map[string]bool{}
|
||||||
|
}
|
||||||
|
if opts.RejectKinds == nil {
|
||||||
|
opts.RejectKinds = map[string]bool{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Validator{
|
||||||
|
opts: opts,
|
||||||
|
schemaDownload: downloadSchema,
|
||||||
|
schemaCache: cache.New(),
|
||||||
|
regs: registries,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Validator) Validate(res resource.Resource) Result {
|
||||||
|
skip := func(signature resource.Signature) bool {
|
||||||
|
isSkipKind, ok := v.opts.SkipKinds[signature.Kind]
|
||||||
|
return ok && isSkipKind
|
||||||
|
}
|
||||||
|
|
||||||
|
reject := func(signature resource.Signature) bool {
|
||||||
|
_, ok := v.opts.RejectKinds[signature.Kind]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := res.Signature()
|
||||||
|
if err != nil {
|
||||||
|
return Result{Resource: res, Err: fmt.Errorf("error while parsing: %s", err), Status: Error}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sig.Kind == "" {
|
||||||
|
return Result{Resource: res, Err: nil, Status: Empty}
|
||||||
|
}
|
||||||
|
|
||||||
|
if skip(*sig) {
|
||||||
|
return Result{Resource: res, Err: nil, Status: Skipped}
|
||||||
|
}
|
||||||
|
|
||||||
|
if reject(*sig) {
|
||||||
|
return Result{Resource: res, Err: fmt.Errorf("prohibited resource kind %s", sig.Kind), Status: Error}
|
||||||
|
}
|
||||||
|
|
||||||
|
cached := false
|
||||||
|
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 !cached {
|
||||||
|
if schema, err = v.schemaDownload(v.regs, sig.Kind, sig.Version, v.opts.KubernetesVersion); err != nil {
|
||||||
|
return Result{Resource: res, Err: err, Status: Error}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.schemaCache != nil {
|
||||||
|
v.schemaCache.Set(cacheKey, schema)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if schema == nil {
|
||||||
|
if v.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 {
|
||||||
|
return Result{Resource: res, Status: Error, Err: fmt.Errorf("error unmarshalling resource: %s", err)}
|
||||||
|
}
|
||||||
|
resourceLoader := gojsonschema.NewGoLoader(resource)
|
||||||
|
|
||||||
|
results, err := schema.Validate(resourceLoader)
|
||||||
|
if err != nil {
|
||||||
|
// This error can only happen if the Object to validate is poorly formed. There's no hope of saving this one
|
||||||
|
return Result{Resource: res, Status: Error, Err: fmt.Errorf("problem validating schema. Check JSON formatting: %s", err)}
|
||||||
|
}
|
||||||
|
|
||||||
|
if results.Valid() {
|
||||||
|
return Result{Resource: res, Status: Valid}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := ""
|
||||||
|
for _, errMsg := range results.Errors() {
|
||||||
|
if msg != "" {
|
||||||
|
msg += " - "
|
||||||
|
}
|
||||||
|
msg += errMsg.Description()
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result{Resource: res, Status: Invalid, Err: fmt.Errorf("%s", msg)}
|
||||||
|
}
|
||||||
|
|
||||||
// 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{}
|
||||||
|
|
@ -52,36 +198,3 @@ func NewError(filename string, err error) Result {
|
||||||
Status: Error,
|
Status: Error,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates a single Kubernetes resource against a Json Schema
|
|
||||||
func Validate(res resource.Resource, schema *gojsonschema.Schema) Result {
|
|
||||||
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 {
|
|
||||||
return Result{Resource: res, Status: Error, Err: fmt.Errorf("error unmarshalling resource: %s", err)}
|
|
||||||
}
|
|
||||||
resourceLoader := gojsonschema.NewGoLoader(resource)
|
|
||||||
|
|
||||||
results, err := schema.Validate(resourceLoader)
|
|
||||||
if err != nil {
|
|
||||||
// This error can only happen if the Object to validate is poorly formed. There's no hope of saving this one
|
|
||||||
return Result{Resource: res, Status: Error, Err: fmt.Errorf("problem validating schema. Check JSON formatting: %s", err)}
|
|
||||||
}
|
|
||||||
|
|
||||||
if results.Valid() {
|
|
||||||
return Result{Resource: res, Status: Valid}
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := ""
|
|
||||||
for _, errMsg := range results.Errors() {
|
|
||||||
if msg != "" {
|
|
||||||
msg += " - "
|
|
||||||
}
|
|
||||||
msg += errMsg.Description()
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result{Resource: res, Status: Invalid, Err: fmt.Errorf("%s", msg)}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package validator
|
package validator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"github.com/yannh/kubeconform/pkg/registry"
|
||||||
"github.com/yannh/kubeconform/pkg/resource"
|
"github.com/yannh/kubeconform/pkg/resource"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -12,11 +12,12 @@ func TestValidate(t *testing.T) {
|
||||||
for i, testCase := range []struct {
|
for i, testCase := range []struct {
|
||||||
name string
|
name string
|
||||||
rawResource, schema []byte
|
rawResource, schema []byte
|
||||||
expect error
|
expect Status
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"valid resource",
|
"valid resource",
|
||||||
[]byte(`
|
[]byte(`
|
||||||
|
Kind: name
|
||||||
firstName: foo
|
firstName: foo
|
||||||
lastName: bar
|
lastName: bar
|
||||||
`),
|
`),
|
||||||
|
|
@ -24,6 +25,9 @@ lastName: bar
|
||||||
"title": "Example Schema",
|
"title": "Example Schema",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"Kind": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"firstName": {
|
"firstName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
@ -38,11 +42,12 @@ lastName: bar
|
||||||
},
|
},
|
||||||
"required": ["firstName", "lastName"]
|
"required": ["firstName", "lastName"]
|
||||||
}`),
|
}`),
|
||||||
nil,
|
Valid,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"invalid resource",
|
"invalid resource",
|
||||||
[]byte(`
|
[]byte(`
|
||||||
|
Kind: name
|
||||||
firstName: foo
|
firstName: foo
|
||||||
lastName: bar
|
lastName: bar
|
||||||
`),
|
`),
|
||||||
|
|
@ -50,6 +55,9 @@ lastName: bar
|
||||||
"title": "Example Schema",
|
"title": "Example Schema",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"Kind": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"firstName": {
|
"firstName": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
|
@ -64,17 +72,21 @@ lastName: bar
|
||||||
},
|
},
|
||||||
"required": ["firstName", "lastName"]
|
"required": ["firstName", "lastName"]
|
||||||
}`),
|
}`),
|
||||||
fmt.Errorf("Invalid type. Expected: number, given: string"),
|
Invalid,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"missing required field",
|
"missing required field",
|
||||||
[]byte(`
|
[]byte(`
|
||||||
|
Kind: name
|
||||||
firstName: foo
|
firstName: foo
|
||||||
`),
|
`),
|
||||||
[]byte(`{
|
[]byte(`{
|
||||||
"title": "Example Schema",
|
"title": "Example Schema",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"Kind": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"firstName": {
|
"firstName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
@ -89,11 +101,12 @@ firstName: foo
|
||||||
},
|
},
|
||||||
"required": ["firstName", "lastName"]
|
"required": ["firstName", "lastName"]
|
||||||
}`),
|
}`),
|
||||||
fmt.Errorf("lastName is required"),
|
Invalid,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"resource has invalid yaml",
|
"resource has invalid yaml",
|
||||||
[]byte(`
|
[]byte(`
|
||||||
|
Kind: name
|
||||||
firstName foo
|
firstName foo
|
||||||
lastName: bar
|
lastName: bar
|
||||||
`),
|
`),
|
||||||
|
|
@ -101,6 +114,9 @@ lastName: bar
|
||||||
"title": "Example Schema",
|
"title": "Example Schema",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"Kind": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"firstName": {
|
"firstName": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
|
@ -115,15 +131,26 @@ lastName: bar
|
||||||
},
|
},
|
||||||
"required": ["firstName", "lastName"]
|
"required": ["firstName", "lastName"]
|
||||||
}`),
|
}`),
|
||||||
fmt.Errorf("error unmarshalling resource: error converting YAML to JSON: yaml: line 3: mapping values are not allowed in this context"),
|
Error,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
schema, err := gojsonschema.NewSchema(gojsonschema.NewBytesLoader(testCase.schema))
|
v := Validator{
|
||||||
if err != nil {
|
opts: &Opts{
|
||||||
t.Errorf("failed parsing test schema")
|
SkipKinds: map[string]bool{},
|
||||||
|
RejectKinds: map[string]bool{},
|
||||||
|
},
|
||||||
|
schemaCache: nil,
|
||||||
|
schemaDownload: func(_ []registry.Registry, _, _, _ string) (*gojsonschema.Schema, error) {
|
||||||
|
schema, err := gojsonschema.NewSchema(gojsonschema.NewBytesLoader(testCase.schema))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed parsing test schema")
|
||||||
|
}
|
||||||
|
return schema, nil
|
||||||
|
},
|
||||||
|
regs: nil,
|
||||||
}
|
}
|
||||||
if got := Validate(resource.Resource{Bytes: testCase.rawResource}, schema); ((got.Err == nil) != (testCase.expect == nil)) || (got.Err != nil && (got.Err.Error() != testCase.expect.Error())) {
|
if got := v.Validate(resource.Resource{Bytes: testCase.rawResource}); got.Status != testCase.expect {
|
||||||
t.Errorf("%d - expected %s, got %s", i, testCase.expect, got.Err)
|
t.Errorf("%d - expected %d, got %d", i, testCase.expect, got.Status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue