mirror of
https://github.com/yannh/kubeconform.git
synced 2026-02-19 01:47:02 +00:00
Forbid duplicate keys in strict mode
Prevents specifying duplicate keys using UnmarshallStrict: https://pkg.go.dev/gopkg.in/yaml.v2#UnmarshalStrict
This commit is contained in:
parent
7bf1e01dec
commit
78ed040b7d
6 changed files with 81 additions and 8 deletions
|
|
@ -98,7 +98,7 @@ Usage: ./bin/kubeconform [OPTION]... [FILE OR FOLDER]...
|
||||||
-skip string
|
-skip string
|
||||||
comma-separated list of kinds to ignore
|
comma-separated list of kinds to ignore
|
||||||
-strict
|
-strict
|
||||||
disallow additional properties not in schema
|
disallow additional properties not in schema or duplicated keys
|
||||||
-summary
|
-summary
|
||||||
print a summary at the end (ignored for junit output)
|
print a summary at the end (ignored for junit output)
|
||||||
-v show version information
|
-v show version information
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ func FromFlags(progName string, args []string) (Config, string, error) {
|
||||||
flags.Var(&ignoreFilenamePatterns, "ignore-filename-pattern", "regular expression specifying paths to ignore (can be specified multiple times)")
|
flags.Var(&ignoreFilenamePatterns, "ignore-filename-pattern", "regular expression specifying paths to ignore (can be specified multiple times)")
|
||||||
flags.BoolVar(&c.Summary, "summary", false, "print a summary at the end (ignored for junit output)")
|
flags.BoolVar(&c.Summary, "summary", false, "print a summary at the end (ignored for junit output)")
|
||||||
flags.IntVar(&c.NumberOfWorkers, "n", 4, "number of goroutines to run concurrently")
|
flags.IntVar(&c.NumberOfWorkers, "n", 4, "number of goroutines to run concurrently")
|
||||||
flags.BoolVar(&c.Strict, "strict", false, "disallow additional properties not in schema")
|
flags.BoolVar(&c.Strict, "strict", false, "disallow additional properties not in schema or duplicated keys")
|
||||||
flags.StringVar(&c.OutputFormat, "output", "text", "output format - json, junit, tap, text")
|
flags.StringVar(&c.OutputFormat, "output", "text", "output format - json, junit, tap, text")
|
||||||
flags.BoolVar(&c.Verbose, "verbose", false, "print results for all resources (ignored for tap and junit output)")
|
flags.BoolVar(&c.Verbose, "verbose", false, "print results for all resources (ignored for tap and junit output)")
|
||||||
flags.BoolVar(&c.SkipTLS, "insecure-skip-tls-verify", false, "disable verification of the server's SSL certificate. This will make your HTTPS connections insecure")
|
flags.BoolVar(&c.SkipTLS, "insecure-skip-tls-verify", false, "disable verification of the server's SSL certificate. This will make your HTTPS connections insecure")
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,12 @@ func (val *v) ValidateResource(res resource.Resource) Result {
|
||||||
}
|
}
|
||||||
|
|
||||||
var r map[string]interface{}
|
var r map[string]interface{}
|
||||||
if err := yaml.Unmarshal(res.Bytes, &r); err != nil {
|
unmarshaller := yaml.Unmarshal
|
||||||
|
if val.opts.Strict {
|
||||||
|
unmarshaller = yaml.UnmarshalStrict
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := unmarshaller(res.Bytes, &r); err != nil {
|
||||||
return Result{Resource: res, Status: Error, Err: fmt.Errorf("error unmarshalling resource: %s", err)}
|
return Result{Resource: res, Status: Error, Err: fmt.Errorf("error unmarshalling resource: %s", err)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ func TestValidate(t *testing.T) {
|
||||||
rawResource, schemaRegistry1 []byte
|
rawResource, schemaRegistry1 []byte
|
||||||
schemaRegistry2 []byte
|
schemaRegistry2 []byte
|
||||||
ignoreMissingSchema bool
|
ignoreMissingSchema bool
|
||||||
|
strict bool
|
||||||
expect Status
|
expect Status
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
|
@ -60,6 +61,7 @@ lastName: bar
|
||||||
}`),
|
}`),
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
Valid,
|
Valid,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -93,6 +95,7 @@ lastName: bar
|
||||||
}`),
|
}`),
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
Invalid,
|
Invalid,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -125,8 +128,61 @@ firstName: foo
|
||||||
}`),
|
}`),
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
Invalid,
|
Invalid,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key \"firstName\" already set in map",
|
||||||
|
[]byte(`
|
||||||
|
kind: name
|
||||||
|
apiVersion: v1
|
||||||
|
firstName: foo
|
||||||
|
firstName: bar
|
||||||
|
`),
|
||||||
|
[]byte(`{
|
||||||
|
"title": "Example Schema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"firstName": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["firstName"]
|
||||||
|
}`),
|
||||||
|
nil,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
Error,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key firstname already set in map in non-strict mode",
|
||||||
|
[]byte(`
|
||||||
|
kind: name
|
||||||
|
apiVersion: v1
|
||||||
|
firstName: foo
|
||||||
|
firstName: bar
|
||||||
|
`),
|
||||||
|
[]byte(`{
|
||||||
|
"title": "Example Schema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"firstName": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["firstName"]
|
||||||
|
}`),
|
||||||
|
nil,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
Valid,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"resource has invalid yaml",
|
"resource has invalid yaml",
|
||||||
[]byte(`
|
[]byte(`
|
||||||
|
|
@ -161,6 +217,7 @@ lastName: bar
|
||||||
}`),
|
}`),
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
Error,
|
Error,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -196,6 +253,7 @@ lastName: bar
|
||||||
},
|
},
|
||||||
"required": ["firstName", "lastName"]
|
"required": ["firstName", "lastName"]
|
||||||
}`),
|
}`),
|
||||||
|
false,
|
||||||
false,
|
false,
|
||||||
Valid,
|
Valid,
|
||||||
},
|
},
|
||||||
|
|
@ -232,6 +290,7 @@ lastName: bar
|
||||||
},
|
},
|
||||||
"required": ["firstName", "lastName"]
|
"required": ["firstName", "lastName"]
|
||||||
}`),
|
}`),
|
||||||
|
false,
|
||||||
false,
|
false,
|
||||||
Valid,
|
Valid,
|
||||||
},
|
},
|
||||||
|
|
@ -246,6 +305,7 @@ lastName: bar
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
Skipped,
|
Skipped,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -259,6 +319,7 @@ lastName: bar
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
Error,
|
Error,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -272,6 +333,7 @@ lastName: bar
|
||||||
[]byte(`<html>error page</html>`),
|
[]byte(`<html>error page</html>`),
|
||||||
[]byte(`<html>error page</html>`),
|
[]byte(`<html>error page</html>`),
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
Skipped,
|
Skipped,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -285,6 +347,7 @@ lastName: bar
|
||||||
[]byte(`<html>error page</html>`),
|
[]byte(`<html>error page</html>`),
|
||||||
[]byte(`<html>error page</html>`),
|
[]byte(`<html>error page</html>`),
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
Error,
|
Error,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
|
@ -293,6 +356,7 @@ lastName: bar
|
||||||
SkipKinds: map[string]struct{}{},
|
SkipKinds: map[string]struct{}{},
|
||||||
RejectKinds: map[string]struct{}{},
|
RejectKinds: map[string]struct{}{},
|
||||||
IgnoreMissingSchemas: testCase.ignoreMissingSchema,
|
IgnoreMissingSchemas: testCase.ignoreMissingSchema,
|
||||||
|
Strict: testCase.strict,
|
||||||
},
|
},
|
||||||
schemaCache: nil,
|
schemaCache: nil,
|
||||||
schemaDownload: downloadSchema,
|
schemaDownload: downloadSchema,
|
||||||
|
|
@ -306,7 +370,11 @@ lastName: bar
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if got := val.ValidateResource(resource.Resource{Bytes: testCase.rawResource}); got.Status != testCase.expect {
|
if got := val.ValidateResource(resource.Resource{Bytes: testCase.rawResource}); got.Status != testCase.expect {
|
||||||
t.Errorf("%d - expected %d, got %d: %s", i, testCase.expect, got.Status, got.Err.Error())
|
if got.Err != nil {
|
||||||
|
t.Errorf("%d - expected %d, got %d: %s", i, testCase.expect, got.Status, got.Err.Error())
|
||||||
|
} else {
|
||||||
|
t.Errorf("%d - expected %d, got %d", i, testCase.expect, got.Status)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ Usage: ./bin/kubeconform [OPTION]... [FILE OR FOLDER]...
|
||||||
-skip string
|
-skip string
|
||||||
comma-separated list of kinds to ignore
|
comma-separated list of kinds to ignore
|
||||||
-strict
|
-strict
|
||||||
disallow additional properties not in schema
|
disallow additional properties not in schema or duplicated keys
|
||||||
-summary
|
-summary
|
||||||
print a summary at the end (ignored for junit output)
|
print a summary at the end (ignored for junit output)
|
||||||
-v show version information
|
-v show version information
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ Usage: ./bin/kubeconform [OPTION]... [FILE OR FOLDER]...
|
||||||
-skip string
|
-skip string
|
||||||
comma-separated list of kinds to ignore
|
comma-separated list of kinds to ignore
|
||||||
-strict
|
-strict
|
||||||
disallow additional properties not in schema
|
disallow additional properties not in schema or duplicated keys
|
||||||
-summary
|
-summary
|
||||||
print a summary at the end (ignored for junit output)
|
print a summary at the end (ignored for junit output)
|
||||||
-v show version information
|
-v show version information
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue