0075. adopt skips unapproved machines and names the v1.14 documents it cannot capture

Status: Accepted · Date: 2026-10-26 · Design section: Bootstrap (ichor adopt), Patch layers

Decided: adopt reads approved machines only; for the v1.14 documents Ichor cannot yet express it says which document and why. Whether those documents become patch kinds or spec fields is left open (below).

Context

A real-cluster test on Talos v1.14.3 could not reach zero drift. Two findings.

  1. ichor adopt reported KubeNetworkConfig, UnattendedInstallConfig and KubeFlannelCNIConfig (in another run: pod subnets, service subnets, cert SANs, UnattendedInstallConfig) as fields it "cannot patch automatically", and the tester fell back to versionContract v1.13, where the default pod subnet 10.244/16 then collided with the outer network.
  2. adopt dialed every TalosMachine of the cluster, including a pending machine in maintenance mode, and failed with a TLS error. The tester had to disable discovery, delete the machine, adopt, and re-enable discovery.

What is true (Talos machinery v1.14.3, Ichor at this commit)

  • pkg/render.baseConfig calls generate.NewInput with only the secrets bundle, the version contract and the control-plane scheduling option. It passes no pod or service subnet, no install disk or image, no certificate SANs and no CNI choice. Nothing in TalosCluster.spec or TalosMachine.spec carries those values either (there are no such fields).
  • At contract v1.14 the generator therefore emits its defaults: KubeNetworkConfig with 10.244.0.0/16 and 10.96.0.0/12, KubeFlannelCNIConfig (vxlan), an empty machine.certSANs, and no UnattendedInstallConfig at all (generate/init.go only emits it when an install disk was passed). Below v1.14 the same values live in the legacy cluster.network, machine.install and machine.certSANs.
  • The TalosConfigPatch document kinds are SysctlConfig, KernelModuleConfig, Config, VolumeConfig, UserVolumeConfig and RawVolumeConfig. KubeNetworkConfig, UnattendedInstallConfig, KubeFlannelCNIConfig and KubeAPIServerConfig (cert SANs) are not among them.
  • The only route left is the legacy Config document, and Talos rejects it next to the v1.14 documents: KubeNetworkConfig and KubeFlannelCNIConfig conflict with cluster.network, UnattendedInstallConfig conflicts with machine.install (V1Alpha1ConflictValidate in machinery). ichor init writes machine.install and machine.certSANs through such a patch, which works only because the base render has no install document.
  • So at v1.14 an Ichor-rendered cluster can have neither non-default subnets, nor a chosen install disk (other than the legacy form), nor "no flannel". A node generated by talosctl gen config (or by a harness that drops the flannel document) differs from the render in exactly those documents, and adopt cannot capture them because it only captures SysctlConfig and KernelModuleConfig.

Decision

Done here:

  • adopt only reads machines with approvalState: approved. Pending and rejected machines are not dialed, and the report says machine "x": skipped, approval state is pending (...). An unapproved machine has no credentials in the bundle and, in maintenance mode, its Talos API accepts no client certificate, so a failure there is certain.
  • When the paths adopt leaves alone belong to KubeNetworkConfig, UnattendedInstallConfig or KubeFlannelCNIConfig, the report names the document, what it holds, and that no patch kind exists for it. The old advice to "write a TalosConfigPatch by hand" is kept only for the legacy Config paths, where it is true.

Open: how a v1.14 cluster gets these values

Decided by ADR 0077 (option C, with the per-node half as a spec field).

Not decided here, because the design document does not settle it and each option reaches beyond pkg/render.

A. Extend the patch allow-list (design: "Kinds outside the allow-list are listed, and the user chooses between extending the list and dropping them"). Add KubeNetworkConfig, UnattendedInstallConfig and KubeAPIServerConfig as mirrorgen roots; then adopt can capture them and subnets/disk/SANs are patch values at the right layer (cluster for subnets, machine for disk and SANs). Cost: CRD size, the proto and generated TS, the UI kind list, fieldmeta rules, conversion in DocumentFromPatch. A patch cannot delete a document, so "no flannel" cannot be a patch. B. Spec fields. TalosCluster.spec.network.{podSubnets,serviceSubnets}, TalosMachine.spec.install.{disk,image} and cert SANs, rendered by the system layer into whichever form the contract allows (the pattern injectKubeProxy already uses). "No flannel" falls out of the system layer: drop KubeFlannelCNIConfig when spec.cni.preset is not flannel (design: the system layer generates "CNI settings"). Cost: CRD changes, webhook rules (subnets immutable after creation), adopt would map drift to those fields. C. A mix: B for the cluster-wide, install-time values (subnets, CNI), A for per-node documents.

Dropping the flannel document for non-flannel presets changes the rendered hash of existing v1.14 Cilium clusters, so that step needs its own review of what the drift check does to them.

Search the docs

Search pages and headings