Skip to content

Instantly share code, notes, and snippets.

@dims
Last active July 20, 2026 16:52
Show Gist options
  • Select an option

  • Save dims/c8dd44ba82ed51dfc242b065a6ff2284 to your computer and use it in GitHub Desktop.

Select an option

Save dims/c8dd44ba82ed51dfc242b065a6ff2284 to your computer and use it in GitHub Desktop.
Kubernetes unwanted vendor dependencies status — July 2026

Kubernetes Unwanted Dependencies: Status Report

Date: July 2026 (2026-07-20) Branch: master (commit fc69f6c85ee05636f782818b8a131be3716ee461) Scope: hack/unwanted-dependencies.json — modules listed in spec.unwantedModules that are still present in vendor/ Prior report: June 2026 (same gist, now in gist revision history). All deltas below are measured against that report.


Background

hack/unwanted-dependencies.json formalizes the list of Go modules the Kubernetes project wants to eliminate. It has three relevant sections:

  • spec.unwantedModules — the authoritative blocklist, each entry with a rationale. Now 89 entries (up from 88): the new entry is github.com/google/cadvisor itself — the fat cadvisor module is now blocklisted in favor of the new lean github.com/google/cadvisor/lib module that kubelet vendors instead.
  • spec.pinnedModules — modules pinned below latest for a stated reason. Still two — github.com/google/cel-gov0.27.0 (#138334) and github.com/fsnotify/fsnotifyv1.9.0 (#138812) — but the cel-go pin is on its way out: #138334 was closed 2026-05-05 (fixed upstream in cel-go v0.28.1 via cel-go#1303) and PR #140205 (pohly, open) deletes the pin and bumps to v0.29.x.
  • status.unwantedReferences — the map of which vendored modules still pull in each unwanted module (the record of why each one cannot yet be removed).
  • status.unwantedVendored — unwanted modules currently still in vendor/.

Of the 89 unwanted modules, 80 are fully cleared. 9 remain — the same nine as May and June, but with the largest single-cycle movement yet underneath them.

Headline: The gogo/protobuf external track collapsed from 6 blockers to 2 in six weeks — a coordinated campaign (liggitt's checklist on k/k #96564, 2026-05-27) cleared ttrpc (v1.2.9 dropped gogo), typeurl and errdefs (left the vendor tree entirely), and cadvisor (replaced by the gogo-free cadvisor/lib). What remains for gogo is one containerd/api tag plus Kubernetes' own go-to-protobuf (KEP-5589 phase two). In the other direction, June's go-spew "✅ done upstream" framing is corrected to 🟡: an importer census shows testify was never the only gate — k8s.io/utils/dump (spew deliberately centralized there, baked into kubelet checkpoint checksums) and kustomize's kyaml stand behind it, and testify still has not released in 11 months. The SMD encoding/json/v2 keystone had its dry-run import land in k/k (#140294) with Go 1.27 a month away.


The 9 Remaining Unwanted Modules

Module Vendored version Blocklist reason
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc refer to #103942 (unmaintained)
github.com/gogo/protobuf v1.3.2 unmaintained
github.com/golang/protobuf v1.5.4 replace with google.golang.org/protobuf
github.com/google/btree v1.1.3 unmaintained, archive mode
github.com/json-iterator/go v1.1.12 refer to #105030
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd problematic reliance on Go internals
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee problematic reliance on Go internals
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f experimental/deprecated packages
gopkg.in/yaml.v3 v3.0.1 prefer sigs.k8s.io/yaml/goyaml.v3

Coupling note: modern-go/concurrent and modern-go/reflect2 are pulled (almost) exclusively by github.com/json-iterator/go; they share its blockers and disappear when it does (analyzed together under json-iterator). The reflect2 pseudo-version oddity persists: k8s vendors v1.0.3-0.2025… although SMD and client_golang both declare v1.0.2.


Per-Module Analysis

1. github.com/davecgh/go-spew — 🟡 (downgraded from June's ✅ framing)

Rationale: Unmaintained (last release v1.1.1, 2018). Tracked via #103942.

The correction: June concluded go-spew was "done upstream; gated only on a testify release." An importer census over vendor/ shows that was incomplete: the vendored source importers are testify (assert, mock), k8s.io/utils/dump, and sigs.k8s.io/kustomize/kyaml/yaml — and the same four existed in June (newly identified, not newly introduced). A testify release alone does not clear go-spew from the tree.

Upstream status:

  • testify — still no release. Latest remains v1.11.1 (2025-08-27, 11 months ago); master still has go-spew removed (June's internal-vendoring commit 39877525 holds; master's go.mod requires only objx v0.5.3 + yaml.v3 v3.0.1). No release-prep signal (last master merge 2026-06-10).
  • k8s.io/utils/dump — spew is there on purpose. kubernetes/utils#343 (merged 2026-02-10) moved dump from apimachinery to utils explicitly to centralize go-spew usage. ~50 k/k files import it, including production paths: pkg/util/hash DeepHashObject, kubelet cpu/memory-manager checkpoint checksums, kubeadm, client-go's retrywatcher. Because spew's output format is baked into persisted checksums, replacement is compatibility-sensitive. Zero removal issues/PRs exist in kubernetes/utils. The hash-safe option is testify-style internal vendoring of spew.
  • kustomize kyaml — fixed on master, unreleased. kustomize#6134 (merged 2026-05-01) removed the direct spew use in fns.go; no tag contains it (newest kyaml tag v0.21.1, 2026-02-09 — exactly what k8s vendors).
  • Spot-checks: etcd v3.7.0 GA, kube-openapi bc653b64f974, prometheus/common v0.67.5 all still pin testify v1.11.1 (spew + yaml.v3 // indirect).

Prognosis: 🟡 Three sequential gates, none passed this cycle: (1) a testify release + tail bumps, (2) a kustomize/kyaml tag + k8s bump, (3) a k8s-owned decision on utils/dump — for which nothing is filed. The last gate is entirely in-project.


2. github.com/gogo/protobuf — 🟡 (upgraded from 🔴; the star of this cycle)

Rationale: Unmaintained fork of the Go protobuf API.

Blocker-list evolution since June (from the JSON at each k/k commit): 6 → 5 (#139594 typeurl v2.3.0 bump, liggitt, 2026-06-09) → 4 (#139870 kubelet→cadvisor/lib migration, dims, merged 2026-06-25 — also evicted typeurl and errdefs from the vendor tree entirely) → 3 (#140337 ttrpc v1.2.9, 2026-07-09) → 2 (#140385 cadvisor/lib v0.60.4, 2026-07-10). Remaining: github.com/containerd/containerd/api and k8s.io/code-generator.

Upstream status:

  • ttrpc — RESOLVED at v1.2.9 (released 2026-07-08): v1.2.8 had gogo direct, v1.2.9 has none. Mechanism: runtime/wire usage was removed back in 2022 (PR #99); the remaining codegen path went via #226 (protobuild→buf, 2026-03-23) enabling ttrpc#239 ("Remove gogo vanity command and gogo dependency", liggitt, merged 2026-06-09). June-report correction: no "new major version" was needed — a patch release sufficed. Stale issue ttrpc#97 is still open and now obsolete; worth asking containerd to close it.
  • typeurl — RESOLVED and gone. June-report correction: removal was not "structurally hard" — typeurl#51 (gogo removal) had been merged since 2024-11-05 and sat unreleased ~18 months until v2.3.0 was tagged (2026-05-27). k/k bumped (#139594), then #139870 removed typeurl (and errdefs) from the tree altogether.
  • cadvisor — RESOLVED via the lib split. google/cadvisor#3895 (dims, merged 2026-06-20) introduced github.com/google/cadvisor/lib, a lean kubelet-focused module whose go.mod carries zero gogo (the old v0.57.0 module had gogo indirect plus ttrpc v1.2.8/typeurl/errdefs). Follow-ups: #3902 (OOM watcher out of lib), #3914 (merged 2026-07-13 — release tags now auto-mirror to lib/ tags, fixing the missing-lib-tag gap). k/k adopted through #139870 → #140026 → #140385 (lib v0.60.4).
  • containerd/api v1.11.1 — graph-only residue. gogo is // indirect purely via its ttrpc v1.2.5 and typeurl/v2 v2.1.1 requirements; its vendored generated code does not import gogo. containerd main's api go.mod is already gogo-free (#13490 merged 2026-05-28, #13740 merged 2026-07-12). Gate: no api tag after v1.11.1 yet — the next tag plus a routine k/k bump clears it with zero engineering.
  • k8s.io/code-generator — the last source importer. 4 files under staging/src/k8s.io/code-generator/cmd/go-to-protobuf/ still import gogo directly (the only staging go.mod with gogo). Vehicle: KEP-5589 (open, milestone v1.38) — phase one (runtime removal, v1.35) and the ProtoMessage removal (v1.36) are done (#134228, #134256, #137084); phase two (generation-time) is not — the fork POC #134530 closed unmerged 2026-04-18 and liggitt marked the approach "still TBD" (2026-05-21).

Net: a source grep of vendor/ confirms code-generator is now the only importer keeping gogo vendored; containerd/api keeps it in the require graph only.

Prognosis: 🟡 From "multi-year" to two well-defined items: wait for a containerd/api tag (mechanical), and do KEP-5589 phase two (the single piece of real engineering left, fully in-tree).


3. github.com/golang/protobuf — 🔴 (edges improved; root unchanged)

Rationale: Legacy v1 protobuf API; since v1.5.0 a shim over google.golang.org/protobuf.

Blockers: 8 (was 9) — konnectivity-client left the list.

Upstream status:

  • konnectivity-client — CLEARED. v0.34.0 had the shim // indirect; v0.36.0 has no entry. Mechanism: apiserver-network-proxy #824 (merged 2026-05-01) bumped client_golang v1.11.1→v1.22.0, dropping the shim edge; k/k consumed via #139636 (merged 2026-06-11, includes commit f9a4b842c42 "Removing konnectivity-client from exception list").
  • grpc-go — the immovable root. v1.82.1 (latest, 2026-07-15) and master both require the shim direct. Policy unchanged: #8940 closed 2026-03-06 ("as long as we are on grpc major version 1 … we cannot remove this dependency"); nothing newer found. (k/k #140740, bumping to v1.82.1 for its security content, is open — no shim impact.)
  • etcd v3.7.0 GA — shim retained by design. Direct in api/client/server go.mods and on main; umbrella issue etcd#14533 ("cleanup both golang/protobuf and gogo/protobuf") closed COMPLETED 2026-05-29 with the shim kept; no successor issue. Four vendored etcd files import the shim.
  • ttrpc — done on main, awaiting a tag. v1.2.9 kept the shim direct, but ttrpc#248 removed it on main — merged 2026-07-09, about five hours after v1.2.9 was published. The next ttrpc tag takes the list 8→7 (same tag-ask as the gogo item above).
  • CSI spec — done on master, partially released. v1.9.0 (vendored) has it direct and its vendored csi.pb.go imports the shim; v1.12.0 (latest) demotes it to // indirect; master removed it entirely (#602, 2026-04-29). A bump to v1.12.0 removes the vendored import but (by konnectivity/middleware precedent) an indirect entry still counts as a graph edge — full clearing needs an unreleased v1.13.0.
  • go-grpc-middleware/providers/prometheus — still v1.1.0/// indirect, and main still carries it; a new release wouldn't clear it.
  • google.golang.org/protobuf pseudo-version — k/k still pins v1.36.12-0.20260120151049-f2248ac996af (#137451: Go 1.26 broke dead-code elimination → binary-size explosion; the pinned commit has the fix). The needed v1.36.12 tag still does not exist (proxy @latest v1.36.11, 2025-12-12); the release request golang/protobuf#1711 (dims, 2026-04-21, re-pinged 06-12) has no maintainer reply. It stays listed because protobuf-go's own go.mod requires the shim (compat-test cycle); June's "target module, not a true blocker" framing holds.

Physical imports in vendor/: only gogo/protobuf's own code (11 files), etcd (4), and CSI v1.9.0 (1) import the shim; grpc and ttrpc are go.mod-edge-only.

Prognosis: 🔴 Blocked indefinitely by grpc-go's v1.x policy; realistic floor is 6 entries (grpc, etcd ×3, middleware, protobuf-go). Near-term achievable: 8→7 via a ttrpc tag, possibly →6 via CSI depending on how the verifier counts indirect edges.


4. github.com/google/btree — 🟡 (no action taken on June's items)

Rationale: Unmaintained, archived. K8s already vendors the replacement (k8s.io/utils/third_party/forked/golang/btree — June's report said internal/btree; corrected).

Current state (all re-verified, all unchanged): sole vendored importer is still vendor/github.com/peterbourgon/diskv/index.go; client-go's discovery disk cache still constructs diskv.Options without an Index (staging/src/k8s.io/client-go/discovery/cached/disk/round_tripper.go:40-45), so the btree path remains dead weight; status.unwantedReferences still has no btree key at all (deleted by the etcd-bump JSON update 30794a7a71f on 2026-06-01; "diskv" appears nowhere in the JSON) — the stale-JSON problem June flagged is still unfixed.

Movement since June: only the close-out — etcd v3.7.0 GA vendored (#140333, merged 2026-07-08) and its GA server/go.mod verified btree-free. Nothing on the two June action items (fix the JSON; patch/replace diskv). All 2026-06+ k/k "btree" PRs (#139467, #139528, #139557, #139658) are apiserver watch-cache work on the utils btree — unrelated. diskv upstream remains dormant (last push 2021-11-10); btree remains archived. diskv /v3 still requires btree, so upgrading diskv does not help.

Prognosis: 🟡 Needs k8s-side action; none taken. Both fixes are fully in-project: record diskv as the go.mod-invisible referencer, and patch/fork diskv's index.go to the utils btree (or drop diskv from the discovery cache).


5. github.com/json-iterator/go + modern-go/{concurrent,reflect2} — 🟡 (SMD leg on final approach)

Rationale: Alternative JSON stack using unsafe reflection; #105030. The modern-go pair are pure satellites.

Blockers unchanged: prometheus/client_golang (v1.23.2 vendored) and sigs.k8s.io/structured-merge-diff/v6 (v6.4.2, was v6.4.0).

Upstream status:

  • SMD — every June precondition executed except the merge itself. PR #292 ("Migrate to encoding/json/v2") is still open, needs-rebase (went lifecycle/rotten 2026-06-19; jpbetz de-rottened it the same day). Verified via the files API: it removes jsoniter and both modern-go modules from SMD's go.mod and rewrites exactly the three files k8s vendors. The v6.4.0→v6.4.2 delta (19 commits; prereqs #324/#328, converter #326, July nullable fixes) does not touch jsoniter or go.mod.
  • The dry-run import happened: k/k #140294 (merged 2026-07-08; cherry-pick #140296 on 07-18) brought SMD HEAD → v6.4.2 into k/k — June's "liggitt proposed" item is done. Prereq test coverage completed early (jpbetz, 2026-05-27; June projected "~July").
  • Go 1.27 timeline firmed up: encoding/json/v2 (golang/go#71497) is closed into the Go 1.27 milestone with release notes merged and a GOEXPERIMENT=nojsonv2 opt-out. Go 1.27 ships ~Aug 2026 → plan of record remains #292 merge ~Aug → k8s 1.38.
  • client_golang — laggard with a new asterisk. v1.24.0 released today (2026-07-20); jsoniter still direct there and on main (api/prometheus/v1/api.go). Zero removal activity — but new issue #1998 (bwplotka, May, active July) floats an OpenAPI-generated HTTP client possibly in a separate module (refs the #1649 module split); not a removal effort, but a split would evict jsoniter from the root module k8s requires. (k/k's v1.24.0 bump is imminent — after the rc dry-run #140680, closed today in favor of the final, dims gave the go-ahead to ship v1.24.0.)
  • Vendoring nuance (new this cycle): the only vendored source importers of jsoniter are three SMD files; client_golang's api/ tree is not vendored at all. So SMD is the sole source-level blocker; client_golang is go.mod residue. Once #292 lands and is vendored, jsoniter + modern-go source leaves vendor/ entirely, with only require-graph entries left (the tooling PR to distinguish those two states, k/k #136748, was closed unmerged).

Prognosis: 🟡 Holding, with the keystone one rebase-and-merge away. Expect source-level clearance around k8s 1.38 if #292 lands on plan (slip past the Nov freeze → 1.39); full go.mod eradication still waits on Prometheus.


6. golang.org/x/exp — 🟡 (antlr promise expired)

Rationale: Experimental/deprecated subrepo; only x/exp/slices is vendored (in stdlib since Go 1.21).

Blockers unchanged: antlr4-go/antlr/v4 v4.13.1 (direct root — one slices.EqualFunc call in lexer_action_executor.go, the sole vendored importer; no first-party k8s import), cel-go (indirect via antlr), go-grpc-middleware/v2 v2.3.3 (indirect).

Upstream status:

  • antlr mirror — frozen; the May signal decayed. jimidle's 2026-05-19 "updates coming in the next two weekends" produced nothing in two months — no tag, commit, or go.mod change since v4.13.1 (2024-05-15). The repo is attended (PRs #16/#17 were closed unmerged, silently, on 2026-06-09), yet nothing ships. In the source repo, antlr/antlr4#4688 (the stdlib-slices swap) is still open (last activity: skitt's ping, 2026-04-23), #4294 likewise; runtime/Go still requires 2023-era x/exp.
  • cel-go v0.29.2 (2026-07-08; repo moved to the cel-expr GitHub org, module path unchanged) — still requires antlr v4.13.1, so x/exp-neutral.
  • The k8s pin is being removed: #138334 closed 2026-05-05 (fixed in cel-go v0.28.1); PR #140205 (pohly, open) deletes the cel-go pin and bumps to v0.29.x (needs a retarget to v0.29.2 + rebase). Doesn't change x/exp either way.
  • Vendored x/exp pseudo-version unchanged since June (…20260410); upstream HEAD drift only.

Prognosis: 🟡 Unchanged, with the upstream signal now negative. Options remain: keep nudging #4688, fork/replace antlr's single slices.EqualFunc call (the replace-directive fork pattern is already demonstrated in #4688's thread), or accept the dep.


7. gopkg.in/yaml.v3 — 🟡 (two gates, zero passed; maintainer silent)

Rationale: go-yaml v3 archived 2025-04-01; prefer sigs.k8s.io/yaml/goyaml.v3.

Upstream status:

  • Vendor-tree source importer census: exactly one filevendor/github.com/stretchr/testify/assert/yaml/yaml_default.go. testify is genuinely the only gate (unlike go-spew).
  • testify master AND v1.11.1 still require yaml.v3 v3.0.1 direct. PR #1772 (swap to go.yaml.in/yaml/v3) is still open: rebased 2026-05-06 at dolmen's request, then 2.5 months of maintainer silence despite community pings through July (including DataDog's dd-trace-go). Two gates — merge #1772, then cut a release — and neither moved this cycle.
  • The replacement is fully staged in-tree: go.yaml.in/yaml v2.4.4 + v3.0.4 already vendored; kyaml and kube-openapi already use go.yaml.in directly.

Prognosis: 🟡 Steady-negative. When testify does merge + release, yaml.v3 clears for the whole ~35-module tail in the same release that drops go-spew from testify itself.


Summary Table

Module Key blocker(s) Status Prognosis Δ since June
github.com/davecgh/go-spew testify (release), kustomize/kyaml (tag), k8s.io/utils/dump (nothing filed) 3 sequential gates; census corrected June's "testify-only" framing 🟡 ✅ → 🟡 correction
github.com/gogo/protobuf containerd/api (awaiting tag), k8s code-generator (KEP-5589 ph. 2) 4 of 6 blockers cleared in 6 weeks (ttrpc, typeurl, errdefs, cadvisor) 🟡 🔴 → 🟡 major
github.com/golang/protobuf grpc-go v1.x policy (+ etcd by design) konnectivity cleared; ttrpc/CSI done-on-main unreleased 🔴 floor 6 edges 9→8, root unchanged
github.com/google/btree peterbourgon/diskv (go.mod-invisible) JSON still stale; no k8s action on June items 🟡 none (etcd GA close-out only)
github.com/json-iterator/go SMD #292 (sole source blocker), client_golang (go.mod-only) dry-run import landed; json/v2 in Go 1.27 (~Aug) 🟡 keystone on final approach
github.com/modern-go/concurrent (coupled to json-iterator) resolves with SMD #292 🟡 follows json-iterator
github.com/modern-go/reflect2 (coupled to json-iterator) resolves with SMD #292 🟡 follows json-iterator
golang.org/x/exp antlr4-go/antlr (frozen mirror) maintainer promise expired; cel-go unpin in flight (#140205) 🟡 signal decayed
gopkg.in/yaml.v3 testify #1772 + release maintainer silent 2.5 months 🟡 steady-negative

Scorecard: 0 ✅ / 8 🟡 / 1 🔴 (June: 1 ✅ / 6 🟡 / 2 🔴 — the ✅ loss is a correction, the 🔴 loss is a genuine upgrade).


Recommended Actions

Immediate (can act now)

  1. Fix the stale unwanted-dependencies.json for btree — carried over from June, still undone. Record github.com/peterbourgon/diskv as the go.mod-invisible referencer; today the file implies btree is removable, which is wrong.
  2. Ask containerd for two tags: a containerd/api tag past #13490/#13740 (clears gogo's last external blocker) and a ttrpc tag past #248 (clears golang/protobuf 8→7). Also ask them to close the obsolete ttrpc#97.
  3. Nudge dolmen on testify #1772 and a v1.12 release. One merge + one tag clears yaml.v3's only gate and ships testify's go-spew removal to the entire ~35-module tail. The PR has been rebase-ready since 2026-05-06.
  4. Ask kustomize to tag kyaml past #6134 (spew removal), then bump k/k's kustomize/kyaml.

Medium-term (k8s-owned work)

  1. File the missing kubernetes/utils issue for dump's spew dependency — the final go-spew gate is entirely k8s-owned and currently untracked. spew's output format is baked into kubelet checkpoint checksums and DeepHashObject hashes, so the safe path is testify-style internal vendoring rather than a behavioral swap.
  2. Drive SMD #292 to merge: it needs a rebase and a decision window before the k8s 1.38 freeze; everything else (prereqs, dry-run import, Go 1.27) is in place. This single merge removes the last source-level use of json-iterator + both modern-go modules.
  3. Land #140205 (cel-go unpin, retargeted to v0.29.2) to shrink pinnedModules back to one.
  4. btree/diskv: patch or fork diskv's index.go onto k8s.io/utils/third_party/forked/golang/btree, or replace diskv in the discovery disk cache.
  5. KEP-5589 phase two (go-to-protobuf without gogo) — the last real engineering item for gogo; approach still TBD per liggitt (2026-05-21).
  6. Ping golang/protobuf#1711 again for the v1.36.12 tag so k/k can drop its protobuf pseudo-version pin (#137451).

Long-term (track externally)

  1. grpc-go / golang/protobuf: carry indefinitely on grpc v1.x; the floor is 6 entries. Re-check only if a grpc v2 ever materializes.
  2. antlr x/exp: keep nudging antlr/antlr4#4688; if the mirror stays frozen through another cycle, evaluate the fork/replace-directive route for the single slices.EqualFunc call.
  3. prometheus/client_golang jsoniter: watch #1998/#1649 — a module split would clear the require-graph edge without upstream ever "removing" jsoniter.

Changes Since the June 2026 Report

  • gogo/protobuf blockers 6 → 2 (🔴 → 🟡): ttrpc v1.2.9 dropped gogo (#239); typeurl v2.3.0 + errdefs left the vendor tree entirely; cadvisor replaced by the gogo-free cadvisor/lib (#3895, dims). k/k chain: #139594 → #139870 → #140337 → #140385. Corrections to June: typeurl's fix had been merged upstream since 2024 (not "structurally hard"); ttrpc needed no major version.
  • go-spew ✅ → 🟡 (correction): importer census surfaced k8s.io/utils/dump (spew centralized there deliberately in utils#343; checkpoint/hash-compat-sensitive; no removal issue exists) and kustomize kyaml (fixed on master via #6134, unreleased) alongside testify — which itself has now gone 11 months without a release.
  • golang/protobuf blockers 9 → 8: konnectivity-client v0.36.0 cleared it (ANP#824 → k/k #139636). ttrpc dropped the shim on main five hours after v1.2.9 tagged (#248); CSI master removed it in April (#602); etcd closed its cleanup umbrella (#14533) explicitly keeping the shim.
  • SMD keystone advanced: dry-run import of SMD HEAD landed (k/k #140294, v6.4.2 vendored); prereq coverage done early; encoding/json/v2 closed into Go 1.27 (~Aug 2026) with GOEXPERIMENT=nojsonv2 opt-out; #292 itself still open (needs-rebase, rescued from lifecycle/rotten on 2026-06-19).
  • client_golang v1.24.0 released 2026-07-20 — jsoniter unchanged; new module-split discussion (#1998) is the first, faint pathway. k/k bump imminent.
  • antlr signal decayed: jimidle's May "two weekends" promise produced nothing in two months; mirror PRs closed silently in June; #4688 still open.
  • cel-go: v0.29.2 out; repo moved to the cel-expr org; k/k pin issue #138334 closed (2026-05-05, pre-dated the June report but went unnoted there) and unpin PR #140205 is open.
  • etcd v3.7.0 GA vendored (#140333) — closes June's rc.0 caveats on both the btree and gogo/golang-protobuf entries.
  • Blocklist 88 → 89: github.com/google/cadvisor itself added (superseded by cadvisor/lib, whose release tags now auto-mirror to lib/ tags via cadvisor#3914).
  • btree: June's two action items — fix the stale JSON, patch/replace diskv — saw zero movement; carried forward as Immediate #1.
  • Version bumps in the blocker set: ttrpc v1.2.8→v1.2.9, containerd/api v1.11.0→v1.11.1, cadvisor v0.57.0→cadvisor/lib v0.60.4, etcd rc.0→v3.7.0, SMD v6.4.0→v6.4.2, kube-openapi 0519→0618 commit, konnectivity v0.34.0→v0.36.0; typeurl/errdefs/cadvisor(fat) left the tree. Path correction from June: the in-tree btree replacement is k8s.io/utils/third_party/forked/golang/btree.

Methodology

All data collected 2026-07-20 against master @ fc69f6c85ee (a detached worktree at upstream/master). go.mod claims verified by fetching the file at the exact tag/branch via raw.githubusercontent.com; releases/issues/PRs via gh; upstream research parallelized across six per-module groups, then cross-verified. Commands:

# Step 1 — unwanted modules still vendored (exact match)
python3 -c "
import json, re
data = json.load(open('hack/unwanted-dependencies.json'))
unwanted = set(data['spec']['unwantedModules'].keys())
vendored = {}
for line in open('vendor/modules.txt'):
    m = re.match(r'^# (\S+) (\S+)', line)
    if m: vendored[m.group(1)] = m.group(2)
for mod in sorted(set(unwanted) & set(vendored)):
    print(mod, vendored[mod], '=>', data['spec']['unwantedModules'][mod])
"

# Step 2 — who pulls each one in (per the JSON)
python3 -c "
import json
data = json.load(open('hack/unwanted-dependencies.json'))
for mod, refs in data['status']['unwantedReferences'].items():
    print(mod, len(refs)); [print('  ', r) for r in refs]
"

# Step 2b — TRUE source importers (catches go.mod-invisible blockers like diskv,
# and separates source-level from require-graph-only blockers like client_golang)
grep -rl '"<unwanted-module>' vendor/ --include='*.go' | grep -v 'vendor/<unwanted-module>/'

# Step 2c — diff the JSON + blocker versions against the previous report's commit
git show <prev-commit>:hack/unwanted-dependencies.json  # compare spec/status in python
git show <prev-commit>:vendor/modules.txt | grep '^# <blocker> '

# Steps 3-6 — vendor versions; go.mod at vendored tag / main / latest release
grep '^# ' vendor/modules.txt | grep -E '<blockers>'
curl -s "https://raw.githubusercontent.com/<org>/<repo>/<tag-or-branch>/<path>/go.mod" | grep '<unwanted>'
gh api repos/<org>/<repo>/releases --jq '.[].tag_name,.published_at' | head
git ls-remote --tags https://github.com/<org>/<repo>

# Step 7 — issue/PR archaeology (fetch real state + dates; don't trust search snippets)
gh search prs    --repo <org>/<repo> "<short-name>" --limit 8 --json number,title,state,url
gh search issues --repo <org>/<repo> "<short-name>" --limit 8 --json number,title,state,url
gh pr view    <n> --repo <org>/<repo> --json title,state,updatedAt,mergedAt,comments
gh issue view <n> --repo <org>/<repo> --json title,state,closedAt,updatedAt

Every go.mod claim was verified at the exact tag/branch; every "no movement" claim is corroborated by both empty searches and unchanged go.mod/source state; blocker-list evolution was reconstructed from the JSON at each k/k merge commit. The compiled report was then independently re-verified end-to-end before publication: ~120 transcribed claims cross-checked against the group research logs, 44 live API spot-checks of the load-bearing facts, all 25 GitHub link targets resolved, and the importer censuses reproduced against the worktree.


Appendix — Reproducing this report

The prompt that regenerates this analysis lives in the same gist as k8s-unwanted-deps-prompt.md: https://gist.github.com/dims/c8dd44ba82ed51dfc242b065a6ff2284

Run it from the root of a current Kubernetes checkout. It produces ~/notes/k8s-unwanted-deps-<YYYY-MM>.md; the gist keeps only the latest report plus the prompt (older reports live in the gist's revision history).

Prompt: Regenerate Kubernetes Unwanted Dependencies Report

The following prompt, given to Claude Code in the root of the Kubernetes repository, will reproduce an equivalent analysis:

Last updated 2026-07-20: added true-source-importer and previous-report-diff steps, corrected the gist-update commands (keep only the latest report + this prompt), refreshed the key-repos hints.


I want a detailed status report on the unwanted dependencies still present in the
Kubernetes vendor/ directory, matching the structure of the previous report in
https://gist.github.com/dims/c8dd44ba82ed51dfc242b065a6ff2284 (read it first for
per-module baselines; measure all deltas against it).

Run against a CURRENT master (git fetch upstream; use a detached worktree at
upstream/master if the local checkout is behind or dirty).

Here is the full workflow to follow:

### Step 1 — Find all unwanted modules still vendored (exact match, no prefix false-positives)

Run this Python snippet:

    python3 -c "
    import json, re
    with open('hack/unwanted-dependencies.json') as f:
        data = json.load(f)
    unwanted = set(data['spec']['unwantedModules'].keys())
    vendored = {}
    with open('vendor/modules.txt') as f:
        for line in f:
            m = re.match(r'^# (\S+) (\S+)', line)
            if m:
                vendored[m.group(1)] = m.group(2)
    found = sorted(set(unwanted) & set(vendored))
    print(f'Still vendored ({len(found)}):')
    for mod in found:
        print(f'  {mod} @ {vendored[mod]}  ({data[\"spec\"][\"unwantedModules\"][mod]})')
    "

Also note the blocklist size and spec.pinnedModules (report changes in both).

### Step 2 — For each remaining module, identify which direct dependencies bring it in

Read hack/unwanted-dependencies.json → status.unwantedReferences.

### Step 2b — Find the TRUE source importers in vendor/ (do not trust the JSON alone)

    grep -rl '"<unwanted-module>' vendor/ --include='*.go' | grep -v 'vendor/<unwanted-module>/'

This catches two failure modes the require-graph JSON misses:
- go.mod-invisible blockers: a GOPATH-era +incompatible module with no go.mod
  (e.g. peterbourgon/diskv keeping google/btree vendored while the JSON shows
  zero blockers for it)
- require-graph-only blockers: a module that requires the unwanted module in
  go.mod but whose importing packages are NOT vendored (e.g.
  prometheus/client_golang's api/ tree — jsoniter edge in go.mod, zero vendored
  source). Distinguish source-level from graph-only blockers in the report;
  they have different removal paths.

### Step 2c — Diff against the previous report's commit (delta reconstruction)

The previous report records its master commit in the header. Use it:

    git show <prev-commit>:hack/unwanted-dependencies.json   # diff blocklist + unwantedReferences in python
    git show <prev-commit>:vendor/modules.txt | grep '^# <blocker> '   # per-blocker version deltas
    git log --oneline <prev-commit>..HEAD -- hack/unwanted-dependencies.json   # find the k/k PRs that changed the list

### Step 3 — Determine current vendor versions of all primary blockers

    grep '^# ' vendor/modules.txt | grep -E '<list of primary blockers>'

### Step 4 — For each primary blocker at its current vendored version, check whether
the unwanted module is in its go.mod

    curl -s "https://raw.githubusercontent.com/<org>/<repo>/refs/tags/<version>/go.mod" \
      | grep '<unwanted-module>'

Note direct vs `// indirect` — an indirect entry still counts as a graph edge.

### Step 5 — Check the blocker's main/master branch for in-progress removal

    curl -s "https://raw.githubusercontent.com/<org>/<repo>/main/go.mod" \
      | grep '<unwanted-module>'

### Step 6 — Find the latest release of each blocker (to catch cases where main is
ahead of what we vendor, or a merged fix sits unreleased)

    gh api repos/<org>/<repo>/releases --jq '.[].tag_name,.published_at' | head
    git ls-remote --tags https://github.com/<org>/<repo>

Then check that latest release's go.mod too. "Merged on main but never tagged"
is a recurring state (typeurl sat 18 months; ttrpc dropped golang/protobuf 5
hours AFTER its tag) — always report the tag gate explicitly.

### Step 7 — Search for open issues and PRs in upstream repos about removal

For each blocker repo, run:

    gh search issues --repo <org>/<repo> "<short-name-of-unwanted-module>" \
      --limit 8 --json number,title,state,url
    gh search prs --repo <org>/<repo> "<short-name-of-unwanted-module>" \
      --limit 8 --json number,title,state,url

Fetch the body and latest comments of the most relevant results (search
snippets lie; get real state + dates):

    gh issue view <number> --repo <org>/<repo> --json title,state,body,comments

Parallelize steps 4-7 across per-module agent groups, each returning claims
tied to fetched URLs/tags plus a verification log; then run an independent
verification pass over the compiled report before publishing.

### Step 8 — Compile the report

Write a 6-7 page markdown to ~/notes/k8s-unwanted-deps-<YYYY-MM>.md with:

1. Background section explaining hack/unwanted-dependencies.json
2. Table of all currently-vendored unwanted modules (with vendored versions)
3. Per-module section for each one covering:
   - Blocklist rationale
   - Primary upstream blockers (from status.unwantedReferences + step 2b census)
   - Upstream status (latest release still has it? main/master removed it? tag gate?)
   - Known open issues and PRs in upstream repos (numbers, states, dates)
   - Prognosis (near / medium / long term)
4. Summary table with prognosis + delta-since-previous columns and a scorecard line
5. Recommended actions (immediate / medium-term / long-term); carry forward
   prior-report action items that saw no movement, and say so
6. Changes-since-previous-report section (include corrections to prior-report
   claims when the census/evidence contradicts them)
7. Methodology section with reproducible shell commands
8. Appendix pointing to the prompt file in the gist

Use ✅ 🟡 🔴 to indicate status (done-unreleased / in-progress / blocked).

### Key repos to check for each module (as of the July 2026 analysis):

- google/btree → peterbourgon/diskv (go.mod-invisible; client-go discovery disk
  cache) — etcd cleared as of v3.7.0
- go-spew → stretchr/testify (release gate), kubernetes/utils (dump — spew
  deliberately centralized there, hash/checkpoint-compat-sensitive),
  kubernetes-sigs/kustomize (kyaml, fixed on master, tag gate)
- yaml.v3 → stretchr/testify (PR #1772 + release; sole source importer)
- json-iterator + modern-go → kubernetes-sigs/structured-merge-diff (PR #292,
  sole SOURCE blocker), prometheus/client_golang (graph-only; api/ not vendored)
- gogo/protobuf → containerd/containerd (api tag gate), k8s.io/code-generator
  go-to-protobuf (KEP-5589 phase two) — ttrpc/typeurl/cadvisor cleared
- golang/protobuf → grpc/grpc-go (v1.x policy, permanent), etcd-io/etcd (shim
  kept by design, #14533), containerd/ttrpc (tag gate), CSI spec (v1.13 gate),
  golang/protobuf#1711 (v1.36.12 tag request for the k/k pseudo-version pin)
- golang.org/x/exp → antlr4-go/antlr (frozen mirror; real fix antlr/antlr4#4688),
  cel-go (now under the cel-expr org), grpc-ecosystem/go-grpc-middleware

### Step 9 — Update the gist (keep ONLY the latest report + this prompt)

The gist holds exactly two files: the newest k8s-unwanted-deps-<YYYY-MM>.md and
k8s-unwanted-deps-prompt.md (update the prompt in place when the methodology
changes). Delete the previous report file — old reports remain available in the
gist's revision history. `gh gist edit --add` cannot delete files; use the API:

    python3 -c "
    import json
    body = {
      'description': 'Kubernetes unwanted vendor dependencies status — <Month YYYY>',
      'files': {
        'k8s-unwanted-deps-<PREV-YYYY-MM>.md': None,
        'k8s-unwanted-deps-<YYYY-MM>.md': {'content': open('<path-to-new-report>').read()},
        'k8s-unwanted-deps-prompt.md': {'content': open('<path-to-updated-prompt>').read()},
      },
    }
    print(json.dumps(body))
    " | gh api --method PATCH gists/c8dd44ba82ed51dfc242b065a6ff2284 --input -

Return the gist URL.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment