Fil-C on Nix

Filnix wiki

mbrock/filnix

tests/nixos/first-switch.nix

The AMI's first boot (nixos/amazon.nix) with #filnix user data: the filnix-first-switch unit switches to the named system while boot goes on. As a oneshot it deadlocked: switch-to-configuration waited for multi-user.target's start job, which waited for the unit doing the switch. The target here is a system in the store, so the VM needs no network.

λfilnix
let
8amazon=
λlib, …
11imports=
import../../nixos/amazon.nix inherit filnix;
12systemd.services.fetch-ec2-metadata.enable=
false
13systemd.services.apply-ec2-data.enable=
false
14systemd.services.growpart.enable=
false

The test VM boots without one, and switching would install it.

16boot.loader.grub.enable=
lib.mkForcefalse
17systemd.services."serial-getty@ttyS0".enable=
lib.mkForcefalse
in
21name=
"first-switch"
22nodes.target=
23imports=
24environment.etc."filnix-switched".text=
"yes"

Test nodes leave switch-to-configuration out by default.

26system.switch.enable=
true

A new service wanted by multi-user.target, as demo-wiki's are: the switch then starts the target, whose boot start job was still waiting for the unit doing the switch.

30systemd.services.filnix-switched=
31wantedBy=
"multi-user.target"
32serviceConfig.Type=
"oneshot"
33serviceConfig.RemainAfterExit=
true
34script=
"true"

A unit that fails after the switch, as ACME does while the name points elsewhere: the switch still counts and does not run again.

38systemd.services.filnix-switch-broken=
39wantedBy=
"multi-user.target"
40serviceConfig.Type=
"oneshot"
41script=
"exit 1"
uses amazon
44nodes.machine=
λnodes, …
47imports=
48virtualisation.additionalPaths=
nodes.target.system.build.toplevel
49environment.etc."ec2-metadata/user-data".text=
#filnix ${nodes.target.system.build.toplevel}
uses amazon
54testScript=
λnodes, …
machine.start()
machine.wait_for_unit("multi-user.target")
machine.wait_until_succeeds("test -e /var/lib/filnix-first-switch/done", timeout=900)
current = machine.succeed("readlink -f /run/current-system").strip()
assert current == "${nodes.target.system.build.toplevel}", current
machine.succeed("test \"$(cat /etc/filnix-switched)\" = yes")
machine.succeed("systemctl is-active filnix-switched.service")
machine.succeed("systemctl show -p Result --value filnix-first-switch | grep -qx success")
# It runs once.
machine.systemctl("restart filnix-first-switch")
machine.succeed("journalctl -u filnix-first-switch | grep -q 'switching to'")
machine.succeed("journalctl -u filnix-first-switch -o cat | grep -A3 'units failed' | grep -q filnix-switch-broken")
print(machine.succeed("journalctl -u filnix-first-switch -o cat"))