deleted diskco

This commit is contained in:
Richard Mauer 2024-12-30 19:09:05 -05:00
parent aecbeb4bd3
commit 8bf0427e52
138 changed files with 0 additions and 10235 deletions

View File

@ -1,6 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

View File

@ -1,22 +0,0 @@
name: "Publish a flake to flakestry"
on:
push:
tags:
- "v?[0-9]+.[0-9]+.[0-9]+"
- "v?[0-9]+.[0-9]+"
workflow_dispatch:
inputs:
tag:
description: "The existing tag to publish"
type: "string"
required: true
jobs:
publish-flake:
runs-on: ubuntu-latest
permissions:
id-token: "write"
contents: "read"
steps:
- uses: flakestry/flakestry-publish@main
with:
version: "${{ inputs.tag || github.ref_name }}"

View File

@ -1,21 +0,0 @@
name: update-flake-lock
on:
workflow_dispatch: # allows manual triggering
schedule:
- cron: '0 0 * * 1,4' # Run twice a week
permissions:
pull-requests: write
contents: write
jobs:
lockfile:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v30
- name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@v24
with:
pr-labels: |
merge-queue

View File

@ -1,4 +0,0 @@
result
# Created by the NixOS interactive test driver
.nixos-test-history

View File

@ -1,12 +0,0 @@
queue_rules:
- name: default
merge_conditions:
- check-success=buildbot/nix-build
merge_method: rebase
pull_request_rules:
- name: merge using the merge queue
conditions:
- base=master
- label=merge-queue
actions:
queue: {}

View File

@ -1,39 +0,0 @@
# Contributing
We welcome contributions of all kinds, be it in terms of bug fixes,
reproductions, features or documentation.
In general, PRs are more likely to be merged quickly if they contain tests which
prove that a feature is working as intended or that a bug was indeed present and
has now been fixed. Creating a draft PR that reproduces a bug is also a great
way to help us fix issues quickly. Check out
[this PR](https://github.com/nix-community/disko/pull/330) as an example.
For more information on how to run and debug tests, check out
[Running and debugging tests](./docs/testing.md).
## How to find issues to work on
If you're looking for a low-hanging fruit, check out
[this list of `good first issue`s](https://github.com/nix-community/disko/labels/good%20first%20issue).
These are issues that we have confirmed to be real and which have a strategy for
a fix already lined out in the comments. All you need to do is implement.
If you're looking for something more challenging, check out
[this list of issues tagged `contributions welcome`](https://github.com/nix-community/disko/labels/contributions%20welcome).
These are issues that we have confirmed to be real and we know we want to be
fixed.
For the real though nuts, we also have
[the `help wanted` label](https://github.com/nix-community/disko/labels/help%20wanted)
for issues that we feel like we need external help with. If you want a real
challenge, take a look there!
If you're looking for bugs that still need to be reproduced, have a look at
[this list of non-`confirmed` bugs](https://github.com/nix-community/disko/issues?q=is%3Aissue+is%3Aopen+label%3Abug+-label%3Aconfirmed+).
These are things that look like bugs but that we haven't reproduced yet.
If you're looking to contribute to the documentation, check out
[the `documentation` tag](https://github.com/nix-community/disko/issues?q=is%3Aissue+is%3Aopen+label%3Adocumentation)
or just read through [our docs](./docs/INDEX.md) and see if you can find any
issues.

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2018, 2019, 20222024 Nix community projects
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,142 +0,0 @@
# disko - Declarative disk partitioning
<img title="" src="./docs/logo.png" alt="Project logo" width="274">
[Documentation Index](./docs/INDEX.md)
NixOS is a Linux distribution where everything is described as code, with one
exception: during installation, the disk partitioning and formatting are manual
steps. **disko** aims to correct this sad 🤡 omission.
This is especially useful for unattended installations, re-installation after a
system crash or for setting up more than one identical server.
## Overview
**disko** can either be used after booting from a NixOS installer, or in
conjunction with [nixos-anywhere](https://github.com/numtide/nixos-anywhere) if
you're installing remotely.
Before using **disko**, the specifications of the disks, partitions, type of
formatting and the mount points must be defined in a Nix configuration. You can
find [examples](./example) of typical configurations in the Nix community
repository, and use one of these as the basis of your own configuration.
You can keep your configuration and re-use it for other installations, or for a
system rebuild.
**disko** is flexible, in that it supports most of the common formatting and
partitioning options, including:
- Disk layouts: GPT, MBR, and mixed.
- Partition tools: LVM, mdadm, LUKS, and more.
- Filesystems: ext4, btrfs, ZFS, bcachefs, tmpfs, and others.
It can work with these in various configurations and orders, and supports
recursive layouts.
## How to use disko
Disko doesn't require installation: it can be run directly from nix-community
repository. The [Quickstart Guide](./docs/quickstart.md) documents how to run
Disko in its simplest form when installing NixOS. Alternatively, you can also
use the new [disko-install](./docs/disko-install.md) tool, which combines
`disko` and `nixos-install` into one step.
For information on other use cases, including upgrading from an older version of
**disko**, using **disko** without NixOS and downloading the module, see the
[How To Guide](./docs/HowTo.md)
For more detailed options, such as command line switches, see the
[Reference Guide](./docs/reference.md)
To access sample configurations for commonly-used disk layouts, refer to the
[examples](./example) provided.
Disko can be also used to create [disk images](./docs/disko-images.md).
## Sample Configuration and CLI command
A simple disko configuration may look like this:
```nix
{
disko.devices = {
disk = {
my-disk = {
device = "/dev/sda";
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
type = "EF00";
size = "500M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}
```
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.
```console
sudo nix --experimental-features "nix-command flakes" run github:nix-community/disko/latest -- --mode destroy,format,mount /tmp/disk-config.nix
```
## Related Tools
This tool is used by
[nixos-anywhere](https://github.com/numtide/nixos-anywhere), which carries out a
fully-automated remote install of NixOS.
We also acknowledge https://github.com/NixOS/nixpart, the conceptual ancestor of
this project.
## Licensing and Contribution details
This software is provided free under the
[MIT Licence](https://opensource.org/licenses/MIT).
If you want to contribute, check out [CONTRIBUTING.md](./CONTRIBUTING.md).
## Get in touch
We have a public matrix channel at
[disko](https://matrix.to/#/#disko:nixos.org).
---
This project is supported by [Numtide](https://numtide.com/).
![Untitledpng](https://codahosted.io/docs/6FCIMTRM0p/blobs/bl-sgSunaXYWX/077f3f9d7d76d6a228a937afa0658292584dedb5b852a8ca370b6c61dabb7872b7f617e603f1793928dc5410c74b3e77af21a89e435fa71a681a868d21fd1f599dd10a647dd855e14043979f1df7956f67c3260c0442e24b34662307204b83ea34de929d)
We are a team of independent freelancers that love open source.  We help our
customers make their project lifecycles more efficient by:
- Providing and supporting useful tools such as this one
- Building and deploying infrastructure, and offering dedicated DevOps support
- Building their in-house Nix skills, and integrating Nix with their workflows
- Developing additional features and tools
- Carrying out custom research and development.
[Contact us](https://numtide.com/contact) if you have a project in mind, or if
you need help with any of our supported tools, including this one. We'd love to
hear from you.

View File

@ -1,116 +0,0 @@
{ pkgs ? import <nixpkgs> { }
, lib ? pkgs.lib
, mode ? "mount"
, flake ? null
, flakeAttr ? null
, diskoFile ? null
, rootMountPoint ? "/mnt"
, noDeps ? false
, ...
}@args:
let
disko = import ./. {
inherit rootMountPoint;
inherit lib;
};
hasDiskoFile = diskoFile != null;
diskoAttr =
(if noDeps then
(if hasDiskoFile then
{
destroy = "_cliDestroyNoDeps";
format = "_cliFormatNoDeps";
mount = "_cliMountNoDeps";
unmount = "_cliUnmountNoDeps";
"format,mount" = "_cliFormatMountNoDeps";
"destroy,format,mount" = "_cliDestroyFormatMountNoDeps";
}
else
{
destroy = "destroyNoDeps";
format = "formatNoDeps";
mount = "mountNoDeps";
unmount = "unmountNoDeps";
"format,mount" = "formatMountNoDeps";
"destroy,format,mount" = "destroyFormatMountNoDeps";
}) // {
# legacy aliases
disko = "diskoScriptNoDeps";
create = "createScriptNoDeps";
zap_create_mount = "diskoScriptNoDeps";
}
else
(if hasDiskoFile then
{
destroy = "_cliDestroy";
format = "_cliFormat";
mount = "_cliMount";
unmount = "_cliUnmount";
"format,mount" = "_cliFormatMount";
"destroy,format,mount" = "_cliDestroyFormatMount";
}
else
{
destroy = "destroy";
format = "format";
mount = "mount";
unmount = "unmount";
"format,mount" = "formatMount";
"destroy,format,mount" = "destroyFormatMount";
}) // {
# legacy aliases
disko = "diskoScript";
create = "createScript";
zap_create_mount = "diskoScript";
}
).${mode};
hasDiskoConfigFlake =
hasDiskoFile || lib.hasAttrByPath [ "diskoConfigurations" flakeAttr ] (builtins.getFlake flake);
hasDiskoModuleFlake =
lib.hasAttrByPath [ "nixosConfigurations" flakeAttr "config" "disko" "devices" ] (builtins.getFlake flake);
diskFormat =
let
diskoConfig =
if hasDiskoFile then
import diskoFile
else
(builtins.getFlake flake).diskoConfigurations.${flakeAttr};
in
if builtins.isFunction diskoConfig then
diskoConfig ({ inherit lib; } // args)
else
diskoConfig;
diskoEval =
disko.${diskoAttr} diskFormat pkgs;
diskoScript =
if hasDiskoConfigFlake then
diskoEval
else if hasDiskoModuleFlake then
(builtins.getFlake flake).nixosConfigurations.${flakeAttr}.config.system.build.${diskoAttr} or (
pkgs.writeShellScriptBin "disko-compat-error" ''
echo 'Error: Attribute `nixosConfigurations.${flakeAttr}.config.system.build.${diskoAttr}` >&2
echo ' not found in flake `${flake}`!' >&2
echo ' This is probably caused by the locked version of disko in the flake' >&2
echo ' being different from the version of disko you executed.' >&2
echo 'EITHER set the `disko` input of your flake to `github:nix-community/disko/latest`,' >&2
echo ' run `nix flake update disko` in the flake directory and then try again,' >&2
echo 'OR run `nix run github:nix-community/disko/v1.9.0 -- --help` and use one of its modes.' >&2
exit 1;''
)
else
(builtins.abort "couldn't find `diskoConfigurations.${flakeAttr}` or `nixosConfigurations.${flakeAttr}.config.disko.devices`");
in
diskoScript

View File

@ -1,65 +0,0 @@
{ lib ? import <nixpkgs/lib>
, rootMountPoint ? "/mnt"
, checked ? false
, diskoLib ? import ./lib { inherit lib rootMountPoint; }
}:
let
eval = cfg: lib.evalModules {
modules = lib.singleton {
# _file = toString input;
imports = lib.singleton { disko.devices = cfg.disko.devices; };
options = {
disko.devices = lib.mkOption {
type = diskoLib.toplevel;
};
};
};
};
# We might instead reuse some of the deprecated output names to refer to the values the _cli* outputs currently do,
# but this warning allows us to get feedback from users early in case they have a use case we haven't considered.
warnDeprecated = name: lib.warn "the ${name} output is deprecated and will be removed, please open an issue if you're using it!";
in
{
lib = warnDeprecated ".lib.lib" diskoLib;
_cliDestroy = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).destroy;
_cliDestroyNoDeps = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).destroyNoDeps;
_cliFormat = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).format;
_cliFormatNoDeps = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).formatNoDeps;
_cliMount = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).mount;
_cliMountNoDeps = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).mountNoDeps;
_cliUnmount = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).unmount;
_cliUnmountNoDeps = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).unmountNoDeps;
_cliFormatMount = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).formatMount;
_cliFormatMountNoDeps = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).formatMountNoDeps;
_cliDestroyFormatMount = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).destroyFormatMount;
_cliDestroyFormatMountNoDeps = cfg: pkgs: ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).destroyFormatMountNoDeps;
# legacy aliases
create = cfg: warnDeprecated "create" (eval cfg).config.disko.devices._create;
createScript = cfg: pkgs: warnDeprecated "createScript" ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).formatScript;
createScriptNoDeps = cfg: pkgs: warnDeprecated "createScriptNoDeps" ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).formatScriptNoDeps;
format = cfg: warnDeprecated "format" (eval cfg).config.disko.devices._create;
formatScript = cfg: pkgs: warnDeprecated "formatScript" ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).formatScript;
formatScriptNoDeps = cfg: pkgs: warnDeprecated "formatScriptNoDeps" ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).formatScriptNoDeps;
mount = cfg: warnDeprecated "mount" (eval cfg).config.disko.devices._mount;
mountScript = cfg: pkgs: warnDeprecated "mountScript" ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).mountScript;
mountScriptNoDeps = cfg: pkgs: warnDeprecated "mountScriptNoDeps" ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).mountScriptNoDeps;
disko = cfg: warnDeprecated "disko" (eval cfg).config.disko.devices._disko;
diskoScript = cfg: pkgs: warnDeprecated "diskoScript" ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).diskoScript;
diskoScriptNoDeps = cfg: pkgs: warnDeprecated "diskoScriptNoDeps" ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).diskoScriptNoDeps;
# we keep this old output for backwards compatibility
diskoNoDeps = cfg: pkgs: warnDeprecated "diskoNoDeps" ((eval cfg).config.disko.devices._scripts { inherit pkgs checked; }).diskoScriptNoDeps;
config = cfg: (eval cfg).config.disko.devices._config;
packages = cfg: (eval cfg).config.disko.devices._packages;
}

View File

@ -1,8 +0,0 @@
#!/usr/bin/env bash
set -efux -o pipefail
# dependencies: bash jq util-linux lvm2 mdadm zfs gnugrep sed
disk=$(realpath "$1")
lsblk -a -f >&2
lsblk --output-all --json | jq -r --arg disk_to_clear "$disk" -f "$(dirname "$0")/disk-deactivate.jq" | bash -x
lsblk -a -f >&2

View File

@ -1,86 +0,0 @@
# since lsblk lacks zfs support, we have to do it this way
def remove:
if .fstype == "zfs_member" then
"if type zpool >/dev/null; then zpool destroy -f \(.label); zpool labelclear -f \(.label); fi"
elif .fstype == "LVM2_member" then
[
"vg=$(pvs \(.path) --noheadings --options vg_name | grep -o '[a-zA-Z0-9-]*')",
"vgchange -a n \"$vg\"",
"vgremove -f \"$vg\""
]
elif .fstype == "swap" then
"swapoff \(.path)"
elif .fstype == null then
# maybe its zfs
[
# the next line has some horrible escaping
"zpool=$(if type zdb >/dev/null; then zdb -l \(.path) | sed -nr $'s/ +name: \\'(.*)\\'/\\\\1/p'; fi)",
"if [[ -n \"${zpool}\" ]]; then zpool destroy -f \"$zpool\"; zpool labelclear -f \"$zpool\"; fi",
"unset zpool"
]
else
[]
end
;
def deactivate:
if .type == "disk" or .type == "loop" then
[
# If this disk is a member of raid, stop that raid
"md_dev=$(lsblk \(.path) -l -p -o type,name | awk 'match($1,\"raid.*\") {print $2}')",
"if [[ -n \"${md_dev}\" ]]; then umount \"$md_dev\"; mdadm --stop \"$md_dev\"; fi",
# Remove all file-systems and other magic strings
"wipefs --all -f \(.path)",
# Remove the MBR bootstrap code
"dd if=/dev/zero of=\(.path) bs=440 count=1"
]
elif .type == "part" then
[
"wipefs --all -f \(.path)"
]
elif .type == "crypt" then
[
"cryptsetup luksClose \(.path)",
"wipefs --all -f \(.path)"
]
elif .type == "lvm" then
(.name | split("-")[0]) as $vgname |
(.name | split("-")[1]) as $lvname |
[
"lvremove -fy \($vgname)/\($lvname)"
]
elif (.type | contains("raid")) then
[
"mdadm --stop \(.name)"
]
else
["echo Warning: unknown type '\(.type)'. Consider handling this in https://github.com/nix-community/disko/blob/master/disk-deactivate/disk-deactivate.jq"]
end
;
def walk:
[
(.mountpoints[] | select(. != null) | "umount -R \(.)"),
((.children // []) | map(walk)),
remove,
deactivate
]
;
def init:
"/dev/\(.name)" as $disk |
"/dev/disk/by-id/\(."id-link")" as $disk_by_id |
"/dev/disk/by-id/\(.tran)-\(.id)" as $disk_by_id2 |
"/dev/disk/by-id/\(.tran)-\(.wwn)" as $disk_by_wwn |
if $disk == $disk_to_clear or $disk_by_id == $disk_to_clear or $disk_by_id2 == $disk_to_clear or $disk_by_wwn == $disk_to_clear then
[
"set -fu",
walk
]
else
[]
end
;
.blockdevices | map(init) | flatten | join("\n")

View File

@ -1,193 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
readonly libexec_dir="${0%/*}"
# a file with the disko config
declare disko_config
# mount was chosen as the default mode because it's less destructive
mode=mount
nix_args=()
skip_destroy_safety_check=false
# DISKO_VERSION is set by the wrapper in package.nix
DISKO_VERSION="${DISKO_VERSION:="unknown! This is a bug, please report it!"}"
onlyPrintVersion=false
showUsage() {
cat <<USAGE
Usage: $0 [options] disk-config.nix
or $0 [options] --flake github:somebody/somewhere#disk-config
With flakes, disk-config is discovered first under the .diskoConfigurations top level attribute
or else from the disko module of a NixOS configuration of that name under .nixosConfigurations.
Options:
* -m, --mode mode
set the mode, either distroy, format, mount, unmount, format,mount or destroy,format,mount
destroy: unmount filesystems and destroy partition tables of the selected disks
format: create partition tables, zpools, lvms, raids and filesystems if they don't exist yet
mount: mount the partitions at the specified root-mountpoint
format,mount: run format and mount in sequence
destroy,format,mount: run all three modes in sequence. Previously known as --mode disko
* -f, --flake uri
fetch the disko config relative to this flake's root
* --arg name value
pass value to nix-build. can be used to set disk-names for example
* --argstr name value
pass value to nix-build as string
* --root-mountpoint /some/other/mnt
where to mount the device tree (default: /mnt)
* --dry-run
just show the path to the script instead of running it
* --no-deps
avoid adding another dependency closure to an in-memory installer
requires all necessary dependencies to be available in the environment
* --yes-wipe-all-disks
skip the safety check for destroying partitions, useful for automation
* --debug
run with set -x
* --help
show this help
USAGE
}
abort() {
echo "aborted: $*" >&2
exit 1
}
## Main ##
[[ $# -eq 0 ]] && {
showUsage
exit 1
}
while [[ $# -gt 0 ]]; do
case "$1" in
--debug)
set -x
;;
-m | --mode)
mode=$2
shift
;;
-f | --flake)
flake=$2
shift
;;
--argstr | --arg)
nix_args+=("$1" "$2" "$3")
shift
shift
;;
-h | --help)
showUsage
exit 0
;;
--dry-run)
dry_run=y
;;
--root-mountpoint)
nix_args+=(--argstr rootMountPoint "$2")
shift
;;
--no-deps)
nix_args+=(--arg noDeps true)
;;
--yes-wipe-all-disks)
skip_destroy_safety_check=true
;;
--show-trace)
nix_args+=("$1")
;;
--version)
onlyPrintVersion=true
;;
*)
if [ -z ${disko_config+x} ]; then
disko_config=$1
else
showUsage
exit 1
fi
;;
esac
shift
done
if [[ "$onlyPrintVersion" = true ]]; then
echo "$DISKO_VERSION"
exit 0
fi
# Always print version information to help with debugging
echo "disko version $DISKO_VERSION"
nixBuild() {
if command -v nom-build > /dev/null; then
nom-build "$@"
else
nix-build "$@"
fi
}
if ! {
# Base modes
[[ $mode = "destroy" ]] || [[ $mode = "format" ]] || [[ $mode = "mount" ]] || [[ $mode = "unmount" ]] ||
# Combined modes
[[ $mode = "format,mount" ]] ||
[[ $mode = "destroy,format,mount" ]] || # Replaces --mode disko
# Legacy modes, will be removed in next major version
[[ $mode = "disko" ]] || [[ $mode = "create" ]] || [[ $mode = "zap_create_mount" ]] ;
}; then
abort 'mode must be one of "destroy", "format", "mount", "destroy,format,mount" or "format,mount"'
fi
if [[ -n "${flake+x}" ]]; then
if [[ $flake =~ ^(.*)\#([^\#\"]*)$ ]]; then
flake="${BASH_REMATCH[1]}"
flakeAttr="${BASH_REMATCH[2]}"
fi
if [[ -z "${flakeAttr-}" ]]; then
echo "Please specify the name of the NixOS configuration to be installed, as a URI fragment in the flake-uri."
echo "For example, to use the output diskoConfigurations.foo from the flake.nix, append \"#foo\" to the flake-uri."
exit 1
fi
if [[ -e "$flake" ]]; then
flake="$(realpath "$flake")"
fi
nix_args+=("--arg" "flake" "\"$flake\"")
nix_args+=("--argstr" "flakeAttr" "$flakeAttr")
nix_args+=(--extra-experimental-features flakes)
elif [[ -n "${disko_config+x}" ]] && [[ -e "$disko_config" ]]; then
nix_args+=("--arg" "diskoFile" "$(realpath "$disko_config")")
else
abort "disko config must be an existing file or flake must be set"
fi
# The "--impure" is still pure, as the path is within the nix store.
script=$(nixBuild "${libexec_dir}"/cli.nix \
--no-out-link \
--impure \
--argstr mode "$mode" \
"${nix_args[@]}"
)
command=("$(echo "$script"/bin/*)")
if [[ $mode = "destroy,format,mount" && $skip_destroy_safety_check = true ]]; then
command+=("--yes-wipe-all-disks")
fi
# Legacy modes don't support --yes-wipe-all-disks and are not in `$script/bin`
if [[ $mode = "disko" ]] || [[ $mode = "create" ]] || [[ $mode = "zap_create_mount" ]] ; then
command=("$script")
fi
if [[ -n "${dry_run+x}" ]]; then
echo "${command[@]}"
else
exec "${command[@]}"
fi

View File

@ -1,273 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
showUsage() {
cat <<EOF
Usage: $0 [OPTIONS]
--mode MODE Specify the mode of operation. Valid modes are: format, mount.
Format will format the disk before installing.
Mount will mount the disk before installing.
Mount is useful for updating an existing system without losing data.
-f, --flake FLAKE_URI#ATTR Use the specified flake to install the NixOS configuration.
--disk NAME DEVICE Map the specified disk name to the specified device path.
--dry-run Print the commands that would be run, but do not run them.
--show-trace Show the stack trace on error.
-h, --help Show this help message.
--extra-files SOURCE DEST Copy the specified file or directory from the host into the NixOS configuration.
--option NAME VALUE Pass the specified option to Nix.
--write-efi-boot-entries Write EFI boot entries to the NVRAM of the system for the installed system.
Specify this option if you plan to boot from this disk on the current machine,
but not if you plan to move the disk to another machine.
--system-config JSON Merges the specified JSON object into the NixOS configuration.
--mount-point PATH Specify the mount point for the NixOS installation.
Default: /mnt/disko-install-root
EOF
}
serialiaseArrayToNix() {
local -n array=$1
nixExpr="{ "
# Iterate over the associative array to populate the Nix attrset string
for key in "${!array[@]}"; do
value=${array[$key]}
nixExpr+="\"${key//\"/\\\"}\" = \"${value//\"/\\\"}\"; "
done
nixExpr+="}"
echo "$nixExpr"
}
readonly libexec_dir="${0%/*}"
nix_args=(
--extra-experimental-features 'nix-command flakes'
"--option" "no-write-lock-file" "true"
)
dry_run=
extraSystemConfig="{}"
diskoAttr=diskoScript
writeEfiBootEntries=false
mountPoint=/mnt/disko-install-root
declare -A diskMappings
declare -A extraFiles
parseArgs() {
[[ $# -eq 0 ]] && {
showUsage
exit 1
}
while [[ $# -gt 0 ]]; do
case "$1" in
-d | --debug)
set -x
;;
-f | --flake)
flake=$2
shift
;;
-h | --help)
showUsage
exit 0
;;
--dry-run)
dry_run=y
;;
--show-trace)
nix_args+=("$1")
;;
--write-efi-boot-entries)
writeEfiBootEntries=true
;;
--mode)
if [[ $# -lt 2 ]]; then
echo "Option $1 requires an argument" >&2
exit 1
fi
case $2 in
format)
diskoAttr=diskoScript
;;
mount)
diskoAttr=mountScript
;;
*)
echo "Invalid mode: $2" >&2
echo "Valid modes are: format, mount" >&2
exit 1
;;
esac
shift
;;
--system-config)
if [[ $# -lt 2 ]]; then
echo "Option $1 requires one JSON argument." >&2
exit 1
fi
# shellcheck disable=SC2034
extraSystemConfig="$2"
shift
;;
--extra-files)
if [[ $# -lt 3 ]]; then
echo "Option $1 requires two arguments: source, destination" >&2
exit 1
fi
extraFiles[$2]=$3
shift
shift
;;
--option)
if [[ $# -lt 3 ]]; then
echo "Option $1 requires two arguments: key, value" >&2
exit 1
fi
nix_args+=(--option "$2" "$3")
shift
shift
;;
--disk)
if [[ $# -lt 3 ]]; then
echo "Option $1 requires two arguments: disk_name, device_path" >&2
exit 1
fi
# shellcheck disable=SC2034
diskMappings[$2]=$3
shift
shift
;;
--mount-point)
if [[ $# -lt 2 ]]; then
echo "Option $1 requires an argument" >&2
exit 1
fi
mountPoint=$2
shift
;;
*)
echo "Unknown option: $1" >&2
showUsage
exit 1
;;
esac
shift
done
}
cleanupMountPoint() {
if mountpoint -q "${mountPoint}"; then
umount -R "${mountPoint}"
fi
rmdir "${mountPoint}"
}
nixBuild() {
if command -v nom-build > /dev/null; then
nom-build "$@"
else
nix-build "$@"
fi
}
maybeRun () {
if [[ -z ${dry_run-} ]]; then
"$@"
else
echo "Would run: $*"
fi
}
main() {
parseArgs "$@"
if [[ -z ${flake-} ]]; then
echo "Please specify the flake-uri with --flake to use for installation." >&2
exit 1
fi
# check if we are root
if [[ "$EUID" -ne 0 ]] && [[ -z ${dry_run-} ]]; then
echo "This script must be run as root" >&2
exit 1
fi
if [[ $flake =~ ^(.*)\#([^\#\"]*)$ ]]; then
flake="${BASH_REMATCH[1]}"
flakeAttr="${BASH_REMATCH[2]}"
fi
if [[ -e "$flake" ]]; then
flake=$(realpath "$flake")
fi
if [[ -z ${flakeAttr-} ]]; then
echo "Please specify the name of the NixOS configuration to be installed, as a URI fragment in the flake-uri." >&2
echo 'For example, to use the output nixosConfigurations.foo from the flake.nix, append "#foo" to the flake-uri.' >&2
exit 1
fi
maybeRun mkdir -p "${mountPoint}"
maybeRun chmod 755 "${mountPoint}" # bcachefs wants 755
# shellcheck disable=SC2064
if [[ -z ${dry_run-} ]]; then
trap cleanupMountPoint EXIT
fi
outputs=$(nixBuild "${libexec_dir}"/install-cli.nix \
"${nix_args[@]}" \
--no-out-link \
--impure \
--argstr flake "$flake" \
--argstr flakeAttr "$flakeAttr" \
--argstr rootMountPoint "$mountPoint" \
--arg writeEfiBootEntries "$writeEfiBootEntries" \
--arg diskMappings "$(serialiaseArrayToNix diskMappings)" \
--argstr extraSystemConfig "$extraSystemConfig" \
-A installToplevel \
-A closureInfo \
-A "$diskoAttr")
IFS=$'\n' mapfile -t artifacts <<<"$outputs"
nixos_system=${artifacts[0]}
closure_info=${artifacts[1]}
disko_script=${artifacts[2]}
if [[ -n ${dry_run-} ]]; then
echo "Would run: $disko_script"
echo "Would run: nixos-install --system '$nixos_system' --root '$mountPoint'"
exit 0
fi
# We don't want swap as can break your running system in weird ways if you eject the disk
# Hopefully disko-install has enough RAM to run without swap, otherwise we can make this configurable in future.
DISKO_SKIP_SWAP=1 "$disko_script"
for source in "${!extraFiles[@]}"; do
destination=${extraFiles[$source]}
mkdir -p "$mountPoint/$(dirname "$destination")"
cp -ar "$source" "$mountPoint/$destination"
done
# nix copy uses up a lot of memory and we work around issues with incorrect checksums in our store
# that can be caused by using closureInfo in combination with multiple builders and non-deterministic builds.
# Therefore if we have a blank store, we copy the store paths and registration from the closureInfo.
if [[ ! -f "${mountPoint}/nix/var/nix/db/db.sqlite" ]]; then
echo "Copying store paths" >&2
mkdir -p "${mountPoint}/nix/store"
xargs cp --recursive --target "${mountPoint}/nix/store" < "${closure_info}/store-paths"
echo "Loading nix database" >&2
NIX_STATE_DIR=${mountPoint}/nix/var/nix nix-store --load-db < "${closure_info}/registration"
fi
nixos-install --no-channel-copy --no-root-password --system "$nixos_system" --root "$mountPoint"
}
if main "$@"; then
echo "disko-install succeeded"
exit 0
else
echo "disko-install failed" >&2
exit 1
fi

View File

@ -1,43 +0,0 @@
{ lib, nixosOptionsDoc, runCommand, fetchurl, pandoc }:
let
diskoLib = import ./lib {
inherit lib;
rootMountPoint = "/mnt";
};
eval = lib.evalModules {
modules = [
{
options.disko = {
devices = lib.mkOption {
type = diskoLib.toplevel;
default = { };
description = "The devices to set up";
};
};
}
];
};
options = nixosOptionsDoc {
options = eval.options;
};
md = (runCommand "disko-options.md" { } ''
cat >$out <<EOF
# Disko options
EOF
cat ${options.optionsCommonMark} >>$out
'').overrideAttrs (_o: {
# Work around https://github.com/hercules-ci/hercules-ci-agent/issues/168
allowSubstitutes = true;
});
css = fetchurl {
url = "https://gist.githubusercontent.com/killercup/5917178/raw/40840de5352083adb2693dc742e9f75dbb18650f/pandoc.css";
sha256 = "sha256-SzSvxBIrylxBF6B/mOImLlZ+GvCfpWNLzGFViLyOeTk=";
};
in
runCommand "disko.html" { nativeBuildInputs = [ pandoc ]; } ''
mkdir $out
cp ${css} $out/pandoc.css
pandoc --css="pandoc.css" ${md} --to=html5 -s -f markdown+smart --metadata pagetitle="Disko options" -o $out/index.html
''

View File

@ -1,178 +0,0 @@
# How-to Guide: Disko
## How to use Disko without NixOS
TODO: Still to be documented
## Upgrading From Older disko versions
TODO: Include documentation here.
For now, see the
[upgrade guide](https://github.com/JillThornhill/disko/blob/master/docs/upgrade-guide.md)
## Installing NixOS module
You can use the NixOS module in one of the following ways:
<details>
<summary>Flakes (Current recommendation)</summary>
If you use nix flakes support:
```nix
{
inputs.disko.url = "github:nix-community/disko/latest";
inputs.disko.inputs.nixpkgs.follows = "nixpkgs";
outputs = { self, nixpkgs, disko }: {
# change `yourhostname` to your actual hostname
nixosConfigurations.yourhostname = nixpkgs.lib.nixosSystem {
# change to your system:
system = "x86_64-linux";
modules = [
./configuration.nix
disko.nixosModules.disko
];
};
};
}
```
</details>
<details>
<summary>niv</summary>
First add it to [niv](https://github.com/nmattia/niv):
```console
niv add nix-community/disko
```
Then add the following to your configuration.nix in the `imports` list:
```nix
{
imports = [ "${(import ./nix/sources.nix).disko}/module.nix" ];
}
```
</details>
<details>
<summary>npins</summary>
First add it to [npins](https://github.com/andir/npins):
```console
npins add github nix-community disko
```
Then add the following to your configuration.nix in the `imports` list:
```nix
let
sources = import ./npins;
disko = import sources.disko {};
in
{
imports = [ "${disko}/module.nix" ];
}
```
</details>
<details>
<summary>nix-channel</summary>
As root run:
```console
nix-channel --add https://github.com/nix-community/disko/archive/master.tar.gz disko
nix-channel --update
```
Then add the following to your configuration.nix in the `imports` list:
```nix
{
imports = [ <disko/module.nix> ];
}
```
</details>
<details>
<summary>fetchTarball</summary>
Add the following to your configuration.nix:
```nix
{
imports = [ "${builtins.fetchTarball "https://github.com/nix-community/disko/archive/master.tar.gz"}/module.nix" ];
}
```
or with pinning:
```nix
{
imports = let
# replace this with an actual commit id or tag
commit = "f2783a8ef91624b375a3cf665c3af4ac60b7c278";
in [
"${builtins.fetchTarball {
url = "https://github.com/nix-community/disko/archive/${commit}.tar.gz";
# replace this with an actual hash
sha256 = "0000000000000000000000000000000000000000000000000000";
}}/module.nix"
];
}
```
</details>
## Using the NixOS module
```nix
{
# checkout the example folder for how to configure different disko layouts
disko.devices = {
disk = {
vdb = {
device = "/dev/disk/by-id/some-disk-id";
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
type = "EF00";
size = "100M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}
```
this will configure `fileSystems` and other required NixOS options to boot the
specified configuration.
If you are on an installer, you probably want to disable `enableConfig`.
disko will create the scripts `disko-create` and `disko-mount` which can be used
to create/mount the configured disk layout.

View File

@ -1,22 +0,0 @@
# disko - Declarative disk partitioning
<img title="" src="./logo.png" alt="" width="220">
## Table of Contents
### For users
- [README](../README.md)
- [Quickstart](./quickstart.md)
- [System Requirements](./requirements.md)
- [How to Guide](./HowTo.md)
- [Disko-Install](./disko-install.md)
- [Disko-Images](./disko-images.md)
- [Support Matrix](./supportmatrix.md)
- [Reference](./reference.md)
- [Upgrade Guide](./upgrade-guide.md)
- [Migrating to the new GPT layout](./table-to-gpt.md)
### For contributors
- [Running and debugging tests](./testing.md)

View File

@ -1,122 +0,0 @@
# Generating Disk Images with Secrets Included using Disko
Using Disko on NixOS allows you to efficiently create `.raw` VM images from a
system configuration. The generated image can be used as a VM or directly
written to a physical drive to create a bootable disk. Follow the steps below to
generate disk images:
## Generating the `.raw` VM Image
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`.
```nix
# save this as flake.nix
{
description = "A disko images example";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
disko.url = "github:nix-community/disko/latest";
disko.inputs.nixpkgs.follows = "nixpkgs";
};
outputs = { self, disko, nixpkgs }: {
nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
# You can get this file from here: https://github.com/nix-community/disko/blob/master/example/simple-efi.nix
./simple-efi.nix
disko.nixosModules.disko
({ config, ... }: {
# shut up state version warning
system.stateVersion = config.system.nixos.version;
# Adjust this to your liking.
# WARNING: if you set a too low value the image might be not big enough to contain the nixos installation
disko.devices.disk.main.imageSize = "10G";
})
];
};
};
}
```
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:
```console
./result --help
Usage: $script [options]
Options:
* --pre-format-files <src> <dst>
copies the src to the dst on the VM, before disko is run
This is useful to provide secrets like LUKS keys, or other files you need for formatting
* --post-format-files <src> <dst>
copies the src to the dst on the finished image
These end up in the images later and is useful if you want to add some extra stateful files
They will have the same permissions but will be owned by root:root
* --build-memory <amt>
specify the amount of memory in MiB that gets allocated to the build VM
This can be useful if you want to build images with a more involed NixOS config
The default is 1024 MiB
```
An example run may look like this:
```
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:
```
$ ls -la main.raw
.rw-r--r-- root root 10 GB 2 minutes ago main.raw
```
## Additional Configuration
- For custom image name output, define the image name in your Disko configuration:
```console
disko.devices.disk.<drive>.imageName = "nixos-x86_64-linux-generic-btrfs"; # Set your preferred name
```
The image scirpt will produce `nixos-x86_64-linux-generic-btrfs.raw` instead of `<drive>.raw`.
- For virtual drive use, define the image size in your Disko configuration:
```console
disko.devices.disk.<drive>.imageSize = "32G"; # Set your preferred size
```
## Understanding the Image Generation Process
1. Files specified in `--pre-format-files` and `--post-format-files` are
temporarily copied to `/tmp`.
2. Files are then moved to their respective locations in the VM both before and
after the Disko partitioning script runs.
3. The NixOS installer is executed, having access only to `--post-format-files`.
4. Upon installer completion, the VM is shutdown, and the `.raw` disk files are
moved to the local directory.
> **Note**: The auto-resizing feature is currently not available in Disko.
> Contributions for this feature are welcomed. Adjust the `imageSize`
> configuration to prevent issues related to file size and padding.
By following these instructions and understanding the process, you can smoothly
generate disk images with Disko for your NixOS system configurations.

View File

@ -1,233 +0,0 @@
# disko-install
**disko-install** enhances the normal `nixos-install` with disko's partitioning
feature. It can be started from the NixOS installer but it can also be used to
create bootable USB-Sticks from your normal workstation. Furthermore
`disko-install` has a mount mode that will only mount but not destroy existing
partitions. The mount mode can be used to mount and repair existing NixOS
installations. This document provides a comprehensive guide on how to use
**disko-install**, including examples for typical usage scenarios.
## Requirements
- a Linux system with Nix installed.
- a target disk or partition for the NixOS installation.
- a Nix flake that defines your desired NixOS configuration.
## Usage
### Fresh Installation
For a fresh installation, where **disko-install** will handle partitioning and
setting up the disk, use the following syntax:
```console
sudo nix run 'github:nix-community/disko/latest#disko-install' -- --flake <flake-url>#<flake-attr> --disk <disk-name> <disk-device>
```
Example:
First run `nixos-generate-config --root /tmp/config --no-filesystems` and edit
`configuration.nix` to your liking.
Then add the following `flake.nix` inside `/tmp/config/etc/nixos`. In this
example we assume a system that has been booted with EFI:
```nix
{
inputs.nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
inputs.disko.url = "github:nix-community/disko/latest";
inputs.disko.inputs.nixpkgs.follows = "nixpkgs";
outputs = { self, disko, nixpkgs }: {
nixosConfigurations.mymachine = nixpkgs.legacyPackages.x86_64-linux.nixos [
./configuration.nix
disko.nixosModules.disko
{
disko.devices = {
disk = {
main = {
# When using disko-install, we will overwrite this value from the commandline
device = "/dev/disk/by-id/some-disk-id";
type = "disk";
content = {
type = "gpt";
partitions = {
MBR = {
type = "EF02"; # for grub MBR
size = "1M";
priority = 1; # Needs to be first partition
};
ESP = {
type = "EF00";
size = "500M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}
];
};
}
```
Identify the device name that you want to install NixOS to:
```console
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 1 14.9G 0 disk
└─sda1 8:1 1 14.9G 0 part
zd0 230:0 0 10G 0 disk
├─zd0p1 230:1 0 500M 0 part
└─zd0p2 230:2 0 9.5G 0 part /mnt
nvme0n1 259:0 0 1.8T 0 disk
├─nvme0n1p1 259:1 0 1G 0 part /boot
├─nvme0n1p2 259:2 0 16M 0 part
├─nvme0n1p3 259:3 0 250G 0 part
└─nvme0n1p4 259:4 0 1.6T 0 part
```
In our example, we want to install to a USB-stick (/dev/sda):
```console
$ sudo nix run 'github:nix-community/disko/latest#disko-install' -- --flake '/tmp/config/etc/nixos#mymachine' --disk main /dev/sda
```
Afterwards you can test your USB-stick by either selecting during the boot or
attaching it to a qemu-vm:
```
$ sudo qemu-kvm -enable-kvm -hda /dev/sda
```
### Persisting boot entries to EFI vars flash
**disko-install** is designed for NixOS installations on portable storage or
disks that may be transferred between computers. As such, it does not modify the
host's NVRAM by default. To ensure your NixOS installation boots seamlessly on
new hardware or to prioritize it in your current machine's boot order, use the
--write-efi-boot-entries option:
```console
$ sudo nix run 'github:nix-community/disko/latest#disko-install' -- --write-efi-boot-entries --flake '/tmp/config/etc/nixos#mymachine' --disk main /dev/sda
```
This command installs NixOS with **disko-install** and sets the newly installed
system as the default boot option, without affecting the flexibility to boot
from other devices if needed.
### Using disko-install in an offline installer
If you want to use **disko-install** from a custom installer without internet,
you need to make sure that in addition to the toplevel of your NixOS closure
that you plan to install, it also needs to contain **diskoScript** and all the
flake inputs for evaluation.
#### Example configuration to install
Add this to your flake.nix output:
```nix
{
nixosConfigurations.your-machine = inputs.nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
# to pass this flake into your configuration (see the example below)
specialArgs = {inherit self;};
modules = [
{
# TODO: add your NixOS configuration here, don't forget your hardware-configuration.nix as well!
boot.loader.systemd-boot.enable = true;
imports = [ self.inputs.disko.nixosModules.disko ];
disko.devices = {
disk = {
vdb = {
device = "/dev/disk/by-id/some-disk-id";
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
type = "EF00";
size = "500M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}
];
};
}
```
#### Example for a NixOS installer
```nix
# `self` here is referring to the flake `self`, you may need to pass it using `specialArgs` or define your NixOS installer configuration
# in the flake.nix itself to get direct access to the `self` flake variable.
{ pkgs, self, ... }:
let
dependencies = [
self.nixosConfigurations.your-machine.config.system.build.toplevel
self.nixosConfigurations.your-machine.config.system.build.diskoScript
self.nixosConfigurations.your-machine.config.system.build.diskoScript.drvPath
self.nixosConfigurations.your-machine.pkgs.stdenv.drvPath
# https://github.com/NixOS/nixpkgs/blob/f2fd33a198a58c4f3d53213f01432e4d88474956/nixos/modules/system/activation/top-level.nix#L342
self.nixosConfigurations.your-machine.pkgs.perlPackages.ConfigIniFiles
self.nixosConfigurations.your-machine.pkgs.perlPackages.FileSlurp
(self.nixosConfigurations.your-machine.pkgs.closureInfo { rootPaths = [ ]; }).drvPath
] ++ builtins.map (i: i.outPath) (builtins.attrValues self.inputs);
closureInfo = pkgs.closureInfo { rootPaths = dependencies; };
in
# Now add `closureInfo` to your NixOS installer
{
environment.etc."install-closure".source = "${closureInfo}/store-paths";
environment.systemPackages = [
(pkgs.writeShellScriptBin "install-nixos-unattended" ''
set -eux
# Replace "/dev/disk/by-id/some-disk-id" with your actual disk ID
exec ${pkgs.disko}/bin/disko-install --flake "${self}#your-machine" --disk vdb "/dev/disk/by-id/some-disk-id"
'')
];
}
```
Also see the
[NixOS test of disko-install](https://github.com/nix-community/disko/blob/master/tests/disko-install/default.nix)
that also runs without internet.

View File

@ -1,26 +0,0 @@
# Running Interactive VMs with disko
disko now exports its own flavor of interactive VMs (similiar to
config.system.build.vm). Simply import the disko module and build the vm runner
with:
```
nix run -L '.#nixosConfigurations.mymachine.config.system.build.vmWithDisko'
```
You can configure the VM using the `virtualisation.vmVariantWithDisko` NixOS
option:
```nix
{
virtualisation.vmVariantWithDisko = {
virtualisation.fileSystems."/persist".neededForBoot = true;
# For running VM on macos: https://www.tweag.io/blog/2023-02-09-nixos-vm-on-macos/
# virtualisation.host.pkgs = inputs.nixpkgs.legacyPackages.aarch64-darwin;
};
}
```
extraConfig that is set in disko.tests.extraConfig is also applied to the
interactive VMs. imageSize of the VMs will be determined by the imageSize in the
disk type in your disko config. memorySize is set by disko.memSize

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

View File

@ -1,225 +0,0 @@
# disko - Declarative disk partitioning
<img src="./logo.png" title="" alt="Project logo" width="247">
[Documentation Index](./INDEX.md)
## Quickstart Guide
This tutorial describes how to install NixOS on a single disk system using
`disko`. You will also need to refer to the NixOS manual, which is available
[here.](https://nixos.org/manual/nixos/stable/index.html#ex-config)
Please note that `disko` will reformat the entire disk and overwrite any
existing partitions. Dual booting with other operating systems is not supported.
### Step 1: Choose a Disk Configuration
Real-world templates are provided in this
[repository](https://github.com/nix-community/disko-templates).
More disk layouts for all filesystems can be also found in the
[example](https://github.com/nix-community/disko/tree/master/example) directory
of disko. However these examples are also used for regression tests in disko and
may have uncommon options in them to fully exercise all features of disko, that
you may need to change or remove.
Decide which of these layouts best suits your requirements. If you're not sure
which layout to pick, use the
[single-disk-ext4](https://github.com/nix-community/disko-templates/blob/main/single-disk-ext4/disko-config.nix)
configuration. This layout is compatible with both BIOS and EFI systems.
Refer to the [reference manual](./reference.md) for more information about the
sample layouts and how to build your own configuration.
To copy a template use this command in your nixos configuration directory:
```
nix flake init --template github:nix-community/disko-templates#single-disk-ext4
```
This will write a file called `disko-config.nix` into the current directory.
Import this file in your NixOS configuration:
```nix
{
imports = [ ./disko-config.nix ];
}
```
If you want to choose a layout from the disko example directory instead, you'll
need to make a note of the URL to the raw file. To do this, open the file in
Github. Immediately below the list of contributors, you will see a button
labelled 'RAW' near the right hand side. Click this. The URL of the raw file
will appear in the search bar of your browser. It will look something like this:
```
https://raw.githubusercontent.com/nix-community/disko/master/example/hybrid.nix
```
### Step 2: Boot the installer
Download the NixOS ISO image from the NixOS
[download page](https://nixos.org/download.html#nixos-iso), and create a
bootable USB drive following the instructions
in [Section 2.4.1 "Booting from a USB flash drive"](https://nixos.org/manual/nixos/stable/index.html#sec-booting-from-usb) of
the NixOS manual. Boot the machine from this USB drive.
### Step 3: Retrieve the disk name
Identify the name of your system disk by using the `lsblk` command as follows:
```console
lsblk
```
The output from this command will look something like this:
```
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
nvme0n1     259:0    0   1,8T  0 disk
```
In this example, an empty NVME SSD with 2TB space is shown with the disk name
"nvme0n1". Make a note of the disk name as you will need it later.
### Step 4: Copy the disk configuration to your machine
In Step 1, you chose a disk layout configuration from the
[examples directory](https://github.com/nix-community/disko/tree/master/example),
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:
```console
cd /tmp
curl https://raw.githubusercontent.com/nix-community/disko/master/example/hybrid.nix -o /tmp/disk-config.nix
```
### Step 5: Adjust the device in the disk configuration
Inside the disk-config.nix the device needs to point to the correct disk name.
Open the configuration in your favorite editor i.e.:
```console
nano /tmp/disk-config.nix
```
Replace `<disk-name>` with the name of your disk obtained in Step 1.
```nix
# ...
main = {
type = "disk";
device = "<disk-name>";
content = {
type = "gpt";
# ...
```
### Step 6: Run disko to partition, format and mount your disks
The following step will partition and format your disk, and mount it to `/mnt`.
**Please note: This will erase any existing data on your disk.**
```console
sudo nix --experimental-features "nix-command flakes" run github:nix-community/disko/latest -- --mode destroy,format,mount /tmp/disk-config.nix
```
After the command has run, your file system should have been formatted and
mounted. You can verify this by running the following command:
```console
mount | grep /mnt
```
The output should look like this if your disk name is `nvme0n1`.
```
/dev/nvme0n1p1 on /mnt type ext4 (rw,relatime,stripe=2)
/dev/nvme0n1p2 on /mnt/boot type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro)
```
### Step 7: Complete the NixOS installation.
Your disks have now been formatted and mounted, and you are ready to complete
the NixOS installation as described in the
[NixOS manual](https://nixos.org/manual/nixos/stable/index.html#sec-installation) -
see the section headed "**Installing**", Steps 3 onwards. However, you will need
to include the partitioning and formatting configurations that you copied into
`/tmp/disk-config.nix` in your configuration, rather than allowing NixOS to
generate information about your file systems. When you are configuring the
system as per Step 4 of the manual, you should:
a) Include the `no-filesystems` switch when using the `nixos-generate-config`
command to generate an initial `configuration.nix`. You will be supplying the
file system configuration details from `disk-config.nix`. Your CLI command to
generate the configuration will be:
```console
nixos-generate-config --no-filesystems --root /mnt
```
This will create the file `configuration.nix` in `/mnt/etc/nixos`.
b) Move the `disko` configuration to /etc/nixos
```console
mv /tmp/disk-config.nix /mnt/etc/nixos
```
c) You can now edit `configuration.nix` as per your requirements. This is
described in Step 4 of the manual. For more information about configuring your
system, refer to the NixOS manual.
[Chapter 6, Configuration Syntax](https://nixos.org/manual/nixos/stable/index.html#sec-configuration-syntax)
describes the NixOS configuration syntax, and
[Appendix A, Configuration Options](https://nixos.org/manual/nixos/stable/options.html)
gives a list of available options. You can find also find a minimal example of a
NixOS configuration in the manual:
[Example: NixOS Configuration](https://nixos.org/manual/nixos/stable/index.html#ex-config).
d) When editing `configuration.nix`, you will need to add the `disko` NixOS
module and `disk-config.nix` to the imports section. This section will already
include the file `./hardware-configuration.nix`, and you can add the new entries
just below this. This section will now include:
```nix
imports =
[ # Include the results of the hardware scan.
./hardware-configuration.nix
"${builtins.fetchTarball "https://github.com/nix-community/disko/archive/master.tar.gz"}/module.nix"
./disk-config.nix
];
```
e) If you chose the hybrid-partition scheme, then choose `grub` as a bootloader,
otherwise follow the recommendations in Step 4 of the **Installation** section
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.
```nix
# ...
#boot.loader.systemd-boot.enable = true;
#boot.loader.efi.canTouchEfiVariables = true;
boot.loader.grub.enable = true;
boot.loader.grub.efiSupport = true;
boot.loader.grub.efiInstallAsRemovable = true;
# ...
```
f) Finish the installation and reboot your machine,
```console
nixos-install
reboot
```

View File

@ -1,47 +0,0 @@
# Reference Manual: disko
## Module Options
We are currently having issues being able to generate proper module option
documentation for our recursive disko types. However you can read the available
options [here](https://github.com/nix-community/disko/tree/master/lib/types).
Combined with the
[examples](https://github.com/nix-community/disko/tree/master/example) this
hopefully gives you an overview.
## Command Line Options
```
Usage: ./disko [options] disk-config.nix
or ./disko [options] --flake github:somebody/somewhere#disk-config
With flakes, disk-config is discovered first under the .diskoConfigurations top level attribute
or else from the disko module of a NixOS configuration of that name under .nixosConfigurations.
Options:
* -m, --mode mode
set the mode, either distroy, format, mount, format,mount or destroy,format,mount
destroy: unmount filesystems and destroy partition tables of the selected disks
format: create partition tables, zpools, lvms, raids and filesystems if they don't exist yet
mount: mount the partitions at the specified root-mountpoint
format,mount: run format and mount in sequence
destroy,format,mount: run all three modes in sequence. Previously known as --mode disko
* -f, --flake uri
fetch the disko config relative to this flake's root
* --arg name value
pass value to nix-build. can be used to set disk-names for example
* --argstr name value
pass value to nix-build as string
* --root-mountpoint /some/other/mnt
where to mount the device tree (default: /mnt)
* --dry-run
just show the path to the script instead of running it
* --no-deps
avoid adding another dependency closure to an in-memory installer
requires all necessary dependencies to be available in the environment
* --debug
run with set -x
* --yes-wipe-all-disks
skip the safety check for destroying partitions, useful for automation
```

View File

@ -1,9 +0,0 @@
# disko - Declarative disk partitioning
<img title="" src="./logo.jpeg" alt="" width="220">
[Documentation Index](./INDEX.md)
## System Requirements
TODO: Populate this

View File

@ -1,9 +0,0 @@
# disko - Declarative disk partitioning
<img title="" src="./logo.jpeg" alt="" width="220">
[Documentation Index](./INDEX.md)
## Support Matrix
TODO: Populate this

View File

@ -1,137 +0,0 @@
# Migrating to the new GPT layout
## Situation
When evaluating your NixOS system closure the following trace appears:
```
trace: warning: The legacy table is outdated and should not be used. We recommend using the gpt type instead.
Please note that certain features, such as the test framework, may not function properly with the legacy table type.
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.
```
The solution is to migrate to the new `gpt` layout type.
## Precondition
Disko was set up with
- `type = "table"` and
- `format = "gpt"`,
for example like this:
```nix
{
disko.devices.disk.example = {
type = "disk";
device = "/dev/nvme0n1";
content = {
type = "table";
format = "gpt";
partitions = [
{
name = "ESP";
start = "0";
end = "512MiB";
fs-type = "fat32";
bootable = true;
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
}
{
name = "root";
start = "512MiB";
end = "100%";
content.format = "ext4";
}
];
};
};
}
```
## Remediation
The new GPT layout (`type = "gpt"`) uses partlabels to realize the partiton
numbering. For this reason you have to manually set up partition labels, if you
want to resolve this issue.
### Create GPT partition labels
For each partition involved, create the partition label from these components:
- The partition number (e.g. /dev/nvme0n**1**, or /dev/sda**1**)
- The parent type in your disko config (value of
`disko.device.disk.example.type = "disk";`)
- The parent name in your disko config (attribute name of
`disko.devices.disk.example`, so `example` in this example)
- The partition name in your disko config (attribute name of
`disko.devices.disk.content.partitions.*.name`)
```bash
# sgdisk -c 1:disk-example-ESP /dev/nvme0n1
# sgdisk -c 2:disk-example-zfs /dev/nvme0n1
Warning: The kernel is still using the old partition table.
The new table will be used at the next reboot or after you
run partprobe(8) or kpartx(8)
The operation has completed successfully.
```
### Update disko configuration
Make the following changes to your disko configuration:
1. Set `disko.devices.disk.example.content.type = "gpt"`
1. Remove `disko.devices.disk.example.content.format`
1. Convert `disko.devices.disk.example.content.partitions` to an attribute set and
promote the `name` field to the key for its partition
1. Add a `priority` field to each partition, to reflect the intended partition
number
Then rebuild your system and reboot.
### Recovering from mistake
If you made a mistake here, your system will be waiting for devices to appear,
and then run into timeouts. You can easily recover from this, since rebooting
into an old generation will still use the legacy way of numbering of partitions.
## Result
The fixed disko configuration would look like this:
```nix
{
disko.devices.disk.example = {
type = "disk";
device = "/dev/nvme0n1";
content = {
type = "gpt";
partitions = {
ESP = {
size = "512MiB";
type = "EF00";
priority = 1;
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
priority = 2;
content.format = "ext4";
};
};
};
};
}
```

View File

@ -1,160 +0,0 @@
# Running and debugging tests
Disko makes extensive use of VM tests. All examples you can find in
[the example directory](../example) have a respective test suite that verifies
the example is working in [the tests directory](../tests/). They utilize the
[NixOS test functionality](https://nixos.org/manual/nixos/stable/#sec-nixos-tests).
We use a wrapper around this called `makeDiskoTest`. There is currently (as of
2024-10-16) no documentation for all its arguments, but you can have a look at
[its current code](https://github.com/nix-community/disko/blob/master/lib/tests.nix#L44C5-L58C10),
that should already be helpful.
However, you don't need to know about all of the inner workings to interact with
the tests effectively. For some of the most common operations, see the sections
below.
## Run just one of the tests
```sh
nix build --no-link .#checks.x86_64-linux.simple-efi
```
This will run the test in [`tests/simple-efi.nix`](../tests/simple-efi.nix),
which builds a VM with all disks specified in the
[`example/simple-efi.nix`](../example/simple-efi.nix) config connected as
virtual devices, run disko to format them, reboot, verify the VM boots properly,
and then run the code specified in `extraTestScript` to validate that the
partitions have been created and were mounted as expected.
### How `extraTestScript` works
This is written in Python. The most common lines you'll see look something like
this:
```python
machine.succeed("test -b /dev/md/raid1");
machine.succeed("mountpoint /");
```
The `machine` in these is a machine object, which defines
[a multitude of functions to interact with and test](https://nixos.org/manual/nixos/stable/#ssec-machine-objects),
assumptions about the state of the VM after formatting and rebooting.
Disko currently (as of 2024-10-16) doesn't have any tests that utilize multiple
VMs at once, so the only machine available in these scripts is always just the
default `machine`.
## Debugging tests
If you make changes to disko, you might break a test, or you may want to modify
a test to prevent regressions. In these cases, running the full test with
`nix build` every time is time-consuming and tedious.
Instead, you can build and then run the VM for a test in interactive mode. This
will create the VM and all virtual disks as required by the test's config, but
allow you to interact with the machine on a terminal afterwards.
First, build the interactive test driver and run it:
```
nix build .#checks.x86_64-linux.simple-efi.driverInteractive
result/bin/nixos-test-driver --keep-vm-state
```
This will open an IPython prompt in which you can use th same objects and
functions as in `extraTestScript`. In there, you can run
```
machine.shell_interact()
```
to start the VM and attach the terminal to it. This will also open a QEMU
window, in which you can log in as `root` with no password, but that makes it
more difficult to paste input and output. Instead, wait for the systemd messages
to settle down, and then **simply start typing**. This should make a `$` prompt
appear, indicating that the machine is ready to take commands. The NixOS manual
calls out a few special messages to look for, but these are buried underneath
the systemd logs.
Once you are in this terminal, you're running commands on the VM. The only thing
that doesn't work here is the `exit` command. Instead, you need to press Ctrl+D
and wait for a second to return to the IPython prompt.
In summary, a full session looks something like this:
```
# nix build .#checks.x86_64-linux.simple-efi.driverInteractive
# result/bin/nixos-test-driver --keep-vm-state
start all VLans
start vlan
running vlan (pid 146244; ctl /tmp/vde1.ctl)
(finished: start all VLans, in 0.00 seconds)
additionally exposed symbols:
machine,
vlan1,
start_all, test_script, machines, vlans, driver, log, os, create_machine, subtest, run_tests, join_all, retry, serial_stdout_off, serial_stdout_on, polling_condition, Machine
>>> machine.shell_interact()
machine: waiting for the VM to finish booting
machine: starting vm
machine: QEMU running (pid 146286)
machine # [ 0.000000] Linux version 6.6.48 (nixbld@localhost) (gcc (GCC) 13.3.0, GNU ld (GNU Binutils) 2.42) #1-NixOS SMP PREEMPT_DYNAMIC Thu Aug 29 15:33:59 UTC 2024
machine # [ 0.000000] Command line: console=ttyS0 panic=1 boot.panic_on_fail clocksource=acpi_pm loglevel=7 net.ifnames=0 init=/nix/store/0a52bbvxr5p7xijbbk17qqlk8xm4790y-nixos-system-machine-test/init regInfo=/nix/store/3sh5nl75bnj1jg87p5gcrdzs0lk154ma-closure-info/registration console=ttyS0
machine # [ 0.000000] BIOS-provided physical RAM map:
...
... more systemd messages
...
machine # [ 6.135577] dhcpcd[679]: DUID 00:01:00:01:2e:a2:74:e6:52:54:00:12:34:56
machine # [ 6.142785] systemd[1]: Finished Kernel Auditing.
machine: Guest shell says: b'Spawning backdoor root shell...\n'
machine: connected to guest root shell
machine: (connecting took 6.61 seconds)
(finished: waiting for the VM to finish booting, in 6.99 seconds)
machine: Terminal is ready (there is no initial prompt):
machine # [ 6.265451] 8021q: 802.1Q VLAN Support v1.8
machine # [ 6.186797] nsncd[669]: Oct 16 13:11:55.010 INFO started, config: Config { ignored_request_types: {}, worker_count: 8, handoff_timeout: 3s }, path: "/var/run/nscd/socket"
...
... more systemd messages
...
machine # [ 12.376900] systemd[1]: Reached target Host and Network Name Lookups.
machine # [ 12.379265] systemd[1]: Reached target User and Group Name Lookups.
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
fd0 2:0 1 4K 0 disk
sr0 11:0 1 1024M 0 rom
vda 253:0 0 1G 0 disk /
vdb 253:16 0 4G 0 disk
├─vdb1 253:17 0 500M 0 part
└─vdb2 253:18 0 3.5G 0 part
```
You can find some additional details in
[the NixOS manual's section on interactive testing](https://nixos.org/manual/nixos/stable/#sec-running-nixos-tests-interactively).
## Running all tests at once
If you have a bit of experience, you might be inclined to run `nix flake check`
to run all tests at once. However, we instead recommend using
[nix-fast-build](https://github.com/Mic92/nix-fast-build). The reason for this
is that each individual test takes a while to run, but only uses <=4GiB of RAM
and a limited amount of CPU resources. This means they can easily be evaluated
and run in parallel to save time, but `nix` doesn't to that, so a full test run
takes >40 minutes on a mid-range system. With `nix-fast-build` you can scale up
the number of workers depending on your system's capabilities. It also utilizes
[`nix-output-monitor`](https://github.com/maralorn/nix-output-monitor) to give
you a progress indicator during the build process as well. For example, on a
machine with 16GB of RAM, this gives you a 2x speed up without clogging your
system:
```sh
nix shell nixpkgs#nix-fast-build
nix-fast-build --no-link -j 2 --eval-workers 2 --flake .#checks
```
You can try higher numbers if you want to. Be careful with scaling up
`--eval-workers`, each of these will use 100% of a CPU core and they don't leave
any time for hyperthreading, so 4 workers will max out a a CPU with 4 cores and
8 threads, potentially rendering your system unresponsive! `-j` is less
dangerous to scale up, but you probably don't want to go higher than
`(<ram in your system> - 4GB)/4GB` to prevent excessive swap usage, which will
would slow down the test VMs to a crawl.

View File

@ -1,173 +0,0 @@
# 2023-07-09 121df48
Changes:
- BTRFS subvolumes are mounted if and only their `mountpoint` is set.
Especially, if you have specific mount options for a subvolume (through
`mountOptions`), make sure to set `mountpoint` otherwise the subvolume will not
be mounted.
This change allows more flexibility when using BTRFS, giving access to its
volume management functionality.
It allows layouts as the following:
```nix
content = {
type = "btrfs";
# BTRFS partition is not mounted as it doesn't set a mountpoint explicitly
subvolumes = {
# This subvolume will not be mounted
"SYSTEM" = { };
# mounted as "/"
"SYSTEM/rootfs" = {
mountpoint = "/";
};
# mounted as "/nix"
"SYSTEM/nix" = {
mountOptions = [ "compress=zstd" "noatime" ];
mountpoint = "/nix";
};
# This subvolume will not be mounted
"DATA" = { };
# mounted as "/home"
"DATA/home" = {
mountOptions = [ "compress=zstd" ];
mountpoint = "/home";
};
# mounted as "/var/www"
"DATA/www" = {
mountpoint = "/var/www";
};
};
};
```
corresponding to the following BTRFS layout:
```
BTRFS partition # not mounted
|
|-SYSTEM # not mounted
| |-rootfs # mounted as "/"
| |-nix # mounted as "/nix"
|
|-DATA # not mounted
|-home # mounted as "/home"
|-www # mounted as "/var/www"
```
# 2023-04-07 7d70009
Changes:
- ZFS datasets have been split into two types: `zfs_fs` and `zfs_volume`.
- The `zfs_type` attribute has been removed.
- The size attribute is now only available for `zfs_volume`.
Updated example/zfs.nix file:
```nix
{
datasets = {
zfs_fs = {
type = "zfs_fs";
mountpoint = "/zfs_fs";
options."com.sun:auto-snapshot" = "true";
};
zfs_unmounted_fs = {
type = "zfs_fs";
options.mountpoint = "none";
};
zfs_legacy_fs = {
type = "zfs_fs";
options.mountpoint = "legacy";
mountpoint = "/zfs_legacy_fs";
};
zfs_testvolume = {
type = "zfs_volume";
size = "10M";
content = {
type = "filesystem";
# ...
}
```
Note: The `zfs_type` attribute has been replaced with a type attribute for each
dataset, and the `size` attribute is only available for `zfs_volume`. These
changes have been reflected in the `example/zfs.nix` file.
# 2023-04-07 654ecb3
The `lvm_lv` type is always part of an `lvm_vg` and it is no longer necessary to
specify the type.
This means that if you were using the `lvm_lv` type in your code, you should
remove it. For example, if you were defining an `lvm_lv` type like this:
```nix
{
type = "lvm_lv";
size = "10G";
# ...
}
```
You should now define it like this:
```nix
{
size = "10G";
# ...
}
```
Note that the `type` field is no longer necessary and should be removed from
your code.
# 2023-04-07 d6f062e
Partition types are now always part of a table and cannot be specified
individually anymore. This change makes the library more consistent and easier
to use.
Example of how to change code:
Before:
```nix
{
type = "partition";
name = "ESP";
start = "1MiB";
end = "100MiB";
part-type = "primary";
}
```
After:
```nix
{
name = "ESP";
start = "1MiB";
end = "100MiB";
part-type = "primary";
}
```
Note that the `type` field is no longer necessary and should be removed from
your code.
# 2023-03-22 2624af6
disk config now needs to be inside a disko.devices attrset always
# 2023-03-22 0577409
the extraArgs option in the luks type was renamed to extraFormatArgs
# 2023-02-14 6d630b8
btrfs, `btrfs_subvol` filesystem and `lvm_lv` extraArgs are now lists

View File

@ -1,34 +0,0 @@
{
disko.devices = {
disk = {
main = {
device = "/dev/disk/by-path/pci-0000:02:00.0-nvme-1";
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
end = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
name = "root";
end = "-0";
content = {
type = "filesystem";
format = "bcachefs";
mountpoint = "/";
};
};
};
};
};
};
};
}

View File

@ -1,89 +0,0 @@
{
disko.devices = {
disk = {
one = {
type = "disk";
device = "/dev/sda";
content = {
type = "gpt";
partitions = {
BOOT = {
size = "1M";
type = "EF02"; # for grub MBR
};
ESP = {
size = "500M";
type = "EF00";
content = {
type = "mdraid";
name = "boot";
};
};
mdadm = {
size = "100%";
content = {
type = "mdraid";
name = "raid1";
};
};
};
};
};
two = {
type = "disk";
device = "/dev/sdb";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02"; # for grub MBR
};
ESP = {
size = "500M";
type = "EF00";
content = {
type = "mdraid";
name = "boot";
};
};
mdadm = {
size = "100%";
content = {
type = "mdraid";
name = "raid1";
};
};
};
};
};
};
mdadm = {
boot = {
type = "mdadm";
level = 1;
metadata = "1.0";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
raid1 = {
type = "mdadm";
level = 1;
content = {
type = "gpt";
partitions.primary = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
}

View File

@ -1,38 +0,0 @@
{
disko.devices = {
disk = {
main = {
type = "disk";
device = "/dev/disk/by-diskseq/1";
content = {
type = "gpt";
partitions = {
ESP = {
priority = 1;
name = "ESP";
start = "1M";
end = "128M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "btrfs";
extraArgs = [ "-f" ]; # Override existing partition
mountpoint = "/";
mountOptions = [ "compress=zstd" "noatime" ];
};
};
};
};
};
};
};
}

View File

@ -1,77 +0,0 @@
{
disko.devices = {
disk = {
main = {
type = "disk";
device = "/dev/disk/by-diskseq/1";
content = {
type = "gpt";
partitions = {
ESP = {
priority = 1;
name = "ESP";
start = "1M";
end = "128M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "btrfs";
extraArgs = [ "-f" ]; # Override existing partition
# Subvolumes must set a mountpoint in order to be mounted,
# unless their parent is mounted
subvolumes = {
# Subvolume name is different from mountpoint
"/rootfs" = {
mountpoint = "/";
};
# Subvolume name is the same as the mountpoint
"/home" = {
mountOptions = [ "compress=zstd" ];
mountpoint = "/home";
};
# Sub(sub)volume doesn't need a mountpoint as its parent is mounted
"/home/user" = { };
# Parent is not mounted so the mountpoint must be set
"/nix" = {
mountOptions = [ "compress=zstd" "noatime" ];
mountpoint = "/nix";
};
# This subvolume will be created but not mounted
"/test" = { };
# Subvolume for the swapfile
"/swap" = {
mountpoint = "/.swapvol";
swap = {
swapfile.size = "20M";
swapfile2.size = "20M";
swapfile2.path = "rel-path";
};
};
};
mountpoint = "/partition-root";
swap = {
swapfile = {
size = "20M";
};
swapfile1 = {
size = "20M";
};
};
};
};
};
};
};
};
};
}

View File

@ -1,192 +0,0 @@
{
disko.devices = {
disk = {
disk0 = {
type = "disk";
device = "/dev/disk/by-id/ata-QEMU_HARDDISK_QM00001";
content = {
type = "gpt";
partitions = {
ESP = {
start = "1M";
end = "128M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
};
};
};
disk1 = {
type = "disk";
device = "/dev/disk/by-id/ata-QEMU_HARDDISK_QM00002";
content = {
type = "gpt";
partitions = {
luks = {
start = "1M";
size = "100%";
content = {
type = "luks";
name = "crypted1";
settings.keyFile = "/tmp/secret.key";
additionalKeyFiles = [ "/tmp/additionalSecret.key" ];
extraFormatArgs = [
"--iter-time 1" # insecure but fast for tests
];
content = {
type = "lvm_pv";
vg = "pool";
};
};
};
};
};
};
disk2 = {
type = "disk";
device = "/dev/disk/by-id/ata-QEMU_HARDDISK_QM00003";
content = {
type = "gpt";
partitions = {
luks = {
start = "1M";
size = "100%";
content = {
type = "luks";
name = "crypted2";
settings = {
keyFile = "/tmp/secret.key";
keyFileSize = 8;
keyFileOffset = 2;
};
extraFormatArgs = [
"--iter-time 1" # insecure but fast for tests
];
content = {
type = "lvm_pv";
vg = "pool";
};
};
};
};
};
};
};
mdadm = {
raid1 = {
type = "mdadm";
level = 1;
content = {
type = "gpt";
partitions = {
bla = {
start = "1M";
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/ext4_mdadm_lvm";
};
};
};
};
};
};
lvm_vg = {
pool = {
type = "lvm_vg";
lvs = {
root = {
size = "10M";
lvm_type = "mirror";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/ext4_on_lvm";
mountOptions = [
"defaults"
];
postMountHook = ''
touch /mnt/ext4_on_lvm/file-from-postMountHook
'';
};
};
raid1 = {
size = "30M";
lvm_type = "raid0";
content = {
type = "mdraid";
name = "raid1";
};
};
raid2 = {
size = "30M";
lvm_type = "raid0";
content = {
type = "mdraid";
name = "raid1";
};
};
zfs1 = {
size = "128M";
lvm_type = "raid0";
content = {
type = "zfs";
pool = "zroot";
};
};
zfs2 = {
size = "128M";
lvm_type = "raid0";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
zpool = {
zroot = {
type = "zpool";
mode = "mirror";
rootFsOptions = {
compression = "zstd";
"com.sun:auto-snapshot" = "false";
};
mountpoint = "/";
datasets = {
zfs_fs = {
type = "zfs_fs";
mountpoint = "/zfs_fs";
options."com.sun:auto-snapshot" = "true";
};
zfs_unmounted_fs = {
type = "zfs_fs";
options.mountpoint = "none";
};
zfs_legacy_fs = {
type = "zfs_fs";
options.mountpoint = "legacy";
mountpoint = "/zfs_legacy_fs";
};
zfs_testvolume = {
type = "zfs_volume";
size = "10M";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/ext4onzfs";
};
};
};
};
};
};
}

View File

@ -1,41 +0,0 @@
{
disko.devices = {
disk = {
main = {
device = "/dev/disk/by-path/pci-0000:02:00.0-nvme-1";
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
end = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
name = "root";
end = "-0";
content = {
type = "filesystem";
format = "f2fs";
mountpoint = "/";
extraArgs = [
"-O"
"extra_attr,inode_checksum,sb_checksum,compression"
];
mountOptions = [
"compress_algorithm=zstd:6,compress_chksum,atgc,gc_merge,lazytime,nodiscard"
];
};
};
};
};
};
};
};
}

View File

@ -1,28 +0,0 @@
# Example to create a bios compatible gpt partition
{
disko.devices = {
disk = {
main = {
device = "/dev/vdb";
type = "disk";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02"; # for grub MBR
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}

View File

@ -1,48 +0,0 @@
{
disko.devices = {
disk = {
vdb = {
device = "/dev/disk/by-id/some-disk-id";
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
type = "EF00";
size = "100M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
"name with spaces" = {
size = "100M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/name with spaces";
};
};
"name^with\\some@special#chars" = {
size = "100M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/name^with\\some@special#chars";
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}

View File

@ -1,36 +0,0 @@
# Example to create a GPT partition but doesn't format it
{
disko.devices = {
disk = {
main = {
device = "/dev/vdb";
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
type = "EF00";
size = "100M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
empty = {
size = "1G";
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}

View File

@ -1,48 +0,0 @@
{
disko.devices = {
disk = {
main = {
type = "disk";
device = "/dev/sdb";
content = {
type = "gpt";
efiGptPartitionFirst = false;
partitions = {
TOW-BOOT-FI = {
priority = 1;
type = "EF00";
size = "32M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = null;
};
hybrid = {
mbrPartitionType = "0x0c";
mbrBootableFlag = false;
};
};
ESP = {
type = "EF00";
size = "512M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}

View File

@ -1,44 +0,0 @@
{
disko.devices = {
disk.main = {
device = "/dev/disk/by-id/ata-Samsung_SSD_860_EVO_500GB_S3Z1NB0K303456L";
type = "disk";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02"; # for grub MBR
};
ESP = {
name = "ESP";
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
nix = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/nix";
};
};
};
};
};
nodev."/" = {
fsType = "tmpfs";
mountOptions = [
"size=2G"
"defaults"
"mode=755"
];
};
};
}

View File

@ -1,37 +0,0 @@
{
disko.devices = {
disk = {
main = {
type = "disk";
device = "/dev/disk/by-id/ata-Samsung_SSD_850_EVO_250GB_S21PNXAGB12345";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02"; # for grub MBR
};
ESP = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}

View File

@ -1,50 +0,0 @@
{
disko.devices = {
disk = {
vdb = {
device = "/dev/sda";
type = "disk";
content = {
type = "table";
format = "gpt";
partitions = [
{
name = "ESP";
start = "1MiB";
end = "100MiB";
bootable = true;
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
}
{
name = "name with spaces";
start = "100MiB";
end = "200MiB";
bootable = true;
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/name_with_spaces";
};
}
{
name = "root";
start = "200MiB";
end = "100%";
part-type = "primary";
bootable = true;
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
}
];
};
};
};
};
}

View File

@ -1,40 +0,0 @@
{
disko.devices = {
disk = {
main = {
device = "/dev/sda";
type = "disk";
content = {
type = "table";
format = "gpt";
partitions = [
{
name = "ESP";
start = "1M";
end = "500M";
bootable = true;
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
}
{
name = "root";
start = "500M";
end = "100%";
part-type = "primary";
bootable = true;
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
}
];
};
};
};
};
}

View File

@ -1,34 +0,0 @@
{
disko.devices = {
disk = {
main = {
device = "/dev/disk/by-id/some-disk-id";
name = "this-is-some-super-long-name-to-test-what-happens-when-the-name-is-too-long";
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
type = "EF00";
size = "500M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}

View File

@ -1,78 +0,0 @@
{
disko.devices = {
disk = {
# Devices will be mounted and formatted in alphabetical order, and btrfs can only mount raids
# when all devices are present. So we define an "empty" luks device on the first disk,
# and the actual btrfs raid on the second disk, and the name of these entries matters!
disk1 = {
type = "disk";
device = "/dev/sda";
content = {
type = "gpt";
partitions = {
ESP = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
crypt_p1 = {
size = "100%";
content = {
type = "luks";
name = "p1"; # device-mapper name when decrypted
# Remove settings.keyFile if you want to use interactive password entry
settings = {
allowDiscards = true;
keyFile = "/tmp/secret.key";
};
};
};
};
};
};
disk2 = {
type = "disk";
device = "/dev/sdb";
content = {
type = "gpt";
partitions = {
crypt_p2 = {
size = "100%";
content = {
type = "luks";
name = "p2";
# Remove settings.keyFile if you want to use interactive password entry
settings = {
allowDiscards = true;
keyFile = "/tmp/secret.key"; # Same key for both devices
};
content = {
type = "btrfs";
extraArgs = [
"-d raid1"
"/dev/mapper/p1" # Use decrypted mapped device, same name as defined in disk1
];
subvolumes = {
"/root" = {
mountpoint = "/";
mountOptions = [
"rw"
"relatime"
"ssd"
];
};
};
};
};
};
};
};
};
};
};
}

View File

@ -1,61 +0,0 @@
{
disko.devices = {
disk = {
main = {
type = "disk";
device = "/dev/vdb";
content = {
type = "gpt";
partitions = {
ESP = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted";
# disable settings.keyFile if you want to use interactive password entry
#passwordFile = "/tmp/secret.key"; # Interactive
settings = {
allowDiscards = true;
keyFile = "/tmp/secret.key";
};
additionalKeyFiles = [ "/tmp/additionalSecret.key" ];
content = {
type = "btrfs";
extraArgs = [ "-f" ];
subvolumes = {
"/root" = {
mountpoint = "/";
mountOptions = [ "compress=zstd" "noatime" ];
};
"/home" = {
mountpoint = "/home";
mountOptions = [ "compress=zstd" "noatime" ];
};
"/nix" = {
mountpoint = "/nix";
mountOptions = [ "compress=zstd" "noatime" ];
};
"/swap" = {
mountpoint = "/.swapvol";
swap.swapfile.size = "20M";
};
};
};
};
};
};
};
};
};
};
}

View File

@ -1,39 +0,0 @@
{
disko.devices = {
disk = {
main = {
type = "disk";
device = "/dev/vdb";
content = {
type = "gpt";
partitions = {
ESP = {
size = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted";
settings.allowDiscards = true;
passwordFile = "/tmp/secret.key";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
};
}

View File

@ -1,73 +0,0 @@
{
disko.devices = {
disk = {
main = {
type = "disk";
device = "/dev/vdb";
content = {
type = "gpt";
partitions = {
ESP = {
size = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
luks = {
size = "100%";
content = {
type = "luks";
name = "crypted";
extraOpenArgs = [ ];
settings = {
# if you want to use the key for interactive login be sure there is no trailing newline
# for example use `echo -n "password" > /tmp/secret.key`
keyFile = "/tmp/secret.key";
allowDiscards = true;
};
additionalKeyFiles = [ "/tmp/additionalSecret.key" ];
content = {
type = "lvm_pv";
vg = "pool";
};
};
};
};
};
};
};
lvm_vg = {
pool = {
type = "lvm_vg";
lvs = {
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
mountOptions = [
"defaults"
];
};
};
home = {
size = "10M";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/home";
};
};
raw = {
size = "10M";
};
};
};
};
};
}

View File

@ -1,58 +0,0 @@
{ lib, ... }:
{
disko.devices.disk = lib.genAttrs [ "a" "b" ] (name: {
type = "disk";
device = "/dev/sd${name}";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02"; # for grub MBR
};
ESP = {
size = "500M";
type = "EF00";
content = {
type = "mdraid";
name = "boot";
};
};
mdadm = {
size = "100%";
content = {
type = "mdraid";
name = "raid1";
};
};
};
};
});
disko.devices.mdadm = {
boot = {
type = "mdadm";
level = 1;
metadata = "1.0";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
raid1 = {
type = "mdadm";
level = 1;
content = {
type = "luks";
name = "crypted";
settings.keyFile = "/tmp/secret.key";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
}

View File

@ -1,94 +0,0 @@
{
disko.devices = {
disk = {
one = {
type = "disk";
device = "/dev/sda";
content = {
type = "gpt";
partitions = {
boot = {
size = "500M";
type = "EF00";
content = {
type = "mdraid";
name = "boot";
};
};
primary = {
size = "100%";
content = {
type = "lvm_pv";
vg = "pool";
};
};
};
};
};
two = {
type = "disk";
device = "/dev/sdb";
content = {
type = "gpt";
partitions = {
boot = {
size = "500M";
type = "EF00";
content = {
type = "mdraid";
name = "boot";
};
};
primary = {
size = "100%";
content = {
type = "lvm_pv";
vg = "pool";
};
};
};
};
};
};
mdadm = {
boot = {
type = "mdadm";
level = 1;
metadata = "1.0";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
};
lvm_vg = {
pool = {
type = "lvm_vg";
lvs = {
root = {
size = "100M";
lvm_type = "mirror";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
mountOptions = [
"defaults"
];
};
};
home = {
size = "10M";
lvm_type = "raid0";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/home";
};
};
};
};
};
};
}

View File

@ -1,64 +0,0 @@
{
disko.devices = {
disk = {
one = {
type = "disk";
device = "/dev/disk/by-id/ata-VMware_Virtual_SATA_CDRW_Drive_00000000000000000001";
content = {
type = "gpt";
partitions = {
boot = {
size = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
primary = {
size = "100%";
content = {
type = "lvm_pv";
vg = "pool";
};
};
};
};
};
};
lvm_vg = {
pool = {
type = "lvm_vg";
lvs = {
aaa = {
size = "1M";
};
zzz = {
size = "1M";
};
root = {
size = "100M";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
mountOptions = [
"defaults"
];
};
};
home = {
size = "100%FREE";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/home";
};
};
};
};
};
};
}

View File

@ -1,69 +0,0 @@
{
disko.devices = {
disk = {
main = {
type = "disk";
device = "/dev/vdb";
content = {
type = "gpt";
partitions = {
ESP = {
size = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
primary = {
size = "100%";
content = {
type = "lvm_pv";
vg = "mainpool";
};
};
};
};
};
};
lvm_vg = {
mainpool = {
type = "lvm_vg";
lvs = {
thinpool = {
size = "100M";
lvm_type = "thin-pool";
};
root = {
size = "10M";
lvm_type = "thinlv";
pool = "thinpool";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
mountOptions = [
"defaults"
];
};
};
home = {
size = "10M";
lvm_type = "thinlv";
pool = "thinpool";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/home";
};
};
raw = {
size = "10M";
};
};
};
};
};
}

View File

@ -1,65 +0,0 @@
{
disko.devices = {
disk = {
disk1 = {
type = "disk";
device = "/dev/my-disk";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02"; # for grub MBR
};
mdadm = {
size = "100%";
content = {
type = "mdraid";
name = "raid0";
};
};
};
};
};
disk2 = {
type = "disk";
device = "/dev/my-disk2";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02"; # for grub MBR
};
mdadm = {
size = "100%";
content = {
type = "mdraid";
name = "raid0";
};
};
};
};
};
};
mdadm = {
raid0 = {
type = "mdadm";
level = 0;
content = {
type = "gpt";
partitions = {
primary = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}

View File

@ -1,65 +0,0 @@
{
disko.devices = {
disk = {
disk1 = {
type = "disk";
device = "/dev/my-disk";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02"; # for grub MBR
};
mdadm = {
size = "100%";
content = {
type = "mdraid";
name = "raid1";
};
};
};
};
};
disk2 = {
type = "disk";
device = "/dev/my-disk2";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02"; # for grub MBR
};
mdadm = {
size = "100%";
content = {
type = "mdraid";
name = "raid1";
};
};
};
};
};
};
mdadm = {
raid1 = {
type = "mdadm";
level = 1;
content = {
type = "gpt";
partitions = {
primary = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}

View File

@ -1,40 +0,0 @@
{
disko.devices = {
disk = {
disk0 = {
device = "/dev/vda";
type = "disk";
content = {
type = "gpt";
partitions = {
nix = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/a";
};
};
};
};
};
disk1 = {
device = "/dev/vdb";
type = "disk";
content = {
type = "gpt";
partitions = {
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/b";
};
};
};
};
};
};
};
}

View File

@ -1,23 +0,0 @@
{
disko.devices = {
disk = {
disk0 = {
device = "/dev/disk/by-id/ata-disk0";
type = "disk";
content = {
type = "gpt";
partitions = {
nix = {
end = "-10M";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}

View File

@ -1,109 +0,0 @@
{
disko.devices = {
disk = {
x = {
type = "disk";
device = "/dev/sdx";
content = {
type = "gpt";
partitions = {
ESP = {
size = "64M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
y = {
type = "disk";
device = "/dev/sdy";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "storage";
};
};
};
};
};
z = {
type = "disk";
device = "/dev/sdz";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "storage";
};
};
};
};
};
a = {
type = "disk";
device = "/dev/sda";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "storage2";
};
};
};
};
};
};
zpool = {
storage = {
type = "zpool";
mode = "mirror";
mountpoint = "/storage";
datasets = {
dataset = {
type = "zfs_fs";
mountpoint = "/storage/dataset";
};
};
};
storage2 = {
type = "zpool";
mountpoint = "/storage2";
rootFsOptions = {
canmount = "off";
};
datasets = {
dataset = {
type = "zfs_fs";
mountpoint = "/storage2/dataset";
};
};
};
};
};
}

View File

@ -1,33 +0,0 @@
{
disko.devices = {
disk = {
main = {
device = "/dev/disk/by-id/some-disk-id";
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
type = "EF00";
size = "500M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
};
}

View File

@ -1,54 +0,0 @@
{ pkgs
, lib
, ...
}:
let
# We just import from the repository for testing here:
disko = import ../../. {
inherit lib;
};
# In your own system use something like this:
#import (builtins.fetchGit {
# url = "https://github.com/nix-community/disko";
# ref = "master";
#}) {
# inherit lib;
#};
cfg.disko.devices = {
disk = {
sda = {
device = "/dev/sda";
type = "disk";
content = {
type = "table";
format = "msdos";
partitions = [
{
name = "root";
part-type = "primary";
start = "1M";
end = "100%";
bootable = true;
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
}
];
};
};
};
};
in
{
imports = [
(disko.config cfg)
];
boot.loader.grub.devices = [ "/dev/sda" ];
system.stateVersion = "22.05";
environment.systemPackages = with pkgs; [
(pkgs.writeScriptBin "tsp-create" (disko.create cfg))
(pkgs.writeScriptBin "tsp-mount" (disko.mount cfg))
];
}

View File

@ -1,49 +0,0 @@
{
disko.devices = {
disk = {
main = {
device = "/dev/vdb";
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
size = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
end = "-1G";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
encryptedSwap = {
size = "10M";
content = {
type = "swap";
randomEncryption = true;
priority = 100; # prefer to encrypt as long as we have space for it
};
};
plainSwap = {
size = "100%";
content = {
type = "swap";
discardPolicy = "both";
resumeDevice = true; # resume from hiberation from this device
};
};
};
};
};
};
};
}

View File

@ -1,41 +0,0 @@
{
disko.devices = {
disk = {
main = {
device = "/dev/vdb";
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
size = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
};
nodev = {
"/tmp" = {
fsType = "tmpfs";
mountOptions = [
"size=200M"
];
};
};
};
}

View File

@ -1,27 +0,0 @@
# Example to create a bios compatible gpt partition
{ disks ? [ "/dev/vdb" ], lib, ... }: {
disko.devices = {
disk = lib.genAttrs disks (device: {
name = lib.replaceStrings [ "/" ] [ "_" ] device;
device = device;
type = "disk";
content = {
type = "gpt";
partitions = {
boot = {
size = "1M";
type = "EF02";
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
});
};
}

View File

@ -1,34 +0,0 @@
{
disko.devices = {
disk = {
main = {
device = "/dev/disk/by-id/some-disk-id";
type = "disk";
content = {
type = "gpt";
partitions = {
ESP = {
type = "EF00";
size = "500M";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
root = {
size = "100%";
content = {
type = "filesystem";
format = "xfs";
mountpoint = "/";
mountOptions = [ "defaults" "pquota" ];
};
};
};
};
};
};
};
}

View File

@ -1,58 +0,0 @@
# systemd will mount an ext4 filesystem at / and zfs will mount the dataset underneath it
{
disko.devices = {
disk = {
disk1 = {
type = "disk";
device = "/dev/vdb";
content = {
type = "gpt";
partitions = {
ESP = {
size = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
primary = {
size = "100%";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
};
};
};
};
};
disk2 = {
type = "disk";
device = "/dev/vdc";
content = {
type = "zfs";
pool = "zroot";
};
};
};
zpool = {
zroot = {
type = "zpool";
datasets = {
"root" = {
type = "zfs_fs";
options.mountpoint = "none";
};
"root/zfs_fs" = {
type = "zfs_fs";
mountpoint = "/zfs_fs";
options."com.sun:auto-snapshot" = "true";
};
};
};
};
};
}

View File

@ -1,305 +0,0 @@
{
disko.devices = {
disk = {
data1 = {
type = "disk";
device = "/dev/vda";
content = {
type = "gpt";
partitions = {
ESP = {
size = "64M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
data2 = {
type = "disk";
device = "/dev/vdb";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
data3 = {
type = "disk";
device = "/dev/vdc";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
spare = {
type = "disk";
device = "/dev/vdd";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
log1 = {
type = "disk";
device = "/dev/vde";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
log2 = {
type = "disk";
device = "/dev/vdf";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
log3 = {
type = "disk";
device = "/dev/vdg";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
dedup1 = {
type = "disk";
device = "/dev/vdh";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
dedup2 = {
type = "disk";
device = "/dev/vdi";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
dedup3 = {
type = "disk";
device = "/dev/vdj";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
special1 = {
type = "disk";
device = "/dev/vdk";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
special2 = {
type = "disk";
device = "/dev/vdl";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
special3 = {
type = "disk";
device = "/dev/vdm";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
cache = {
type = "disk";
device = "/dev/vdn";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
};
zpool = {
zroot = {
type = "zpool";
mode = {
topology = {
type = "topology";
vdev = [
{
# This syntax expects a disk called 'data3' with a gpt partition called 'zfs'.
members = [ "data1" ];
# It's also possible to use the full path of the device or partition
# members = [ "/dev/disk/by-id/wwn-0x5000c500af8b2a14" ];
}
{
mode = "mirror";
members = [ "data2" "data3" ];
}
];
spare = [ "spare" ];
log = [
{
mode = "mirror";
members = [ "log1" "log2" ];
}
{
members = [ "log3" ];
}
];
dedup = [
{
mode = "mirror";
members = [ "dedup1" "dedup2" ];
}
{
members = [ "dedup3" ];
}
];
special = [
{
mode = "mirror";
members = [ "special1" "special2" ];
}
{
members = [ "special3" ];
}
];
cache = [ "cache" ];
};
};
rootFsOptions = {
compression = "zstd";
"com.sun:auto-snapshot" = "false";
};
mountpoint = "/";
datasets = {
# See examples/zfs.nix for more comprehensive usage.
zfs_fs = {
type = "zfs_fs";
mountpoint = "/zfs_fs";
options."com.sun:auto-snapshot" = "true";
};
};
};
};
};
}

View File

@ -1,119 +0,0 @@
{
disko.devices = {
disk = {
x = {
type = "disk";
device = "/dev/sdx";
content = {
type = "gpt";
partitions = {
ESP = {
size = "64M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
mountOptions = [ "umask=0077" ];
};
};
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
y = {
type = "disk";
device = "/dev/sdy";
content = {
type = "gpt";
partitions = {
zfs = {
size = "100%";
content = {
type = "zfs";
pool = "zroot";
};
};
};
};
};
};
zpool = {
zroot = {
type = "zpool";
mode = "mirror";
# Workaround: cannot import 'zroot': I/O error in disko tests
options.cachefile = "none";
rootFsOptions = {
compression = "zstd";
"com.sun:auto-snapshot" = "false";
};
mountpoint = "/";
postCreateHook = "zfs list -t snapshot -H -o name | grep -E '^zroot@blank$' || zfs snapshot zroot@blank";
datasets = {
zfs_fs = {
type = "zfs_fs";
mountpoint = "/zfs_fs";
options."com.sun:auto-snapshot" = "true";
};
zfs_unmounted_fs = {
type = "zfs_fs";
options.mountpoint = "none";
};
zfs_legacy_fs = {
type = "zfs_fs";
options.mountpoint = "legacy";
mountpoint = "/zfs_legacy_fs";
};
zfs_volume = {
type = "zfs_volume";
size = "10M";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/ext4onzfs";
};
};
zfs_encryptedvolume = {
type = "zfs_volume";
size = "10M";
options = {
encryption = "aes-256-gcm";
keyformat = "passphrase";
keylocation = "file:///tmp/secret.key";
};
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/ext4onzfsencrypted";
};
};
encrypted = {
type = "zfs_fs";
options = {
mountpoint = "none";
encryption = "aes-256-gcm";
keyformat = "passphrase";
keylocation = "file:///tmp/secret.key";
};
# use this to read the key during boot
# postCreateHook = ''
# zfs set keylocation="prompt" "zroot/$name";
# '';
};
"encrypted/test" = {
type = "zfs_fs";
mountpoint = "/zfs_crypted";
};
};
};
};
};
}

View File

@ -1,27 +0,0 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1734435836,
"narHash": "sha256-kMBQ5PRiFLagltK0sH+08aiNt3zGERC2297iB6vrvlU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4989a246d7a390a859852baddb1013f825435cee",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

View File

@ -1,155 +0,0 @@
{
description = "Disko - declarative disk partitioning";
# FIXME: in future we don't want lock here to give precedence to a USB live-installer's registry,
# but garnix currently does not allow this.
#inputs.nixpkgs.url = "nixpkgs";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
outputs = { self, nixpkgs, ... }:
let
lib = nixpkgs.lib;
supportedSystems = [
"x86_64-linux"
"i686-linux"
"aarch64-linux"
"riscv64-linux"
];
forAllSystems = lib.genAttrs supportedSystems;
versionInfo = import ./version.nix;
version = versionInfo.version + (lib.optionalString (!versionInfo.released) "-dirty");
diskoLib = import ./lib {
inherit (nixpkgs) lib;
};
in
{
nixosModules.default = self.nixosModules.disko; # convention
nixosModules.disko.imports = [ ./module.nix ];
lib = diskoLib;
packages = forAllSystems (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
disko = pkgs.callPackage ./package.nix { diskoVersion = version; };
# alias to make `nix run` more convenient
disko-install = self.packages.${system}.disko.overrideAttrs (_old: {
name = "disko-install";
});
default = self.packages.${system}.disko;
create-release = pkgs.callPackage ./scripts/create-release.nix { };
} // pkgs.lib.optionalAttrs (!pkgs.stdenv.buildPlatform.isRiscV64) {
disko-doc = pkgs.callPackage ./doc.nix { };
});
# TODO: disable bios-related tests on aarch64...
# Run checks: nix flake check -L
checks = forAllSystems (system:
let
pkgs = nixpkgs.legacyPackages.${system};
# FIXME: aarch64-linux seems to hang on boot
nixosTests = lib.optionalAttrs pkgs.stdenv.hostPlatform.isx86_64 (import ./tests {
inherit pkgs;
makeTest = import (pkgs.path + "/nixos/tests/make-test-python.nix");
eval-config = import (pkgs.path + "/nixos/lib/eval-config.nix");
});
disko-install = pkgs.callPackage ./tests/disko-install {
inherit self;
diskoVersion = version;
};
shellcheck = pkgs.runCommand "shellcheck" { nativeBuildInputs = [ pkgs.shellcheck ]; } ''
cd ${./.}
shellcheck disk-deactivate/disk-deactivate disko
touch $out
'';
jsonTypes = pkgs.writeTextFile { name = "jsonTypes"; text = (builtins.toJSON diskoLib.jsonTypes); };
in
# FIXME: aarch64-linux seems to hang on boot
lib.optionalAttrs pkgs.stdenv.hostPlatform.isx86_64 (nixosTests // { inherit disko-install; }) //
pkgs.lib.optionalAttrs (!pkgs.stdenv.buildPlatform.isRiscV64 && !pkgs.stdenv.hostPlatform.isx86_32) {
inherit shellcheck jsonTypes;
inherit (self.packages.${system}) disko-doc;
});
nixosConfigurations.testmachine = lib.nixosSystem {
system = "x86_64-linux";
modules = [
./tests/disko-install/configuration.nix
./example/hybrid.nix
./module.nix
];
};
formatter = forAllSystems (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
pkgs.writeShellApplication {
name = "format";
runtimeInputs = with pkgs; [
nixpkgs-fmt
deno
deadnix
];
text = ''
showUsage() {
cat <<EOF
Usage: $0 [OPTIONS] FILES...
-c, --check Only check formatting, do not modify files.
-h, --help Show this help message.
EOF
}
check=
files=()
parseArgs() {
while [[ $# -gt 0 ]]; do
case "$1" in
-h | --help)
showUsage
exit 0
;;
-c | --check)
check=1
;;
*)
files+=("$1")
;;
esac
shift
done
if [[ ''${#files[@]} -eq 0 ]]; then
files=(.)
fi
}
main() {
parseArgs "$@"
if [[ -z "$check" ]]; then
set -o xtrace
nixpkgs-fmt -- "''${files[@]}"
deno fmt -- "''${files[@]}"
deadnix --edit -- "''${files[@]}"
else
set -o xtrace
nixpkgs-fmt --check -- "''${files[@]}"
deno fmt --check -- "''${files[@]}"
deadnix -- "''${files[@]}"
fi
}
main "$@"
'';
}
);
};
}

View File

@ -1,69 +0,0 @@
{ flake
, flakeAttr
, diskMappings
, extraSystemConfig ? "{}"
, writeEfiBootEntries ? false
, rootMountPoint ? "/mnt"
,
}:
let
originalSystem = (builtins.getFlake "${flake}").nixosConfigurations."${flakeAttr}";
lib = originalSystem.pkgs.lib;
deviceName =
name:
if diskMappings ? ${name} then
diskMappings.${name}
else
throw "No device passed for disk '${name}'. Pass `--disk ${name} /dev/name` via commandline";
modifiedDisks = builtins.mapAttrs
(
name: value:
let
dev = deviceName name;
in
value
// {
device = dev;
content = value.content // {
device = dev;
};
}
)
originalSystem.config.disko.devices.disk;
# filter all nixos module internal attributes
cleanedDisks = lib.filterAttrsRecursive (n: _: !lib.hasPrefix "_" n) modifiedDisks;
diskoSystem = originalSystem.extendModules {
modules = [
{
disko.rootMountPoint = rootMountPoint;
disko.devices.disk = lib.mkVMOverride cleanedDisks;
}
];
};
installSystem = originalSystem.extendModules {
modules = [
(
{ lib, ... }:
{
boot.loader.efi.canTouchEfiVariables = lib.mkVMOverride writeEfiBootEntries;
boot.loader.grub.devices = lib.mkVMOverride diskoSystem.config.boot.loader.grub.devices;
imports = [
({ _file = "disko-install --system-config"; } // (builtins.fromJSON extraSystemConfig))
];
}
)
];
};
in
{
installToplevel = installSystem.config.system.build.toplevel;
closureInfo = installSystem.pkgs.closureInfo {
rootPaths = [ installSystem.config.system.build.toplevel ];
};
inherit (diskoSystem.config.system.build) formatScript mountScript diskoScript;
}

View File

@ -1,841 +0,0 @@
{ lib ? import <nixpkgs/lib>
, rootMountPoint ? "/mnt"
, makeTest ? import <nixpkgs/nixos/tests/make-test-python.nix>
, eval-config ? import <nixpkgs/nixos/lib/eval-config.nix>
}:
let
outputs = import ../default.nix { inherit lib diskoLib; };
diskoLib = {
testLib = import ./tests.nix { inherit lib makeTest eval-config; };
# like lib.types.oneOf but instead of a list takes an attrset
# uses the field "type" to find the correct type in the attrset
subType = { types, extraArgs ? { parent = { type = "rootNode"; name = "root"; }; } }: lib.mkOptionType {
name = "subType";
description = "one of ${lib.concatStringsSep "," (lib.attrNames types)}";
check = x: if x ? type then types.${x.type}.check x else throw "No type option set in:\n${lib.generators.toPretty {} x}";
merge = loc: lib.foldl'
(_res: def: types.${def.value.type}.merge loc [
# we add a dummy root parent node to render documentation
(lib.recursiveUpdate { value._module.args = extraArgs; } def)
])
{ };
nestedTypes = types;
};
# option for valid contents of partitions (basically like devices, but without tables)
_partitionTypes = { inherit (diskoLib.types) btrfs filesystem zfs mdraid luks lvm_pv swap; };
partitionType = extraArgs: lib.mkOption {
type = lib.types.nullOr (diskoLib.subType {
types = diskoLib._partitionTypes;
inherit extraArgs;
});
default = null;
description = "The type of partition";
};
# option for valid contents of devices
_deviceTypes = { inherit (diskoLib.types) table gpt btrfs filesystem zfs mdraid luks lvm_pv swap; };
deviceType = extraArgs: lib.mkOption {
type = lib.types.nullOr (diskoLib.subType {
types = diskoLib._deviceTypes;
inherit extraArgs;
});
default = null;
description = "The type of device";
};
/* deepMergeMap takes a function and a list of attrsets and deep merges them
deepMergeMap :: (AttrSet -> AttrSet ) -> [ AttrSet ] -> Attrset
Example:
deepMergeMap (x: x.t = "test") [ { x = { y = 1; z = 3; }; } { x = { bla = 234; }; } ]
=> { x = { y = 1; z = 3; bla = 234; t = "test"; }; }
*/
deepMergeMap = f: lib.foldr (attr: acc: (lib.recursiveUpdate acc (f attr))) { };
/* get a device and an index to get the matching device name
deviceNumbering :: str -> int -> str
Example:
deviceNumbering "/dev/sda" 3
=> "/dev/sda3"
deviceNumbering "/dev/disk/by-id/xxx" 2
=> "/dev/disk/by-id/xxx-part2"
*/
deviceNumbering = dev: index:
let inherit (lib) match; in
if match "/dev/([vs]|(xv)d).+" dev != null then
dev + toString index # /dev/{s,v,xv}da style
else if match "/dev/(disk|zvol)/.+" dev != null then
"${dev}-part${toString index}" # /dev/disk/by-id/xxx style, also used by zfs's zvolumes
else if match "/dev/((nvme|mmcblk).+|md/.*[[:digit:]])" dev != null then
"${dev}p${toString index}" # /dev/nvme0n1p1 style
else if match "/dev/md/.+" dev != null then
"${dev}${toString index}" # /dev/md/raid1 style
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
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
'';
/* Escape a string as required to be used in udev symlinks
The allowed characters are "0-9A-Za-z#+-.:=@_/", valid UTF-8 character sequences, and "\x00" hex encoding.
Everything else is escaped as "\xXX" where XX is the hex value of the character.
The source of truth for the list of allowed characters is the udev documentation:
https://www.freedesktop.org/software/systemd/man/latest/udev.html#SYMLINK1
This function is implemented as a best effort. It is not guaranteed to be 100% in line
with the udev implementation, and we hope that you're not crazy enough to try to break it.
hexEscapeUdevSymlink :: str -> str
Example:
hexEscapeUdevSymlink "Boot data partition"
=> "Boot\x20data\x20partition"
hexEscapeUdevSymlink "Even(crazier)par&titi^onName"
=> "Even\x28crazier\x29par\x26titi\x5EonName"
hexEscapeUdevSymlink "all0these@char#acters+_are-allow.ed"
=> "all0these@char#acters+_are-allow.ed"
*/
hexEscapeUdevSymlink =
let
allowedChars = "[0-9A-Za-z#+-.:=@_/]";
charToHex = c: lib.toHexString (lib.strings.charToInt c);
in
lib.stringAsChars
(c: if lib.match allowedChars c != null || c == "" then c else "\\x" + charToHex c);
/* get the index an item in a list
indexOf :: (a -> bool) -> [a] -> int -> int
Example:
indexOf (x: x == 2) [ 1 2 3 ] 0
=> 2
indexOf (x: x == "x") [ 1 2 3 ] 0
=> 0
*/
indexOf = f: list: fallback:
let
iter = index: list:
if list == [ ] then
fallback
else if f (lib.head list) then
index
else
iter (index + 1) (lib.tail list);
in
iter 1 list;
/* indent takes a multiline string and indents it by 2 spaces starting on the second line
indent :: str -> str
Example:
indent "test\nbla"
=> "test\n bla"
*/
indent = lib.replaceStrings [ "\n" ] [ "\n " ];
/* A nix option type representing a json datastructure, vendored from nixpkgs to avoid dependency on pkgs */
jsonType =
let
valueType = lib.types.nullOr
(lib.types.oneOf [
lib.types.bool
lib.types.int
lib.types.float
lib.types.str
lib.types.path
(lib.types.attrsOf valueType)
(lib.types.listOf valueType)
]) // {
description = "JSON value";
};
in
valueType;
/* Given a attrset of deviceDependencies and a devices attrset
returns a sorted list by deviceDependencies. aborts if a loop is found
sortDevicesByDependencies :: AttrSet -> AttrSet -> [ [ str str ] ]
*/
sortDevicesByDependencies = deviceDependencies: devices:
let
dependsOn = a: b:
lib.elem a (lib.attrByPath b [ ] deviceDependencies);
maybeSortedDevices = lib.toposort dependsOn (diskoLib.deviceList devices);
in
if (lib.hasAttr "cycle" maybeSortedDevices) then
abort "detected a cycle in your disk setup: ${maybeSortedDevices.cycle}"
else
maybeSortedDevices.result;
/* Takes a devices attrSet and returns it as a list
deviceList :: AttrSet -> [ [ str str ] ]
Example:
deviceList { zfs.pool1 = {}; zfs.pool2 = {}; mdadm.raid1 = {}; }
=> [ [ "zfs" "pool1" ] [ "zfs" "pool2" ] [ "mdadm" "raid1" ] ]
*/
deviceList = devices:
lib.concatLists (lib.mapAttrsToList (n: v: (map (x: [ n x ]) (lib.attrNames v))) devices);
/* Takes either a string or null and returns the string or an empty string
maybeStr :: Either (str null) -> str
Example:
maybeStr null
=> ""
maybeSTr "hello world"
=> "hello world"
*/
maybeStr = x: lib.optionalString (x != null) x;
/* Takes a Submodules config and options argument and returns a serializable
subset of config variables as a shell script snippet.
*/
defineHookVariables = { options }:
let
sanitizeName = lib.replaceStrings [ "-" ] [ "_" ];
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"
|| n == "mode"
);
in
lib.toShellVars
(lib.mapAttrs'
(n: o: lib.nameValuePair (sanitizeName n) o.value)
(lib.filterAttrs isSerializable options));
mkHook = description: lib.mkOption {
inherit description;
type = lib.types.lines;
default = "";
};
mkSubType = module: lib.types.submodule [
module
{
options = {
preCreateHook = diskoLib.mkHook "shell commands to run before create";
postCreateHook = diskoLib.mkHook "shell commands to run after create";
preMountHook = diskoLib.mkHook "shell commands to run before mount";
postMountHook = diskoLib.mkHook "shell commands to run after mount";
preUnmountHook = diskoLib.mkHook "shell commands to run before unmount";
postUnmountHook = diskoLib.mkHook "shell commands to run after unmount";
};
config._module.args = {
inherit diskoLib rootMountPoint;
};
}
];
mkCreateOption = { config, options, default }@attrs:
lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.str;
default = ''
( # ${config.type} ${lib.concatMapStringsSep " " (n: toString (config.${n} or "")) ["name" "device" "format" "mountpoint"]} #
${diskoLib.indent (diskoLib.defineHookVariables { inherit options; })}
${diskoLib.indent config.preCreateHook}
${diskoLib.indent attrs.default}
${diskoLib.indent config.postCreateHook}
)
'';
description = "Creation script";
};
mkMountOption = { config, options, default }@attrs:
lib.mkOption {
internal = true;
readOnly = true;
type = diskoLib.jsonType;
default = lib.mapAttrsRecursive
(_name: value:
if builtins.isString value then ''
(
${diskoLib.indent (diskoLib.defineHookVariables { inherit options; })}
${diskoLib.indent config.preMountHook}
${diskoLib.indent value}
${diskoLib.indent config.postMountHook}
)
'' else value)
attrs.default;
description = "Mount script";
};
mkUnmountOption = { config, options, default }@attrs:
lib.mkOption {
internal = true;
readOnly = true;
type = diskoLib.jsonType;
default = lib.mapAttrsRecursive
(_name: value:
if builtins.isString value then ''
(
${diskoLib.indent (diskoLib.defineHookVariables { inherit options; })}
${diskoLib.indent config.preUnmountHook}
${diskoLib.indent value}
${diskoLib.indent config.postUnmountHook}
)
'' else value)
attrs.default;
description = "Unmount script";
};
/* Writer for optionally checking bash scripts before writing them to the store
writeCheckedBash :: AttrSet -> str -> str -> derivation
*/
writeCheckedBash = { pkgs, checked ? false, noDeps ? false }: pkgs.writers.makeScriptWriter {
interpreter = if noDeps then "/usr/bin/env bash" else "${pkgs.bash}/bin/bash";
check = lib.optionalString (checked && !pkgs.stdenv.hostPlatform.isRiscV64 && !pkgs.stdenv.hostPlatform.isx86_32) (pkgs.writeScript "check" ''
set -efu
# SC2054: our toShellVars function doesn't quote list elements with commas
# SC2034: We don't use all variables exported by hooks.
${pkgs.shellcheck}/bin/shellcheck -e SC2034,SC2054 "$1"
'');
};
/* Takes a disko device specification, returns an attrset with metadata
meta :: lib.types.devices -> AttrSet
*/
meta = toplevel: toplevel._meta;
/* Takes a disko device specification and returns a string which formats the disks
create :: lib.types.devices -> str
*/
create = toplevel: toplevel._create;
/* Takes a disko device specification and returns a string which mounts the disks
mount :: lib.types.devices -> str
*/
mount = toplevel: toplevel._mount;
/* takes a disko device specification and returns a string which unmounts, destroys all disks and then runs create and mount
zapCreateMount :: lib.types.devices -> str
*/
zapCreateMount = toplevel:
''
set -efux
${toplevel._disko}
'';
/* Takes a disko device specification and returns a nixos configuration
config :: lib.types.devices -> nixosConfig
*/
config = toplevel: toplevel._config;
/* Takes a disko device specification and returns a function to get the needed packages to format/mount the disks
packages :: lib.types.devices -> pkgs -> [ derivation ]
*/
packages = toplevel: toplevel._packages;
/* Checks whether nixpkgs is recent enough for vmTools to support the customQemu argument.
Returns false, which is technically incorrect, for a few commits on 2024-07-08, but we can't be more accurate.
Make sure to pass lib, not pkgs.lib! See https://github.com/nix-community/disko/issues/904
vmToolsSupportsCustomQemu :: final_lib -> bool
*/
vmToolsSupportsCustomQemu = final_lib: lib.versionAtLeast final_lib.version "24.11.20240709";
optionTypes = rec {
filename = lib.mkOptionType {
name = "filename";
check = lib.isString;
merge = lib.mergeOneOption;
description = "A filename";
};
absolute-pathname = lib.mkOptionType {
name = "absolute pathname";
check = x: lib.isString x && lib.substring 0 1 x == "/" && pathname.check x;
merge = lib.mergeOneOption;
description = "An absolute path";
};
pathname = lib.mkOptionType {
name = "pathname";
check = x:
with lib; let
# The filter is used to normalize paths, i.e. to remove duplicated and
# trailing slashes. It also removes leading slashes, thus we have to
# check for "/" explicitly below.
xs = filter (s: stringLength s > 0) (splitString "/" x);
in
isString x && (x == "/" || (length xs > 0 && all filename.check xs));
merge = lib.mergeOneOption;
description = "A path name";
};
};
/* topLevel type of the disko config, takes attrsets of disks, mdadms, zpools, nodevs, and lvm vgs.
*/
toplevel = lib.types.submodule (cfg:
let
devices = { inherit (cfg.config) disk mdadm zpool lvm_vg nodev; };
in
{
options = {
disk = lib.mkOption {
type = lib.types.attrsOf diskoLib.types.disk;
default = { };
description = "Block device";
};
mdadm = lib.mkOption {
type = lib.types.attrsOf diskoLib.types.mdadm;
default = { };
description = "mdadm device";
};
zpool = lib.mkOption {
type = lib.types.attrsOf diskoLib.types.zpool;
default = { };
description = "ZFS pool device";
};
lvm_vg = lib.mkOption {
type = lib.types.attrsOf diskoLib.types.lvm_vg;
default = { };
description = "LVM VG device";
};
nodev = lib.mkOption {
type = lib.types.attrsOf diskoLib.types.nodev;
default = { };
description = "A non-block device";
};
_meta = lib.mkOption {
internal = true;
description = ''
meta informationen generated by disko
currently used for building a dependency list so we know in which order to create the devices
'';
default = diskoLib.deepMergeMap (dev: dev._meta) (lib.flatten (map lib.attrValues (lib.attrValues devices)));
};
_packages = lib.mkOption {
internal = true;
description = ''
packages required by the disko configuration
coreutils is always included
'';
default = pkgs: with lib; unique ((flatten (map (dev: dev._pkgs pkgs) (flatten (map attrValues (attrValues devices))))) ++ [ pkgs.coreutils-full ]);
};
_scripts = lib.mkOption {
internal = true;
description = ''
The scripts generated by disko
'';
default = { pkgs, checked ? false }:
let
throwIfNoDisksDetected = _: v: if devices.disk == { } then throw "No disks defined, did you forget to import your disko config?" else v;
destroyDependencies = with pkgs; [
util-linux
e2fsprogs
mdadm
zfs
lvm2
bash
jq
gnused
gawk
coreutils-full
];
in
lib.mapAttrs throwIfNoDisksDetected {
destroy = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "/bin/disko-destroy" ''
export PATH=${lib.makeBinPath destroyDependencies}:$PATH
${cfg.config._destroy}
'';
format = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "/bin/disko-format" ''
export PATH=${lib.makeBinPath (cfg.config._packages pkgs)}:$PATH
${cfg.config._create}
'';
mount = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "/bin/disko-mount" ''
export PATH=${lib.makeBinPath (cfg.config._packages pkgs)}:$PATH
${cfg.config._mount}
'';
unmount = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "/bin/disko-unmount" ''
export PATH=${lib.makeBinPath (cfg.config._packages pkgs)}:$PATH
${cfg.config._unmount}
'';
formatMount = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "/bin/disko-format-mount" ''
export PATH=${lib.makeBinPath ((cfg.config._packages pkgs) ++ [ pkgs.bash ])}:$PATH
${cfg.config._formatMount}
'';
destroyFormatMount = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "/bin/disko-destroy-format-mount" ''
export PATH=${lib.makeBinPath ((cfg.config._packages pkgs) ++ [ pkgs.bash ] ++ destroyDependencies)}:$PATH
${cfg.config._destroyFormatMount}
'';
# These are useful to skip copying executables uploading a script to an in-memory installer
destroyNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "/bin/disko-destroy" ''
${cfg.config._destroy}
'';
formatNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "/bin/disko-format" ''
${cfg.config._create}
'';
mountNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "/bin/disko-mount" ''
${cfg.config._mount}
'';
unmountNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "/bin/disko-unmount" ''
${cfg.config._unmount}
'';
formatMountNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "/bin/disko-format-mount" ''
${cfg.config._formatMount}
'';
destroyFormatMountNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "/bin/disko-destroy-format-mount" ''
${cfg.config._destroyFormatMount}
'';
# Legacy scripts, to be removed in version 2.0.0
# They are generally less useful, because the scripts are directly written to their $out path instead of
# into the $out/bin directory, which makes them incompatible with `nix run`
# (see https://github.com/nix-community/disko/pull/78), `lib.buildEnv` and thus `environment.systemPackages`,
# `user.users.<name>.packages` and `home.packages`, see https://github.com/nix-community/disko/issues/454
destroyScript = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "disko-destroy" ''
export PATH=${lib.makeBinPath destroyDependencies}:$PATH
${cfg.config._legacyDestroy}
'';
formatScript = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "disko-format" ''
export PATH=${lib.makeBinPath (cfg.config._packages pkgs)}:$PATH
${cfg.config._create}
'';
mountScript = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "disko-mount" ''
export PATH=${lib.makeBinPath (cfg.config._packages pkgs)}:$PATH
${cfg.config._mount}
'';
diskoScript = (diskoLib.writeCheckedBash { inherit pkgs checked; }) "disko" ''
export PATH=${lib.makeBinPath ((cfg.config._packages pkgs) ++ [ pkgs.bash ] ++ destroyDependencies)}:$PATH
${cfg.config._disko}
'';
# These are useful to skip copying executables uploading a script to an in-memory installer
destroyScriptNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "disko-destroy" ''
${cfg.config._legacyDestroy}
'';
formatScriptNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "disko-format" ''
${cfg.config._create}
'';
mountScriptNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "disko-mount" ''
${cfg.config._mount}
'';
diskoScriptNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "disko" ''
${cfg.config._disko}
'';
};
};
_legacyDestroy = lib.mkOption {
internal = true;
type = lib.types.str;
description = ''
The script to unmount (& destroy) all devices defined by disko.devices
Does not ask for confirmation! Depracated in favor of _destroy
'';
default = ''
umount -Rv "${rootMountPoint}" || :
# shellcheck disable=SC2043
for dev in ${toString (lib.catAttrs "device" (lib.attrValues devices.disk))}; do
$BASH ${../disk-deactivate}/disk-deactivate "$dev"
done
'';
};
_destroy = lib.mkOption {
internal = true;
type = lib.types.str;
description = ''
The script to unmount (& destroy) all devices defined by disko.devices
'';
default =
let
selectedDisks = lib.escapeShellArgs (lib.catAttrs "device" (lib.attrValues devices.disk));
in
''
if [ "$1" != "--yes-wipe-all-disks" ]; then
echo "WARNING: This will destroy all data on the disks defined in disko.devices, which are:"
echo
# shellcheck disable=SC2043,2041
for dev in ${selectedDisks}; do
echo " - $dev"
done
echo
echo " (If you want to skip this dialogue, pass --yes-wipe-all-disks)"
echo
echo "Are you sure you want to wipe the devices listed above?"
read -rp "Type 'yes' to continue, anything else to abort: " confirmation
if [ "$confirmation" != "yes" ]; then
echo "Aborted."
exit 1
fi
fi
umount -Rv "${rootMountPoint}" || :
# shellcheck disable=SC2043
for dev in ${selectedDisks}; do
$BASH ${../disk-deactivate}/disk-deactivate "$dev"
done
'';
};
_create = lib.mkOption {
internal = true;
type = lib.types.str;
description = ''
The script to create all devices defined by disko.devices
'';
default =
with lib; let
sortedDeviceList = diskoLib.sortDevicesByDependencies (cfg.config._meta.deviceDependencies or { }) devices;
in
''
set -efux
disko_devices_dir=$(mktemp -d)
trap 'rm -rf "$disko_devices_dir"' EXIT
mkdir -p "$disko_devices_dir"
${concatMapStrings (dev: (attrByPath (dev ++ [ "_create" ]) {} devices)) sortedDeviceList}
'';
};
_mount = lib.mkOption {
internal = true;
type = lib.types.str;
description = ''
The script to mount all devices defined by disko.devices
'';
default =
with lib; let
fsMounts = diskoLib.deepMergeMap (dev: dev._mount.fs or { }) (flatten (map attrValues (attrValues devices)));
sortedDeviceList = diskoLib.sortDevicesByDependencies (cfg.config._meta.deviceDependencies or { }) devices;
in
''
set -efux
# first create the necessary devices
${concatMapStrings (dev: (attrByPath (dev ++ [ "_mount" ]) {} devices).dev or "") sortedDeviceList}
# and then mount the filesystems in alphabetical order
${concatStrings (attrValues fsMounts)}
'';
};
_unmount = lib.mkOption {
internal = true;
type = lib.types.str;
description = ''
The script to unmount all devices defined by disko.devices
'';
default =
with lib; let
fsMounts = diskoLib.deepMergeMap (dev: dev._unmount.fs or { }) (flatten (map attrValues (attrValues devices)));
sortedDeviceList = diskoLib.sortDevicesByDependencies (cfg.config._meta.deviceDependencies or { }) devices;
in
''
set -efux
# first unmount the filesystems in reverse alphabetical order
${concatStrings (lib.reverseList (attrValues fsMounts))}
# Than close the devices
${concatMapStrings (dev: (attrByPath (dev ++ [ "_unmount" ]) {} devices).dev or "") (lib.reverseList sortedDeviceList)}
'';
};
_disko = lib.mkOption {
internal = true;
type = lib.types.str;
description = ''
The script to umount, create and mount all devices defined by disko.devices
Deprecated in favor of _destroyFormatMount
'';
default = ''
${cfg.config._legacyDestroy}
${cfg.config._create}
${cfg.config._mount}
'';
};
_destroyFormatMount = lib.mkOption {
internal = true;
type = lib.types.str;
description = ''
The script to unmount, create and mount all devices defined by disko.devices
'';
default = ''
${cfg.config._destroy}
${cfg.config._create}
${cfg.config._mount}
'';
};
_formatMount = lib.mkOption {
internal = true;
type = lib.types.str;
description = ''
The script to create and mount all devices defined by disko.devices, without wiping the disks first
'';
default = ''
${cfg.config._create}
${cfg.config._mount}
'';
};
_config = lib.mkOption {
internal = true;
description = ''
The NixOS config generated by disko
'';
default =
with lib; let
configKeys = flatten (map attrNames (flatten (map (dev: dev._config) (flatten (map attrValues (attrValues devices))))));
collectedConfigs = flatten (map (dev: dev._config) (flatten (map attrValues (attrValues devices))));
in
genAttrs configKeys (key: mkMerge (catAttrs key collectedConfigs));
};
};
});
# import all the types from the types directory
types = lib.listToAttrs (
map
(file: lib.nameValuePair
(lib.removeSuffix ".nix" file)
(diskoLib.mkSubType (./types + "/${file}"))
)
(lib.attrNames (builtins.readDir ./types))
);
# render types into an json serializable format
serializeType = type:
let
options = lib.filter (x: !lib.hasPrefix "_" x) (lib.attrNames type.options);
in
lib.listToAttrs (
map
(option: lib.nameValuePair
option
type.options.${option}
)
options
);
typesSerializerLib = {
rootMountPoint = "";
options = null;
config = {
_module = {
args.name = "<self.name>";
args._parent.name = "<parent.name>";
args._parent.type = "<parent.type>";
};
name = "<config.name>";
};
parent = { };
device = "/dev/<device>";
# Spoof part of nixpkgs/lib to analyze the types
lib = lib // {
mkOption = option: {
inherit (option) type;
description = option.description or null;
default = option.defaultText or option.default or null;
};
types = {
attrsOf = subType: {
type = "attrsOf";
inherit subType;
};
listOf = subType: {
type = "listOf";
inherit subType;
};
nullOr = subType: {
type = "nullOr";
inherit subType;
};
oneOf = types: {
type = "oneOf";
inherit types;
};
either = t1: t2: {
type = "oneOf";
types = [ t1 t2 ];
};
enum = choices: {
type = "enum";
inherit choices;
};
anything = "anything";
nonEmptyStr = "str";
strMatching = _: "str";
str = "str";
bool = "bool";
int = "int";
submodule = x: x {
inherit (diskoLib.typesSerializerLib) lib config options;
name = "<self.name>";
};
};
};
diskoLib = {
optionTypes.absolute-pathname = "absolute-pathname";
# Spoof these types to avoid infinite recursion
deviceType = _: "<deviceType>";
partitionType = _: "<partitionType>";
subType = { types, ... }: {
type = "oneOf";
types = lib.attrNames types;
};
mkCreateOption = option: "_create";
};
};
jsonTypes = lib.listToAttrs
(
map
(file: lib.nameValuePair
(lib.removeSuffix ".nix" file)
(diskoLib.serializeType (import (./types + "/${file}") diskoLib.typesSerializerLib))
)
(lib.filter (name: lib.hasSuffix ".nix" name) (lib.attrNames (builtins.readDir ./types)))
) // {
partitionType = {
type = "oneOf";
types = lib.attrNames diskoLib._partitionTypes;
};
deviceType = {
type = "oneOf";
types = lib.attrNames diskoLib._deviceTypes;
};
};
} // outputs;
in
diskoLib

View File

@ -1,81 +0,0 @@
{ diskoLib, modulesPath, config, pkgs, lib, ... }:
let
vm_disko = (diskoLib.testLib.prepareDiskoConfig config diskoLib.testLib.devices).disko;
cfg_ = (lib.evalModules {
modules = lib.singleton {
# _file = toString input;
imports = lib.singleton { disko.devices = vm_disko.devices; };
options = {
disko.devices = lib.mkOption {
type = diskoLib.toplevel;
};
disko.testMode = lib.mkOption {
type = lib.types.bool;
default = true;
};
};
};
}).config;
disks = lib.attrValues cfg_.disko.devices.disk;
rootDisk = {
name = "root";
file = ''"$tmp"/${lib.escapeShellArg (builtins.head disks).name}.qcow2'';
driveExtraOpts.cache = "writeback";
driveExtraOpts.werror = "report";
deviceExtraOpts.bootindex = "1";
deviceExtraOpts.serial = "root";
};
otherDisks = map
(disk: {
name = disk.name;
file = ''"$tmp"/${lib.escapeShellArg disk.name}.qcow2'';
driveExtraOpts.werror = "report";
})
(builtins.tail disks);
diskoBasedConfiguration = {
# generated from disko config
virtualisation.fileSystems = cfg_.disko.devices._config.fileSystems;
boot = cfg_.disko.devices._config.boot or { };
swapDevices = cfg_.disko.devices._config.swapDevices or [ ];
};
hostPkgs = config.virtualisation.host.pkgs;
in
{
imports = [
(modulesPath + "/virtualisation/qemu-vm.nix")
diskoBasedConfiguration
];
disko.testMode = true;
disko.imageBuilder.copyNixStore = false;
disko.imageBuilder.extraConfig = {
disko.devices = cfg_.disko.devices;
};
disko.imageBuilder.imageFormat = "qcow2";
virtualisation.useEFIBoot = config.disko.tests.efi;
virtualisation.memorySize = lib.mkDefault config.disko.memSize;
virtualisation.useDefaultFilesystems = false;
virtualisation.diskImage = null;
virtualisation.qemu.drives = [ rootDisk ] ++ otherDisks;
boot.zfs.devNodes = "/dev/disk/by-uuid"; # needed because /dev/disk/by-id is empty in qemu-vms
boot.zfs.forceImportAll = true;
boot.zfs.forceImportRoot = lib.mkForce true;
system.build.vmWithDisko = hostPkgs.writers.writeDashBin "disko-vm" ''
set -efux
export tmp=$(${hostPkgs.coreutils}/bin/mktemp -d)
trap 'rm -rf "$tmp"' EXIT
${lib.concatMapStringsSep "\n" (disk: ''
${hostPkgs.qemu}/bin/qemu-img create -f qcow2 \
-b ${config.system.build.diskoImages}/${lib.escapeShellArg disk.name}.qcow2 \
-F qcow2 "$tmp"/${lib.escapeShellArg disk.name}.qcow2
'') disks}
set +f
${config.system.build.vm}/bin/run-*-vm
'';
}

View File

@ -1,228 +0,0 @@
{ config
, diskoLib
, lib
, extendModules
, options
, ...
}:
let
diskoCfg = config.disko;
cfg = diskoCfg.imageBuilder;
inherit (cfg) pkgs imageFormat;
checked = diskoCfg.checkScripts;
configSupportsZfs = config.boot.supportedFilesystems.zfs or false;
vmTools = pkgs.vmTools.override
{
rootModules = [
"9p" "9pnet_virtio" # we can drop those in future if we stop supporting 24.11
"virtiofs"
"virtio_pci"
"virtio_blk"
"virtio_balloon"
"virtio_rng"
]
++ (lib.optional configSupportsZfs "zfs")
++ cfg.extraRootModules;
kernel = pkgs.aggregateModules
(with cfg.kernelPackages; [ kernel ]
++ lib.optional (lib.elem "zfs" cfg.extraRootModules || configSupportsZfs) zfs);
}
// lib.optionalAttrs (diskoLib.vmToolsSupportsCustomQemu lib)
{
customQemu = cfg.qemu;
};
cleanedConfig = diskoLib.testLib.prepareDiskoConfig config diskoLib.testLib.devices;
systemToInstall = extendModules {
modules = [
cfg.extraConfig
{
disko.testMode = true;
disko.devices = lib.mkForce cleanedConfig.disko.devices;
boot.loader.grub.devices = lib.mkForce cleanedConfig.boot.loader.grub.devices;
}
];
};
dependencies = with pkgs; [
bash
coreutils
gnused
parted # for partprobe
systemdMinimal
nix
util-linux
findutils
kmod
] ++ cfg.extraDependencies;
preVM = ''
# shellcheck disable=SC2154
mkdir -p "$out"
${lib.concatMapStringsSep "\n" (disk:
# shellcheck disable=SC2154
"${pkgs.qemu}/bin/qemu-img create -f ${imageFormat} \"$out/${disk.imageName}.${imageFormat}\" ${disk.imageSize}"
) (lib.attrValues diskoCfg.devices.disk)}
# This makes disko work, when canTouchEfiVariables is set to true.
# Technically these boot entries will no be persisted this way, but
# in most cases this is OK, because we can rely on the standard location for UEFI executables.
install -m600 ${pkgs.OVMF.variables} efivars.fd
'';
closureInfo = pkgs.closureInfo {
rootPaths = [ systemToInstall.config.system.build.toplevel ];
};
partitioner = ''
set -efux
# running udev, stolen from stage-1.sh
echo "running udev..."
ln -sfn /proc/self/fd /dev/fd
ln -sfn /proc/self/fd/0 /dev/stdin
ln -sfn /proc/self/fd/1 /dev/stdout
ln -sfn /proc/self/fd/2 /dev/stderr
mkdir -p /etc/udev
mount -t efivarfs none /sys/firmware/efi/efivars
ln -sfn ${systemToInstall.config.system.build.etc}/etc/udev/rules.d /etc/udev/rules.d
mkdir -p /dev/.mdadm
${pkgs.systemdMinimal}/lib/systemd/systemd-udevd --daemon
partprobe
udevadm trigger --action=add
udevadm settle
${lib.optionalString diskoCfg.testMode ''
export IN_DISKO_TEST=1
''}
${lib.getExe systemToInstall.config.system.build.destroyFormatMount} --yes-wipe-all-disks
'';
installer = lib.optionalString cfg.copyNixStore ''
# populate nix db, so nixos-install doesn't complain
export NIX_STATE_DIR=${systemToInstall.config.disko.rootMountPoint}/nix/var/nix
nix-store --load-db < "${closureInfo}/registration"
# We copy files with cp because `nix copy` seems to have a large memory leak
mkdir -p ${systemToInstall.config.disko.rootMountPoint}/nix/store
time xargs cp --recursive --target ${systemToInstall.config.disko.rootMountPoint}/nix/store < ${closureInfo}/store-paths
${systemToInstall.config.system.build.nixos-install}/bin/nixos-install --root ${systemToInstall.config.disko.rootMountPoint} --system ${systemToInstall.config.system.build.toplevel} --keep-going --no-channel-copy -v --no-root-password --option binary-caches ""
umount -Rv ${systemToInstall.config.disko.rootMountPoint}
'';
QEMU_OPTS = lib.concatStringsSep " " ([
"-drive if=pflash,format=raw,unit=0,readonly=on,file=${pkgs.OVMF.firmware}"
"-drive if=pflash,format=raw,unit=1,file=efivars.fd"
] ++ builtins.map
(disk:
"-drive file=\"$out\"/${disk.imageName}.${imageFormat},if=virtio,cache=unsafe,werror=report,format=${imageFormat}"
)
(lib.attrValues diskoCfg.devices.disk));
in
{
system.build.diskoImages = vmTools.runInLinuxVM (pkgs.runCommand cfg.name
{
buildInputs = dependencies;
inherit preVM QEMU_OPTS;
postVm = cfg.extraPostVM;
inherit (diskoCfg) memSize;
}
(partitioner + installer));
system.build.diskoImagesScript = diskoLib.writeCheckedBash { inherit checked pkgs; } cfg.name ''
set -efu
export PATH=${lib.makeBinPath dependencies}
showUsage() {
cat <<\USAGE
Usage: $script [options]
Options:
* --pre-format-files <src> <dst>
copies the src to the dst on the VM, before disko is run
This is useful to provide secrets like LUKS keys, or other files you need for formatting
* --post-format-files <src> <dst>
copies the src to the dst on the finished image
These end up in the images later and is useful if you want to add some extra stateful files
They will have the same permissions but will be owned by root:root
* --build-memory <amt>
specify the amount of memory in MiB that gets allocated to the build VM
This can be useful if you want to build images with a more involed NixOS config
The default is disko.memSize which defaults to ${builtins.toString options.disko.memSize.default} MiB
USAGE
}
export out=$PWD
TMPDIR=$(mktemp -d); export TMPDIR
trap 'rm -rf "$TMPDIR"' EXIT
cd "$TMPDIR"
mkdir copy_before_disko copy_after_disko
while [[ $# -gt 0 ]]; do
case "$1" in
--pre-format-files)
src=$2
dst=$3
cp --reflink=auto -r "$src" copy_before_disko/"$(echo "$dst" | base64)"
shift 2
;;
--post-format-files)
src=$2
dst=$3
cp --reflink=auto -r "$src" copy_after_disko/"$(echo "$dst" | base64)"
shift 2
;;
--build-memory)
regex="^[0-9]+$"
if ! [[ $2 =~ $regex ]]; then
echo "'$2' is not a number"
exit 1
fi
build_memory=$2
shift 1
;;
*)
showUsage
exit 1
;;
esac
shift
done
export preVM=${diskoLib.writeCheckedBash { inherit pkgs checked; } "preVM.sh" ''
set -efu
mv copy_before_disko copy_after_disko xchg/
origBuilder=${pkgs.writeScript "disko-builder" ''
set -eu
export PATH=${lib.makeBinPath dependencies}
for src in /tmp/xchg/copy_before_disko/*; do
[ -e "$src" ] || continue
dst=$(basename "$src" | base64 -d)
mkdir -p "$(dirname "$dst")"
cp -r "$src" "$dst"
done
set -f
${partitioner}
set +f
for src in /tmp/xchg/copy_after_disko/*; do
[ -e "$src" ] || continue
dst=/mnt/$(basename "$src" | base64 -d)
mkdir -p "$(dirname "$dst")"
cp -r "$src" "$dst"
done
${installer}
''}
echo "export origBuilder=$origBuilder" >> xchg/saved-env
${preVM}
''}
export postVM=${diskoLib.writeCheckedBash { inherit pkgs checked; } "postVM.sh" cfg.extraPostVM}
build_memory=''${build_memory:-${builtins.toString diskoCfg.memSize}}
# shellcheck disable=SC2016
QEMU_OPTS=${lib.escapeShellArg QEMU_OPTS}
# replace quoted $out with the actual path
QEUM_OPTS=''${QEMU_OPTS//\$out/$out}
QEMU_OPTS+=" -m $build_memory"
export QEMU_OPTS
${pkgs.bash}/bin/sh -e ${vmTools.vmRunCommand vmTools.qemuCommandLinux}
cd /
'';
}

View File

@ -1,337 +0,0 @@
{ lib
, makeTest
, eval-config
, ...
}:
let
testLib = {
# this takes a nixos config and changes the disk devices so we can run them inside the qemu test runner
# basically changes all the disk.*.devices to something like /dev/vda or /dev/vdb etc.
prepareDiskoConfig = cfg: devices:
let
cleanedTopLevel = lib.filterAttrsRecursive (n: _: !lib.hasPrefix "_" n) cfg;
preparedDisks = lib.foldlAttrs
(acc: n: v: {
devices = lib.tail acc.devices;
grub-devices = acc.grub-devices ++ (lib.optional (lib.any (part: (part.type or "") == "EF02") (lib.attrValues (v.content.partitions or { }))) (lib.head acc.devices));
disks = acc.disks // {
"${n}" = v // {
device = lib.head acc.devices;
content = v.content // { device = lib.head acc.devices; };
};
};
})
{
inherit devices;
grub-devices = [ ];
disks = { };
}
cleanedTopLevel.disko.devices.disk;
in
cleanedTopLevel // {
boot.loader.grub.devices = if (preparedDisks.grub-devices != [ ]) then preparedDisks.grub-devices else [ "nodev" ];
disko.devices = cleanedTopLevel.disko.devices // {
disk = preparedDisks.disks;
};
};
# list of devices generated inside qemu
devices = [
"/dev/vda"
"/dev/vdb"
"/dev/vdc"
"/dev/vdd"
"/dev/vde"
"/dev/vdf"
"/dev/vdg"
"/dev/vdh"
"/dev/vdi"
"/dev/vdj"
"/dev/vdk"
"/dev/vdl"
"/dev/vdm"
"/dev/vdn"
"/dev/vdo"
];
# This is the test generator for a disko test
makeDiskoTest =
{ name
, disko-config
, extendModules ? null
, pkgs ? import <nixpkgs> { }
, extraTestScript ? ""
, bootCommands ? ""
, extraInstallerConfig ? { }
, extraSystemConfig ? { }
, efi ? !pkgs.stdenv.hostPlatform.isRiscV64
, postDisko ? ""
, testMode ? "module" # can be one of direct module cli
, testBoot ? true # if we actually want to test booting or just create/mount
, enableOCR ? false
}:
let
makeTest' = args:
makeTest args {
inherit pkgs;
inherit (pkgs) system;
};
# for installation we skip /dev/vda because it is the test runner disk
importedDiskoConfig =
if builtins.isPath disko-config then
import disko-config
else
disko-config;
diskoConfigWithArgs =
if builtins.isFunction importedDiskoConfig then
importedDiskoConfig { inherit lib; }
else
importedDiskoConfig;
testConfigInstall = testLib.prepareDiskoConfig diskoConfigWithArgs (lib.tail testLib.devices);
# we need to shift the disks by one because the first disk is the /dev/vda of the test runner
# so /dev/vdb becomes /dev/vda etc.
testConfigBooted = testLib.prepareDiskoConfig diskoConfigWithArgs testLib.devices;
tsp-generator = pkgs.callPackage ../. { checked = true; };
tsp-format = (tsp-generator._cliFormat testConfigInstall) pkgs;
tsp-mount = (tsp-generator._cliMount testConfigInstall) pkgs;
tsp-unmount = (tsp-generator._cliUnmount testConfigInstall) pkgs;
tsp-disko = (tsp-generator._cliDestroyFormatMount testConfigInstall) pkgs;
tsp-config = tsp-generator.config testConfigBooted;
num-disks = builtins.length (lib.attrNames testConfigBooted.disko.devices.disk);
installed-system = { config, ... }: {
imports = [
(lib.optionalAttrs (testMode == "direct") tsp-config)
(lib.optionalAttrs (testMode == "module") {
disko.enableConfig = true;
imports = [
../module.nix
testConfigBooted
];
})
];
# config for tests to make them run faster or work at all
documentation.enable = false;
hardware.enableAllFirmware = lib.mkForce false;
# FIXME: we don't have an systemd in stage-1 equialvent for this
boot.initrd.preDeviceCommands = lib.mkIf (!config.boot.initrd.systemd.enable) ''
echo -n 'secretsecret' > /tmp/secret.key
'';
boot.consoleLogLevel = lib.mkForce 100;
boot.loader.systemd-boot.enable = lib.mkDefault efi;
};
installed-system-eval = eval-config {
modules = [ installed-system ];
inherit (pkgs) system;
};
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;
})
];
# 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
# 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.
'';
}
];
})
];
}).config.system.build.toplevel;
in
makeTest' {
name = "disko-${name}";
meta.timeout = 600; # 10 minutes
inherit enableOCR;
nodes.machine = { pkgs, ... }: {
imports = [
(lib.optionalAttrs (testMode == "module") {
imports = [
../module.nix
];
disko = {
enableConfig = false;
checkScripts = true;
devices = testConfigInstall.disko.devices;
};
})
extraInstallerConfig
# from base.nix
({ config, ... }: {
boot.supportedFilesystems =
[ "btrfs" "cifs" "f2fs" "jfs" "ntfs" "reiserfs" "vfat" "xfs" ] ++
lib.optional (config.networking.hostId != null && lib.meta.availableOn pkgs.stdenv.hostPlatform config.boot.zfs.package) "zfs";
})
(if lib.versionAtLeast (lib.versions.majorMinor lib.version) "23.11" then {
boot.swraid.enable = true;
} else {
boot.initrd.services.swraid.enable = true;
})
];
systemd.services.mdmonitor.enable = false; # silence some weird warnings
environment.systemPackages = [
pkgs.jq
];
# speed-up eval
documentation.enable = false;
nix.settings = {
substituters = lib.mkForce [ ];
hashed-mirrors = null;
connect-timeout = 1;
};
networking.hostId = lib.mkIf
(
(testConfigInstall ? networking.hostId) && (testConfigInstall.networking.hostId != null)
)
testConfigInstall.networking.hostId;
virtualisation.emptyDiskImages = builtins.genList (_: 4096) num-disks;
# useful for debugging via repl
system.build.systemToInstall = installed-system-eval;
};
testScript = { nodes, ... }: ''
def disks(oldmachine, num_disks):
disk_flags = []
for i in range(num_disks):
disk_flags += [
'-drive',
f"file={oldmachine.state_dir}/empty{i}.qcow2,id=drive{i + 1},if=none,index={i + 1},werror=report",
'-device',
f"virtio-blk-pci,drive=drive{i + 1}"
]
return disk_flags
def create_test_machine(
oldmachine=None, **kwargs
): # taken from <nixpkgs/nixos/tests/installer.nix>
start_command = [
"${pkgs.qemu_test}/bin/qemu-kvm",
"-cpu",
"max",
"-m",
"1024",
"-virtfs",
"local,path=/nix/store,security_model=none,mount_tag=nix-store",
*disks(oldmachine, ${toString num-disks})
]
${lib.optionalString efi ''
start_command += ["-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.start()
machine.succeed("echo -n 'additionalSecret' > /tmp/additionalSecret.key")
machine.succeed("echo -n 'secretsecret' > /tmp/secret.key")
${lib.optionalString (testMode == "direct") ''
# running direct mode
machine.succeed("${lib.getExe tsp-format}")
machine.succeed("${lib.getExe tsp-mount}")
machine.succeed("${lib.getExe tsp-mount}") # verify that mount is idempotent
machine.succeed("${lib.getExe tsp-unmount}")
machine.succeed("${lib.getExe tsp-unmount}") # verify that umount is idempotent
machine.succeed("${lib.getExe tsp-mount}") # verify that mount is idempotent
machine.succeed("${lib.getExe tsp-disko} --yes-wipe-all-disks") # verify that we can destroy and recreate
machine.succeed("mkdir -p /mnt/home")
machine.succeed("touch /mnt/home/testfile")
machine.succeed("${lib.getExe tsp-format}") # verify that format is idempotent
machine.succeed("test -e /mnt/home/testfile")
''}
${lib.optionalString (testMode == "module") ''
# running module mode
machine.succeed("${lib.getExe nodes.machine.system.build.format}")
machine.succeed("${lib.getExe nodes.machine.system.build.mount}")
machine.succeed("${lib.getExe nodes.machine.system.build.mount}") # verify that mount is idempotent
machine.succeed("${lib.getExe nodes.machine.system.build.destroyFormatMount} --yes-wipe-all-disks") # verify that we can destroy and recreate again
machine.succeed("mkdir -p /mnt/home")
machine.succeed("touch /mnt/home/testfile")
machine.succeed("${lib.getExe nodes.machine.system.build.format}") # verify that format is idempotent
machine.succeed("test -e /mnt/home/testfile")
''}
${postDisko}
${lib.optionalString testBoot ''
# mount nix-store in /mnt
machine.succeed("mkdir -p /mnt/nix/store")
machine.succeed("mount --bind /nix/store /mnt/nix/store")
machine.succeed("nix-store --load-db < ${pkgs.closureInfo {rootPaths = [installedTopLevel];}}/registration")
# fix "this is not a NixOS installation"
machine.succeed("mkdir -p /mnt/etc")
machine.succeed("touch /mnt/etc/NIXOS")
machine.succeed("mkdir -p /mnt/nix/var/nix/profiles")
machine.succeed("nix-env -p /mnt/nix/var/nix/profiles/system --set ${installedTopLevel}")
machine.succeed("NIXOS_INSTALL_BOOTLOADER=1 nixos-enter --root /mnt -- ${installedTopLevel}/bin/switch-to-configuration boot")
machine.succeed("sync")
machine.shutdown()
machine = create_test_machine(oldmachine=machine, name="booted_machine")
machine.start()
${bootCommands}
machine.wait_for_unit("local-fs.target")
''}
${extraTestScript}
'';
};
};
in
testLib

View File

@ -1,272 +0,0 @@
{ config, options, diskoLib, lib, rootMountPoint, parent, device, ... }:
let
swapType = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: {
options = {
size = lib.mkOption {
type = lib.types.strMatching "^([0-9]+[KMGTP])?$";
description = "Size of the swap file (e.g. 2G)";
};
path = lib.mkOption {
type = lib.types.str;
default = name;
description = "Path to the swap file (relative to the mountpoint)";
};
priority = lib.mkOption {
type = lib.types.nullOr lib.types.int;
default = null;
description = ''
Specify the priority of the swap file. Priority is a value between 0 and 32767.
Higher numbers indicate higher priority.
null lets the kernel choose a priority, which will show up as a negative value.
'';
};
options = lib.mkOption {
type = lib.types.listOf lib.types.nonEmptyStr;
default = [ "defaults" ];
example = [ "nofail" ];
description = "Options used to mount the swap.";
};
};
}));
default = { };
description = "Swap files";
};
swapConfig = { mountpoint, swap }:
{
swapDevices = builtins.map
(file: {
device = "${mountpoint}/${file.path}";
inherit (file) priority options;
})
(lib.attrValues swap);
};
swapCreate = mountpoint: swap:
lib.concatMapStringsSep
"\n"
(file: ''
if ! test -e "${mountpoint}/${file.path}"; then
btrfs filesystem mkswapfile --size ${file.size} "${mountpoint}/${file.path}"
fi
'')
(lib.attrValues swap);
in
{
options = {
type = lib.mkOption {
type = lib.types.enum [ "btrfs" ];
internal = true;
description = "Type";
};
device = lib.mkOption {
type = lib.types.str;
default = device;
description = "Device to use";
};
extraArgs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Extra arguments";
};
mountOptions = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "defaults" ];
description = "A list of options to pass to mount.";
};
subvolumes = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({ config, ... }: {
options = {
name = lib.mkOption {
type = lib.types.str;
default = config._module.args.name;
description = "Name of the BTRFS subvolume.";
};
type = lib.mkOption {
type = lib.types.enum [ "btrfs_subvol" ];
default = "btrfs_subvol";
internal = true;
description = "Type";
};
extraArgs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Extra arguments";
};
mountOptions = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "defaults" ];
description = "Options to pass to mount";
};
mountpoint = lib.mkOption {
type = lib.types.nullOr diskoLib.optionTypes.absolute-pathname;
default = null;
description = "Location to mount the subvolume to.";
};
swap = swapType;
};
}));
default = { };
description = "Subvolumes to define for BTRFS.";
};
mountpoint = lib.mkOption {
type = lib.types.nullOr diskoLib.optionTypes.absolute-pathname;
default = null;
description = "A path to mount the BTRFS filesystem to.";
};
swap = swapType;
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo diskoLib.jsonType;
default = _dev: { };
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
default = ''
# create the filesystem only if the device seems empty
if ! (blkid "${config.device}" -o export | grep -q '^TYPE='); then
mkfs.btrfs "${config.device}" ${toString config.extraArgs}
fi
${lib.optionalString (config.swap != {} || config.subvolumes != {}) ''
if (blkid "${config.device}" -o export | grep -q '^TYPE=btrfs$'); then
${lib.optionalString (config.swap != {}) ''
(
MNTPOINT=$(mktemp -d)
mount ${device} "$MNTPOINT" -o subvol=/
trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT
${swapCreate "$MNTPOINT" config.swap}
)
''}
${lib.concatMapStrings (subvol: ''
(
MNTPOINT=$(mktemp -d)
mount "${config.device}" "$MNTPOINT" -o subvol=/
trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT
SUBVOL_ABS_PATH="$MNTPOINT/${subvol.name}"
mkdir -p "$(dirname "$SUBVOL_ABS_PATH")"
if ! btrfs subvolume show "$SUBVOL_ABS_PATH" > /dev/null 2>&1; then
btrfs subvolume create "$SUBVOL_ABS_PATH" ${toString subvol.extraArgs}
fi
${swapCreate "$SUBVOL_ABS_PATH" subvol.swap}
)
'') (lib.attrValues config.subvolumes)}
fi
''}
'';
};
_mount = diskoLib.mkMountOption {
inherit config options;
default =
let
subvolMounts = lib.concatMapAttrs
(_: subvol:
lib.warnIf (subvol.mountOptions != (options.subvolumes.type.getSubOptions [ ]).mountOptions.default && subvol.mountpoint == null)
"Subvolume ${subvol.name} has mountOptions but no mountpoint. See upgrade guide (2023-07-09 121df48)."
lib.optionalAttrs
(subvol.mountpoint != null)
{
${subvol.mountpoint} = ''
if ! findmnt "${config.device}" "${rootMountPoint}${subvol.mountpoint}" > /dev/null 2>&1; then
mount "${config.device}" "${rootMountPoint}${subvol.mountpoint}" \
${lib.concatMapStringsSep " " (opt: "-o ${opt}") (subvol.mountOptions ++ [ "subvol=${subvol.name}" ])} \
-o X-mount.mkdir
fi
'';
}
)
config.subvolumes;
in
{
fs = subvolMounts // lib.optionalAttrs (config.mountpoint != null) {
${config.mountpoint} = ''
if ! findmnt "${config.device}" "${rootMountPoint}${config.mountpoint}" > /dev/null 2>&1; then
mount "${config.device}" "${rootMountPoint}${config.mountpoint}" \
${lib.concatMapStringsSep " " (opt: "-o ${opt}") config.mountOptions} \
-o X-mount.mkdir
fi
'';
};
};
};
_unmount = diskoLib.mkUnmountOption {
inherit config options;
default =
let
subvolMounts = lib.concatMapAttrs
(_: subvol:
lib.optionalAttrs
(subvol.mountpoint != null)
{
${subvol.mountpoint} = ''
if findmnt "${config.device}" "${rootMountPoint}${subvol.mountpoint}" > /dev/null 2>&1; then
umount "${rootMountPoint}${subvol.mountpoint}"
fi
'';
}
)
config.subvolumes;
in
{
fs = subvolMounts // lib.optionalAttrs (config.mountpoint != null) {
${config.mountpoint} = ''
if findmnt "${config.device}" "${rootMountPoint}${config.mountpoint}" > /dev/null 2>&1; then
umount "${rootMountPoint}${config.mountpoint}"
fi
'';
};
};
};
_config = lib.mkOption {
internal = true;
readOnly = true;
default = [
(map
(subvol:
lib.optional (subvol.mountpoint != null) {
fileSystems.${subvol.mountpoint} = {
device = config.device;
fsType = "btrfs";
options = subvol.mountOptions ++ [ "subvol=${subvol.name}" ];
};
}
)
(lib.attrValues config.subvolumes))
(lib.optional (config.mountpoint != null) {
fileSystems.${config.mountpoint} = {
device = config.device;
fsType = "btrfs";
options = config.mountOptions;
};
})
(map
(subvol: swapConfig {
inherit (subvol) mountpoint swap;
})
(lib.attrValues config.subvolumes))
(swapConfig {
inherit (config) mountpoint swap;
})
];
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = pkgs:
[ pkgs.btrfs-progs pkgs.gnugrep ];
description = "Packages";
};
};
}

View File

@ -1,71 +0,0 @@
{ config, options, lib, diskoLib, ... }:
{
options = {
name = lib.mkOption {
type = lib.types.str;
default = lib.replaceStrings [ "/" ] [ "_" ] config._module.args.name;
description = "Device name";
};
type = lib.mkOption {
type = lib.types.enum [ "disk" ];
default = "disk";
internal = true;
description = "Type";
};
device = lib.mkOption {
type = diskoLib.optionTypes.absolute-pathname; # TODO check if subpath of /dev ? - No! eg: /.swapfile
description = "Device path";
};
imageName = lib.mkOption {
type = lib.types.str;
default = config.name;
description = ''
name of the image when disko images are created
is used as an argument to "qemu-img create ..."
'';
};
imageSize = lib.mkOption {
type = lib.types.strMatching "[0-9]+[KMGTP]?";
description = ''
size of the image when disko images are created
is used as an argument to "qemu-img create ..."
'';
default = "2G";
};
content = diskoLib.deviceType { parent = config; device = config.device; };
_meta = lib.mkOption {
internal = true;
readOnly = true;
type = diskoLib.jsonType;
default =
lib.optionalAttrs (config.content != null) (config.content._meta [ "disk" config.device ]);
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
default = config.content._create;
};
_mount = diskoLib.mkMountOption {
inherit config options;
default = lib.optionalAttrs (config.content != null) config.content._mount;
};
_unmount = diskoLib.mkUnmountOption {
inherit config options;
default = lib.optionalAttrs (config.content != null) config.content._unmount;
};
_config = lib.mkOption {
internal = true;
readOnly = true;
default =
lib.optional (config.content != null) config.content._config;
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = pkgs: [ pkgs.jq ] ++ lib.optionals (config.content != null) (config.content._pkgs pkgs);
description = "Packages";
};
};
}

View File

@ -1,109 +0,0 @@
{ config, options, lib, diskoLib, rootMountPoint, parent, device, ... }:
{
options = {
type = lib.mkOption {
type = lib.types.enum [ "filesystem" ];
internal = true;
description = "Type";
};
device = lib.mkOption {
type = lib.types.str;
default = device;
description = "Device to use";
};
extraArgs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Extra arguments";
};
mountOptions = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "defaults" ];
description = "Options to pass to mount";
};
mountpoint = lib.mkOption {
type = lib.types.nullOr diskoLib.optionTypes.absolute-pathname;
default = null;
description = "Path to mount the filesystem to";
};
format = lib.mkOption {
type = lib.types.str;
description = "Format of the filesystem";
};
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo diskoLib.jsonType;
default = _dev: { };
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
default = ''
if ! (blkid "${config.device}" | grep -q 'TYPE='); then
mkfs.${config.format} \
${lib.escapeShellArgs config.extraArgs} \
"${config.device}"
fi
'';
};
_mount = diskoLib.mkMountOption {
inherit config options;
default = lib.optionalAttrs (config.mountpoint != null) {
fs.${config.mountpoint} = ''
if ! findmnt "${config.device}" "${rootMountPoint}${config.mountpoint}" >/dev/null 2>&1; then
mount "${config.device}" "${rootMountPoint}${config.mountpoint}" \
-t "${config.format}" \
${lib.concatMapStringsSep " " (opt: "-o ${lib.escapeShellArg opt}") config.mountOptions} \
-o X-mount.mkdir
fi
'';
};
};
_unmount = diskoLib.mkUnmountOption {
inherit config options;
default = lib.optionalAttrs (config.mountpoint != null) {
fs.${config.mountpoint} = ''
if findmnt "${config.device}" "${rootMountPoint}${config.mountpoint}" >/dev/null 2>&1; then
umount "${rootMountPoint}${config.mountpoint}"
fi
'';
};
};
_config = lib.mkOption {
internal = true;
readOnly = true;
default = lib.optional (config.mountpoint != null) {
fileSystems.${config.mountpoint} = {
device = config.device;
fsType = config.format;
options = config.mountOptions;
};
};
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
# type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = pkgs:
[ pkgs.util-linux pkgs.gnugrep ] ++ (
# TODO add many more
if (config.format == "xfs") then [ pkgs.xfsprogs ]
else if (config.format == "btrfs") then [ pkgs.btrfs-progs ]
else if (config.format == "vfat") then [ pkgs.dosfstools ]
else if (config.format == "ext2") then [ pkgs.e2fsprogs ]
else if (config.format == "ext3") then [ pkgs.e2fsprogs ]
else if (config.format == "ext4") then [ pkgs.e2fsprogs ]
else if (config.format == "bcachefs") then [ pkgs.bcachefs-tools ]
else if (config.format == "f2fs") then [ pkgs.f2fs-tools ]
else [ ]
);
description = "Packages";
};
};
}

View File

@ -1,297 +0,0 @@
{ config, options, lib, diskoLib, parent, device, ... }:
let
sortedPartitions = lib.sort (x: y: x.priority < y.priority) (lib.attrValues config.partitions);
sortedHybridPartitions = lib.filter (p: p.hybrid != null) sortedPartitions;
in
{
options = {
type = lib.mkOption {
type = lib.types.enum [ "gpt" ];
internal = true;
description = "Partition table";
};
device = lib.mkOption {
type = lib.types.str;
default = device;
description = "Device to use for the partition table";
};
partitions = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({ name, ... }@partition: {
options = {
type = lib.mkOption {
type =
let
hexPattern = len: "[A-Fa-f0-9]{${toString len}}";
in
lib.types.either
(lib.types.strMatching (hexPattern 4))
(lib.types.strMatching (lib.concatMapStringsSep "-" hexPattern [ 8 4 4 4 12 ]));
default = if partition.config.content != null && partition.config.content.type == "swap" then "8200" else "8300";
defaultText = ''8300 (Linux filesystem) normally, 8200 (Linux swap) if content.type is "swap"'';
description = ''
Filesystem type to use.
This can either be an sgdisk-specific short code (run sgdisk -L to see what is available),
or a fully specified GUID (see https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs).
'';
};
device = lib.mkOption {
type = lib.types.str;
default =
if config._parent.type == "mdadm" then
# workaround because mdadm partlabel do not appear in /dev/disk/by-partlabel
"/dev/disk/by-id/md-name-any:${config._parent.name}-part${toString partition.config._index}"
else
"/dev/disk/by-partlabel/${diskoLib.hexEscapeUdevSymlink partition.config.label}";
defaultText = ''
if the parent is an mdadm device:
/dev/disk/by-id/md-name-any:''${config._parent.name}-part''${toString partition.config._index}
otherwise:
/dev/disk/by-partlabel/''${diskoLib.hexEscapeUdevSymlink partition.config.label}
'';
description = "Device to use for the partition";
};
priority = lib.mkOption {
type = lib.types.int;
default =
if partition.config.size or "" == "100%" then
9001
else if partition.config.type == "EF02" then
# Boot partition should be created first, because some BIOS implementations require it.
# Priority defaults to 100 here to support any potential use-case for placing partitions prior to EF02
100
else
1000;
defaultText = ''
1000: normal partitions
9001: partitions with 100% size
100: boot partitions (EF02)
'';
description = "Priority of the partition, smaller values are created first";
};
name = lib.mkOption {
type = lib.types.str;
description = "Name of the partition";
default = name;
};
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;
defaultText = ''
''${config._parent.type}-''${config._parent.name}-''${partition.config.name}
or a truncated hash of the above if it is longer than 36 characters
'';
};
size = lib.mkOption {
type = lib.types.either (lib.types.enum [ "100%" ]) (lib.types.strMatching "[0-9]+[KMGTP]?");
default = "0";
description = ''
Size of the partition, in sgdisk format.
sets end automatically with the + prefix
can be 100% for the whole remaining disk, will be done last in that case.
'';
};
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;
defaultText = "1 if the unit of start or end is sectors, 0 otherwise";
description = "Alignment of the partition, if sectors are used as start or end it can be aligned to 1";
};
start = lib.mkOption {
type = lib.types.str;
default = "0";
description = "Start of the partition, in sgdisk format, use 0 for next available range";
};
end = lib.mkOption {
type = lib.types.str;
default = if partition.config.size == "100%" then "-0" else "+${partition.config.size}";
defaultText = ''
if partition.config.size == "100%" then "-0" else "+''${partition.config.size}";
'';
description = ''
End of the partition, in sgdisk format.
Use + for relative sizes from the partitions start
or - for relative sizes from the disks end
'';
};
content = diskoLib.partitionType { parent = config; device = partition.config.device; };
hybrid = lib.mkOption {
type = lib.types.nullOr (lib.types.submodule ({ ... } @ hp: {
options = {
mbrPartitionType = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "MBR type code";
};
mbrBootableFlag = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Set the bootable flag (aka the active flag) on any or all of your hybridized partitions";
};
_create = diskoLib.mkCreateOption {
inherit config options;
default = ''
${lib.optionalString (hp.config.mbrPartitionType != null) ''
sfdisk --label-nested dos --part-type "${parent.device}" ${(toString partition.config._index)} ${hp.config.mbrPartitionType}
udevadm trigger --subsystem-match=block
udevadm settle
''}
${lib.optionalString hp.config.mbrBootableFlag ''
sfdisk --label-nested dos --activate "${parent.device}" ${(toString partition.config._index)}
''}
'';
};
};
}));
default = null;
description = "Entry to add to the Hybrid MBR table";
};
_index = lib.mkOption {
type = lib.types.int;
internal = true;
default = diskoLib.indexOf (x: x.name == partition.config.name) sortedPartitions 0;
defaultText = null;
};
};
}));
default = { };
description = "Attrs of partitions to add to the partition table";
};
efiGptPartitionFirst = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Place EFI GPT (0xEE) partition first in MBR (good for GRUB)";
};
_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 { } (map
(partition:
lib.optionalAttrs (partition.content != null) (partition.content._meta dev)
)
(lib.attrValues config.partitions));
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
default = ''
if ! blkid "${config.device}" >&2; then
sgdisk --clear "${config.device}"
fi
${lib.concatStrings (map (partition: ''
# try to create the partition, if it fails, try to change the type and name
if ! sgdisk \
--align-end ${lib.optionalString (partition.alignment != 0) ''--set-alignment=${builtins.toString partition.alignment}''} \
--new=${toString partition._index}:${partition.start}:${partition.end} \
--change-name="${toString partition._index}:${partition.label}" \
--typecode=${toString partition._index}:${partition.type} \
"${config.device}"
then sgdisk \
--change-name="${toString partition._index}:${partition.label}" \
--typecode=${toString partition._index}:${partition.type} \
"${config.device}"
fi
# ensure /dev/disk/by-path/..-partN exists before continuing
partprobe "${config.device}" || : # sometimes partprobe fails, but the partitions are still up2date
udevadm trigger --subsystem-match=block
udevadm settle
'') sortedPartitions)}
${
lib.optionalString (sortedHybridPartitions != [])
("sgdisk -h "
+ (lib.concatStringsSep ":" (map (p: (toString p._index)) sortedHybridPartitions))
+ (
lib.optionalString (!config.efiGptPartitionFirst) ":EE "
)
+ ''"${parent.device}"'')
}
${lib.concatMapStrings (p:
p.hybrid._create
)
sortedHybridPartitions
}
${lib.concatStrings (map (partition: ''
${lib.optionalString (partition.content != null) partition.content._create}
'') sortedPartitions)}
'';
};
_mount = diskoLib.mkMountOption {
inherit config options;
default =
let
partMounts = lib.foldr lib.recursiveUpdate { } (map
(partition:
lib.optionalAttrs (partition.content != null) partition.content._mount
)
(lib.attrValues config.partitions));
in
{
dev = partMounts.dev or "";
fs = partMounts.fs or { };
};
};
_unmount = diskoLib.mkUnmountOption {
inherit config options;
default =
let
partMounts = lib.foldr lib.recursiveUpdate { } (map
(partition:
lib.optionalAttrs (partition.content != null) partition.content._unmount
)
(lib.attrValues 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
)
(lib.attrValues config.partitions))
++ (lib.optional (lib.any (part: part.type == "EF02") (lib.attrValues config.partitions)) {
boot.loader.grub.devices = [ config.device ];
});
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = pkgs:
[
pkgs.gptfdisk
pkgs.systemdMinimal
pkgs.parted # for partprobe
] ++ lib.flatten (map
(partition:
lib.optional (partition.content != null) (partition.content._pkgs pkgs)
)
(lib.attrValues config.partitions));
description = "Packages";
};
};
}

View File

@ -1,210 +0,0 @@
{ config, options, lib, diskoLib, parent, device, ... }:
let
keyFile =
if config.settings ? "keyFile"
then config.settings.keyFile
else if config.askPassword
then ''<(set +x; echo -n "$password"; set -x)''
else if config.passwordFile != null
# do not print the password to the console
then ''<(set +x; echo -n "$(cat ${config.passwordFile})"; set -x)''
else if config.keyFile != null
then
lib.warn
("The option `keyFile` is deprecated."
+ "Use passwordFile instead if you want to use interactive login or settings.keyFile if you want to use key file login")
config.keyFile
else null;
keyFileArgs = ''
${lib.optionalString (keyFile != null) "--key-file ${keyFile}"} \
${lib.optionalString (lib.hasAttr "keyFileSize" config.settings) "--keyfile-size ${builtins.toString config.settings.keyFileSize}"} \
${lib.optionalString (lib.hasAttr "keyFileOffset" config.settings) "--keyfile-offset ${builtins.toString config.settings.keyFileOffset}"} \
'';
cryptsetupOpen = ''
cryptsetup open "${config.device}" "${config.name}" \
${lib.optionalString (config.settings.allowDiscards or false) "--allow-discards"} \
${lib.optionalString (config.settings.bypassWorkqueues or false) "--perf-no_read_workqueue --perf-no_write_workqueue"} \
${toString config.extraOpenArgs} \
${keyFileArgs} \
'';
in
{
options = {
type = lib.mkOption {
type = lib.types.enum [ "luks" ];
internal = true;
description = "Type";
};
device = lib.mkOption {
type = lib.types.str;
description = "Device to encrypt";
default = device;
};
name = lib.mkOption {
type = lib.types.str;
description = "Name of the LUKS";
};
keyFile = lib.mkOption {
type = lib.types.nullOr diskoLib.optionTypes.absolute-pathname;
default = null;
description = "DEPRECATED use passwordFile or settings.keyFile. Path to the key for encryption";
example = "/tmp/disk.key";
};
passwordFile = lib.mkOption {
type = lib.types.nullOr diskoLib.optionTypes.absolute-pathname;
default = null;
description = "Path to the file which contains the password for initial encryption";
example = "/tmp/disk.key";
};
askPassword = lib.mkOption {
type = lib.types.bool;
default = config.keyFile == null && config.passwordFile == null && (! config.settings ? "keyFile");
defaultText = "true if neither keyFile nor passwordFile are set";
description = "Whether to ask for a password for initial encryption";
};
settings = lib.mkOption {
type = lib.types.attrsOf lib.types.anything;
default = { };
description = "LUKS settings (as defined in configuration.nix in boot.initrd.luks.devices.<name>)";
example = ''{
keyFile = "/tmp/disk.key";
keyFileSize = 2048;
keyFileOffset = 1024;
fallbackToPassword = true;
allowDiscards = true;
};
'';
};
additionalKeyFiles = lib.mkOption {
type = lib.types.listOf diskoLib.optionTypes.absolute-pathname;
default = [ ];
description = "Path to additional key files for encryption";
example = [ "/tmp/disk2.key" ];
};
initrdUnlock = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether to add a boot.initrd.luks.devices entry for the specified disk.";
};
extraFormatArgs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Extra arguments to pass to `cryptsetup luksFormat` when formatting";
example = [ "--pbkdf argon2id" ];
};
extraOpenArgs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Extra arguments to pass to `cryptsetup luksOpen` when opening";
example = [ "--timeout 10" ];
};
content = diskoLib.deviceType { parent = config; device = "/dev/mapper/${config.name}"; };
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo diskoLib.jsonType;
default = dev:
lib.optionalAttrs (config.content != null) (config.content._meta dev);
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
default = ''
if ! blkid "${config.device}" >/dev/null || ! (blkid "${config.device}" -o export | grep -q '^TYPE='); then
${lib.optionalString config.askPassword ''
askPassword() {
if [ -z ''${IN_DISKO_TEST+x} ]; then
set +x
echo "Enter password for ${config.device}: "
IFS= read -r -s password
echo "Enter password for ${config.device} again to be safe: "
IFS= read -r -s password_check
export password
[ "$password" = "$password_check" ]
set -x
else
export password=disko
fi
}
until askPassword; do
echo "Passwords did not match, please try again."
done
''}
cryptsetup -q luksFormat "${config.device}" ${toString config.extraFormatArgs} ${keyFileArgs}
${cryptsetupOpen} --persistent
${toString (lib.forEach config.additionalKeyFiles (keyFile: ''
cryptsetup luksAddKey "${config.device}" ${keyFile} ${keyFileArgs}
''))}
fi
${lib.optionalString (config.content != null) config.content._create}
'';
};
_mount = diskoLib.mkMountOption {
inherit config options;
default =
let
contentMount = config.content._mount;
in
{
dev = ''
if ! cryptsetup status "${config.name}" >/dev/null 2>/dev/null; then
${lib.optionalString config.askPassword ''
if [ -z ''${IN_DISKO_TEST+x} ]; then
set +x
echo "Enter password for ${config.device}"
IFS= read -r -s password
export password
set -x
else
export password=disko
fi
''}
${cryptsetupOpen}
fi
${lib.optionalString (config.content != null) contentMount.dev or ""}
'';
fs = lib.optionalAttrs (config.content != null) contentMount.fs or { };
};
};
_unmount = diskoLib.mkUnmountOption {
inherit config options;
default =
let
contentUnmount = config.content._unmount;
in
{
dev = ''
${lib.optionalString (config.content != null) contentUnmount.dev or ""}
if cryptsetup status "${config.name}" >/dev/null 2>/dev/null; then
cryptsetup close "${config.name}"
fi
'';
};
};
_config = lib.mkOption {
internal = true;
readOnly = true;
default = [ ]
# If initrdUnlock is true, then add a device entry to the initrd.luks.devices config.
++ (lib.optional config.initrdUnlock [
{
boot.initrd.luks.devices.${config.name} = {
inherit (config) device;
} // config.settings;
}
]) ++ (lib.optional (config.content != null) config.content._config);
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = pkgs: [ pkgs.gnugrep pkgs.cryptsetup ] ++ (lib.optionals (config.content != null) (config.content._pkgs pkgs));
description = "Packages";
};
};
}

View File

@ -1,62 +0,0 @@
{ config, options, lib, diskoLib, parent, device, ... }:
{
options = {
type = lib.mkOption {
type = lib.types.enum [ "lvm_pv" ];
internal = true;
description = "Type";
};
device = lib.mkOption {
type = lib.types.str;
description = "Device";
default = device;
};
vg = lib.mkOption {
type = lib.types.str;
description = "Volume group";
};
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo diskoLib.jsonType;
default = dev: {
deviceDependencies.lvm_vg.${config.vg} = [ dev ];
};
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
default = ''
if ! (blkid "${config.device}" | grep -q 'TYPE='); then
pvcreate "${config.device}"
fi
echo "${config.device}" >>"$disko_devices_dir"/lvm_${lib.escapeShellArg config.vg}
'';
};
_mount = diskoLib.mkMountOption {
inherit config options;
default = { };
};
_unmount = diskoLib.mkUnmountOption {
inherit config options;
default = { };
};
_config = lib.mkOption {
internal = true;
readOnly = true;
default = [ ];
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = pkgs: [ pkgs.gnugrep pkgs.lvm2 ];
description = "Packages";
};
};
}

View File

@ -1,183 +0,0 @@
{ config, options, lib, diskoLib, ... }:
let
# Load kernel modules to ensure device mapper types are available
kernelModules =
[
# Prevent unbootable systems if LVM snapshots are present at boot time.
"dm-snapshot"
] ++
lib.filter (x: x != "") (map
(lv: lib.optionalString (lv.lvm_type != null && lv.lvm_type != "thinlv") "dm-${lv.lvm_type}")
(lib.attrValues config.lvs));
in
{
options = {
name = lib.mkOption {
type = lib.types.str;
default = config._module.args.name;
description = "Name of the volume group";
};
type = lib.mkOption {
type = lib.types.enum [ "lvm_vg" ];
internal = true;
description = "Type";
};
lvs = lib.mkOption {
type = lib.types.attrsOf (lib.types.submodule ({ name, ... }@lv: {
options = {
name = lib.mkOption {
type = lib.types.str;
default = name;
description = "Name of the logical volume";
};
priority = lib.mkOption {
type = lib.types.int;
default = (if lv.config.lvm_type == "thin-pool" then 501 else 1000) + (if lib.hasInfix "100%" lv.config.size then 251 else 0);
defaultText = lib.literalExpression ''
if (lib.hasInfix "100%" lv.config.size) then 9001 else 1000
'';
description = "Priority of the logical volume, smaller values are created first";
};
size = lib.mkOption {
type = lib.types.str; # TODO lvm size type
description = "Size of the logical volume";
};
lvm_type = lib.mkOption {
# TODO: add raid10
type = lib.types.nullOr (lib.types.enum [ "mirror" "raid0" "raid1" "raid4" "raid5" "raid6" "thin-pool" "thinlv" ]); # TODO add all lib.types
default = null; # maybe there is always a default type?
description = "LVM type";
};
extraArgs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Extra arguments";
};
pool = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
description = "Name of pool LV that this LV belongs to";
};
content = diskoLib.partitionType { parent = config; device = "/dev/${config.name}/${lv.config.name}"; };
};
}));
default = { };
description = "LVS for the volume group";
};
_meta = lib.mkOption {
internal = true;
readOnly = true;
type = diskoLib.jsonType;
default =
diskoLib.deepMergeMap
(lv:
lib.optionalAttrs (lv.content != null) (lv.content._meta [ "lvm_vg" config.name ])
)
(lib.attrValues config.lvs);
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
default =
let
sortedLvs = lib.sort (a: b: a.priority < b.priority) (lib.attrValues config.lvs);
in
''
${lib.concatMapStringsSep "\n" (k: ''modprobe "${k}"'') kernelModules}
readarray -t lvm_devices < <(cat "$disko_devices_dir"/lvm_${lib.escapeShellArg config.name})
if ! vgdisplay "${config.name}" >/dev/null; then
vgcreate "${config.name}" \
"''${lvm_devices[@]}"
fi
${lib.concatMapStrings (lv: ''
if ! lvdisplay "${config.name}/${lv.name}"; then
lvcreate \
--yes \
${if (lv.lvm_type == "thinlv") then "-V"
else if lib.hasInfix "%" lv.size then "-l" else "-L"} \
${if lib.hasSuffix "%" lv.size then "${lv.size}FREE" else lv.size} \
-n "${lv.name}" \
${lib.optionalString (lv.lvm_type == "thinlv") "--thinpool=${lv.pool}"} \
${lib.optionalString (lv.lvm_type != null && lv.lvm_type != "thinlv") "--type=${lv.lvm_type}"} \
${toString lv.extraArgs} \
"${config.name}"
fi
'') sortedLvs}
${lib.concatMapStrings (lv: ''
${lib.optionalString (lv.content != null) lv.content._create}
'') sortedLvs}
'';
};
_mount = diskoLib.mkMountOption {
inherit config options;
default =
let
lvMounts = diskoLib.deepMergeMap
(lv:
lib.optionalAttrs (lv.content != null) lv.content._mount
)
(lib.attrValues config.lvs);
in
{
dev = ''
vgchange -a y
${lib.concatMapStrings (x: x.dev or "") (lib.attrValues lvMounts)}
'';
fs = lvMounts.fs or { };
};
};
_unmount = diskoLib.mkUnmountOption {
inherit config options;
default =
let
lvMounts = diskoLib.deepMergeMap
(lv:
lib.optionalAttrs (lv.content != null) lv.content._unmount
)
(lib.attrValues config.lvs);
in {
dev = ''
${lib.concatMapStrings (x: x.dev or "") (lib.attrValues lvMounts)}
vgchange -a n
'';
fs = lvMounts.fs or { };
};
};
_config = lib.mkOption {
internal = true;
readOnly = true;
default = [{ boot.initrd.kernelModules = kernelModules; }] ++
map
(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")
]
++ 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.attrValues config.lvs);
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = pkgs: lib.flatten (map
(lv:
lib.optional (lv.content != null) (lv.content._pkgs pkgs)
)
(lib.attrValues config.lvs));
description = "Packages";
};
};
}

View File

@ -1,99 +0,0 @@
{ config, options, lib, diskoLib, ... }:
{
options = {
name = lib.mkOption {
type = lib.types.str;
default = config._module.args.name;
description = "Name";
};
type = lib.mkOption {
type = lib.types.enum [ "mdadm" ];
default = "mdadm";
internal = true;
description = "Type";
};
level = lib.mkOption {
type = lib.types.int;
default = 1;
description = "mdadm level";
};
metadata = lib.mkOption {
type = lib.types.enum [ "1" "1.0" "1.1" "1.2" "default" "ddf" "imsm" ];
default = "default";
description = "Metadata";
};
content = diskoLib.deviceType { parent = config; device = "/dev/md/${config.name}"; };
_meta = lib.mkOption {
internal = true;
readOnly = true;
type = diskoLib.jsonType;
default =
lib.optionalAttrs (config.content != null) (config.content._meta [ "mdadm" config.name ]);
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
default = ''
if ! test -e "/dev/md/${config.name}"; then
readarray -t disk_devices < <(cat "$disko_devices_dir"/raid_${lib.escapeShellArg config.name})
echo 'y' | mdadm --create "/dev/md/${config.name}" \
--level=${toString config.level} \
--raid-devices="$(wc -l "$disko_devices_dir"/raid_${lib.escapeShellArg config.name} | cut -f 1 -d " ")" \
--metadata=${config.metadata} \
--force \
--homehost=any \
"''${disk_devices[@]}"
partprobe "/dev/md/${config.name}"
udevadm trigger --subsystem-match=block
udevadm settle
# for some reason mdadm devices spawn with an existing partition table, so we need to wipe it
sgdisk --zap-all "/dev/md/${config.name}"
fi
${lib.optionalString (config.content != null) config.content._create}
'';
};
_mount = diskoLib.mkMountOption {
inherit config options;
default =
lib.optionalAttrs (config.content != null) config.content._mount;
# TODO we probably need to assemble the mdadm somehow
};
_unmount = diskoLib.mkUnmountOption {
inherit config options;
default = let
content = lib.optionalAttrs (config.content != null) config.content._unmount;
in {
fs = content.fs;
dev = ''
${content.dev or ""}
if [ -e "/dev/md/${config.name}" ]; then
mdadm --stop "/dev/md/${config.name}"
fi
'';
};
};
_config = lib.mkOption {
internal = true;
readOnly = true;
default =
[
(if lib.versionAtLeast (lib.versions.majorMinor lib.version) "23.11" then {
boot.swraid.enable = true;
} else {
boot.initrd.services.swraid.enable = true;
})
] ++
lib.optional (config.content != null) config.content._config;
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = pkgs: [
pkgs.parted # for partprobe
] ++ (lib.optionals (config.content != null) (config.content._pkgs pkgs));
description = "Packages";
};
};
}

View File

@ -1,60 +0,0 @@
{ config, options, lib, diskoLib, parent, device, ... }:
{
options = {
type = lib.mkOption {
type = lib.types.enum [ "mdraid" ];
internal = true;
description = "Type";
};
device = lib.mkOption {
type = lib.types.str;
description = "Device";
default = device;
};
name = lib.mkOption {
type = lib.types.str;
description = "Name";
};
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo diskoLib.jsonType;
default = dev: {
deviceDependencies.mdadm.${config.name} = [ dev ];
};
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
default = ''
echo "${config.device}" >>"$disko_devices_dir"/raid_${lib.escapeShellArg config.name}
'';
};
_mount = diskoLib.mkMountOption {
inherit config options;
default = { };
};
_unmount = diskoLib.mkUnmountOption {
inherit config options;
default = { };
};
_config = lib.mkOption {
internal = true;
readOnly = true;
default = [ ];
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = pkgs: [ pkgs.mdadm ];
description = "Packages";
};
};
}

View File

@ -1,82 +0,0 @@
{ lib, config, options, diskoLib, rootMountPoint, ... }:
{
options = {
type = lib.mkOption {
type = lib.types.enum [ "nodev" ];
default = "nodev";
internal = true;
description = "Device type";
};
fsType = lib.mkOption {
type = lib.types.str;
description = "File system type";
};
device = lib.mkOption {
type = lib.types.str;
default = "none";
description = "Device to use";
};
mountpoint = lib.mkOption {
type = lib.types.nullOr diskoLib.optionTypes.absolute-pathname;
default = config._module.args.name;
description = "Location to mount the file system at";
};
mountOptions = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "defaults" ];
description = "Options to pass to mount";
};
_meta = lib.mkOption {
internal = true;
readOnly = true;
type = diskoLib.jsonType;
default = { };
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
default = "";
};
_mount = diskoLib.mkMountOption {
inherit config options;
default = lib.optionalAttrs (config.mountpoint != null) {
fs.${config.mountpoint} = ''
if ! findmnt ${config.fsType} "${rootMountPoint}${config.mountpoint}" > /dev/null 2>&1; then
mount -t ${config.fsType} "${config.device}" "${rootMountPoint}${config.mountpoint}" \
${lib.concatMapStringsSep " " (opt: "-o ${opt}") config.mountOptions} \
-o X-mount.mkdir
fi
'';
};
};
_unmount = diskoLib.mkUnmountOption {
inherit config options;
default = lib.optionalAttrs (config.mountpoint != null) {
fs.${config.mountpoint} = ''
if findmnt ${config.fsType} "${rootMountPoint}${config.mountpoint}" > /dev/null 2>&1; then
umount "${rootMountPoint}${config.mountpoint}"
fi
'';
};
};
_config = lib.mkOption {
internal = true;
readOnly = true;
default = lib.optional (config.mountpoint != null) {
fileSystems.${config.mountpoint} = {
device = config.device;
fsType = config.fsType;
options = config.mountOptions;
};
};
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = _pkgs: [ ];
description = "Packages";
};
};
}

View File

@ -1,134 +0,0 @@
{ diskoLib, config, options, lib, parent, device, ... }:
{
options = {
type = lib.mkOption {
type = lib.types.enum [ "swap" ];
internal = true;
description = "Type";
};
device = lib.mkOption {
type = lib.types.str;
default = device;
description = "Device";
};
discardPolicy = lib.mkOption {
default = null;
example = "once";
type = lib.types.nullOr (lib.types.enum [ "once" "pages" "both" ]);
description = ''
Specify the discard policy for the swap device. If "once", then the
whole swap space is discarded at swapon invocation. If "pages",
asynchronous discard on freed pages is performed, before returning to
the available pages pool. With "both", both policies are activated.
See swapon(8) for more information.
'';
};
extraArgs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Extra arguments";
};
mountOptions = lib.mkOption {
type = lib.types.listOf lib.types.nonEmptyStr;
default = [ "defaults" ];
example = [ "nofail" ];
description = "Options used to mount the swap.";
};
priority = lib.mkOption {
type = lib.types.nullOr lib.types.int;
default = null;
description = ''
Specify the priority of the swap device. Priority is a value between 0 and 32767.
Higher numbers indicate higher priority.
null lets the kernel choose a priority, which will show up as a negative value.
'';
};
randomEncryption = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to randomly encrypt the swap";
};
resumeDevice = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to use this as a boot.resumeDevice";
};
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo diskoLib.jsonType;
default = _dev: { };
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
# TODO: we don't support encrypted swap yet
default = lib.optionalString (!config.randomEncryption) ''
if ! blkid "${config.device}" -o export | grep -q '^TYPE='; then
mkswap \
${toString config.extraArgs} \
"${config.device}"
fi
'';
};
_mount = diskoLib.mkMountOption {
inherit config options;
# TODO: we don't support encrypted swap yet
default = lib.optionalAttrs (!config.randomEncryption) {
fs.${config.device} = ''
if test "''${DISKO_SKIP_SWAP:-}" != 1 && ! swapon --show | grep -q "^$(readlink -f "${config.device}") "; then
swapon ${
lib.optionalString (config.discardPolicy != null)
"--discard${lib.optionalString (config.discardPolicy != "both")
"=${config.discardPolicy}"
}"} ${
lib.optionalString (config.priority != null)
"--priority=${toString config.priority}"
} \
--options=${lib.concatStringsSep "," config.mountOptions} \
"${config.device}"
fi
'';
};
};
_unmount = diskoLib.mkUnmountOption {
inherit config options;
default = lib.optionalAttrs (!config.randomEncryption) {
fs.${config.device} = ''
if swapon --show | grep -q "^$(readlink -f "${config.device}") "; then
swapoff "${config.device}"
fi
'';
};
};
_config = lib.mkOption {
internal = true;
readOnly = true;
default = [{
swapDevices = [{
device = config.device;
inherit (config) discardPolicy priority;
randomEncryption = {
enable = config.randomEncryption;
# forward discard/TRIM attempts through dm-crypt
allowDiscards = config.discardPolicy != null;
};
options = config.mountOptions;
}];
boot.resumeDevice = lib.mkIf config.resumeDevice config.device;
}];
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = pkgs: [ pkgs.gnugrep pkgs.util-linux ];
description = "Packages";
};
};
}

View File

@ -1,179 +0,0 @@
{ config, options, lib, diskoLib, parent, device, ... }:
{
options = lib.warn ''
The legacy table is outdated and should not be used. We recommend using the gpt type instead.
Please note that certain features, such as the test framework, may not function properly with the legacy table type.
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.
for a migration you can follow the guide at https://github.com/nix-community/disko/blob/master/docs/table-to-gpt.md
''
{
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 {
type = lib.types.int;
internal = true;
default = lib.toInt (lib.head (builtins.match ".*entry ([[:digit:]]+)]" name));
defaultText = null;
};
};
}));
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 "${diskoLib.hexEscapeUdevSymlink 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 { };
};
};
_unmount = diskoLib.mkUnmountOption {
inherit config options;
default =
let
partMounts = lib.foldr lib.recursiveUpdate { } (map
(partition:
lib.optionalAttrs (partition.content != null) partition.content._unmount
)
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";
};
};
}

View File

@ -1,59 +0,0 @@
{ config, options, lib, diskoLib, parent, device, ... }:
{
options = {
type = lib.mkOption {
type = lib.types.enum [ "zfs" ];
internal = true;
description = "Type";
};
device = lib.mkOption {
type = lib.types.str;
default = device;
description = "Device";
};
pool = lib.mkOption {
type = lib.types.str;
description = "Name of the ZFS pool";
};
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo diskoLib.jsonType;
default = dev: {
deviceDependencies.zpool.${config.pool} = [ dev ];
};
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
default = ''
echo "${config.device}" >>"$disko_devices_dir"/zfs_${lib.escapeShellArg config.pool}
'';
};
_mount = diskoLib.mkMountOption {
inherit config options;
default = { };
};
_unmount = diskoLib.mkUnmountOption {
inherit config options;
default = { };
};
_config = lib.mkOption {
internal = true;
readOnly = true;
default = [ ];
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = pkgs: [ pkgs.zfs ];
description = "Packages";
};
};
}

View File

@ -1,160 +0,0 @@
{ config, options, lib, diskoLib, rootMountPoint, parent, ... }:
{
options = {
name = lib.mkOption {
type = lib.types.str;
default = config._module.args.name;
description = "Name of the dataset";
};
_name = lib.mkOption {
type = lib.types.str;
default = "${config._parent.name}/${config.name}";
internal = true;
description = "Fully quantified name for dataset";
};
type = lib.mkOption {
type = lib.types.enum [ "zfs_fs" ];
default = "zfs_fs";
internal = true;
description = "Type";
};
options = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
description = "Options to set for the dataset";
};
mountOptions = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "defaults" ];
description = "Mount options";
};
mountpoint = lib.mkOption {
type = lib.types.nullOr diskoLib.optionTypes.absolute-pathname;
default = null;
description = "Path to mount the dataset to";
};
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo diskoLib.jsonType;
default = _dev: { };
description = "Metadata";
};
_createFilesystem = lib.mkOption {
internal = true;
type = lib.types.bool;
default = true;
};
_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 =
let
createOptions = (lib.optionalAttrs (config.mountpoint != null) { mountpoint = config.mountpoint; }) // config.options;
# All options defined as PROP_ONETIME or PROP_ONETIME_DEFAULT in https://github.com/openzfs/zfs/blob/master/module/zcommon/zfs_prop.c
onetimeProperties = [
"encryption"
"casesensitivity"
"utf8only"
"normalization"
"volblocksize"
"pbkdf2iters"
"pbkdf2salt"
"keyformat"
];
updateOptions = builtins.removeAttrs createOptions onetimeProperties;
in
''
if ! zfs get type ${config._name} >/dev/null 2>&1; then
${if config._createFilesystem then ''
zfs create -up ${config._name} \
${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "-o ${n}=${v}") (createOptions))}
'' else ''
# don't create anything for root dataset of zpools
true
''}
${lib.optionalString (updateOptions != {}) ''
else
zfs set -u ${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "${n}=${v}") updateOptions)} ${config._name}
''}
fi
'';
};
_mount = diskoLib.mkMountOption {
inherit config options;
default =
(lib.optionalAttrs (config.options.keylocation or "none" != "none") {
dev = ''
if [ "$(zfs get keystatus ${config._name} -H -o value)" == "unavailable" ]; then
zfs load-key ${config._name}
fi
'';
}) // lib.optionalAttrs (config.options.mountpoint or "" != "none" && config.options.canmount or "" != "off") {
fs.${config.mountpoint} = ''
if ! findmnt ${config._name} "${rootMountPoint}${config.mountpoint}" >/dev/null 2>&1; then
mount ${config._name} "${rootMountPoint}${config.mountpoint}" \
-o X-mount.mkdir \
${lib.concatMapStringsSep " " (opt: "-o ${opt}") config.mountOptions} \
${lib.optionalString ((config.options.mountpoint or "") != "legacy") "-o zfsutil"} \
-t zfs
fi
'';
};
};
_unmount = diskoLib.mkUnmountOption {
inherit config options;
default =
(lib.optionalAttrs (config.options.keylocation or "none" != "none") {
dev = "zfs unload-key ${config.name}";
}) // lib.optionalAttrs (config.options.mountpoint or "" != "none" && config.options.canmount or "" != "off") {
fs.${config.mountpoint} = ''
if findmnt ${config._name} "${rootMountPoint}${config.mountpoint}" >/dev/null 2>&1; then
umount "${rootMountPoint}${config.mountpoint}"
fi
'';
};
};
_config = lib.mkOption {
internal = true;
readOnly = true;
default =
lib.optional (config.options.mountpoint or "" != "none" && config.options.canmount or "" != "off") {
fileSystems.${config.mountpoint} = {
device = "${config._name}";
fsType = "zfs";
options = config.mountOptions ++ lib.optional ((config.options.mountpoint or "") != "legacy") "zfsutil";
};
};
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = pkgs: [ pkgs.util-linux ];
description = "Packages";
};
};
}

View File

@ -1,113 +0,0 @@
{ config, options, lib, diskoLib, parent, ... }:
{
options = {
name = lib.mkOption {
type = lib.types.str;
default = config._module.args.name;
description = "Name of the dataset";
};
type = lib.mkOption {
type = lib.types.enum [ "zfs_volume" ];
default = "zfs_volume";
internal = true;
description = "Type";
};
options = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
description = "Options to set for the dataset";
};
extraArgs = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = "Extra arguments passed to `zfs create`";
};
mountOptions = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "defaults" ];
description = "Mount options";
};
# volume options
size = lib.mkOption {
type = lib.types.nullOr lib.types.str; # TODO size
default = null;
description = "Size of the dataset";
};
content = diskoLib.partitionType { parent = config; device = "/dev/zvol/${config._parent.name}/${config.name}"; };
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo diskoLib.jsonType;
default = dev:
lib.optionalAttrs (config.content != null) (config.content._meta dev);
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
default = ''
if ! zfs get type "${config._parent.name}/${config.name}" >/dev/null 2>&1; then
zfs create "${config._parent.name}/${config.name}" \
${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "-o ${n}=${v}") config.options)} \
-V ${config.size} ${toString (builtins.map lib.escapeShellArg config.extraArgs)}
zvol_wait
partprobe "/dev/zvol/${config._parent.name}/${config.name}"
udevadm trigger --subsystem-match=block
udevadm settle
fi
${lib.optionalString (config.content != null) config.content._create}
'';
};
_mount = diskoLib.mkMountOption {
inherit config options;
default = {
dev = ''
${lib.optionalString (config.options.keylocation or "none" != "none") ''
if [ "$(zfs get keystatus ${config.name} -H -o value)" == "unavailable" ]; then
zfs load-key ${config.name}
fi
''}
${config.content._mount.dev or ""}
'';
fs = config.content._mount.fs or { };
};
};
_unmount = diskoLib.mkUnmountOption {
inherit config options;
default = {
dev = ''
${lib.optionalString (config.options.keylocation or "none" != "none") "zfs unload-key ${config.name}"}
${config.content._unmount.dev or ""}
'';
fs = config.content._unmount.fs;
};
};
_config = lib.mkOption {
internal = true;
readOnly = true;
default =
lib.optional (config.content != null) config.content._config;
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = pkgs: [
pkgs.util-linux
pkgs.parted # for partprobe
] ++ lib.optionals (config.content != null) (config.content._pkgs pkgs);
description = "Packages";
};
};
}

View File

@ -1,390 +0,0 @@
{ config, options, lib, diskoLib, rootMountPoint, ... }:
let
# TODO: Consider expanding to handle `file` and `draid` mode options.
modeOptions = [
""
"mirror"
"raidz"
"raidz1"
"raidz2"
"raidz3"
];
in
{
options = {
name = lib.mkOption {
type = lib.types.str;
default = config._module.args.name;
description = "Name of the ZFS pool";
};
type = lib.mkOption {
type = lib.types.enum [ "zpool" ];
default = "zpool";
internal = true;
description = "Type";
};
mode = lib.mkOption {
default = "";
example = {
mode = {
topology = {
type = "topology";
vdev = [
{
# Members can be either specified by a full path or by a disk name
# This is example uses the full path
members = [ "/dev/disk/by-id/wwn-0x5000c500af8b2a14" ];
}
];
log = [
{
# Example using gpt partition labels
# This expects an disk called `ssd` with a gpt partition called `zfs`
# disko.devices.disk.ssd = {
# type = "disk";
# device = "/dev/nvme0n1";
# content = {
# type = "gpt";
# partitions = {
# zfs = {
# size = "100%";
# content = {
# type = "zfs";
# # use your own pool name here
# pool = "zroot";
# };
# };
# };
# };
# };
members = [ "ssd" ];
}
];
};
};
};
type = (lib.types.oneOf [
(lib.types.enum modeOptions)
(lib.types.attrsOf (diskoLib.subType {
types = {
topology =
let
vdev = lib.types.submodule ({ ... }: {
options = {
mode = lib.mkOption {
type = lib.types.enum modeOptions;
default = "";
description = "Mode of the zfs vdev";
};
members = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "Members of the vdev";
};
};
});
in
lib.types.submodule
({ ... }: {
options = {
type = lib.mkOption {
type = lib.types.enum [ "topology" ];
default = "topology";
internal = true;
description = "Type";
};
# zfs device types
vdev = lib.mkOption {
type = lib.types.listOf vdev;
default = [ ];
description = ''
A list of storage vdevs. See
https://openzfs.github.io/openzfs-docs/man/master/7/zpoolconcepts.7.html#Virtual_Devices_(vdevs)
for details.
'';
example = [
{
mode = "mirror";
members = [ "x" "y" ];
}
{
members = [ "z" ];
}
];
};
spare = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = ''
A list of devices to use as hot spares. See
https://openzfs.github.io/openzfs-docs/man/master/7/zpoolconcepts.7.html#Hot_Spares
for details.
'';
example = [ "x" "y" ];
};
log = lib.mkOption {
type = lib.types.listOf vdev;
default = [ ];
description = ''
A list of vdevs used for the zfs intent log (ZIL). See
https://openzfs.github.io/openzfs-docs/man/master/7/zpoolconcepts.7.html#Intent_Log
for details.
'';
example = [
{
mode = "mirror";
members = [ "x" "y" ];
}
{
members = [ "z" ];
}
];
};
dedup = lib.mkOption {
type = lib.types.listOf vdev;
default = [ ];
description = ''
A list of vdevs used for the deduplication table. See
https://openzfs.github.io/openzfs-docs/man/master/7/zpoolconcepts.7.html#dedup
for details.
'';
example = [
{
mode = "mirror";
members = [ "x" "y" ];
}
{
members = [ "z" ];
}
];
};
special = lib.mkOption {
type = lib.types.either (lib.types.listOf vdev) (lib.types.nullOr vdev);
default = [ ];
description = ''
A list of vdevs used as special devices. See
https://openzfs.github.io/openzfs-docs/man/master/7/zpoolconcepts.7.html#special
for details.
'';
example = [
{
mode = "mirror";
members = [ "x" "y" ];
}
{
members = [ "z" ];
}
];
};
cache = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ ];
description = ''
A dedicated zfs cache device (L2ARC). See
https://openzfs.github.io/openzfs-docs/man/master/7/zpoolconcepts.7.html#Cache_Devices
for details.
'';
example = [ "x" "y" ];
};
};
});
};
extraArgs.parent = config;
}))
]);
description = "Mode of the ZFS pool";
};
options = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
description = "Options for the ZFS pool";
};
rootFsOptions = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
description = "Options for the root filesystem";
};
mountpoint = lib.mkOption {
type = lib.types.nullOr diskoLib.optionTypes.absolute-pathname;
default = null;
description = "The mountpoint of the pool";
};
mountOptions = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "defaults" ];
description = "Options to pass to mount";
};
datasets = lib.mkOption {
type = lib.types.attrsOf (diskoLib.subType {
types = { inherit (diskoLib.types) zfs_fs zfs_volume; };
extraArgs.parent = config;
});
description = "List of datasets to define";
};
_meta = lib.mkOption {
internal = true;
readOnly = true;
type = diskoLib.jsonType;
default =
diskoLib.deepMergeMap (dataset: dataset._meta [ "zpool" config.name ]) (lib.attrValues config.datasets);
description = "Metadata";
};
_create = diskoLib.mkCreateOption {
inherit config options;
default =
let
formatOutput = type: mode: members: ''
entries+=("${type} ${mode}=${
lib.concatMapStringsSep " "
(d: if lib.strings.hasPrefix "/" d then d else "/dev/disk/by-partlabel/disk-${d}-zfs") members
}")
'';
formatVdev = type: vdev: formatOutput type vdev.mode vdev.members;
formatVdevList = type: vdevs: lib.concatMapStrings
(formatVdev type)
(builtins.sort (a: _: a.mode == "") vdevs);
hasTopology = !(builtins.isString config.mode);
mode = if hasTopology then "prescribed" else config.mode;
topology = lib.optionalAttrs hasTopology config.mode.topology;
in
''
readarray -t zfs_devices < <(cat "$disko_devices_dir/zfs_${config.name}")
if [ ''${#zfs_devices[@]} -eq 0 ]; then
echo "no devices found for zpool ${config.name}. Did you misspell the pool name?" >&2
exit 1
fi
# Try importing the pool without mounting anything if it exists.
# This allows us to set mounpoints.
if zpool import -N -f "${config.name}" || zpool list "${config.name}"; then
echo "not creating zpool ${config.name} as a pool with that name already exists" >&2
else
continue=1
for dev in "''${zfs_devices[@]}"; do
if ! blkid "$dev" >/dev/null; then
# blkid fails, so device seems empty
:
elif (blkid "$dev" -o export | grep '^PTUUID='); then
echo "device $dev already has a partuuid, skipping creating zpool ${config.name}" >&2
continue=0
elif (blkid "$dev" -o export | grep '^TYPE=zfs_member'); then
# zfs_member is a zfs partition, so we try to add the device to the pool
:
elif (blkid "$dev" -o export | grep '^TYPE='); then
echo "device $dev already has a partition, skipping creating zpool ${config.name}" >&2
continue=0
fi
done
if [ $continue -eq 1 ]; then
topology=""
# For shell check
mode="${mode}"
if [ "$mode" != "prescribed" ]; then
topology="${mode} ''${zfs_devices[*]}"
else
entries=()
${lib.optionalString (hasTopology && topology.vdev != null)
(formatVdevList "" topology.vdev)}
${lib.optionalString (hasTopology && topology.spare != [])
(formatOutput "spare" "" topology.spare)}
${lib.optionalString (hasTopology && topology.log != [])
(formatVdevList "log" topology.log)}
${lib.optionalString (hasTopology && topology.dedup != [])
(formatVdevList "dedup" topology.dedup)}
${lib.optionalString (hasTopology && topology.special != null && topology.special != [])
(formatVdevList "special" (lib.lists.toList topology.special))}
${lib.optionalString (hasTopology && topology.cache != [])
(formatOutput "cache" "" topology.cache)}
all_devices=()
last_type=
for line in "''${entries[@]}"; do
# lineformat is type mode=device1 device2 device3
mode="''${line%%=*}"
type="''${mode%% *}"
mode="''${mode#"$type "}"
devs="''${line#*=}"
IFS=' ' read -r -a devices <<< "$devs"
all_devices+=("''${devices[@]}")
if ! [ "$type" = "$last_type" ]; then
topology+=" $type"
last_type="$type"
fi
topology+=" ''${mode} ''${devices[*]}"
done
# all_devices sorted should equal zfs_devices sorted
all_devices_list=$(echo "''${all_devices[*]}" | tr ' ' '\n' | sort)
zfs_devices_list=$(echo "''${zfs_devices[*]}" | tr ' ' '\n' | sort)
if [[ "$all_devices_list" != "$zfs_devices_list" ]]; then
echo "not all disks accounted for, skipping creating zpool ${config.name}" >&2
diff <(echo "$all_devices_list" ) <(echo "$zfs_devices_list") >&2
continue=0
fi
fi
fi
if [ $continue -eq 1 ]; then
zpool create -f "${config.name}" \
-R ${rootMountPoint} \
${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "-o ${n}=${v}") config.options)} \
${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "-O ${n}=${v}") config.rootFsOptions)} \
''${topology:+ $topology}
if [[ $(zfs get -H mounted "${config.name}" | cut -f3) == "yes" ]]; then
zfs unmount "${config.name}"
fi
fi
fi
${lib.concatMapStrings (dataset: dataset._create) (lib.attrValues config.datasets)}
'';
};
_mount = diskoLib.mkMountOption {
inherit config options;
default =
let
datasetFilesystemsMounts = diskoLib.deepMergeMap (dataset: dataset._mount.fs or {}) (lib.attrValues config.datasets);
in
{
dev = ''
zpool list "${config.name}" >/dev/null 2>/dev/null ||
zpool import -l -R ${rootMountPoint} "${config.name}"
${lib.concatMapStrings (x: x._mount.dev or "") (lib.attrValues config.datasets)}
'';
fs = datasetFilesystemsMounts;
};
};
_unmount = diskoLib.mkUnmountOption {
inherit config options;
default = {
dev = ''
${lib.concatMapStrings (dataset: dataset._unmount.dev or "") (lib.attrValues config.datasets)}
if zpool list "${config.name}" >/dev/null 2>/dev/null; then
zpool export "${config.name}"
fi
'';
fs = diskoLib.deepMergeMap (dataset: dataset._unmount.fs or {}) (lib.attrValues config.datasets);
};
};
_config = lib.mkOption {
internal = true;
readOnly = true;
default = map (dataset: dataset._config) (lib.attrValues config.datasets);
description = "NixOS configuration";
};
_pkgs = lib.mkOption {
internal = true;
readOnly = true;
type = lib.types.functionTo (lib.types.listOf lib.types.package);
default = pkgs: [ pkgs.gnugrep pkgs.util-linux ] ++ lib.flatten (map (dataset: dataset._pkgs pkgs) (lib.attrValues config.datasets));
description = "Packages";
};
};
config = {
datasets."__root" = {
_name = config.name;
_createFilesystem = false;
type = "zfs_fs";
mountpoint = config.mountpoint;
options = config.rootFsOptions;
mountOptions = config.mountOptions;
};
};
}

View File

@ -1,266 +0,0 @@
{ config, lib, pkgs, extendModules, diskoLib, ... }:
let
cfg = config.disko;
vmVariantWithDisko = extendModules {
modules = [
./lib/interactive-vm.nix
config.disko.tests.extraConfig
];
};
in
{
imports = [ ./lib/make-disk-image.nix ];
options.disko = {
imageBuilder = {
qemu = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = ''
the qemu emulator string used when building disk images via make-disk-image.nix.
Useful when using binfmt on your build host, and wanting to build disk
images for a foreign architecture
'';
default = null;
example = lib.literalExpression "\${pkgs.qemu_kvm}/bin/qemu-system-aarch64";
};
pkgs = lib.mkOption {
type = lib.types.attrs;
description = ''
the pkgs instance used when building disk images via make-disk-image.nix.
Useful when the config's kernel won't boot in the image-builder.
'';
default = pkgs;
defaultText = lib.literalExpression "pkgs";
example = lib.literalExpression "pkgs";
};
kernelPackages = lib.mkOption {
type = lib.types.attrs;
description = ''
the kernel used when building disk images via make-disk-image.nix.
Useful when the config's kernel won't boot in the image-builder.
'';
default = config.boot.kernelPackages;
defaultText = lib.literalExpression "config.boot.kernelPackages";
example = lib.literalExpression "pkgs.linuxPackages_testing";
};
extraRootModules = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = ''
extra kernel modules to pass to the vmTools.runCommand invocation in the make-disk-image.nix builder
'';
default = [ ];
example = [ "bcachefs" ];
};
extraPostVM = lib.mkOption {
type = lib.types.lines;
description = ''
extra shell code to execute once the disk image(s) have been succesfully created and moved to $out
'';
default = "";
example = lib.literalExpression ''
''${pkgs.zstd}/bin/zstd --compress $out/*raw
rm $out/*raw
'';
};
extraDependencies = lib.mkOption {
type = lib.types.listOf lib.types.package;
description = ''
list of extra packages to make available in the make-disk-image.nix VM builder, an example might be f2fs-tools
'';
default = [ ];
};
name = lib.mkOption {
type = lib.types.str;
description = "name for the disk images";
default = "${config.networking.hostName}-disko-images";
defaultText = "\${config.networking.hostName}-disko-images";
};
copyNixStore = lib.mkOption {
type = lib.types.bool;
description = "whether to copy the nix store into the disk images we just created";
default = true;
};
extraConfig = lib.mkOption {
description = ''
Extra NixOS config for your test. Can be used to specify a different luks key for tests.
A dummy key is in /tmp/secret.key
'';
default = { };
};
imageFormat = lib.mkOption {
type = lib.types.enum [ "raw" "qcow2" ];
description = "QEMU image format to use for the disk images";
default = "raw";
};
};
memSize = lib.mkOption {
type = lib.types.int;
description = ''
size of the memory passed to runInLinuxVM, in megabytes
'';
default = 1024;
};
devices = lib.mkOption {
type = diskoLib.toplevel;
default = { };
description = "The devices to set up";
};
rootMountPoint = lib.mkOption {
type = lib.types.str;
default = "/mnt";
description = "Where the device tree should be mounted by the mountScript";
};
enableConfig = lib.mkOption {
description = ''
configure nixos with the specified devices
should be true if the system is booted with those devices
should be false on an installer image etc.
'';
type = lib.types.bool;
default = true;
};
checkScripts = lib.mkOption {
description = ''
Whether to run shellcheck on script outputs
'';
type = lib.types.bool;
default = false;
};
testMode = lib.mkOption {
internal = true;
description = ''
this is true if the system is being run in test mode.
like a vm test or an interactive vm
'';
type = lib.types.bool;
default = false;
};
tests = {
bootCommands = lib.mkOption {
description = ''
NixOS test script commands to run after the machine has started. Can
be used to enter an interactive password.
'';
type = lib.types.lines;
default = "";
};
efi = lib.mkOption {
description = ''
Whether efi is enabled for the `system.build.installTest`.
We try to automatically detect efi based on the configured bootloader.
'';
type = lib.types.bool;
defaultText = "config.boot.loader.systemd-boot.enable || config.boot.loader.grub.efiSupport";
default = config.boot.loader.systemd-boot.enable || config.boot.loader.grub.efiSupport;
};
enableOCR = lib.mkOption {
description = ''
Sets the enableOCR option in the NixOS VM test driver.
'';
type = lib.types.bool;
default = false;
};
extraChecks = lib.mkOption {
description = ''
extra checks to run in the `system.build.installTest`.
'';
type = lib.types.lines;
default = "";
example = ''
machine.succeed("test -e /var/secrets/my.secret")
'';
};
extraConfig = lib.mkOption {
description = ''
Extra NixOS config for your test. Can be used to specify a different luks key for tests.
A dummy key is in /tmp/secret.key
'';
default = { };
};
};
};
options.virtualisation.vmVariantWithDisko = lib.mkOption {
description = ''
Machine configuration to be added for the vm script available at `.system.build.vmWithDisko`.
'';
inherit (vmVariantWithDisko) type;
default = { };
visible = "shallow";
};
config = {
assertions = [
{
assertion = config.disko.imageBuilder.qemu != null -> diskoLib.vmToolsSupportsCustomQemu lib;
message = ''
You have set config.disko.imageBuild.qemu, but vmTools in your nixpkgs version "${lib.version}"
does not support overriding the qemu package with the customQemu option yet.
Please upgrade nixpkgs so that `lib.version` is at least "24.11.20240709".
'';
}
];
_module.args.diskoLib = import ./lib {
inherit lib;
rootMountPoint = config.disko.rootMountPoint;
makeTest = import (pkgs.path + "/nixos/tests/make-test-python.nix");
eval-config = import (pkgs.path + "/nixos/lib/eval-config.nix");
};
system.build = (cfg.devices._scripts { inherit pkgs; checked = cfg.checkScripts; }) // (
let
throwIfNoDisksDetected = _: v: if cfg.devices.disk == { } then throw "No disks defined, did you forget to import your disko config?" else v;
in
lib.mapAttrs throwIfNoDisksDetected {
# we keep these old outputs for compatibility
disko = builtins.trace "the .disko output is deprecated, please use .diskoScript instead" (cfg.devices._scripts { inherit pkgs; }).diskoScript;
diskoNoDeps = builtins.trace "the .diskoNoDeps output is deprecated, please use .diskoScriptNoDeps instead" (cfg.devices._scripts { inherit pkgs; }).diskoScriptNoDeps;
installTest = diskoLib.testLib.makeDiskoTest {
inherit extendModules pkgs;
name = "${config.networking.hostName}-disko";
disko-config = builtins.removeAttrs config [ "_module" ];
testMode = "direct";
bootCommands = cfg.tests.bootCommands;
efi = cfg.tests.efi;
enableOCR = cfg.tests.enableOCR;
extraSystemConfig = cfg.tests.extraConfig;
extraTestScript = cfg.tests.extraChecks;
};
vmWithDisko = lib.mkDefault config.virtualisation.vmVariantWithDisko.system.build.vmWithDisko;
}
);
# we need to specify the keys here, so we don't get an infinite recursion error
# Remember to add config keys here if they are added to types
fileSystems = lib.mkIf
cfg.enableConfig cfg.devices._config.fileSystems or { };
boot = lib.mkIf
cfg.enableConfig cfg.devices._config.boot or { };
swapDevices = lib.mkIf
cfg.enableConfig cfg.devices._config.swapDevices or [ ];
};
}

View File

@ -1,38 +0,0 @@
{ stdenvNoCC, makeWrapper, lib, path, nix, coreutils, nixos-install-tools, binlore, diskoVersion }:
let
self = stdenvNoCC.mkDerivation (finalAttrs: {
name = "disko";
src = ./.;
nativeBuildInputs = [
makeWrapper
];
installPhase = ''
mkdir -p $out/bin $out/share/disko
cp -r install-cli.nix cli.nix default.nix disk-deactivate lib $out/share/disko
for i in disko disko-install; do
sed -e "s|libexec_dir=\".*\"|libexec_dir=\"$out/share/disko\"|" "$i" > "$out/bin/$i"
chmod 755 "$out/bin/$i"
wrapProgram "$out/bin/$i" \
--set DISKO_VERSION "${diskoVersion}" \
--prefix PATH : ${lib.makeBinPath [ nix coreutils nixos-install-tools ]} \
--prefix NIX_PATH : "nixpkgs=${path}"
done
'';
# Otherwise resholve thinks that disko and disko-install might be able to execute their arguments
passthru.binlore.out = binlore.synthesize self ''
execer cannot bin/.disko-wrapped
execer cannot bin/.disko-install-wrapped
'';
meta = with lib; {
description = "Format disks with nix-config";
homepage = "https://github.com/nix-community/disko";
license = licenses.mit;
maintainers = with maintainers; [ lassulus ];
platforms = platforms.linux;
mainProgram = finalAttrs.name;
};
});
in
self

View File

@ -1,18 +0,0 @@
{
lib,
writeShellApplication,
bash,
coreutils,
git,
nix-fast-build,
}:
writeShellApplication {
name = "create-release";
runtimeInputs = [
bash
git
coreutils
nix-fast-build
];
text = lib.readFile ./create-release.sh;
}

View File

@ -1,65 +0,0 @@
# Don't run directly! Instead, use
# nix run .#create-release
version=${1:-}
if [[ -z "$version" ]]; then
echo "USAGE: nix run .#create-release -- <version>" >&2
exit 1
fi
# Check if we're running from the root of the repository
if [[ ! -f "flake.nix" || ! -f "version.nix" ]]; then
echo "This script must be run from the root of the repository" >&2
exit 1
fi
# Check if the version matches the semver pattern (without suffixes)
semver_regex="^([0-9]+)\.([0-9]+)\.([0-9]+)$"
if [[ ! "$version" =~ $semver_regex ]]; then
echo "Version must match the semver pattern (e.g., 1.0.0, 2.3.4)" >&2
exit 1
fi
if [[ "$(git symbolic-ref --short HEAD)" != "master" ]]; then
echo "must be on master branch" >&2
exit 1
fi
# Ensure there are no uncommitted or unpushed changes
uncommited_changes=$(git diff --compact-summary)
if [[ -n "$uncommited_changes" ]]; then
echo -e "There are uncommited changes, exiting:\n${uncommited_changes}" >&2
exit 1
fi
git pull git@github.com:nix-community/disko master
unpushed_commits=$(git log --format=oneline origin/master..master)
if [[ "$unpushed_commits" != "" ]]; then
echo -e "\nThere are unpushed changes, exiting:\n$unpushed_commits" >&2
exit 1
fi
# Run all tests to ensure we don't release a broken version
# Two workers are safe on systems with at least 16GB of RAM
nix-fast-build --no-link -j 2 --eval-workers 2 --flake .#checks
# Update the version file
echo "{ version = \"$version\"; released = true; }" > version.nix
# Commit and tag the release
git commit -am "release: v$version"
git tag -a "v$version" -m "release: v$version"
git tag -d "latest"
git tag -a "latest" -m "release: v$version"
# a revsion suffix when run from the tagged release commit
echo "{ version = \"$version\"; released = false; }" > version.nix
git commit -am "release: reset released flag"
echo "Release was prepared successfully!"
echo "To push the release, run the following command:"
echo
echo " git push origin master v$version && git push --force origin latest"
echo
echo "After that, create a release on GitHub:"
echo
echo " https://github.com/nix-community/disko/releases/new"

View File

@ -1,4 +0,0 @@
disabled = [
"manual_inherit", # Prefer `inherit types;` instead of `types = types;`
"manual_inherit_from", # Prefer `inherit (eval) options;` instead of `options = eval.options`.
]

View File

@ -1,16 +0,0 @@
{ pkgs ? import <nixpkgs> { }
, diskoLib ? pkgs.callPackage ../lib { }
}:
diskoLib.testLib.makeDiskoTest {
inherit pkgs;
name = "bcachefs";
disko-config = ../example/bcachefs.nix;
extraTestScript = ''
machine.succeed("mountpoint /");
machine.succeed("lsblk >&2");
'';
# so that the installer boots with a bcachefs enabled kernel
extraInstallerConfig = {
boot.supportedFilesystems = [ "bcachefs" ];
};
}

View File

@ -1,16 +0,0 @@
{ pkgs ? import <nixpkgs> { }
, diskoLib ? pkgs.callPackage ../lib { }
}:
diskoLib.testLib.makeDiskoTest {
inherit pkgs;
name = "boot-raid1";
disko-config = ../example/boot-raid1.nix;
extraTestScript = ''
machine.succeed("test -b /dev/md/boot");
machine.succeed("mountpoint /boot");
'';
extraSystemConfig = {
# sadly systemd-boot fails to install to a raid /boot device
boot.loader.systemd-boot.enable = false;
};
}

View File

@ -1,11 +0,0 @@
{ pkgs ? import <nixpkgs> { }
, diskoLib ? pkgs.callPackage ../lib { }
}:
diskoLib.testLib.makeDiskoTest {
inherit pkgs;
name = "btrfs-only-root-subvolume";
disko-config = ../example/btrfs-only-root-subvolume.nix;
extraTestScript = ''
machine.succeed("btrfs subvolume list /");
'';
}

View File

@ -1,20 +0,0 @@
{ pkgs ? import <nixpkgs> { }
, diskoLib ? pkgs.callPackage ../lib { }
}:
diskoLib.testLib.makeDiskoTest {
inherit pkgs;
name = "btrfs-subvolumes";
disko-config = ../example/btrfs-subvolumes.nix;
extraTestScript = ''
machine.succeed("test ! -e /test");
machine.succeed("test -e /home/user");
machine.succeed("btrfs subvolume list / | grep -qs 'path test$'");
machine.succeed("btrfs subvolume list / | grep -qs 'path nix$'");
machine.succeed("btrfs subvolume list / | grep -qs 'path home$'");
machine.succeed("test -e /.swapvol/swapfile");
machine.succeed("test -e /.swapvol/rel-path");
machine.succeed("test -e /partition-root/swapfile");
machine.succeed("test -e /partition-root/swapfile1");
'';
}

View File

@ -1,34 +0,0 @@
{ pkgs ? import <nixpkgs> { }
, diskoLib ? pkgs.callPackage ../lib { }
}:
diskoLib.testLib.makeDiskoTest {
inherit pkgs;
name = "cli";
disko-config = ../example/complex.nix;
extraInstallerConfig.networking.hostId = "8425e349";
extraSystemConfig = {
networking.hostId = "8425e349";
fileSystems."/zfs_legacy_fs".options = [ "nofail" ]; # TODO find out why we need this!
fileSystems."/zfs_fs".options = [ "nofail" ]; # TODO find out why we need this!
};
testMode = "direct";
extraTestScript = ''
machine.succeed("test -b /dev/md/raid1p1");
machine.succeed("mountpoint /zfs_fs");
machine.succeed("mountpoint /zfs_legacy_fs");
machine.succeed("mountpoint /ext4onzfs");
machine.succeed("mountpoint /ext4_on_lvm");
'';
extraSystemConfig = {
imports = [
../module.nix
];
};
extraInstallerConfig = {
boot.kernelModules = [ "dm-raid" "dm-mirror" ];
imports = [
../module.nix
];
};
}

View File

@ -1,29 +0,0 @@
{ pkgs ? import <nixpkgs> { }
, diskoLib ? pkgs.callPackage ../lib { }
}:
diskoLib.testLib.makeDiskoTest {
inherit pkgs;
name = "complex";
disko-config = ../example/complex.nix;
extraInstallerConfig.networking.hostId = "8425e349";
extraSystemConfig = {
networking.hostId = "8425e349";
fileSystems."/zfs_legacy_fs".options = [ "nofail" ]; # TODO find out why we need this!
fileSystems."/zfs_fs".options = [ "nofail" ]; # TODO find out why we need this!
};
extraTestScript = ''
machine.succeed("test -b /dev/md/raid1p1");
machine.succeed("mountpoint /zfs_fs");
machine.succeed("mountpoint /zfs_legacy_fs");
machine.succeed("mountpoint /ext4onzfs");
machine.succeed("mountpoint /ext4_on_lvm");
machine.succeed("test -e /ext4_on_lvm/file-from-postMountHook");
'';
extraInstallerConfig = {
boot.kernelModules = [ "dm-raid" "dm-mirror" ];
};
}

View File

@ -1,20 +0,0 @@
{ makeTest ? import <nixpkgs/nixos/tests/make-test-python.nix>
, eval-config ? import <nixpkgs/nixos/lib/eval-config.nix>
, pkgs ? import <nixpkgs> { }
}:
let
lib = pkgs.lib;
diskoLib = import ../lib { inherit lib makeTest eval-config; };
allTestFilenames =
builtins.map (lib.removeSuffix ".nix") (
builtins.filter
(x: lib.hasSuffix ".nix" x && x != "default.nix")
(lib.attrNames (builtins.readDir ./.))
);
incompatibleTests = lib.optionals pkgs.stdenv.buildPlatform.isRiscV64 [ "zfs" "zfs-over-legacy" "cli" "module" "complex" ];
allCompatibleFilenames = lib.subtractLists incompatibleTests allTestFilenames;
allTests = lib.genAttrs allCompatibleFilenames (test: import (./. + "/${test}.nix") { inherit diskoLib pkgs; });
in
allTests

Some files were not shown because too many files have changed in this diff Show More