mirror of
https://github.com/yannh/kubeconform.git
synced 2026-02-21 19:07:00 +00:00
Allow duration format used in Kubernetes API machinery
This commit is contained in:
parent
1bd44986dd
commit
cf079213ed
2 changed files with 100 additions and 1 deletions
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
jsonschema "github.com/santhosh-tekuri/jsonschema/v5"
|
jsonschema "github.com/santhosh-tekuri/jsonschema/v5"
|
||||||
_ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
|
_ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
|
||||||
|
|
@ -202,7 +203,6 @@ func (val *v) ValidateResource(res resource.Resource) Result {
|
||||||
Msg: ve.Message,
|
Msg: ve.Message,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return Result{
|
return Result{
|
||||||
Resource: res,
|
Resource: res,
|
||||||
|
|
@ -248,6 +248,21 @@ func (val *v) Validate(filename string, r io.ReadCloser) []Result {
|
||||||
return val.ValidateWithContext(context.Background(), filename, r)
|
return val.ValidateWithContext(context.Background(), filename, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validateDuration is a custom validator for the duration format
|
||||||
|
// as JSONSchema only supports the ISO 8601 format, i.e. `PT1H30M`,
|
||||||
|
// while Kubernetes API machinery expects the Go duration format, i.e. `1h30m`
|
||||||
|
// which is commonly used in Kubernetes operators for specifying intervals.
|
||||||
|
// https://github.com/kubernetes/apiextensions-apiserver/blob/1ecd29f74da0639e2e6e3b8fac0c9bfd217e05eb/pkg/apis/apiextensions/v1/types_jsonschema.go#L71
|
||||||
|
func validateDuration(v any) bool {
|
||||||
|
// Try validation with the Go duration format
|
||||||
|
if _, err := time.ParseDuration(v.(string)); err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try validation with the ISO 8601 format
|
||||||
|
return jsonschema.Formats["duration"](v)
|
||||||
|
}
|
||||||
|
|
||||||
func downloadSchema(registries []registry.Registry, kind, version, k8sVersion string) (*jsonschema.Schema, error) {
|
func downloadSchema(registries []registry.Registry, kind, version, k8sVersion string) (*jsonschema.Schema, error) {
|
||||||
var err error
|
var err error
|
||||||
var schemaBytes []byte
|
var schemaBytes []byte
|
||||||
|
|
@ -257,6 +272,9 @@ func downloadSchema(registries []registry.Registry, kind, version, k8sVersion st
|
||||||
path, schemaBytes, err = reg.DownloadSchema(kind, version, k8sVersion)
|
path, schemaBytes, err = reg.DownloadSchema(kind, version, k8sVersion)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
c := jsonschema.NewCompiler()
|
c := jsonschema.NewCompiler()
|
||||||
|
|
||||||
|
// Overwrite duration validatior
|
||||||
|
c.Formats["duration"] = validateDuration
|
||||||
c.Draft = jsonschema.Draft4
|
c.Draft = jsonschema.Draft4
|
||||||
if err := c.AddResource(path, bytes.NewReader(schemaBytes)); err != nil {
|
if err := c.AddResource(path, bytes.NewReader(schemaBytes)); err != nil {
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -377,6 +377,87 @@ lastName: bar
|
||||||
Error,
|
Error,
|
||||||
[]ValidationError{},
|
[]ValidationError{},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"valid resource duration - go format",
|
||||||
|
[]byte(`
|
||||||
|
kind: name
|
||||||
|
apiVersion: v1
|
||||||
|
interval: 5s
|
||||||
|
`),
|
||||||
|
[]byte(`{
|
||||||
|
"title": "Example Schema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"interval": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "duration"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["interval"]
|
||||||
|
}`),
|
||||||
|
nil,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
Valid,
|
||||||
|
[]ValidationError{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"valid resource duration - iso8601 format",
|
||||||
|
[]byte(`
|
||||||
|
kind: name
|
||||||
|
apiVersion: v1
|
||||||
|
interval: PT1H
|
||||||
|
`),
|
||||||
|
[]byte(`{
|
||||||
|
"title": "Example Schema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"interval": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "duration"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["interval"]
|
||||||
|
}`),
|
||||||
|
nil,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
Valid,
|
||||||
|
[]ValidationError{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"invalid resource duration",
|
||||||
|
[]byte(`
|
||||||
|
kind: name
|
||||||
|
apiVersion: v1
|
||||||
|
interval: test
|
||||||
|
`),
|
||||||
|
[]byte(`{
|
||||||
|
"title": "Example Schema",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"interval": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "duration"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["interval"]
|
||||||
|
}`),
|
||||||
|
nil,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
Invalid,
|
||||||
|
[]ValidationError{{Path: "/interval", Msg: "'test' is not valid 'duration'"}},
|
||||||
|
},
|
||||||
} {
|
} {
|
||||||
val := v{
|
val := v{
|
||||||
opts: Opts{
|
opts: Opts{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue