0
0
Fork 0
mirror of https://github.com/NixOS/nixpkgs.git synced 2025-07-13 21:50:33 +03:00

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. # even in `inheritParentConfig = false` specialisations.
{ lib, ... }: { lib, ... }:
let let
inherit (lib) mkForce; inherit (lib) mkDefault mkForce;
in in
{ {
imports = [ imports = [
@ -22,6 +22,11 @@ in
label = mkForce "test"; 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"; boot.loader.grub.device = mkOverride 0 "nodev";
specialisation = mkOverride 0 {}; specialisation = mkOverride 0 {};
isSpecialisation = mkOverride 0 true;
} }

View file

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

View file

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

View file

@ -1,71 +1,57 @@
import ./make-test-python.nix ({ pkgs, lib, ... }: 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"; name = "containers-reloadable";
meta = { meta = {
maintainers = with lib.maintainers; [ danbst ]; maintainers = with lib.maintainers; [ danbst ];
}; };
nodes = { nodes = {
client = { ... }: { machine = { lib, ... }: {
imports = [ client_base ]; containers.test1 = {
}; autoStart = true;
config.environment.etc.check.text = "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";
}; };
};
client_c2 = { lib, ... }: {
imports = [ client_base ];
containers.test1.config = { # prevent make-test-python.nix to change IP
environment.etc.check.text = lib.mkForce "client_c2"; networking.interfaces.eth1.ipv4.addresses = lib.mkOverride 0 [ ];
services.nginx.enable = true;
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 testScript = ''
c1System = nodes.client_c1.config.system.build.toplevel; machine.start()
c2System = nodes.client_c2.config.system.build.toplevel; machine.wait_for_unit("default.target")
in ''
client.start()
client.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"): with subtest("httpd is available after activating config1"):
client.succeed( machine.succeed(
"${c1System}/bin/switch-to-configuration test >&2", "/run/booted-system/specialisation/c1/bin/switch-to-configuration test >&2",
"[[ $(nixos-container run test1 cat /etc/check) == client_c1 ]] >&2", "[[ $(nixos-container run test1 cat /etc/check) == client_c1 ]] >&2",
"systemctl status httpd -M test1 >&2", "systemctl status httpd -M test1 >&2",
) )
with subtest("httpd is not available any longer after switching to config2"): with subtest("httpd is not available any longer after switching to config2"):
client.succeed( machine.succeed(
"${c2System}/bin/switch-to-configuration test >&2", "/run/booted-system/specialisation/c2/bin/switch-to-configuration test >&2",
"[[ $(nixos-container run test1 cat /etc/check) == client_c2 ]] >&2", "[[ $(nixos-container run test1 cat /etc/check) == client_c2 ]] >&2",
"systemctl status nginx -M test1 >&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 import ./make-test-python.nix ({ pkgs, lib, ... }:
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, ... }:
{ {
name = "containers-restart_networking"; name = "containers-restart_networking";
meta = { meta = {
@ -22,46 +6,55 @@ in import ./make-test-python.nix ({ pkgs, lib, ... }:
}; };
nodes = { nodes = {
client = { lib, ... }: client_base // { client = {
virtualisation.vlans = [ 1 ]; 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 = { networking.bridges.br0 = {
interfaces = []; interfaces = [];
rstp = false; rstp = false;
}; };
networking.interfaces = {
eth1.ipv4.addresses = lib.mkOverride 0 [ ]; networking.interfaces.br0.ipv4.addresses = [ { address = "192.168.1.1"; prefixLength = 24; } ];
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; } ];
};
}; };
}; specialisation.eth1-rstp.configuration = {
client_eth1 = { lib, ... }: client_base // { networking.bridges.br0 = {
networking.bridges.br0 = { interfaces = [ "eth1" ];
interfaces = [ "eth1" ]; rstp = lib.mkForce true;
rstp = false; };
};
networking.interfaces = { networking.interfaces = {
eth1.ipv4.addresses = lib.mkOverride 0 [ ]; eth1.ipv4.addresses = lib.mkForce [ ];
br0.ipv4.addresses = [ { address = "192.168.1.2"; prefixLength = 24; } ]; eth1.ipv6.addresses = lib.mkForce [ ];
}; 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; } ];
}; };
}; };
}; };
testScript = {nodes, ...}: let testScript = ''
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 ''
client.start() client.start()
client.wait_for_unit("default.target") 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"): with subtest("Bridged configuration without STP preserves connectivity"):
client.succeed( client.succeed(
"${eth1_bridged}/bin/switch-to-configuration test >&2" "/run/booted-system/specialisation/eth1/bin/switch-to-configuration test >&2"
) )
client.succeed( 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 # activating rstp needs another service, therefore the bridge will restart and the container will lose its connectivity
# with subtest("Bridged configuration with STP"): # 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 -4 a >&2")
# client.execute("ip l >&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"): with subtest("Reverting to initial configuration preserves connectivity"):
client.succeed( 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") 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; networking.nftables.enable = nftables;
services.httpd.enable = true; services.httpd.enable = true;
services.httpd.adminAddr = "foo@example.org"; services.httpd.adminAddr = "foo@example.org";
};
# Dummy configuration to check whether firewall.service will be honored specialisation.different-config.configuration = {
# during system activation. This only needs to be different to the networking.firewall.rejectPackets = true;
# 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;
}; };
attacker = attacker =
@ -36,7 +29,6 @@ import ./make-test-python.nix ( { pkgs, nftables, ... } : {
}; };
testScript = { nodes, ... }: let testScript = { nodes, ... }: let
newSystem = nodes.walled2.system.build.toplevel;
unit = if nftables then "nftables" else "firewall"; unit = if nftables then "nftables" else "firewall";
in '' in ''
start_all() start_all()
@ -62,7 +54,7 @@ import ./make-test-python.nix ( { pkgs, nftables, ... } : {
# Check whether activation of a new configuration reloads the firewall. # Check whether activation of a new configuration reloads the firewall.
walled.succeed( 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 ])) (python3.withPackages (p: [ p.mistune ]))
shared-mime-info shared-mime-info
sudo sudo
switch-to-configuration-ng
texinfo texinfo
unionfs-fuse unionfs-fuse
xorg.lndir xorg.lndir
@ -648,6 +649,10 @@ let
in [ in [
(pkgs.grub2.override { inherit zfsSupport; }) (pkgs.grub2.override { inherit zfsSupport; })
(pkgs.grub2_efi.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") [ ++ optionals (bootLoader == "systemd-boot") [
pkgs.zstd.bin pkgs.zstd.bin

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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