[dmt] feat: matrix rendering and Kubernetes schema validation - #426
Merged
Conversation
ldmonster
force-pushed
the
feat/check-resources-by-crds
branch
2 times, most recently
from
August 26, 2026 14:10
ea5ee49 to
3d9fe5a
Compare
ldmonster
force-pushed
the
feat/check-resources-by-crds
branch
from
September 4, 2026 17:22
3d9fe5a to
05efdab
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Two user-facing additions to
dmt lint.--matrix— lint every value combination, not just the default oneA single default render only exercises one branch of a chart's templates, so
resources behind a feature flag, a mode enum or an
x-examplesalternative arenever produced and never linted.
--matrixdiscovers the axes of variation fromthe module's own openapi schema and renders each combination:
x-examplescontributes one variant per example;enumcontributes one variant per allowed value;trueandfalse.The first variant is always the default render, so matrix output is a superset
of an ordinary lint. Combinations are the cartesian product capped by
--matrix-limit(default 100); past the cap it falls back to an all-pairs(pairwise) set, so every pair of axis values still co-occurs somewhere — enough to
reach a resource gated by two conditions at once.
The CI plumbing for this already exists on
main: the DMT-verify jobs carrymatrix/matrix_limitworkflow_dispatchinputs that pass--matrixthroughto the binary. They now do something.
schema-validation— check rendered resources against the Kubernetes APIA new rule in the
templateslinter. Each rendered standard Kubernetesresource is decoded into the Go type that serves its
apiVersion/kind,strictly. Two things are reported:
replicas: "3"where an integer belongs;level off, such as
resources.memoryinstead ofresources.limits.memory.This is the error server-side apply reports as "field not declared in schema".
Each offending field is reported separately, by its full path.
Configurable like every other rule:
Why custom resources are not checked
The branch name says CRDs, and an earlier iteration did resolve CRD schemas — from
the module's own
crds/, from a repo-wide scan of the deckhouse tree, and from abundled third-party catalog. All of it was dropped.
The definition a cluster actually serves is not knowable from a source tree. The
only answers available come either from a snapshot that lags upstream or from a
CRD that may not be the one installed, and a false finding against a stale schema
costs more than the check is worth.
Two consequences worth knowing:
regenerate, no sync job to keep green.
k8s.io/apiis ingo.mod. Bumping that dependency is the whole of updating the rule, and thecheck can never drift from the API types the rest of dmt already uses.
Custom resources — anything served by a CRD — have no registered Go type and are
skipped silently. The rule reports violations, never the absence of a type.
Design notes
Targets are streamed, not collected.
manager.Source.Targetsnow pushes eachtarget into a
yieldcallback instead of returning[]Target. A matrix runexpands one module into many renders, and materializing them would hold every
rendered chart in memory at once.
yieldblocks while the worker pool is full, sothe source renders the next variant only once an earlier one has been linted and
freed — a module expanding to thousands of combinations never has more than the
worker count resident. Object stores are pooled and handed back by
Module.Release()as soon as a target is linted.Rendering and linting of one module never overlap. Rendering writes into the
module's own
templates/: an image-resolution helper goes in for the duration ofeach render and comes out again, and a template that aborts the render is
neutralized in place and restored. Because matrix re-renders the same directory,
a variant target is linted to completion before the source is let go to render the
next one. Without this,
dmt lint --matrixcrashed 3 runs out of 12 on a paththat vanished mid-walk. Ordinary runs render each module once, never wait, and stay
fully parallel.
Findings are de-duplicated in a variant run. The same resource is linted under
many combinations, so a finding that does not depend on the combination would be
reported once per combination. Findings identical in every user-visible field are
collapsed, and the summary counts the same set that was printed.
Bugs found and fixed along the way
--values-filesilently did nothing.OverrideValueswrapped the caller'svalues under an extra
Valueskey, landing them at.Values.Values.…, where notemplate reads them.
fsutils.GetFilesdiscarded thefilepath.Walkerror and dereferenced the nilFileInfothat comes with it.Every other walker in the repo handles the error; this one did not.
placeholder was mixed-case, which fails the DNS-1123 pattern that name, namespace
and label fields are constrained by. An int-or-string
oneOf— the quantityshape used for
resources.requests.cpuand VPA bounds — generated{}insteadof a scalar.
mergoshares nested maps when thedestination has no key yet, so with
--values-fileeach variant's overrides werewritten into the shared base tree and accumulated across the run.