Watching Rancher Desktop reconcile
Rancher Desktop 2.0's settings screen covers a handful of fields, and the API
underneath it has no reference docs. But you don't really need them; you can
ask rdd itself what you can change, and once you change something, you can
watch it reshape the virtual machine, step by step, until the actual state
matches what you asked for.
The companion post explains
why Rancher Desktop can behave this way. rdd is a Kubernetes API server, and
Rancher Desktop's own configuration lives in it as a single object called
App. This post is about what that buys you in practice.
The API describes itself​
There are no external docs for the App object, but it doesn't need any,
because you can query the API itself for documentation. You can read it with
explain, for example:
$ rdd ctl explain app.spec.kubernetes
FIELD: kubernetes <Object>
DESCRIPTION:
kubernetes specifies the Kubernetes configuration.
FIELDS:
enabled <boolean> -required-
enabled specifies whether Kubernetes should be enabled in the VM.
version <string>
version is the Kubernetes version to use (e.g. "1.32.2").
You can walk any part of the object, from app.spec down to a single field.
The descriptions are not baked into explain; they come straight from the Go
source.
Each field on the App type carries a comment, a build step turns those types
into the CRD1 and copies the comments into its OpenAPI schema, and the API
server hands that schema to any client that asks (explain is just one such
client). So the documentation is generated from the code, and it cannot drift
out of sync with it.
The writable side works the same way. rdd set is how you change the App
object, and it says up front where its knobs come from: "Valid property names
and types are derived from the App CRD at runtime." So the property list in its
help comes straight from the CRD too:
$ rdd set --help # trimmed to the property list
Available properties:
containerEngine.name (moby|containerd)
kubernetes.enabled (boolean)
kubernetes.version (string)
running (boolean)
Each of those shows the same description you'd get from explain, and
nobody maintains this list by hand; when we add a field to the App type,
it turns up here, typed and documented, in the next build.
One setting in motion​
So let's change something, and turn Kubernetes on:
rdd set kubernetes.enabled=true
The command doesn't return right away. It writes your one-line change into the
App object's spec and then waits, quietly, until everything has settled.
rdd logs at warn level by default, and a condition changing is only info, so
you see nothing unless you ask for it. Ask, and you can watch it work:
$ rdd --log-level=info set kubernetes.enabled=true # trimmed
INFO[0000] App updated
INFO[0000] Waiting for App to settle
INFO[0000] ContainerEngineReady=True: Container engine synced (Connected)
INFO[0000] Settled=False: Applying the configuration change to the VM (ApplyingTemplate)
INFO[0000] ContainerEngineReady=False: failed to ping Docker: ... no such file or directory (ConnectFailed)
INFO[0002] Running=False: Stopped for restart (Stopped)
INFO[0002] Running=False: Lima instance is starting (Starting)
INFO[0002] ContainerEngineReady=False: Container engine stopped (Stopped)
INFO[0022] Running=True: Lima instance is running (Started)
INFO[0022] KubernetesReady=False: Waiting for k3s API server (Probing)
INFO[0022] ContainerEngineReady=True: Container engine synced (Connected)
INFO[0027] KubernetesReady=True: Kubernetes cluster is ready (Ready)
INFO[0027] Settled=True: App has reached the desired state (Settled)
The whole change took 27 seconds,2 and 20 of those were the VM
restarting. The Docker error partway through comes from that same restart.
The container engine goes down with the VM, so its socket disappears for a
while, and ContainerEngineReady goes from connected to failed to stopped and
back again. Conditions describe the state at that moment. They change as often
as the state does.
That's all ordinary Kubernetes behaviour. You wrote down a desired state. A
controller saw the gap between what you asked for and what was actually true,
and closed it. To do that, it reconfigured the virtual machine and
restarted it, waited for k3s to answer, and merged the new cluster into your
kubeconfig. Most of those lines are a condition changing on the App object's
status; these are the same conditions you'd watch on a Deployment rolling
out. rdd set returns once Settled reaches True. The cluster is up at that
point.
That's the reconcile loop, the pattern Kubernetes itself is built on. Rancher
Desktop manages a VM and a cluster the same way Kubernetes manages your pods;
the approach survives interruption. Kill rdd in the middle of a change, and
the next start should pick up from wherever the world actually is. A script
would have to remember which steps it had already run, because a crash loses
that record.
Why build it this way​
Modelling the app as a controller puts everything it can do behind an API, and
Rancher Desktop's own GUI is just another client of it. Nothing is locked
inside the window. Anything you can do by clicking, you can do from a script,
with kubectl or with the standard Kubernetes client libraries, and our own
integration tests exercise the application the same way a GUI user would.
The API also describes itself, which is what explain was doing earlier. You
can find out what a field is for without hunting down a separate document that
has since gone stale, and so can an autonomous AI agent working on your behalf.
💬 Questions or feedback? Discuss this post on GitHub →