Skip to content

kyaml/openapi: replace the embedded Kubernetes schema with precomputed patch metadata#6194

Closed
dims wants to merge 3 commits into
kubernetes-sigs:masterfrom
dims:precomputed-patch-metadata
Closed

kyaml/openapi: replace the embedded Kubernetes schema with precomputed patch metadata#6194
dims wants to merge 3 commits into
kubernetes-sigs:masterfrom
dims:precomputed-patch-metadata

Conversation

@dims

@dims dims commented Jul 20, 2026

Copy link
Copy Markdown
Member

What type of PR is this?

/kind cleanup

What this PR does / why we need it:

This removes the embedded Kubernetes OpenAPI schema document (the 4.6 MiB kyaml/openapi/kubernetesapi/v1_21_2 tree) and replaces the one thing the merge code actually reads from it with a small precomputed table.

The kyaml merge walker reads exactly two extensions from the schema: x-kubernetes-patch-strategy and x-kubernetes-patch-merge-key. In the whole 3.4 MB document, only 49 fields carry a patch strategy. The first commit generates a table of those fields plus the definition structure needed to reach them (about 69 KB of Go), adds PatchMetaSchemaForResourceType to serve it, and switches the merge walker to it. The second commit deletes the embedded document, the go-bindata plumbing, and the kind based fetch scripts, and changes the Makefile to download api/openapi-spec/swagger.json for an exact kubernetes/kubernetes tag (API_VERSION) and regenerate the table from that.

Numbers:

  • kubectl and kubeadm each carry about 340 KiB of linked schema bytes from this document today. The table is about 69 KB of source.
  • The vendored copy in kubernetes/kubernetes is 4.6 MiB. That goes away when this lands and kubernetes picks up the new kyaml.
  • A default kustomize build no longer parses the document at all, which removes the parse cost that openapi parsing performance improvement with protobuffer #4568 reduced from 600ms to 39ms.

Fidelity notes, both verified by a generative test:

  • The table is merge equivalent to the source document. make verify-patchmeta downloads the pinned tag's document and walks every resource root in lockstep against it, asserting identical strategies and merge keys at every path, and that every pruned subtree carries no patch metadata.
  • The old embedded document was fetched from a kind cluster with alpha APIs disabled, so every v1alpha1 type was missing from it. The published tag document includes them, so the table now covers types the embedded document never did.

Relationship to existing work: #5016 tracks the schema version question and #6190 repackages the embedded document with a bundle compiler. This PR is a different point in that design space: it removes the document and keeps only the merge metadata, generated from an exact tag. Because the table costs kilobytes per version instead of megabytes, the union across releases discussed in #5016 becomes cheap: the generator already accepts multiple documents. Happy to rework this to sit on top of #6190 if that ordering is preferred.

Which issue(s) this PR fixes:

Related to #5016

Behavior changes to review carefully:

  • SchemaForResourceType returns nil for builtin types unless a schema document has been supplied. Callers that need descriptions or validation data must supply a document; merge consumers use PatchMetaSchemaForResourceType.
  • The openapi field's version selector accepts only the pinned default version and errors otherwise, with a message pointing at the path field. The path field and AddSchema are unchanged, and supplied definitions override the table for the types they define.
  • $ref field comments that reference builtin definitions, and schema driven formatting of builtin types, need a supplied schema document now. The tests exercising those mechanisms supply small schema fixtures, so the mechanisms themselves remain covered.
  • Custom schema documents must be self-contained. Before this change, a custom document could $ref builtin definitions (for example io.k8s.api.core.v1.PodTemplateSpec) and the embedded document was merged underneath it, but only when a builtin lookup happened to run first, so the old behavior was order dependent. The openshift test fixture relied on this and now defines everything it references.
  • One quirk is preserved deliberately: the schema lookup drops patch extensions on fields that resolve through a $ref, so retainKeys on Deployment.spec.strategy has never taken effect. The table reproduces this so merge output is unchanged. The equivalence test documents it.

Special notes for your reviewer:

  • The kyaml test suite passes. Tests that asserted the embedded document's contents (descriptions of builtin types) were repurposed to document the new contract and the supplied schema path.
  • The api module test suite shows no new failures against a pristine master baseline on the same machine.
  • Regenerate with make zz_generated_patchmeta.go in kyaml/openapi; verify with make verify-patchmeta.

Does this PR introduce a user-facing change?

kyaml no longer embeds a Kubernetes OpenAPI schema document. Strategic-merge patches for builtin types behave the same, served from a precomputed table generated from an exact kubernetes/kubernetes tag. The kustomization openapi version field now accepts only the default version; use the path field to supply a full custom schema document.

dims added 2 commits July 20, 2026 18:12
The kyaml merge walker only reads two OpenAPI extensions from the
embedded Kubernetes schema: x-kubernetes-patch-strategy and
x-kubernetes-patch-merge-key. Parsing the whole 3.4 MB swagger document
to answer those lookups costs tens of milliseconds per process, and
embedding it costs about 340 KiB in every binary that links kyaml,
including kubectl and kubeadm.

