Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Networkd containers #140669

Draft
wants to merge 79 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
202254d
nixos/systemd-nspawn: Allow to modify .nspawn-units with a derivation
Ma27 Apr 14, 2020
80045ce
nixos/containers-next: initialize first draft for new NixOS container…
Ma27 Oct 13, 2020
6068759
nixos/containers-next: implement small wrapper for nspawn port-forwards
Ma27 Feb 14, 2021
fe18e25
nixos/containers-next: implement more advanced networking tests
Ma27 Mar 5, 2021
e727866
nixos/tests/container-migration: init
Ma27 Mar 10, 2021
891b998
nixos/containers-next: allow static configuration for a virtual zone …
Ma27 Mar 12, 2021
3199c8f
nixos/switch-to-configuration: import old config activation changes
Ma27 Feb 22, 2021
1f9aca8
nixos/containers-next: fix broken machinectl reboot and probably more
Ma27 Mar 16, 2021
4738b8e
nixos/switch-to-configuration: Implement more generic decisions for c…
Ma27 Mar 17, 2021
e33228f
nixos/containers-next: add read-only `nixos.containers.rendered` option
Ma27 Apr 3, 2021
bcf4864
nixos/all-tests: register tests
Ma27 Apr 12, 2021
a5db036
nixos/containers-next: make sure that the module works fine with `res…
Ma27 Apr 12, 2021
aa7956b
nixos/containers-next: add test for SSH inside a nspawn machine
Ma27 Apr 29, 2021
bcb6671
nixos/containers-next: enable private users by default
Ma27 Apr 29, 2021
a3a28fc
nixos/systemd-nspawn: make `/etc/systemd/nspawn` mutable
Ma27 May 15, 2021
184f470
nixos/containers-next: fix eval after 21.05 breaking changes
Ma27 Jul 2, 2021
7423d9a
nixos-nspawn: init
Ma27 Aug 29, 2021
8811e17
nixos/containers-next: implement proper user-namespacing support
Ma27 Sep 1, 2021
b769a0a
nixos/containers-next: add support for `LoadCredential=`
Ma27 Sep 3, 2021
8fc451d
nixos/containers-next-imperative: init
Ma27 Sep 3, 2021
e12408a
sudo-nspawn: init
Ma27 Sep 5, 2021
10214d0
nixos/switch-to-configuration: fix a few problems with nspawn instances
Ma27 Sep 10, 2021
107d4ca
nixos-nspawn: misc improvements & cleanups
Ma27 Sep 12, 2021
cbdf5d3
nixos/containers-next: move to subdir and factor out defaults for con…
Ma27 Sep 13, 2021
ea7bd5a
nixos-nspawn: implement activation & networking
Ma27 Sep 13, 2021
85b8882
nixos/activation-scripts: turn off `var`-script for containers
Ma27 Sep 22, 2021
0caa260
Revert "nixos/activation-scripts: turn off `var`-script for containers"
Ma27 Sep 24, 2021
c8355e5
nixos/containers-next: only create OS structure in `/var/lib/machines…
Ma27 Sep 24, 2021
62e4520
nixos/tests/containers-next: add testcase for custom `ResolvConf`-set…
Ma27 Sep 24, 2021
6467ce6
nixos/container-migration-test: confirm that nixos-container is still…
Ma27 Sep 24, 2021
9f80931
nixos/containers-next: assert that networkd is used
Ma27 Sep 24, 2021
82a3419
nixos/tests/containers-next-imperative: ensure that imperative contai…
Ma27 Sep 24, 2021
ebcdc12
nixos/tests/container-migration: fix eval
Ma27 Nov 27, 2021
a33d4ca
nixos/containers-next: fix eval
Ma27 Jan 3, 2022
9a3b5ec
nixos/qemu-vm: increase /boot to 120M
Ma27 Jan 3, 2022
40a8152
nixos/container-migration: actually move state of containers
Ma27 Jan 3, 2022
ba4d079
nixos/containers-next: fix test
Ma27 Jan 3, 2022
d4a16d5
nixos/containers-next: s/literalExample/literalExpression/g
Ma27 Jan 4, 2022
7006d0d
nixos/useHostResolvConf: deprecate option
Ma27 Jan 4, 2022
5d8c6e1
nixos/containers-next-imperative: fix test
Ma27 Jan 4, 2022
f93309d
nixos/containers-next: fix `systemd-networkd-wait-online.service` han…
Ma27 Jan 4, 2022
2c2b642
nixos/containers-next: config -> system-config
Ma27 Jan 4, 2022
7ab5a06
nixos/containers-next: confirm that exposed hostnames also work for s…
Ma27 Jan 4, 2022
b371881
nixos/containers-next: review fixes
Ma27 Jan 4, 2022
1fd3769
sudo-nspawn: merge with `pkgs.sudo`
Ma27 Jan 4, 2022
2f08185
nixos-nspawn: refactor python setup
Ma27 Jan 4, 2022
cd533c3
Merge branch 'master' into networkd-containers
Ma27 Jun 12, 2022
feffb03
nixos/qemu-vm: fix manual evaluation
Ma27 Jun 12, 2022
c1443db
Merge branch 'master' into networkd-containers
Ma27 Dec 9, 2022
dc7aaba
nixos/containers-next-imperative: workarounds to get the test green f…
Ma27 Dec 9, 2022
e6cc908
nixos/containers-next: fix option doc wording
Ma27 Feb 16, 2023
b69a0b5
Merge branch 'master' into networkd-containers
Ma27 Feb 16, 2023
fa25e04
nixos/module-list: fix merge
Ma27 Feb 16, 2023
be5fa64
nixos/containers-next: remove forwardPorts option
Ma27 Feb 16, 2023
d9fdcef
nixos/containers-next-macvlan: init
Ma27 Feb 16, 2023
c262041
nixos/tests: move all rfc108 related tests into a common subdir
Ma27 Feb 17, 2023
b81a276
nixos/tests/containers-next/wireguard: init
Ma27 Feb 17, 2023
6d0511f
nixos/tests/containers-next/basic: clean up
Ma27 Feb 17, 2023
29bb8dc
nixos/tests/containers-next: rm skipLint
Ma27 Feb 17, 2023
d0c7529
nixos: use mdDoc for rfc108 related changes
Ma27 Feb 18, 2023
f4e5691
nixos/containers-next: remove `rendered` option
Ma27 Feb 19, 2023
d6dded1
nixos/containers-next: remove `|| true` from creation of `/etc/{os-re…
Ma27 Feb 19, 2023
6ba0180
nixos/tests/containers-next/config-activation: remove unneeded f-str …
Ma27 Feb 19, 2023
c050ae4
nixos/systemd-nspawn: Updates to switch-to-configuration.pl
m1cr0man Mar 31, 2023
d39b85f
nixos/switch-to-configuration: query active nspawn instance names via…
Ma27 Apr 2, 2023
4611773
Merge branch 'master' into networkd-containers
Ma27 Apr 2, 2023
08b30c7
nixos/tests/containers-next: fix handling of underscores in container…
Ma27 Apr 3, 2023
a76c41e
nixos/switch-to-configuration: fix comment position
Ma27 Apr 3, 2023
4f4c56d
nixos/tests/containers-next: fix test
Ma27 Apr 3, 2023
8cc3b52
nixos/containers-next: minor refactorings / cleanups
Ma27 Apr 3, 2023
b418f0e
nixos/containers-next: simplify networkd expressions, prohibit contai…
Ma27 Apr 4, 2023
ddd47d6
nixos/tests: default.nix for containers-next
Ma27 Apr 4, 2023
bac3021
nixos/containers-next: add support for v6 nat
Ma27 Apr 4, 2023
71d3cd8
nixos/containers-next: simplify / refactor tests
Ma27 Apr 4, 2023
60f41e6
nixos/containers-next: make start timeout configurable
Ma27 Apr 4, 2023
5b8eb90
nixos/containers-next: fix bind-mounts of daemon-socket
Ma27 Apr 4, 2023
5d7d31e
nixos/containers-next: only enable DHCP for a container's host0 if ne…
Ma27 Apr 4, 2023
d20ccb1
nixos/containers-next: test privateuserschown on container migration
Ma27 Apr 4, 2023
d09cddd
nixos/containers-next: fix manual build
Ma27 Apr 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
21 changes: 21 additions & 0 deletions jobset.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# FIXME remove before merging!

{ nixpkgs }:
Ma27 marked this conversation as resolved.
Show resolved Hide resolved
let
release = import ./nixos/release.nix {
supportedSystems = [ "x86_64-linux" ];
inherit nixpkgs;
};
in

{
container-tests = {
general = release.tests.containers-next.basic;
migration = release.tests.containers-next.migration;
activation = release.tests.containers-next.config-activation;
imperative = release.tests.containers-next.imperative;
nat = release.tests.containers-next.nat;
daemon-mount = release.tests.containers-next.daemon-mount;
wireguard = release.tests.containers-next.wireguard;
};
}
9 changes: 9 additions & 0 deletions nixos/lib/test-driver/test_driver/machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,15 @@ def systemctl(self, q: str, user: Optional[str] = None) -> Tuple[int, str]:
)
return self.execute(f"systemctl {q}")

def wait_until_unit_stops(self, unit: str) -> None:
def wait_inactive(_: Any) -> bool:
info = self.get_unit_info(unit)
state = info["ActiveState"]
return state == "inactive"

with self.nested(f"waiting for unit '{unit}' to stop"):
retry(wait_inactive)

def require_unit_state(self, unit: str, require_state: str = "active") -> None:
with self.nested(
f"checking if unit '{unit}' has reached state '{require_state}'"
Expand Down
2 changes: 2 additions & 0 deletions nixos/modules/module-list.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1377,6 +1377,8 @@
./virtualisation/container-config.nix
./virtualisation/containerd.nix
./virtualisation/containers.nix
./virtualisation/containers-next
./virtualisation/nixos-containers.nix
./virtualisation/cri-o.nix
./virtualisation/docker-rootless.nix
./virtualisation/docker.nix
Expand Down
2 changes: 1 addition & 1 deletion nixos/modules/services/misc/nix-daemon.nix
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ in
# systemd.tmpfiles.packages = [ nixPackage ];

# Can be dropped for Nix > https://github.com/NixOS/nix/pull/6285
systemd.tmpfiles.rules = [
systemd.tmpfiles.rules = mkIf (!config.boot.isContainer) [
"d /nix/var/nix/daemon-socket 0755 root root - -"
];

Expand Down
198 changes: 180 additions & 18 deletions nixos/modules/system/activation/switch-to-configuration.pl
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,11 @@
# virtual console 1 and we restart the "tty1" unit.
$SIG{PIPE} = "IGNORE";

# Replacement for Net::DBus that calls busctl of the current systemd, parses
# it's json output and returns the response using only core modules to reduce
# dependencies on perlPackages in baseSystem
sub busctl_call_systemd1_mgr {
my (@args) = @_;
sub call_systemd_dbus_api {
my ($api_name, @args) = @_;
my $cmd = [
"$cur_systemd/busctl", "--json=short", "call", "org.freedesktop.systemd1",
"/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager",
"$cur_systemd/busctl", "--json=short", "call", "org.freedesktop.${api_name}",
"/org/freedesktop/${api_name}", "org.freedesktop.${api_name}.Manager",
@args
];

Expand All @@ -143,6 +140,14 @@ sub busctl_call_systemd1_mgr {
return $res;
}

# Replacement for Net::DBus that calls busctl of the current systemd, parses
# it's json output and returns the response using only core modules to reduce
# dependencies on perlPackages in baseSystem
sub busctl_call_systemd1_mgr {
my (@args) = @_;
return call_systemd_dbus_api("systemd1", @args);
}

# Asks the currently running systemd instance via dbus which units are active.
# Returns a hash where the key is the name of each unit and the value a hash
# of load, state, substate.
Expand Down Expand Up @@ -294,11 +299,18 @@ sub record_unit {
sub unrecord_unit {
my ($fn, $unit) = @_;
if ($action ne "dry-activate") {
edit_file(sub { s/^$unit\n//msx }, $fn);
if (index($unit, "systemd-nspawn@") == -1) {
edit_file(sub { s/^$unit\n//msx }, $fn);
}
}
return;
}

sub comp_array {
my ($a, $b) = @_;
return join("\0", @{$a}) eq join("\0", @{$b});
};

# Compare the contents of two unit files and return whether the unit
# needs to be restarted or reloaded. If the units differ, the service
# is restarted unless the only difference is `X-Reload-Triggers` in the
Expand All @@ -322,11 +334,6 @@ sub compare_units { ## no critic(Subroutines::ProhibitExcessComplexity)
SourcePath
);

my $comp_array = sub {
my ($a, $b) = @_;
return join("\0", @{$a}) eq join("\0", @{$b});
};

# Comparison hash for the sections
my %section_cmp = map { $_ => 1 } keys(%{$new_unit});
# Iterate over the sections
Expand Down Expand Up @@ -370,7 +377,7 @@ sub compare_units { ## no critic(Subroutines::ProhibitExcessComplexity)
}
my @new_value = @{$new_unit->{$section_name}{$ini_key}};
# If the contents are different, the units are different
if (not $comp_array->(\@cur_value, \@new_value)) {
if (not comp_array(\@cur_value, \@new_value)) {
# Check if only the reload triggers changed or one of the ignored keys
if ($section_name eq "Unit") {
if ($ini_key eq "X-Reload-Triggers") {
Expand Down Expand Up @@ -418,6 +425,79 @@ sub compare_units { ## no critic(Subroutines::ProhibitExcessComplexity)
return $ret;
}


sub compare_nspawn_units {
# Intentionally trying to keep this similar to compare_units.
my ($cur_unit, $new_unit) = @_;

# Keys to ignore
my %ignored_keys = map { $_ => 1 } qw(
Parameters
X-ActivationStrategy
);

# Comparison hash for the sections
my %section_cmp = map { $_ => 1 } keys(%{$new_unit});

# Iterate over the sections
foreach my $section_name (keys(%{$cur_unit})) {
# Missing section in the new unit.
if (not exists($section_cmp{$section_name})) {
return 1;
}

# Delete the key from the hashmap. Used later to determine
# if some sections exist in new unit but not in current unit.
delete $section_cmp{$section_name};

# Comparison hash for the section contents
my %ini_cmp = map { $_ => 1 } keys(%{$new_unit->{$section_name}});

# Iterate over the keys of the section
foreach my $ini_key (keys(%{$cur_unit->{$section_name}})) {
# Check that the key exists in the new unit, matches in value or is ignored.
if (
exists($ini_cmp{$ini_key}) and (
defined($ignored_keys{$ini_key})
or comp_array(
\@{$cur_unit->{$section_name}{$ini_key}},
\@{$new_unit->{$section_name}{$ini_key}}
)
)
) {
# Delete the key from the hashmap. Used later to determine
# if some keys exist in new unit but not in current unit,
# or they differ.
delete $ini_cmp{$ini_key};

} else {
# Key is missing or differs to the new unit.
return 1;
}
}

# Missing key(s) in the current unit.
# If they are not ignorable, a restart is required.
foreach my $ini_key (keys(%ini_cmp)) {
if (not defined($ignored_keys{$ini_key})) {
return 1;
}
}
}

# Missing section(s) in the current unit.
if (%section_cmp) {
return 1;
}
return 0;
}

sub list_running_nspawn_machines {
my $raw = call_systemd_dbus_api("machine1", qw/ListMachines/)->{data}->[0];
my %running_machines = map { $_->[0] => 1 } grep { $_->[0] ne ".host" } @{$raw};
return %running_machines;
}

# Called when a unit exists in both the old systemd and the new system and the units
# differ. This figures out of what units are to be stopped, restarted, reloaded, started, and skipped.
sub handle_modified_unit { ## no critic(Subroutines::ProhibitManyArgs, Subroutines::ProhibitExcessComplexity)
Expand Down Expand Up @@ -495,6 +575,11 @@ sub handle_modified_unit { ## no critic(Subroutines::ProhibitManyArgs, Subroutin
}
}

# Skip the following logic for systemd-nspawn units
if (index($unit, "systemd-nspawn@") ne -1) {
return;
}

# If the unit is not socket-activated, record
# that this unit needs to be started below.
# We write this to a file to ensure that the
Expand Down Expand Up @@ -534,6 +619,62 @@ sub handle_modified_unit { ## no critic(Subroutines::ProhibitManyArgs, Subroutin
%units_to_reload = map { $_ => 1 }
split(/\n/msx, read_file($reload_list_file, err_mode => "quiet") // "");

# Handle nspawn unit changes
my @current_nspawn_units = glob("/etc/systemd/nspawn/*.nspawn");
my @new_nspawn_units = glob("$out/etc/systemd/nspawn/*.nspawn");
my %current_units_cmp = map { $_ => 1 } @current_nspawn_units;
my %running_containers = list_running_nspawn_machines();
foreach my $new_unit_file (@new_nspawn_units) {
my $container_name = basename($new_unit_file);
$container_name =~ s/\.nspawn//;
my $unit_name = "systemd-nspawn\@$container_name.service";

my $cur_unit_file = $new_unit_file;
$cur_unit_file =~ s/^$out//;
if (exists($current_units_cmp{$cur_unit_file})) {
my %new_unit_info = parse_unit($new_unit_file);
my $strategy = $new_unit_info{"Exec"}{"X-ActivationStrategy"}[0] // "dynamic";

# Skip comparison logic/restart check if ActivationStrategy is "none"
next if $strategy eq "none";

my %cur_unit_info = parse_unit($cur_unit_file);
my $changed = compare_nspawn_units(\%cur_unit_info, \%new_unit_info);

=pod Truth table for restarts
|Strategy|Changed|Reload|Restart|
|--------|-------|------|-------|
|Dynamic |0 |Y |- |
|Dynamic |1 |- |Y |
|Restart |0 |- |- |
|Restart |1 |- |Y |
|Reload |0 |Y |- |
|Reload |1 |Y |- |
=cut
if ($strategy ne "restart" and ($changed == 0 or $strategy eq "reload")) {
if (exists($running_containers{$container_name})) {
$units_to_reload{$unit_name} = 1;
}
} elsif ($changed == 1) {
$units_to_restart{$unit_name} = 1;
}
} else {
# Start the unit if it didn't exist before
$units_to_start{$unit_name} = 1;
}
}

# Stop all now removed nspawn containers
foreach my $cur_unit_file (@current_nspawn_units) {
my %cur_unit_info = parse_unit($cur_unit_file);
unless (-f "$out$cur_unit_file" || ($cur_unit_info{"Exec"}{'X-Imperative'}[0] // "0") == "1") {
my $container_name = basename($cur_unit_file);
$container_name =~ s/\.nspawn//;
my $unit_name = "systemd-nspawn\@$container_name.service";
$units_to_stop{$unit_name} = 1;
}
}

my $active_cur = get_active_units();
while (my ($unit, $state) = each(%{$active_cur})) {
my $base_unit = $unit;
Expand Down Expand Up @@ -859,8 +1000,10 @@ sub filter_units {
# Figure out if we need to start the unit
my %unit_info = parse_unit("$out/etc/systemd/system/$unit");
if (!(parse_systemd_bool(\%unit_info, "Unit", "RefuseManualStart", 0) || parse_systemd_bool(\%unit_info, "Unit", "X-OnlyManualStart", 0))) {
$units_to_start{$unit} = 1;
record_unit($start_list_file, $unit);
if (index($unit, "systemd-nspawn@") == -1) {
$units_to_start{$unit} = 1;
record_unit($start_list_file, $unit);
}
}
# Don't reload the unit, reloading would fail
delete %units_to_reload{$unit};
Expand All @@ -871,8 +1014,27 @@ sub filter_units {
# Reload units that need it. This includes remounting changed mount
# units.
if (scalar(keys(%units_to_reload)) > 0) {
print STDERR "reloading the following units: ", join(", ", sort(keys(%units_to_reload))), "\n";
system("$new_systemd/bin/systemctl", "reload", "--", sort(keys(%units_to_reload))) == 0 or $res = 4;
my @to_reload = sort(keys(%units_to_reload));
print STDERR "reloading the following units: ", join(", ", @to_reload), "\n";

# Reloading containers & dbus.service in the same transaction causes
# the system to stall for about 1 minute.
my (@services, @containers);
foreach my $s (@to_reload) {
if (index($s, "systemd-nspawn@") == 0) {
push @containers, $s;
} else {
push @services, $s;
}
}

if (scalar(@services) > 0) {
system("$new_systemd/bin/systemctl", "reload", "--", @services) == 0 or $res = 4;
}
if (scalar(@containers) > 0) {
system("$new_systemd/bin/systemctl", "reload", "--", @containers) == 0 or $res = 4;
}

unlink($reload_list_file);
}

Expand Down