TIL: what is structual schema, and how to drop unknown fields in a custom resource (CR)
Today, during our pair-(learning/programming) session for the KEP 4595 aka, CEL for CRD AdditionalPrinterColumns, Sreeram and I, came across this article – Future of CRDs: Structural Schemas.
(It’s an old article from 2019, written by Dr. Stefan Schimanski1. I will check if & what anything changed since 2019, but still reading this article in its current state, itself, was a turning point for me w.r.t my understanding of CRD(s).)
For the first time today, I understood - what is structual schema
(something that I keep reading about and keep finding it refereneced everywhere within the kubernetes codebase, over and over again.)
So, Today I Learnt (TIL)2:
An OpenAPI v3 schema is a Structual Schema, if:
- the core of an OpenAPI v3 schema, is made out of the following 7 constructs:
properties
items
additionalProperties
type
nullable
title
descriptions
In addition, all types must be non-empty, and in each sub-schema only one of properties
, additionalProperties
or items
may be used.
-
if all types are defined
-
the core is extended with value validation following the constraints:
- (i) inside of value validations, there’s no
additionalProperties
,type
,nullable
,title
,description
- (ii) all fields mentioned in value validation are specified in the core.
- (i) inside of value validations, there’s no
Example of a structual schema (understand below as a snippet from a CRD definition file, in the CRD schema
field):
type: object
properties:
spec:
type: object
properties
command:
type: string
minLength: 1 # value validation
shell:
type: string
minLength: 1 # value validation
machines:
type: array
items:
type: string
pattern: “^[a-z0-9]+(-[a-z0-9]+)*$” # value validation
oneOf: # value validation
- required: [“command”] # value validation
- required: [“shell”] # value validation
required: [“spec”] # value validation
This schema is structural:
- because we have only used the permitted OpenAPI constructs (Rule 1).
- and each field (like
spec
,command
,shell
,machines
, …) has their types defined (Rule 2). - plus, all value validations also follow the above defined rules (Rule 3).
Example of a non-structural schema (understand below as a snippet from a CRD definition file, in the CRD schema
field):
properties:
spec:
type: object
properties
command:
type: string
minLength: 1
shell:
type: string
minLength: 1
machines:
type: array
items:
type: string
pattern: “^[a-z0-9]+(-[a-z0-9]+)*$”
oneOf:
- properties:
command:
type: string
required: [“command”]
- properties:
shell:
type: string
required: [“shell”]
not:
properties:
privileged: {}
required: [“spec”]
This spec is non-structural for many reasons:
type:
object at the root is missing (Rule 2).- inside of
oneOf
it is not allowed to usetype
(Rule 3-i). - inside of
not
the propertyprivileged
is mentioned, but it is not specified in the core (Rule 3-ii).
The non-structural schema, highlighted a big problem (before we had structural schema in place), that is - if we can’t use not
to indicate the Kubernetes API server to drop the privileged
field, then it will be forever preserved by the API server in Etcd.
This was fixed by Pruning. (which is not on by default in apiextensions.k8s.io/v1
but had to be explicitly enabled in apiextensions.k8s.io/v1beta1
).
Pruning in apiextensions.k8s.io/v1beta1
is enabled via:
apiVersion: apiextensions/v1beta1
kind: CustomResourceDefinition
spec:
…
preserveUnknownFields: false
- Kubernetes Deep Dive: Code Generation for CustomResources
- Kubernetes deep dive: API Server - part 1
- Kubernetes Deep Dive: API Server – Part 2
- Kubernetes Deep Dive: API Server – Part 3a
-
well, while searching for links to add as hyperlink for Dr. Stefan Schimanski, I came across more articles written by him, so, those are for my ToDos now. ↩
-
This’s just me rephrasing, all what I learnt from and is mentioned in the original article – Future of CRDs: Structural Schemas. ↩