Is there a way to access existing validation specs? For example, I want to be able to set NodeAffinity on my CRD, and would like to just $ref: . I found the entire API here: https://github.com/kubernetes/kubernetes/blob/master/api/openapi-spec/swagger.json OR kubectl proxy -> localhost:8001/openapi/v2 (from within my cluster)
I could manually copy paste the api validation schema, but I was wondering if there was a way to automatically reference an existing OpenAPI Validation Spec from within my CRD with $ref. I imagine something like $ref: localhost:8001/openapi/v2/definitions/io.k8s.api.core.v1.NodeAffinity
If this is even possible, will it resolve the inner $refs as well?
For reference, here's what the nodeaffinity definition looks like in the API:
"io.k8s.api.core.v1.NodeAffinity": {
"description": "Node affinity is a group of node affinity scheduling rules.",
"properties": {
"preferredDuringSchedulingIgnoredDuringExecution": {
"description": "The scheduler will prefer to schedule pods to nodes that satisfy the affinity expressions specified by this field, but it may choose a node that violates one or more of the expressions. The node that is most preferred is the one with the greatest sum of weights, i.e. for each node that meets all of the scheduling requirements (resource request, requiredDuringScheduling affinity expressions, etc.), compute a sum by iterating through the elements of this field and adding \"weight\" to the sum if the node matches the corresponding matchExpressions; the node(s) with the highest sum are the most preferred.",
"items": {
"$ref": "#/definitions/io.k8s.api.core.v1.PreferredSchedulingTerm"
},
"type": "array"
},
"requiredDuringSchedulingIgnoredDuringExecution": {
"$ref": "#/definitions/io.k8s.api.core.v1.NodeSelector",
"description": "If the affinity requirements specified by this field are not met at scheduling time, the pod will not be scheduled onto the node. If the affinity requirements specified by this field cease to be met at some point during pod execution (e.g. due to an update), the system may or may not try to eventually evict the pod from its node."
}
},
"type": "object"
},
(using Operator-SDK with Ansible, incase that matters)
EDIT: (adding a full example to further explain)
I have a CRD called Workshop, and I require validation on certain spec parameters.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: workshops.k8s.example.tk
spec:
group: k8s.example.tk
names:
kind: Workshop
listKind: WorkshopList
plural: workshops
singular: workshop
scope: Namespaced
subresources:
status: {}
validation:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required:
- workshopID
properties:
workshopID: #
type: string
description: Unique identifier for this particular virtual
workshop
example: d8e8fca2dc0f896fd7cb4cb0031ba249
Now I need to add a nodeAffinity spec field that will be applied to any pods that live under this CustomResourceDefinition. The validation for it is going to be the exact same as the validation for nodeAffinity in pods.
Let me pull the validation spec that is ALREADY WRITTEN in OpenApi from: https://github.com/kubernetes/kubernetes/blob/master/api/openapi-spec/swagger.json and convert it to YAML then add it to my spec.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: workshops.k8s.example.tk
spec:
group: k8s.example.tk
names:
kind: Workshop
listKind: WorkshopList
plural: workshops
singular: workshop
scope: Namespaced
subresources:
status: {}
validation:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required:
- workshopID
properties:
workshopID: #
type: string
description: Unique identifier for this particular virtual
workshop
example: d8e8fca2dc0f896fd7cb4cb0031ba249
affinity: #
type: object
properties:
nodeAffinity: #
description: Node affinity is a group of node affinity scheduling rules.
type: object
properties:
preferredDuringSchedulingIgnoredDuringExecution:
description: The scheduler will prefer to schedule pods to nodes that satisfy
the affinity expressions specified by this field, but it may choose a node that
violates one or more of the expressions. The node that is most preferred is
the one with the greatest sum of weights, i.e. for each node that meets all
of the scheduling requirements (resource request, requiredDuringScheduling affinity
expressions, etc.), compute a sum by iterating through the elements of this
field and adding "weight" to the sum if the node matches the corresponding matchExpressions;
the node(s) with the highest sum are the most preferred.
type: array
items:
description: An empty preferred scheduling term matches all objects with implicit
weight 0 (i.e. it's a no-op). A null preferred scheduling term matches no
objects (i.e. is also a no-op).
type: object
required:
- weight
- preference
properties:
preference:
description: A node selector term, associated with the corresponding weight.
A null or empty node selector term matches no objects. The requirements
of them are ANDed. The TopologySelectorTerm type implements a subset of
the NodeSelectorTerm.
type: object
properties:
matchExpressions:
description: A list of node selector requirements by node's labels.
type: array
items:
description: A node selector requirement is a selector that contains
values, a key, and an operator that relates the key and values.
type: object
required:
- key
- operator
properties:
key:
description: The label key that the selector applies to.
type: string
operator:
description: Represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and
Lt.
type: string
values:
description: An array of string values. If the operator is In
or NotIn, the values array must be non-empty. If the operator
is Exists or DoesNotExist, the values array must be empty. If
the operator is Gt or Lt, the values array must have a single
element, which will be interpreted as an integer. This array
is replaced during a strategic merge patch.
type: array
items:
type: string
matchFields:
description: A list of node selector requirements by node's fields.
type: array
items:
description: A node selector requirement is a selector that contains
values, a key, and an operator that relates the key and values.
type: object
required:
- key
- operator
properties:
key:
description: The label key that the selector applies to.
type: string
operator:
description: Represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and
Lt.
type: string
values:
description: An array of string values. If the operator is In
or NotIn, the values array must be non-empty. If the operator
is Exists or DoesNotExist, the values array must be empty. If
the operator is Gt or Lt, the values array must have a single
element, which will be interpreted as an integer. This array
is replaced during a strategic merge patch.
type: array
items:
type: string
weight:
description: Weight associated with matching the corresponding nodeSelectorTerm,
in the range 1-100.
type: integer
format: int32
requiredDuringSchedulingIgnoredDuringExecution:
description: If the affinity requirements specified by this field are not met
at scheduling time, the pod will not be scheduled onto the node. If the affinity
requirements specified by this field cease to be met at some point during pod
execution (e.g. due to an update), the system may or may not try to eventually
evict the pod from its node. A node selector represents the union of the results
of one or more label queries over a set of nodes; that is, it represents the
OR of the selectors represented by the node selector terms.
type: object
required:
- nodeSelectorTerms
properties:
nodeSelectorTerms:
description: Required. A list of node selector terms. The terms are ORed.
type: array
items:
description: A null or empty node selector term matches no objects. The
requirements of them are ANDed. The TopologySelectorTerm type implements
a subset of the NodeSelectorTerm.
type: object
properties:
matchExpressions:
description: A list of node selector requirements by node's labels.
type: array
items:
description: A node selector requirement is a selector that contains
values, a key, and an operator that relates the key and values.
type: object
required:
- key
- operator
properties:
key:
description: The label key that the selector applies to.
type: string
operator:
description: Represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and
Lt.
type: string
values:
description: An array of string values. If the operator is In
or NotIn, the values array must be non-empty. If the operator
is Exists or DoesNotExist, the values array must be empty. If
the operator is Gt or Lt, the values array must have a single
element, which will be interpreted as an integer. This array
is replaced during a strategic merge patch.
type: array
items:
type: string
matchFields:
description: A list of node selector requirements by node's fields.
type: array
items:
description: A node selector requirement is a selector that contains
values, a key, and an operator that relates the key and values.
type: object
required:
- key
- operator
properties:
key:
description: The label key that the selector applies to.
type: string
operator:
description: Represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and
Lt.
type: string
values:
description: An array of string values. If the operator is In
or NotIn, the values array must be non-empty. If the operator
is Exists or DoesNotExist, the values array must be empty. If
the operator is Gt or Lt, the values array must have a single
element, which will be interpreted as an integer. This array
is replaced during a strategic merge patch.
type: array
items:
type: string
Wow, for just one field (and its sub fields) to be validated, my CRD definition has grown by 100+ lines, all just to reimplement something that already exists in the Kubernetes-native pod api definition. It also took about 15 minutes to manually copy paste and resolve all the references in the Kubernetes spec by hand. Wouldn't it make so much sense to either:
A) Store this long API spec in an external file, and use $ref: externalfile.json to pull it in to keep my CRD small and clean.
OR BETTER YET
B) Insert the actual Kubernetes-native validation spec that ALREADY EXISTS with a $ref tag like this:
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: workshops.k8s.example.tk
spec:
group: k8s.example.tk
names:
kind: Workshop
listKind: WorkshopList
plural: workshops
singular: workshop
scope: Namespaced
subresources:
status: {}
validation:
openAPIV3Schema:
type: object
properties:
spec:
type: object
required:
- workshopID
properties:
workshopID: #
type: string
description: Unique identifier for this particular virtual
workshop
example: d8e8fca2dc0f896fd7cb4cb0031ba249
affinity:
type: object
properties:
nodeAffinity:
$ref: <kubernetes-api>/openapi/v2#/definitions/io.k8s.api.core.v1.NodeAffinity
Back down to 30 or so lines of code, AND the validation spec stays up-to-date with Kubernetes native validation, since it's pulling the information from Kubernetes API itself. According to this, $ref should be supported in doing this: https://swagger.io/docs/specification/using-ref/#syntax