Introduction
Usually, the easiest approach is not a full conversion step but a direct extraction. Modern Kubernetes CRDs already contain schema information under spec.versions[].schema.openAPIV3Schema, and for many tooling workflows that exported schema is close enough to JSON Schema to be useful immediately.
Where the Schema Lives in a CRD
In apiextensions.k8s.io/v1, each served version of a CRD can carry its own schema.
Important fields are:
That means there is rarely one single schema for the whole CRD. The first decision is which version you want to export.
kubectl get crd widgets.example.com -o json \
| jq '.spec.versions[] | {name: .name, served: .served, storage: .storage, hasSchema: (.schema.openAPIV3Schema != null)}' ``` That quick inspection tells you which versioned schema blocks are available. ## Extraction Is Often Enough If the downstream tool already understands OpenAPI-style schema or simply needs a JSON representation of the structure, extracting the schema block is often the whole solution. ```bash kubectl get crd widgets.example.com -o json \ | jq '.spec.versions[] | select(.name == "v1") | .schema.openAPIV3Schema' \ > widget-v1-schema.json ``` The same idea works from a YAML file in source control. ```bash yq '.spec.versions[] | select(.name == "v1") | .schema.openAPIV3Schema' crd.yaml \ | yq -o=json \ > widget-v1-schema.json ``` For documentation generators, validators, and internal codegen tools, this is often enough without any extra translation layer. ## Why “JSON Schema” Is Not Always Exact Kubernetes uses an OpenAPI v3-flavored schema with Kubernetes-specific extensions. That overlaps heavily with JSON Schema, but it is not always identical to what a generic JSON Schema validator expects. Examples of Kubernetes-specific fields include: - '`x-kubernetes-validations`' - '`x-kubernetes-int-or-string`' - structural schema rules used only by CRDs - Kubernetes-specific list and object semantics So the practical rule is: - easy export of schema data: yes - perfect drop-in compatibility with every JSON Schema consumer: not guaranteed If the target validator is strict, you may need a normalization step that removes or translates Kubernetes extension fields. ## Generating From Source Can Be Better Than Reverse Conversion If you own the controller source, another strong workflow is to generate the CRD from typed API definitions and then extract the schema from the generated manifest. For Go-based operators, `controller-gen` is common. ```bash controller-gen crd paths=./api/... output:crd:dir=./config/crd ``` Once the CRD file exists, you can extract the relevant `openAPIV3Schema` block exactly as shown earlier. This keeps the schema tied to the same source types that define the API instead of relying on a live-cluster snapshot. ## Test the Exported Schema Against the Actual Consumer The exported schema may look like valid JSON and still not be accepted by the exact validator or code generator you want to use. That is why the best workflow is often: 1. extract the schema for the CRD version you care about 2. store the result in a file 3. run the target validator or generator against that file 4. normalize only if the target tool rejects Kubernetes-specific extensions That sequence is safer than assuming every JSON-schema-aware tool interprets Kubernetes schema the same way. ## CI Can Track Schema Drift A useful operational pattern is to export the schema in CI and compare it with a committed snapshot. ```bash kubectl get crd widgets.example.com -o json \ | jq '.spec.versions[] | select(.name == "v1") | .schema.openAPIV3Schema' \ > /tmp/schema.json diff -u schemas/widget-v1-schema.json /tmp/schema.json ``` This makes API drift visible in code review and is often more valuable than a one-off manual export. ## Common Pitfalls The most common mistake is assuming a CRD has one schema instead of versioned schemas. Another common issue is treating Kubernetes OpenAPI schema as perfectly identical to generic JSON Schema. They are very similar, but Kubernetes extensions can still matter. Developers also often export from a live cluster and stop there, even though the schema should usually be tracked in source control if it matters to downstream tooling. ## Summary - Modern CRDs already store schema under `spec.versions[].schema.openAPIV3Schema`. - In many cases, direct extraction is easier than a true conversion step. - Choose the CRD version explicitly before exporting. - Kubernetes schema and generic JSON Schema overlap heavily but are not always interchangeable. - Test the exported schema with the actual downstream tool that will consume it.