diff --git a/flake.nix b/flake.nix index 8d005ef24..fca1afd18 100644 --- a/flake.nix +++ b/flake.nix @@ -184,6 +184,7 @@ ./targets/flake-module.nix ./hydrajobs/flake-module.nix ./templates/flake-module.nix + ./tests/flake-module.nix ]; flake.lib = lib; diff --git a/packages/installer/default.nix b/packages/installer/default.nix index 18fe555d6..35835ca9d 100644 --- a/packages/installer/default.nix +++ b/packages/installer/default.nix @@ -4,6 +4,7 @@ coreutils, util-linux, hwinfo, + ncurses, writeShellApplication, zstd, }: @@ -14,6 +15,7 @@ writeShellApplication { util-linux zstd hwinfo + ncurses # Needed for `clear` command ]; text = builtins.readFile ./ghaf-installer.sh; } diff --git a/packages/installer/ghaf-installer.sh b/packages/installer/ghaf-installer.sh index 4b5f693c0..2a4c92f90 100755 --- a/packages/installer/ghaf-installer.sh +++ b/packages/installer/ghaf-installer.sh @@ -33,7 +33,8 @@ while getopts "w" opt; do esac done -clear +# Fails when TERM=`dumb`. +clear || true cat <<"EOF" ,----.. ,---, diff --git a/tests/flake-module.nix b/tests/flake-module.nix new file mode 100644 index 000000000..89dac0d16 --- /dev/null +++ b/tests/flake-module.nix @@ -0,0 +1,18 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +{ self, ... }: +{ + flake.checks = + let + pkgsPerSystem = system: self.inputs.nixpkgs.legacyPackages.${system}; + in + { + x86_64-linux = + let + pkgs = pkgsPerSystem "x86_64-linux"; + in + { + installer = pkgs.callPackage ./installer { inherit self; }; + }; + }; +} diff --git a/tests/installer/default.nix b/tests/installer/default.nix new file mode 100644 index 000000000..e728d6251 --- /dev/null +++ b/tests/installer/default.nix @@ -0,0 +1,101 @@ +# Copyright 2022-2024 TII (SSRC) and the Ghaf contributors +# SPDX-License-Identifier: Apache-2.0 +# +# This is a main test for ghaf. If you want to debug it interactively +# you can follow this guide https://blog.thalheim.io/2023/01/08/how-to-execute-nixos-tests-interactively-for-debugging/. +# In other words, call breakpoint() at the point of testScript +# that you interested in to debug. After that you can build test driver using +# `nix build .#checks.x86_64-linux.installer.driver` and then run driver +# `./result/bin/nixos-test-driver`. Wait until the moment logs stop appear, +# after that you can execute `interact` and finally `new_machine.shell_interact()`. +# This will allow you to interact with installed ghaf. +{ pkgs, self }: +let + testConfig = "lenovo-x1-carbon-gen11-debug"; + system = "x86_64-linux"; + expectedHostname = "ghaf-host"; + + cfg = self.nixosConfigurations.${testConfig}; + + testingConfig = cfg.extendModules { + modules = [ + (cfg._module.specialArgs.modulesPath + "/testing/test-instrumentation.nix") + (cfg._module.specialArgs.modulesPath + "/profiles/qemu-guest.nix") + (_: { + testing.initrdBackdoor = true; + services.openssh.enable = true; + # https://github.com/nix-community/disko/blob/e55f9a8678adc02024a4877c2a403e3f6daf24fe/lib/interactive-vm.nix#L63 + boot.zfs.devNodes = "/dev/disk/by-uuid"; # needed because /dev/disk/by-id is empty in qemu-vms + boot.zfs.forceImportAll = true; + }) + ]; + }; + + # FIXME: Only one attribute supported. What about ISO? + imagePath = testingConfig.config.system.build.diskoImages + "/disk1.raw.zst"; + targetPath = "/dev/vdb"; + installScript = pkgs.callPackage ../../packages/installer { }; + installerInput = pkgs.lib.strings.escapeNixString "${targetPath}\ny\ny\n"; +in +pkgs.nixosTest { + name = "installer-test"; + nodes.machine = { + virtualisation.emptyDiskImages = [ (1024 * 256) ]; + virtualisation.memorySize = 1024 * 16; + + environment.sessionVariables = { + IMG_PATH = imagePath; + }; + + environment.systemPackages = [ + installScript + self.packages.x86_64-linux.hardware-scan + ]; + }; + + testScript = '' + def create_test_machine( + oldmachine=None, **kwargs + ): # taken from + # TODO: tpm2-abrmd.service fails to start. https://qemu-project.gitlab.io/qemu/specs/tpm.html + start_command = [ + "${pkgs.qemu_test}/bin/qemu-kvm", + "-cpu", + "max", + "-m", + "16384", + "-virtfs", + "local,path=/nix/store,security_model=none,mount_tag=nix-store", + "-drive", + f"file={oldmachine.state_dir}/empty0.qcow2,id=drive1,if=none,index=1,werror=report", + "-device", + "virtio-blk-pci,drive=drive1", + # UEFI support + "-drive", + "if=pflash,format=raw,unit=0,readonly=on,file=${pkgs.OVMF.firmware}", + "-drive", + "if=pflash,format=raw,unit=1,readonly=on,file=${pkgs.OVMF.variables}" + ] + machine = create_machine(start_command=" ".join(start_command), **kwargs) + driver.machines.append(machine) + return machine + + machine.succeed("lsblk >&2") + print(machine.succeed("tty")) + machine.succeed('printf ${installerInput} | ghaf-installer', timeout=3600) + print("Shutting installer image machine down") + machine.shutdown() + + new_machine = create_test_machine(oldmachine=machine, name="after_install") + new_machine.start() + new_machine.switch_root() # Check documentation of options.testing.initrdBackdoor to understand why we need this. + new_machine.succeed("lsblk >&2") + print(new_machine.succeed("tty")) + name = new_machine.succeed("cat /proc/sys/kernel/hostname").strip() + assert name == "${expectedHostname}", f"expected hostname '${expectedHostname}', got {name}" + new_machine.shutdown() + ''; +} +// { + inherit testingConfig; +}