Skip to content

Commit

Permalink
Allow to skip resources using the GVK notation
Browse files Browse the repository at this point in the history
  • Loading branch information
atosatto committed Jan 18, 2022
1 parent 85b30f8 commit ed4c919
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 73 deletions.
Binary file removed kubeconform
Binary file not shown.
17 changes: 12 additions & 5 deletions pkg/resource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ type Signature struct {
Kind, Version, Namespace, Name string
}

// GroupVersionKind returns a string with the GVK encoding of a resource signature.
// This encoding slightly differs from the Kubernetes upstream implementation
// in order to be suitable for being used in the kubeconform command-line arguments.
func (sig *Signature) GroupVersionKind() string {
return fmt.Sprintf("%s/%s", sig.Version, sig.Kind)
}

// QualifiedName returns a string for a signature in the format version/kind/namespace/name
func (sig *Signature) QualifiedName() string {
return fmt.Sprintf("%s/%s/%s/%s", sig.Version, sig.Kind, sig.Namespace, sig.Name)
}

// Signature computes a signature for a resource, based on its Kind, Version, Namespace & Name
func (res *Resource) Signature() (*Signature, error) {
if res.sig != nil {
Expand Down Expand Up @@ -119,8 +131,3 @@ func (res *Resource) Resources() []Resource {

return []Resource{*res}
}

// QualifiedName returns a string for a signature in the format version/kind/namespace/name
func (sig *Signature) QualifiedName() string {
return fmt.Sprintf("%s/%s/%s/%s", sig.Version, sig.Kind, sig.Namespace, sig.Name)
}
11 changes: 11 additions & 0 deletions pkg/validator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,23 @@ type v struct {
// ValidateResource validates a single resource. This allows to validate
// large resource streams using multiple Go Routines.
func (val *v) ValidateResource(res resource.Resource) Result {
// For backward compatibility reasons when determining whether
// a resource should be skipped or rejected we use both
// the GVK encoding of the resource signatures (the recommended method
// for skipping/rejecting resources) and the raw Kind.

skip := func(signature resource.Signature) bool {
if _, ok := val.opts.SkipKinds[signature.GroupVersionKind()]; ok {
return ok
}
_, ok := val.opts.SkipKinds[signature.Kind]
return ok
}

reject := func(signature resource.Signature) bool {
if _, ok := val.opts.RejectKinds[signature.GroupVersionKind()]; ok {
return ok
}
_, ok := val.opts.RejectKinds[signature.Kind]
return ok
}
Expand Down
202 changes: 134 additions & 68 deletions pkg/validator/validator_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package validator

import (
"github.com/yannh/kubeconform/pkg/registry"
"testing"

"github.com/yannh/kubeconform/pkg/registry"

"github.com/yannh/kubeconform/pkg/resource"
)

Expand All @@ -23,21 +24,24 @@ func (m mockRegistry) DownloadSchema(resourceKind, resourceAPIVersion, k8sVersio

func TestValidate(t *testing.T) {
for i, testCase := range []struct {
name string
rawResource, schemaRegistry1 []byte
schemaRegistry2 []byte
ignoreMissingSchema bool
expect Status
name string
rawResource []byte
schemaRegistry1 []byte
schemaRegistry2 []byte
ignoreMissingSchema bool
rejectKinds map[string]struct{}
skipKinds map[string]struct{}
expect Status
}{
{
"valid resource",
[]byte(`
name: "valid resource",
rawResource: []byte(`
kind: name
apiVersion: v1
firstName: foo
lastName: bar
`),
[]byte(`{
schemaRegistry1: []byte(`{
"title": "Example Schema",
"type": "object",
"properties": {
Expand All @@ -58,19 +62,17 @@ lastName: bar
},
"required": ["firstName", "lastName"]
}`),
nil,
false,
Valid,
expect: Valid,
},
{
"invalid resource",
[]byte(`
name: "invalid resource",
rawResource: []byte(`
kind: name
apiVersion: v1
firstName: foo
lastName: bar
`),
[]byte(`{
schemaRegistry1: []byte(`{
"title": "Example Schema",
"type": "object",
"properties": {
Expand All @@ -91,18 +93,47 @@ lastName: bar
},
"required": ["firstName", "lastName"]
}`),
nil,
false,
Invalid,
expect: Invalid,
},
{
name: "missing required field",
rawResource: []byte(`
kind: name
apiVersion: v1
firstName: foo
`),
schemaRegistry1: []byte(`{
"title": "Example Schema",
"type": "object",
"properties": {
"kind": {
"type": "string"
},
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
}
},
"required": ["firstName", "lastName"]
}`),
expect: Invalid,
},
{
"missing required field",
[]byte(`
name: "resource is skipped using Kind",
rawResource: []byte(`
kind: name
apiVersion: v1
firstName: foo
lastName: bar
`),
[]byte(`{
schemaRegistry1: []byte(`{
"title": "Example Schema",
"type": "object",
"properties": {
Expand All @@ -123,19 +154,54 @@ firstName: foo
},
"required": ["firstName", "lastName"]
}`),
nil,
false,
Invalid,
skipKinds: map[string]struct{}{
"name": {},
},
expect: Skipped,
},
{
name: "resource is skipped using GroupVersionKind",
rawResource: []byte(`
kind: name
apiVersion: v1
firstName: foo
lastName: bar
`),
schemaRegistry1: []byte(`{
"title": "Example Schema",
"type": "object",
"properties": {
"kind": {
"type": "string"
},
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
}
},
"required": ["firstName", "lastName"]
}`),
skipKinds: map[string]struct{}{
"v1/name": {},
},
expect: Skipped,
},
{
"resource has invalid yaml",
[]byte(`
name: "resource has invalid yaml",
rawResource: []byte(`
kind: name
apiVersion: v1
firstName foo
lastName: bar
`),
[]byte(`{
schemaRegistry1: []byte(`{
"title": "Example Schema",
"type": "object",
"properties": {
Expand All @@ -159,20 +225,20 @@ lastName: bar
},
"required": ["firstName", "lastName"]
}`),
nil,
false,
Error,
schemaRegistry2: nil,
ignoreMissingSchema: false,
expect: Error,
},
{
"missing schema in 1st registry",
[]byte(`
name: "missing schema in 1st registry",
rawResource: []byte(`
kind: name
apiVersion: v1
firstName: foo
lastName: bar
`),
nil,
[]byte(`{
schemaRegistry1: nil,
schemaRegistry2: []byte(`{
"title": "Example Schema",
"type": "object",
"properties": {
Expand All @@ -196,19 +262,19 @@ lastName: bar
},
"required": ["firstName", "lastName"]
}`),
false,
Valid,
ignoreMissingSchema: false,
expect: Valid,
},
{
"non-json response in 1st registry",
[]byte(`
name: "non-json response in 1st registry",
rawResource: []byte(`
kind: name
apiVersion: v1
firstName: foo
lastName: bar
`),
[]byte(`<html>error page</html>`),
[]byte(`{
schemaRegistry1: []byte(`<html>error page</html>`),
schemaRegistry2: []byte(`{
"title": "Example Schema",
"type": "object",
"properties": {
Expand All @@ -232,66 +298,66 @@ lastName: bar
},
"required": ["firstName", "lastName"]
}`),
false,
Valid,
ignoreMissingSchema: false,
expect: Valid,
},
{
"missing schema in both registries, ignore missing",
[]byte(`
name: "missing schema in both registries, ignore missing",
rawResource: []byte(`
kind: name
apiVersion: v1
firstName: foo
lastName: bar
`),
nil,
nil,
true,
Skipped,
schemaRegistry1: nil,
schemaRegistry2: nil,
ignoreMissingSchema: true,
expect: Skipped,
},
{
"missing schema in both registries, do not ignore missing",
[]byte(`
name: "missing schema in both registries, do not ignore missing",
rawResource: []byte(`
kind: name
apiVersion: v1
firstName: foo
lastName: bar
`),
nil,
nil,
false,
Error,
schemaRegistry1: nil,
schemaRegistry2: nil,
ignoreMissingSchema: false,
expect: Error,
},
{
"non-json response in both registries, ignore missing",
[]byte(`
name: "non-json response in both registries, ignore missing",
rawResource: []byte(`
kind: name
apiVersion: v1
firstName: foo
lastName: bar
`),
[]byte(`<html>error page</html>`),
[]byte(`<html>error page</html>`),
true,
Skipped,
schemaRegistry1: []byte(`<html>error page</html>`),
schemaRegistry2: []byte(`<html>error page</html>`),
ignoreMissingSchema: true,
expect: Skipped,
},
{
"non-json response in both registries, do not ignore missing",
[]byte(`
name: "non-json response in both registries, do not ignore missing",
rawResource: []byte(`
kind: name
apiVersion: v1
firstName: foo
lastName: bar
`),
[]byte(`<html>error page</html>`),
[]byte(`<html>error page</html>`),
false,
Error,
schemaRegistry1: []byte(`<html>error page</html>`),
schemaRegistry2: []byte(`<html>error page</html>`),
ignoreMissingSchema: false,
expect: Error,
},
} {
val := v{
opts: Opts{
SkipKinds: map[string]struct{}{},
RejectKinds: map[string]struct{}{},
SkipKinds: testCase.skipKinds,
RejectKinds: testCase.rejectKinds,
IgnoreMissingSchemas: testCase.ignoreMissingSchema,
},
schemaCache: nil,
Expand Down

0 comments on commit ed4c919

Please sign in to comment.