Merge master into staging-next

This commit is contained in:
Frederik Rietdijk
2020-08-29 13:30:25 +02:00
89 changed files with 2557 additions and 3738 deletions
@@ -204,6 +204,16 @@ GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost' WITH GRANT OPTION;
Note: Password support is only avaiable in GRUB version 2.
</para>
</listitem>
<listitem>
<para>
Following its deprecation in 20.03, the Perl NixOS test driver has been removed.
All remaining tests have been ported to the Python test framework.
Code outside nixpkgs using <filename>make-test.nix</filename> or
<filename>testing.nix</filename> needs to be ported to
<filename>make-test-python.nix</filename> and
<filename>testing-python.nix</filename> respectively.
</para>
</listitem>
</itemizedlist>
</section>
@@ -223,6 +233,11 @@ GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost' WITH GRANT OPTION;
<para>
There is a new <xref linkend="opt-security.doas.enable"/> module that provides <command>doas</command>, a lighter alternative to <command>sudo</command> with many of the same features.
</para>
</listitem>
<listitem>
<para>
<link xlink:href="https://hercules-ci.com">Hercules CI</link> Agent is a specialized build agent for projects built with Nix. See the <link xlink:href="https://nixos.org/nixos/options.html#services.hercules-ci-agent">options</link> and <link xlink:href="https://docs.hercules-ci.com/hercules-ci/getting-started/#deploy-agent">setup</link>.
</para>
</listitem>
</itemizedlist>
-75
View File
@@ -1,75 +0,0 @@
package Logger;
use strict;
use Thread::Queue;
use XML::Writer;
use Encode qw(decode encode);
use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC);
sub new {
my ($class) = @_;
my $logFile = defined $ENV{LOGFILE} ? "$ENV{LOGFILE}" : "/dev/null";
my $log = new XML::Writer(OUTPUT => new IO::File(">$logFile"));
my $self = {
log => $log,
logQueue => Thread::Queue->new()
};
$self->{log}->startTag("logfile");
bless $self, $class;
return $self;
}
sub close {
my ($self) = @_;
$self->{log}->endTag("logfile");
$self->{log}->end;
}
sub drainLogQueue {
my ($self) = @_;
while (defined (my $item = $self->{logQueue}->dequeue_nb())) {
$self->{log}->dataElement("line", sanitise($item->{msg}), 'machine' => $item->{machine}, 'type' => 'serial');
}
}
sub maybePrefix {
my ($msg, $attrs) = @_;
$msg = $attrs->{machine} . ": " . $msg if defined $attrs->{machine};
return $msg;
}
sub nest {
my ($self, $msg, $coderef, $attrs) = @_;
print STDERR maybePrefix("$msg\n", $attrs);
$self->{log}->startTag("nest");
$self->{log}->dataElement("head", $msg, %{$attrs});
my $now = clock_gettime(CLOCK_MONOTONIC);
$self->drainLogQueue();
eval { &$coderef };
my $res = $@;
$self->drainLogQueue();
$self->log(sprintf("(%.2f seconds)", clock_gettime(CLOCK_MONOTONIC) - $now));
$self->{log}->endTag("nest");
die $@ if $@;
}
sub sanitise {
my ($s) = @_;
$s =~ s/[[:cntrl:]\xff]//g;
$s = decode('UTF-8', $s, Encode::FB_DEFAULT);
return encode('UTF-8', $s, Encode::FB_CROAK);
}
sub log {
my ($self, $msg, $attrs) = @_;
chomp $msg;
print STDERR maybePrefix("$msg\n", $attrs);
$self->drainLogQueue();
$self->{log}->dataElement("line", $msg, %{$attrs});
}
1;
-734
View File
@@ -1,734 +0,0 @@
package Machine;
use strict;
use threads;
use Socket;
use IO::Handle;
use POSIX qw(dup2);
use FileHandle;
use Cwd;
use File::Basename;
use File::Path qw(make_path);
use File::Slurp;
use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC);
my $showGraphics = defined $ENV{'DISPLAY'};
my $sharedDir;
sub new {
my ($class, $args) = @_;
my $startCommand = $args->{startCommand};
my $name = $args->{name};
if (!$name) {
$startCommand =~ /run-(.*)-vm$/ if defined $startCommand;
$name = $1 || "machine";
}
if (!$startCommand) {
# !!! merge with qemu-vm.nix.
my $netBackend = "-netdev user,id=net0";
my $netFrontend = "-device virtio-net-pci,netdev=net0";
$netBackend .= "," . $args->{netBackendArgs}
if defined $args->{netBackendArgs};
$netFrontend .= "," . $args->{netFrontendArgs}
if defined $args->{netFrontendArgs};
$startCommand =
"qemu-kvm -m 384 $netBackend $netFrontend \$QEMU_OPTS ";
if (defined $args->{hda}) {
if ($args->{hdaInterface} eq "scsi") {
$startCommand .= "-drive id=hda,file="
. Cwd::abs_path($args->{hda})
. ",werror=report,if=none "
. "-device scsi-hd,drive=hda ";
} else {
$startCommand .= "-drive file=" . Cwd::abs_path($args->{hda})
. ",if=" . $args->{hdaInterface}
. ",werror=report ";
}
}
$startCommand .= "-cdrom $args->{cdrom} "
if defined $args->{cdrom};
$startCommand .= "-device piix3-usb-uhci -drive id=usbdisk,file=$args->{usb},if=none,readonly -device usb-storage,drive=usbdisk "
if defined $args->{usb};
$startCommand .= "-bios $args->{bios} "
if defined $args->{bios};
$startCommand .= $args->{qemuFlags} || "";
}
my $tmpDir = $ENV{'TMPDIR'} || "/tmp";
unless (defined $sharedDir) {
$sharedDir = $tmpDir . "/xchg-shared";
make_path($sharedDir, { mode => 0700, owner => $< });
}
my $allowReboot = 0;
$allowReboot = $args->{allowReboot} if defined $args->{allowReboot};
my $self = {
startCommand => $startCommand,
name => $name,
allowReboot => $allowReboot,
booted => 0,
pid => 0,
connected => 0,
socket => undef,
stateDir => "$tmpDir/vm-state-$name",
monitor => undef,
log => $args->{log},
redirectSerial => $args->{redirectSerial} // 1,
};
mkdir $self->{stateDir}, 0700;
bless $self, $class;
return $self;
}
sub log {
my ($self, $msg) = @_;
$self->{log}->log($msg, { machine => $self->{name} });
}
sub nest {
my ($self, $msg, $coderef, $attrs) = @_;
$self->{log}->nest($msg, $coderef, { %{$attrs || {}}, machine => $self->{name} });
}
sub name {
my ($self) = @_;
return $self->{name};
}
sub stateDir {
my ($self) = @_;
return $self->{stateDir};
}
sub start {
my ($self) = @_;
return if $self->{booted};
$self->log("starting vm");
# Create a socket pair for the serial line input/output of the VM.
my ($serialP, $serialC);
socketpair($serialP, $serialC, PF_UNIX, SOCK_STREAM, 0) or die;
# Create a Unix domain socket to which QEMU's monitor will connect.
my $monitorPath = $self->{stateDir} . "/monitor";
unlink $monitorPath;
my $monitorS;
socket($monitorS, PF_UNIX, SOCK_STREAM, 0) or die;
bind($monitorS, sockaddr_un($monitorPath)) or die "cannot bind monitor socket: $!";
listen($monitorS, 1) or die;
# Create a Unix domain socket to which the root shell in the guest will connect.
my $shellPath = $self->{stateDir} . "/shell";
unlink $shellPath;
my $shellS;
socket($shellS, PF_UNIX, SOCK_STREAM, 0) or die;
bind($shellS, sockaddr_un($shellPath)) or die "cannot bind shell socket: $!";
listen($shellS, 1) or die;
# Start the VM.
my $pid = fork();
die if $pid == -1;
if ($pid == 0) {
close $serialP;
close $monitorS;
close $shellS;
if ($self->{redirectSerial}) {
open NUL, "</dev/null" or die;
dup2(fileno(NUL), fileno(STDIN));
dup2(fileno($serialC), fileno(STDOUT));
dup2(fileno($serialC), fileno(STDERR));
}
$ENV{TMPDIR} = $self->{stateDir};
$ENV{SHARED_DIR} = $sharedDir;
$ENV{USE_TMPDIR} = 1;
$ENV{QEMU_OPTS} =
($self->{allowReboot} ? "" : "-no-reboot ") .
"-monitor unix:./monitor -chardev socket,id=shell,path=./shell " .
"-device virtio-serial -device virtconsole,chardev=shell " .
"-device virtio-rng-pci " .
($showGraphics ? "-serial stdio" : "-nographic") . " " . ($ENV{QEMU_OPTS} || "");
chdir $self->{stateDir} or die;
exec $self->{startCommand};
die "running VM script: $!";
}
# Process serial line output.
close $serialC;
threads->create(\&processSerialOutput, $self, $serialP)->detach;
sub processSerialOutput {
my ($self, $serialP) = @_;
while (<$serialP>) {
chomp;
s/\r$//;
print STDERR $self->{name}, "# $_\n";
$self->{log}->{logQueue}->enqueue({msg => $_, machine => $self->{name}}); # !!!
}
}
eval {
local $SIG{CHLD} = sub { die "QEMU died prematurely\n"; };
# Wait until QEMU connects to the monitor.
accept($self->{monitor}, $monitorS) or die;
# Wait until QEMU connects to the root shell socket. QEMU
# does so immediately; this doesn't mean that the root shell
# has connected yet inside the guest.
accept($self->{socket}, $shellS) or die;
$self->{socket}->autoflush(1);
};
die "$@" if $@;
$self->waitForMonitorPrompt;
$self->log("QEMU running (pid $pid)");
$self->{pid} = $pid;
$self->{booted} = 1;
}
# Send a command to the monitor and wait for it to finish. TODO: QEMU
# also has a JSON-based monitor interface now, but it doesn't support
# all commands yet. We should use it once it does.
sub sendMonitorCommand {
my ($self, $command) = @_;
$self->log("sending monitor command: $command");
syswrite $self->{monitor}, "$command\n";
return $self->waitForMonitorPrompt;
}
# Wait until the monitor sends "(qemu) ".
sub waitForMonitorPrompt {
my ($self) = @_;
my $res = "";
my $s;
while (sysread($self->{monitor}, $s, 1024)) {
$res .= $s;
last if $res =~ s/\(qemu\) $//;
}
return $res;
}
# Call the given code reference repeatedly, with 1 second intervals,
# until it returns 1 or a timeout is reached.
sub retry {
my ($coderef) = @_;
my $n;
for ($n = 899; $n >=0; $n--) {
return if &$coderef($n);
sleep 1;
}
die "action timed out after $n seconds";
}
sub connect {
my ($self) = @_;
return if $self->{connected};
$self->nest("waiting for the VM to finish booting", sub {
$self->start;
my $now = clock_gettime(CLOCK_MONOTONIC);
local $SIG{ALRM} = sub { die "timed out waiting for the VM to connect\n"; };
alarm 600;
readline $self->{socket} or die "the VM quit before connecting\n";
alarm 0;
$self->log("connected to guest root shell");
# We're interested in tracking how close we are to `alarm`.
$self->log(sprintf("(connecting took %.2f seconds)", clock_gettime(CLOCK_MONOTONIC) - $now));
$self->{connected} = 1;
});
}
sub waitForShutdown {
my ($self) = @_;
return unless $self->{booted};
$self->nest("waiting for the VM to power off", sub {
waitpid $self->{pid}, 0;
$self->{pid} = 0;
$self->{booted} = 0;
$self->{connected} = 0;
});
}
sub isUp {
my ($self) = @_;
return $self->{booted} && $self->{connected};
}
sub execute_ {
my ($self, $command) = @_;
$self->connect;
print { $self->{socket} } ("( $command ); echo '|!=EOF' \$?\n");
my $out = "";
while (1) {
my $line = readline($self->{socket});
die "connection to VM lost unexpectedly" unless defined $line;
#$self->log("got line: $line");
if ($line =~ /^(.*)\|\!\=EOF\s+(\d+)$/) {
$out .= $1;
$self->log("exit status $2");
return ($2, $out);
}
$out .= $line;
}
}
sub execute {
my ($self, $command) = @_;
my @res;
$self->nest("running command: $command", sub {
@res = $self->execute_($command);
});
return @res;
}
sub succeed {
my ($self, @commands) = @_;
my $res;
foreach my $command (@commands) {
$self->nest("must succeed: $command", sub {
my ($status, $out) = $self->execute_($command);
if ($status != 0) {
$self->log("output: $out");
die "command `$command' did not succeed (exit code $status)\n";
}
$res .= $out;
});
}
return $res;
}
sub mustSucceed {
succeed @_;
}
sub waitUntilSucceeds {
my ($self, $command) = @_;
$self->nest("waiting for success: $command", sub {
retry sub {
my ($status, $out) = $self->execute($command);
return 1 if $status == 0;
};
});
}
sub waitUntilFails {
my ($self, $command) = @_;
$self->nest("waiting for failure: $command", sub {
retry sub {
my ($status, $out) = $self->execute($command);
return 1 if $status != 0;
};
});
}
sub fail {
my ($self, $command) = @_;
$self->nest("must fail: $command", sub {
my ($status, $out) = $self->execute_($command);
die "command `$command' unexpectedly succeeded"
if $status == 0;
});
}
sub mustFail {
fail @_;
}
sub getUnitInfo {
my ($self, $unit, $user) = @_;
my ($status, $lines) = $self->systemctl("--no-pager show \"$unit\"", $user);
return undef if $status != 0;
my $info = {};
foreach my $line (split '\n', $lines) {
$line =~ /^([^=]+)=(.*)$/ or next;
$info->{$1} = $2;
}
return $info;
}
sub systemctl {
my ($self, $q, $user) = @_;
if ($user) {
$q =~ s/'/\\'/g;
return $self->execute("su -l $user -c \$'XDG_RUNTIME_DIR=/run/user/`id -u` systemctl --user $q'");
}
return $self->execute("systemctl $q");
}
# Fail if the given systemd unit is not in the "active" state.
sub requireActiveUnit {
my ($self, $unit) = @_;
$self->nest("checking if unit $unit has reached state 'active'", sub {
my $info = $self->getUnitInfo($unit);
my $state = $info->{ActiveState};
if ($state ne "active") {
die "Expected unit $unit to to be in state 'active' but it is in state $state\n";
};
});
}
# Wait for a systemd unit to reach the "active" state.
sub waitForUnit {
my ($self, $unit, $user) = @_;
$self->nest("waiting for unit $unit", sub {
retry sub {
my $info = $self->getUnitInfo($unit, $user);
my $state = $info->{ActiveState};
die "unit $unit reached state $state\n" if $state eq "failed";
if ($state eq "inactive") {
# If there are no pending jobs, then assume this unit
# will never reach active state.
my ($status, $jobs) = $self->systemctl("list-jobs --full 2>&1", $user);
if ($jobs =~ /No jobs/) { # FIXME: fragile
# Handle the case where the unit may have started
# between the previous getUnitInfo() and
# list-jobs.
my $info2 = $self->getUnitInfo($unit);
die "unit $unit is inactive and there are no pending jobs\n"
if $info2->{ActiveState} eq $state;
}
}
return 1 if $state eq "active";
};
});
}
sub waitForJob {
my ($self, $jobName) = @_;
return $self->waitForUnit($jobName);
}
# Wait until the specified file exists.
sub waitForFile {
my ($self, $fileName) = @_;
$self->nest("waiting for file $fileName", sub {
retry sub {
my ($status, $out) = $self->execute("test -e $fileName");
return 1 if $status == 0;
}
});
}
sub startJob {
my ($self, $jobName, $user) = @_;
$self->systemctl("start $jobName", $user);
# FIXME: check result
}
sub stopJob {
my ($self, $jobName, $user) = @_;
$self->systemctl("stop $jobName", $user);
}
# Wait until the machine is listening on the given TCP port.
sub waitForOpenPort {
my ($self, $port) = @_;
$self->nest("waiting for TCP port $port", sub {
retry sub {
my ($status, $out) = $self->execute("nc -z localhost $port");
return 1 if $status == 0;
}
});
}
# Wait until the machine is not listening on the given TCP port.
sub waitForClosedPort {
my ($self, $port) = @_;
retry sub {
my ($status, $out) = $self->execute("nc -z localhost $port");
return 1 if $status != 0;
}
}
sub shutdown {
my ($self) = @_;
return unless $self->{booted};
print { $self->{socket} } ("poweroff\n");
$self->waitForShutdown;
}
sub crash {
my ($self) = @_;
return unless $self->{booted};
$self->log("forced crash");
$self->sendMonitorCommand("quit");
$self->waitForShutdown;
}
# Make the machine unreachable by shutting down eth1 (the multicast
# interface used to talk to the other VMs). We keep eth0 up so that
# the test driver can continue to talk to the machine.
sub block {
my ($self) = @_;
$self->sendMonitorCommand("set_link virtio-net-pci.1 off");
}
# Make the machine reachable.
sub unblock {
my ($self) = @_;
$self->sendMonitorCommand("set_link virtio-net-pci.1 on");
}
# Take a screenshot of the X server on :0.0.
sub screenshot {
my ($self, $filename) = @_;
my $dir = $ENV{'out'} || Cwd::abs_path(".");
$filename = "$dir/${filename}.png" if $filename =~ /^\w+$/;
my $tmp = "${filename}.ppm";
my $name = basename($filename);
$self->nest("making screenshot $name", sub {
$self->sendMonitorCommand("screendump $tmp");
system("pnmtopng $tmp > ${filename}") == 0
or die "cannot convert screenshot";
unlink $tmp;
}, { image => $name } );
}
# Get the text of TTY<n>
sub getTTYText {
my ($self, $tty) = @_;
my ($status, $out) = $self->execute("fold -w\$(stty -F /dev/tty${tty} size | awk '{print \$2}') /dev/vcs${tty}");
return $out;
}
# Wait until TTY<n>'s text matches a particular regular expression
sub waitUntilTTYMatches {
my ($self, $tty, $regexp) = @_;
$self->nest("waiting for $regexp to appear on tty $tty", sub {
retry sub {
my ($retries_remaining) = @_;
if ($retries_remaining == 0) {
$self->log("Last chance to match /$regexp/ on TTY$tty, which currently contains:");
$self->log($self->getTTYText($tty));
}
return 1 if $self->getTTYText($tty) =~ /$regexp/;
}
});
}
# Debugging: Dump the contents of the TTY<n>
sub dumpTTYContents {
my ($self, $tty) = @_;
$self->execute("fold -w 80 /dev/vcs${tty} | systemd-cat");
}
# Take a screenshot and return the result as text using optical character
# recognition.
sub getScreenText {
my ($self) = @_;
system("command -v tesseract &> /dev/null") == 0
or die "getScreenText used but enableOCR is false";
my $text;
$self->nest("performing optical character recognition", sub {
my $tmpbase = Cwd::abs_path(".")."/ocr";
my $tmpin = $tmpbase."in.ppm";
$self->sendMonitorCommand("screendump $tmpin");
my $magickArgs = "-filter Catrom -density 72 -resample 300 "
. "-contrast -normalize -despeckle -type grayscale "
. "-sharpen 1 -posterize 3 -negate -gamma 100 "
. "-blur 1x65535";
my $tessArgs = "-c debug_file=/dev/null --psm 11 --oem 2";
$text = `convert $magickArgs $tmpin tiff:- | tesseract - - $tessArgs`;
my $status = $? >> 8;
unlink $tmpin;
die "OCR failed with exit code $status" if $status != 0;
});
return $text;
}
# Wait until a specific regexp matches the textual contents of the screen.
sub waitForText {
my ($self, $regexp) = @_;
$self->nest("waiting for $regexp to appear on the screen", sub {
retry sub {
my ($retries_remaining) = @_;
if ($retries_remaining == 0) {
$self->log("Last chance to match /$regexp/ on the screen, which currently contains:");
$self->log($self->getScreenText);
}
return 1 if $self->getScreenText =~ /$regexp/;
}
});
}
# Wait until it is possible to connect to the X server. Note that
# testing the existence of /tmp/.X11-unix/X0 is insufficient.
sub waitForX {
my ($self, $regexp) = @_;
$self->nest("waiting for the X11 server", sub {
retry sub {
my ($status, $out) = $self->execute("journalctl -b SYSLOG_IDENTIFIER=systemd | grep 'Reached target Current graphical'");
return 0 if $status != 0;
($status, $out) = $self->execute("[ -e /tmp/.X11-unix/X0 ]");
return 1 if $status == 0;
}
});
}
sub getWindowNames {
my ($self) = @_;
my $res = $self->mustSucceed(
q{xwininfo -root -tree | sed 's/.*0x[0-9a-f]* \"\([^\"]*\)\".*/\1/; t; d'});
return split /\n/, $res;
}
sub waitForWindow {
my ($self, $regexp) = @_;
$self->nest("waiting for a window to appear", sub {
retry sub {
my @names = $self->getWindowNames;
my ($retries_remaining) = @_;
if ($retries_remaining == 0) {
$self->log("Last chance to match /$regexp/ on the the window list, which currently contains:");
$self->log(join(", ", @names));
}
foreach my $n (@names) {
return 1 if $n =~ /$regexp/;
}
}
});
}
sub copyFileFromHost {
my ($self, $from, $to) = @_;
my $s = `cat $from` or die;
$s =~ s/'/'\\''/g;
$self->mustSucceed("echo '$s' > $to");
}
my %charToKey = (
'A' => "shift-a", 'N' => "shift-n", '-' => "0x0C", '_' => "shift-0x0C", '!' => "shift-0x02",
'B' => "shift-b", 'O' => "shift-o", '=' => "0x0D", '+' => "shift-0x0D", '@' => "shift-0x03",
'C' => "shift-c", 'P' => "shift-p", '[' => "0x1A", '{' => "shift-0x1A", '#' => "shift-0x04",
'D' => "shift-d", 'Q' => "shift-q", ']' => "0x1B", '}' => "shift-0x1B", '$' => "shift-0x05",
'E' => "shift-e", 'R' => "shift-r", ';' => "0x27", ':' => "shift-0x27", '%' => "shift-0x06",
'F' => "shift-f", 'S' => "shift-s", '\'' => "0x28", '"' => "shift-0x28", '^' => "shift-0x07",
'G' => "shift-g", 'T' => "shift-t", '`' => "0x29", '~' => "shift-0x29", '&' => "shift-0x08",
'H' => "shift-h", 'U' => "shift-u", '\\' => "0x2B", '|' => "shift-0x2B", '*' => "shift-0x09",
'I' => "shift-i", 'V' => "shift-v", ',' => "0x33", '<' => "shift-0x33", '(' => "shift-0x0A",
'J' => "shift-j", 'W' => "shift-w", '.' => "0x34", '>' => "shift-0x34", ')' => "shift-0x0B",
'K' => "shift-k", 'X' => "shift-x", '/' => "0x35", '?' => "shift-0x35",
'L' => "shift-l", 'Y' => "shift-y", ' ' => "spc",
'M' => "shift-m", 'Z' => "shift-z", "\n" => "ret",
);
sub sendKeys {
my ($self, @keys) = @_;
foreach my $key (@keys) {
$key = $charToKey{$key} if exists $charToKey{$key};
$self->sendMonitorCommand("sendkey $key");
}
}
sub sendChars {
my ($self, $chars) = @_;
$self->nest("sending keys $chars", sub {
$self->sendKeys(split //, $chars);
});
}
# Sleep N seconds (in virtual guest time, not real time).
sub sleep {
my ($self, $time) = @_;
$self->succeed("sleep $time");
}
# Forward a TCP port on the host to a TCP port on the guest. Useful
# during interactive testing.
sub forwardPort {
my ($self, $hostPort, $guestPort) = @_;
$hostPort = 8080 unless defined $hostPort;
$guestPort = 80 unless defined $guestPort;
$self->sendMonitorCommand("hostfwd_add tcp::$hostPort-:$guestPort");
}
1;
-191
View File
@@ -1,191 +0,0 @@
#! /somewhere/perl -w
use strict;
use Machine;
use Term::ReadLine;
use IO::File;
use IO::Pty;
use Logger;
use Cwd;
use POSIX qw(_exit dup2);
use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC);
$SIG{PIPE} = 'IGNORE'; # because Unix domain sockets may die unexpectedly
STDERR->autoflush(1);
my $log = new Logger;
# Start vde_switch for each network required by the test.
my %vlans;
foreach my $vlan (split / /, $ENV{VLANS} || "") {
next if defined $vlans{$vlan};
# Start vde_switch as a child process. We don't run it in daemon
# mode because we want the child process to be cleaned up when we
# die. Since we have to make sure that the control socket is
# ready, we send a dummy command to vde_switch (via stdin) and
# wait for a reply. Note that vde_switch requires stdin to be a
# TTY, so we create one.
$log->log("starting VDE switch for network $vlan");
my $socket = Cwd::abs_path "./vde$vlan.ctl";
my $pty = new IO::Pty;
my ($stdoutR, $stdoutW); pipe $stdoutR, $stdoutW;
my $pid = fork(); die "cannot fork" unless defined $pid;
if ($pid == 0) {
dup2(fileno($pty->slave), 0);
dup2(fileno($stdoutW), 1);
exec "vde_switch -s $socket --dirmode 0700" or _exit(1);
}
close $stdoutW;
print $pty "version\n";
readline $stdoutR or die "cannot start vde_switch";
$ENV{"QEMU_VDE_SOCKET_$vlan"} = $socket;
$vlans{$vlan} = $pty;
die unless -e "$socket/ctl";
}
my %vms;
my $context = "";
sub createMachine {
my ($args) = @_;
my $vm = Machine->new({%{$args}, log => $log, redirectSerial => ($ENV{USE_SERIAL} // "0") ne "1"});
$vms{$vm->name} = $vm;
$context .= "my \$" . $vm->name . " = \$vms{'" . $vm->name . "'}; ";
return $vm;
}
foreach my $vmScript (@ARGV) {
my $vm = createMachine({startCommand => $vmScript});
}
sub startAll {
$log->nest("starting all VMs", sub {
$_->start foreach values %vms;
});
}
# Wait until all VMs have terminated.
sub joinAll {
$log->nest("waiting for all VMs to finish", sub {
$_->waitForShutdown foreach values %vms;
});
}
# In interactive tests, this allows the non-interactive test script to
# be executed conveniently.
sub testScript {
eval "$context $ENV{testScript};\n";
warn $@ if $@;
}
my $nrTests = 0;
my $nrSucceeded = 0;
sub subtest {
my ($name, $coderef) = @_;
$log->nest("subtest: $name", sub {
$nrTests++;
eval { &$coderef };
if ($@) {
$log->log("error: $@", { error => 1 });
} else {
$nrSucceeded++;
}
});
}
sub runTests {
if (defined $ENV{tests}) {
$log->nest("running the VM test script", sub {
eval "$context $ENV{tests}";
if ($@) {
$log->log("error: $@", { error => 1 });
die $@;
}
}, { expanded => 1 });
} else {
my $term = Term::ReadLine->new('nixos-vm-test');
$term->ReadHistory;
while (defined ($_ = $term->readline("> "))) {
eval "$context $_\n";
warn $@ if $@;
}
$term->WriteHistory;
}
# Copy the kernel coverage data for each machine, if the kernel
# has been compiled with coverage instrumentation.
$log->nest("collecting coverage data", sub {
foreach my $vm (values %vms) {
my $gcovDir = "/sys/kernel/debug/gcov";
next unless $vm->isUp();
my ($status, $out) = $vm->execute("test -e $gcovDir");
next if $status != 0;
# Figure out where to put the *.gcda files so that the
# report generator can find the corresponding kernel
# sources.
my $kernelDir = $vm->mustSucceed("echo \$(dirname \$(readlink -f /run/current-system/kernel))/.build/linux-*");
chomp $kernelDir;
my $coverageDir = "/tmp/xchg/coverage-data/$kernelDir";
# Copy all the *.gcda files.
$vm->execute("for d in $gcovDir/nix/store/*/.build/linux-*; do for i in \$(cd \$d && find -name '*.gcda'); do echo \$i; mkdir -p $coverageDir/\$(dirname \$i); cp -v \$d/\$i $coverageDir/\$i; done; done");
}
});
$log->nest("syncing", sub {
foreach my $vm (values %vms) {
next unless $vm->isUp();
$vm->execute("sync");
}
});
if ($nrTests != 0) {
$log->log("$nrSucceeded out of $nrTests tests succeeded",
($nrSucceeded < $nrTests ? { error => 1 } : { }));
}
}
# Create an empty raw virtual disk with the given name and size (in
# MiB).
sub createDisk {
my ($name, $size) = @_;
system("qemu-img create -f raw $name ${size}M") == 0
or die "cannot create image of size $size";
}
END {
$log->nest("cleaning up", sub {
foreach my $vm (values %vms) {
if ($vm->{pid}) {
$log->log("killing " . $vm->{name} . " (pid " . $vm->{pid} . ")");
kill 9, $vm->{pid};
}
}
});
$log->close();
}
my $now1 = clock_gettime(CLOCK_MONOTONIC);
runTests;
my $now2 = clock_gettime(CLOCK_MONOTONIC);
printf STDERR "test script finished in %.2fs\n", $now2 - $now1;
exit ($nrSucceeded < $nrTests ? 1 : 0);
-258
View File
@@ -1,258 +0,0 @@
{ system
, pkgs ? import ../.. { inherit system config; }
# Use a minimal kernel?
, minimal ? false
# Ignored
, config ? {}
# Modules to add to each VM
, extraConfigurations ? [] }:
with import ./build-vms.nix { inherit system pkgs minimal extraConfigurations; };
with pkgs;
rec {
inherit pkgs;
testDriver = lib.warn ''
Perl VM tests are deprecated and will be removed for 20.09.
Please update your tests to use the python test driver.
See https://github.com/NixOS/nixpkgs/pull/71684 for details.
'' stdenv.mkDerivation {
name = "nixos-test-driver";
buildInputs = [ makeWrapper perl ];
dontUnpack = true;
preferLocalBuild = true;
installPhase =
''
mkdir -p $out/bin
cp ${./test-driver/test-driver.pl} $out/bin/nixos-test-driver
chmod u+x $out/bin/nixos-test-driver
libDir=$out/${perl.libPrefix}
mkdir -p $libDir
cp ${./test-driver/Machine.pm} $libDir/Machine.pm
cp ${./test-driver/Logger.pm} $libDir/Logger.pm
wrapProgram $out/bin/nixos-test-driver \
--prefix PATH : "${lib.makeBinPath [ qemu_test vde2 netpbm coreutils ]}" \
--prefix PERL5LIB : "${with perlPackages; makePerlPath [ TermReadLineGnu XMLWriter IOTty FileSlurp ]}:$out/${perl.libPrefix}"
'';
};
# Run an automated test suite in the given virtual network.
# `driver' is the script that runs the network.
runTests = driver:
stdenv.mkDerivation {
name = "vm-test-run-${driver.testName}";
requiredSystemFeatures = [ "kvm" "nixos-test" ];
buildCommand =
''
mkdir -p $out
LOGFILE=/dev/null tests='eval $ENV{testScript}; die $@ if $@;' ${driver}/bin/nixos-test-driver
for i in */xchg/coverage-data; do
mkdir -p $out/coverage-data
mv $i $out/coverage-data/$(dirname $(dirname $i))
done
'';
};
makeTest =
{ testScript
, makeCoverageReport ? false
, enableOCR ? false
, name ? "unnamed"
, ...
} @ t:
let
# A standard store path to the vm monitor is built like this:
# /tmp/nix-build-vm-test-run-$name.drv-0/vm-state-machine/monitor
# The max filename length of a unix domain socket is 108 bytes.
# This means $name can at most be 50 bytes long.
maxTestNameLen = 50;
testNameLen = builtins.stringLength name;
testDriverName = with builtins;
if testNameLen > maxTestNameLen then
abort ("The name of the test '${name}' must not be longer than ${toString maxTestNameLen} " +
"it's currently ${toString testNameLen} characters long.")
else
"nixos-test-driver-${name}";
nodes = buildVirtualNetwork (
t.nodes or (if t ? machine then { machine = t.machine; } else { }));
testScript' =
# Call the test script with the computed nodes.
if lib.isFunction testScript
then testScript { inherit nodes; }
else testScript;
vlans = map (m: m.config.virtualisation.vlans) (lib.attrValues nodes);
vms = map (m: m.config.system.build.vm) (lib.attrValues nodes);
ocrProg = tesseract4.override { enableLanguages = [ "eng" ]; };
imagemagick_tiff = imagemagick_light.override { inherit libtiff; };
# Generate onvenience wrappers for running the test driver
# interactively with the specified network, and for starting the
# VMs from the command line.
driver = runCommand testDriverName
{ buildInputs = [ makeWrapper];
testScript = testScript';
preferLocalBuild = true;
testName = name;
}
''
mkdir -p $out/bin
echo "$testScript" > $out/test-script
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/
vms=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done))
wrapProgram $out/bin/nixos-test-driver \
--add-flags "''${vms[*]}" \
${lib.optionalString enableOCR
"--prefix PATH : '${ocrProg}/bin:${imagemagick_tiff}/bin'"} \
--run "export testScript=\"\$(cat $out/test-script)\"" \
--set VLANS '${toString vlans}'
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
wrapProgram $out/bin/nixos-run-vms \
--add-flags "''${vms[*]}" \
${lib.optionalString enableOCR "--prefix PATH : '${ocrProg}/bin'"} \
--set tests 'startAll; joinAll;' \
--set VLANS '${toString vlans}' \
${lib.optionalString (builtins.length vms == 1) "--set USE_SERIAL 1"}
''; # "
passMeta = drv: drv // lib.optionalAttrs (t ? meta) {
meta = (drv.meta or {}) // t.meta;
};
test = passMeta (runTests driver);
report = passMeta (releaseTools.gcovReport { coverageRuns = [ test ]; });
nodeNames = builtins.attrNames nodes;
invalidNodeNames = lib.filter
(node: builtins.match "^[A-z_][A-z0-9_]+$" node == null) nodeNames;
in
if lib.length invalidNodeNames > 0 then
throw ''
Cannot create machines out of (${lib.concatStringsSep ", " invalidNodeNames})!
All machines are referenced as perl variables in the testing framework which will break the
script when special characters are used.
Please stick to alphanumeric chars and underscores as separation.
''
else
(if makeCoverageReport then report else test) // {
inherit nodes driver test;
};
runInMachine =
{ drv
, machine
, preBuild ? ""
, postBuild ? ""
, ... # ???
}:
let
vm = buildVM { }
[ machine
{ key = "run-in-machine";
networking.hostName = "client";
nix.readOnlyStore = false;
virtualisation.writableStore = false;
}
];
buildrunner = writeText "vm-build" ''
source $1
${coreutils}/bin/mkdir -p $TMPDIR
cd $TMPDIR
exec $origBuilder $origArgs
'';
testScript = ''
startAll;
$client->waitForUnit("multi-user.target");
${preBuild}
$client->succeed("env -i ${bash}/bin/bash ${buildrunner} /tmp/xchg/saved-env >&2");
${postBuild}
$client->succeed("sync"); # flush all data before pulling the plug
'';
vmRunCommand = writeText "vm-run" ''
xchg=vm-state-client/xchg
${coreutils}/bin/mkdir $out
${coreutils}/bin/mkdir -p $xchg
for i in $passAsFile; do
i2=''${i}Path
_basename=$(${coreutils}/bin/basename ''${!i2})
${coreutils}/bin/cp ''${!i2} $xchg/$_basename
eval $i2=/tmp/xchg/$_basename
${coreutils}/bin/ls -la $xchg
done
unset i i2 _basename
export | ${gnugrep}/bin/grep -v '^xchg=' > $xchg/saved-env
unset xchg
export tests='${testScript}'
${testDriver}/bin/nixos-test-driver ${vm.config.system.build.vm}/bin/run-*-vm
''; # */
in
lib.overrideDerivation drv (attrs: {
requiredSystemFeatures = [ "kvm" ];
builder = "${bash}/bin/sh";
args = ["-e" vmRunCommand];
origArgs = attrs.args;
origBuilder = attrs.builder;
});
runInMachineWithX = { require ? [], ... } @ args:
let
client =
{ ... }:
{
inherit require;
imports = [
../tests/common/auto.nix
];
virtualisation.memorySize = 1024;
services.xserver.enable = true;
test-support.displayManager.auto.enable = true;
services.xserver.displayManager.defaultSession = "none+icewm";
services.xserver.windowManager.icewm.enable = true;
};
in
runInMachine ({
machine = client;
preBuild =
''
$client->waitForX;
'';
} // args);
simpleTest = as: (makeTest as).test;
}
+1
View File
@@ -262,6 +262,7 @@
./services/continuous-integration/buildbot/worker.nix
./services/continuous-integration/buildkite-agents.nix
./services/continuous-integration/hail.nix
./services/continuous-integration/hercules-ci-agent/default.nix
./services/continuous-integration/hydra/default.nix
./services/continuous-integration/gitlab-runner.nix
./services/continuous-integration/gocd-agent/default.nix
+6
View File
@@ -23,11 +23,17 @@ in
default = [];
description = "List of packages to be added to apparmor's include path";
};
parserConfig = mkOption {
type = types.str;
default = "";
description = "AppArmor parser configuration file content";
};
};
};
config = mkIf cfg.enable {
environment.systemPackages = [ pkgs.apparmor-utils ];
environment.etc."apparmor/parser.conf".text = cfg.parserConfig;
boot.kernelParams = [ "apparmor=1" "security=apparmor" ];
@@ -0,0 +1,213 @@
/*
This file is for options that NixOS and nix-darwin have in common.
Platform-specific code is in the respective default.nix files.
*/
{ config, lib, options, pkgs, ... }:
let
inherit (lib) mkOption mkIf types filterAttrs literalExample mkRenamedOptionModule;
cfg =
config.services.hercules-ci-agent;
format = pkgs.formats.toml {};
settingsModule = { config, ... }: {
freeformType = format.type;
options = {
baseDirectory = mkOption {
type = types.path;
default = "/var/lib/hercules-ci-agent";
description = ''
State directory (secrets, work directory, etc) for agent
'';
};
concurrentTasks = mkOption {
description = ''
Number of tasks to perform simultaneously, such as evaluations, derivations.
You must have a total capacity across agents of at least 2 concurrent tasks on <literal>x86_64-linux</literal>
to allow for import from derivation.
'';
type = types.int;
default = 4;
};
workDirectory = mkOption {
description = ''
The directory in which temporary subdirectories are created for task state. This includes sources for Nix evaluation.
'';
type = types.path;
default = config.baseDirectory + "/work";
defaultText = literalExample ''baseDirectory + "/work"'';
};
staticSecretsDirectory = mkOption {
description = ''
This is the default directory to look for statically configured secrets like <literal>cluster-join-token.key</literal>.
'';
type = types.path;
default = config.baseDirectory + "/secrets";
defaultText = literalExample ''baseDirectory + "/secrets"'';
};
clusterJoinTokenPath = mkOption {
description = ''
Location of the cluster-join-token.key file.
'';
type = types.path;
default = config.staticSecretsDirectory + "/cluster-join-token.key";
defaultText = literalExample ''staticSecretsDirectory + "/cluster-join-token.key"'';
# internal: It's a bit too detailed to show by default in the docs,
# but useful to define explicitly to allow reuse by other modules.
internal = true;
};
binaryCachesPath = mkOption {
description = ''
Location of the binary-caches.json file.
'';
type = types.path;
default = config.staticSecretsDirectory + "/binary-caches.json";
defaultText = literalExample ''staticSecretsDirectory + "/binary-caches.json"'';
# internal: It's a bit too detailed to show by default in the docs,
# but useful to define explicitly to allow reuse by other modules.
internal = true;
};
};
};
checkNix =
if !cfg.checkNix
then ""
else if lib.versionAtLeast config.nix.package.version "2.4.0"
then ""
else pkgs.stdenv.mkDerivation {
name = "hercules-ci-check-system-nix-src";
inherit (config.nix.package) src patches;
configurePhase = ":";
buildPhase = ''
echo "Checking in-memory pathInfoCache expiry"
if ! grep 'struct PathInfoCacheValue' src/libstore/store-api.hh >/dev/null; then
cat 1>&2 <<EOF
You are deploying Hercules CI Agent on a system with an incompatible
nix-daemon. Please
- either upgrade Nix to version 2.4.0 (when released),
- or set option services.hercules-ci-agent.patchNix = true;
- or set option nix.package to a build of Nix 2.3 with this patch applied:
https://github.com/NixOS/nix/pull/3405
The patch is required for Nix-daemon clients that expect a change in binary
cache contents while running, like the agent's evaluator. Without it, import
from derivation will fail if your cluster has more than one machine.
We are conservative with changes to the overall system, which is why we
keep changes to a minimum and why we ask for confirmation in the form of
services.hercules-ci-agent.patchNix = true before applying.
EOF
exit 1
fi
'';
installPhase = "touch $out";
};
patchedNix = lib.mkIf (!lib.versionAtLeast pkgs.nix.version "2.4.0") (
if lib.versionAtLeast pkgs.nix.version "2.4pre"
then lib.warn "Hercules CI Agent module will not patch 2.4 pre-release. Make sure it includes (equivalently) PR #3043, commit d048577909 or is no older than 2020-03-13." pkgs.nix
else pkgs.nix.overrideAttrs (
o: {
patches = (o.patches or []) ++ [ backportNix3398 ];
}
)
);
backportNix3398 = pkgs.fetchurl {
url = "https://raw.githubusercontent.com/hercules-ci/hercules-ci-agent/hercules-ci-agent-0.7.3/for-upstream/issue-3398-path-info-cache-ttls-backport-2.3.patch";
sha256 = "0jfckqjir9il2il7904yc1qyadw366y7xqzg81sp9sl3f1pw70ib";
};
in
{
imports = [
(mkRenamedOptionModule ["services" "hercules-ci-agent" "extraOptions"] ["services" "hercules-ci-agent" "settings"])
(mkRenamedOptionModule ["services" "hercules-ci-agent" "baseDirectory"] ["services" "hercules-ci-agent" "settings" "baseDirectory"])
(mkRenamedOptionModule ["services" "hercules-ci-agent" "concurrentTasks"] ["services" "hercules-ci-agent" "settings" "concurrentTasks"])
];
options.services.hercules-ci-agent = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Enable to run Hercules CI Agent as a system service.
<link xlink:href="https://hercules-ci.com">Hercules CI</link> is a
continuous integation service that is centered around Nix.
Support is available at <link xlink:href="mailto:help@hercules-ci.com">help@hercules-ci.com</link>.
'';
};
patchNix = mkOption {
type = types.bool;
default = false;
description = ''
Fix Nix 2.3 cache path metadata caching behavior. Has the effect of <literal>nix.package = patch pkgs.nix;</literal>
This option will be removed when Hercules CI Agent moves to Nix 2.4 (upcoming Nix release).
'';
};
checkNix = mkOption {
type = types.bool;
default = true;
description = ''
Whether to make sure that the system's Nix (nix-daemon) is compatible.
If you set this to false, please keep up with the change log.
'';
};
package = mkOption {
description = ''
Package containing the bin/hercules-ci-agent executable.
'';
type = types.package;
default = pkgs.hercules-ci-agent;
defaultText = literalExample "pkgs.hercules-ci-agent";
};
settings = mkOption {
description = ''
These settings are written to the <literal>agent.toml</literal> file.
Not all settings are listed as options, can be set nonetheless.
For the exhaustive list of settings, see <link xlink:href="https://docs.hercules-ci.com/hercules-ci/reference/agent-config/"/>.
'';
type = types.submoduleWith { modules = [ settingsModule ]; };
};
/*
Internal and/or computed values.
These are written as options instead of let binding to allow sharing with
default.nix on both NixOS and nix-darwin.
*/
tomlFile = mkOption {
type = types.path;
internal = true;
defaultText = "generated hercules-ci-agent.toml";
description = ''
The fully assembled config file.
'';
};
};
config = mkIf cfg.enable {
nix.extraOptions = lib.addContextFrom checkNix ''
# A store path that was missing at first may well have finished building,
# even shortly after the previous lookup. This *also* applies to the daemon.
narinfo-cache-negative-ttl = 0
'';
nix.package = mkIf cfg.patchNix patchedNix;
services.hercules-ci-agent.tomlFile =
format.generate "hercules-ci-agent.toml" cfg.settings;
};
}
@@ -0,0 +1,86 @@
/*
This file is for NixOS-specific options and configs.
Code that is shared with nix-darwin goes in common.nix.
*/
{ pkgs, config, lib, ... }:
let
inherit (lib) mkIf mkDefault;
cfg = config.services.hercules-ci-agent;
command = "${cfg.package}/bin/hercules-ci-agent --config ${cfg.tomlFile}";
testCommand = "${command} --test-configuration";
in
{
imports = [
./common.nix
(lib.mkRenamedOptionModule ["services" "hercules-ci-agent" "user"] ["systemd" "services" "hercules-ci-agent" "serviceConfig" "User"])
];
config = mkIf cfg.enable {
systemd.services.hercules-ci-agent = {
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
path = [ config.nix.package ];
serviceConfig = {
User = "hercules-ci-agent";
ExecStart = command;
ExecStartPre = testCommand;
Restart = "on-failure";
RestartSec = 120;
StartLimitBurst = 30 * 1000000; # practically infinite
};
};
# Changes in the secrets do not affect the unit in any way that would cause
# a restart, which is currently necessary to reload the secrets.
systemd.paths.hercules-ci-agent-restart-files = {
wantedBy = [ "hercules-ci-agent.service" ];
pathConfig = {
Unit = "hercules-ci-agent-restarter.service";
PathChanged = [ cfg.settings.clusterJoinTokenPath cfg.settings.binaryCachesPath ];
};
};
systemd.services.hercules-ci-agent-restarter = {
serviceConfig.Type = "oneshot";
script = ''
# Wait a bit, with the effect of bundling up file changes into a single
# run of this script and hopefully a single restart.
sleep 10
if systemctl is-active --quiet hercules-ci-agent.service; then
if ${testCommand}; then
systemctl restart hercules-ci-agent.service
else
echo 1>&2 "WARNING: Not restarting agent because config is not valid at this time."
fi
else
echo 1>&2 "Not restarting hercules-ci-agent despite config file update, because it is not already active."
fi
'';
};
# Trusted user allows simplified configuration and better performance
# when operating in a cluster.
nix.trustedUsers = [ config.systemd.services.hercules-ci-agent.serviceConfig.User ];
services.hercules-ci-agent.settings.nixUserIsTrusted = true;
users.users.hercules-ci-agent = {
home = cfg.settings.baseDirectory;
createHome = true;
group = "hercules-ci-agent";
description = "Hercules CI Agent system user";
isSystemUser = true;
};
users.groups.hercules-ci-agent = {};
};
}
+1 -1
View File
@@ -322,7 +322,7 @@ https://nixos.org/nixpkgs/manual/#sec-modify-via-packageOverrides
If you want, you can tweak the Emacs package itself from your
<filename>emacs.nix</filename>. For example, if you want to have a
GTK 3-based Emacs instead of the default GTK 2-based binary and remove the
automatically generated <filename>emacs.desktop</filename> (useful is you
automatically generated <filename>emacs.desktop</filename> (useful if you
only use <command>emacsclient</command>), you can change your file
<filename>emacs.nix</filename> in this way:
</para>
+17
View File
@@ -106,11 +106,28 @@ in rec {
(onFullSupported "nixos.tests.networking.scripted.bridge")
(onFullSupported "nixos.tests.networking.scripted.dhcpOneIf")
(onFullSupported "nixos.tests.networking.scripted.dhcpSimple")
(onFullSupported "nixos.tests.networking.scripted.link")
(onFullSupported "nixos.tests.networking.scripted.loopback")
(onFullSupported "nixos.tests.networking.scripted.macvlan")
(onFullSupported "nixos.tests.networking.scripted.privacy")
(onFullSupported "nixos.tests.networking.scripted.routes")
(onFullSupported "nixos.tests.networking.scripted.sit")
(onFullSupported "nixos.tests.networking.scripted.static")
(onFullSupported "nixos.tests.networking.scripted.virtual")
(onFullSupported "nixos.tests.networking.scripted.vlan")
(onFullSupported "nixos.tests.networking.networkd.bond")
(onFullSupported "nixos.tests.networking.networkd.bridge")
(onFullSupported "nixos.tests.networking.networkd.dhcpOneIf")
(onFullSupported "nixos.tests.networking.networkd.dhcpSimple")
(onFullSupported "nixos.tests.networking.networkd.link")
(onFullSupported "nixos.tests.networking.networkd.loopback")
(onFullSupported "nixos.tests.networking.networkd.macvlan")
(onFullSupported "nixos.tests.networking.networkd.privacy")
(onFullSupported "nixos.tests.networking.networkd.routes")
(onFullSupported "nixos.tests.networking.networkd.sit")
(onFullSupported "nixos.tests.networking.networkd.static")
(onFullSupported "nixos.tests.networking.networkd.virtual")
(onFullSupported "nixos.tests.networking.networkd.vlan")
(onFullSupported "nixos.tests.systemd-networkd-ipv6-prefix-delegation")
(onFullSupported "nixos.tests.nfs3.simple")
(onFullSupported "nixos.tests.nfs4.simple")
+1
View File
@@ -49,6 +49,7 @@ in
ceph-multi-node = handleTestOn ["x86_64-linux"] ./ceph-multi-node.nix {};
certmgr = handleTest ./certmgr.nix {};
cfssl = handleTestOn ["x86_64-linux"] ./cfssl.nix {};
charliecloud = handleTest ./charliecloud.nix {};
chromium = (handleTestOn ["x86_64-linux"] ./chromium.nix {}).stable or {};
cjdns = handleTest ./cjdns.nix {};
clickhouse = handleTest ./clickhouse.nix {};
+43
View File
@@ -0,0 +1,43 @@
# This test checks charliecloud image construction and run
import ./make-test-python.nix ({ pkgs, ...} : let
dockerfile = pkgs.writeText "Dockerfile" ''
FROM nix
RUN mkdir /home /tmp
RUN touch /etc/passwd /etc/group
CMD ["true"]
'';
in {
name = "charliecloud";
meta = with pkgs.stdenv.lib.maintainers; {
maintainers = [ bzizou ];
};
nodes = {
host = { ... }: {
environment.systemPackages = [ pkgs.charliecloud ];
virtualisation.docker.enable = true;
users.users.alice = {
isNormalUser = true;
extraGroups = [ "docker" ];
};
};
};
testScript = ''
host.start()
host.wait_for_unit("docker.service")
host.succeed(
'su - alice -c "docker load --input=${pkgs.dockerTools.examples.nix}"'
)
host.succeed(
"cp ${dockerfile} /home/alice/Dockerfile"
)
host.succeed('su - alice -c "ch-build -t hello ."')
host.succeed('su - alice -c "ch-builder2tar hello /var/tmp"')
host.succeed('su - alice -c "ch-tar2dir /var/tmp/hello.tar.gz /var/tmp"')
host.succeed('su - alice -c "ch-run /var/tmp/hello -- echo Running_From_Container_OK"')
'';
})
-9
View File
@@ -1,9 +0,0 @@
f: {
system ? builtins.currentSystem,
pkgs ? import ../.. { inherit system; config = {}; },
...
} @ args:
with import ../lib/testing.nix { inherit system pkgs; };
makeTest (if pkgs.lib.isFunction f then f (args // { inherit pkgs; inherit (pkgs) lib; }) else f)