3 minute read  

CRD Validations for etcd

  • The validations for the fields within the etcd resource are done via kubebuilder markers for CRD validation.
  • The validations for clusters with kubernetes versions >= 1.29 are written using a combination of CEL expressions via the x-validation tag which provides a straightforward syntax to write validation rules for the fields, and pattern matching with the use of the validation tag.
  • The validations for clusters with kubernetes versions < 1.29 will not contain validations via CEL expressions since this is GA for kubernetes version 1.29 or higher.
  • Upon any changes to the validation rules to the etcd resource, the yaml files for the same can be generated by running the make generate command.

Validation rules:

Type Validation rules:

The validations for fields of types Duration(metav1.Duration) and cron expressions are done via regex matching. These use the validation:Pattern marker.(The checking for the Quantity(resource.Quantity) fields are done by default, hence, no explicit validation is needed for the fields of this type):

  • Duration fields: '^([0-9]+([.][0-9]+)?h)?([0-9]+([.][0-9]+)?m)?([0-9]+([.][0-9]+)?s)?([0-9]+([.][0-9]+)?d)?$')

  • Cron expression: ^(\*|[1-5]?[0-9]|[1-5]?[0-9]-[1-5]?[0-9]|(?:[1-9]|[1-4][0-9]|5[0-9])\/(?:[1-9]|[1-4][0-9]|5[0-9]|60)|\*\/(?:[1-9]|[1-4][0-9]|5[0-9]|60))\s+(\*|[0-9]|1[0-9]|2[0-3]|[0-9]-(?:[0-9]|1[0-9]|2[0-3])|1[0-9]-(?:1[0-9]|2[0-3])|2[0-3]-2[0-3]|(?:[1-9]|1[0-9]|2[0-3])\/(?:[1-9]|1[0-9]|2[0-4])|\*\/(?:[1-9]|1[0-9]|2[0-4]))\s+(\*|[1-9]|[12][0-9]|3[01]|[1-9]-(?:[1-9]|[12][0-9]|3[01])|[12][0-9]-(?:[12][0-9]|3[01])|3[01]-3[01]|(?:[1-9]|[12][0-9]|30)\/(?:[1-9]|[12][0-9]|3[01])|\*\/(?:[1-9]|[12][0-9]|3[01]))\s+(\*|[1-9]|1[0-2]|[1-9]-(?:[1-9]|1[0-2])|1[0-2]-1[0-2]|(?:[1-9]|1[0-2])\/(?:[1-9]|1[0-2])|\*\/(?:[1-9]|1[0-2]))\s+(\*|[1-7]|[1-6]-[1-7]|[1-6]\/[1-7]|\*\/[1-7])$

    • NOTE: The provided regex does not account for special strings such as @yearly or @monthly. Additionally, it fails to invalidate cases involving the step operator (x/y) and the range operator (x-y), where the cron expression is considered valid even if x > y. Please ensure these values are validated before passing the expression.

Update validations

These validations are triggered when an update operation is done on the etcd resource.

  • Immutable fields: The fields etcd.spec.StorageClass , etcd.spec.StorageCapacity and etcd.spec.VolumeClaimTemplate are immutable. The immutability is enforced by the CEL expression : self == oldSelf.

  • The value set for the field etcd.spec.replicas can either be decreased to 0 or increased. This is enforced by the CEL expression: self==0 ? true : self < oldSelf ? false : true

Field validations

  • The fields which expect only a particular set of values are checked by using the kubebuilder marker: +kubebuilder:validation:Enum=<value1>;<value2>
    • The etcd.spec.etcd.metrics can only be set as either basic or extensive.
    • The etcd.spec.backup.garbageCollectionPolicy can only be set as Exponential or LimitBased
    • The etcd.spec.backup.compression.policy can only be set as either gzip or lzw or zlib.
    • The etcd.spec.sharedConfig.autoCompactionMode can only be set as either periodic or revision.
  • The value of etcd.spec.backup.garbageCollectionPeriod must be greater than etcd.spec.backup.deltaSnapshotPeriod. This is enforced by the CEL expression !(has(self.deltaSnapshotPeriod) && has(self.garbageCollectionPeriod)) || duration(self.deltaSnapshotPeriod).getSeconds() < duration(self.garbageCollectionPeriod).getSeconds(). The first part of the expression ensures that both the fields are present and then compares the values of the garbageCollectionPeriod and deltaSnapshotPeriod fields, if not, skips the check.

  • The value of etcd.spec.StorageCapacity must be more than 3 times that of the etcd.spec.etcd.quota if backups are enabled. If not, the value must be greater than that of the etcd.spec.etcd.quota field. This is enforced by using the CEL expression: has(self.storageCapacity) && has(self.etcd.quota) ? (has(self.backup.store) ? quantity(self.storageCapacity).compareTo(quantity(self.etcd.quota).add(quantity(self.etcd.quota)).add(quantity(self.etcd.quota))) > 0 : quantity(self.storageCapacity).compareTo(quantity(self.etcd.quota)) > 0 ): true The check for whether backups are enabled or not is done by checking if the field etcd.spec.backup.store exists.