Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nixos/qemu-vm: Allow building a Nix store image instead of relying on 9p + more #140792

Merged
merged 11 commits into from
Oct 28, 2021
27 changes: 20 additions & 7 deletions nixos/doc/manual/from_md/release-notes/rl-2111.section.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,13 @@ Superuser created successfully.
dropped because it was EOLed by upstream in 2021-10.
</para>
</listitem>
<listitem>
<para>
The <literal>virtualisation.pathsInNixDB</literal> option was
renamed
<link xlink:href="options.html#opt-virtualisation.additionalPaths"><literal>virtualisation.additionalPaths</literal></link>.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="sec-release-21.11-notable-changes">
Expand All @@ -1199,25 +1206,31 @@ Superuser created successfully.
<para>
In NixOS virtual machines (QEMU), the
<literal>virtualisation</literal> module has been updated with
new options to configure:
new options:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
IPv4 port forwarding
(<link xlink:href="options.html#opt-virtualisation.forwardPorts"><literal>virtualisation.forwardPorts</literal></link>),
<link xlink:href="options.html#opt-virtualisation.forwardPorts"><literal>forwardPorts</literal></link>
to configure IPv4 port forwarding,
</para>
</listitem>
<listitem>
<para>
<link xlink:href="options.html#opt-virtualisation.sharedDirectories"><literal>sharedDirectories</literal></link>
to set up shared host directories,
</para>
</listitem>
<listitem>
<para>
shared host directories
(<link xlink:href="options.html#opt-virtualisation.sharedDirectories"><literal>virtualisation.sharedDirectories</literal></link>),
<link xlink:href="options.html#opt-virtualisation.resolution"><literal>resolution</literal></link>
to set the screen resolution,
</para>
</listitem>
<listitem>
<para>
screen resolution
(<link xlink:href="options.html#opt-virtualisation.resolution"><literal>virtualisation.resolution</literal></link>).
<link xlink:href="options.html#opt-virtualisation.useNixStoreImage"><literal>useNixStoreImage</literal></link>
to use a disk image for the Nix store instead of 9P.
</para>
</listitem>
</itemizedlist>
Expand Down
12 changes: 8 additions & 4 deletions nixos/doc/manual/release-notes/rl-2111.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,16 +362,20 @@ In addition to numerous new and upgraded packages, this release has the followin

- Nextcloud 20 (`pkgs.nextcloud20`) has been dropped because it was EOLed by upstream in 2021-10.

