{"id":8357,"date":"2019-06-20T19:00:02","date_gmt":"2019-06-20T19:00:02","guid":{"rendered":"http:\/\/howk.de\/w1\/blog-future-of-crds-structural-schemas\/"},"modified":"2019-06-20T19:00:02","modified_gmt":"2019-06-20T19:00:02","slug":"blog-future-of-crds-structural-schemas","status":"publish","type":"post","link":"https:\/\/howk.de\/?p=8357","title":{"rendered":"Blog: Future of CRDs: Structural Schemas"},"content":{"rendered":"<p><strong>Authors:<\/strong> Stefan Schimanski (Red Hat)<\/p>\n<p>CustomResourceDefinitions were introduced roughly two years ago as the primary way to extend the Kubernetes API with custom resources. From the beginning they stored arbitrary JSON data, with the exception that <code>kind<\/code>, <code>apiVersion<\/code> and <code>metadata<\/code> had to follow the Kubernetes API conventions. In Kubernetes 1.8 CRDs gained the ability to define an optional OpenAPI v3 based validation schema.<\/p>\n<p>By the nature of OpenAPI specifications though\u2014only describing what must be there, not what shouldn\u2019t, and by being potentially incomplete specifications\u2014the Kubernetes API server never knew the complete structure of CustomResource instances. As a consequence, kube-apiserver\u2014until today\u2014stores all JSON data received in an API request (if it validates against the OpenAPI spec). This especially includes anything that is not specified in the OpenAPI schema.<\/p>\n<h2 id=\"the-story-of-malicious-unspecified-data\">The story of malicious, unspecified data<\/h2>\n<p>To understand this, we assume a CRD for maintenance jobs by the operations team, running each night as a service user:<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#f8f8f8\"><code class=\"language-yaml\" data-lang=\"yaml\">apiVersion:<span style=\"color:#bbb\"> <\/span>operations\/v1<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"><\/span>kind:<span style=\"color:#bbb\"> <\/span>MaintenanceNightlyJob<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"><\/span>spec:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>shell:<span style=\"color:#bbb\"> <\/span><span style=\"color:#b44;font-style:italic\">&gt;\n<\/span><span style=\"color:#b44;font-style:italic\"> grep backdoor \/etc\/passwd ||\n<\/span><span style=\"color:#b44;font-style:italic\"> echo \u201cbackdoor:76asdfh76:\/bin\/bash\u201d &gt;&gt; \/etc\/passwd || true<\/span><span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>machines:<span style=\"color:#bbb\"> <\/span>[\u201caz1-master1\u201d,\u201daz1-master2\u201d,\u201daz2-master3\u201d]<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>privileged:<span style=\"color:#bbb\"> <\/span><span style=\"color:#a2f;font-weight:bold\">true<\/span><\/code><\/pre>\n<\/div>\n<p>The privileged field is not specified by the operations team. Their controller does not know it, and their validating admission webhook does not know about it either. Nevertheless, kube-apiserver persists this suspicious, but unknown field without ever validating it.<\/p>\n<p>When run in the night, this job never fails, but because the service user is not able to write <code>\/etc\/passwd<\/code>, it will also not cause any harm.<\/p>\n<p>The maintenance team needs support for privileged jobs. It adds the <code>privileged<\/code> support, but is super careful to implement authorization for privileged jobs by only allowing those to be created by very few people in the company. That malicious job though has long been persisted to etcd. The next night arrives and the malicious job is executed.<\/p>\n<h2 id=\"towards-complete-knowledge-of-the-data-structure\">Towards complete knowledge of the data structure<\/h2>\n<p>This example shows that we cannot trust CustomResource data in etcd. Without having complete knowledge about the JSON structure, the kube-apsierver cannot do anything to prevent persistence of unknown data.<\/p>\n<p>Kubernetes 1.15 introduces the concept of a (complete) structural OpenAPI schema\u2014an OpenAPI schema with a certain shape, more in a second\u2014which will fill this knowledge gap.<\/p>\n<p>If the provided OpenAPI validation schema provided by the CRD author is not structural, violations are reported in a <code>NonStructural<\/code> condition in the CRD.<\/p>\n<p>A structural schema for CRDs in <code>apiextensions.k8s.io\/v1beta1<\/code> will not be required. But we plan to require structural schemas for every CRD created in <code>apiextensions.k8s.io\/v1<\/code>, targeted for 1.16.<\/p>\n<p>But now let us see what a structural schema looks like.<\/p>\n<h2 id=\"structural-schema\">Structural Schema<\/h2>\n<p>The <strong>core of a structural schema<\/strong> is an OpenAPI v3 schema made out of<\/p>\n<ul>\n<li><code>properties<\/code><\/li>\n<li><code>items<\/code><\/li>\n<li><code>additionalProperties<\/code><\/li>\n<li><code>type<\/code><\/li>\n<li><code>nullable<\/code><\/li>\n<li><code>title<\/code><\/li>\n<li><code>descriptions<\/code>.<\/li>\n<\/ul>\n<p>In addition, all types must be non-empty, and in each sub-schema only one of <code>properties<\/code>, <code>additionalProperties<\/code> or <code>items<\/code> may be used.<\/p>\n<p>Here is an example of our <code>MaintenanceJob<\/code>:<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#f8f8f8\"><code class=\"language-yaml\" data-lang=\"yaml\">type:<span style=\"color:#bbb\"> <\/span>object<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"><\/span>properties:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>spec:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>object<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>properties<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>command:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>string<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>machines:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>array<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>items:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>string<\/code><\/pre>\n<\/div>\n<p>This schema is structural because we only use the permitted OpenAPI constructs, and we specify each type.<\/p>\n<p>Note that we leave out <code>apiVersion<\/code>, <code>kind<\/code> and <code>metadata<\/code>. These are implicitly defined for each object.<\/p>\n<p>Starting from this structural core of our schema, we might enhance it for value validation purposes with nearly all other OpenAPI constructs, with only a few restrictions, for example:<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#f8f8f8\"><code class=\"language-yaml\" data-lang=\"yaml\">type:<span style=\"color:#bbb\"> <\/span>object<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"><\/span>properties:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>spec:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>object<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>properties<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>command:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>string<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>minLength:<span style=\"color:#bbb\"> <\/span><span style=\"color:#666\">1<\/span><span style=\"color:#bbb\"> <\/span><span style=\"color:#080;font-style:italic\"># value validation<\/span><span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>shell:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>string<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>minLength:<span style=\"color:#bbb\"> <\/span><span style=\"color:#666\">1<\/span><span style=\"color:#bbb\"> <\/span><span style=\"color:#080;font-style:italic\"># value validation<\/span><span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>oneOf:<span style=\"color:#bbb\"> <\/span><span style=\"color:#080;font-style:italic\"># value validation<\/span><span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>-<span style=\"color:#bbb\"> <\/span>required:<span style=\"color:#bbb\"> <\/span>[\u201ccommand\u201d]<span style=\"color:#bbb\"> <\/span><span style=\"color:#080;font-style:italic\"># value validation<\/span><span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>-<span style=\"color:#bbb\"> <\/span>required:<span style=\"color:#bbb\"> <\/span>[\u201cshell\u201d]<span style=\"color:#bbb\"> <\/span><span style=\"color:#080;font-style:italic\"># value validation<\/span><span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>machines:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>array<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>items:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>string<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>pattern:<span style=\"color:#bbb\"> <\/span>\u201c^[a-z0-<span style=\"color:#666\">9<\/span>]+(-[a-z0-<span style=\"color:#666\">9<\/span>]+)<span style=\"color:#080\">*$\u201d<\/span><span style=\"color:#bbb\"> <\/span><span style=\"color:#080;font-style:italic\"># value validation<\/span><span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"><\/span>required:<span style=\"color:#bbb\"> <\/span>[\u201cspec\u201d]<span style=\"color:#bbb\"> <\/span><span style=\"color:#080;font-style:italic\"># value validation<\/span><\/code><\/pre>\n<\/div>\n<p>Some notable restrictions for these additional value validations:<\/p>\n<ul>\n<li>the last 5 of the core constructs are not allowed: <code>additionalProperties<\/code>, <code>type<\/code>, <code>nullable<\/code>, <code>title<\/code>, <code>description<\/code><\/li>\n<li>every properties field mentioned, must also show up in the core (without the blue value validations).<\/li>\n<\/ul>\n<p>As you can see also logical constraints using <code>oneOf<\/code>, <code>allOf<\/code>, <code>anyOf<\/code>, <code>not<\/code> are allowed.<\/p>\n<p>To sum up, an OpenAPI schema is structural if<br \/>\n1. it has the core as defined above out of <code>properties<\/code>, <code>items<\/code>, <code>additionalProperties<\/code>, <code>type<\/code>, <code>nullable<\/code>, <code>title<\/code>, <code>description<\/code>,<br \/>\n2. all types are defined,<br \/>\n3. the core is extended with value validation following the constraints:<br \/>\n1. inside of value validations no <code>additionalProperties<\/code>, <code>type<\/code>, <code>nullable<\/code>, <code>title<\/code>, <code>description<\/code>,<br \/>\n2. all fields mentioned in value validation are specified in the core.<\/p>\n<p>Let us modify our example spec slightly, to make it non-structural:<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#f8f8f8\"><code class=\"language-yaml\" data-lang=\"yaml\">properties:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>spec:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>object<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>properties<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>command:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>string<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>minLength:<span style=\"color:#bbb\"> <\/span><span style=\"color:#666\">1<\/span><span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>shell:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>string<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>minLength:<span style=\"color:#bbb\"> <\/span><span style=\"color:#666\">1<\/span><span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>oneOf:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>-<span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>string<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>required:<span style=\"color:#bbb\"> <\/span>[\u201ccommand\u201d]<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>-<span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>string<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>required:<span style=\"color:#bbb\"> <\/span>[\u201cshell\u201d]<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>machines:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>array<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>items:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>string<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>pattern:<span style=\"color:#bbb\"> <\/span>\u201c^[a-z0-<span style=\"color:#666\">9<\/span>]+(-[a-z0-<span style=\"color:#666\">9<\/span>]+)<span style=\"color:#080\">*$\u201d<\/span><span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>not:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>properties:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>privileged:<span style=\"color:#bbb\"> <\/span>{}<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"><\/span>required:<span style=\"color:#bbb\"> <\/span>[\u201cspec\u201d]<\/code><\/pre>\n<\/div>\n<p>This spec is non-structural for many reasons:<\/p>\n<ul>\n<li><code>type: object<\/code> at the root is missing (rule 2).<\/li>\n<li>inside of <code>oneOf<\/code> it is not allowed to use <code>type<\/code> (rule 3-i).<\/li>\n<li>inside of <code>not<\/code> the property <code>privileged<\/code> is mentioned, but it is not specified in the core (rule 3-ii).<\/li>\n<\/ul>\n<p>Now that we know what a structural schema is, and what is not, let us take a look at our attempt above to forbid <code>privileged<\/code> as a field. While we have seen that this is not possible in a structural schema, the good news is that we don\u2019t have to explicitly attempt to forbid unwanted fields in advance.<\/p>\n<h2 id=\"pruning-don-t-preserve-unknown-fields\">Pruning \u2013 don\u2019t preserve unknown fields<\/h2>\n<p>In <code>apiextensions.k8s.io\/v1<\/code> pruning will be the default, with ways to opt-out of it. Pruning in <code>apiextensions.k8s.io\/v1beta1<\/code> is enabled via<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#f8f8f8\"><code class=\"language-yaml\" data-lang=\"yaml\">apiVersion:<span style=\"color:#bbb\"> <\/span>apiextensions\/v1beta1<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"><\/span>kind:<span style=\"color:#bbb\"> <\/span>CustomResourceDefinition<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"><\/span>spec:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>\u2026<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>preserveUnknownFields:<span style=\"color:#bbb\"> <\/span><span style=\"color:#a2f;font-weight:bold\">false<\/span><\/code><\/pre>\n<\/div>\n<p>Pruning can only be enabled if the global schema or the schemas of all versions are structural.<\/p>\n<p>If pruning is enabled, the pruning algorithm<br \/>\n* assumes that the schema is complete, i.e. every field is mentioned and not-mentioned fields can be dropped<br \/>\n* is run on<br \/>\n* data received via an API request<br \/>\n* after conversion and admission requests<br \/>\n* when reading from etcd (using the schema version of the data in etcd).<\/p>\n<p>As we don\u2019t specify <code>privileged<\/code> in our structural example schema, the malicious field is pruned from before persisting to etcd:<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#f8f8f8\"><code class=\"language-yaml\" data-lang=\"yaml\">apiVersion:<span style=\"color:#bbb\"> <\/span>operations\/v1<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"><\/span>kind:<span style=\"color:#bbb\"> <\/span>MaintenanceNightlyJob<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"><\/span>spec:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>command:<span style=\"color:#bbb\"> <\/span>grep<span style=\"color:#bbb\"> <\/span>backdoor<span style=\"color:#bbb\"> <\/span>\/etc\/passwd<span style=\"color:#bbb\"> <\/span>|<span style=\"color:#b44;font-style:italic\">| echo \u201cbackdoor:76asdfh76:\/bin\/bash\u201d &gt;&gt; \/etc\/passwd || true\n<\/span><span style=\"color:#b44;font-style:italic\"> machines: [\u201caz1-master1\u201d,\u201daz1-master2\u201d,\u201daz2-master3\u201d]\n<\/span><span style=\"color:#b44;font-style:italic\"> # pruned: privileged: true<\/span><\/code><\/pre>\n<\/div>\n<h2 id=\"extensions\">Extensions<\/h2>\n<p>While most Kubernetes-like APIs can be expressed with a structural schema, there are a few exceptions, notably <code>intstr.IntOrString<\/code>, <code>runtime.RawExtension<\/code>s and pure JSON fields.<\/p>\n<p>Because we want CRDs to make use of these types as well, we introduce the following OpenAPI vendor extensions to the permitted core constructs:<\/p>\n<ul>\n<li><code>x-kubernetes-embedded-resource: true<\/code> \u2014 specifies that this is an `runtime.RawExtensions-like field, with a Kubernetes resource with apiVersion, kind and metadata. The consequence is that those 3 fields are not pruned and are automatically validated.<\/li>\n<li><code>x-kubernetes-int-or-string: true<\/code> \u2014 specifies that this is either an integer or a string. No types must be specified, but<br \/>\n<\/li>\n<\/ul>\n<div class=\"highlight\">\n<pre style=\"background-color:#f8f8f8\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"color:#bbb\"> <\/span>oneOf:<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>-<span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>integer<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"> <\/span>-<span style=\"color:#bbb\"> <\/span>type:<span style=\"color:#bbb\"> <\/span>string<\/code><\/pre>\n<\/div>\n<p>is permitted, though optional.<br \/>\n* <code>x-kubernetes-preserve-unknown-fields: true<\/code> \u2014 specifies that the pruning algorithm should not prune any field. This can be combined with <code>x-kubernetes-embedded-resource<\/code>. Note that within a nested <code>properties<\/code> or <code>additionalProperties<\/code> OpenAPI schema the pruning starts again.<\/p>\n<p>One can use <code>x-kubernetes-preserve-unknown-fields: true<\/code> at the root of the schema (and inside any <code>properties<\/code>, <code>additionalProperties<\/code>) to get the traditional CRD behaviour that nothing is prune, despite being <code>spec.preserveUnknownProperties: false<\/code> is set.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>With this we conclude the discussion of the structural schema in Kubernetes 1.15 and beyond. To sum up:<\/p>\n<ul>\n<li>structural schemas are optional in <code>apiextensions.k8s.io\/v1beta1<\/code>. Non-structural CRDs will keep working as before.<\/li>\n<li>pruning (enabled via <code>spec.preserveUnknownProperties: false<\/code>) requires a structural schema.<\/li>\n<li>structural schema violations are signalled via the <code>NonStructural<\/code> condition in the CRD.<\/li>\n<\/ul>\n<p>Structural schemas are the future of CRDs. <code>apiextensions.k8s.io\/v1<\/code> will require them. But<\/p>\n<div class=\"highlight\">\n<pre style=\"background-color:#f8f8f8\"><code class=\"language-yaml\" data-lang=\"yaml\">type:<span style=\"color:#bbb\"> <\/span>object<span style=\"color:#bbb\">\n<\/span><span style=\"color:#bbb\"><\/span>x-kubernetes-preserve-unknown-fields:<span style=\"color:#bbb\"> <\/span><span style=\"color:#a2f;font-weight:bold\">true<\/span><\/code><\/pre>\n<\/div>\n<p>is a valid structural schema that will lead to the old schema-less behaviour.<\/p>\n<p>Any new feature for CRDs starting from Kubernetes 1.15 will require to have a structural schema:<\/p>\n<ul>\n<li>publishing of OpenAPI validation schemas and therefore support for kubectl client-side validation, and <code>kubectl explain<\/code> support (beta in Kubernetes 1.15)<\/li>\n<li>CRD conversion (beta in Kubernetes 1.15)<\/li>\n<li>CRD defaulting (beta in Kubernetes 1.15)<\/li>\n<li>Server-side apply (alpha in Kubernetes 1.15, CRD support pending).<\/li>\n<\/ul>\n<p>Of course <a href=\"https:\/\/kubernetes.io\/docs\/concepts\/extend-kubernetes\/api-extension\/custom-resources\/#specifying-a-structural-schema\" target=\"_blank\">structural schemas<\/a> are also described in the Kubernetes documentation for the 1.15 release.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Authors: Stefan Schimanski (Red Hat) CustomResourceDefinitions were introduced roughly two years ago as the primary way to extend the Kubernetes API with custom resources. From the beginning they stored arbitrary JSON data, with the exception that kind, apiVersion and metadata had to follow the Kubernetes API conventions. In Kubernetes 1.8 CRDs gained the ability to [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[13],"tags":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v21.9.1 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Blog: Future of CRDs: Structural Schemas - Howk IT-Dienstleistungen<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/howk.de\/?p=8357\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Blog: Future of CRDs: Structural Schemas - Howk IT-Dienstleistungen\" \/>\n<meta property=\"og:description\" content=\"Authors: Stefan Schimanski (Red Hat) CustomResourceDefinitions were introduced roughly two years ago as the primary way to extend the Kubernetes API with custom resources. From the beginning they stored arbitrary JSON data, with the exception that kind, apiVersion and metadata had to follow the Kubernetes API conventions. In Kubernetes 1.8 CRDs gained the ability to [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/howk.de\/?p=8357\" \/>\n<meta property=\"og:site_name\" content=\"Howk IT-Dienstleistungen\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/howk.de\" \/>\n<meta property=\"article:published_time\" content=\"2019-06-20T19:00:02+00:00\" \/>\n<meta name=\"author\" content=\"admin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"admin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/howk.de\/?p=8357#article\",\"isPartOf\":{\"@id\":\"https:\/\/howk.de\/?p=8357\"},\"author\":{\"name\":\"admin\",\"@id\":\"https:\/\/howk.de\/#\/schema\/person\/b029bd02d4f35dce869ef54c81a100c5\"},\"headline\":\"Blog: Future of CRDs: Structural Schemas\",\"datePublished\":\"2019-06-20T19:00:02+00:00\",\"dateModified\":\"2019-06-20T19:00:02+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/howk.de\/?p=8357\"},\"wordCount\":1100,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/howk.de\/#organization\"},\"articleSection\":[\"Hi Tech\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/howk.de\/?p=8357#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/howk.de\/?p=8357\",\"url\":\"https:\/\/howk.de\/?p=8357\",\"name\":\"Blog: Future of CRDs: Structural Schemas - Howk IT-Dienstleistungen\",\"isPartOf\":{\"@id\":\"https:\/\/howk.de\/#website\"},\"datePublished\":\"2019-06-20T19:00:02+00:00\",\"dateModified\":\"2019-06-20T19:00:02+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/howk.de\/?p=8357#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/howk.de\/?p=8357\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/howk.de\/?p=8357#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/howk.de\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Blog: Future of CRDs: Structural Schemas\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/howk.de\/#website\",\"url\":\"https:\/\/howk.de\/\",\"name\":\"Howk IT-Dienstleistungen\",\"description\":\"Howk IT Services - Howk IT-Dienstleistungen\",\"publisher\":{\"@id\":\"https:\/\/howk.de\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/howk.de\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/howk.de\/#organization\",\"name\":\"HowK\",\"url\":\"https:\/\/howk.de\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/howk.de\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/howk.de\/w1\/wp-content\/uploads\/2013\/12\/howk-logo.png\",\"contentUrl\":\"https:\/\/howk.de\/w1\/wp-content\/uploads\/2013\/12\/howk-logo.png\",\"width\":170,\"height\":170,\"caption\":\"HowK\"},\"image\":{\"@id\":\"https:\/\/howk.de\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/howk.de\",\"http:\/\/de.linkedin.com\/in\/howkde\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/howk.de\/#\/schema\/person\/b029bd02d4f35dce869ef54c81a100c5\",\"name\":\"admin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/howk.de\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/b5a20f4d07bca1b73f25cff58a1116c4?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/b5a20f4d07bca1b73f25cff58a1116c4?s=96&d=mm&r=g\",\"caption\":\"admin\"},\"url\":\"https:\/\/howk.de\/?author=1\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Blog: Future of CRDs: Structural Schemas - Howk IT-Dienstleistungen","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/howk.de\/?p=8357","og_locale":"en_US","og_type":"article","og_title":"Blog: Future of CRDs: Structural Schemas - Howk IT-Dienstleistungen","og_description":"Authors: Stefan Schimanski (Red Hat) CustomResourceDefinitions were introduced roughly two years ago as the primary way to extend the Kubernetes API with custom resources. From the beginning they stored arbitrary JSON data, with the exception that kind, apiVersion and metadata had to follow the Kubernetes API conventions. In Kubernetes 1.8 CRDs gained the ability to [&hellip;]","og_url":"https:\/\/howk.de\/?p=8357","og_site_name":"Howk IT-Dienstleistungen","article_publisher":"https:\/\/www.facebook.com\/howk.de","article_published_time":"2019-06-20T19:00:02+00:00","author":"admin","twitter_card":"summary_large_image","twitter_misc":{"Written by":"admin","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/howk.de\/?p=8357#article","isPartOf":{"@id":"https:\/\/howk.de\/?p=8357"},"author":{"name":"admin","@id":"https:\/\/howk.de\/#\/schema\/person\/b029bd02d4f35dce869ef54c81a100c5"},"headline":"Blog: Future of CRDs: Structural Schemas","datePublished":"2019-06-20T19:00:02+00:00","dateModified":"2019-06-20T19:00:02+00:00","mainEntityOfPage":{"@id":"https:\/\/howk.de\/?p=8357"},"wordCount":1100,"commentCount":0,"publisher":{"@id":"https:\/\/howk.de\/#organization"},"articleSection":["Hi Tech"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/howk.de\/?p=8357#respond"]}]},{"@type":"WebPage","@id":"https:\/\/howk.de\/?p=8357","url":"https:\/\/howk.de\/?p=8357","name":"Blog: Future of CRDs: Structural Schemas - Howk IT-Dienstleistungen","isPartOf":{"@id":"https:\/\/howk.de\/#website"},"datePublished":"2019-06-20T19:00:02+00:00","dateModified":"2019-06-20T19:00:02+00:00","breadcrumb":{"@id":"https:\/\/howk.de\/?p=8357#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/howk.de\/?p=8357"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/howk.de\/?p=8357#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/howk.de\/"},{"@type":"ListItem","position":2,"name":"Blog: Future of CRDs: Structural Schemas"}]},{"@type":"WebSite","@id":"https:\/\/howk.de\/#website","url":"https:\/\/howk.de\/","name":"Howk IT-Dienstleistungen","description":"Howk IT Services - Howk IT-Dienstleistungen","publisher":{"@id":"https:\/\/howk.de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/howk.de\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/howk.de\/#organization","name":"HowK","url":"https:\/\/howk.de\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/howk.de\/#\/schema\/logo\/image\/","url":"https:\/\/howk.de\/w1\/wp-content\/uploads\/2013\/12\/howk-logo.png","contentUrl":"https:\/\/howk.de\/w1\/wp-content\/uploads\/2013\/12\/howk-logo.png","width":170,"height":170,"caption":"HowK"},"image":{"@id":"https:\/\/howk.de\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/howk.de","http:\/\/de.linkedin.com\/in\/howkde"]},{"@type":"Person","@id":"https:\/\/howk.de\/#\/schema\/person\/b029bd02d4f35dce869ef54c81a100c5","name":"admin","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/howk.de\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/b5a20f4d07bca1b73f25cff58a1116c4?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/b5a20f4d07bca1b73f25cff58a1116c4?s=96&d=mm&r=g","caption":"admin"},"url":"https:\/\/howk.de\/?author=1"}]}},"_links":{"self":[{"href":"https:\/\/howk.de\/index.php?rest_route=\/wp\/v2\/posts\/8357"}],"collection":[{"href":"https:\/\/howk.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/howk.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/howk.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/howk.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=8357"}],"version-history":[{"count":0,"href":"https:\/\/howk.de\/index.php?rest_route=\/wp\/v2\/posts\/8357\/revisions"}],"wp:attachment":[{"href":"https:\/\/howk.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=8357"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/howk.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=8357"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/howk.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=8357"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}