nixos/tests: don't include switch-to-configuration in DUT by default (#340445)

This commit is contained in:
K900 2024-09-09 16:20:41 +03:00 committed by GitHub
commit a9c0a2e2a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 149 additions and 172 deletions

View File

@ -3,7 +3,7 @@
# even in `inheritParentConfig = false` specialisations.
{ lib, ... }:
let
inherit (lib) mkForce;
inherit (lib) mkDefault mkForce;
in
{
imports = [
@ -22,6 +22,11 @@ in
label = mkForce "test";
};
}
({ config, ... }: {
# Don't pull in switch-to-configuration by default, except when specialisations are involved.
# This is mostly a Hydra optimization, so we don't rebuild all the tests every time switch-to-configuration-ng changes.
key = "no-switch-to-configuration";
system.switch.enable = mkDefault (config.isSpecialisation || config.specialisation != {});
})
];
}

View File

@ -5,4 +5,5 @@ with lib;
{
boot.loader.grub.device = mkOverride 0 "nodev";
specialisation = mkOverride 0 {};
isSpecialisation = mkOverride 0 true;
}

View File

@ -23,6 +23,12 @@ let
in
{
options = {
isSpecialisation = mkOption {
type = lib.types.bool;
internal = true;
default = false;
description = "Whether this system is a specialisation of another.";
};
specialisation = mkOption {
default = { };

View File

@ -7,25 +7,24 @@ import ./make-test-python.nix ({ lib, ... }:
};
nodes = {
default = {
machine = {
services.chrony.enable = true;
};
graphene-hardened = {
services.chrony.enable = true;
services.chrony.enableMemoryLocking = true;
environment.memoryAllocator.provider = "graphene-hardened";
# dhcpcd privsep is incompatible with graphene-hardened
networking.useNetworkd = true;
specialisation.hardened.configuration = {
services.chrony.enableMemoryLocking = true;
environment.memoryAllocator.provider = "graphene-hardened";
# dhcpcd privsep is incompatible with graphene-hardened
networking.useNetworkd = true;
};
};
};
testScript = {nodes, ...} : let
graphene-hardened = nodes.graphene-hardened.system.build.toplevel;
in ''
default.start()
default.wait_for_unit('multi-user.target')
default.succeed('systemctl is-active chronyd.service')
default.succeed('${graphene-hardened}/bin/switch-to-configuration test')
default.succeed('systemctl is-active chronyd.service')
testScript = ''
machine.start()
machine.wait_for_unit('multi-user.target')
machine.succeed('systemctl is-active chronyd.service')
machine.succeed('/run/booted-system/specialisation/hardened/bin/switch-to-configuration test')
machine.succeed('systemctl restart chronyd.service')
machine.wait_for_unit('chronyd.service')
'';
})

View File

@ -1,71 +1,57 @@
import ./make-test-python.nix ({ pkgs, lib, ... }:
let
client_base = {
containers.test1 = {
autoStart = true;
config = {
environment.etc.check.text = "client_base";
};
};
# prevent make-test-python.nix to change IP
networking.interfaces = {
eth1.ipv4.addresses = lib.mkOverride 0 [ ];
};
};
in {
{
name = "containers-reloadable";
meta = {
maintainers = with lib.maintainers; [ danbst ];
};
nodes = {
client = { ... }: {
imports = [ client_base ];
};
client_c1 = { lib, ... }: {
imports = [ client_base ];
containers.test1.config = {
environment.etc.check.text = lib.mkForce "client_c1";
services.httpd.enable = true;
services.httpd.adminAddr = "nixos@example.com";
machine = { lib, ... }: {
containers.test1 = {
autoStart = true;
config.environment.etc.check.text = "client_base";
};
};
client_c2 = { lib, ... }: {
imports = [ client_base ];
containers.test1.config = {
environment.etc.check.text = lib.mkForce "client_c2";
services.nginx.enable = true;
# prevent make-test-python.nix to change IP
networking.interfaces.eth1.ipv4.addresses = lib.mkOverride 0 [ ];
specialisation.c1.configuration = {
containers.test1.config = {
environment.etc.check.text = lib.mkForce "client_c1";
services.httpd.enable = true;
services.httpd.adminAddr = "nixos@example.com";
};
};
specialisation.c2.configuration = {
containers.test1.config = {
environment.etc.check.text = lib.mkForce "client_c2";
services.nginx.enable = true;
};
};
};
};
testScript = {nodes, ...}: let
c1System = nodes.client_c1.config.system.build.toplevel;
c2System = nodes.client_c2.config.system.build.toplevel;
in ''
client.start()
client.wait_for_unit("default.target")
testScript = ''
machine.start()
machine.wait_for_unit("default.target")
assert "client_base" in client.succeed("nixos-container run test1 cat /etc/check")
assert "client_base" in machine.succeed("nixos-container run test1 cat /etc/check")
with subtest("httpd is available after activating config1"):
client.succeed(
"${c1System}/bin/switch-to-configuration test >&2",
machine.succeed(
"/run/booted-system/specialisation/c1/bin/switch-to-configuration test >&2",
"[[ $(nixos-container run test1 cat /etc/check) == client_c1 ]] >&2",
"systemctl status httpd -M test1 >&2",
)
with subtest("httpd is not available any longer after switching to config2"):
client.succeed(
"${c2System}/bin/switch-to-configuration test >&2",
machine.succeed(
"/run/booted-system/specialisation/c2/bin/switch-to-configuration test >&2",
"[[ $(nixos-container run test1 cat /etc/check) == client_c2 ]] >&2",
"systemctl status nginx -M test1 >&2",
)
client.fail("systemctl status httpd -M test1 >&2")
machine.fail("systemctl status httpd -M test1 >&2")
'';
})

View File

@ -1,20 +1,4 @@
let
client_base = {
networking.firewall.enable = false;
containers.webserver = {
autoStart = true;
privateNetwork = true;
hostBridge = "br0";
config = {
networking.firewall.enable = false;
networking.interfaces.eth0.ipv4.addresses = [
{ address = "192.168.1.122"; prefixLength = 24; }
];
};
};
};
in import ./make-test-python.nix ({ pkgs, lib, ... }:
import ./make-test-python.nix ({ pkgs, lib, ... }:
{
name = "containers-restart_networking";
meta = {
@ -22,46 +6,55 @@ in import ./make-test-python.nix ({ pkgs, lib, ... }:
};
nodes = {
client = { lib, ... }: client_base // {
client = {
virtualisation.vlans = [ 1 ];
networking.firewall.enable = false;
containers.webserver = {
autoStart = true;
privateNetwork = true;
hostBridge = "br0";
config = {
networking.firewall.enable = false;
networking.interfaces.eth0.ipv4.addresses = [
{ address = "192.168.1.122"; prefixLength = 24; }
];
};
};
networking.bridges.br0 = {
interfaces = [];
rstp = false;
};
networking.interfaces = {
eth1.ipv4.addresses = lib.mkOverride 0 [ ];
br0.ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ];
networking.interfaces.br0.ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ];
specialisation.eth1.configuration = {
networking.bridges.br0.interfaces = [ "eth1" ];
networking.interfaces = {
eth1.ipv4.addresses = lib.mkForce [ ];
eth1.ipv6.addresses = lib.mkForce [ ];
br0.ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
};
};
};
client_eth1 = { lib, ... }: client_base // {
networking.bridges.br0 = {
interfaces = [ "eth1" ];
rstp = false;
};
networking.interfaces = {
eth1.ipv4.addresses = lib.mkOverride 0 [ ];
br0.ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
};
};
client_eth1_rstp = { lib, ... }: client_base // {
networking.bridges.br0 = {
interfaces = [ "eth1" ];
rstp = true;
};
networking.interfaces = {
eth1.ipv4.addresses = lib.mkOverride 0 [ ];
br0.ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
specialisation.eth1-rstp.configuration = {
networking.bridges.br0 = {
interfaces = [ "eth1" ];
rstp = lib.mkForce true;
};
networking.interfaces = {
eth1.ipv4.addresses = lib.mkForce [ ];
eth1.ipv6.addresses = lib.mkForce [ ];
br0.ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ];
};
};
};
};
testScript = {nodes, ...}: let
originalSystem = nodes.client.config.system.build.toplevel;
eth1_bridged = nodes.client_eth1.config.system.build.toplevel;
eth1_rstp = nodes.client_eth1_rstp.config.system.build.toplevel;
in ''
testScript = ''
client.start()
client.wait_for_unit("default.target")
@ -75,7 +68,7 @@ in import ./make-test-python.nix ({ pkgs, lib, ... }:
with subtest("Bridged configuration without STP preserves connectivity"):
client.succeed(
"${eth1_bridged}/bin/switch-to-configuration test >&2"
"/run/booted-system/specialisation/eth1/bin/switch-to-configuration test >&2"
)
client.succeed(
@ -87,7 +80,7 @@ in import ./make-test-python.nix ({ pkgs, lib, ... }:
# activating rstp needs another service, therefore the bridge will restart and the container will lose its connectivity
# with subtest("Bridged configuration with STP"):
# client.succeed("${eth1_rstp}/bin/switch-to-configuration test >&2")
# client.succeed("/run/booted-system/specialisation/eth1-rstp/bin/switch-to-configuration test >&2")
# client.execute("ip -4 a >&2")
# client.execute("ip l >&2")
#
@ -100,7 +93,7 @@ in import ./make-test-python.nix ({ pkgs, lib, ... }:
with subtest("Reverting to initial configuration preserves connectivity"):
client.succeed(
"${originalSystem}/bin/switch-to-configuration test >&2"
"/run/booted-system/bin/switch-to-configuration test >&2"
)
client.succeed("ping 192.168.1.122 -c 1 -n >&2")

View File

@ -14,17 +14,10 @@ import ./make-test-python.nix ( { pkgs, nftables, ... } : {
networking.nftables.enable = nftables;
services.httpd.enable = true;
services.httpd.adminAddr = "foo@example.org";
};
# Dummy configuration to check whether firewall.service will be honored
# during system activation. This only needs to be different to the
# original walled configuration so that there is a change in the service
# file.
walled2 =
{ ... }:
{ networking.firewall.enable = true;
networking.firewall.rejectPackets = true;
networking.nftables.enable = nftables;
specialisation.different-config.configuration = {
networking.firewall.rejectPackets = true;
};
};
attacker =
@ -36,7 +29,6 @@ import ./make-test-python.nix ( { pkgs, nftables, ... } : {
};
testScript = { nodes, ... }: let
newSystem = nodes.walled2.system.build.toplevel;
unit = if nftables then "nftables" else "firewall";
in ''
start_all()
@ -62,7 +54,7 @@ import ./make-test-python.nix ( { pkgs, nftables, ... } : {
# Check whether activation of a new configuration reloads the firewall.
walled.succeed(
"${newSystem}/bin/switch-to-configuration test 2>&1 | grep -qF ${unit}.service"
"/run/booted-system/specialisation/different-config/bin/switch-to-configuration test 2>&1 | grep -qF ${unit}.service"
)
'';
})

View File

@ -635,6 +635,7 @@ let
(python3.withPackages (p: [ p.mistune ]))
shared-mime-info
sudo
switch-to-configuration-ng
texinfo
unionfs-fuse
xorg.lndir
@ -648,6 +649,10 @@ let
in [
(pkgs.grub2.override { inherit zfsSupport; })
(pkgs.grub2_efi.override { inherit zfsSupport; })
pkgs.nixos-artwork.wallpapers.simple-dark-gray-bootloader
pkgs.perlPackages.FileCopyRecursive
pkgs.perlPackages.XMLSAX
pkgs.perlPackages.XMLSAXBase
])
++ optionals (bootLoader == "systemd-boot") [
pkgs.zstd.bin

View File

@ -7,19 +7,19 @@ import ./make-test-python.nix ({ pkgs, ...} : {
};
nodes = {
machine = { ... }: {
users.mutableUsers = false;
};
mutable = { ... }: {
users.mutableUsers = true;
users.users.dry-test.isNormalUser = true;
machine = {
specialisation.immutable.configuration = {
users.mutableUsers = false;
};
specialisation.mutable.configuration = {
users.mutableUsers = true;
users.users.dry-test.isNormalUser = true;
};
};
};
testScript = {nodes, ...}: let
immutableSystem = nodes.machine.config.system.build.toplevel;
mutableSystem = nodes.mutable.config.system.build.toplevel;
in ''
testScript = ''
machine.start()
machine.wait_for_unit("default.target")
@ -30,7 +30,7 @@ import ./make-test-python.nix ({ pkgs, ...} : {
machine.succeed("sudo useradd foobar")
assert "foobar" in machine.succeed("cat /etc/passwd")
machine.succeed(
"${immutableSystem}/bin/switch-to-configuration test"
"/run/booted-system/specialisation/immutable/bin/switch-to-configuration test"
)
assert "foobar" not in machine.succeed("cat /etc/passwd")
@ -39,7 +39,7 @@ import ./make-test-python.nix ({ pkgs, ...} : {
with subtest("Password is wrapped in mutable mode"):
assert "/run/current-system/" in machine.succeed("which passwd")
machine.succeed(
"${mutableSystem}/bin/switch-to-configuration test"
"/run/booted-system/specialisation/mutable/bin/switch-to-configuration test"
)
assert "/run/wrappers/" in machine.succeed("which passwd")
@ -63,7 +63,7 @@ import ./make-test-python.nix ({ pkgs, ...} : {
expected_hashes[file] = machine.succeed(f"sha256sum {file}")
expected_stats[file] = machine.succeed(f"stat {file}")
machine.succeed("/run/current-system/bin/switch-to-configuration dry-activate")
machine.succeed("/run/booted-system/specialisation/mutable/bin/switch-to-configuration dry-activate")
machine.fail('test -e /home/dry-test') # home was not recreated
for file in files_to_check:

View File

@ -6,17 +6,6 @@
import ./make-test-python.nix ({ pkgs, lib, withFirewall, nftables ? false, ... }:
let
unit = if nftables then "nftables" else (if withFirewall then "firewall" else "nat");
routerBase =
lib.mkMerge [
{ virtualisation.vlans = [ 2 1 ];
networking.firewall.enable = withFirewall;
networking.firewall.filterForward = nftables;
networking.nftables.enable = nftables;
networking.nat.internalIPs = [ "192.168.1.0/24" ];
networking.nat.externalInterface = "eth1";
}
];
in
{
name = "nat" + (lib.optionalString nftables "Nftables")
@ -26,27 +15,27 @@ import ./make-test-python.nix ({ pkgs, lib, withFirewall, nftables ? false, ...
};
nodes =
{ client =
{ pkgs, nodes, ... }:
lib.mkMerge [
{ virtualisation.vlans = [ 1 ];
networking.defaultGateway =
(pkgs.lib.head nodes.router.networking.interfaces.eth2.ipv4.addresses).address;
networking.nftables.enable = nftables;
}
];
{
client = { lib, nodes, ... }: {
virtualisation.vlans = [ 1 ];
networking.defaultGateway =
(lib.head nodes.router.networking.interfaces.eth2.ipv4.addresses).address;
networking.nftables.enable = nftables;
};
router =
{ ... }: lib.mkMerge [
routerBase
{ networking.nat.enable = true; }
];
router = { lib, ... }: {
virtualisation.vlans = [ 2 1 ];
networking.firewall.enable = withFirewall;
networking.firewall.filterForward = nftables;
networking.nftables.enable = nftables;
networking.nat.enable = true;
networking.nat.internalIPs = [ "192.168.1.0/24" ];
networking.nat.externalInterface = "eth1";
routerDummyNoNat =
{ ... }: lib.mkMerge [
routerBase
{ networking.nat.enable = false; }
];
specialisation.no-nat.configuration = {
networking.nat.enable = lib.mkForce false;
};
};
server =
{ ... }:
@ -59,11 +48,7 @@ import ./make-test-python.nix ({ pkgs, lib, withFirewall, nftables ? false, ...
};
};
testScript =
{ nodes, ... }: let
routerDummyNoNatClosure = nodes.routerDummyNoNat.system.build.toplevel;
routerClosure = nodes.router.system.build.toplevel;
in ''
testScript = ''
client.start()
router.start()
server.start()
@ -94,14 +79,14 @@ import ./make-test-python.nix ({ pkgs, lib, withFirewall, nftables ? false, ...
# If we turn off NAT, the client shouldn't be able to reach the server.
router.succeed(
"${routerDummyNoNatClosure}/bin/switch-to-configuration test 2>&1"
"/run/booted-system/specialisation/no-nat/bin/switch-to-configuration test 2>&1"
)
client.fail("curl -4 --fail --connect-timeout 5 http://server/ >&2")
client.fail("ping -4 -c 1 server >&2")
# And make sure that reloading the NAT job works.
router.succeed(
"${routerClosure}/bin/switch-to-configuration test 2>&1"
"/run/booted-system/bin/switch-to-configuration test 2>&1"
)
# FIXME: this should not be necessary, but nat.service is not started because
# network.target is not triggered

View File

@ -7,6 +7,8 @@ import ./make-test-python.nix ({ pkgs, ...} : {
nodes.machine = { pkgs, ... }: {
imports = [ ../modules/profiles/minimal.nix ];
system.switch.enable = true;
systemd.services.restart-me = {
wantedBy = [ "multi-user.target" ];
serviceConfig = {

View File

@ -591,6 +591,7 @@ in {
};
other = {
system.switch.enable = true;
users.mutableUsers = true;
};
};

View File

@ -13,6 +13,7 @@ let
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
environment.systemPackages = [ pkgs.efibootmgr ];
system.switch.enable = true;
};
commonXbootldr = { config, lib, pkgs, ... }:

View File

@ -3,6 +3,7 @@ import ./make-test-python.nix ({ lib, ... }: {
meta = with lib.maintainers; { maintainers = [ chkno ]; };
nodes.machine = {
system.switch.enable = true;
system.userActivationScripts.foo = "mktemp ~/user-activation-ran.XXXXXX";
users.users.alice = {
initialPassword = "pass1";