|
| 1 | +<!-- |
| 2 | +# Copyright 2025 Crunchy Data Solutions, Inc. |
| 3 | +# |
| 4 | +# SPDX-License-Identifier: Apache-2.0 |
| 5 | +--> |
| 6 | + |
| 7 | +# Custom Resource Definitions |
| 8 | + |
| 9 | +These directories contain Go types that serve as [DTO]s for communicating with the Kubernetes API. |
| 10 | +We use the [controller-gen] tool to produce [CRD]s with schemas that match the Go types. |
| 11 | +The CRD schema tells Kubernetes what fields and values are allowed in our API objects and how to handle changes to values. |
| 12 | + |
| 13 | +> [!TIP] |
| 14 | +> The CRD schema is *most* of the UX of our API |
| 15 | +
|
| 16 | +CRD schemas are modified OpenAPI 3.0 [validation] schemas. |
| 17 | +Much of the schema defines what fields, types, and values are *allowed*. |
| 18 | +`controller-gen` considers the [Go type] of a field and its [validation markers] for this. |
| 19 | + |
| 20 | +Kubernetes uses its own algorithm to consider and accept changes to API objects: [Server-Side Apply], SSA. |
| 21 | +CRD schemas contain non-standard attributes that affect SSA. |
| 22 | +`controller-gen` considers the [processing markers] of a field for this. |
| 23 | + |
| 24 | +🤔 Validation is *what* and SSA/processing is *how* 🤔 |
| 25 | + |
| 26 | +[controller-gen]: https://book.kubebuilder.io/reference/controller-gen |
| 27 | +[CRD]: https://docs.k8s.io/tasks/extend-kubernetes/custom-resources/custom-resource-definitions |
| 28 | +[DTO]: https://martinfowler.com/eaaCatalog/dataTransferObject.html |
| 29 | +[Go type]: https://go.dev/ref/spec#Types |
| 30 | +[Kubernetes API]: https://docs.k8s.io/concepts/overview/kubernetes-api |
| 31 | +[processing markers]: https://book.kubebuilder.io/reference/markers/crd-processing |
| 32 | +[Server-Side Apply]: https://docs.k8s.io/reference/using-api/server-side-apply |
| 33 | +[validation]: https://docs.k8s.io/tasks/extend-kubernetes/custom-resources/custom-resource-definitions#validation |
| 34 | +[validation markers]: https://book.kubebuilder.io/reference/markers/crd-validation |
| 35 | + |
| 36 | + |
| 37 | +# OpenAPI Properties |
| 38 | + |
| 39 | +Kubernetes supports a subset of the OpenAPI schema definition, which is a subset of an old draft of [JSON schema]. |
| 40 | + |
| 41 | +Fields can have these properties: |
| 42 | + |
| 43 | +- `enum` is a list of values allowed in the field. |
| 44 | +- `required` is a boolean indicating the field must be present in whatever payload. |
| 45 | + This does *NOT* indicate anything about the value being `null`, "zero," or "empty." |
| 46 | +- `nullable` is a boolean indicating if the value can be `null`. |
| 47 | + When this is omitted or `false`, a `null` value is removed from whatever payload or replaced with the field's [default value]. |
| 48 | +- `type` is one of `integer`, `number`, `string`, `boolean`, `array`, or `object`. |
| 49 | +- `format` constrains the `type` a bit further. |
| 50 | + It affects how `kubectl` presents the field and how the value turns up in CEL validation. |
| 51 | + |
| 52 | +Numeric fields can have these properties: |
| 53 | + |
| 54 | +- `minimum` and `maximum` are the smallest and largest numbers allowed in the field. |
| 55 | +- `exclusiveMinimum` and `exclusiveMaximum` are booleans that indicate if the exact values above are allowed. |
| 56 | + That is, `true` here changes min and max from ≥ and ≤ to > and <. |
| 57 | +- `multipleOf` is a number. Values in the field must be nicely divisible by this number. |
| 58 | + |
| 59 | +String fields can have these properties: |
| 60 | + |
| 61 | +- `minLength` and `maxLength` are the smallest and largest number of characters allowed in the field. |
| 62 | +- `pattern` is a regular expression describing values allowed in the field. |
| 63 | + In Kubernetes, this is an [RE2] expression, *NOT* an ECMA expression. |
| 64 | + |
| 65 | +Array fields can have these properties: |
| 66 | + |
| 67 | +- `items` is a schema that describes what values are allowed inside the field. |
| 68 | +- `minItems` and `maxItems` are the smallest and largest number of items allowed in the field. |
| 69 | + |
| 70 | +The value of an object field is an unordered set of key/value pairs; a JSON object, a YAML mapping. |
| 71 | +Kubernetes supports only two ways to treat the keys of those values: known or unknown. |
| 72 | + |
| 73 | +The `properties` property indicates that the keys are known; these fields can have these properties: |
| 74 | + |
| 75 | +- `properties` is an unordered set of key/value pairs (JSON object, YAML mapping) in which: |
| 76 | + - the key is the name of a field allowed in the object |
| 77 | + - the value is a schema that describes what values are allowed in that field. |
| 78 | + |
| 79 | +The `additionalProperties` property indicates that the keys are unknown; these fields can have these properties: |
| 80 | + |
| 81 | +- `additionalProperties` is a schema that describes what values are allowed in every... key-value value of the field. |
| 82 | +- `minProperties` and `maxProperties` are the smallest and largest number of key-value keys allowed in the field. |
| 83 | + |
| 84 | +> [!TIP] |
| 85 | +> `properties` is like a Go struct // `additionalProperties` is like a Go map |
| 86 | +
|
| 87 | +[default value]: https://docs.k8s.io/tasks/extend-kubernetes/custom-resources/custom-resource-definitions#defaulting-and-nullable |
| 88 | +[JSON schema]: https://json-schema.org/draft-06 |
| 89 | +[RE2]: https://github.com/google/re2#syntax |
| 90 | + |
| 91 | + |
| 92 | +# CEL Rules |
| 93 | + |
| 94 | +> [!IMPORTANT] |
| 95 | +> When possible, use [OpenAPI properties](#FIXME) rather than CEL rules. |
| 96 | +> The former do not affect the CRD [validation budget](#FIXME). <!-- https://imgur.com/CzpJn3j --> |
| 97 | +
|
0 commit comments