This change precomputes that metadata into a generated table, the same
approach precomputedIsNamespaceScoped already uses for namespace
scoping. The table holds the 47 fields that carry a patch strategy in
the builtin schema, plus the definition spine needed to reach them:
about 63 KB of generated Go instead of a 4.6 MiB embedded document.

A new function PatchMetaSchemaForResourceType serves builtin types from
the table when the global schema is in its default state, and falls
back to the full schema for unknown types or any customized state. The
merge walker now uses it. SchemaForResourceType keeps its full
contract, so callers that need descriptions or validation data see no
change, and the openapi field in kustomization.yaml keeps working.

A generative equivalence test walks every builtin resource root in
lockstep against the parsed schema and asserts the table reports the
same strategy and merge keys at every path, and that pruned subtrees
contain no patch metadata. This includes reproducing an existing quirk:
the schema lookup drops patch extensions on fields that resolve through
a $ref, so the table does too.

With this change a default kustomize build never parses the embedded
swagger. The document itself is unchanged and still used by
SchemaForResourceType; slimming or replacing it becomes a follow-up
once the remaining full-schema consumers are accounted for (issue kubernetes-sigs#5016
tracks the broader schema question).

Regenerate with:
  go run ./openapi/internal/patchmetagen openapi/kubernetesapi/v1_21_2/swagger.pb

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
The strategic-merge patch metadata for builtin types now comes entirely
from the precomputed table, so the 4.6 MiB embedded schema document
(kubernetesapi/v1_21_2, go-bindata plumbing, and the kind-based fetch
scripts) can go. The table is generated from the published OpenAPI
document of an exact kubernetes/kubernetes tag: the Makefile downloads
api/openapi-spec/swagger.json for the pinned API_VERSION from
raw.githubusercontent.com and regenerates zz_generated_patchmeta.go
from it. No schema data is fetched from a live cluster anymore, and no
schema document is embedded or committed.

Generating from the published tag also fixes a fidelity gap: the old
embedded document was fetched from a kind cluster with alpha APIs
disabled, so every v1alpha1 type was missing from it. The table now
covers them.

What still works unchanged:
- strategic-merge patches for builtin types (the precomputed table;
  equivalence to the source document is verified by make
  verify-patchmeta, which downloads the pinned tag and runs
  TestPrecomputedPatchMetaEquivalence)
- the openapi field in kustomization.yaml with a path to a custom
  schema document, and AddSchema; supplied definitions override the
  table per type
- the Kustomization schema (kustomizationapi), which stays embedded
- namespace-scope checks (precomputedIsNamespaceScoped)

What changes:
- SchemaForResourceType returns nil for builtin types unless a schema
  document has been supplied; PatchMetaSchemaForResourceType serves
  merge metadata
- the openapi version field accepts only the pinned default version;
  other versions error with a pointer to the path field
- $ref field comments referencing builtin definitions, and
  schema-driven formatting of builtin types, need a supplied schema
  document; tests exercising those mechanisms now supply small schema
  fixtures

Regenerate with:
  make zz_generated_patchmeta.go   # in kyaml/openapi
Verify with:
  make verify-patchmeta

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
@kubernetes-prow

Copy link
Copy Markdown
Contributor

This PR has multiple commits, and the default merge method is: merge.
You can request commits to be squashed using the label: tide/merge-method-squash

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@kubernetes-prow kubernetes-prow Bot added the kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. label Jul 20, 2026
@kubernetes-prow
kubernetes-prow Bot requested a review from koba1t July 20, 2026 23:31
@kubernetes-prow

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: dims
Once this PR has been reviewed and has the lgtm label, please assign koba1t for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kubernetes-prow
kubernetes-prow Bot requested a review from varshaprasad96 July 20, 2026 23:31
@kubernetes-prow kubernetes-prow Bot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Jul 20, 2026
The kustomize openapi info command and one api test imported the
deleted kubernetesapi package, which broke compilation of the api and
kustomize modules and, through them, the Lint and module check jobs.

- kustomize openapi info prints the same output format as before,
  built from openapi.GetSchemaVersion().
- The api test asserts the default version string directly, matching
  the other version assertions in the same file.
- The openshift custom-schema test fixture referenced builtin
  definitions (PodTemplateSpec, ConfigMapVolumeSource) that it never
  defined. It only worked before because the old delayed-parse
  machinery merged the embedded builtin document underneath custom
  schemas, and only when a builtin lookup happened to run first. With
  no embedded document, custom schema documents must be
  self-contained; the fixture now defines everything it references.

Signed-off-by: Davanum Srinivas <davanum@gmail.com>
@koba1t

koba1t commented Jul 20, 2026

Copy link
Copy Markdown
Member

/assign @koba1t

@koba1t

koba1t commented Jul 20, 2026

Copy link
Copy Markdown
Member

This PR might be trying to achieve the same thing as #6190

@dims
Could you take your time to check #6190?

@dims

dims commented Jul 21, 2026

Copy link
Copy Markdown
Member Author

@koba1t i was running an analysis and this came up again:
https://gist.github.com/dims/44936292606e746c8ccb032db67007b0#highest-value-finding-unchanged-kustomize-embedded-openapi-schema

hence my attempt in this PR. I am ok with whichever approach :) Happy to close this out, just let me know.

@dims dims closed this Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants