add diskco pkg
This commit is contained in:
parent
dca840c3f1
commit
da11e4ce56
6
pkgs/disko/.github/dependabot.yml
vendored
Normal file
6
pkgs/disko/.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
22
pkgs/disko/.github/workflows/publish.yml
vendored
Normal file
22
pkgs/disko/.github/workflows/publish.yml
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
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 }}"
|
||||||
21
pkgs/disko/.github/workflows/update-flake-lock.yml
vendored
Normal file
21
pkgs/disko/.github/workflows/update-flake-lock.yml
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
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
|
||||||
4
pkgs/disko/.gitignore
vendored
Normal file
4
pkgs/disko/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
result
|
||||||
|
|
||||||
|
# Created by the NixOS interactive test driver
|
||||||
|
.nixos-test-history
|
||||||
12
pkgs/disko/.mergify.yml
Normal file
12
pkgs/disko/.mergify.yml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
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: {}
|
||||||
39
pkgs/disko/CONTRIBUTING.md
Normal file
39
pkgs/disko/CONTRIBUTING.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# 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.
|
||||||
21
pkgs/disko/LICENSE
Normal file
21
pkgs/disko/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018, 2019, 2022–2024 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.
|
||||||
142
pkgs/disko/README.md
Normal file
142
pkgs/disko/README.md
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
# 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/).
|
||||||
|

|
||||||
|
|
||||||
|
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.
|
||||||
116
pkgs/disko/cli.nix
Normal file
116
pkgs/disko/cli.nix
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
{ 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
|
||||||
65
pkgs/disko/default.nix
Normal file
65
pkgs/disko/default.nix
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
{ 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;
|
||||||
|
}
|
||||||
8
pkgs/disko/disk-deactivate/disk-deactivate
Executable file
8
pkgs/disko/disk-deactivate/disk-deactivate
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#!/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
|
||||||
86
pkgs/disko/disk-deactivate/disk-deactivate.jq
Normal file
86
pkgs/disko/disk-deactivate/disk-deactivate.jq
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# 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")
|
||||||
|
|
||||||
193
pkgs/disko/disko
Executable file
193
pkgs/disko/disko
Executable file
@ -0,0 +1,193 @@
|
|||||||
|
#!/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
|
||||||
273
pkgs/disko/disko-install
Executable file
273
pkgs/disko/disko-install
Executable file
@ -0,0 +1,273 @@
|
|||||||
|
#!/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
|
||||||
43
pkgs/disko/doc.nix
Normal file
43
pkgs/disko/doc.nix
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{ 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
|
||||||
|
''
|
||||||
178
pkgs/disko/docs/HowTo.md
Normal file
178
pkgs/disko/docs/HowTo.md
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
# 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.
|
||||||
22
pkgs/disko/docs/INDEX.md
Normal file
22
pkgs/disko/docs/INDEX.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# 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)
|
||||||
122
pkgs/disko/docs/disko-images.md
Normal file
122
pkgs/disko/docs/disko-images.md
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
# 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.
|
||||||
233
pkgs/disko/docs/disko-install.md
Normal file
233
pkgs/disko/docs/disko-install.md
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
# 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.
|
||||||
26
pkgs/disko/docs/interactive-vm.md
Normal file
26
pkgs/disko/docs/interactive-vm.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# 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
|
||||||
BIN
pkgs/disko/docs/logo.png
Normal file
BIN
pkgs/disko/docs/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
225
pkgs/disko/docs/quickstart.md
Normal file
225
pkgs/disko/docs/quickstart.md
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
# 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
|
||||||
|
```
|
||||||
47
pkgs/disko/docs/reference.md
Normal file
47
pkgs/disko/docs/reference.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# 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
|
||||||
|
```
|
||||||
9
pkgs/disko/docs/requirements.md
Normal file
9
pkgs/disko/docs/requirements.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# disko - Declarative disk partitioning
|
||||||
|
|
||||||
|
<img title="" src="./logo.jpeg" alt="" width="220">
|
||||||
|
|
||||||
|
[Documentation Index](./INDEX.md)
|
||||||
|
|
||||||
|
## System Requirements
|
||||||
|
|
||||||
|
TODO: Populate this
|
||||||
9
pkgs/disko/docs/supportmatrix.md
Normal file
9
pkgs/disko/docs/supportmatrix.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# disko - Declarative disk partitioning
|
||||||
|
|
||||||
|
<img title="" src="./logo.jpeg" alt="" width="220">
|
||||||
|
|
||||||
|
[Documentation Index](./INDEX.md)
|
||||||
|
|
||||||
|
## Support Matrix
|
||||||
|
|
||||||
|
TODO: Populate this
|
||||||
137
pkgs/disko/docs/table-to-gpt.md
Normal file
137
pkgs/disko/docs/table-to-gpt.md
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
# 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
160
pkgs/disko/docs/testing.md
Normal file
160
pkgs/disko/docs/testing.md
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
# 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.
|
||||||
173
pkgs/disko/docs/upgrade-guide.md
Normal file
173
pkgs/disko/docs/upgrade-guide.md
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
# 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
|
||||||
34
pkgs/disko/example/bcachefs.nix
Normal file
34
pkgs/disko/example/bcachefs.nix
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
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 = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
89
pkgs/disko/example/boot-raid1.nix
Normal file
89
pkgs/disko/example/boot-raid1.nix
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
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 = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
38
pkgs/disko/example/btrfs-only-root-subvolume.nix
Normal file
38
pkgs/disko/example/btrfs-only-root-subvolume.nix
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
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" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
77
pkgs/disko/example/btrfs-subvolumes.nix
Normal file
77
pkgs/disko/example/btrfs-subvolumes.nix
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
{
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
192
pkgs/disko/example/complex.nix
Normal file
192
pkgs/disko/example/complex.nix
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
{
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
41
pkgs/disko/example/f2fs.nix
Normal file
41
pkgs/disko/example/f2fs.nix
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
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"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
28
pkgs/disko/example/gpt-bios-compat.nix
Normal file
28
pkgs/disko/example/gpt-bios-compat.nix
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# 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 = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
48
pkgs/disko/example/gpt-name-with-whitespace.nix
Normal file
48
pkgs/disko/example/gpt-name-with-whitespace.nix
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
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 = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
36
pkgs/disko/example/gpt-unformatted.nix
Normal file
36
pkgs/disko/example/gpt-unformatted.nix
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# 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 = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
48
pkgs/disko/example/hybrid-mbr.nix
Normal file
48
pkgs/disko/example/hybrid-mbr.nix
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
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 = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
44
pkgs/disko/example/hybrid-tmpfs-on-root.nix
Normal file
44
pkgs/disko/example/hybrid-tmpfs-on-root.nix
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
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"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
37
pkgs/disko/example/hybrid.nix
Normal file
37
pkgs/disko/example/hybrid.nix
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
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 = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
50
pkgs/disko/example/legacy-table-with-whitespace.nix
Normal file
50
pkgs/disko/example/legacy-table-with-whitespace.nix
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
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 = "/";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
40
pkgs/disko/example/legacy-table.nix
Normal file
40
pkgs/disko/example/legacy-table.nix
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
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 = "/";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
34
pkgs/disko/example/long-device-name.nix
Normal file
34
pkgs/disko/example/long-device-name.nix
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
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 = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
78
pkgs/disko/example/luks-btrfs-raid.nix
Normal file
78
pkgs/disko/example/luks-btrfs-raid.nix
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
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"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
61
pkgs/disko/example/luks-btrfs-subvolumes.nix
Normal file
61
pkgs/disko/example/luks-btrfs-subvolumes.nix
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
39
pkgs/disko/example/luks-interactive-login.nix
Normal file
39
pkgs/disko/example/luks-interactive-login.nix
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
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 = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
73
pkgs/disko/example/luks-lvm.nix
Normal file
73
pkgs/disko/example/luks-lvm.nix
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
58
pkgs/disko/example/luks-on-mdadm.nix
Normal file
58
pkgs/disko/example/luks-on-mdadm.nix
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
{ 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 = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
94
pkgs/disko/example/lvm-raid.nix
Normal file
94
pkgs/disko/example/lvm-raid.nix
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
{
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
64
pkgs/disko/example/lvm-sizes-sort.nix
Normal file
64
pkgs/disko/example/lvm-sizes-sort.nix
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
69
pkgs/disko/example/lvm-thin.nix
Normal file
69
pkgs/disko/example/lvm-thin.nix
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
65
pkgs/disko/example/mdadm-raid0.nix
Normal file
65
pkgs/disko/example/mdadm-raid0.nix
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
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 = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
65
pkgs/disko/example/mdadm.nix
Normal file
65
pkgs/disko/example/mdadm.nix
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
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 = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
40
pkgs/disko/example/multi-device-no-deps.nix
Normal file
40
pkgs/disko/example/multi-device-no-deps.nix
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
23
pkgs/disko/example/negative-size.nix
Normal file
23
pkgs/disko/example/negative-size.nix
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
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 = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
109
pkgs/disko/example/non-root-zfs.nix
Normal file
109
pkgs/disko/example/non-root-zfs.nix
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
{
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
33
pkgs/disko/example/simple-efi.nix
Normal file
33
pkgs/disko/example/simple-efi.nix
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
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 = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
54
pkgs/disko/example/stand-alone/configuration.nix
Normal file
54
pkgs/disko/example/stand-alone/configuration.nix
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
{ 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))
|
||||||
|
];
|
||||||
|
}
|
||||||
49
pkgs/disko/example/swap.nix
Normal file
49
pkgs/disko/example/swap.nix
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
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
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
41
pkgs/disko/example/tmpfs.nix
Normal file
41
pkgs/disko/example/tmpfs.nix
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
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"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
27
pkgs/disko/example/with-lib.nix
Normal file
27
pkgs/disko/example/with-lib.nix
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# 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 = "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
34
pkgs/disko/example/xfs-with-quota.nix
Normal file
34
pkgs/disko/example/xfs-with-quota.nix
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
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" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
58
pkgs/disko/example/zfs-over-legacy.nix
Normal file
58
pkgs/disko/example/zfs-over-legacy.nix
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
305
pkgs/disko/example/zfs-with-vdevs.nix
Normal file
305
pkgs/disko/example/zfs-with-vdevs.nix
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
{
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
119
pkgs/disko/example/zfs.nix
Normal file
119
pkgs/disko/example/zfs.nix
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
{
|
||||||
|
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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
27
pkgs/disko/flake.lock
Normal file
27
pkgs/disko/flake.lock
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
155
pkgs/disko/flake.nix
Normal file
155
pkgs/disko/flake.nix
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
{
|
||||||
|
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 "$@"
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
69
pkgs/disko/install-cli.nix
Normal file
69
pkgs/disko/install-cli.nix
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
{ 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;
|
||||||
|
}
|
||||||
841
pkgs/disko/lib/default.nix
Normal file
841
pkgs/disko/lib/default.nix
Normal file
@ -0,0 +1,841 @@
|
|||||||
|
{ 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
|
||||||
81
pkgs/disko/lib/interactive-vm.nix
Normal file
81
pkgs/disko/lib/interactive-vm.nix
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
{ 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
|
||||||
|
'';
|
||||||
|
}
|
||||||
228
pkgs/disko/lib/make-disk-image.nix
Normal file
228
pkgs/disko/lib/make-disk-image.nix
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
{ 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 /
|
||||||
|
'';
|
||||||
|
}
|
||||||
337
pkgs/disko/lib/tests.nix
Normal file
337
pkgs/disko/lib/tests.nix
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
{ 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
|
||||||
272
pkgs/disko/lib/types/btrfs.nix
Normal file
272
pkgs/disko/lib/types/btrfs.nix
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
{ 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
71
pkgs/disko/lib/types/disk.nix
Normal file
71
pkgs/disko/lib/types/disk.nix
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
{ 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
109
pkgs/disko/lib/types/filesystem.nix
Normal file
109
pkgs/disko/lib/types/filesystem.nix
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
{ 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
297
pkgs/disko/lib/types/gpt.nix
Normal file
297
pkgs/disko/lib/types/gpt.nix
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
{ 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
210
pkgs/disko/lib/types/luks.nix
Normal file
210
pkgs/disko/lib/types/luks.nix
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
{ 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
62
pkgs/disko/lib/types/lvm_pv.nix
Normal file
62
pkgs/disko/lib/types/lvm_pv.nix
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
{ 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
183
pkgs/disko/lib/types/lvm_vg.nix
Normal file
183
pkgs/disko/lib/types/lvm_vg.nix
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
{ 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
99
pkgs/disko/lib/types/mdadm.nix
Normal file
99
pkgs/disko/lib/types/mdadm.nix
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
{ 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
60
pkgs/disko/lib/types/mdraid.nix
Normal file
60
pkgs/disko/lib/types/mdraid.nix
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
{ 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
82
pkgs/disko/lib/types/nodev.nix
Normal file
82
pkgs/disko/lib/types/nodev.nix
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
{ 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
134
pkgs/disko/lib/types/swap.nix
Normal file
134
pkgs/disko/lib/types/swap.nix
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
{ 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
179
pkgs/disko/lib/types/table.nix
Normal file
179
pkgs/disko/lib/types/table.nix
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
{ 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
59
pkgs/disko/lib/types/zfs.nix
Normal file
59
pkgs/disko/lib/types/zfs.nix
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
{ 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
160
pkgs/disko/lib/types/zfs_fs.nix
Normal file
160
pkgs/disko/lib/types/zfs_fs.nix
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
{ 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
113
pkgs/disko/lib/types/zfs_volume.nix
Normal file
113
pkgs/disko/lib/types/zfs_volume.nix
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
{ 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";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
390
pkgs/disko/lib/types/zpool.nix
Normal file
390
pkgs/disko/lib/types/zpool.nix
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
{ 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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
266
pkgs/disko/module.nix
Normal file
266
pkgs/disko/module.nix
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
{ 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 [ ];
|
||||||
|
};
|
||||||
|
}
|
||||||
38
pkgs/disko/package.nix
Normal file
38
pkgs/disko/package.nix
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{ 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
|
||||||
18
pkgs/disko/scripts/create-release.nix
Normal file
18
pkgs/disko/scripts/create-release.nix
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
65
pkgs/disko/scripts/create-release.sh
Executable file
65
pkgs/disko/scripts/create-release.sh
Executable file
@ -0,0 +1,65 @@
|
|||||||
|
# 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"
|
||||||
4
pkgs/disko/statix.toml
Normal file
4
pkgs/disko/statix.toml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
disabled = [
|
||||||
|
"manual_inherit", # Prefer `inherit types;` instead of `types = types;`
|
||||||
|
"manual_inherit_from", # Prefer `inherit (eval) options;` instead of `options = eval.options`.
|
||||||
|
]
|
||||||
16
pkgs/disko/tests/bcachefs.nix
Normal file
16
pkgs/disko/tests/bcachefs.nix
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{ 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" ];
|
||||||
|
};
|
||||||
|
}
|
||||||
16
pkgs/disko/tests/boot-raid1.nix
Normal file
16
pkgs/disko/tests/boot-raid1.nix
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{ 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
11
pkgs/disko/tests/btrfs-only-root-subvolume.nix
Normal file
11
pkgs/disko/tests/btrfs-only-root-subvolume.nix
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{ 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 /");
|
||||||
|
'';
|
||||||
|
}
|
||||||
20
pkgs/disko/tests/btrfs-subvolumes.nix
Normal file
20
pkgs/disko/tests/btrfs-subvolumes.nix
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{ 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");
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
||||||
34
pkgs/disko/tests/cli.nix
Normal file
34
pkgs/disko/tests/cli.nix
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{ 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
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
29
pkgs/disko/tests/complex.nix
Normal file
29
pkgs/disko/tests/complex.nix
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{ 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" ];
|
||||||
|
};
|
||||||
|
}
|
||||||
20
pkgs/disko/tests/default.nix
Normal file
20
pkgs/disko/tests/default.nix
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{ 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
Loading…
Reference in New Issue
Block a user