From 6fca3f8a9f273787604297382b93647906e8b82a Mon Sep 17 00:00:00 2001 From: Joel Speed Date: Tue, 23 Apr 2024 17:47:04 +0100 Subject: [PATCH] Add FieldPath and Reason to XValidation marker --- pkg/crd/markers/validation.go | 14 ++++++++++++++ pkg/crd/markers/zz_generated.markerhelp.go | 8 ++++++++ pkg/crd/testdata/cronjob_types.go | 5 +++++ .../testdata.kubebuilder.io_cronjobs.yaml | 17 ++++++++++++++--- 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/pkg/crd/markers/validation.go b/pkg/crd/markers/validation.go index 245c5ef92..06d1cab6d 100644 --- a/pkg/crd/markers/validation.go +++ b/pkg/crd/markers/validation.go @@ -312,6 +312,8 @@ type XValidation struct { Rule string Message string `marker:",optional"` MessageExpression string `marker:"messageExpression,optional"` + Reason string `marker:"reason,optional"` + FieldPath string `marker:"fieldPath,optional"` } func (m Maximum) ApplyToSchema(schema *apiext.JSONSchemaProps) error { @@ -534,10 +536,22 @@ func (m XIntOrString) ApplyPriority() ApplyPriority { } func (m XValidation) ApplyToSchema(schema *apiext.JSONSchemaProps) error { + var reason *apiext.FieldValueErrorReason + if m.Reason != "" { + switch m.Reason { + case string(apiext.FieldValueRequired), string(apiext.FieldValueInvalid), string(apiext.FieldValueForbidden), string(apiext.FieldValueDuplicate): + reason = (*apiext.FieldValueErrorReason)(&m.Reason) + default: + return fmt.Errorf("invalid reason %s, valid values are %s, %s, %s and %s", m.Reason, apiext.FieldValueRequired, apiext.FieldValueInvalid, apiext.FieldValueForbidden, apiext.FieldValueDuplicate) + } + } + schema.XValidations = append(schema.XValidations, apiext.ValidationRule{ Rule: m.Rule, Message: m.Message, MessageExpression: m.MessageExpression, + Reason: reason, + FieldPath: m.FieldPath, }) return nil } diff --git a/pkg/crd/markers/zz_generated.markerhelp.go b/pkg/crd/markers/zz_generated.markerhelp.go index c10ffb2fc..73432ebd4 100644 --- a/pkg/crd/markers/zz_generated.markerhelp.go +++ b/pkg/crd/markers/zz_generated.markerhelp.go @@ -523,6 +523,14 @@ func (XValidation) Help() *markers.DefinitionHelp { Summary: "", Details: "", }, + "Reason": { + Summary: "", + Details: "", + }, + "FieldPath": { + Summary: "", + Details: "", + }, }, } } diff --git a/pkg/crd/testdata/cronjob_types.go b/pkg/crd/testdata/cronjob_types.go index d1754a8d3..015197316 100644 --- a/pkg/crd/testdata/cronjob_types.go +++ b/pkg/crd/testdata/cronjob_types.go @@ -39,6 +39,7 @@ import ( // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. // CronJobSpec defines the desired state of CronJob +// +kubebuilder:validation:XValidation:rule="has(oldSelf.forbiddenInt) || !has(self.forbiddenInt)",message="forbiddenInt is not allowed",fieldPath=".forbiddenInt",reason="FieldValueForbidden" type CronJobSpec struct { // The schedule in Cron format, see https://en.wikipedia.org/wiki/Cron. Schedule string `json:"schedule"` @@ -251,6 +252,10 @@ type CronJobSpec struct { // +kubebuilder:validation:XValidation:rule="self.size() % 2 == 0",messageExpression="'Length has to be even but is ' + len(self.stringWithEvenLengthAndMessageExpression) + ' instead'" StringWithEvenLengthAndMessageExpression string `json:"stringWithEvenLengthAndMessageExpression,omitempty"` + // Test that we can add a forbidden field using XValidation Reason and FieldPath. + // The validation is applied to the spec struct itself and not the field. + ForbiddenInt int `json:"forbiddenInt,omitempty"` + // Checks that fixed-length arrays work Array [3]int `json:"array,omitempty"` diff --git a/pkg/crd/testdata/testdata.kubebuilder.io_cronjobs.yaml b/pkg/crd/testdata/testdata.kubebuilder.io_cronjobs.yaml index ef633766e..757f7333d 100644 --- a/pkg/crd/testdata/testdata.kubebuilder.io_cronjobs.yaml +++ b/pkg/crd/testdata/testdata.kubebuilder.io_cronjobs.yaml @@ -196,6 +196,11 @@ spec: description: This tests that exported fields are not skipped in the schema generation type: string + forbiddenInt: + description: |- + Test that we can add a forbidden field using XValidation Reason and FieldPath. + The validation is applied to the spec struct itself and not the field. + type: integer hosts: description: This tests string slice item validation. items: @@ -6806,11 +6811,12 @@ spec: rule: self.size() % 2 == 0 - rule: "true" stringWithEvenLengthAndMessageExpression: - description: Test of the expression-based validation with - messageExpression marker. + description: Test of the expression-based validation with messageExpression + marker. type: string x-kubernetes-validations: - - messageExpression: "'Length has to be even but is ' + len(self.stringWithEvenLengthAndMessageExpression) + ' instead'" + - messageExpression: '''Length has to be even but is '' + len(self.stringWithEvenLengthAndMessageExpression) + + '' instead''' rule: self.size() % 2 == 0 structWithSeveralFields: description: A struct that can only be entirely replaced @@ -6903,6 +6909,11 @@ spec: - unprunedFomTypeAndField - unprunedJSON type: object + x-kubernetes-validations: + - fieldPath: .forbiddenInt + message: forbiddenInt is not allowed + reason: FieldValueForbidden + rule: has(oldSelf.forbiddenInt) || !has(self.forbiddenInt) status: description: CronJobStatus defines the observed state of CronJob properties: