forked from lix-project/hydra
Merge pull request #1176 from DeterminateSystems/broken-constituent
Broken constituents: emit useful log messages on evaluation errors on constituents
This commit is contained in:
commit
22026da4f8
4 changed files with 103 additions and 19 deletions
|
@ -799,7 +799,13 @@ sub checkJobsetWrapped {
|
||||||
|
|
||||||
foreach my $job (values %{$jobs}) {
|
foreach my $job (values %{$jobs}) {
|
||||||
next unless $job->{constituents};
|
next unless $job->{constituents};
|
||||||
my $x = $drvPathToId{$job->{drvPath}} or die;
|
|
||||||
|
if (defined $job->{error}) {
|
||||||
|
die "aggregate job ‘$job->{jobName}’ failed with the error: $job->{error}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
my $x = $drvPathToId{$job->{drvPath}} or
|
||||||
|
die "aggregate job ‘$job->{jobName}’ has no corresponding build record.\n";
|
||||||
foreach my $drvPath (@{$job->{constituents}}) {
|
foreach my $drvPath (@{$job->{constituents}}) {
|
||||||
my $constituent = $drvPathToId{$drvPath};
|
my $constituent = $drvPathToId{$drvPath};
|
||||||
if (defined $constituent) {
|
if (defined $constituent) {
|
||||||
|
|
32
t/evaluator/evaluate-constituents-broken.t
Normal file
32
t/evaluator/evaluate-constituents-broken.t
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Setup;
|
||||||
|
use Test2::V0;
|
||||||
|
use Hydra::Helper::Exec;
|
||||||
|
|
||||||
|
my $ctx = test_context();
|
||||||
|
|
||||||
|
my $jobsetCtx = $ctx->makeJobset(
|
||||||
|
expression => 'constituents-broken.nix',
|
||||||
|
);
|
||||||
|
my $jobset = $jobsetCtx->{"jobset"};
|
||||||
|
|
||||||
|
my ($res, $stdout, $stderr) = captureStdoutStderr(60,
|
||||||
|
("hydra-eval-jobset", $jobsetCtx->{"project"}->name, $jobset->name)
|
||||||
|
);
|
||||||
|
isnt($res, 0, "hydra-eval-jobset exits non-zero");
|
||||||
|
ok(utf8::decode($stderr), "Stderr output is UTF8-clean");
|
||||||
|
like(
|
||||||
|
$stderr,
|
||||||
|
qr/aggregate job ‘mixed_aggregate’ failed with the error: constituentA: does not exist/,
|
||||||
|
"The stderr record includes a relevant error message"
|
||||||
|
);
|
||||||
|
|
||||||
|
$jobset->discard_changes; # refresh from DB
|
||||||
|
like(
|
||||||
|
$jobset->errormsg,
|
||||||
|
qr/aggregate job ‘mixed_aggregate’ failed with the error: constituentA: does not exist/,
|
||||||
|
"The jobset records a relevant error message"
|
||||||
|
);
|
||||||
|
|
||||||
|
done_testing;
|
19
t/jobs/constituents-broken.nix
Normal file
19
t/jobs/constituents-broken.nix
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
with import ./config.nix;
|
||||||
|
rec {
|
||||||
|
constituentA = null;
|
||||||
|
|
||||||
|
constituentB = mkDerivation {
|
||||||
|
name = "empty-dir-B";
|
||||||
|
builder = ./empty-dir-builder.sh;
|
||||||
|
};
|
||||||
|
|
||||||
|
mixed_aggregate = mkDerivation {
|
||||||
|
name = "mixed_aggregate";
|
||||||
|
_hydraAggregate = true;
|
||||||
|
constituents = [
|
||||||
|
"constituentA"
|
||||||
|
constituentB
|
||||||
|
];
|
||||||
|
builder = ./empty-dir-builder.sh;
|
||||||
|
};
|
||||||
|
}
|
|
@ -145,10 +145,47 @@ sub nix_state_dir {
|
||||||
sub makeAndEvaluateJobset {
|
sub makeAndEvaluateJobset {
|
||||||
my ($self, %opts) = @_;
|
my ($self, %opts) = @_;
|
||||||
|
|
||||||
my $expression = $opts{'expression'} || die "Mandatory 'expression' option not passed to makeAndEValuateJobset.";
|
my $expression = $opts{'expression'} || die "Mandatory 'expression' option not passed to makeAndEvaluateJobset.\n";
|
||||||
my $should_build = $opts{'build'} // 0;
|
|
||||||
my $jobsdir = $opts{'jobsdir'} // $self->jobsdir;
|
my $jobsdir = $opts{'jobsdir'} // $self->jobsdir;
|
||||||
|
my $should_build = $opts{'build'} // 0;
|
||||||
|
|
||||||
|
my $jobsetCtx = $self->makeJobset(
|
||||||
|
expression => $expression,
|
||||||
|
jobsdir => $jobsdir,
|
||||||
|
);
|
||||||
|
my $jobset = $jobsetCtx->{"jobset"};
|
||||||
|
|
||||||
|
evalSucceeds($jobset) or die "Evaluating jobs/$expression should exit with return code 0.\n";
|
||||||
|
|
||||||
|
my $builds = {};
|
||||||
|
|
||||||
|
for my $build ($jobset->builds) {
|
||||||
|
if ($should_build) {
|
||||||
|
runBuild($build) or die "Build '".$build->job."' from jobs/$expression should exit with return code 0.\n";
|
||||||
|
$build->discard_changes();
|
||||||
|
}
|
||||||
|
|
||||||
|
$builds->{$build->job} = $build;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $builds;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create a jobset.
|
||||||
|
#
|
||||||
|
# In return, you get a hash of the user, project, and jobset records.
|
||||||
|
#
|
||||||
|
# This always uses an `expression` from the `jobsdir` directory.
|
||||||
|
#
|
||||||
|
# Hash Parameters:
|
||||||
|
#
|
||||||
|
# * expression: The file in the jobsdir directory to evaluate
|
||||||
|
# * jobsdir: An alternative jobsdir to source the expression from
|
||||||
|
sub makeJobset {
|
||||||
|
my ($self, %opts) = @_;
|
||||||
|
|
||||||
|
my $expression = $opts{'expression'} || die "Mandatory 'expression' option not passed to makeJobset.\n";
|
||||||
|
my $jobsdir = $opts{'jobsdir'} // $self->jobsdir;
|
||||||
|
|
||||||
# Create a new user for this test
|
# Create a new user for this test
|
||||||
my $user = $self->db()->resultset('Users')->create({
|
my $user = $self->db()->resultset('Users')->create({
|
||||||
|
@ -174,23 +211,13 @@ sub makeAndEvaluateJobset {
|
||||||
my $jobsetinput = $jobset->jobsetinputs->create({name => "jobs", type => "path"});
|
my $jobsetinput = $jobset->jobsetinputs->create({name => "jobs", type => "path"});
|
||||||
$jobsetinput->jobsetinputalts->create({altnr => 0, value => $jobsdir});
|
$jobsetinput->jobsetinputalts->create({altnr => 0, value => $jobsdir});
|
||||||
|
|
||||||
evalSucceeds($jobset) or die "Evaluating jobs/$expression should exit with return code 0";
|
return {
|
||||||
|
user => $user,
|
||||||
my $builds = {};
|
project => $project,
|
||||||
|
jobset => $jobset,
|
||||||
for my $build ($jobset->builds) {
|
};
|
||||||
if ($should_build) {
|
|
||||||
runBuild($build) or die "Build '".$build->job."' from jobs/$expression should exit with return code 0";
|
|
||||||
$build->discard_changes();
|
|
||||||
}
|
|
||||||
|
|
||||||
$builds->{$build->job} = $build;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $builds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub DESTROY
|
sub DESTROY
|
||||||
{
|
{
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
@ -200,7 +227,7 @@ sub DESTROY
|
||||||
|
|
||||||
sub write_file {
|
sub write_file {
|
||||||
my ($path, $text) = @_;
|
my ($path, $text) = @_;
|
||||||
open(my $fh, '>', $path) or die "Could not open file '$path' $!";
|
open(my $fh, '>', $path) or die "Could not open file '$path' $!\n.";
|
||||||
print $fh $text || "";
|
print $fh $text || "";
|
||||||
close $fh;
|
close $fh;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue