Skip to content

Commit

Permalink
pkg/crd: support validating internal list items on list types
Browse files Browse the repository at this point in the history
For #342

Signed-off-by: Alexander Yastrebov <alexander.yastrebov@zalando.de>
  • Loading branch information
AlexanderYastrebov committed Mar 27, 2024
1 parent 3f5bd8e commit f718c4e
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 97 deletions.
44 changes: 43 additions & 1 deletion pkg/crd/markers/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ var ValidationMarkers = mustMakeAllWithPrefix("kubebuilder:validation", markers.
MinLength(0),
Pattern(""),

ItemMaxLength(0),
ItemMinLength(0),
ItemPattern(""),

// slice markers

MaxItems(0),
Expand Down Expand Up @@ -168,6 +172,18 @@ type MinLength int
// Pattern specifies that this string must match the given regular expression.
type Pattern string

// +controllertools:marker:generateHelp:category="CRD validation"
// ItemMinLength specifies the minimum length for string items of this array.
type ItemMinLength MinLength

// +controllertools:marker:generateHelp:category="CRD validation"
// ItemMaxLength specifies the maximum length for string items of this array.
type ItemMaxLength MaxLength

// +controllertools:marker:generateHelp:category="CRD validation"
// ItemPattern specifies that this array string items must match the given regular expression.
type ItemPattern Pattern

// +controllertools:marker:generateHelp:category="CRD validation"
// MaxItems specifies the maximum length for this list.
type MaxItems int
Expand Down Expand Up @@ -266,7 +282,7 @@ type XEmbeddedResource struct{}
// IntOrString marks a fields as an IntOrString.
//
// This is required when applying patterns or other validations to an IntOrString
// field. Knwon information about the type is applied during the collapse phase
// field. Known information about the type is applied during the collapse phase
// and as such is not normally available during marker application.
type XIntOrString struct{}

Expand Down Expand Up @@ -367,6 +383,13 @@ func (m MaxLength) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
return nil
}

func (m ItemMaxLength) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
if schema.Type != "array" || schema.Items == nil || schema.Items.Schema == nil {
return fmt.Errorf("must apply MaxLength to a string array")
}
return MaxLength(m).ApplyToSchema(schema.Items.Schema)
}

func (m MinLength) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
if schema.Type != "string" {
return fmt.Errorf("must apply minlength to a string")
Expand All @@ -376,6 +399,13 @@ func (m MinLength) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
return nil
}

func (m ItemMinLength) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
if schema.Type != "array" || schema.Items == nil || schema.Items.Schema == nil {
return fmt.Errorf("must apply ItemMinLength to a string array")
}
return MinLength(m).ApplyToSchema(schema.Items.Schema)
}

func (m Pattern) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
// Allow string types or IntOrStrings. An IntOrString will still
// apply the pattern validation when a string is detected, the pattern
Expand All @@ -387,6 +417,13 @@ func (m Pattern) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
return nil
}

func (m ItemPattern) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
if schema.Type != "array" || schema.Items == nil || schema.Items.Schema == nil {
return fmt.Errorf("must apply ItemPattern to a string array")
}
return Pattern(m).ApplyToSchema(schema.Items.Schema)
}

func (m MaxItems) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
if schema.Type != "array" {
return fmt.Errorf("must apply maxitem to an array")
Expand Down Expand Up @@ -510,6 +547,11 @@ func (m XEmbeddedResource) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
// which means the "XIntOrString" marker *must* be applied first.

func (m XIntOrString) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
if schema.Type == "array" &&
schema.Items != nil &&
schema.Items.Schema != nil {
schema = schema.Items.Schema
}
schema.XIntOrString = true
return nil
}
Expand Down
135 changes: 84 additions & 51 deletions pkg/crd/markers/zz_generated.markerhelp.go

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions pkg/crd/testdata/cronjob_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,24 @@ type CronJobSpec struct {

// Checks that arrays work when the type contains a composite literal
ArrayUsingCompositeLiteral [len(struct{ X [3]int }{}.X)]string `json:"arrayUsingCompositeLiteral,omitempty"`

// This tests string slice item validation.
// +kubebuilder:validation:ItemMinLength=1
// +kubebuilder:validation:ItemMaxLength=255
// +kubebuilder:validation:ItemPattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
Hosts []string `json:"hosts,omitempty"`

HostsAlias Hosts `json:"hostsAlias,omitempty"`

// This tests string alias slice item validation.
LongerStringArray []LongerString `json:"longerStringArray,omitempty"`

// This tests that a slice of IntOrString can also have a pattern attached to it.
// This can be useful if you want to limit the string to a perecentage or integer.
// The XIntOrString marker is a requirement for having a pattern on this type.
// +kubebuilder:validation:XIntOrString
// +kubebuilder:validation:ItemPattern="^((100|[0-9]{1,2})%|[0-9]+)$"
IntOrStringArrayWithAPattern []*intstr.IntOrString `json:"intOrStringArrayWithAPattern,omitempty"`
}

type ContainsNestedMap struct {
Expand Down Expand Up @@ -360,6 +378,12 @@ type LongerString string
// TotallyABool is a bool that serializes as a string.
type TotallyABool bool

// This tests string slice item validation.
// +kubebuilder:validation:ItemMinLength=1
// +kubebuilder:validation:ItemMaxLength=255
// +kubebuilder:validation:ItemPattern=^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type Hosts []string

func (t TotallyABool) MarshalJSON() ([]byte, error) {
if t {
return []byte(`"true"`), nil
Expand Down
36 changes: 36 additions & 0 deletions pkg/crd/testdata/testdata.kubebuilder.io_cronjobs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -196,12 +196,40 @@ spec:
description: This tests that exported fields are not skipped in the
schema generation
type: string
hosts:
description: This tests string slice item validation.
items:
maxLength: 255
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
type: array
hostsAlias:
description: This tests string slice item validation.
items:
maxLength: 255
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
type: array
int32WithValidations:
format: int32
maximum: 2
minimum: -2
multipleOf: 2
type: integer
intOrStringArrayWithAPattern:
description: |-
This tests that a slice of IntOrString can also have a pattern attached to it.
This can be useful if you want to limit the string to a perecentage or integer.
The XIntOrString marker is a requirement for having a pattern on this type.
items:
anyOf:
- type: integer
- type: string
pattern: ^((100|[0-9]{1,2})%|[0-9]+)$
x-kubernetes-int-or-string: true
type: array
intOrStringWithAPattern:
anyOf:
- type: integer
Expand Down Expand Up @@ -6609,6 +6637,14 @@ spec:
- bar
- foo
type: object
longerStringArray:
description: This tests string alias slice item validation.
items:
description: This tests that markers that are allowed on both fields
and types are applied to types
minLength: 4
type: string
type: array
mapOfArraysOfFloats:
additionalProperties:
items:
Expand Down
14 changes: 7 additions & 7 deletions pkg/crd/zz_generated.markerhelp.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pkg/deepcopy/zz_generated.markerhelp.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions pkg/genall/zz_generated.markerhelp.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions pkg/rbac/zz_generated.markerhelp.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions pkg/schemapatcher/zz_generated.markerhelp.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f718c4e

Please sign in to comment.