From 348ba1b33c039a3524dcda2a874c83fb44680f9c Mon Sep 17 00:00:00 2001 From: Raito Bezarius Date: Wed, 30 Nov 2022 17:55:41 +0100 Subject: [PATCH] nixos/activation/bootspec: module-ify This does the following: * turns bootspec into a NixOS module * validates bootspecs with Cue * exposes internal knobs --- nixos/modules/module-list.nix | 1 + nixos/modules/system/activation/bootspec.cue | 23 +++++++ nixos/modules/system/activation/bootspec.nix | 69 +++++++++++++++---- nixos/modules/system/activation/top-level.nix | 11 +-- 4 files changed, 81 insertions(+), 23 deletions(-) create mode 100644 nixos/modules/system/activation/bootspec.cue diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index b2723f8df9a..da56e7a8a69 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1241,6 +1241,7 @@ ./services/x11/xserver.nix ./system/activation/activation-script.nix ./system/activation/specialisation.nix + ./system/activation/bootspec.nix ./system/activation/top-level.nix ./system/boot/binfmt.nix ./system/boot/emergency-mode.nix diff --git a/nixos/modules/system/activation/bootspec.cue b/nixos/modules/system/activation/bootspec.cue new file mode 100644 index 00000000000..ad21ffcd28f --- /dev/null +++ b/nixos/modules/system/activation/bootspec.cue @@ -0,0 +1,23 @@ +#V1: { + init: string + initrd?: string + initrdSecrets?: string + kernel: string + kernelParams: [...string] + label: string + toplevel: string + specialisation?: { + [=~"^"]: #V1 + } + extensions?: {...} +} + +#SecureBootExtensions: #V1 & { + extensions: { + osRelease: string + } +} + +Document: { + v1: #V1 +} diff --git a/nixos/modules/system/activation/bootspec.nix b/nixos/modules/system/activation/bootspec.nix index ed5714d87ce..aa5928ddc15 100644 --- a/nixos/modules/system/activation/bootspec.nix +++ b/nixos/modules/system/activation/bootspec.nix @@ -3,23 +3,31 @@ # Changes to the structure of the document, or the semantics of the values should go through an RFC. # # See: https://github.com/NixOS/rfcs/pull/125 -{ config, pkgs, lib, children }: +{ config +, pkgs +, lib +, ... +}: let + cfg = config.boot.bootspec; + children = lib.mapAttrs (childName: childConfig: childConfig.configuration.system.build.toplevel) config.specialisation; schemas = { v1 = rec { - filename = "boot.v1.json"; + filename = "boot.json"; json = pkgs.writeText filename (builtins.toJSON - { - schemaVersion = 1; - + { + v1 = { kernel = "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}"; kernelParams = config.boot.kernelParams; initrd = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}"; initrdSecrets = "${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets"; label = "NixOS ${config.system.nixos.codeName} ${config.system.nixos.label} (Linux ${config.boot.kernelPackages.kernel.modDirVersion})"; - }); + + inherit (cfg) extensions; + }; + }); generator = let @@ -31,8 +39,8 @@ let mkdir -p $out/bootspec ${pkgs.jq}/bin/jq ' - .toplevel = $toplevel | - .init = $init + .v1.toplevel = $toplevel | + .v1.init = $init ' \ --sort-keys \ --arg toplevel "$out" \ @@ -40,17 +48,50 @@ let < ${json} \ | ${pkgs.jq}/bin/jq \ --sort-keys \ - '.specialisation = ($ARGS.named | map_values(. | first))' \ + '.v1.specialisation = ($ARGS.named | map_values(. | first | .v1))' \ ${lib.concatStringsSep " " specialisationLoader} \ > $out/bootspec/${filename} ''; + + validator = pkgs.writeCueValidator ./bootspec.cue { + document = "Document"; # Universal validator for any version as long the schema is correctly set. + }; }; }; in { - # This will be run as a part of the `systemBuilder` in ./top-level.nix. This - # means `$out` points to the output of `config.system.build.toplevel` and can - # be used for a variety of things (though, for now, it's only used to report - # the path of the `toplevel` itself and the `init` executable). - writer = schemas.v1.generator; + options.boot.bootspec = { + enable = lib.mkEnableOption "Enable generation of RFC-0125 bootspec in $system/bootspec, e.g. /run/current-system/bootspec"; + extensions = lib.mkOption { + type = lib.types.attrs; + default = {}; + }; + # This will be run as a part of the `systemBuilder` in ./top-level.nix. This + # means `$out` points to the output of `config.system.build.toplevel` and can + # be used for a variety of things (though, for now, it's only used to report + # the path of the `toplevel` itself and the `init` executable). + writer = lib.mkOption { + internal = true; + default = schemas.v1.generator; + }; + validator = lib.mkOption { + internal = true; + default = schemas.v1.validator; + }; + filename = lib.mkOption { + internal = true; + default = schemas.v1.filename; + }; + }; + + config = lib.mkIf (cfg.enable) { + warnings = [ + ''RFC-0125 is not merged yet, this is a feature preview of bootspec. + Schema is not definitive and features are not stabilized until RFC-0125 is merged. + See: + - https://github.com/NixOS/nixpkgs/pull/172237 to track merge status in nixpkgs. + - https://github.com/NixOS/rfcs/pull/125 to track RFC status. + '' + ]; + }; } diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index 9733bc55ae1..7d44d37a0fc 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -9,14 +9,6 @@ let "${config.system.boot.loader.kernelFile}"; initrdPath = "${config.system.build.initialRamdisk}/" + "${config.system.boot.loader.initrdFile}"; - - bootSpec = import ./bootspec.nix { - inherit - config - pkgs - lib - children; - }; in '' mkdir $out @@ -88,7 +80,8 @@ let echo -n "${toString config.system.extraDependencies}" > $out/extra-dependencies ${optionalString (!config.boot.isContainer) '' - ${bootSpec.writer} + ${config.boot.bootspec.writer} + ${config.boot.bootspec.validator} "$out/bootspec/${config.boot.bootspec.filename}" ''} ${config.system.extraSystemBuilderCmds}