forked from lix-project/hydra
Merge pull request #912 from grahamc/test-notifications
Notifications: Test behavior of the queue runner
This commit is contained in:
commit
d2512e327c
|
@ -8,10 +8,8 @@ TESTS_ENVIRONMENT = \
|
||||||
NIX_REMOTE_SYSTEMS= \
|
NIX_REMOTE_SYSTEMS= \
|
||||||
NIX_CONF_DIR="$(abs_builddir)/nix/etc/nix" \
|
NIX_CONF_DIR="$(abs_builddir)/nix/etc/nix" \
|
||||||
NIX_STATE_DIR="$(abs_builddir)/nix/var/nix" \
|
NIX_STATE_DIR="$(abs_builddir)/nix/var/nix" \
|
||||||
NIX_MANIFESTS_DIR="$(abs_builddir)/nix/var/nix/manifests" \
|
|
||||||
NIX_STORE_DIR="$(abs_builddir)/nix/store" \
|
NIX_STORE_DIR="$(abs_builddir)/nix/store" \
|
||||||
NIX_LOG_DIR="$(abs_builddir)/nix/var/log/nix" \
|
NIX_LOG_DIR="$(abs_builddir)/nix/var/log/nix" \
|
||||||
NIX_BUILD_HOOK= \
|
|
||||||
PGHOST=/tmp \
|
PGHOST=/tmp \
|
||||||
PERL5LIB="$(srcdir):$(abs_top_srcdir)/src/lib:$$PERL5LIB" \
|
PERL5LIB="$(srcdir):$(abs_top_srcdir)/src/lib:$$PERL5LIB" \
|
||||||
PYTHONPATH= \
|
PYTHONPATH= \
|
||||||
|
|
14
t/jobs/notifications.nix
Normal file
14
t/jobs/notifications.nix
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
with import ./config.nix;
|
||||||
|
{
|
||||||
|
canbesubstituted =
|
||||||
|
mkDerivation {
|
||||||
|
name = "can-be-substituted";
|
||||||
|
builder = ./empty-dir-builder.sh;
|
||||||
|
};
|
||||||
|
|
||||||
|
unsubstitutable =
|
||||||
|
mkDerivation {
|
||||||
|
name = "unsubstitutable";
|
||||||
|
builder = ./empty-dir-builder.sh;
|
||||||
|
};
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ our @EXPORT = qw(test_init hydra_setup nrBuildsForJobset queuedBuildsForJobset
|
||||||
# Hash Parameters:
|
# Hash Parameters:
|
||||||
#
|
#
|
||||||
# * hydra_config: configuration for the Hydra processes for your test.
|
# * hydra_config: configuration for the Hydra processes for your test.
|
||||||
|
# * nix_config: text to include in the test's nix.conf
|
||||||
#
|
#
|
||||||
# This clears several environment variables and sets them to ephemeral
|
# This clears several environment variables and sets them to ephemeral
|
||||||
# values: a temporary database, temporary Nix store, temporary Hydra
|
# values: a temporary database, temporary Nix store, temporary Hydra
|
||||||
|
@ -47,6 +48,7 @@ sub test_init {
|
||||||
my $nixconf = "$ENV{'NIX_CONF_DIR'}/nix.conf";
|
my $nixconf = "$ENV{'NIX_CONF_DIR'}/nix.conf";
|
||||||
open(my $fh, '>', $nixconf) or die "Could not open file '$nixconf' $!";
|
open(my $fh, '>', $nixconf) or die "Could not open file '$nixconf' $!";
|
||||||
print $fh "sandbox = false\n";
|
print $fh "sandbox = false\n";
|
||||||
|
print $fh $opts{'nix_config'} || "";
|
||||||
close $fh;
|
close $fh;
|
||||||
|
|
||||||
$ENV{'HYDRA_CONFIG'} = "$dir/hydra.conf";
|
$ENV{'HYDRA_CONFIG'} = "$dir/hydra.conf";
|
||||||
|
@ -55,11 +57,11 @@ sub test_init {
|
||||||
print $fh $opts{'hydra_config'} || "";
|
print $fh $opts{'hydra_config'} || "";
|
||||||
close $fh;
|
close $fh;
|
||||||
|
|
||||||
$ENV{'NIX_STATE_DIR'} = "$dir/nix/var/nix";
|
|
||||||
|
|
||||||
$ENV{'NIX_MANIFESTS_DIR'} = "$dir/nix/var/nix/manifests";
|
|
||||||
$ENV{'NIX_STORE_DIR'} = "$dir/nix/store";
|
|
||||||
$ENV{'NIX_LOG_DIR'} = "$dir/nix/var/log/nix";
|
$ENV{'NIX_LOG_DIR'} = "$dir/nix/var/log/nix";
|
||||||
|
$ENV{'NIX_REMOTE_SYSTEMS'} = '';
|
||||||
|
$ENV{'NIX_REMOTE'} = '';
|
||||||
|
$ENV{'NIX_STATE_DIR'} = "$dir/nix/var/nix";
|
||||||
|
$ENV{'NIX_STORE_DIR'} = "$dir/nix/store";
|
||||||
|
|
||||||
my $pgsql = Test::PostgreSQL->new(
|
my $pgsql = Test::PostgreSQL->new(
|
||||||
extra_initdb_args => "--locale C.UTF-8"
|
extra_initdb_args => "--locale C.UTF-8"
|
||||||
|
|
142
t/queue-runner/notifications.t
Normal file
142
t/queue-runner/notifications.t
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
use feature 'unicode_strings';
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use JSON;
|
||||||
|
use Setup;
|
||||||
|
|
||||||
|
my $binarycachedir = File::Temp->newdir();
|
||||||
|
|
||||||
|
my %ctx = test_init(
|
||||||
|
nix_config => qq|
|
||||||
|
experimental-features = nix-command
|
||||||
|
substituters = file://${binarycachedir}?trusted=1
|
||||||
|
|,
|
||||||
|
hydra_config => q|
|
||||||
|
use-substitutes = 1
|
||||||
|
<runcommand>
|
||||||
|
command = cp "$HYDRA_JSON" "$HYDRA_DATA/joboutput.json"
|
||||||
|
</runcommand>
|
||||||
|
|);
|
||||||
|
|
||||||
|
require Hydra::Schema;
|
||||||
|
require Hydra::Model::DB;
|
||||||
|
|
||||||
|
use Test2::V0;
|
||||||
|
|
||||||
|
# Check that hydra's queue runner sends notifications.
|
||||||
|
#
|
||||||
|
# The prelude to the test prebuilds one attribute and puts it in a
|
||||||
|
# binary cache. The jobset will try to build that job plus another,
|
||||||
|
# and we'll be able to check the behavior in both cases.
|
||||||
|
#
|
||||||
|
# Our test checks that the queue runner sends notifications even when the
|
||||||
|
# build it is performing can be substituted from a configured cache.
|
||||||
|
# To replicate this behavior we need to build an exact match of the
|
||||||
|
# derivation, upload it to a configured binary cache, then delete it
|
||||||
|
# locally. For completeness, we also verify that we can substitute
|
||||||
|
# the build locally.
|
||||||
|
|
||||||
|
subtest "Pre-build the job, upload to the cache, and then delete locally" => sub {
|
||||||
|
my $scratchlogdir = File::Temp->newdir();
|
||||||
|
$ENV{'NIX_LOG_DIR'} = "$scratchlogdir";
|
||||||
|
|
||||||
|
my $outlink = "$ctx{tmpdir}/basic-canbesubstituted";
|
||||||
|
is(system("nix-build '${ctx{jobsdir}}/notifications.nix' -A canbesubstituted --out-link '${outlink}'"), 0, "Building notifications.nix succeeded");
|
||||||
|
is(system("nix copy --to 'file://${binarycachedir}' '${outlink}'"), 0, "Copying the closure to the binary cache succeeded");
|
||||||
|
my $outpath = readlink($outlink);
|
||||||
|
|
||||||
|
# Delete the store path and all of the system's garbage
|
||||||
|
is(unlink($outlink), 1, "Deleting the GC root succeeds");
|
||||||
|
is(system("nix log '$outpath'"), 0, "Reading the output's log succeeds");
|
||||||
|
is(system("nix-store --delete '$outpath'"), 0, "Deleting the notifications.nix output succeeded");
|
||||||
|
is(system("nix-collect-garbage"), 0, "Delete all the system's garbage");
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "Ensure substituting the job works, but reading the log fails" => sub {
|
||||||
|
# Build the store path, with --max-jobs 0 to prevent builds
|
||||||
|
my $outlink = "$ctx{tmpdir}/basic-canbesubstituted";
|
||||||
|
is(system("nix-build '${ctx{jobsdir}}/notifications.nix' -A canbesubstituted --max-jobs 0 --out-link '${outlink}'"), 0, "Building notifications.nix succeeded");
|
||||||
|
my $outpath = readlink($outlink);
|
||||||
|
|
||||||
|
# Verify trying to read this path's log fails, since we substituted it
|
||||||
|
isnt(system("nix log '$outpath'"), 0, "Reading the deleted output's log fails");
|
||||||
|
|
||||||
|
# Delete the store path again and all of the store's garbage, ensuring
|
||||||
|
# Hydra will try to build it.
|
||||||
|
is(unlink($outlink), 1, "Deleting the GC root succeeds");
|
||||||
|
is(system("nix-store --delete '$outpath'"), 0, "Deleting the basic output succeeded");
|
||||||
|
is(system("nix-collect-garbage"), 0, "Delete all the system's garbage");
|
||||||
|
};
|
||||||
|
|
||||||
|
my $db = Hydra::Model::DB->new;
|
||||||
|
hydra_setup($db);
|
||||||
|
|
||||||
|
my $jobset = createBaseJobset("queue-runner-notifs", "notifications.nix", $ctx{jobsdir});
|
||||||
|
|
||||||
|
my $dbh = $db->storage->dbh;
|
||||||
|
$dbh->do("listen build_started");
|
||||||
|
$dbh->do("listen build_finished");
|
||||||
|
$dbh->do("listen step_finished");
|
||||||
|
|
||||||
|
subtest "Evaluation of the jobset" => sub {
|
||||||
|
ok(evalSucceeds($jobset), "Evaluation should exit with return code 0");
|
||||||
|
is(nrQueuedBuildsForJobset($jobset), 2, "Evaluation should result in 2 builds");
|
||||||
|
};
|
||||||
|
|
||||||
|
my @builds = queuedBuildsForJobset($jobset);
|
||||||
|
|
||||||
|
|
||||||
|
subtest "Build: substitutable, canbesubstituted" => sub {
|
||||||
|
my ($build) = grep { $_->nixname eq "can-be-substituted" } @builds;
|
||||||
|
ok(runBuild($build), "Build should exit with code 0");
|
||||||
|
|
||||||
|
my $newbuild = $db->resultset('Builds')->find($build->id);
|
||||||
|
is($newbuild->finished, 1, "Build should be finished.");
|
||||||
|
is($newbuild->buildstatus, 0, "Build should have buildstatus 0.");
|
||||||
|
|
||||||
|
# Verify that hydra-notify will process this job, even if hydra-notify isn't
|
||||||
|
# running at the time.
|
||||||
|
isnt($newbuild->notificationpendingsince, undef, "The build has a pending notification");
|
||||||
|
|
||||||
|
subtest "First notification: build_finished" => sub {
|
||||||
|
my ($channelName, $pid, $payload) = @{$dbh->func("pg_notifies")};
|
||||||
|
is($channelName, "build_finished", "The event is for the build finishing");
|
||||||
|
is($payload, $build->id, "The payload is the build's ID");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "Build: not substitutable, unsubstitutable" => sub {
|
||||||
|
my ($build) = grep { $_->nixname eq "unsubstitutable" } @builds;
|
||||||
|
ok(runBuild($build), "Build should exit with code 0");
|
||||||
|
|
||||||
|
my $newbuild = $db->resultset('Builds')->find($build->id);
|
||||||
|
is($newbuild->finished, 1, "Build should be finished.");
|
||||||
|
is($newbuild->buildstatus, 0, "Build should have buildstatus 0.");
|
||||||
|
|
||||||
|
# Verify that hydra-notify will process this job, even if hydra-notify isn't
|
||||||
|
# running at the time.
|
||||||
|
isnt($newbuild->notificationpendingsince, undef, "The build has a pending notification");
|
||||||
|
|
||||||
|
subtest "First notification: build_started" => sub {
|
||||||
|
my ($channelName, $pid, $payload) = @{$dbh->func("pg_notifies")};
|
||||||
|
is($channelName, "build_started", "The event is for the build starting");
|
||||||
|
is($payload, $build->id, "The payload is the build's ID");
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "Second notification: step_finished" => sub {
|
||||||
|
my ($channelName, $pid, $payload) = @{$dbh->func("pg_notifies")};
|
||||||
|
is($channelName, "step_finished", "The event is for the step finishing");
|
||||||
|
my ($buildId, $stepNr, $logFile) = split "\t", $payload;
|
||||||
|
is($buildId, $build->id, "The payload is the build's ID");
|
||||||
|
is($stepNr, 1, "The payload is the build's step number");
|
||||||
|
isnt($logFile, undef, "The log file is passed");
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "Third notification: build_finished" => sub {
|
||||||
|
my ($channelName, $pid, $payload) = @{$dbh->func("pg_notifies")};
|
||||||
|
is($channelName, "build_finished", "The event is for the build finishing");
|
||||||
|
is($payload, $build->id, "The payload is the build's ID");
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
done_testing;
|
Loading…
Reference in a new issue