Skip to content

Commit

Permalink
Support mixed case Kind
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
Maria Ntalla authored and hoegaarden committed Nov 13, 2019
1 parent 1705128 commit 5742ddd
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 16 deletions.
25 changes: 23 additions & 2 deletions pkg/scaffold/resource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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).
Expand All @@ -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 {
Expand Down
31 changes: 17 additions & 14 deletions pkg/scaffold/resource/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down

0 comments on commit 5742ddd

Please sign in to comment.