mirror of
https://github.com/yannh/kubeconform.git
synced 2026-02-18 09:27:02 +00:00
Merge 6d5b7385f2 into 1bd44986dd
This commit is contained in:
commit
3625be2d75
3 changed files with 63 additions and 9 deletions
|
|
@ -86,6 +86,7 @@ func kubeconform(cfg config.Config) int {
|
||||||
RejectKinds: cfg.RejectKinds,
|
RejectKinds: cfg.RejectKinds,
|
||||||
KubernetesVersion: cfg.KubernetesVersion.String(),
|
KubernetesVersion: cfg.KubernetesVersion.String(),
|
||||||
Strict: cfg.Strict,
|
Strict: cfg.Strict,
|
||||||
|
StrictExceptions: cfg.StrictExceptions,
|
||||||
IgnoreMissingSchemas: cfg.IgnoreMissingSchemas,
|
IgnoreMissingSchemas: cfg.IgnoreMissingSchemas,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ type Config struct {
|
||||||
SkipKinds map[string]struct{} `yaml:"skip" json:"skip"`
|
SkipKinds map[string]struct{} `yaml:"skip" json:"skip"`
|
||||||
SkipTLS bool `yaml:"insecureSkipTLSVerify" json:"insecureSkipTLSVerify"`
|
SkipTLS bool `yaml:"insecureSkipTLSVerify" json:"insecureSkipTLSVerify"`
|
||||||
Strict bool `yaml:"strict" json:"strict"`
|
Strict bool `yaml:"strict" json:"strict"`
|
||||||
|
StrictExceptions map[string]struct{} `yaml:"strictExceptions" json:"strictExceptions"`
|
||||||
Summary bool `yaml:"summary" json:"summary"`
|
Summary bool `yaml:"summary" json:"summary"`
|
||||||
Verbose bool `yaml:"verbose" json:"verbose"`
|
Verbose bool `yaml:"verbose" json:"verbose"`
|
||||||
Version bool `yaml:"version" json:"version"`
|
Version bool `yaml:"version" json:"version"`
|
||||||
|
|
@ -75,7 +76,7 @@ func splitCSV(csvStr string) map[string]struct{} {
|
||||||
// FromFlags retrieves kubeconform's runtime configuration from the command-line parameters
|
// FromFlags retrieves kubeconform's runtime configuration from the command-line parameters
|
||||||
func FromFlags(progName string, args []string) (Config, string, error) {
|
func FromFlags(progName string, args []string) (Config, string, error) {
|
||||||
var schemaLocationsParam, ignoreFilenamePatterns arrayParam
|
var schemaLocationsParam, ignoreFilenamePatterns arrayParam
|
||||||
var skipKindsCSV, rejectKindsCSV string
|
var skipKindsCSV, rejectKindsCSV, strictExceptionsCSV string
|
||||||
flags := flag.NewFlagSet(progName, flag.ContinueOnError)
|
flags := flag.NewFlagSet(progName, flag.ContinueOnError)
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
flags.SetOutput(&buf)
|
flags.SetOutput(&buf)
|
||||||
|
|
@ -94,6 +95,7 @@ func FromFlags(progName string, args []string) (Config, string, error) {
|
||||||
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 or duplicated keys")
|
flags.BoolVar(&c.Strict, "strict", false, "disallow additional properties not in schema or duplicated keys")
|
||||||
|
flags.StringVar(&strictExceptionsCSV, "strict-exception", "", "comma-separated list of yaml paths to ignore when strict is enabled")
|
||||||
flags.StringVar(&c.OutputFormat, "output", "text", "output format - json, junit, pretty, tap, text")
|
flags.StringVar(&c.OutputFormat, "output", "text", "output format - json, junit, pretty, 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")
|
||||||
|
|
@ -109,6 +111,7 @@ func FromFlags(progName string, args []string) (Config, string, error) {
|
||||||
|
|
||||||
c.SkipKinds = splitCSV(skipKindsCSV)
|
c.SkipKinds = splitCSV(skipKindsCSV)
|
||||||
c.RejectKinds = splitCSV(rejectKindsCSV)
|
c.RejectKinds = splitCSV(rejectKindsCSV)
|
||||||
|
c.StrictExceptions = splitCSV(strictExceptionsCSV)
|
||||||
c.IgnoreFilenamePatterns = ignoreFilenamePatterns
|
c.IgnoreFilenamePatterns = ignoreFilenamePatterns
|
||||||
c.SchemaLocations = schemaLocationsParam
|
c.SchemaLocations = schemaLocationsParam
|
||||||
c.Files = flags.Args()
|
c.Files = flags.Args()
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ type Opts struct {
|
||||||
RejectKinds map[string]struct{} // List of resource Kinds to reject
|
RejectKinds map[string]struct{} // List of resource Kinds to reject
|
||||||
KubernetesVersion string // Kubernetes Version - has to match one in https://github.com/instrumenta/kubernetes-json-schema
|
KubernetesVersion string // Kubernetes Version - has to match one in https://github.com/instrumenta/kubernetes-json-schema
|
||||||
Strict bool // thros an error if resources contain undocumented fields
|
Strict bool // thros an error if resources contain undocumented fields
|
||||||
|
StrictExceptions map[string]struct{} // list of field paths (e.g. spec.template.metadata) to ignore when strict is enabled
|
||||||
IgnoreMissingSchemas bool // skip a resource if no schema for that resource can be found
|
IgnoreMissingSchemas bool // skip a resource if no schema for that resource can be found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,8 +69,10 @@ type Opts struct {
|
||||||
func New(schemaLocations []string, opts Opts) (Validator, error) {
|
func New(schemaLocations []string, opts Opts) (Validator, error) {
|
||||||
// Default to our kubernetes-json-schema fork
|
// Default to our kubernetes-json-schema fork
|
||||||
// raw.githubusercontent.com is frontend by Fastly and very fast
|
// raw.githubusercontent.com is frontend by Fastly and very fast
|
||||||
|
defaultLocationsUsed := false
|
||||||
if len(schemaLocations) == 0 {
|
if len(schemaLocations) == 0 {
|
||||||
schemaLocations = []string{"https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/{{ .NormalizedKubernetesVersion }}-standalone{{ .StrictSuffix }}/{{ .ResourceKind }}{{ .KindSuffix }}.json"}
|
schemaLocations = []string{"https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/{{ .NormalizedKubernetesVersion }}-standalone{{ .StrictSuffix }}/{{ .ResourceKind }}{{ .KindSuffix }}.json"}
|
||||||
|
defaultLocationsUsed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
registries := []registry.Registry{}
|
registries := []registry.Registry{}
|
||||||
|
|
@ -91,20 +94,62 @@ func New(schemaLocations []string, opts Opts) (Validator, error) {
|
||||||
if opts.RejectKinds == nil {
|
if opts.RejectKinds == nil {
|
||||||
opts.RejectKinds = map[string]struct{}{}
|
opts.RejectKinds = map[string]struct{}{}
|
||||||
}
|
}
|
||||||
|
if opts.StrictExceptions == nil {
|
||||||
|
opts.StrictExceptions = map[string]struct{}{}
|
||||||
|
}
|
||||||
|
if len(opts.StrictExceptions) == 0 {
|
||||||
|
// If no strict exceptions are specified, we add the metadata field
|
||||||
|
// as it is mostly not autogenerated in the OpenAPI schemas generated from CRDs
|
||||||
|
opts.StrictExceptions["metadata"] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
return &v{
|
return &v{
|
||||||
opts: opts,
|
opts: opts,
|
||||||
schemaDownload: downloadSchema,
|
schemaDownload: downloadSchema,
|
||||||
schemaCache: cache.NewInMemoryCache(),
|
schemaCache: cache.NewInMemoryCache(),
|
||||||
regs: registries,
|
regs: registries,
|
||||||
|
defaultLocationsUsed: defaultLocationsUsed,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (val *v) setStrictSchema(schema *jsonschema.Schema, prefixPath string) {
|
||||||
|
if _, ok := val.opts.StrictExceptions[prefixPath]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if schema.AdditionalProperties == nil {
|
||||||
|
schema.AdditionalProperties = false
|
||||||
|
}
|
||||||
|
if schema.Items != nil {
|
||||||
|
casted, ok := schema.Items.(*jsonschema.Schema)
|
||||||
|
if ok {
|
||||||
|
fullPath := prefixPath + ".0"
|
||||||
|
val.setStrictSchema(casted, fullPath)
|
||||||
|
} else {
|
||||||
|
casted, ok := schema.Items.([]*jsonschema.Schema)
|
||||||
|
if ok {
|
||||||
|
for i, s := range casted {
|
||||||
|
fullPath := prefixPath + "." + fmt.Sprintf("%d", i)
|
||||||
|
val.setStrictSchema(s, fullPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, s := range schema.Properties {
|
||||||
|
fullPath := prefixPath + "." + name
|
||||||
|
if fullPath[0] == '.' {
|
||||||
|
fullPath = fullPath[1:]
|
||||||
|
}
|
||||||
|
val.setStrictSchema(s, fullPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type v struct {
|
type v struct {
|
||||||
opts Opts
|
opts Opts
|
||||||
schemaCache cache.Cache
|
schemaCache cache.Cache
|
||||||
schemaDownload func(registries []registry.Registry, kind, version, k8sVersion string) (*jsonschema.Schema, error)
|
schemaDownload func(registries []registry.Registry, kind, version, k8sVersion string) (*jsonschema.Schema, error)
|
||||||
regs []registry.Registry
|
regs []registry.Registry
|
||||||
|
defaultLocationsUsed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateResource validates a single resource. This allows to validate
|
// ValidateResource validates a single resource. This allows to validate
|
||||||
|
|
@ -191,6 +236,11 @@ 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}
|
return Result{Resource: res, Err: fmt.Errorf("could not find schema for %s", sig.Kind), Status: Error}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If strict mode is enabled, we set additionalProperties to false
|
||||||
|
// if not explicitly set in the schema
|
||||||
|
if val.opts.Strict && !val.defaultLocationsUsed {
|
||||||
|
val.setStrictSchema(schema, "")
|
||||||
|
}
|
||||||
err = schema.Validate(r)
|
err = schema.Validate(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
validationErrors := []ValidationError{}
|
validationErrors := []ValidationError{}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue