Skip to content

Migrate off the removed Talos MachineConfig.Kubelet / ClusterConfig.CoreDNS accessorsΒ #6776

Description

@devantler

πŸ€– Generated by the Agentic Engineer

Evidence

chore(deps): bump github.com/siderolabs/omni/client from 1.9.1 to 1.10.4 (#6724) has been red and conflicting since 2026-08-27. It is not a flaky or transient failure β€” the bump is structurally unmergeable as it stands.

go.mod on main holds both Talos modules at the same version:

github.com/siderolabs/talos                 v1.14.0-alpha.2
github.com/siderolabs/talos/pkg/machinery   v1.14.0-alpha.2

omni/client v1.10.4 requires github.com/siderolabs/talos/pkg/machinery v1.14.0-beta.1 (read from its published go.mod on the module proxy), so the bump raises machinery alone and leaves the pair mismatched. beta.1 removed accessors that both KSail and talos v1.14.0-alpha.2 still call:

pkg/fsutil/configmanager/talos/configs.go:615  cp.Machine().Kubelet undefined
pkg/fsutil/configmanager/talos/configs.go:619  cp.Machine().Kubelet undefined
pkg/fsutil/configmanager/talos/version.go:223  machineCfg.Kubelet undefined
talos@v1.14.0-alpha.2/pkg/images/list.go:43    config.Cluster().CoreDNS undefined
talos@v1.14.0-alpha.2/pkg/images/list.go:45    config.Machine().Kubelet undefined

The last two are inside the pinned talos module itself, so the dependency graph is internally inconsistent β€” this cannot be fixed in KSail's code alone while talos stays at alpha.2.

Confirmed against machinery v1.14.0-rc.2: config.MachineConfig no longer declares Kubelet(), and CoreDNS survives only as K8sCoreDNSConfig in config/config/k8s.go. Upstream moved kubelet and CoreDNS settings out of the machine/cluster config into a separate K8s config document.

Problem and who it affects

Every omni/client bump is blocked behind this, so KSail silently stops receiving Omni updates β€” including security fixes. Renovate/Dependabot will keep reopening the bump and it will keep failing, which also burns CI on every attempt.

What needs to happen

Bump github.com/siderolabs/talos and github.com/siderolabs/talos/pkg/machinery together to a matched version (v1.14.0-rc.2 is the newest published pair), then migrate KSail's three call sites off the removed accessors onto the new K8s config document API.

Acceptance criteria

  • Both Talos modules pinned to the same version in go.mod.
  • The three call sites above read kubelet/CoreDNS settings through the new document API.
  • The absent-document case is handled explicitly β€” a config with no K8s document must not silently read as "no kubelet configured" (this is the fail-open shape, not a nil check to wave through).
  • Existing tests pass unmodified; behaviour is preserved.
  • chore(deps): bump github.com/siderolabs/omni/client from 1.9.1 to 1.10.4Β #6724 (or its successor bump) goes green and merges.

Size

Medium. Three call sites, but the new accessor pattern and the absent-document semantics need to be understood first, so it is not a mechanical rename.

Blocker: loft-sh/vcluster k8s.io 0.37 compatibility | upstream | last-verified 2026-10-24: the old apiserver claim is superseded: the official Go proxy now selects loft-sh/apiserver v0.0.0-20261015114517-9c2b700ef6e6 with k8s.io/apiserver and client-go v0.37.1. The latest stable vcluster v0.37.4 still requires both at v0.36.0, leaving that integration prerequisite unmet.


Generated by Claude Code

Activity

  1. moved this to πŸ“₯ Backlog in 🌊 Project Boardon Aug 30, 2026
  2. devantler commented on Sep 1, 2026

    @devantler
    ContributorAuthor

    πŸ€– Generated by the Agentic Engineer

    Raising the priority signal on this: two open dependency PRs are now parked on this migration, and neither can build until it lands.

    Both fail identically at the current head, with compile errors in pkg/fsutil/configmanager/talos/version.go and configs.go naming exactly the accessors this issue covers (machineCfg.Kubelet, cp.Machine().Kubelet, config.Cluster().CoreDNS). Verified against both heads on 2026-09-01.

    Worth noting for whoever picks this up: the failures present as 29 jobs all failing at a step called πŸ“¦ Cache KSail Binary, which reads like one shared infrastructure flake. It is not β€” that step is where the binary is compiled. Only the job annotations reveal the real cause, so diagnose this class from annotations rather than from the failing step name.

    Neither PR was closed, because closing a Dependabot PR emits a persistent ignore for that release and would suppress the bump after this merges. Rebase both once this lands.

  3. self-assigned this
    on Sep 1, 2026
  4. devantler commented on Sep 1, 2026

    @devantler
    ContributorAuthor

    πŸ€– Generated by the Agentic Engineer

    Measured: this is blocked upstream, and the "three call sites" framing understates it

    I attempted the migration end to end today (bumped both Talos modules to the matched pair v1.14.0-rc.2, ran go mod tidy, built). The three KSail call sites are real and small β€” but they are not what stops this landing.

    Every Talos version that removed the accessors also moves Kubernetes to 0.37

    Read from the module proxy:

    siderolabs/talos requires k8s.io/api
    v1.14.0-alpha.2 (current pin) v0.36.2
    v1.14.0-beta.0 v0.37.0-beta.0
    v1.14.0-beta.1 v0.37.0-beta.0
    v1.14.0-rc.1 v0.37.0-rc.0
    v1.14.0-rc.2 v0.37.0-rc.1

    The accessors were removed in the same line that jumps to Kubernetes 0.37, so the code migration and the whole-stack Kubernetes bump are inseparable. machinery v1.14.0-alpha.2 does not declare K8sKubeletConfig() / K8sCoreDNSConfig() at all (checked its config/config/{config,k8s}.go), so there is also no forward-compatible rewrite that works against both pins.

    With the bump applied, two transitive dependencies stop compiling β€” one is fixable, one is not

    go build ./... after the bump:

    1. k8s.io/cri-client v0.36.1 β€” missing CheckpointPod for cri-api 0.37. Fixable: v0.37.0-rc.1 is published; bumping it clears these errors entirely (verified).
    2. github.com/loft-sh/apiserver β€” not fixable by us:
    pkg/builders/default_storage_strategy.go:33: *DefaultStorageStrategy does not implement
      k8s.io/apiserver/pkg/registry/rest.RESTUpdateStrategy (wrong type for method AllowCreateOnUpdate)
        have AllowCreateOnUpdate() bool
        want AllowCreateOnUpdate(context.Context) bool
    

    It reaches us as ksail/pkg/svc/provisioner/cluster/vcluster β†’ loft-sh/vcluster/pkg/cli β†’ loft-sh/agentapi/v4 β†’ loft-sh/apiserver/pkg/builders. Its newest published revision is v0.0.0-20260707184419-aef558a5ae8d (2026-07-07), which pins k8s.io/apiserver v0.36.0. Upgrading vcluster does not help: even v0.37.0-rc.4 still requires that exact same loft-sh/apiserver revision and still pins k8s.io/apiserver v0.36.0.

    After bumping cri-client, loft-sh/apiserver is the only remaining non-KSail wall β€” the conjunct is isolated.

    A hazard for whoever picks this up

    The absent-document criterion above is sharper than it looks, and it is the same shape as the #5947 phantom-upgrade defence already documented in detect_inplace.go.

    Old: provider.Machine().Kubelet() returns nil when the config carries no machine.kubelet section, so KubernetesVersionFromProvider falls through and returns "".

    New: K8sKubeletConfig() returns a non-nil, defaulted &KubeletConfig{} in that case, and its Image() yields the machinery's compiled-in KubeletImage:v<DefaultKubernetesVersion>. A literal != nil port therefore makes the function report Talos's bundled default as if it were the cluster's running version β€” exactly the phantom worker kubelet upgrade alignKubernetesVersion was written to prevent. The new interface exposes no "was this document present" signal, so the guard has to be explicit.

    Effect on the blocked PRs

    #6724, #6796 and #6813 all fail with the identical three-call-site compile errors and are parked on this issue. Size is not Medium; it is gated on an upstream release.

  5. removed their assignment
    on Sep 1, 2026
  6. added a commit that references this issue on Sep 2, 2026
  7. devantler commented on Sep 2, 2026

    @devantler
    ContributorAuthor

    πŸ€– Generated by the Agentic Engineer

    Re-verified today against talos/machinery v1.14.0-rc.2. The blocker is unchanged and still
    correct, so this stays blocked β€” but the KSail-side work is now fully scoped, so it can be executed
    directly once the upstream ships rather than re-investigated.

    Verified accessor mapping (each confirmed present in rc.2 and absent from the alpha.2 that
    main pins, which is why none of this can land ahead of the bump):

    Removed Replacement Sites
    cfg.Machine().Kubelet() cfg.K8sKubeletConfig() configmanager/talos/configs.go:615,619; version.go:243
    cfg.Cluster().Endpoint() cfg.K8sClusterConfig().ClusterEndpoint() provisioner/cluster/talos/detect_inplace.go:270,320,324; provisioner_hetzner.go:550,554; update.go:436,440,1206
    cfg.Cluster().IssuingCA() cfg.K8sAPIServerCAConfig().IssuingCA() detect_inplace.go:413

    12 sites total. K8sKubeletConfig returns the same ExtraArgs() map[string][]string and
    Image() string shape the old Kubelet interface had, so the call sites change but their logic does
    not. I applied the 3 kubelet sites locally and confirmed those specific compile errors disappear;
    the remaining 9 are a mechanical application of the same pattern.

    Note the repository has already done this migration once for a sibling document β€” the
    K8sAPIServerConfig comment in version.go records the same move for cluster.apiServer β€” so this
    follows existing in-repo precedent rather than introducing a new pattern.

    Also worth recording, because it changes the shape of the blocker: of the two Kubernetes 0.37
    skews, only one is external.

    • k8s.io/cri-client@v0.36.1 (missing CheckpointPod) is fixable β€” v0.37.0-rc.1 exists and
      clears that error; verified locally.
    • loft-sh/apiserver is the only genuinely external half, and it is unmoved: newest published
      revision is still v0.0.0-20260707184419-aef558a5ae8d (2026-07-07), and loft-sh/vcluster v0.37.0-rc.4
      still requires exactly that revision.

    Blocker: loft-sh/apiserver β€” needs a revision built against Kubernetes 0.37 apiserver | last-verified 2026-09-02: not shipped

  8. devantler commented on Sep 2, 2026

    @devantler
    ContributorAuthor

    πŸ€– Generated by the Agentic Engineer

    Blast radius is wider than the body records

    The body cites #6724 (omni/client) as the affected bump. Measured today, this blocker strands every open dependency PR in KSail that pulls machinery past alpha.2 β€” it is now the queue-level blocker, not a single-bump one:

    PR Bump Reaches machinery Failing
    #6826 talos group (3 updates) v1.14.0-rc.2 39
    #6845 image-factory 1.3.3 β†’ 1.5.1 v1.14.0-rc.2.0.20260825161121 40

    #6845 is worth calling out because it is not a Talos bump by name β€” image-factory drags machinery forward transitively, so this blocker is reached by bumps that never mention Talos.

    Both fail with a byte-identical error triple, read from their own build logs (#6826 job 100032940941, #6845 job 100396113250):

    pkg/fsutil/configmanager/talos/configs.go:615  cp.Machine().Kubelet undefined
    loft-sh/apiserver@v0.0.0-20260707184419  *DefaultStorageStrategy does not implement RESTUpdateStrategy
    k8s.io/cri-client@v0.36.1  *remoteRuntimeService does not implement RuntimeService (missing method CheckpointPod)
    

    Only the first line is KSail's own code and in scope for the migration in this issue's acceptance criteria. The other two are the loft-sh/apiserver blocker already recorded in the **Blocker:** line, which independently re-confirms it: the pinned revision still carries the 0.36 apiserver, so the KSail-side migration alone cannot turn these PRs green.

    No change to actionability

    The **Blocker:** line was re-verified today and remains accurate; this issue stays correctly blocked. Recording the radius so a later run does not re-diagnose #6826/#6845 from scratch, and so the priority of the upstream wait is visible.

    Note one detail for whoever picks this up: the blocker line records k8s.io/cri-client reaching a stable v0.37.0, but both PRs still resolve cri-client@v0.36.1, so that half has not actually moved in the built graph yet.

  9. devantler commented on Sep 7, 2026

    @devantler
    ContributorAuthor

    πŸ€– Generated by the Agentic Engineer

    The migration is now a solved, measured quantity β€” only the upstream blocker (#6728) remains.

    I carried this out end-to-end on #6826's head (talos + machinery v1.14.0-rc.2) to find out whether anything here is actually hard. Nothing is: every call site is a receiver-preserving accessor move, and the whole thing builds.

    The complete mapping β€” 28 call sites, 11 files:

    removed accessor replacement sites
    X.Machine().Kubelet() X.K8sKubeletConfig() 2 + 1 doc example
    X.Cluster().Endpoint() X.K8sClusterConfig().ClusterEndpoint() 19
    X.Cluster().IssuingCA() X.K8sAPIServerCAConfig().IssuingCA() 9

    All three land on the Config interface, the same K8s*Config family the code already uses for K8sAPIServerConfig() and K8sFlannelCNIConfig(), so this finishes a migration that is already half-done. ExtraArgs() needs no work β€” it was already adapted to map[string][]string.

    Two shapes simplify rather than just move:

    • IsKubeletCertRotationEnabled loses its cp.Machine() == nil guard, since the kubelet document is reached directly from the config.
    • KubernetesVersionFromProvider loses a nesting level for the same reason.

    Measured result: with this migration applied and k8s.io/cri-client raised to v0.37.0-rc.1 (published since this issue was filed β€” it clears the CheckpointPod mismatch), go build ./... leaves exactly one error, in loft-sh/apiserver. That is #6728, and it is not ours to fix.

    So the ordering is settled: this issue is mechanical and low-risk, but cannot land alone β€” the replacement accessors do not exist in the pinned v1.14.0-alpha.2 (verified against the module cache), so the change only compiles with the bump. It rides with whichever dependency PR lands once #6728 clears; it is not startable before then.

  10. devantler commented on Sep 16, 2026

    @devantler
    ContributorAuthor

    πŸ€– Generated by the Agentic Engineer

    This migration provably cannot be done ahead of the bump

    Worth stating plainly, because the issue title reads like ordinary prep work that could land on its own: it cannot. Verified directly against the module cache at the version main pins today (v1.14.0-alpha.2):

    • MachineConfig.Kubelet() still exists β€” config/config/machine.go:35
    • the replacement root-level accessor Config.K8sKubeletConfig() does not exist yet β€” 0 occurrences in config/config/config.go, against a control grep for Machine() in the same file returning 1, so the absence is a real absence and not a failed read

    So at the pinned version there is nothing to migrate to, and the old accessor only disappears at the moment the bump lands. The accessor migration and the version bump have to be one change β€” either an agent-authored PR that does both, or an adaptation commit on the bump PR itself. Splitting them is not an option that exists.

    Consolidating #6962 into this issue

    #6962 describes the same root cause and the same three call sites. I am closing it as a duplicate of this issue (older, and broader β€” it also covers ClusterConfig.CoreDNS). Its analysis is better in one respect, so it is preserved here:

    Diffing the config/config/machine.go interfaces across the window shows this is a relocation, not a rename, and that eight methods left MachineConfig: Controlplane, Pods, Kubelet, Kernel, NodeLabels, NodeAnnotations, NodeTaints, BaseRuntimeSpecOverrides. The build only ever reports Kubelet because compilation stops at the first failure β€” the three-error list understates the work. Kubelet config now hangs off the root config as Config.K8sKubeletConfig() K8sKubeletConfig.

    The true root blocker is #6728, not this issue

    Live today on #6826 and #6845 (both re-created by Dependabot, both re-locked minutes later by ksail-bot's module sync, both now parked), πŸ—οΈ Build fails with an identical three-class error set:

    1. the Talos accessor removal above β€” ours, and fixable once the bump is in the same change
    2. loft-sh/apiserver β€” *DefaultStorageStrategy does not implement rest.RESTUpdateStrategy (wrong type for method AllowCreateOnUpdate)
    3. k8s.io/cri-client@v0.36.3 β€” *remoteRuntimeService does not implement RuntimeService (missing method CheckpointPod)

    Classes 2 and 3 are the k8s 0.36/0.37 pin conflict tracked by #6728, and they cannot be fixed from this repository. Fixing the accessors alone will not make any talos bump green β€” #6728 gates the whole lane, and this issue is downstream of it.

    Blocker re-verified 2026-09-16 β€” still real, unchanged

    Re-checked via the Go module proxy (not the upstream repository): loft-sh/apiserver@latest still resolves to v0.0.0-20260903125814-2353e4b52582 (2026-09-03) β€” no new revision since the last record β€” and its published go.mod still requires k8s.io/apiserver v0.36.0. The replace k8s.io/apiserver => github.com/loft-sh/k8s-apiserver v0.36.0-loft.0 it carries is ignored by consumers, so it does not reach ksail.

    Blocker: loft-sh/apiserver β€” needs a revision built against Kubernetes 0.37 apiserver | upstream | last-verified 2026-09-16: not shipped (@latest = v0.0.0-20260903125814-2353e4b52582, go.mod still requires k8s.io/apiserver v0.36.0); see ksail#6728 for the full pin conflict

  11. moved this from πŸ“₯ Backlog to 🧊 Icebox in 🌊 Project Boardon Oct 8, 2026
  12. added a commit that references this issue on Oct 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Fields

    No fields configured for Refactor.

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions