This commit is contained in:
Qubasa 2024-04-21 21:25:44 +02:00 committed by mergify[bot]
parent 35d4378e74
commit 9f5d4e45cd
12 changed files with 263 additions and 250 deletions

View file

@ -93,8 +93,8 @@ A simple disko configuration may look like this:
```
If you'd saved this configuration in /tmp/disk-config.nix, and wanted to create
a disk named /dev/sda, you would run the following command to partition,
format and mount the disk.
a disk named /dev/sda, you would run the following command to partition, format
and mount the disk.
```console
sudo nix --experimental-features "nix-command flakes" run github:nix-community/disko -- --mode disko /tmp/disk-config.nix

View file

@ -70,8 +70,8 @@ and made a note of its URL.
Your configuration needs to be saved on the new machine for example
as /tmp/disk-config.nix. You can do this using the `curl` command to download
from the url you noted above, using the `-o` option to save the file as
disk-config.nix. Your commands would look like this if you had chosen the
hybrid layout:
disk-config.nix. Your commands would look like this if you had chosen the hybrid
layout:
```console
cd /tmp
@ -182,8 +182,8 @@ of the NixOS manual. The following configuration for `grub` works for both EFI
and BIOS systems. Add this to your configuration.nix, commenting out the
existing lines that configure `systemd-boot`. The entries will look like this:
**Note:** Its not necessary to set `boot.loader.grub.device` here, since Disko will
take care of that automatically.
**Note:** Its not necessary to set `boot.loader.grub.device` here, since Disko
will take care of that automatically.
```nix
# ...

View file

