From fdf0ebbc389361c94121b8c2fc40c7714aaf99d2 Mon Sep 17 00:00:00 2001 From: shui Date: Mon, 13 Apr 2026 09:09:13 +0800 Subject: [PATCH] fix: support Scala duration format (weeks/days) without P prefix Kubernetes API machinery accepts Scala duration format, e.g., '2w' for 2 weeks and '3d' for 3 days, in addition to Go duration format (e.g., '1h30m') and ISO 8601 format (e.g., 'P2W'). This fix adds validation support for these commonly used duration formats. Closes #347 --- pkg/validator/validator.go | 57 ++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/pkg/validator/validator.go b/pkg/validator/validator.go index 3c9d6f6..7b0e3a1 100644 --- a/pkg/validator/validator.go +++ b/pkg/validator/validator.go @@ -292,16 +292,63 @@ func (val *v) Validate(filename string, r io.ReadCloser) []Result { // https://github.com/kubernetes/apiextensions-apiserver/blob/1ecd29f74da0639e2e6e3b8fac0c9bfd217e05eb/pkg/apis/apiextensions/v1/types_jsonschema.go#L71 func validateDuration(v any) error { // Try validation with the Go duration format - if _, err := time.ParseDuration(v.(string)); err == nil { - return nil - } - s, ok := v.(string) if !ok { return nil } - // must start with 'P' + // Try Go duration format (e.g., "1h30m", "30s") + if _, err := time.ParseDuration(s); err == nil { + return nil + } + + // Kubernetes API machinery also accepts Scala duration format without P prefix + // specifically for weeks (e.g., "2w") and days (e.g., "3d") + // https://github.com/kubernetes/apiextensions-apiserver/blob/1ecd29f74da0639e2e6e3b8fac0c9bfd217e05eb/pkg/apis/apiextensions/v1/types_jsonschema.go#L71 + // Check for weeks (e.g., "2w", "3W") + if s, ok := strings.CutSuffix(s, "w"); ok { + if s != "" { + for _, ch := range s { + if ch < '0' || ch > '9' { + return fmt.Errorf("invalid duration: weeks must be a number") + } + } + return nil + } + } + if s, ok := strings.CutSuffix(s, "W"); ok { + if s != "" { + for _, ch := range s { + if ch < '0' || ch > '9' { + return fmt.Errorf("invalid duration: weeks must be a number") + } + } + return nil + } + } + // Check for days (e.g., "3d", "5D") + if s, ok := strings.CutSuffix(s, "d"); ok { + if s != "" { + for _, ch := range s { + if ch < '0' || ch > '9' { + return fmt.Errorf("invalid duration: days must be a number") + } + } + return nil + } + } + if s, ok := strings.CutSuffix(s, "D"); ok { + if s != "" { + for _, ch := range s { + if ch < '0' || ch > '9' { + return fmt.Errorf("invalid duration: days must be a number") + } + } + return nil + } + } + + // Must start with 'P' for ISO 8601 format s, ok = strings.CutPrefix(s, "P") if !ok { return fmt.Errorf("must start with P")