Skip to content

Commit 18a4a69

Browse files
Mic92mergify[bot]
authored andcommitted
integrate hardware configuration generation
wip
1 parent 9508c00 commit 18a4a69

File tree

4 files changed

+134
-3
lines changed

4 files changed

+134
-3
lines changed

src/get-facts.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ hasDoas=$(has doas)
1818
hasWget=$(has wget)
1919
hasCurl=$(has curl)
2020
hasSetsid=$(has setsid)
21+
hasNixOSFacter=$(command -v nixos-facter >/dev/null && echo "y" || echo "n")
2122
FACTS

src/nixos-anywhere.sh

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ phases[disko]=1
2323
phases[install]=1
2424
phases[reboot]=1
2525

26+
hardwareConfigBackend=none
27+
hardwareConfigPath=
2628
sshPrivateKeyFile=
2729
if [ -t 0 ]; then # stdin is a tty, we allow interactive input to ssh i.e. passwords
2830
sshTtyParam="-t"
@@ -47,6 +49,7 @@ hasDoas=
4749
hasWget=
4850
hasCurl=
4951
hasSetsid=
52+
hasNixOSFacter=
5053

5154
sshKeyDir=$(mktemp -d)
5255
trap 'rm -rf "$sshKeyDir"' EXIT
@@ -104,6 +107,9 @@ Options:
104107
build the closure on the remote machine instead of locally and copy-closuring it
105108
* --vm-test
106109
build the system and test the disk configuration inside a VM without installing it to the target.
110+
* --generate-hardware-config facter|nixos-generate-config <path>
111+
generate a hardware-configuration.nix file using the specified backend and write it to the specified path.
112+
The backend can be either 'facter' or 'nixos-generate-config'.
107113
* --phases
108114
comma separated list of phases to run. Default is: kexec,disko,install,reboot
109115
kexec: kexec into the nixos installer
@@ -152,6 +158,22 @@ parseArgs() {
152158
shift
153159
shift
154160
;;
161+
--generate-hardware-config)
162+
if [[ $# -lt 3 ]]; then
163+
abort "Missing arguments for --generate-hardware-config <backend> <path>"
164+
fi
165+
case "$2" in
166+
nixos-facter | nixos-generate-config)
167+
hardwareConfigBackend=$2
168+
;;
169+
*)
170+
abort "Unknown hardware config backend: $2"
171+
;;
172+
esac
173+
hardwareConfigPath=$3
174+
shift
175+
shift
176+
;;
155177
-t | --tty)
156178
echo "the '$1' flag is deprecated, a tty is now detected automatically" >&2
157179
;;
@@ -387,6 +409,39 @@ importFacts() {
387409
done
388410
}
389411

412+
generateHardwareConfig() {
413+
local maybeSudo="$maybeSudo"
414+
case "$hardwareConfigBackend" in
415+
nixos-facter)
416+
if [[ ${isInstaller} == "y" ]]; then
417+
if [[ ${hasNixOSFacter} == "n" ]]; then
418+
abort "nixos-facter is not available in booted installer. You may want to boot an installer image from here instead: https://github.com/nix-community/nixos-images"
419+
fi
420+
else
421+
maybeSudo=""
422+
fi
423+
424+
step "Generating hardware-configuration.nix using nixos-facter"
425+
# FIXME: if we take the output directly it adds some weird characters at the beginning
426+
runSsh -o ConnectTimeout=10 ${maybeSudo} "nixos-facter" >"$hardwareConfigPath"
427+
;;
428+
nixos-generate-config)
429+
step "Generating hardware-configuration.nix using nixos-generate-config"
430+
runSsh -o ConnectTimeout=10 nixos-generate-config --show-hardware-config >"$hardwareConfigPath"
431+
;;
432+
*)
433+
abort "Unknown hardware config backend: $hardwareConfigBackend"
434+
;;
435+
esac
436+
437+
# to make sure nix knows about the new file
438+
if command -v git >/dev/null; then
439+
pushd "$(dirname "$hardwareConfigPath")"
440+
git add --intent-to-add --force -- "$hardwareConfigPath" >/dev/null 2>&1 || true
441+
popd
442+
fi
443+
}
444+
390445
runKexec() {
391446
if [[ ${isKexec} == "y" ]] || [[ ${isInstaller} == "y" ]]; then
392447
return
@@ -546,7 +601,7 @@ main() {
546601

547602
# parse flake nixos-install style syntax, get the system attr
548603
if [[ -n ${flake} ]]; then
549-
if [[ ${buildOnRemote} == "n" ]]; then
604+
if [[ ${buildOnRemote} == "n" ]] && [[ ${hardwareConfigBackend} == "none" ]]; then
550605
diskoScript=$(nixBuild "${flake}#nixosConfigurations.\"${flakeAttr}\".config.system.build.diskoScript")
551606
nixosSystem=$(nixBuild "${flake}#nixosConfigurations.\"${flakeAttr}\".config.system.build.toplevel")
552607
fi
@@ -598,9 +653,18 @@ main() {
598653
runKexec
599654
fi
600655

656+
if [[ ${hardwareConfigBackend} != "none" ]]; then
657+
generateHardwareConfig
658+
fi
659+
660+
if [[ ${buildOnRemote} == "n" ]] && [[ -n ${flake} ]] && [[ ${hardwareConfigBackend} != "none" ]]; then
661+
diskoScript=$(nixBuild "${flake}#nixosConfigurations.\"${flakeAttr}\".config.system.build.diskoScript")
662+
nixosSystem=$(nixBuild "${flake}#nixosConfigurations.\"${flakeAttr}\".config.system.build.toplevel")
663+
fi
664+
601665
# Installation will fail if non-root user is used for installer.
602666
# Switch to root user by copying authorized_keys.
603-
if [[ ${isInstaller-n} == "y" ]] && [[ ${sshUser} != "root" ]]; then
667+
if [[ ${isInstaller} == "y" ]] && [[ ${sshUser} != "root" ]]; then
604668
# Allow copy to fail if authorized_keys does not exist, like if using /etc/ssh/authorized_keys.d/
605669
runSsh "${maybeSudo} mkdir -p /root/.ssh; ${maybeSudo} cp ~/.ssh/authorized_keys /root/.ssh || true"
606670
sshConnection="root@${sshHost}"

tests/flake-module.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from-nixos-stable = import ./from-nixos.nix testInputsStable;
1919
from-nixos-with-sudo = import ./from-nixos-with-sudo.nix testInputsUnstable;
2020
from-nixos-with-sudo-stable = import ./from-nixos-with-sudo.nix testInputsStable;
21-
21+
from-nixos-with-generated-config = import ./from-nixos-generate-config.nix testInputsUnstable;
2222
from-nixos-build-on-remote = import ./from-nixos-build-on-remote.nix testInputsUnstable;
2323
});
2424
}

