2008-11-28 16:13:06 +00:00
|
|
|
#! /var/run/current-system/sw/bin/perl -w
|
2008-10-10 16:05:05 +00:00
|
|
|
|
|
|
|
use strict;
|
2009-03-09 13:04:46 +00:00
|
|
|
use feature 'switch';
|
2008-11-25 11:09:15 +00:00
|
|
|
use Hydra::Schema;
|
2008-11-28 14:36:04 +00:00
|
|
|
use Hydra::Helper::Nix;
|
2009-10-26 15:39:14 +00:00
|
|
|
use Hydra::Helper::AddBuilds;
|
2009-11-17 13:55:22 +00:00
|
|
|
use Digest::SHA qw(sha256_hex);
|
2008-10-28 10:18:03 +00:00
|
|
|
|
2009-12-18 12:07:45 +00:00
|
|
|
use Email::Sender::Simple qw(sendmail);
|
|
|
|
use Email::Sender::Transport::SMTP;
|
|
|
|
use Email::Simple;
|
|
|
|
use Email::Simple::Creator;
|
|
|
|
use Sys::Hostname::Long;
|
|
|
|
use Config::General;
|
|
|
|
|
2008-10-28 10:18:03 +00:00
|
|
|
|
2009-04-22 22:59:54 +00:00
|
|
|
STDOUT->autoflush();
|
|
|
|
|
2008-11-28 14:36:04 +00:00
|
|
|
my $db = openHydraDB;
|
2009-12-18 12:07:45 +00:00
|
|
|
my %config = new Config::General($ENV{"HYDRA_CONFIG"})->getall;
|
2008-11-05 04:52:52 +00:00
|
|
|
|
2009-03-09 13:04:46 +00:00
|
|
|
sub fetchInputs {
|
2009-03-09 13:58:43 +00:00
|
|
|
my ($project, $jobset, $inputInfo) = @_;
|
2009-03-09 13:04:46 +00:00
|
|
|
foreach my $input ($jobset->jobsetinputs->all) {
|
|
|
|
foreach my $alt ($input->jobsetinputalts->all) {
|
2009-10-26 15:39:14 +00:00
|
|
|
my $info = fetchInput($db, $project, $jobset, $input->name, $input->type, $alt->value);
|
2009-03-09 13:58:43 +00:00
|
|
|
push @{$$inputInfo{$input->name}}, $info if defined $info;
|
2009-03-09 13:04:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-25 13:27:57 +00:00
|
|
|
sub setJobsetError {
|
|
|
|
my ($jobset, $errorMsg) = @_;
|
|
|
|
eval {
|
2009-04-22 22:43:04 +00:00
|
|
|
txn_do($db, sub {
|
2009-03-09 16:22:41 +00:00
|
|
|
$jobset->update({errormsg => $errorMsg, errortime => time});
|
2008-11-25 13:27:57 +00:00
|
|
|
});
|
|
|
|
};
|
2009-12-18 12:07:45 +00:00
|
|
|
sendJobsetErrorNotification($jobset, $errorMsg);
|
2008-11-25 13:27:57 +00:00
|
|
|
}
|
|
|
|
|
2009-12-18 12:07:45 +00:00
|
|
|
sub sendJobsetErrorNotification() {
|
|
|
|
my ($jobset, $errorMsg) = @_;
|
|
|
|
|
|
|
|
return if $jobset->project->owner->emailonerror == 0;
|
|
|
|
|
|
|
|
my $projectName = $jobset->project->name;
|
|
|
|
my $jobsetName = $jobset->name;
|
|
|
|
|
|
|
|
my $sender = $config{'notification_sender'} ||
|
|
|
|
(($ENV{'USER'} || "hydra") . "@" . hostname_long);
|
|
|
|
|
|
|
|
my $body = "Hi,\n"
|
|
|
|
. "\n"
|
|
|
|
. "This is to let you know that Hydra jobset evalation of $projectName:$jobsetName "
|
|
|
|
. "resulted in the following error:\n"
|
|
|
|
. "\n"
|
|
|
|
. "$errorMsg"
|
|
|
|
. "\n"
|
|
|
|
. "Regards,\n\nThe Hydra build daemon.\n";
|
|
|
|
|
|
|
|
my $email = Email::Simple->create(
|
|
|
|
header => [
|
|
|
|
To => $jobset->project->owner->emailaddress,
|
|
|
|
From => "Hydra Build Daemon <$sender>",
|
|
|
|
Subject => "Hydra $projectName:$jobsetName evaluation error",
|
|
|
|
],
|
|
|
|
body => $body,
|
|
|
|
);
|
|
|
|
|
|
|
|
print $email->as_string if $ENV{'HYDRA_MAIL_TEST'};
|
|
|
|
|
|
|
|
sendmail($email);
|
|
|
|
}
|
2008-11-25 13:27:57 +00:00
|
|
|
|
2009-04-23 15:40:36 +00:00
|
|
|
sub permute {
|
|
|
|
my @list = @_;
|
|
|
|
for (my $n = scalar @list - 1; $n > 0; $n--) {
|
|
|
|
my $k = int(rand($n + 1)); # 0 <= $k <= $n
|
|
|
|
@list[$n, $k] = @list[$k, $n];
|
|
|
|
}
|
|
|
|
return @list;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-20 14:50:09 +00:00
|
|
|
sub checkJobset {
|
2008-11-07 14:51:44 +00:00
|
|
|
my ($project, $jobset) = @_;
|
|
|
|
my $inputInfo = {};
|
2008-11-26 13:39:15 +00:00
|
|
|
|
2009-03-09 13:04:46 +00:00
|
|
|
# Fetch all values for all inputs.
|
2009-03-09 13:58:43 +00:00
|
|
|
fetchInputs($project, $jobset, $inputInfo);
|
2008-11-07 14:51:44 +00:00
|
|
|
|
2009-11-17 13:55:22 +00:00
|
|
|
# Hash the arguments to hydra_eval_jobs and check the
|
|
|
|
# JobsetInputHashes to see if we've already evaluated this set of
|
|
|
|
# inputs. If so, bail out.
|
|
|
|
my @args = ($jobset->nixexprinput, $jobset->nixexprpath, inputsToArgs($inputInfo));
|
|
|
|
my $argsHash = sha256_hex("@args");
|
|
|
|
|
|
|
|
if ($jobset->jobsetinputhashes->find({hash => $argsHash})) {
|
|
|
|
print " already evaluated, skipping\n";
|
|
|
|
txn_do($db, sub {
|
|
|
|
$jobset->update({lastcheckedtime => time});
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-03-09 13:04:46 +00:00
|
|
|
# Evaluate the job expression.
|
2009-10-26 17:01:23 +00:00
|
|
|
my ($jobs, $nixExprInput) = evalJobs($inputInfo, $jobset->nixexprinput, $jobset->nixexprpath);
|
2008-11-04 18:23:28 +00:00
|
|
|
|
2009-03-09 13:04:46 +00:00
|
|
|
# Schedule each successfully evaluated job.
|
2009-10-02 16:06:28 +00:00
|
|
|
my %currentBuilds;
|
2009-04-23 15:40:36 +00:00
|
|
|
foreach my $job (permute @{$jobs->{job}}) {
|
2009-03-13 14:49:25 +00:00
|
|
|
next if $job->{jobName} eq "";
|
2009-03-09 13:04:46 +00:00
|
|
|
print "considering job " . $job->{jobName} . "\n";
|
2009-10-26 17:01:23 +00:00
|
|
|
checkBuild($db, $project, $jobset, $inputInfo, $nixExprInput, $job, \%currentBuilds);
|
2008-11-06 18:26:29 +00:00
|
|
|
}
|
2009-03-09 15:16:11 +00:00
|
|
|
|
2009-04-22 22:43:04 +00:00
|
|
|
txn_do($db, sub {
|
2009-10-02 16:06:28 +00:00
|
|
|
|
2009-10-08 11:19:39 +00:00
|
|
|
# Update the last checked times and error messages for each
|
|
|
|
# job.
|
2009-10-02 16:06:28 +00:00
|
|
|
my %failedJobNames;
|
|
|
|
push @{$failedJobNames{$_->{location}}}, $_->{msg} foreach @{$jobs->{error}};
|
|
|
|
|
2009-03-20 14:50:09 +00:00
|
|
|
$jobset->update({lastcheckedtime => time});
|
|
|
|
|
2009-10-26 15:55:19 +00:00
|
|
|
foreach my $job ($jobset->jobs->all) {
|
|
|
|
if ($failedJobNames{$job->name}) {
|
|
|
|
$job->update({errormsg => join '\n', @{$failedJobNames{$job->name}}});
|
2009-03-13 14:49:25 +00:00
|
|
|
} else {
|
2009-10-26 15:55:19 +00:00
|
|
|
$job->update({errormsg => undef});
|
2009-03-13 14:49:25 +00:00
|
|
|
}
|
|
|
|
}
|
2009-10-02 16:06:28 +00:00
|
|
|
|
|
|
|
# Clear the "current" flag on all builds that are no longer
|
|
|
|
# current.
|
|
|
|
foreach my $build ($jobset->builds->search({iscurrent => 1})) {
|
|
|
|
$build->update({iscurrent => 0}) unless $currentBuilds{$build->id};
|
|
|
|
}
|
2009-11-17 13:55:22 +00:00
|
|
|
|
|
|
|
$jobset->jobsetinputhashes->create({hash => $argsHash, timestamp => time});
|
2009-03-13 14:49:25 +00:00
|
|
|
|
2009-10-02 16:06:28 +00:00
|
|
|
});
|
|
|
|
|
2009-03-09 15:16:11 +00:00
|
|
|
# Store the errors messages for jobs that failed to evaluate.
|
|
|
|
my $msg = "";
|
|
|
|
foreach my $error (@{$jobs->{error}}) {
|
2009-03-20 17:06:50 +00:00
|
|
|
my $bindings = "";
|
|
|
|
foreach my $arg (@{$error->{arg}}) {
|
|
|
|
my $input = $inputInfo->{$arg->{name}}->[$arg->{altnr}] or die "invalid input";
|
|
|
|
$bindings .= ", " if $bindings ne "";
|
|
|
|
$bindings .= $arg->{name} . " = ";
|
|
|
|
given ($input->{type}) {
|
|
|
|
when ("string") { $bindings .= "\"" . $input->{value} . "\""; }
|
|
|
|
when ("boolean") { $bindings .= $input->{value}; }
|
|
|
|
default { $bindings .= "..."; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$msg .= "at `" . $error->{location} . "' [$bindings]:\n" . $error->{msg} . "\n\n";
|
2009-03-09 15:16:11 +00:00
|
|
|
}
|
|
|
|
setJobsetError($jobset, $msg);
|
2008-11-04 18:23:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-20 14:50:09 +00:00
|
|
|
sub checkJobsetWrapped {
|
|
|
|
my ($project, $jobset) = @_;
|
|
|
|
|
|
|
|
print "considering jobset ", $jobset->name, " in ", $project->name, "\n";
|
|
|
|
|
|
|
|
eval {
|
|
|
|
checkJobset($project, $jobset);
|
|
|
|
};
|
|
|
|
|
|
|
|
if ($@) {
|
|
|
|
my $msg = $@;
|
|
|
|
print "error evaluating jobset ", $jobset->name, ": $msg";
|
2009-04-22 22:43:04 +00:00
|
|
|
txn_do($db, sub {
|
2009-03-20 14:50:09 +00:00
|
|
|
$jobset->update({lastcheckedtime => time});
|
|
|
|
setJobsetError($jobset, $msg);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2008-11-04 18:23:28 +00:00
|
|
|
|
2009-03-20 14:50:09 +00:00
|
|
|
|
2009-10-26 15:55:19 +00:00
|
|
|
sub checkProjects {
|
2008-11-18 12:48:58 +00:00
|
|
|
foreach my $project ($db->resultset('Projects')->search({enabled => 1})) {
|
2008-11-25 13:27:57 +00:00
|
|
|
print "considering project ", $project->name, "\n";
|
2009-10-08 11:39:16 +00:00
|
|
|
checkJobsetWrapped($project, $_)
|
|
|
|
foreach $project->jobsets->search({enabled => 1});
|
2008-11-04 18:23:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-05 12:32:14 +00:00
|
|
|
# For testing: evaluate a single jobset, then exit.
|
|
|
|
if (scalar @ARGV == 2) {
|
|
|
|
my $projectName = $ARGV[0];
|
|
|
|
my $jobsetName = $ARGV[1];
|
|
|
|
my $jobset = $db->resultset('Jobsets')->find($projectName, $jobsetName) or die;
|
2009-03-20 14:50:09 +00:00
|
|
|
checkJobsetWrapped($jobset->project, $jobset);
|
2009-03-05 12:32:14 +00:00
|
|
|
exit 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-11 10:27:36 +00:00
|
|
|
while (1) {
|
2009-03-23 01:13:37 +00:00
|
|
|
eval {
|
2009-10-26 15:55:19 +00:00
|
|
|
checkProjects;
|
2009-03-23 01:13:37 +00:00
|
|
|
};
|
|
|
|
if ($@) { print "$@"; }
|
2008-11-11 10:27:36 +00:00
|
|
|
print "sleeping...\n";
|
2008-11-29 01:20:13 +00:00
|
|
|
sleep 30;
|
2008-11-11 10:27:36 +00:00
|
|
|
}
|