diff --git a/nixos/modules/virtualisation/incus.nix b/nixos/modules/virtualisation/incus.nix index da7873c7bec8..a89d000ed299 100644 --- a/nixos/modules/virtualisation/incus.nix +++ b/nixos/modules/virtualisation/incus.nix @@ -1,8 +1,80 @@ -{ config, lib, pkgs, ... }: +{ + config, + lib, + pkgs, + ... +}: let cfg = config.virtualisation.incus; preseedFormat = pkgs.formats.yaml { }; + + serverBinPath = ''${pkgs.qemu_kvm}/libexec:${ + lib.makeBinPath ( + with pkgs; + [ + cfg.package + + acl + attr + bash + btrfs-progs + cdrkit + coreutils + criu + dnsmasq + e2fsprogs + findutils + getent + gnugrep + gnused + gnutar + gptfdisk + gzip + iproute2 + iptables + kmod + lvm2 + minio + nftables + qemu_kvm + qemu-utils + rsync + squashfsTools + systemd + thin-provisioning-tools + util-linux + virtiofsd + xz + + (writeShellScriptBin "apparmor_parser" '' + exec '${apparmor-parser}/bin/apparmor_parser' -I '${apparmor-profiles}/etc/apparmor.d' "$@" + '') + ] + ++ lib.optionals config.boot.zfs.enabled [ + config.boot.zfs.package + "${config.boot.zfs.package}/lib/udev" + ] + ++ lib.optionals config.virtualisation.vswitch.enable [ config.virtualisation.vswitch.package ] + ) + }''; + + # https://github.com/lxc/incus/blob/cff35a29ee3d7a2af1f937cbb6cf23776941854b/internal/server/instance/drivers/driver_qemu.go#L123 + ovmf-prefix = if pkgs.stdenv.hostPlatform.isAarch64 then "AAVMF" else "OVMF"; + ovmf = pkgs.linkFarm "incus-ovmf" [ + { + name = "OVMF_CODE.4MB.fd"; + path = "${pkgs.OVMFFull.fd}/FV/${ovmf-prefix}_CODE.fd"; + } + { + name = "OVMF_VARS.4MB.fd"; + path = "${pkgs.OVMFFull.fd}/FV/${ovmf-prefix}_VARS.fd"; + } + { + name = "OVMF_VARS.4MB.ms.fd"; + path = "${pkgs.OVMFFull.fd}/FV/${ovmf-prefix}_VARS.fd"; + } + ]; in { meta = { @@ -11,26 +83,29 @@ in options = { virtualisation.incus = { - enable = lib.mkEnableOption (lib.mdDoc '' + enable = lib.mkEnableOption '' incusd, a daemon that manages containers and virtual machines. Users in the "incus-admin" group can interact with the daemon (e.g. to start or stop containers) using the {command}`incus` command line tool, among others. - ''); + ''; package = lib.mkPackageOption pkgs "incus" { }; lxcPackage = lib.mkPackageOption pkgs "lxc" { }; + clientPackage = lib.mkPackageOption pkgs [ + "incus" + "client" + ] { }; + preseed = lib.mkOption { - type = lib.types.nullOr ( - lib.types.submodule { freeformType = preseedFormat.type; } - ); + type = lib.types.nullOr (lib.types.submodule { freeformType = preseedFormat.type; }); default = null; - description = lib.mdDoc '' + description = '' Configuration for Incus preseed, see for supported values. @@ -80,18 +155,16 @@ in }; }; - socketActivation = lib.mkEnableOption ( - lib.mdDoc '' - socket-activation for starting incus.service. Enabling this option - will stop incus.service from starting automatically on boot. - '' - ); + socketActivation = lib.mkEnableOption ('' + socket-activation for starting incus.service. Enabling this option + will stop incus.service from starting automatically on boot. + ''); startTimeout = lib.mkOption { type = lib.types.ints.unsigned; default = 600; apply = toString; - description = lib.mdDoc '' + description = '' Time to wait (in seconds) for incusd to become ready to process requests. If incusd does not reply within the configured time, `incus.service` will be considered failed and systemd will attempt to restart it. @@ -99,9 +172,12 @@ in }; ui = { - enable = lib.mkEnableOption (lib.mdDoc "(experimental) Incus UI"); + enable = lib.mkEnableOption "(experimental) Incus UI"; - package = lib.mkPackageOption pkgs [ "incus" "ui" ] { }; + package = lib.mkPackageOption pkgs [ + "incus" + "ui" + ] { }; }; }; }; @@ -109,7 +185,12 @@ in config = lib.mkIf cfg.enable { assertions = [ { - assertion = !(config.networking.firewall.enable && !config.networking.nftables.enable && config.virtualisation.incus.enable); + assertion = + !( + config.networking.firewall.enable + && !config.networking.nftables.enable + && config.virtualisation.incus.enable + ); message = "Incus on NixOS is unsupported using iptables. Set `networking.nftables.enable = true;`"; } ]; @@ -137,7 +218,12 @@ in "vhost_vsock" ] ++ lib.optionals (!config.networking.nftables.enable) [ "iptable_mangle" ]; - environment.systemPackages = [ cfg.package ]; + environment.systemPackages = [ + cfg.clientPackage + + # gui console support + pkgs.spice-gtk + ]; # Note: the following options are also declared in virtualisation.lxc, but # the latter can't be simply enabled to reuse the formers, because it @@ -164,32 +250,24 @@ in "network-online.target" "lxcfs.service" "incus.socket" - ] - ++ lib.optional config.virtualisation.vswitch.enable "ovs-vswitchd.service"; + ] ++ lib.optionals config.virtualisation.vswitch.enable [ "ovs-vswitchd.service" ]; requires = [ "lxcfs.service" "incus.socket" - ] - ++ lib.optional config.virtualisation.vswitch.enable "ovs-vswitchd.service"; + ] ++ lib.optionals config.virtualisation.vswitch.enable [ "ovs-vswitchd.service" ]; - wants = [ - "network-online.target" + wants = [ "network-online.target" ]; + + environment = lib.mkMerge [ + { + INCUS_LXC_TEMPLATE_CONFIG = "${pkgs.lxcfs}/share/lxc/config"; + INCUS_OVMF_PATH = ovmf; + PATH = lib.mkForce serverBinPath; + } + (lib.mkIf (cfg.ui.enable) { "INCUS_UI" = cfg.ui.package; }) ]; - path = lib.optionals config.boot.zfs.enabled [ - config.boot.zfs.package - "${config.boot.zfs.package}/lib/udev" - ] - ++ lib.optional config.virtualisation.vswitch.enable config.virtualisation.vswitch.package; - - environment = lib.mkMerge [ { - # Override Path to the LXC template configuration directory - INCUS_LXC_TEMPLATE_CONFIG = "${pkgs.lxcfs}/share/lxc/config"; - } (lib.mkIf (cfg.ui.enable) { - "INCUS_UI" = cfg.ui.package; - }) ]; - serviceConfig = { ExecStart = "${cfg.package}/bin/incusd --group incus-admin"; ExecStartPost = "${cfg.package}/bin/incusd waitready --timeout=${cfg.startTimeout}"; @@ -222,15 +300,13 @@ in systemd.services.incus-preseed = lib.mkIf (cfg.preseed != null) { description = "Incus initialization with preseed file"; - wantedBy = ["incus.service"]; - after = ["incus.service"]; - bindsTo = ["incus.service"]; - partOf = ["incus.service"]; + wantedBy = [ "incus.service" ]; + after = [ "incus.service" ]; + bindsTo = [ "incus.service" ]; + partOf = [ "incus.service" ]; script = '' - ${cfg.package}/bin/incus admin init --preseed <${ - preseedFormat.generate "incus-preseed.yaml" cfg.preseed - } + ${cfg.package}/bin/incus admin init --preseed <${preseedFormat.generate "incus-preseed.yaml" cfg.preseed} ''; serviceConfig = { diff --git a/nixos/tests/incus/container.nix b/nixos/tests/incus/container.nix index 9260f70da98c..a71c5355046a 100644 --- a/nixos/tests/incus/container.nix +++ b/nixos/tests/incus/container.nix @@ -1,20 +1,21 @@ -import ../make-test-python.nix ({ pkgs, lib, extra ? {}, ... } : +import ../make-test-python.nix ({ pkgs, lib, extra ? {}, name ? "incus-container", ... } : let releases = import ../../release.nix { - configuration = { - # Building documentation makes the test unnecessarily take a longer time: - documentation.enable = lib.mkForce false; + configuration = lib.recursiveUpdate { + # Building documentation makes the test unnecessarily take a longer time: + documentation.enable = lib.mkForce false; - boot.kernel.sysctl."net.ipv4.ip_forward" = "1"; - } // extra; + boot.kernel.sysctl."net.ipv4.ip_forward" = "1"; + } + extra; }; container-image-metadata = releases.lxdContainerMeta.${pkgs.stdenv.hostPlatform.system}; container-image-rootfs = releases.lxdContainerImage.${pkgs.stdenv.hostPlatform.system}; in { - name = "incus-container"; + inherit name; meta = { maintainers = lib.teams.lxc.members; diff --git a/nixos/tests/incus/default.nix b/nixos/tests/incus/default.nix index 32bc5396a164..b850c4fba018 100644 --- a/nixos/tests/incus/default.nix +++ b/nixos/tests/incus/default.nix @@ -5,16 +5,22 @@ handleTestOn, }: { - container-old-init = import ./container.nix { inherit system pkgs; }; - container-new-init = import ./container.nix { inherit system pkgs; extra = { - # Enable new systemd init - boot.initrd.systemd.enable = true; - }; }; + container-legacy-init = import ./container.nix { + name = "container-legacy-init"; + inherit system pkgs; + }; + container-systemd-init = import ./container.nix { + name = "container-systemd-init"; + inherit system pkgs; + extra = { + boot.initrd.systemd.enable = true; + }; + }; lxd-to-incus = import ./lxd-to-incus.nix { inherit system pkgs; }; openvswitch = import ./openvswitch.nix { inherit system pkgs; }; preseed = import ./preseed.nix { inherit system pkgs; }; socket-activated = import ./socket-activated.nix { inherit system pkgs; }; storage = import ./storage.nix { inherit system pkgs; }; - ui = import ./ui.nix {inherit system pkgs;}; + ui = import ./ui.nix { inherit system pkgs; }; virtual-machine = handleTestOn [ "x86_64-linux" ] ./virtual-machine.nix { inherit system pkgs; }; } diff --git a/nixos/tests/incus/lxd-to-incus.nix b/nixos/tests/incus/lxd-to-incus.nix index 262f63c0f26f..e93b76591eca 100644 --- a/nixos/tests/incus/lxd-to-incus.nix +++ b/nixos/tests/incus/lxd-to-incus.nix @@ -95,7 +95,7 @@ import ../make-test-python.nix ( machine.wait_for_unit("incus.service") with machine.nested("run migration"): - machine.succeed("lxd-to-incus --yes") + machine.succeed("${pkgs.incus}/bin/lxd-to-incus --yes") with machine.nested("verify resources migrated to incus"): machine.succeed("incus config show container") diff --git a/pkgs/by-name/in/incus/529.patch b/pkgs/by-name/in/incus/529.patch new file mode 100644 index 000000000000..5e4156b907ca --- /dev/null +++ b/pkgs/by-name/in/incus/529.patch @@ -0,0 +1,29 @@ +From 32a4beecbf8098fdbb15ef5f36088956922630f7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?St=C3=A9phane=20Graber?= +Date: Fri, 23 Feb 2024 18:47:15 -0500 +Subject: [PATCH] incusd/device/disk: Fix incorrect block volume usage +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Stéphane Graber +--- + internal/server/device/disk.go | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/internal/server/device/disk.go b/internal/server/device/disk.go +index 0d19e21139..4f9a3e7c1b 100644 +--- a/internal/server/device/disk.go ++++ b/internal/server/device/disk.go +@@ -339,6 +339,11 @@ func (d *disk) validateConfig(instConf instance.ConfigReader) error { + var usedBy []string + + err = storagePools.VolumeUsedByInstanceDevices(d.state, d.pool.Name(), storageProjectName, &dbVolume.StorageVolume, true, func(inst db.InstanceArgs, project api.Project, usedByDevices []string) error { ++ // Don't count the current instance. ++ if d.inst != nil && d.inst.Project().Name == inst.Project && d.inst.Name() == inst.Name { ++ return nil ++ } ++ + usedBy = append(usedBy, inst.Name) + + return nil diff --git a/pkgs/by-name/in/incus/client.nix b/pkgs/by-name/in/incus/client.nix index 76f792377b10..5d69725ba59d 100644 --- a/pkgs/by-name/in/incus/client.nix +++ b/pkgs/by-name/in/incus/client.nix @@ -1,28 +1,28 @@ { lts ? false, + meta, + patches, + src, + vendorHash, + version, lib, buildGoModule, - fetchpatch, - fetchFromGitHub, installShellFiles, }: let - releaseFile = if lts then ./lts.nix else ./latest.nix; - inherit (import releaseFile { inherit fetchpatch; }) version hash vendorHash; + pname = "incus${lib.optionalString lts "-lts"}-client"; in -buildGoModule rec { - pname = "incus-client"; - - inherit vendorHash version; - - src = fetchFromGitHub { - owner = "lxc"; - repo = "incus"; - rev = "refs/tags/v${version}"; - inherit hash; - }; +buildGoModule { + inherit + meta + patches + pname + src + vendorHash + version + ; CGO_ENABLED = 0; @@ -41,14 +41,4 @@ buildGoModule rec { # don't run the full incus test suite doCheck = false; - - meta = { - description = "Powerful system container and virtual machine manager"; - homepage = "https://linuxcontainers.org/incus"; - changelog = "https://github.com/lxc/incus/releases/tag/v${version}"; - license = lib.licenses.asl20; - maintainers = lib.teams.lxc.members; - platforms = lib.platforms.unix; - mainProgram = "incus"; - }; } diff --git a/pkgs/by-name/in/incus/unwrapped.nix b/pkgs/by-name/in/incus/generic.nix similarity index 73% rename from pkgs/by-name/in/incus/unwrapped.nix rename to pkgs/by-name/in/incus/generic.nix index 7a28737ea390..f6a8954066c3 100644 --- a/pkgs/by-name/in/incus/unwrapped.nix +++ b/pkgs/by-name/in/incus/generic.nix @@ -1,10 +1,18 @@ { + hash, lts ? false, + patches, + updateScriptArgs ? "", + vendorHash, + version, +}: +{ + callPackage, lib, buildGoModule, - fetchpatch, fetchFromGitHub, + writeScript, writeShellScript, acl, cowsql, @@ -19,31 +27,28 @@ }: let - releaseFile = if lts then ./lts.nix else ./latest.nix; - inherit (import releaseFile { inherit fetchpatch; }) - version - hash - patches - vendorHash - ; - name = "incus${lib.optionalString lts "-lts"}"; + pname = "incus${lib.optionalString lts "-lts"}"; in -buildGoModule { - pname = "${name}-unwrapped"; - - inherit patches vendorHash version; +buildGoModule rec { + inherit + patches + pname + vendorHash + version + ; src = fetchFromGitHub { owner = "lxc"; repo = "incus"; - rev = "v${version}"; + rev = "refs/tags/v${version}"; inherit hash; }; + # replace with env var > 0.6 https://github.com/lxc/incus/pull/610 postPatch = '' substituteInPlace internal/usbid/load.go \ - --replace "/usr/share/misc/usb.ids" "${hwdata}/share/hwdata/usb.ids" + --replace-fail "/usr/share/misc/usb.ids" "${hwdata}/share/hwdata/usb.ids" ''; excludedPackages = [ @@ -107,12 +112,23 @@ buildGoModule { ''; passthru = { - tests.incus = nixosTests.incus; + client = callPackage ./client.nix { + inherit + lts + meta + patches + src + vendorHash + version + ; + }; - updateScript = writeShellScript "update-incus" '' - nix-update ${name}.unwrapped -vr 'v(.*)' --override-filename pkgs/by-name/in/incus/${ - if lts then "lts" else "latest" - }.nix + tests = nixosTests.incus; + + ui = callPackage ./ui.nix { }; + + updateScript = writeScript "ovs-update.nu" '' + ${./update.nu} ${updateScriptArgs} ''; }; @@ -123,5 +139,6 @@ buildGoModule { license = lib.licenses.asl20; maintainers = lib.teams.lxc.members; platforms = lib.platforms.linux; + mainProgram = "incus"; }; } diff --git a/pkgs/by-name/in/incus/latest.nix b/pkgs/by-name/in/incus/latest.nix deleted file mode 100644 index 78c09a857488..000000000000 --- a/pkgs/by-name/in/incus/latest.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ fetchpatch }: -{ - hash = "sha256-tGuAS0lZvoYb+TvmCklQ8TADZhbm4w/lhdI0ycS4/0o="; - version = "0.6.0"; - vendorHash = "sha256-+WmgLOEBJ/7GF596iiTgyTPxn8l+hE6RVqjLKfCi5rs="; - patches = [ - (fetchpatch { - url = "https://github.com/lxc/incus/pull/529.patch"; - hash = "sha256-2aaPrzW/LVJidWeom0rqYOGpT2gvuV1yHLJN/TwQ1fk="; - }) - ]; -} diff --git a/pkgs/by-name/in/incus/lts.nix b/pkgs/by-name/in/incus/lts.nix index a78deb48e23b..1537d4d99bc6 100644 --- a/pkgs/by-name/in/incus/lts.nix +++ b/pkgs/by-name/in/incus/lts.nix @@ -1,3 +1,3 @@ # this release doesn't exist yet, but satisfay the by-name checks # will be added as incus-lts in all-packages.nix once ready -_: { } +import ./generic.nix { } diff --git a/pkgs/by-name/in/incus/package.nix b/pkgs/by-name/in/incus/package.nix index 2958ab036ac9..40fd52de8617 100644 --- a/pkgs/by-name/in/incus/package.nix +++ b/pkgs/by-name/in/incus/package.nix @@ -1,157 +1,9 @@ -{ - lts ? false, - - lib, - callPackage, - linkFarm, - makeWrapper, - stdenv, - symlinkJoin, - writeShellScriptBin, - acl, - apparmor-parser, - apparmor-profiles, - attr, - bash, - btrfs-progs, - cdrkit, - criu, - dnsmasq, - e2fsprogs, - getent, - gnutar, - gptfdisk, - gzip, - iproute2, - iptables, - kmod, - lvm2, - minio, - nftables, - OVMF, - qemu_kvm, - qemu-utils, - rsync, - spice-gtk, - squashfsTools, - thin-provisioning-tools, - util-linux, - virtiofsd, - xz, -}: -let - unwrapped = callPackage ./unwrapped.nix { inherit lts; }; - client = callPackage ./client.nix { inherit lts; }; - name = "incus${lib.optionalString lts "-lts"}"; - - binPath = lib.makeBinPath [ - acl - attr - bash - btrfs-progs - cdrkit - criu - dnsmasq - e2fsprogs - getent - gnutar - gptfdisk - gzip - iproute2 - iptables - kmod - lvm2 - minio - nftables - qemu_kvm - qemu-utils - rsync - squashfsTools - thin-provisioning-tools - util-linux - virtiofsd - xz - - (writeShellScriptBin "apparmor_parser" '' - exec '${apparmor-parser}/bin/apparmor_parser' -I '${apparmor-profiles}/etc/apparmor.d' "$@" - '') +import ./generic.nix { + hash = "sha256-tGuAS0lZvoYb+TvmCklQ8TADZhbm4w/lhdI0ycS4/0o="; + version = "0.6.0"; + vendorHash = "sha256-+WmgLOEBJ/7GF596iiTgyTPxn8l+hE6RVqjLKfCi5rs="; + patches = [ + # fix storage bug, fixed in > 0.6 + ./529.patch ]; - - clientBinPath = [ spice-gtk ]; - - ovmf-2mb = OVMF.override { - secureBoot = true; - fdSize2MB = true; - }; - - ovmf-4mb = OVMF.override { - secureBoot = true; - fdSize4MB = true; - }; - - ovmf-prefix = if stdenv.hostPlatform.isAarch64 then "AAVMF" else "OVMF"; - - # mimic ovmf from https://github.com/canonical/incus-pkg-snap/blob/3abebe1dfeb20f9b7729556960c7e9fe6ad5e17c/snapcraft.yaml#L378 - # also found in /snap/incus/current/share/qemu/ on a snap install - ovmf = linkFarm "incus-ovmf" [ - { - name = "OVMF_CODE.2MB.fd"; - path = "${ovmf-2mb.fd}/FV/${ovmf-prefix}_CODE.fd"; - } - { - name = "OVMF_CODE.4MB.fd"; - path = "${ovmf-4mb.fd}/FV/${ovmf-prefix}_CODE.fd"; - } - { - name = "OVMF_CODE.fd"; - path = "${ovmf-2mb.fd}/FV/${ovmf-prefix}_CODE.fd"; - } - - { - name = "OVMF_VARS.2MB.fd"; - path = "${ovmf-2mb.fd}/FV/${ovmf-prefix}_VARS.fd"; - } - { - name = "OVMF_VARS.2MB.ms.fd"; - path = "${ovmf-2mb.fd}/FV/${ovmf-prefix}_VARS.fd"; - } - { - name = "OVMF_VARS.4MB.fd"; - path = "${ovmf-4mb.fd}/FV/${ovmf-prefix}_VARS.fd"; - } - { - name = "OVMF_VARS.4MB.ms.fd"; - path = "${ovmf-4mb.fd}/FV/${ovmf-prefix}_VARS.fd"; - } - { - name = "OVMF_VARS.fd"; - path = "${ovmf-2mb.fd}/FV/${ovmf-prefix}_VARS.fd"; - } - { - name = "OVMF_VARS.ms.fd"; - path = "${ovmf-2mb.fd}/FV/${ovmf-prefix}_VARS.fd"; - } - ]; -in -symlinkJoin { - name = "${name}-${unwrapped.version}"; - - paths = [ unwrapped ]; - - nativeBuildInputs = [ makeWrapper ]; - - postBuild = '' - wrapProgram $out/bin/incusd --prefix PATH : ${lib.escapeShellArg binPath}:${qemu_kvm}/libexec:$out/bin --set INCUS_OVMF_PATH ${ovmf} - - wrapProgram $out/bin/incus --prefix PATH : ${lib.makeBinPath clientBinPath} - ''; - - passthru = { - inherit client unwrapped; - ui = callPackage ./ui.nix {}; - - inherit (unwrapped) tests; - }; - - inherit (unwrapped) meta pname version; } diff --git a/pkgs/by-name/in/incus/update.nu b/pkgs/by-name/in/incus/update.nu new file mode 100755 index 000000000000..1a0755e8f32d --- /dev/null +++ b/pkgs/by-name/in/incus/update.nu @@ -0,0 +1,22 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i nu -p nushell common-updater-scripts gnused + +def main [--lts = false, --regex: string] { + let attr = $"incus(if $lts {"-lts"})" + let file = $"(pwd)/pkgs/by-name/in/incus/(if $lts { "lts" } else { "package" }).nix" + + let tags = list-git-tags --url=https://github.com/lxc/incus | lines | sort --natural | str replace v '' + let latest_tag = if $regex == null { $tags } else { $tags | find --regex $regex } | last + let current_version = nix eval --raw -f default.nix $"($attr).version" | str trim + + if $latest_tag != $current_version { + update-source-version $attr $latest_tag $"--file=($file)" + + let oldVendorHash = nix-instantiate . --eval --strict -A $"($attr).goModules.drvAttrs.outputHash" --json | from json + let vendorHash = do { nix-build -A $"($attr).goModules" } | complete | get stderr | lines | str trim | find --regex 'got:[[:space:]]*sha256' | split row ' ' | last + open $file | str replace $oldVendorHash $vendorHash | save --force $file + + } + + {"lts?": $lts, before: $current_version, after: $latest_tag} +}