Reading talosctl apply-config --dry-run from a script

The dry run prints its summary and diff to stderr, no drift is the text No changes, and a regex that anchors at the start of the string makes a role re-apply every node every run. Plus how to find what forces a reboot, and why failed applies must never be logged.

The core of my Talos role is: render the config, ask every node what would change, apply only where something would. That “ask” is talosctl apply-config --dry-run, and getting a script to read it correctly took longer than the rest of the role.

Where the output is

The “Dry run summary”, the line “Applied configuration without a reboot” and the diff all go to stderr. Stdout is empty. A node with nothing to do prints No changes. after Config diff:. A script has to capture and parse both streams.

An anchored match applied every node every run

Jinja’s regex test anchors at the start of the string. The text I was looking for sits in the middle of multi-line output, so it never matched, and the role decided that every node had drifted on every run. Use search:

- when: output is search('Applied configuration without a reboot')

Which change forces a reboot

--mode auto decides by itself whether a reboot is needed, and the dry run tells you which way it would go. I used that to find out why a first apply would reboot every node of my live cluster. I rendered variants of the config with -e overrides and dry-ran each against one node, read-only: removing a pin, matching the installer image, turning off certificate SANs made no difference. Collapsing the long list of identical machine.disks entries did. Any change to machine.disks made auto choose a reboot. That meant a rolling reboot of the whole cluster, and I knew it before touching anything.

A failed apply echoes the whole config

When apply-config fails, the error includes the machine config, certificate authority keys among it. Run those tasks with no_log, and when debugging by hand look at only the first lines of the error.

When a command hangs

talosctl etcd members never returns when etcd is half up. The usual perl one-liner, perl -e 'alarm ...; exec', does nothing here, because Go programs ignore SIGALRM. A perl fork that kills the child with kill 9 after a deadline works on Linux and macOS, and needs no coreutils timeout. And in 1.14 --insecure is a flag of the subcommand, so it goes after it: get disks --insecure --nodes <ip>.