1 import ./make-test-python.nix ({ pkgs, ... }:
4 container = { config, ... }: {
5 # We re-use the NixOS container option ...
6 boot.isContainer = true;
7 # ... and revert unwanted defaults
8 networking.useHostResolvConf = false;
10 # use networkd to obtain systemd network setup
11 networking.useNetworkd = true;
12 networking.useDHCP = false;
14 # systemd-nspawn expects /sbin/init
15 boot.loader.initScript.enable = true;
17 imports = [ ../modules/profiles/minimal.nix ];
19 system.stateVersion = config.system.nixos.version;
22 containerSystem = (import ../lib/eval-config.nix {
23 inherit (pkgs) system;
24 modules = [ container ];
25 }).config.system.build.toplevel;
27 containerName = "container";
28 containerRoot = "/var/lib/machines/${containerName}";
30 containerTarball = pkgs.callPackage ../lib/make-system-tarball.nix {
33 object = containerSystem;
34 symlink = "/nix/var/nix/profiles/system";
40 source = containerSystem + "/etc/os-release";
41 target = "/etc/os-release";
44 source = containerSystem + "/init";
45 target = "/sbin/init";
51 name = "systemd-machinectl";
53 nodes.machine = { lib, ... }: {
54 # use networkd to obtain systemd network setup
55 networking.useNetworkd = true;
56 networking.useDHCP = false;
58 # do not try to access cache.nixos.org
59 nix.settings.substituters = lib.mkForce [ ];
61 # auto-start container
62 systemd.targets.machines.wants = [ "systemd-nspawn@${containerName}.service" ];
64 virtualisation.additionalPaths = [ containerSystem containerTarball ];
66 systemd.tmpfiles.rules = [
67 "d /var/lib/machines/shared-decl 0755 root root - -"
69 systemd.nspawn.shared-decl = {
72 Parameters = "${containerSystem}/init";
75 BindReadOnly = "/nix/store";
79 systemd.services."systemd-nspawn@${containerName}" = {
80 serviceConfig.Environment = [
81 # Disable tmpfs for /tmp
82 "SYSTEMD_NSPAWN_TMPFS_TMP=0"
84 overrideStrategy = "asDropin";
87 # open DHCP for container
88 networking.firewall.extraCommands = ''
89 ${pkgs.iptables}/bin/iptables -A nixos-fw -i ve-+ -p udp -m udp --dport 67 -j nixos-fw-accept
95 machine.wait_for_unit("default.target");
97 # Test machinectl start stop of shared-decl
98 machine.succeed("machinectl start shared-decl");
99 machine.wait_until_succeeds("systemctl -M shared-decl is-active default.target");
100 machine.succeed("machinectl stop shared-decl");
102 # create containers root
103 machine.succeed("mkdir -p ${containerRoot}");
105 # start container with shared nix store by using same arguments as for systemd-nspawn@.service
106 machine.succeed("systemd-run systemd-nspawn --machine=${containerName} --network-veth -U --bind-ro=/nix/store ${containerSystem}/init")
107 machine.wait_until_succeeds("systemctl -M ${containerName} is-active default.target");
109 # Test machinectl stop
110 machine.succeed("machinectl stop ${containerName}");
113 # Workaround for nixos-install
114 machine.succeed("chmod o+rx /var/lib/machines");
115 machine.succeed("nixos-install --root ${containerRoot} --system ${containerSystem} --no-channel-copy --no-root-passwd");
117 # Allow systemd-nspawn to apply user namespace on immutable files
118 machine.succeed("chattr -i ${containerRoot}/var/empty");
120 # Test machinectl start
121 machine.succeed("machinectl start ${containerName}");
122 machine.wait_until_succeeds("systemctl -M ${containerName} is-active default.target");
124 # Test nss_mymachines without nscd
125 machine.succeed('LD_LIBRARY_PATH="/run/current-system/sw/lib" getent -s hosts:mymachines hosts ${containerName}');
127 # Test nss_mymachines via nscd
128 machine.succeed("getent hosts ${containerName}");
130 # Test systemd-nspawn network configuration to container
131 machine.succeed("networkctl --json=short status ve-${containerName} | ${pkgs.jq}/bin/jq -e '.OperationalState == \"routable\"'");
133 # Test systemd-nspawn network configuration to host
134 machine.succeed("machinectl shell ${containerName} /run/current-system/sw/bin/networkctl --json=short status host0 | ${pkgs.jq}/bin/jq -r '.OperationalState == \"routable\"'");
136 # Test systemd-nspawn network configuration
137 machine.succeed("ping -n -c 1 ${containerName}");
139 # Test systemd-nspawn uses a user namespace
140 machine.succeed("test $(machinectl status ${containerName} | grep 'UID Shift: ' | wc -l) = 1")
142 # Test systemd-nspawn reboot
143 machine.succeed("machinectl shell ${containerName} /run/current-system/sw/bin/reboot");
144 machine.wait_until_succeeds("systemctl -M ${containerName} is-active default.target");
146 # Test machinectl reboot
147 machine.succeed("machinectl reboot ${containerName}");
148 machine.wait_until_succeeds("systemctl -M ${containerName} is-active default.target");
153 machine.wait_for_unit("default.target");
156 machine.succeed("machinectl show ${containerName}")
158 # Test machinectl stop
159 machine.succeed("machinectl stop ${containerName}");
160 machine.wait_until_succeeds("test $(systemctl is-active systemd-nspawn@${containerName}) = inactive");
162 # Test tmpfs for /tmp
163 machine.fail("mountpoint /tmp");
165 # Show to to delete the container
166 machine.succeed("chattr -i ${containerRoot}/var/empty");
167 machine.succeed("rm -rf ${containerRoot}");
169 # Test import tarball, start, stop and remove
170 machine.succeed("machinectl import-tar ${containerTarball}/tarball/*.tar* ${containerName}");
171 machine.succeed("machinectl start ${containerName}");
172 machine.wait_until_succeeds("systemctl -M ${containerName} is-active default.target");
173 machine.succeed("machinectl stop ${containerName}");
174 machine.wait_until_succeeds("test $(systemctl is-active systemd-nspawn@${containerName}) = inactive");
175 machine.succeed("machinectl remove ${containerName}");