Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to santhosh-tekuri/jsonschema #168

Merged
merged 11 commits into from
Jan 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions acceptance.bats
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,12 @@ resetCacheFolder() {
}

@test "Pass when using a valid HTTP -schema-location" {
run bin/kubeconform -schema-location 'https://kubernetesjsonschema.dev/{{ .NormalizedKubernetesVersion }}-standalone{{ .StrictSuffix }}/{{ .ResourceKind }}{{ .KindSuffix }}.json' fixtures/valid.yaml
run bin/kubeconform -schema-location 'https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/{{ .NormalizedKubernetesVersion }}-standalone{{ .StrictSuffix }}/{{ .ResourceKind }}{{ .KindSuffix }}.json' fixtures/valid.yaml
[ "$status" -eq 0 ]
}

@test "Pass when using schemas with HTTP references" {
run bin/kubeconform -summary -schema-location 'https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/{{ .NormalizedKubernetesVersion }}{{ .StrictSuffix }}/{{ .ResourceKind }}{{ .KindSuffix }}.json' fixtures/valid.yaml
[ "$status" -eq 0 ]
}

Expand Down Expand Up @@ -252,7 +257,7 @@ resetCacheFolder() {

@test "Fail when no schema found, ensure 404 is not cached on disk" {
resetCacheFolder
run bin/kubeconform -cache cache -schema-location 'https://raw.githubusercontent.com/garethr/openshift-json-schema/master/doesnotexist.json' fixtures/valid.yaml
run bin/kubeconform -cache cache -schema-location 'https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/doesnotexist.json' fixtures/valid.yaml
[ "$status" -eq 1 ]
[ "$output" == 'fixtures/valid.yaml - ReplicationController bob failed validation: could not find schema for ReplicationController' ]
[ "`ls cache/ | wc -l`" -eq 0 ]
Expand Down Expand Up @@ -287,14 +292,14 @@ resetCacheFolder() {
@test "Fail when parsing a List that contains an invalid resource" {
run bin/kubeconform -summary fixtures/list_invalid.yaml
[ "$status" -eq 1 ]
[ "${lines[0]}" == 'fixtures/list_invalid.yaml - ReplicationController bob is invalid: For field spec.replicas: Invalid type. Expected: [integer,null], given: string' ]
[ "${lines[0]}" == 'fixtures/list_invalid.yaml - ReplicationController bob is invalid: problem validating schema. Check JSON formatting: jsonschema: '\''/spec/replicas'\'' does not validate with https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/master-standalone/replicationcontroller-v1.json#/properties/spec/properties/replicas/type: expected integer or null, but got string' ]
[ "${lines[1]}" == 'Summary: 2 resources found in 1 file - Valid: 1, Invalid: 1, Errors: 0, Skipped: 0' ]
}

@test "Fail when parsing a List that contains an invalid resource from stdin" {
run bash -c "cat fixtures/list_invalid.yaml | bin/kubeconform -summary -"
[ "$status" -eq 1 ]
[ "${lines[0]}" == 'stdin - ReplicationController bob is invalid: For field spec.replicas: Invalid type. Expected: [integer,null], given: string' ]
[ "${lines[0]}" == 'stdin - ReplicationController bob is invalid: problem validating schema. Check JSON formatting: jsonschema: '\''/spec/replicas'\'' does not validate with https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/master-standalone/replicationcontroller-v1.json#/properties/spec/properties/replicas/type: expected integer or null, but got string' ]
[ "${lines[1]}" == 'Summary: 2 resources found parsing stdin - Valid: 1, Invalid: 1, Errors: 0, Skipped: 0' ]
}

Expand Down
8 changes: 3 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ module github.com/yannh/kubeconform
go 1.17

require (
github.com/beevik/etree v1.1.0
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0
gopkg.in/yaml.v2 v2.4.0 // indirect
github.com/santhosh-tekuri/jsonschema/v5 v5.1.1
sigs.k8s.io/yaml v1.2.0
)

require gopkg.in/yaml.v2 v2.4.0 // indirect
17 changes: 2 additions & 15 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,20 +1,7 @@
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/santhosh-tekuri/jsonschema/v5 v5.1.1 h1:lEOLY2vyGIqKWUI9nzsOJRV3mb3WC9dXYORsLEUcoeY=
github.com/santhosh-tekuri/jsonschema/v5 v5.1.1/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down
18 changes: 9 additions & 9 deletions pkg/registry/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@ func newHTTPRegistry(schemaPathTemplate string, cacheFolder string, strict bool,
}

// DownloadSchema downloads the schema for a particular resource from an HTTP server
func (r SchemaRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVersion string) ([]byte, error) {
func (r SchemaRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVersion string) (string, []byte, error) {
url, err := schemaPath(r.schemaPathTemplate, resourceKind, resourceAPIVersion, k8sVersion, r.strict)
if err != nil {
return nil, err
return "", nil, err
}

if r.cache != nil {
if b, err := r.cache.Get(resourceKind, resourceAPIVersion, k8sVersion); err == nil {
return b.([]byte), nil
return url, b.([]byte), nil
}
}

Expand All @@ -79,7 +79,7 @@ func (r SchemaRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVers
if r.debug {
log.Println(msg)
}
return nil, errors.New(msg)
return url, nil, errors.New(msg)
}
defer resp.Body.Close()

Expand All @@ -88,15 +88,15 @@ func (r SchemaRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVers
if r.debug {
log.Print(msg)
}
return nil, newNotFoundError(errors.New(msg))
return url, nil, newNotFoundError(errors.New(msg))
}

if resp.StatusCode != http.StatusOK {
msg := fmt.Sprintf("error while downloading schema at %s - received HTTP status %d", url, resp.StatusCode)
if r.debug {
log.Print(msg)
}
return nil, fmt.Errorf(msg)
return url, nil, fmt.Errorf(msg)
}

body, err := ioutil.ReadAll(resp.Body)
Expand All @@ -105,7 +105,7 @@ func (r SchemaRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVers
if r.debug {
log.Print(msg)
}
return nil, errors.New(msg)
return url, nil, errors.New(msg)
}

if r.debug {
Expand All @@ -114,9 +114,9 @@ func (r SchemaRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVers

if r.cache != nil {
if err := r.cache.Set(resourceKind, resourceAPIVersion, k8sVersion, body); err != nil {
return nil, fmt.Errorf("failed writing schema to cache: %s", err)
return url, nil, fmt.Errorf("failed writing schema to cache: %s", err)
}
}

return body, nil
return url, body, nil
}
2 changes: 1 addition & 1 deletion pkg/registry/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func TestDownloadSchema(t *testing.T) {
strict: testCase.strict,
}

res, err := reg.DownloadSchema(testCase.resourceKind, testCase.resourceAPIVersion, testCase.k8sversion)
_, res, err := reg.DownloadSchema(testCase.resourceKind, testCase.resourceAPIVersion, testCase.k8sversion)
if err == nil || testCase.expectErr == nil {
if err != testCase.expectErr {
t.Errorf("during test '%s': expected error, got:\n%s\n%s\n", testCase.name, testCase.expectErr, err)
Expand Down
12 changes: 6 additions & 6 deletions pkg/registry/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ func newLocalRegistry(pathTemplate string, strict bool, debug bool) (*LocalRegis
}

// DownloadSchema retrieves the schema from a file for the resource
func (r LocalRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVersion string) ([]byte, error) {
func (r LocalRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVersion string) (string, []byte, error) {
schemaFile, err := schemaPath(r.pathTemplate, resourceKind, resourceAPIVersion, k8sVersion, r.strict)
if err != nil {
return []byte{}, nil
return schemaFile, []byte{}, nil
}
f, err := os.Open(schemaFile)
if err != nil {
Expand All @@ -36,14 +36,14 @@ func (r LocalRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVersi
if r.debug {
log.Print(msg)
}
return nil, newNotFoundError(errors.New(msg))
return schemaFile, nil, newNotFoundError(errors.New(msg))
}

msg := fmt.Sprintf("failed to open schema at %s: %s", schemaFile, err)
if r.debug {
log.Print(msg)
}
return nil, errors.New(msg)
return schemaFile, nil, errors.New(msg)
}

defer f.Close()
Expand All @@ -53,11 +53,11 @@ func (r LocalRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVersi
if r.debug {
log.Print(msg)
}
return nil, err
return schemaFile, nil, err
}

if r.debug {
log.Printf("using schema found at %s", schemaFile)
}
return content, nil
return schemaFile, content, nil
}
2 changes: 1 addition & 1 deletion pkg/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type Manifest struct {

// Registry is an interface that should be implemented by any source of Kubernetes schemas
type Registry interface {
DownloadSchema(resourceKind, resourceAPIVersion, k8sVersion string) ([]byte, error)
DownloadSchema(resourceKind, resourceAPIVersion, k8sVersion string) (string, []byte, error)
}

// Retryable indicates whether an error is a temporary or a permanent failure
Expand Down
40 changes: 12 additions & 28 deletions pkg/validator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import (
"fmt"
"io"

jsonschema "github.com/santhosh-tekuri/jsonschema/v5"
_ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
"github.com/yannh/kubeconform/pkg/cache"
"github.com/yannh/kubeconform/pkg/registry"
"github.com/yannh/kubeconform/pkg/resource"

"github.com/xeipuuv/gojsonschema"
"sigs.k8s.io/yaml"
)

Expand Down Expand Up @@ -91,7 +91,7 @@ func New(schemaLocations []string, opts Opts) (Validator, error) {
type v struct {
opts Opts
schemaCache cache.Cache
schemaDownload func(registries []registry.Registry, kind, version, k8sVersion string) (*gojsonschema.Schema, error)
schemaDownload func(registries []registry.Registry, kind, version, k8sVersion string) (*jsonschema.Schema, error)
regs []registry.Registry
}

Expand Down Expand Up @@ -151,13 +151,13 @@ func (val *v) ValidateResource(res resource.Resource) Result {
}

cached := false
var schema *gojsonschema.Schema
var schema *jsonschema.Schema

if val.schemaCache != nil {
s, err := val.schemaCache.Get(sig.Kind, sig.Version, val.opts.KubernetesVersion)
if err == nil {
cached = true
schema = s.(*gojsonschema.Schema)
schema = s.(*jsonschema.Schema)
}
}

Expand All @@ -179,28 +179,12 @@ func (val *v) ValidateResource(res resource.Resource) Result {
return Result{Resource: res, Err: fmt.Errorf("could not find schema for %s", sig.Kind), Status: Error}
}

resourceLoader := gojsonschema.NewGoLoader(r)

results, err := schema.Validate(resourceLoader)
err = schema.Validate(r)
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}
return Result{Resource: res, Status: Invalid, Err: fmt.Errorf("problem validating schema. Check JSON formatting: %s", err)}
}

msg := ""
for _, errMsg := range results.Errors() {
if msg != "" {
msg += " - "
}
details := errMsg.Details()
msg += fmt.Sprintf("For field %s: %s", details["field"].(string), errMsg.Description())
}

return Result{Resource: res, Status: Invalid, Err: fmt.Errorf("%s", msg)}
return Result{Resource: res, Status: Valid}
}

// ValidateWithContext validates resources found in r
Expand Down Expand Up @@ -235,15 +219,15 @@ func (val *v) Validate(filename string, r io.ReadCloser) []Result {
return val.ValidateWithContext(context.Background(), filename, r)
}

func downloadSchema(registries []registry.Registry, kind, version, k8sVersion string) (*gojsonschema.Schema, error) {
func downloadSchema(registries []registry.Registry, kind, version, k8sVersion string) (*jsonschema.Schema, error) {
var err error
var schemaBytes []byte
var path string

for _, reg := range registries {
schemaBytes, err = reg.DownloadSchema(kind, version, k8sVersion)
path, schemaBytes, err = reg.DownloadSchema(kind, version, k8sVersion)
if err == nil {
schema, err := gojsonschema.NewSchema(gojsonschema.NewBytesLoader(schemaBytes))

schema, err := jsonschema.CompileString(path, string(schemaBytes))
// If we got a non-parseable response, we try the next registry
if err != nil {
continue
Expand Down
16 changes: 8 additions & 8 deletions pkg/validator/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import (
)

type mockRegistry struct {
SchemaDownloader func() ([]byte, error)
SchemaDownloader func() (string, []byte, error)
}

func newMockRegistry(f func() ([]byte, error)) *mockRegistry {
func newMockRegistry(f func() (string, []byte, error)) *mockRegistry {
return &mockRegistry{
SchemaDownloader: f,
}
}

func (m mockRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVersion string) ([]byte, error) {
func (m mockRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVersion string) (string, []byte, error) {
return m.SchemaDownloader()
}

Expand Down Expand Up @@ -362,17 +362,17 @@ lastName: bar
schemaCache: nil,
schemaDownload: downloadSchema,
regs: []registry.Registry{
newMockRegistry(func() ([]byte, error) {
return testCase.schemaRegistry1, nil
newMockRegistry(func() (string, []byte, error) {
return "", testCase.schemaRegistry1, nil
}),
newMockRegistry(func() ([]byte, error) {
return testCase.schemaRegistry2, nil
newMockRegistry(func() (string, []byte, error) {
return "", testCase.schemaRegistry2, nil
}),
},
}
if got := val.ValidateResource(resource.Resource{Bytes: testCase.rawResource}); got.Status != testCase.expect {
if got.Err != nil {
t.Errorf("%d - expected %d, got %d: %s", i, testCase.expect, got.Status, got.Err.Error())
t.Errorf("Test '%s' - expected %d, got %d: %s", testCase.name, testCase.expect, got.Status, got.Err.Error())
} else {
t.Errorf("%d - expected %d, got %d", i, testCase.expect, got.Status)
}
Expand Down
14 changes: 0 additions & 14 deletions vendor/github.com/beevik/etree/.travis.yml

This file was deleted.

10 changes: 0 additions & 10 deletions vendor/github.com/beevik/etree/CONTRIBUTORS

This file was deleted.

24 changes: 0 additions & 24 deletions vendor/github.com/beevik/etree/LICENSE

This file was deleted.

Loading