@ -51,9 +51,11 @@ generate disk images:
### Generating the `.raw` VM Image
1. **Create a NixOS configuration that includes the disko and the disk configuration of your choice**
1. **Create a NixOS configuration that includes the disko and the disk
configuration of your choice**
In the this example we create a flake containing a nixos configuration for `myhost`.
In the this example we create a flake containing a nixos configuration for
`myhost`.
```nix
# save this as flake.nix
@ -86,15 +88,15 @@ In the this example we create a flake containing a nixos configuration for `myho
}
```
2. **Build the disko image script:** Replace `myhost` in the command below with your
specific system configuration name:
2. **Build the disko image script:** Replace `myhost` in the command below with
your specific system configuration name:
```console
nix build .#nixosConfigurations.myhost.config.system.build.diskoImagesScript
```
3. **Execute the disko image script:** Execute the generated disko image script. Running
`./result --help` will output the available options:
3. **Execute the disko image script:** Execute the generated disko image script.
Running `./result --help` will output the available options:
```console
./result --help
@ -124,9 +126,10 @@ In the this example we create a flake containing a nixos configuration for `myho
sudo ./result --build-memory 2048
```
The script will generate the actual image outside of the nix store in the current working directory.
The create image names depend on the names used in `disko.devices.disk` attrset in the NixOS configuration.
In our code example it will produce the following image:
The script will generate the actual image outside of the nix store in the
current working directory. The create image names depend on the names used in
`disko.devices.disk` attrset in the NixOS configuration. In our code example it will
produce the following image:
```
$ ls -la vdb.raw
@ -142,8 +145,8 @@ In the this example we create a flake containing a nixos configuration for `myho
```
- If the `.raw` image size is not optimal, use `--write-to-disk` to write
directly to a drive. This bypasses the `.raw` file generation, which saves on read/write operations
and is suitable for single disk setups.
directly to a drive. This bypasses the `.raw` file generation, which saves on
read/write operations and is suitable for single disk setups.
### Understanding the Image Generation Process

View file

@ -28,8 +28,8 @@
in
{
disko = pkgs.callPackage ./package.nix { };
# alias to make `nix run` more convenient
disko-install = self.packages.${system}.disko.overrideAttrs (old: {
# alias to make `nix run` more convenient
disko-install = self.packages.${system}.disko.overrideAttrs (_old: {
name = "disko-install";
});
default = self.packages.${system}.disko;

View file

@ -85,7 +85,7 @@ let
else if match "/dev/mapper/.+" dev != null then
"${dev}${toString index}" # /dev/mapper/vg-lv1 style
else if match "/dev/loop[[:digit:]]+" dev != null
then "${dev}p${toString index}" # /dev/mapper/vg-lv1 style
then "${dev}p${toString index}" # /dev/mapper/vg-lv1 style
else
abort ''
${dev} seems not to be a supported disk format. Please add this to disko in https://github.com/nix-community/disko/blob/master/lib/default.nix
@ -191,10 +191,10 @@ let
isAttrsOfSubmodule = o: o.type.name == "attrsOf" && o.type.nestedTypes.elemType.name == "submodule";
isSerializable = n: o: !(
lib.hasPrefix "_" n
|| lib.hasSuffix "Hook" n
|| isAttrsOfSubmodule o
# TODO don't hardcode diskoLib.subType options.
|| n == "content" || n == "partitions" || n == "datasets" || n == "swap"
|| lib.hasSuffix "Hook" n
|| isAttrsOfSubmodule o
# TODO don't hardcode diskoLib.subType options.
|| n == "content" || n == "partitions" || n == "datasets" || n == "swap"
);
in
lib.toShellVars
@ -245,14 +245,17 @@ let
internal = true;
readOnly = true;
type = diskoLib.jsonType;
default = lib.mapAttrsRecursive (name: value: if builtins.isString value then ''
(
${diskoLib.indent (diskoLib.defineHookVariables { inherit options; })}
${config.preMountHook}
${diskoLib.indent value}
${config.postMountHook}
)
'' else value) attrs.default;
default = lib.mapAttrsRecursive
(_name: value:
if builtins.isString value then ''
(
${diskoLib.indent (diskoLib.defineHookVariables { inherit options; })}
${config.preMountHook}
${diskoLib.indent value}
${config.postMountHook}
)
'' else value)
attrs.default;
description = "Mount script";
};

View file

@ -8,10 +8,10 @@
}:
let
vmTools = pkgs.vmTools.override {
rootModules = ["9p" "9pnet_virtio" "virtio_pci" "virtio_blk"] ++ nixosConfig.config.disko.extraRootModules;
rootModules = [ "9p" "9pnet_virtio" "virtio_pci" "virtio_blk" ] ++ nixosConfig.config.disko.extraRootModules;
kernel = pkgs.aggregateModules
(with nixosConfig.config.boot.kernelPackages; [ kernel ]
++ lib.optional (lib.elem "zfs" nixosConfig.config.disko.extraRootModules) zfs);
(with nixosConfig.config.boot.kernelPackages; [ kernel ]
++ lib.optional (lib.elem "zfs" nixosConfig.config.disko.extraRootModules) zfs);
};
cleanedConfig = diskoLib.testLib.prepareDiskoConfig nixosConfig.config diskoLib.testLib.devices;
systemToInstall = nixosConfig.extendModules {

View file

@ -114,47 +114,49 @@ let
};
installedTopLevel = ((if extendModules != null then extendModules else installed-system-eval.extendModules) {
modules = [({config, ...}: {
imports = [
extraSystemConfig
({ modulesPath, ... }: {
imports = [
(modulesPath + "/testing/test-instrumentation.nix") # we need these 2 modules always to be able to run the tests
(modulesPath + "/profiles/qemu-guest.nix")
];
disko.devices = lib.mkForce testConfigBooted.disko.devices;
})
];
modules = [
({ config, ... }: {
imports = [
extraSystemConfig
({ modulesPath, ... }: {
imports = [
(modulesPath + "/testing/test-instrumentation.nix") # we need these 2 modules always to be able to run the tests
(modulesPath + "/profiles/qemu-guest.nix")
];
disko.devices = lib.mkForce testConfigBooted.disko.devices;
})
];
# since we boot on a different machine, the efi payload needs to be portable
boot.loader.grub.efiInstallAsRemovable = efi;
boot.loader.grub.efiSupport = efi;
boot.loader.systemd-boot.graceful = true;
# since we boot on a different machine, the efi payload needs to be portable
boot.loader.grub.efiInstallAsRemovable = efi;
boot.loader.grub.efiSupport = efi;
boot.loader.systemd-boot.graceful = true;
# we always want the bind-mounted nix store. otherwise tests take forever
fileSystems."/nix/store" = lib.mkForce {
device = "nix-store";
fsType = "9p";
neededForBoot = true;
options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ];
};
boot.zfs.devNodes = "/dev/disk/by-uuid"; # needed because /dev/disk/by-id is empty in qemu-vms
# we always want the bind-mounted nix store. otherwise tests take forever
fileSystems."/nix/store" = lib.mkForce {
device = "nix-store";
fsType = "9p";
neededForBoot = true;
options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ];
};
boot.zfs.devNodes = "/dev/disk/by-uuid"; # needed because /dev/disk/by-id is empty in qemu-vms
# grub will install to these devices, we need to force those or we are offset by 1
# we use mkOveride 70, so that users can override this with mkForce in case they are testing grub mirrored boots
boot.loader.grub.devices = lib.mkOverride 70 testConfigInstall.boot.loader.grub.devices;
# grub will install to these devices, we need to force those or we are offset by 1
# we use mkOveride 70, so that users can override this with mkForce in case they are testing grub mirrored boots
boot.loader.grub.devices = lib.mkOverride 70 testConfigInstall.boot.loader.grub.devices;
assertions = [
{
assertion = builtins.length config.boot.loader.grub.mirroredBoots > 1 -> config.boot.loader.grub.devices == [];
message = ''
When using `--vm-test` in combination with `mirroredBoots`,
it is necessary to configure `boot.loader.grub.devices` as an empty list by setting `boot.loader.grub.devices = lib.mkForce [];`.
This adjustment is crucial because the `--vm-test` mechanism automatically overrides the grub boot devices as part of the virtual machine test.
'';
}
];
})];
assertions = [
{
assertion = builtins.length config.boot.loader.grub.mirroredBoots > 1 -> config.boot.loader.grub.devices == [ ];
message = ''
When using `--vm-test` in combination with `mirroredBoots`,
it is necessary to configure `boot.loader.grub.devices` as an empty list by setting `boot.loader.grub.devices = lib.mkForce [];`.
This adjustment is crucial because the `--vm-test` mechanism automatically overrides the grub boot devices as part of the virtual machine test.
'';
}
];
})
];
}).config.system.build.toplevel;
in

View file

@ -32,9 +32,9 @@ let
lib.concatMapStringsSep
"\n"
(file: ''
if ! test -e "${mountpoint}/${file.path}"; then
btrfs filesystem mkswapfile --size ${file.size} "${mountpoint}/${file.path}"
fi
if ! test -e "${mountpoint}/${file.path}"; then
btrfs filesystem mkswapfile --size ${file.size} "${mountpoint}/${file.path}"
fi
'')
(lib.attrValues swap);

View file

@ -55,15 +55,17 @@ in
};
label = lib.mkOption {
type = lib.types.str;
default = let
# 72 bytes is the maximum length of a GPT partition name
# the labels seem to be in UTF-16, so 2 bytes per character
limit = 36;
label = "${config._parent.type}-${config._parent.name}-${partition.config.name}";
in if (lib.stringLength label) > limit then
builtins.substring 0 limit (builtins.hashString "sha256" label)
else
label;
default =
let
# 72 bytes is the maximum length of a GPT partition name
# the labels seem to be in UTF-16, so 2 bytes per character
limit = 36;
label = "${config._parent.type}-${config._parent.name}-${partition.config.name}";
in
if (lib.stringLength label) > limit then
builtins.substring 0 limit (builtins.hashString "sha256" label)
else
label;
};
size = lib.mkOption {
type = lib.types.either (lib.types.enum [ "100%" ]) (lib.types.strMatching "[0-9]+[KMGTP]?");
@ -76,7 +78,7 @@ in
};
alignment = lib.mkOption {
type = lib.types.int;
default = if (builtins.substring (builtins.stringLength partition.config.start - 1) 1 partition.config.start == "s" || (builtins.substring (builtins.stringLength partition.config.end - 1) 1 partition.config.end == "s" )) then 1 else 0;
default = if (builtins.substring (builtins.stringLength partition.config.start - 1) 1 partition.config.start == "s" || (builtins.substring (builtins.stringLength partition.config.end - 1) 1 partition.config.end == "s")) then 1 else 0;
description = "Alignment of the partition, if sectors are used as start or end it can be aligned to 1";
};
start = lib.mkOption {
@ -95,7 +97,7 @@ in
};
content = diskoLib.partitionType { parent = config; device = partition.config.device; };
hybrid = lib.mkOption {
type = lib.types.nullOr (lib.types.submodule ({name, ...} @ hp: {
type = lib.types.nullOr (lib.types.submodule ({ ... } @ hp: {
options = {
mbrPartitionType = lib.mkOption {
type = lib.types.nullOr lib.types.str;

View file

@ -115,13 +115,14 @@
(lv: [
(lib.optional (lv.content != null) lv.content._config)
(lib.optional (lv.lvm_type != null) {
boot.initrd.kernelModules = [(if lv.lvm_type == "mirror" then "dm-mirror" else "dm-raid")]
boot.initrd.kernelModules = [ (if lv.lvm_type == "mirror" then "dm-mirror" else "dm-raid") ]
++ lib.optional (lv.lvm_type == "raid0") "raid0"
++ lib.optional (lv.lvm_type == "raid1") "raid1"
# ++ lib.optional (lv.lvm_type == "raid10") "raid10"
++ lib.optional (lv.lvm_type == "raid4" ||
lv.lvm_type == "raid5" ||
lv.lvm_type == "raid6") "raid456";
++ lib.optional
(lv.lvm_type == "raid4" ||
lv.lvm_type == "raid5" ||
lv.lvm_type == "raid6") "raid456";
})
])

View file

@ -6,155 +6,156 @@
If you encounter errors similar to:
"error: The option `disko.devices.disk.disk1.content.partitions."[definition 1-entry 1]".content._config` is read-only, but it's set multiple times,"
this is likely due to the use of the legacy table type.
'' {
type = lib.mkOption {
type = lib.types.enum [ "table" ];
internal = true;
description = "Partition table";
};
device = lib.mkOption {
type = lib.types.str;
default = device;
description = "Device to partition";
};
format = lib.mkOption {
type = lib.types.enum [ "gpt" "msdos" ];
default = "gpt";
description = "The kind of partition table";
};
partitions = lib.mkOption {
type = lib.types.listOf (lib.types.submodule ({ name, ... }@partition: {
options = {
part-type = lib.mkOption {
type = lib.types.enum [ "primary" "logical" "extended" ];
default = "primary";
description = "Partition type";
''
{
type = lib.mkOption {
type = lib.types.enum [ "table" ];
internal = true;
description = "Partition table";
};
device = lib.mkOption {
type = lib.types.str;
default = device;
description = "Device to partition";
};
format = lib.mkOption {
type = lib.types.enum [ "gpt" "msdos" ];
default = "gpt";
description = "The kind of partition table";
};
partitions = lib.mkOption {
type = lib.types.listOf (lib.types.submodule ({ name, ... }@partition: {
options = {
part-type = lib.mkOption {
type = lib.types.enum [ "primary" "logical" "extended" ];
default = "primary";
description = "Partition type";
};
fs-type = lib.mkOption {
type = lib.types.nullOr (lib.types.enum [ "btrfs" "ext2" "ext3" "ext4" "fat16" "fat32" "hfs" "hfs+" "linux-swap" "ntfs" "reiserfs" "udf" "xfs" ]);
default = null;
description = "Filesystem type to use";
};
name = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = "Name of the partition";
};
start = lib.mkOption {
type = lib.types.str;
default = "0%";
description = "Start of the partition";
};
end = lib.mkOption {
type = lib.types.str;
default = "100%";
description = "End of the partition";
};
flags = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Partition flags";
};
bootable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to make the partition bootable";
};
content = diskoLib.partitionType { parent = config; device = diskoLib.deviceNumbering config.device partition.config._index; };
_index = lib.mkOption {
internal = true;
default = lib.toInt (lib.head (builtins.match ".*entry ([[:digit:]]+)]" name));
};
};
fs-type = lib.mkOption {
type = lib.types.nullOr (lib.types.enum [ "btrfs" "ext2" "ext3" "ext4" "fat16" "fat32" "hfs" "hfs+" "linux-swap" "ntfs" "reiserfs" "udf" "xfs" ]);
default = null;
description = "Filesystem type to use";
};
name = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = "Name of the partition";
};
start = lib.mkOption {
type = lib.types.str;
default = "0%";
description = "Start of the partition";
};
end = lib.mkOption {
type = lib.types.str;
default = "100%";
description = "End of the partition";
};
flags = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Partition flags";
};
bootable = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to make the partition bootable";
};
content = diskoLib.partitionType { parent = config; device = diskoLib.deviceNumbering config.device partition.config._index; };
_index = lib.mkOption {
internal = true;
default = lib.toInt (lib.head (builtins.match ".*entry ([[:digit:]]+)]" name));
};
};
}));
default = [ ];
description = "List of partitions to add to the partition table";
};
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo diskoLib.jsonType;
default = dev:
lib.foldr lib.recursiveUpdate { } (lib.imap
(_index: partition:
lib.optionalAttrs (partition.content != null) (partition.content._meta dev)
)
config.partitions);
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
default = ''
if ! blkid "${config.device}" >/dev/null; then
parted -s ${config.device} -- mklabel ${config.format}
${lib.concatStrings (map (partition: ''
${lib.optionalString (config.format == "gpt") ''
parted -s ${config.device} -- mkpart ${partition.name} ${diskoLib.maybeStr partition.fs-type} ${partition.start} ${partition.end}
''}
${lib.optionalString (config.format == "msdos") ''
parted -s ${config.device} -- mkpart ${partition.part-type} ${diskoLib.maybeStr partition.fs-type} ${partition.start} ${partition.end}
''}
# ensure /dev/disk/by-path/..-partN exists before continuing
partprobe ${config.device}
udevadm trigger --subsystem-match=block
udevadm settle
${lib.optionalString partition.bootable ''
parted -s ${config.device} -- set ${toString partition._index} boot on
''}
${lib.concatMapStringsSep "" (flag: ''
parted -s ${config.device} -- set ${toString partition._index} ${flag} on
'') partition.flags}
# ensure further operations can detect new partitions
partprobe ${config.device}
udevadm trigger --subsystem-match=block
udevadm settle
'') config.partitions)}
fi
${lib.concatStrings (map (partition: ''
${lib.optionalString (partition.content != null) partition.content._create}
'') config.partitions)}
'';
};
_mount = diskoLib.mkMountOption {
inherit config options;
default =
let
partMounts = lib.foldr lib.recursiveUpdate { } (map
(partition:
lib.optionalAttrs (partition.content != null) partition.content._mount
}));
default = [ ];
description = "List of partitions to add to the partition table";
};
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo diskoLib.jsonType;
default = dev:
lib.foldr lib.recursiveUpdate { } (lib.imap
(_index: partition:
lib.optionalAttrs (partition.content != null) (partition.content._meta dev)
)
config.partitions);
in
{
dev = partMounts.dev or "";
fs = partMounts.fs or { };
};
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
default = ''
if ! blkid "${config.device}" >/dev/null; then
parted -s ${config.device} -- mklabel ${config.format}
${lib.concatStrings (map (partition: ''
${lib.optionalString (config.format == "gpt") ''
parted -s ${config.device} -- mkpart ${partition.name} ${diskoLib.maybeStr partition.fs-type} ${partition.start} ${partition.end}
''}
${lib.optionalString (config.format == "msdos") ''
parted -s ${config.device} -- mkpart ${partition.part-type} ${diskoLib.maybeStr partition.fs-type} ${partition.start} ${partition.end}
''}
# ensure /dev/disk/by-path/..-partN exists before continuing
partprobe ${config.device}
udevadm trigger --subsystem-match=block
udevadm settle
${lib.optionalString partition.bootable ''
parted -s ${config.device} -- set ${toString partition._index} boot on
''}
${lib.concatMapStringsSep "" (flag: ''
parted -s ${config.device} -- set ${toString partition._index} ${flag} on
'') partition.flags}
# ensure further operations can detect new partitions
partprobe ${config.device}
udevadm trigger --subsystem-match=block
udevadm settle
'') config.partitions)}
fi
${lib.concatStrings (map (partition: ''
${lib.optionalString (partition.content != null) partition.content._create}
'') config.partitions)}
'';
};
_mount = diskoLib.mkMountOption {
inherit config options;
default =
let
partMounts = lib.foldr lib.recursiveUpdate { } (map
(partition:
lib.optionalAttrs (partition.content != null) partition.content._mount
)
config.partitions);
in
{
dev = partMounts.dev or "";
fs = partMounts.fs or { };
};
};
_config = lib.mkOption {
internal = true;
readOnly = true;
default =
map
(partition:
lib.optional (partition.content != null) partition.content._config
)
config.partitions;
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = pkgs:
[ pkgs.parted pkgs.systemdMinimal ] ++ lib.flatten (map
(partition:
lib.optional (partition.content != null) (partition.content._pkgs pkgs)
)
config.partitions);
description = "Packages";
};
};
_config = lib.mkOption {
internal = true;
readOnly = true;
default =
map
(partition:
lib.optional (partition.content != null) partition.content._config
)
config.partitions;
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = pkgs:
[ pkgs.parted pkgs.systemdMinimal ] ++ lib.flatten (map
(partition:
lib.optional (partition.content != null) (partition.content._pkgs pkgs)
)
config.partitions);
description = "Packages";
};
};
}

View file

@ -52,19 +52,20 @@
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
# -u prevents mounting newly created datasets, which is
# important to prevent accidental shadowing of mount points
# since (create order != mount order)
# -p creates parents automatically
default = ''
if ! zfs get type ${config._name} >/dev/null 2>&1; then
zfs create -up ${config._name} \
${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "-o ${n}=${v}") config.options)}
fi
'';
} // { readOnly = false; };
_create = diskoLib.mkCreateOption
{
inherit config options;
# -u prevents mounting newly created datasets, which is
# important to prevent accidental shadowing of mount points
# since (create order != mount order)
# -p creates parents automatically
default = ''
if ! zfs get type ${config._name} >/dev/null 2>&1; then
zfs create -up ${config._name} \
${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "-o ${n}=${v}") config.options)}
fi
'';
} // { readOnly = false; };
_mount = diskoLib.mkMountOption {
inherit config options;