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 kubernetes-sigs#342

Signed-off-by: Alexander Yastrebov <alexander.yastrebov@zalando.de>
  • Loading branch information
AlexanderYastrebov committed Mar 26, 2024
1 parent 3f5bd8e commit a0d0d21
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 3 deletions.
19 changes: 16 additions & 3 deletions pkg/crd/markers/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,15 @@ func isIntegral(value float64) bool {
return value == math.Trunc(value) && !math.IsNaN(value) && !math.IsInf(value, 0)
}

func thisOrItemsSchema(schema *apiext.JSONSchemaProps) *apiext.JSONSchemaProps {
if schema.Type == "array" &&
schema.Items != nil &&
schema.Items.Schema != nil {
return schema.Items.Schema
}
return schema
}

// +controllertools:marker:generateHelp:category="CRD validation"
// XValidation marks a field as requiring a value for which a given
// expression evaluates to true.
Expand Down Expand Up @@ -359,17 +368,19 @@ func (m MultipleOf) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
}

func (m MaxLength) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
schema = thisOrItemsSchema(schema)
if schema.Type != "string" {
return fmt.Errorf("must apply maxlength to a string")
return fmt.Errorf("must apply maxlength to a string or a string array")
}
val := int64(m)
schema.MaxLength = &val
return nil
}

func (m MinLength) ApplyToSchema(schema *apiext.JSONSchemaProps) error {
schema = thisOrItemsSchema(schema)
if schema.Type != "string" {
return fmt.Errorf("must apply minlength to a string")
return fmt.Errorf("must apply minlength to a string or a string array")
}
val := int64(m)
schema.MinLength = &val
Expand All @@ -380,8 +391,9 @@ 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
// will not apply to ints though.
schema = thisOrItemsSchema(schema)
if schema.Type != "string" && !schema.XIntOrString {
return fmt.Errorf("must apply pattern to a `string` or `IntOrString`")
return fmt.Errorf("must apply pattern to a string or IntOrString or an array of them")
}
schema.Pattern = string(m)
return nil
Expand Down Expand Up @@ -510,6 +522,7 @@ 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 {
schema = thisOrItemsSchema(schema)
schema.XIntOrString = true
return nil
}
Expand Down
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:MinLength=1
// +kubebuilder:validation:MaxLength=255
// +kubebuilder:validation:Pattern=^[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:Pattern="^((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 array item validation.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=255
// +kubebuilder:validation:Pattern=^[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 array 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

0 comments on commit a0d0d21

Please sign in to comment.