tests/from-nixos-generate-config.nix

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
(import ./lib/test-base.nix) {
2+
name = "from-nixos-generate-config";
3+
nodes = {
4+
installer = { pkgs, ... }: {
5+
imports = [
6+
./modules/installer.nix
7+
];
8+
environment.systemPackages = [ pkgs.jq ];
9+
};
10+
installed = {
11+
services.openssh.enable = true;
12+
virtualisation.memorySize = 1024;
13+
14+
users.users.root.openssh.authorizedKeys.keyFiles = [ ./modules/ssh-keys/ssh.pub ];
15+
};
16+
};
17+
testScript = ''
18+
start_all()
19+
installer.succeed("echo super-secret > /tmp/disk-1.key")
20+
output = installer.succeed("""
21+
nixos-anywhere \
22+
-i /root/.ssh/install_key \
23+
--debug \
24+
--kexec /etc/nixos-anywhere/kexec-installer \
25+
--disk-encryption-keys /tmp/disk-1.key /tmp/disk-1.key \
26+
--disk-encryption-keys /tmp/disk-2.key <(echo another-secret) \
27+
--phases kexec,disko \
28+
--generate-hardware-config nixos-generate-config /tmp/config.nix \
29+
--store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
30+
root@installed >&2
31+
echo "disk-1.key: '$(ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
32+
root@installed cat /tmp/disk-1.key)'"
33+
echo "disk-2.key: '$(ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
34+
root@installed cat /tmp/disk-2.key)'"
35+
""")
36+
37+
installer.succeed("cat /tmp/config.nix >&2")
38+
installer.succeed("nix-instantiate --parse /tmp/config.nix")
39+
40+
assert "disk-1.key: 'super-secret'" in output, f"output does not contain expected values: {output}"
41+
assert "disk-2.key: 'another-secret'" in output, f"output does not contain expected values: {output}"
42+
43+
output = installer.succeed("""
44+
nixos-anywhere \
45+
-i /root/.ssh/install_key \
46+
--debug \
47+
--kexec /etc/nixos-anywhere/kexec-installer \
48+
--disk-encryption-keys /tmp/disk-1.key /tmp/disk-1.key \
49+
--disk-encryption-keys /tmp/disk-2.key <(echo another-secret) \
50+
--phases kexec,disko \
51+
--generate-hardware-config nixos-facter /tmp/config.json \
52+
--store-paths /etc/nixos-anywhere/disko /etc/nixos-anywhere/system-to-install \
53+
installed >&2
54+
echo "disk-1.key: '$(ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
55+
root@installed cat /tmp/disk-1.key)'"
56+
echo "disk-2.key: '$(ssh -i /root/.ssh/install_key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
57+
root@installed cat /tmp/disk-2.key)'"
58+
""")
59+
60+
installer.succeed("cat /tmp/config.json >&2")
61+
installer.succeed("jq < /tmp/config.json")
62+
63+
assert "disk-1.key: 'super-secret'" in output, f"output does not contain expected values: {output}"
64+
assert "disk-2.key: 'another-secret'" in output, f"output does not contain expected values: {output}"
65+
'';
66+
}

0 commit comments

Comments
 (0)