feat(podspec): find PodSpecs structurally, validated by the Go types (option B) - #82
Open
MPV wants to merge 3 commits into
Open
feat(podspec): find PodSpecs structurally, validated by the Go types (option B)#82MPV wants to merge 3 commits into
MPV wants to merge 3 commits into
Conversation
MPV
force-pushed
the
claude/podspec-b-structural-matching
branch
from
August 9, 2026 20:35
d1615b8 to
fca629d
Compare
This was referenced Aug 9, 2026
MPV
force-pushed
the
claude/podspec-b-structural-matching
branch
from
August 10, 2026 06:46
19076cb to
8c44fd5
Compare
Option B of four candidate answers to #26, raised side by side for comparison. Stops decoding into typed objects; finds the PodSpec by shape instead. Each document is decoded into plain Go values and walked. A node is a PodSpec when its containers/initContainers/ephemeralContainers decode into the real corev1 types with unknown fields rejected — the Kubernetes Go types are the schema, so the test is "would Kubernetes call this a PodSpec", not "is there a field named containers". Custom resources therefore work, which is the half of #26 that option A cannot reach: $ kir rollout.yaml # an Argo Rollout; no kind named anywhere in kir my-registry/app:1.4.2 busybox:1.36 List stops being a special case (its items are just more nodes), taking the kind allow-list and the unstructured item handling with it. Dropping typed decoding drops k8s.io/client-go entirely: 28.9 MB -> 12.8 MB. The costs are real and covered by fixtures. Matching on shape could match on name alone, so TestSkipsNonWorkloads.Lookalike pins that a custom resource with a containers field holding non-containers yields nothing. Walking every node costs 169ms -> 203ms over 1000 documents. Precision is now tied to the vendored k8s.io/api. See ADR 0008. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013RYRsLAGHPcRuwhodXXmWP
The ADR carried figures taken before the rebase onto 0.4.2. Re-measured on the rebased branch: 152ms -> 211ms over 1000 documents, and a 12.2 MB binary against 27.6 MB. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013RYRsLAGHPcRuwhodXXmWP
The ADR carried figures from the 0.4.2 base. Re-measured on 0.4.3: 105ms -> 133ms over 1000 documents against a 105ms master, and a 12.4 MB binary against 27.2 MB. The bump is also evidence for the ADR claim it sits under: strict decoding is bound to the vendored k8s.io/api, and four minor versions moved no goldens and needed no code change. Say so. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013RYRsLAGHPcRuwhodXXmWP
MPV
force-pushed
the
claude/podspec-b-structural-matching
branch
from
August 13, 2026 07:50
8c44fd5 to
eafaf7a
Compare
MPV
pushed a commit
that referenced
this pull request
Aug 13, 2026
A fifth candidate for #26, combining the two that matter. Structural inference (#82) and configured expressions (#84) each fail exactly where the other succeeds, so this runs both with configuration taking precedence per kind. Config.FindImages looks up the document's kind. An entry decides on its own — its expressions are followed and the walk is not consulted. Everything else is inferred. The two never both contribute to one document, so an image cannot be reported twice. The union reaches more than either alone: $ kir rollout.yaml # inferred; no configuration, no kind named my-registry/app:1.4.2 busybox:1.36 $ kir workflow.yaml # bare containers: no PodSpec shape to match $ kir --config workflows.yaml workflow.yaml builder:1.2.0 python:3.12 And one thing neither can do alone: an entry with no expressions silences a kind, so a user can overrule the walk where it reads something wrongly. Inference cannot be told to ignore; configuration has nothing to ignore. It is also cheaper than inference alone on ordinary input, which is the reverse of what combining two mechanisms usually costs: configured kinds take the exact lookup and never walk, so 1000 Deployments run in 107ms against inference alone's 171ms, near configuration alone's 92ms. The built-in resources.yaml is an accelerator, not knowledge: TestBuiltInConfigIsRedundant compares every built-in kind's configured result against its inferred one, so deleting the file would change speed and nothing else. Without that test it would quietly become the hardcoded kind list #26 set out to remove — reverting one entry to a wrong path fails it. See ADR 0009. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013RYRsLAGHPcRuwhodXXmWP
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.
Option B of the candidates for #26, raised side by side so they can be compared. Scope
feat(podspec)finds the set — #81 #82 #83 #84 #106. Only one should be merged.Rebased onto
2abd515(0.4.3) — Kubernetes v0.36.3, Go 1.26.masterRollout)Workflow)containerslookalikego.sumlinesmake schema--schema--config--configOne interleaved best-of-7 run; every variant emits the same 2000 images. Absolute timings drift with machine load between runs — the ratios are the stable quantity.
Approach
Stop decoding into typed Kubernetes objects. Decode each document into plain Go values, walk it, and test each node for PodSpec shape.
The shape test is the load-bearing part, and it is deliberately not a field-name heuristic: a candidate's
containers/initContainers/ephemeralContainersare decoded into the realcorev1types with unknown fields rejected. The Kubernetes Go types are the schema, so a node matches when Kubernetes itself would call it a PodSpec.What it buys
Custom resources work, which is the half of #26 option A cannot reach:
A checked-in fixture with a golden, so the example can't drift.
Liststops being a special case — its items are just more nodes — taking the kind allow-list and the unstructured item handling with it.ReplicationControllerandPodTemplatecome along for free.Dropping typed decoding drops
k8s.io/client-goentirely: 27.2 MB → 12.4 MB.k8s.io/apistays, as the schema.What it costs
TestSkipsNonWorkloads.Lookalikepins it: a resource with acontainersfield holding non-containers yields nothing. Worth reviewing that fixture specifically — it is the guard against this approach going wrong.Workflowis the common case. This is the gap feat(podspec): infer images structurally, with configured overrides (option E) #106 closes.k8s.io/api: a container field newer than it fails the strict decode. In practice the binding is loose — this rebase carried Kubernetes v0.32.3 → v0.36.3, four minor versions, and moved no goldens and needed no code change.Notes for review
TestFindImagesOrderIsStableguards it.k8s/k8s_test.gois rewritten because the type switch it tested is gone. The replacement corpus is shared verbatim with option C, so the two engines are compared on identical cases.TestFailure.BadYAML's stderr gains anerror converting YAML to JSON:prefix — same class, same exit 1, different parser wrapping the message. Shared with C, D and E.gofmt,go vet,go mod tidyno-op,go test -race ./...green. ADR 0009 records the decision as proposed, superseding ADR 0001 only if merged.