diff --git a/nixos/doc/manual/release-notes/rl-2311.section.md b/nixos/doc/manual/release-notes/rl-2311.section.md index 623576ce4ff2..13b009e76be1 100644 --- a/nixos/doc/manual/release-notes/rl-2311.section.md +++ b/nixos/doc/manual/release-notes/rl-2311.section.md @@ -6,6 +6,8 @@ - Support for WiFi6 (IEEE 802.11ax) and WPA3-SAE-PK was enabled in the `hostapd` package, along with a significant rework of the hostapd module. +- LXD now supports virtual machine instances to complement the existing container support + ## New Services {#sec-release-23.11-new-services} - [MCHPRS](https://github.com/MCHPR/MCHPRS), a multithreaded Minecraft server built for redstone. Available as [services.mchprs](#opt-services.mchprs.enable). diff --git a/nixos/maintainers/scripts/lxd/lxd-container-image-inner.nix b/nixos/maintainers/scripts/lxd/lxd-container-image-inner.nix new file mode 100644 index 000000000000..7b743d170bc6 --- /dev/null +++ b/nixos/maintainers/scripts/lxd/lxd-container-image-inner.nix @@ -0,0 +1,20 @@ +# Edit this configuration file to define what should be installed on +# your system. Help is available in the configuration.nix(5) man page +# and in the NixOS manual (accessible by running ‘nixos-help’). + +{ config, pkgs, lib, ... }: + +{ + imports = + [ + # Include the default lxd configuration. + ../../../modules/virtualisation/lxc-container.nix + # Include the container-specific autogenerated configuration. + ./lxd.nix + ]; + + networking.useDHCP = false; + networking.interfaces.eth0.useDHCP = true; + + system.stateVersion = "21.05"; # Did you read the comment? +} diff --git a/nixos/maintainers/scripts/lxd/lxd-image.nix b/nixos/maintainers/scripts/lxd/lxd-container-image.nix similarity index 64% rename from nixos/maintainers/scripts/lxd/lxd-image.nix rename to nixos/maintainers/scripts/lxd/lxd-container-image.nix index 07605c5c3120..3bd1320b2b68 100644 --- a/nixos/maintainers/scripts/lxd/lxd-image.nix +++ b/nixos/maintainers/scripts/lxd/lxd-container-image.nix @@ -1,4 +1,4 @@ -{ lib, config, pkgs, ... }: +{ lib, pkgs, ... }: { imports = [ @@ -16,8 +16,8 @@ system.activationScripts.config = '' if [ ! -e /etc/nixos/configuration.nix ]; then mkdir -p /etc/nixos - cat ${./lxd-image-inner.nix} > /etc/nixos/configuration.nix - sed 's|../../../modules/virtualisation/lxc-container.nix||g' -i /etc/nixos/configuration.nix + cat ${./lxd-container-image-inner.nix} > /etc/nixos/configuration.nix + ${lib.getExe pkgs.gnused} 's|../../../modules/virtualisation/lxc-container.nix||g' -i /etc/nixos/configuration.nix fi ''; diff --git a/nixos/maintainers/scripts/lxd/lxd-image-inner.nix b/nixos/maintainers/scripts/lxd/lxd-image-inner.nix deleted file mode 100644 index c1a9b1aacd18..000000000000 --- a/nixos/maintainers/scripts/lxd/lxd-image-inner.nix +++ /dev/null @@ -1,95 +0,0 @@ -# Edit this configuration file to define what should be installed on -# your system. Help is available in the configuration.nix(5) man page -# and in the NixOS manual (accessible by running ‘nixos-help’). - -{ config, pkgs, lib, ... }: - -{ - imports = - [ # Include the default lxd configuration. - ../../../modules/virtualisation/lxc-container.nix - # Include the container-specific autogenerated configuration. - ./lxd.nix - ]; - - # networking.hostName = mkForce "nixos"; # Overwrite the hostname. - # networking.wireless.enable = true; # Enables wireless support via wpa_supplicant. - - # Set your time zone. - # time.timeZone = "Europe/Amsterdam"; - - # The global useDHCP flag is deprecated, therefore explicitly set to false here. - # Per-interface useDHCP will be mandatory in the future, so this generated config - # replicates the default behaviour. - networking.useDHCP = false; - networking.interfaces.eth0.useDHCP = true; - - # Configure network proxy if necessary - # networking.proxy.default = "http://user:password@proxy:port/"; - # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain"; - - # Select internationalisation properties. - # i18n.defaultLocale = "en_US.UTF-8"; - # console = { - # font = "Lat2-Terminus16"; - # keyMap = "us"; - # }; - - # Enable the X11 windowing system. - # services.xserver.enable = true; - - # Configure keymap in X11 - # services.xserver.layout = "us"; - # services.xserver.xkbOptions = "eurosign:e"; - - # Enable CUPS to print documents. - # services.printing.enable = true; - - # Enable sound. - # sound.enable = true; - # hardware.pulseaudio.enable = true; - - # Enable touchpad support (enabled default in most desktopManager). - # services.xserver.libinput.enable = true; - - # Define a user account. Don't forget to set a password with ‘passwd’. - # users.users.alice = { - # isNormalUser = true; - # extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user. - # }; - - # List packages installed in system profile. To search, run: - # $ nix search wget - # environment.systemPackages = with pkgs; [ - # vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default. - # wget - # firefox - # ]; - - # Some programs need SUID wrappers, can be configured further or are - # started in user sessions. - # programs.mtr.enable = true; - # programs.gnupg.agent = { - # enable = true; - # enableSSHSupport = true; - # }; - - # List services that you want to enable: - - # Enable the OpenSSH daemon. - # services.openssh.enable = true; - - # Open ports in the firewall. - # networking.firewall.allowedTCPPorts = [ ... ]; - # networking.firewall.allowedUDPPorts = [ ... ]; - # Or disable the firewall altogether. - # networking.firewall.enable = false; - - # This value determines the NixOS release from which the default - # settings for stateful data, like file locations and database versions - # on your system were taken. It’s perfectly fine and recommended to leave - # this value at the release version of the first install of this system. - # Before changing this value read the documentation for this option - # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). - system.stateVersion = "21.05"; # Did you read the comment? -} diff --git a/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image-inner.nix b/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image-inner.nix new file mode 100644 index 000000000000..a8f2c63ac5c6 --- /dev/null +++ b/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image-inner.nix @@ -0,0 +1,20 @@ +# Edit this configuration file to define what should be installed on +# your system. Help is available in the configuration.nix(5) man page +# and in the NixOS manual (accessible by running ‘nixos-help’). + +{ config, pkgs, lib, ... }: + +{ + imports = + [ + # Include the default lxd configuration. + ../../../modules/virtualisation/lxd-virtual-machine.nix + # Include the container-specific autogenerated configuration. + ./lxd.nix + ]; + + networking.useDHCP = false; + networking.interfaces.eth0.useDHCP = true; + + system.stateVersion = "23.05"; # Did you read the comment? +} diff --git a/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix b/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix new file mode 100644 index 000000000000..eb0d9217d402 --- /dev/null +++ b/nixos/maintainers/scripts/lxd/lxd-virtual-machine-image.nix @@ -0,0 +1,27 @@ +{ lib, pkgs, ... }: + +{ + imports = [ + ../../../modules/virtualisation/lxd-virtual-machine.nix + ]; + + virtualisation.lxc.templates.nix = { + enable = true; + target = "/etc/nixos/lxd.nix"; + template = ./nix.tpl; + when = ["create" "copy"]; + }; + + # copy the config for nixos-rebuild + system.activationScripts.config = '' + if [ ! -e /etc/nixos/configuration.nix ]; then + mkdir -p /etc/nixos + cat ${./lxd-virtual-machine-image-inner.nix} > /etc/nixos/configuration.nix + ${lib.getExe pkgs.gnused} 's|../../../modules/virtualisation/lxd-virtual-machine.nix||g' -i /etc/nixos/configuration.nix + fi + ''; + + # Network + networking.useDHCP = false; + networking.interfaces.enp5s0.useDHCP = true; +} diff --git a/nixos/modules/virtualisation/lxc-container.nix b/nixos/modules/virtualisation/lxc-container.nix index 55b285b69147..9402d3bf37d0 100644 --- a/nixos/modules/virtualisation/lxc-container.nix +++ b/nixos/modules/virtualisation/lxc-container.nix @@ -1,96 +1,16 @@ { lib, config, pkgs, ... }: -with lib; - let - templateSubmodule = { ... }: { - options = { - enable = mkEnableOption (lib.mdDoc "this template"); - - target = mkOption { - description = lib.mdDoc "Path in the container"; - type = types.path; - }; - template = mkOption { - description = lib.mdDoc ".tpl file for rendering the target"; - type = types.path; - }; - when = mkOption { - description = lib.mdDoc "Events which trigger a rewrite (create, copy)"; - type = types.listOf (types.str); - }; - properties = mkOption { - description = lib.mdDoc "Additional properties"; - type = types.attrs; - default = {}; - }; - }; - }; - - toYAML = name: data: pkgs.writeText name (generators.toYAML {} data); - cfg = config.virtualisation.lxc; - templates = if cfg.templates != {} then let - list = mapAttrsToList (name: value: { inherit name; } // value) - (filterAttrs (name: value: value.enable) cfg.templates); - in - { - files = map (tpl: { - source = tpl.template; - target = "/templates/${tpl.name}.tpl"; - }) list; - properties = listToAttrs (map (tpl: nameValuePair tpl.target { - when = tpl.when; - template = "${tpl.name}.tpl"; - properties = tpl.properties; - }) list); - } - else { files = []; properties = {}; }; - -in -{ +in { imports = [ - ../installer/cd-dvd/channel.nix - ../profiles/clone-config.nix - ../profiles/minimal.nix + ./lxc-instance-common.nix ]; options = { virtualisation.lxc = { - templates = mkOption { - description = lib.mdDoc "Templates for LXD"; - type = types.attrsOf (types.submodule (templateSubmodule)); - default = {}; - example = literalExpression '' - { - # create /etc/hostname on container creation. also requires networking.hostName = "" to be set - "hostname" = { - enable = true; - target = "/etc/hostname"; - template = builtins.toFile "hostname.tpl" "{{ container.name }}"; - when = [ "create" ]; - }; - # create /etc/nixos/hostname.nix with a configuration for keeping the hostname applied - "hostname-nix" = { - enable = true; - target = "/etc/nixos/hostname.nix"; - template = builtins.toFile "hostname-nix.tpl" "{ ... }: { networking.hostName = \"{{ container.name }}\"; }"; - # copy keeps the file updated when the container is changed - when = [ "create" "copy" ]; - }; - # copy allow the user to specify a custom configuration.nix - "configuration-nix" = { - enable = true; - target = "/etc/nixos/configuration.nix"; - template = builtins.toFile "configuration-nix" "{{ config_get(\"user.user-data\", properties.default) }}"; - when = [ "create" ]; - }; - }; - ''; - }; - - privilegedContainer = mkOption { - type = types.bool; + privilegedContainer = lib.mkOption { + type = lib.types.bool; default = false; description = lib.mdDoc '' Whether this LXC container will be running as a privileged container or not. If set to `true` then @@ -116,24 +36,6 @@ in ${config.nix.package.out}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system ''; - system.build.metadata = pkgs.callPackage ../../lib/make-system-tarball.nix { - contents = [ - { - source = toYAML "metadata.yaml" { - architecture = builtins.elemAt (builtins.match "^([a-z0-9_]+).+" (toString pkgs.system)) 0; - creation_date = 1; - properties = { - description = "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} ${pkgs.system}"; - os = "${config.system.nixos.distroId}"; - release = "${config.system.nixos.codeName}"; - }; - templates = templates.properties; - }; - target = "/metadata.yaml"; - } - ] ++ templates.files; - }; - # TODO: build rootfs as squashfs for faster unpack system.build.tarball = pkgs.callPackage ../../lib/make-system-tarball.nix { extraArgs = "--owner=0"; @@ -180,7 +82,7 @@ in ProtectKernelTunables=no NoNewPrivileges=no LoadCredential= - '' + optionalString cfg.privilegedContainer '' + '' + lib.optionalString cfg.privilegedContainer '' # Additional settings for privileged containers ProtectHome=no ProtectSystem=no @@ -193,28 +95,8 @@ in }) ]; - # Allow the user to login as root without password. - users.users.root.initialHashedPassword = mkOverride 150 ""; - - system.activationScripts.installInitScript = mkForce '' + system.activationScripts.installInitScript = lib.mkForce '' ln -fs $systemConfig/init /sbin/init ''; - - # Some more help text. - services.getty.helpLine = - '' - - Log in as "root" with an empty password. - ''; - - # Containers should be light-weight, so start sshd on demand. - services.openssh.enable = mkDefault true; - services.openssh.startWhenNeeded = mkDefault true; - - # As this is intended as a standalone image, undo some of the minimal profile stuff - environment.noXlibs = false; - documentation.enable = true; - documentation.nixos.enable = true; - services.logrotate.enable = true; }; } diff --git a/nixos/modules/virtualisation/lxc-image-metadata.nix b/nixos/modules/virtualisation/lxc-image-metadata.nix new file mode 100644 index 000000000000..2c0568b4c468 --- /dev/null +++ b/nixos/modules/virtualisation/lxc-image-metadata.nix @@ -0,0 +1,104 @@ +{ lib, config, pkgs, ... }: + +let + templateSubmodule = {...}: { + options = { + enable = lib.mkEnableOption "this template"; + + target = lib.mkOption { + description = "Path in the container"; + type = lib.types.path; + }; + template = lib.mkOption { + description = ".tpl file for rendering the target"; + type = lib.types.path; + }; + when = lib.mkOption { + description = "Events which trigger a rewrite (create, copy)"; + type = lib.types.listOf (lib.types.str); + }; + properties = lib.mkOption { + description = "Additional properties"; + type = lib.types.attrs; + default = {}; + }; + }; + }; + + toYAML = name: data: pkgs.writeText name (lib.generators.toYAML {} data); + + cfg = config.virtualisation.lxc; + templates = if cfg.templates != {} then let + list = lib.mapAttrsToList (name: value: { inherit name; } // value) + (lib.filterAttrs (name: value: value.enable) cfg.templates); + in + { + files = map (tpl: { + source = tpl.template; + target = "/templates/${tpl.name}.tpl"; + }) list; + properties = lib.listToAttrs (map (tpl: lib.nameValuePair tpl.target { + when = tpl.when; + template = "${tpl.name}.tpl"; + properties = tpl.properties; + }) list); + } + else { files = []; properties = {}; }; + +in { + options = { + virtualisation.lxc = { + templates = lib.mkOption { + description = "Templates for LXD"; + type = lib.types.attrsOf (lib.types.submodule templateSubmodule); + default = {}; + example = lib.literalExpression '' + { + # create /etc/hostname on container creation + "hostname" = { + enable = true; + target = "/etc/hostname"; + template = builtins.writeFile "hostname.tpl" "{{ container.name }}"; + when = [ "create" ]; + }; + # create /etc/nixos/hostname.nix with a configuration for keeping the hostname applied + "hostname-nix" = { + enable = true; + target = "/etc/nixos/hostname.nix"; + template = builtins.writeFile "hostname-nix.tpl" "{ ... }: { networking.hostName = "{{ container.name }}"; }"; + # copy keeps the file updated when the container is changed + when = [ "create" "copy" ]; + }; + # copy allow the user to specify a custom configuration.nix + "configuration-nix" = { + enable = true; + target = "/etc/nixos/configuration.nix"; + template = builtins.writeFile "configuration-nix" "{{ config_get(\"user.user-data\", properties.default) }}"; + when = [ "create" ]; + }; + }; + ''; + }; + }; + }; + + config = { + system.build.metadata = pkgs.callPackage ../../lib/make-system-tarball.nix { + contents = [ + { + source = toYAML "metadata.yaml" { + architecture = builtins.elemAt (builtins.match "^([a-z0-9_]+).+" (toString pkgs.system)) 0; + creation_date = 1; + properties = { + description = "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} ${pkgs.system}"; + os = "${config.system.nixos.distroId}"; + release = "${config.system.nixos.codeName}"; + }; + templates = templates.properties; + }; + target = "/metadata.yaml"; + } + ] ++ templates.files; + }; + }; +} diff --git a/nixos/modules/virtualisation/lxc-instance-common.nix b/nixos/modules/virtualisation/lxc-instance-common.nix new file mode 100644 index 000000000000..d6a0e05fb1c9 --- /dev/null +++ b/nixos/modules/virtualisation/lxc-instance-common.nix @@ -0,0 +1,30 @@ +{lib, ...}: + +{ + imports = [ + ./lxc-image-metadata.nix + + ../installer/cd-dvd/channel.nix + ../profiles/clone-config.nix + ../profiles/minimal.nix + ]; + + # Allow the user to login as root without password. + users.users.root.initialHashedPassword = lib.mkOverride 150 ""; + + # Some more help text. + services.getty.helpLine = '' + + Log in as "root" with an empty password. + ''; + + # Containers should be light-weight, so start sshd on demand. + services.openssh.enable = lib.mkDefault true; + services.openssh.startWhenNeeded = lib.mkDefault true; + + # As this is intended as a standalone image, undo some of the minimal profile stuff + environment.noXlibs = false; + documentation.enable = true; + documentation.nixos.enable = true; + services.logrotate.enable = true; +} diff --git a/nixos/modules/virtualisation/lxd-virtual-machine.nix b/nixos/modules/virtualisation/lxd-virtual-machine.nix new file mode 100644 index 000000000000..ba729465ec2f --- /dev/null +++ b/nixos/modules/virtualisation/lxd-virtual-machine.nix @@ -0,0 +1,46 @@ +{ config, lib, pkgs, ... }: + +let + serialDevice = + if pkgs.stdenv.hostPlatform.isx86 + then "ttyS0" + else "ttyAMA0"; # aarch64 +in { + imports = [ + ./lxc-instance-common.nix + + ../profiles/qemu-guest.nix + ]; + + config = { + system.build.qemuImage = import ../../lib/make-disk-image.nix { + inherit pkgs lib config; + + partitionTableType = "efi"; + format = "qcow2-compressed"; + copyChannel = true; + }; + + fileSystems = { + "/" = { + device = "/dev/disk/by-label/nixos"; + autoResize = true; + fsType = "ext4"; + }; + "/boot" = { + device = "/dev/disk/by-label/ESP"; + fsType = "vfat"; + }; + }; + + boot.growPartition = true; + boot.loader.systemd-boot.enable = true; + + # image building needs to know what device to install bootloader on + boot.loader.grub.device = "/dev/vda"; + + boot.kernelParams = ["console=tty1" "console=${serialDevice}"]; + + virtualisation.lxd.agent.enable = lib.mkDefault true; + }; +} diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix index e22ba9a0ae2c..07c5e550ec58 100644 --- a/nixos/modules/virtualisation/lxd.nix +++ b/nixos/modules/virtualisation/lxd.nix @@ -196,7 +196,7 @@ in { "kernel.keys.maxkeys" = 2000; }; - boot.kernelModules = [ "veth" "xt_comment" "xt_CHECKSUM" "xt_MASQUERADE" ] + boot.kernelModules = [ "veth" "xt_comment" "xt_CHECKSUM" "xt_MASQUERADE" "vhost_vsock" ] ++ optionals (!config.networking.nftables.enable) [ "iptable_mangle" ]; }; } diff --git a/nixos/release.nix b/nixos/release.nix index 6da6faab73be..356fcbbd3b9b 100644 --- a/nixos/release.nix +++ b/nixos/release.nix @@ -310,7 +310,7 @@ in rec { ); # An image that can be imported into lxd and used for container creation - lxdImage = forMatchingSystems [ "x86_64-linux" "aarch64-linux" ] (system: + lxdContainerImage = forMatchingSystems [ "x86_64-linux" "aarch64-linux" ] (system: with import ./.. { inherit system; }; @@ -319,14 +319,14 @@ in rec { modules = [ configuration versionModule - ./maintainers/scripts/lxd/lxd-image.nix + ./maintainers/scripts/lxd/lxd-container-image.nix ]; }).config.system.build.tarball) ); # Metadata for the lxd image - lxdMeta = forMatchingSystems [ "x86_64-linux" "aarch64-linux" ] (system: + lxdContainerMeta = forMatchingSystems [ "x86_64-linux" "aarch64-linux" ] (system: with import ./.. { inherit system; }; @@ -335,7 +335,39 @@ in rec { modules = [ configuration versionModule - ./maintainers/scripts/lxd/lxd-image.nix + ./maintainers/scripts/lxd/lxd-container-image.nix + ]; + }).config.system.build.metadata) + + ); + + # An image that can be imported into lxd and used for container creation + lxdVirtualMachineImage = forMatchingSystems [ "x86_64-linux" "aarch64-linux" ] (system: + + with import ./.. { inherit system; }; + + hydraJob ((import lib/eval-config.nix { + inherit system; + modules = + [ configuration + versionModule + ./maintainers/scripts/lxd/lxd-virtual-machine-image.nix + ]; + }).config.system.build.qemuImage) + + ); + + # Metadata for the lxd image + lxdVirtualMachineImageMeta = forMatchingSystems [ "x86_64-linux" "aarch64-linux" ] (system: + + with import ./.. { inherit system; }; + + hydraJob ((import lib/eval-config.nix { + inherit system; + modules = + [ configuration + versionModule + ./maintainers/scripts/lxd/lxd-virtual-machine-image.nix ]; }).config.system.build.metadata) diff --git a/nixos/tests/lxd/container.nix b/nixos/tests/lxd/container.nix index 9e56f6e41e05..a2b61b78f7d6 100644 --- a/nixos/tests/lxd/container.nix +++ b/nixos/tests/lxd/container.nix @@ -1,7 +1,7 @@ import ../make-test-python.nix ({ pkgs, lib, ... } : let - lxd-image = import ../../release.nix { + releases = import ../../release.nix { configuration = { # Building documentation makes the test unnecessarily take a longer time: documentation.enable = lib.mkForce false; @@ -11,14 +11,14 @@ let }; }; - lxd-image-metadata = lxd-image.lxdMeta.${pkgs.stdenv.hostPlatform.system}; - lxd-image-rootfs = lxd-image.lxdImage.${pkgs.stdenv.hostPlatform.system}; + lxd-image-metadata = releases.lxdContainerMeta.${pkgs.stdenv.hostPlatform.system}; + lxd-image-rootfs = releases.lxdContainerImage.${pkgs.stdenv.hostPlatform.system}; in { - name = "lxd"; + name = "lxd-container"; meta = with pkgs.lib.maintainers; { - maintainers = [ patryk27 ]; + maintainers = [ patryk27 adamcstephens ]; }; nodes.machine = { lib, ... }: { diff --git a/nixos/tests/lxd/default.nix b/nixos/tests/lxd/default.nix index 2e34907d7936..8ca591211a06 100644 --- a/nixos/tests/lxd/default.nix +++ b/nixos/tests/lxd/default.nix @@ -6,4 +6,5 @@ container = import ./container.nix {inherit system pkgs;}; nftables = import ./nftables.nix {inherit system pkgs;}; ui = import ./ui.nix {inherit system pkgs;}; + virtual-machine = import ./virtual-machine.nix { inherit system pkgs; }; } diff --git a/nixos/tests/lxd/virtual-machine.nix b/nixos/tests/lxd/virtual-machine.nix new file mode 100644 index 000000000000..93705e9350c5 --- /dev/null +++ b/nixos/tests/lxd/virtual-machine.nix @@ -0,0 +1,64 @@ +import ../make-test-python.nix ({ pkgs, lib, ... }: + +let + releases = import ../../release.nix { + configuration = { + # Building documentation makes the test unnecessarily take a longer time: + documentation.enable = lib.mkForce false; + + # Our tests require `grep` & friends: + environment.systemPackages = with pkgs; [busybox]; + }; + }; + + lxd-image-metadata = releases.lxdVirtualMachineImageMeta.${pkgs.stdenv.hostPlatform.system}; + lxd-image-disk = releases.lxdVirtualMachineImage.${pkgs.stdenv.hostPlatform.system}; + + instance-name = "instance1"; +in { + name = "lxd-virtual-machine"; + + meta = with pkgs.lib.maintainers; { + maintainers = [adamcstephens]; + }; + + nodes.machine = {lib, ...}: { + virtualisation = { + diskSize = 4096; + + cores = 2; + + # Ensure we have enough memory for the nested virtual machine + memorySize = 1024; + + lxc.lxcfs.enable = true; + lxd.enable = true; + }; + }; + + testScript = '' + def instance_is_up(_) -> bool: + status, _ = machine.execute("lxc exec ${instance-name} --disable-stdin --force-interactive /run/current-system/sw/bin/true") + return status == 0 + + machine.wait_for_unit("sockets.target") + machine.wait_for_unit("lxd.service") + machine.wait_for_file("/var/lib/lxd/unix.socket") + + # Wait for lxd to settle + machine.succeed("lxd waitready") + + machine.succeed("lxd init --minimal") + + with subtest("virtual-machine image can be imported"): + machine.succeed("lxc image import ${lxd-image-metadata}/*/*.tar.xz ${lxd-image-disk}/nixos.qcow2 --alias nixos") + + with subtest("virtual-machine can be launched and become available"): + machine.succeed("lxc launch nixos ${instance-name} --vm --config limits.memory=512MB --config security.secureboot=false") + with machine.nested("Waiting for instance to start and be usable"): + retry(instance_is_up) + + with subtest("lxd-agent is started"): + machine.succeed("lxc exec ${instance-name} systemctl is-active lxd-agent") + ''; +})