Skip to content

Commit

Permalink
Allow duration format used in Kubernetes API machinery
Browse files Browse the repository at this point in the history
  • Loading branch information
MattiasAng committed Feb 11, 2025
1 parent 1bd4498 commit cf07921
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 1 deletion.
20 changes: 19 additions & 1 deletion pkg/validator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"io"
"time"

jsonschema "github.com/santhosh-tekuri/jsonschema/v5"
_ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
Expand Down Expand Up @@ -202,7 +203,6 @@ func (val *v) ValidateResource(res resource.Resource) Result {
Msg: ve.Message,
})
}

}
return Result{
Resource: res,
Expand Down Expand Up @@ -248,6 +248,21 @@ func (val *v) Validate(filename string, r io.ReadCloser) []Result {
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) {
var err error
var schemaBytes []byte
Expand All @@ -257,6 +272,9 @@ func downloadSchema(registries []registry.Registry, kind, version, k8sVersion st
path, schemaBytes, err = reg.DownloadSchema(kind, version, k8sVersion)
if err == nil {
c := jsonschema.NewCompiler()

// Overwrite duration validatior
c.Formats["duration"] = validateDuration
c.Draft = jsonschema.Draft4
if err := c.AddResource(path, bytes.NewReader(schemaBytes)); err != nil {
continue
Expand Down
81 changes: 81 additions & 0 deletions pkg/validator/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,87 @@ lastName: bar
Error,
[]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{
opts: Opts{
Expand Down

0 comments on commit cf07921

Please sign in to comment.