From 5742ddd7a096570cd7fcc0881176d9dcb189d27b Mon Sep 17 00:00:00 2001 From: Maria Ntalla Date: Wed, 13 Nov 2019 16:13:27 +0000 Subject: [PATCH] Support mixed case Kind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The implementation uses the same validation logic that runs against `Kind` in apimachinery; i.e. allows any `Kind` value that, when converted to lowercase, is a valid DNS-1035 label. Signed-off-by: Hannes Hörl --- pkg/scaffold/resource/resource.go | 25 +++++++++++++++++++-- pkg/scaffold/resource/resource_test.go | 31 ++++++++++++++------------ 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/pkg/scaffold/resource/resource.go b/pkg/scaffold/resource/resource.go index 2d06bf57431..4afdf70ae62 100644 --- a/pkg/scaffold/resource/resource.go +++ b/pkg/scaffold/resource/resource.go @@ -79,8 +79,11 @@ func (r *Resource) Validate() error { return fmt.Errorf( "version must match ^v\\d+(alpha\\d+|beta\\d+)?$ (was %s)", r.Version) } - if r.Kind != flect.Pascalize(r.Kind) { - return fmt.Errorf("kind must be PascalCase (expected %s was %s)", flect.Pascalize(r.Kind), r.Kind) + + validationErrors := isDNS1035Label(strings.ToLower(r.Kind)) + + if len(validationErrors) != 0 { + return fmt.Errorf("Kind failed validation: %#v", validationErrors) } return nil @@ -96,9 +99,16 @@ const ( // dns1123SubdomainMaxLength is a subdomain's max length in DNS (RFC 1123) dns1123SubdomainMaxLength int = 253 + + dns1035LabelFmt string = "[a-z]([-a-z0-9]*[a-z0-9])?" + dns1035LabelErrMsg string = "a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character" + + // DNS1035LabelMaxLength is a label's max length in DNS (RFC 1035) + dns1035LabelMaxLength int = 63 ) var dns1123SubdomainRegexp = regexp.MustCompile("^" + dns1123SubdomainFmt + "$") +var dns1035LabelRegexp = regexp.MustCompile("^" + dns1035LabelFmt + "$") // IsDNS1123Subdomain tests for a string that conforms to the definition of a // subdomain in DNS (RFC 1123). @@ -113,6 +123,17 @@ func IsDNS1123Subdomain(value string) []string { return errs } +func isDNS1035Label(value string) []string { + var errs []string + if len(value) > dns1035LabelMaxLength { + errs = append(errs, maxLenError(dns1035LabelMaxLength)) + } + if !dns1035LabelRegexp.MatchString(value) { + errs = append(errs, regexError(dns1035LabelErrMsg, dns1035LabelFmt, "my-name", "abc-123")) + } + return errs +} + // MaxLenError returns a string explanation of a "string too long" validation // failure. func maxLenError(length int) string { diff --git a/pkg/scaffold/resource/resource_test.go b/pkg/scaffold/resource/resource_test.go index 551a33904b4..6454b5b1b2f 100644 --- a/pkg/scaffold/resource/resource_test.go +++ b/pkg/scaffold/resource/resource_test.go @@ -71,24 +71,27 @@ var _ = Describe("Resource", func() { Expect(instance.Validate().Error()).To(ContainSubstring("kind cannot be empty")) }) - It("should fail if the Kind is not pascal cased", func() { - // Base case - instance := &Resource{Group: "crew", Kind: "FirstMate", Version: "v1"} + It("should succeed if the Kind is valid, according to core Kubernetes", func() { + instance := &Resource{Group: "crew", Kind: "fIRstMate", Version: "v1"} Expect(instance.Validate()).To(Succeed()) + }) - // Can't detect this case :( - instance = &Resource{Group: "crew", Kind: "Firstmate", Version: "v1"} - Expect(instance.Validate()).To(Succeed()) + It("should fail if Kind is too long", func() { + var kind string - instance = &Resource{Group: "crew", Kind: "firstMate", Version: "v1"} - Expect(instance.Validate()).NotTo(Succeed()) - Expect(instance.Validate().Error()).To(ContainSubstring( - `kind must be PascalCase (expected FirstMate was firstMate)`)) + for i := 0; i < 65; i++ { + kind += "a" + } - instance = &Resource{Group: "crew", Kind: "firstmate", Version: "v1"} - Expect(instance.Validate()).NotTo(Succeed()) - Expect(instance.Validate().Error()).To(ContainSubstring( - `kind must be PascalCase (expected Firstmate was firstmate)`)) + instance := &Resource{Group: "crew", Kind: kind, Version: "v1"} + err := instance.Validate() + Expect(err).To(MatchError(ContainSubstring("must be no more than 63 characters"))) + }) + + It("should fail if Kind is invalid, according to core Kubernetes", func() { + instance := &Resource{Group: "crew", Kind: "Something withSpaces", Version: "v1"} + err := instance.Validate() + Expect(err).To(MatchError(ContainSubstring("a DNS-1035 label must consist of lower case alphanumeric characters"))) }) It("should default the Resource by pluralizing the Kind", func() {