- The `virtualisation.pathsInNixDB` option was renamed
[`virtualisation.additionalPaths`](options.html#opt-virtualisation.additionalPaths).

## Other Notable Changes {#sec-release-21.11-notable-changes}


- The linux kernel package infrastructure was moved out of `all-packages.nix`, and restructured. Linux related functions and attributes now live under the `pkgs.linuxKernel` attribute set.
In particular the versioned `linuxPackages_*` package sets (such as `linuxPackages_5_4`) and kernels from `pkgs` were moved there and now live under `pkgs.linuxKernel.packages.*`. The unversioned ones (such as `linuxPackages_latest`) remain untouched.

- In NixOS virtual machines (QEMU), the `virtualisation` module has been updated with new options to configure:
- IPv4 port forwarding ([`virtualisation.forwardPorts`](options.html#opt-virtualisation.forwardPorts)),
- shared host directories ([`virtualisation.sharedDirectories`](options.html#opt-virtualisation.sharedDirectories)),
- screen resolution ([`virtualisation.resolution`](options.html#opt-virtualisation.resolution)).
- In NixOS virtual machines (QEMU), the `virtualisation` module has been updated with new options:
- [`forwardPorts`](options.html#opt-virtualisation.forwardPorts) to configure IPv4 port forwarding,
- [`sharedDirectories`](options.html#opt-virtualisation.sharedDirectories) to set up shared host directories,
- [`resolution`](options.html#opt-virtualisation.resolution) to set the screen resolution,
- [`useNixStoreImage`](options.html#opt-virtualisation.useNixStoreImage) to use a disk image for the Nix store instead of 9P.

In addition, the default [`msize`](options.html#opt-virtualisation.msize) parameter in 9P filesystems (including /nix/store and all shared directories) has been increased to 16K for improved performance.

Expand Down
88 changes: 65 additions & 23 deletions nixos/lib/make-disk-image.nix
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,14 @@
# most likely fails as GRUB will probably refuse to install.
partitionTableType ? "legacy"

, # Whether to invoke `switch-to-configuration boot` during image creation
installBootLoader ? true

, # The root file system type.
fsType ? "ext4"

, # Filesystem label
label ? "nixos"
label ? if onlyNixStore then "nix-store" else "nixos"

, # The initial NixOS configuration file to be copied to
# /etc/nixos/configuration.nix.
Expand All @@ -57,10 +60,24 @@
, # Shell code executed after the VM has finished.
postVM ? ""

, # Copy the contents of the Nix store to the root of the image and
# skip further setup. Incompatible with `contents`,
# `installBootLoader` and `configFile`.
onlyNixStore ? false

, name ? "nixos-disk-image"

, # Disk image format, one of qcow2, qcow2-compressed, vdi, vpc, raw.
format ? "raw"

, # Whether a nix channel based on the current source tree should be
# made available inside the image. Useful for interactive use of nix
# utils, but changes the hash of the image when the sources are
# updated.
copyChannel ? true

, # Additional store paths to copy to the image's store.
additionalPaths ? []
}:

assert partitionTableType == "legacy" || partitionTableType == "legacy+gpt" || partitionTableType == "efi" || partitionTableType == "hybrid" || partitionTableType == "none";
Expand All @@ -71,6 +88,7 @@ assert lib.all
(attrs: ((attrs.user or null) == null)
== ((attrs.group or null) == null))
contents;
assert onlyNixStore -> contents == [] && configFile == null && !installBootLoader;

with lib;

Expand Down Expand Up @@ -163,7 +181,14 @@ let format' = format; in let
users = map (x: x.user or "''") contents;
groups = map (x: x.group or "''") contents;

closureInfo = pkgs.closureInfo { rootPaths = [ config.system.build.toplevel channelSources ]; };
basePaths = [ config.system.build.toplevel ]
++ lib.optional copyChannel channelSources;

additionalPaths' = subtractLists basePaths additionalPaths;

closureInfo = pkgs.closureInfo {
rootPaths = basePaths ++ additionalPaths';
};

blockSize = toString (4 * 1024); # ext4fs block size (not block device sector size)

Expand Down Expand Up @@ -251,7 +276,13 @@ let format' = format; in let
chmod 755 "$TMPDIR"
echo "running nixos-install..."
nixos-install --root $root --no-bootloader --no-root-passwd \
--system ${config.system.build.toplevel} --channel ${channelSources} --substituters ""
--system ${config.system.build.toplevel} \
${if copyChannel then "--channel ${channelSources}" else "--no-channel-copy"} \
--substituters ""

${optionalString (additionalPaths' != []) ''
nix copy --to $root --no-check-sigs ${concatStringsSep " " additionalPaths'}
''}

diskImage=nixos.raw

Expand Down Expand Up @@ -320,25 +351,29 @@ let format' = format; in let
''}

echo "copying staging root to image..."
cptofs -p ${optionalString (partitionTableType != "none") "-P ${rootPartition}"} -t ${fsType} -i $diskImage $root/* / ||
cptofs -p ${optionalString (partitionTableType != "none") "-P ${rootPartition}"} \
-t ${fsType} \
-i $diskImage \
$root${optionalString onlyNixStore builtins.storeDir}/* / ||
(echo >&2 "ERROR: cptofs failed. diskSize might be too small for closure."; exit 1)
'';
in pkgs.vmTools.runInLinuxVM (
pkgs.runCommand name
{ preVM = prepareImage;

moveOrConvertImage = ''
${if format == "raw" then ''
mv $diskImage $out/${filename}
'' else ''
${pkgs.qemu}/bin/qemu-img convert -f raw -O ${format} ${compress} $diskImage $out/${filename}
''}
diskImage=$out/${filename}
'';

buildImage = pkgs.vmTools.runInLinuxVM (
pkgs.runCommand name {
preVM = prepareImage;
buildInputs = with pkgs; [ util-linux e2fsprogs dosfstools ];
postVM = ''
${if format == "raw" then ''
mv $diskImage $out/${filename}
'' else ''
${pkgs.qemu}/bin/qemu-img convert -f raw -O ${format} ${compress} $diskImage $out/${filename}
''}
diskImage=$out/${filename}
${postVM}
'';
postVM = moveOrConvertImage + postVM;
memSize = 1024;
}
''
} ''
export PATH=${binPath}:$PATH

rootDisk=${if partitionTableType != "none" then "/dev/vda${rootPartition}" else "/dev/vda"}
Expand Down Expand Up @@ -368,11 +403,13 @@ in pkgs.vmTools.runInLinuxVM (
cp ${configFile} /mnt/etc/nixos/configuration.nix
''}

# Set up core system link, GRUB, etc.
NIXOS_INSTALL_BOOTLOADER=1 nixos-enter --root $mountPoint -- /nix/var/nix/profiles/system/bin/switch-to-configuration boot
${lib.optionalString installBootLoader ''
# Set up core system link, GRUB, etc.
NIXOS_INSTALL_BOOTLOADER=1 nixos-enter --root $mountPoint -- /nix/var/nix/profiles/system/bin/switch-to-configuration boot

# The above scripts will generate a random machine-id and we don't want to bake a single ID into all our images
rm -f $mountPoint/etc/machine-id
# The above scripts will generate a random machine-id and we don't want to bake a single ID into all our images
rm -f $mountPoint/etc/machine-id
''}

# Set the ownerships of the contents. The modes are set in preVM.
# No globbing on targets, so no need to set -f
Expand All @@ -398,4 +435,9 @@ in pkgs.vmTools.runInLinuxVM (
tune2fs -T now -c 0 -i 0 $rootDisk
''}
''
)
);
in
if onlyNixStore then
pkgs.runCommand name {}
(prepareImage + moveOrConvertImage + postVM)
else buildImage
19 changes: 19 additions & 0 deletions nixos/lib/testing-python.nix
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,30 @@ rec {
let
nodes = qemu_pkg:
let
testScript' =
# Call the test script with the computed nodes.
if lib.isFunction testScript
then testScript { nodes = nodes qemu_pkg; }
else testScript;

build-vms = import ./build-vms.nix {
inherit system lib pkgs minimal specialArgs;
extraConfigurations = extraConfigurations ++ [(
{
virtualisation.qemu.package = qemu_pkg;

# Make sure all derivations referenced by the test
# script are available on the nodes. When the store is
# accessed through 9p, this isn't important, since
# everything in the store is available to the guest,
# but when building a root image it is, as all paths
# that should be available to the guest has to be
# copied to the image.
virtualisation.additionalPaths =
lib.optional
(builtins.hasContext testScript')
(pkgs.writeStringReferencesToFile testScript');

# Ensure we do not use aliases. Ideally this is only set
# when the test framework is used by Nixpkgs NixOS tests.
nixpkgs.config.allowAliases = false;
Expand Down
Loading