@@ -427,6 +427,8 @@ type VariableSchema struct {
427
427
OpenAPIV3Schema JSONSchemaProps `json:"openAPIV3Schema"`
428
428
}
429
429
430
+ // Adapted from https://github.com/kubernetes/apiextensions-apiserver/blob/v0.28.5/pkg/apis/apiextensions/v1/types_jsonschema.go#L40
431
+
430
432
// JSONSchemaProps is a JSON-Schema following Specification Draft 4 (http://json-schema.org/).
431
433
// This struct has been initially copied from apiextensionsv1.JSONSchemaProps, but all fields
432
434
// which are not supported in CAPI have been removed.
@@ -461,6 +463,16 @@ type JSONSchemaProps struct {
461
463
// +kubebuilder:validation:Schemaless
462
464
AdditionalProperties * JSONSchemaProps `json:"additionalProperties,omitempty"`
463
465
466
+ // MaxProperties is the maximum amount of entries in a map or properties in an object.
467
+ // NOTE: Can only be set if type is object.
468
+ // +optional
469
+ MaxProperties * int64 `json:"maxProperties,omitempty"`
470
+
471
+ // MinProperties is the minimum amount of entries in a map or properties in an object.
472
+ // NOTE: Can only be set if type is object.
473
+ // +optional
474
+ MinProperties * int64 `json:"minProperties,omitempty"`
475
+
464
476
// Required specifies which fields of an object are required.
465
477
// NOTE: Can only be set if type is object.
466
478
// +optional
@@ -551,7 +563,123 @@ type JSONSchemaProps struct {
551
563
// NOTE: Can be set for all types.
552
564
// +optional
553
565
Default * apiextensionsv1.JSON `json:"default,omitempty"`
554
- }
566
+
567
+ // XValidations describes a list of validation rules written in the CEL expression language.
568
+ // +optional
569
+ // +listType=map
570
+ // +listMapKey=rule
571
+ XValidations []ValidationRule `json:"x-kubernetes-validations,omitempty"`
572
+ }
573
+
574
+ // ValidationRule describes a validation rule written in the CEL expression language.
575
+ type ValidationRule struct {
576
+ // Rule represents the expression which will be evaluated by CEL.
577
+ // ref: https://github.com/google/cel-spec
578
+ // The Rule is scoped to the location of the x-kubernetes-validations extension in the schema.
579
+ // The `self` variable in the CEL expression is bound to the scoped value.
580
+ // If the Rule is scoped to an object with properties, the accessible properties of the object are field selectable
581
+ // via `self.field` and field presence can be checked via `has(self.field)`.
582
+ // If the Rule is scoped to an object with additionalProperties (i.e. a map) the value of the map
583
+ // are accessible via `self[mapKey]`, map containment can be checked via `mapKey in self` and all entries of the map
584
+ // are accessible via CEL macros and functions such as `self.all(...)`.
585
+ // If the Rule is scoped to an array, the elements of the array are accessible via `self[i]` and also by macros and
586
+ // functions.
587
+ // If the Rule is scoped to a scalar, `self` is bound to the scalar value.
588
+ // Examples:
589
+ // - Rule scoped to a map of objects: {"rule": "self.components['Widget'].priority < 10"}
590
+ // - Rule scoped to a list of integers: {"rule": "self.values.all(value, value >= 0 && value < 100)"}
591
+ // - Rule scoped to a string value: {"rule": "self.startsWith('kube')"}
592
+ //
593
+ // Unknown data preserved in custom resources via x-kubernetes-preserve-unknown-fields is not accessible in CEL
594
+ // expressions. This includes:
595
+ // - Unknown field values that are preserved by object schemas with x-kubernetes-preserve-unknown-fields.
596
+ // - Object properties where the property schema is of an "unknown type". An "unknown type" is recursively defined as:
597
+ // - A schema with no type and x-kubernetes-preserve-unknown-fields set to true
598
+ // - An array where the items schema is of an "unknown type"
599
+ // - An object where the additionalProperties schema is of an "unknown type"
600
+ //
601
+ // Only property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible.
602
+ // Accessible property names are escaped according to the following rules when accessed in the expression:
603
+ // - '__' escapes to '__underscores__'
604
+ // - '.' escapes to '__dot__'
605
+ // - '-' escapes to '__dash__'
606
+ // - '/' escapes to '__slash__'
607
+ // - Property names that exactly match a CEL RESERVED keyword escape to '__{keyword}__'. The keywords are:
608
+ // "true", "false", "null", "in", "as", "break", "const", "continue", "else", "for", "function", "if",
609
+ // "import", "let", "loop", "package", "namespace", "return".
610
+ // Examples:
611
+ // - Rule accessing a property named "namespace": {"rule": "self.__namespace__ > 0"}
612
+ // - Rule accessing a property named "x-prop": {"rule": "self.x__dash__prop > 0"}
613
+ // - Rule accessing a property named "redact__d": {"rule": "self.redact__underscores__d > 0"}
614
+ //
615
+ //
616
+ // If `rule` makes use of the `oldSelf` variable it is implicitly a
617
+ // `transition rule`.
618
+ //
619
+ // By default, the `oldSelf` variable is the same type as `self`.
620
+ //
621
+ // Transition rules by default are applied only on UPDATE requests and are
622
+ // skipped if an old value could not be found.
623
+ //
624
+ // +kubebuilder:validation:Required
625
+ Rule string `json:"rule"`
626
+ // Message represents the message displayed when validation fails. The message is required if the Rule contains
627
+ // line breaks. The message must not contain line breaks.
628
+ // If unset, the message is "failed rule: {Rule}".
629
+ // e.g. "must be a URL with the host matching spec.host"
630
+ // +optional
631
+ Message string `json:"message,omitempty"`
632
+ // MessageExpression declares a CEL expression that evaluates to the validation failure message that is returned when this rule fails.
633
+ // Since messageExpression is used as a failure message, it must evaluate to a string.
634
+ // If both message and messageExpression are present on a rule, then messageExpression will be used if validation
635
+ // fails. If messageExpression results in a runtime error, the validation failure message is produced
636
+ // as if the messageExpression field were unset. If messageExpression evaluates to an empty string, a string with only spaces, or a string
637
+ // that contains line breaks, then the validation failure message will also be produced as if the messageExpression field were unset.
638
+ // messageExpression has access to all the same variables as the rule; the only difference is the return type.
639
+ // Example:
640
+ // "x must be less than max ("+string(self.max)+")"
641
+ // +optional
642
+ MessageExpression string `json:"messageExpression,omitempty"`
643
+ // Reason provides a machine-readable validation failure reason that is returned to the caller when a request fails this validation rule.
644
+ // The currently supported reasons are: "FieldValueInvalid", "FieldValueForbidden", "FieldValueRequired", "FieldValueDuplicate".
645
+ // If not set, default to use "FieldValueInvalid".
646
+ // All future added reasons must be accepted by clients when reading this value and unknown reasons should be treated as FieldValueInvalid.
647
+ // +optional
648
+ // +kubebuilder:validation:Enum=FieldValueInvalid;FieldValueForbidden;FieldValueRequired;FieldValueDuplicate
649
+ // +kubebuilder:default=FieldValueInvalid
650
+ // +default=ref(sigs.k8s.io/cluster-api/api/v1beta1.FieldValueInvalid)
651
+ Reason FieldValueErrorReason `json:"reason,omitempty"`
652
+ // FieldPath represents the field path returned when the validation fails.
653
+ // It must be a relative JSON path (i.e. with array notation) scoped to the location of this x-kubernetes-validations extension in the schema and refer to an existing field.
654
+ // e.g. when validation checks if a specific attribute `foo` under a map `testMap`, the fieldPath could be set to `.testMap.foo`
655
+ // If the validation checks two lists must have unique attributes, the fieldPath could be set to either of the list: e.g. `.testList`
656
+ // It does not support list numeric index.
657
+ // It supports child operation to refer to an existing field currently. Refer to [JSONPath support in Kubernetes](https://kubernetes.io/docs/reference/kubectl/jsonpath/) for more info.
658
+ // Numeric index of array is not supported.
659
+ // For field name which contains special characters, use `['specialName']` to refer the field name.
660
+ // e.g. for attribute `foo.34$` appears in a list `testList`, the fieldPath could be set to `.testList['foo.34$']`
661
+ // +optional
662
+ FieldPath string `json:"fieldPath,omitempty"`
663
+ }
664
+
665
+ // FieldValueErrorReason is a machine-readable value providing more detail about why a field failed the validation.
666
+ type FieldValueErrorReason string
667
+
668
+ const (
669
+ // FieldValueRequired is used to report required values that are not
670
+ // provided (e.g. empty strings, null values, or empty arrays).
671
+ FieldValueRequired FieldValueErrorReason = "FieldValueRequired"
672
+ // FieldValueDuplicate is used to report collisions of values that must be
673
+ // unique (e.g. unique IDs).
674
+ FieldValueDuplicate FieldValueErrorReason = "FieldValueDuplicate"
675
+ // FieldValueInvalid is used to report malformed values (e.g. failed regex
676
+ // match, too long, out of bounds).
677
+ FieldValueInvalid FieldValueErrorReason = "FieldValueInvalid"
678
+ // FieldValueForbidden is used to report valid (as per formatting rules)
679
+ // values which would be accepted under some conditions, but which are not
680
+ // permitted by the current conditions (such as security policy).
681
+ FieldValueForbidden FieldValueErrorReason = "FieldValueForbidden"
682
+ )
555
683
556
684
// ClusterClassPatch defines a patch which is applied to customize the referenced templates.
557
685
type ClusterClassPatch struct {
0 commit comments