2008-11-25 11:01:42 +00:00
|
|
|
|
package Hydra::Controller::Root;
|
2008-10-28 10:19:31 +00:00
|
|
|
|
|
2016-10-24 16:08:19 +02:00
|
|
|
|
use utf8;
|
2008-10-28 10:19:31 +00:00
|
|
|
|
use strict;
|
|
|
|
|
use warnings;
|
2016-12-06 14:25:09 +01:00
|
|
|
|
use base 'Hydra::Base::Controller::ListBuilds';
|
2008-11-25 11:01:42 +00:00
|
|
|
|
use Hydra::Helper::Nix;
|
2009-02-25 12:03:13 +00:00
|
|
|
|
use Hydra::Helper::CatalystUtils;
|
2019-09-24 16:34:16 -04:00
|
|
|
|
use Hydra::View::TT;
|
2011-12-01 20:46:02 -05:00
|
|
|
|
use Nix::Store;
|
2012-07-30 20:26:34 +00:00
|
|
|
|
use Nix::Config;
|
2013-10-03 17:20:00 +02:00
|
|
|
|
use Encode;
|
2017-04-05 17:55:56 +02:00
|
|
|
|
use File::Basename;
|
2021-10-19 22:53:39 -04:00
|
|
|
|
use JSON::MaybeXS;
|
2021-01-30 08:37:47 -05:00
|
|
|
|
use List::Util qw[min max];
|
2021-12-14 10:08:30 -05:00
|
|
|
|
use List::SomeUtils qw{any};
|
2019-09-24 16:34:16 -04:00
|
|
|
|
use Net::Prometheus;
|
2021-11-30 12:37:17 -08:00
|
|
|
|
use Types::Standard qw/StrMatch/;
|
|
|
|
|
|
|
|
|
|
use constant NARINFO_REGEX => qr{^([a-z0-9]{32})\.narinfo$};
|
2009-02-25 10:52:41 +00:00
|
|
|
|
|
|
|
|
|
# Put this controller at top-level.
|
2008-10-28 10:19:31 +00:00
|
|
|
|
__PACKAGE__->config->{namespace} = '';
|
|
|
|
|
|
2016-01-13 17:32:52 +01:00
|
|
|
|
|
2014-01-10 11:04:28 +01:00
|
|
|
|
sub noLoginNeeded {
|
|
|
|
|
my ($c) = @_;
|
|
|
|
|
|
2017-09-22 11:36:49 +00:00
|
|
|
|
my $hostname = $c->request->headers->header('X-Forwarded-For') || $c->request->hostname;
|
|
|
|
|
my $readonly_ips = $c->config->{readonly_ips} // "";
|
2017-09-25 11:54:55 +00:00
|
|
|
|
my $whitelisted = any { $_ eq $hostname } split(/,/, $readonly_ips);
|
2017-09-22 11:36:49 +00:00
|
|
|
|
|
|
|
|
|
return $whitelisted ||
|
2018-06-14 17:22:35 +02:00
|
|
|
|
$c->request->path eq "api/push-github" ||
|
2017-09-22 11:36:49 +00:00
|
|
|
|
$c->request->path eq "google-login" ||
|
2020-12-26 17:58:16 +01:00
|
|
|
|
$c->request->path eq "github-redirect" ||
|
|
|
|
|
$c->request->path eq "github-login" ||
|
2014-01-10 11:04:28 +01:00
|
|
|
|
$c->request->path eq "login" ||
|
|
|
|
|
$c->request->path eq "logo" ||
|
|
|
|
|
$c->request->path =~ /^static\//;
|
|
|
|
|
}
|
2008-10-28 10:19:31 +00:00
|
|
|
|
|
2014-08-23 16:39:16 +02:00
|
|
|
|
|
2008-11-13 09:25:38 +00:00
|
|
|
|
sub begin :Private {
|
2010-08-31 15:27:46 +00:00
|
|
|
|
my ($self, $c, @args) = @_;
|
2014-01-10 11:04:28 +01:00
|
|
|
|
|
2008-11-13 09:48:10 +00:00
|
|
|
|
$c->stash->{curUri} = $c->request->uri;
|
2009-03-23 13:52:24 +00:00
|
|
|
|
$c->stash->{version} = $ENV{"HYDRA_RELEASE"} || "<devel>";
|
2011-04-01 07:40:06 +00:00
|
|
|
|
$c->stash->{nixVersion} = $ENV{"NIX_RELEASE"} || "<devel>";
|
2010-08-31 15:27:46 +00:00
|
|
|
|
$c->stash->{curTime} = time;
|
2015-07-01 11:34:19 +02:00
|
|
|
|
$c->stash->{logo} = defined $c->config->{hydra_logo} ? "/logo" : "";
|
2013-02-27 18:33:47 +01:00
|
|
|
|
$c->stash->{tracker} = $ENV{"HYDRA_TRACKER"};
|
|
|
|
|
$c->stash->{flashMsg} = $c->flash->{flashMsg};
|
|
|
|
|
$c->stash->{successMsg} = $c->flash->{successMsg};
|
2011-04-19 12:00:54 +00:00
|
|
|
|
|
2014-01-10 11:04:28 +01:00
|
|
|
|
$c->stash->{isPrivateHydra} = $c->config->{private} // "0" ne "0";
|
|
|
|
|
|
|
|
|
|
if ($c->stash->{isPrivateHydra} && ! noLoginNeeded($c)) {
|
|
|
|
|
requireUser($c);
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-31 15:37:50 +00:00
|
|
|
|
if (scalar(@args) == 0 || $args[0] ne "static") {
|
2016-03-16 15:19:18 +01:00
|
|
|
|
$c->stash->{nrRunningBuilds} = dbh($c)->selectrow_array(
|
2017-12-07 15:35:31 +01:00
|
|
|
|
"select count(distinct build) from buildsteps where busy != 0");
|
2013-01-22 14:09:37 +01:00
|
|
|
|
$c->stash->{nrQueuedBuilds} = $c->model('DB::Builds')->search({ finished => 0 })->count();
|
2010-08-31 15:27:46 +00:00
|
|
|
|
}
|
2013-05-25 15:36:58 -04:00
|
|
|
|
|
|
|
|
|
# Gather the supported input types.
|
|
|
|
|
$c->stash->{inputTypes} = {
|
|
|
|
|
'string' => 'String value',
|
|
|
|
|
'boolean' => 'Boolean',
|
2013-09-30 12:03:25 +02:00
|
|
|
|
'nix' => 'Nix expression',
|
2013-11-11 21:36:26 +00:00
|
|
|
|
'build' => 'Previous Hydra build',
|
|
|
|
|
'sysbuild' => 'Previous Hydra build (same system)',
|
2013-11-11 21:17:22 +00:00
|
|
|
|
'eval' => 'Previous Hydra evaluation'
|
2013-05-25 15:36:58 -04:00
|
|
|
|
};
|
|
|
|
|
$_->supportedInputTypes($c->stash->{inputTypes}) foreach @{$c->hydra_plugins};
|
2013-06-17 12:34:21 -04:00
|
|
|
|
|
2016-10-20 16:11:33 +02:00
|
|
|
|
# XSRF protection: require POST requests to have the same origin.
|
2018-06-14 17:22:35 +02:00
|
|
|
|
if ($c->req->method eq "POST" && $c->req->path ne "api/push-github") {
|
2021-02-07 19:18:29 +01:00
|
|
|
|
my $referer = $c->req->header('Referer');
|
|
|
|
|
$referer //= $c->req->header('Origin');
|
2016-10-20 16:11:33 +02:00
|
|
|
|
my $base = $c->req->base;
|
2016-11-01 11:00:30 +01:00
|
|
|
|
die unless $base =~ /\/$/;
|
|
|
|
|
$referer .= "/";
|
2016-10-24 16:08:19 +02:00
|
|
|
|
error($c, "POST requests should come from ‘$base’.")
|
2016-10-21 17:56:34 +02:00
|
|
|
|
unless defined $referer && substr($referer, 0, length $base) eq $base;
|
2016-10-20 16:11:33 +02:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-17 12:34:21 -04:00
|
|
|
|
$c->forward('deserialize');
|
|
|
|
|
|
|
|
|
|
$c->stash->{params} = $c->request->data or $c->request->params;
|
|
|
|
|
unless (defined $c->stash->{params} and %{$c->stash->{params}}) {
|
|
|
|
|
$c->stash->{params} = $c->request->params;
|
|
|
|
|
}
|
2016-03-25 14:48:12 +01:00
|
|
|
|
|
|
|
|
|
# Set the Vary header to "Accept" to ensure that browsers don't
|
|
|
|
|
# mix up HTML and JSON responses.
|
|
|
|
|
$c->response->headers->header('Vary', 'Accept');
|
2008-11-13 09:25:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-13 17:32:52 +01:00
|
|
|
|
|
2013-06-17 12:34:21 -04:00
|
|
|
|
sub deserialize :ActionClass('Deserialize') { }
|
|
|
|
|
|
2008-11-13 09:25:38 +00:00
|
|
|
|
|
2008-10-28 10:19:31 +00:00
|
|
|
|
sub index :Path :Args(0) {
|
2008-11-26 17:43:45 +00:00
|
|
|
|
my ($self, $c) = @_;
|
2009-04-02 16:15:57 +00:00
|
|
|
|
$c->stash->{template} = 'overview.tt';
|
2021-03-25 18:14:01 -04:00
|
|
|
|
$c->stash->{projects} = [$c->model('DB::Projects')->search({}, {order_by => ['enabled DESC', 'name']})];
|
2010-04-27 13:29:08 +00:00
|
|
|
|
$c->stash->{newsItems} = [$c->model('DB::NewsItems')->search({}, { order_by => ['createtime DESC'], rows => 5 })];
|
2013-10-03 14:50:56 +02:00
|
|
|
|
$self->status_ok($c,
|
2013-10-24 16:12:36 -04:00
|
|
|
|
entity => $c->stash->{projects}
|
2013-06-17 12:34:21 -04:00
|
|
|
|
);
|
2008-10-28 10:19:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-06-17 12:34:21 -04:00
|
|
|
|
sub queue :Local :Args(0) :ActionClass('REST') { }
|
|
|
|
|
|
|
|
|
|
sub queue_GET {
|
2008-11-26 19:48:04 +00:00
|
|
|
|
my ($self, $c) = @_;
|
2008-11-26 17:43:45 +00:00
|
|
|
|
$c->stash->{template} = 'queue.tt';
|
2013-02-27 18:33:47 +01:00
|
|
|
|
$c->stash->{flashMsg} //= $c->flash->{buildMsg};
|
2013-06-17 12:34:21 -04:00
|
|
|
|
$self->status_ok(
|
|
|
|
|
$c,
|
2015-10-27 15:37:17 +01:00
|
|
|
|
entity => [$c->model('DB::Builds')->search(
|
|
|
|
|
{ finished => 0 },
|
|
|
|
|
{ order_by => ["globalpriority desc", "id"],
|
|
|
|
|
, columns => [@buildListColumns]
|
|
|
|
|
})]
|
2013-06-17 12:34:21 -04:00
|
|
|
|
);
|
2008-11-26 17:43:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-08-31 15:27:46 +00:00
|
|
|
|
|
2016-03-08 19:44:51 +01:00
|
|
|
|
sub queue_summary :Local :Path('queue-summary') :Args(0) {
|
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
|
$c->stash->{template} = 'queue-summary.tt';
|
|
|
|
|
|
2016-03-16 15:19:18 +01:00
|
|
|
|
$c->stash->{queued} = dbh($c)->selectall_arrayref(
|
2016-03-08 19:44:51 +01:00
|
|
|
|
"select project, jobset, count(*) as queued, min(timestamp) as oldest, max(timestamp) as newest from Builds " .
|
|
|
|
|
"where finished = 0 group by project, jobset order by queued desc",
|
|
|
|
|
{ Slice => {} });
|
2016-03-22 17:03:26 +01:00
|
|
|
|
|
|
|
|
|
$c->stash->{systems} = dbh($c)->selectall_arrayref(
|
|
|
|
|
"select system, count(*) as c from Builds where finished = 0 group by system order by c desc",
|
|
|
|
|
{ Slice => {} });
|
2016-03-08 19:44:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-06-17 12:34:21 -04:00
|
|
|
|
sub status :Local :Args(0) :ActionClass('REST') { }
|
|
|
|
|
|
|
|
|
|
sub status_GET {
|
2010-08-31 15:27:46 +00:00
|
|
|
|
my ($self, $c) = @_;
|
2013-06-17 12:34:21 -04:00
|
|
|
|
$self->status_ok(
|
|
|
|
|
$c,
|
2015-10-27 15:37:17 +01:00
|
|
|
|
entity => [$c->model('DB::Builds')->search(
|
2017-12-07 15:35:31 +01:00
|
|
|
|
{ "buildsteps.busy" => { '!=', 0 } },
|
2015-10-27 15:37:17 +01:00
|
|
|
|
{ order_by => ["globalpriority DESC", "id"],
|
|
|
|
|
join => "buildsteps",
|
|
|
|
|
columns => [@buildListColumns]
|
|
|
|
|
})]
|
2013-06-17 12:34:21 -04:00
|
|
|
|
);
|
2010-08-31 15:27:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-02-18 17:11:46 +01:00
|
|
|
|
sub queue_runner_status :Local :Path('queue-runner-status') :Args(0) :ActionClass('REST') { }
|
|
|
|
|
|
|
|
|
|
sub queue_runner_status_GET {
|
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
|
|
|
|
|
|
#my $status = from_json($c->model('DB::SystemStatus')->find('queue-runner')->status);
|
2021-12-15 17:22:11 -05:00
|
|
|
|
my $status = decode_json(`hydra-queue-runner --status`);
|
2016-02-18 17:11:46 +01:00
|
|
|
|
if ($?) { $status->{status} = "unknown"; }
|
|
|
|
|
my $json = JSON->new->pretty()->canonical();
|
|
|
|
|
|
|
|
|
|
$c->stash->{template} = 'queue-runner-status.tt';
|
|
|
|
|
$c->stash->{status} = $json->encode($status);
|
|
|
|
|
$self->status_ok($c, entity => $status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-02-20 16:40:09 +01:00
|
|
|
|
sub machines :Local Args(0) {
|
|
|
|
|
my ($self, $c) = @_;
|
2013-03-04 15:37:20 -05:00
|
|
|
|
my $machines = getMachines;
|
2013-04-23 15:20:24 +02:00
|
|
|
|
|
|
|
|
|
# Add entry for localhost.
|
2015-09-09 16:51:20 +02:00
|
|
|
|
$machines->{''} //= {};
|
|
|
|
|
delete $machines->{'localhost'};
|
|
|
|
|
|
|
|
|
|
my $status = $c->model('DB::SystemStatus')->find("queue-runner");
|
|
|
|
|
if ($status) {
|
|
|
|
|
my $ms = decode_json($status->status)->{"machines"};
|
|
|
|
|
foreach my $name (keys %{$ms}) {
|
|
|
|
|
$name = "" if $name eq "localhost";
|
|
|
|
|
$machines->{$name} //= {disabled => 1};
|
|
|
|
|
$machines->{$name}->{nrStepsDone} = $ms->{$name}->{nrStepsDone};
|
|
|
|
|
$machines->{$name}->{avgStepBuildTime} = $ms->{$name}->{avgStepBuildTime} // 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-04-23 15:20:24 +02:00
|
|
|
|
|
2013-03-04 15:37:20 -05:00
|
|
|
|
$c->stash->{machines} = $machines;
|
2016-03-16 15:19:18 +01:00
|
|
|
|
$c->stash->{steps} = dbh($c)->selectall_arrayref(
|
2017-12-07 16:20:23 +01:00
|
|
|
|
"select build, stepnr, s.system as system, s.drvpath as drvpath, machine, s.starttime as starttime, project, jobset, job, s.busy as busy " .
|
2016-03-16 15:19:18 +01:00
|
|
|
|
"from BuildSteps s join Builds b on s.build = b.id " .
|
2017-12-07 15:35:31 +01:00
|
|
|
|
"where busy != 0 order by machine, stepnr",
|
2016-03-16 15:19:18 +01:00
|
|
|
|
{ Slice => {} });
|
2013-02-20 16:40:09 +01:00
|
|
|
|
$c->stash->{template} = 'machine-status.tt';
|
2019-02-14 01:18:31 +01:00
|
|
|
|
$self->status_ok($c, entity => $c->stash->{machines});
|
2013-02-20 16:40:09 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-24 16:34:16 -04:00
|
|
|
|
sub prometheus :Local Args(0) {
|
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
|
my $machines = getMachines;
|
|
|
|
|
|
|
|
|
|
my $client = Net::Prometheus->new;
|
|
|
|
|
my $duration = $client->new_histogram(
|
|
|
|
|
name => "hydra_machine_build_duration",
|
|
|
|
|
help => "How long builds are taking per server. Note: counts are gauges, NOT counters.",
|
|
|
|
|
labels => [ "machine" ],
|
|
|
|
|
buckets => [
|
|
|
|
|
60,
|
|
|
|
|
600,
|
|
|
|
|
1800,
|
|
|
|
|
3600,
|
|
|
|
|
7200,
|
|
|
|
|
21600,
|
|
|
|
|
43200,
|
|
|
|
|
86400,
|
|
|
|
|
172800,
|
|
|
|
|
259200,
|
|
|
|
|
345600,
|
|
|
|
|
518400,
|
|
|
|
|
604800,
|
|
|
|
|
691200
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
my $steps = dbh($c)->selectall_arrayref(
|
|
|
|
|
"select machine, s.starttime as starttime " .
|
|
|
|
|
"from BuildSteps s join Builds b on s.build = b.id " .
|
|
|
|
|
"where busy != 0 order by machine, stepnr",
|
|
|
|
|
{ Slice => {} });
|
|
|
|
|
|
|
|
|
|
foreach my $step (@$steps) {
|
|
|
|
|
my $name = $step->{machine} ? Hydra::View::TT->stripSSHUser(undef, $step->{machine}) : "";
|
|
|
|
|
$name = "localhost" unless $name;
|
|
|
|
|
$duration->labels($name)->observe(time - $step->{starttime});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$c->stash->{'plain'} = { data => $client->render };
|
|
|
|
|
$c->forward('Hydra::View::Plain');
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-20 16:40:09 +01:00
|
|
|
|
|
2016-12-06 14:25:09 +01:00
|
|
|
|
# Hydra::Base::Controller::ListBuilds needs this.
|
|
|
|
|
sub get_builds : Chained('/') PathPart('') CaptureArgs(0) {
|
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
|
$c->stash->{allBuilds} = $c->model('DB::Builds');
|
|
|
|
|
$c->stash->{latestSucceeded} = $c->model('DB')->resultset('LatestSucceeded');
|
|
|
|
|
$c->stash->{channelBaseName} = "everything";
|
|
|
|
|
$c->stash->{total} = $c->model('DB::NrBuilds')->find('finished')->count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-03-31 13:48:03 +00:00
|
|
|
|
sub robots_txt : Path('robots.txt') {
|
|
|
|
|
my ($self, $c) = @_;
|
2016-11-17 18:13:57 +01:00
|
|
|
|
$c->stash->{'plain'} = { data => "User-agent: *\nDisallow: /*\n" };
|
2009-03-31 13:48:03 +00:00
|
|
|
|
$c->forward('Hydra::View::Plain');
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-01 07:40:06 +00:00
|
|
|
|
|
2008-10-28 10:19:31 +00:00
|
|
|
|
sub default :Path {
|
2008-11-26 17:43:45 +00:00
|
|
|
|
my ($self, $c) = @_;
|
2009-02-25 14:34:29 +00:00
|
|
|
|
notFound($c, "Page not found.");
|
2009-02-13 17:35:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-02-25 10:52:41 +00:00
|
|
|
|
sub end : ActionClass('RenderView') {
|
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
|
|
2013-03-04 15:25:23 +01:00
|
|
|
|
if (defined $c->stash->{json}) {
|
2014-08-23 16:39:16 +02:00
|
|
|
|
if (scalar @{$c->error}) {
|
|
|
|
|
# FIXME: dunno why we need to do decode_utf8 here.
|
|
|
|
|
$c->stash->{json}->{error} = join "\n", map { decode_utf8($_); } @{$c->error};
|
2013-03-04 15:25:23 +01:00
|
|
|
|
$c->clear_errors;
|
|
|
|
|
}
|
|
|
|
|
$c->forward('View::JSON');
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-03 14:45:23 +02:00
|
|
|
|
elsif (scalar @{$c->error}) {
|
|
|
|
|
$c->stash->{resource} = { error => join "\n", @{$c->error} };
|
2018-12-01 13:39:10 -05:00
|
|
|
|
if ($c->stash->{lazy}) {
|
|
|
|
|
$c->response->headers->header('X-Hydra-Lazy', 'Yes');
|
|
|
|
|
$c->stash->{template} = 'lazy_error.tt';
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$c->stash->{template} = 'error.tt';
|
|
|
|
|
}
|
2014-08-23 16:39:16 +02:00
|
|
|
|
$c->stash->{errors} = $c->error;
|
2013-02-22 14:27:38 +01:00
|
|
|
|
$c->response->status(500) if $c->response->status == 200;
|
2009-02-25 16:29:54 +00:00
|
|
|
|
if ($c->response->status >= 300) {
|
|
|
|
|
$c->stash->{httpStatus} =
|
|
|
|
|
$c->response->status . " " . HTTP::Status::status_message($c->response->status);
|
|
|
|
|
}
|
2009-02-25 10:52:41 +00:00
|
|
|
|
$c->clear_errors;
|
|
|
|
|
}
|
2013-06-17 12:34:21 -04:00
|
|
|
|
|
2013-07-26 12:04:27 -04:00
|
|
|
|
$c->forward('serialize') if defined $c->stash->{resource};
|
2009-02-25 10:52:41 +00:00
|
|
|
|
}
|
2008-10-28 10:19:31 +00:00
|
|
|
|
|
2013-10-03 14:45:23 +02:00
|
|
|
|
|
2013-06-17 12:34:21 -04:00
|
|
|
|
sub serialize : ActionClass('Serialize') { }
|
|
|
|
|
|
2008-10-28 10:19:31 +00:00
|
|
|
|
|
2010-06-22 12:00:19 +00:00
|
|
|
|
sub nar :Local :Args(1) {
|
|
|
|
|
my ($self, $c, $path) = @_;
|
|
|
|
|
|
2016-02-26 17:27:30 +01:00
|
|
|
|
die if $path =~ /\//;
|
2010-06-22 12:00:19 +00:00
|
|
|
|
|
2017-10-18 12:23:07 +02:00
|
|
|
|
if (!isLocalStore) {
|
2016-02-26 17:27:30 +01:00
|
|
|
|
notFound($c, "There is no binary cache here.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
$path = $Nix::Config::storeDir . "/$path";
|
|
|
|
|
|
|
|
|
|
gone($c, "Path " . $path . " is no longer available.") unless isValidPath($path);
|
|
|
|
|
|
|
|
|
|
$c->stash->{current_view} = 'NixNAR';
|
|
|
|
|
$c->stash->{storePath} = $path;
|
|
|
|
|
}
|
2010-06-22 12:00:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-07-02 15:18:30 +00:00
|
|
|
|
|
2012-07-30 20:26:34 +00:00
|
|
|
|
sub nix_cache_info :Path('nix-cache-info') :Args(0) {
|
|
|
|
|
my ($self, $c) = @_;
|
2016-02-26 17:27:30 +01:00
|
|
|
|
|
2017-10-18 12:23:07 +02:00
|
|
|
|
if (!isLocalStore) {
|
2016-02-26 17:27:30 +01:00
|
|
|
|
notFound($c, "There is no binary cache here.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
$c->response->content_type('text/plain');
|
|
|
|
|
$c->stash->{plain}->{data} =
|
|
|
|
|
"StoreDir: $Nix::Config::storeDir\n" .
|
|
|
|
|
"WantMassQuery: 0\n" .
|
|
|
|
|
# Give Hydra binary caches a very low priority (lower than the
|
|
|
|
|
# static binary cache http://nixos.org/binary-cache).
|
|
|
|
|
"Priority: 100\n";
|
|
|
|
|
setCacheHeaders($c, 24 * 60 * 60);
|
|
|
|
|
$c->forward('Hydra::View::Plain');
|
|
|
|
|
}
|
2012-07-30 20:26:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-11-30 12:37:17 -08:00
|
|
|
|
sub narinfo :Path :Args(StrMatch[NARINFO_REGEX]) {
|
|
|
|
|
my ($self, $c, $narinfo) = @_;
|
2013-04-23 15:33:58 +02:00
|
|
|
|
|
2017-10-18 12:23:07 +02:00
|
|
|
|
if (!isLocalStore) {
|
2016-02-26 17:27:30 +01:00
|
|
|
|
notFound($c, "There is no binary cache here.");
|
2013-04-23 15:33:58 +02:00
|
|
|
|
}
|
2013-04-30 16:23:19 +02:00
|
|
|
|
|
2016-02-26 17:27:30 +01:00
|
|
|
|
else {
|
2021-11-30 12:37:17 -08:00
|
|
|
|
my ($hash) = $narinfo =~ NARINFO_REGEX;
|
2016-02-26 17:27:30 +01:00
|
|
|
|
|
2021-11-30 12:37:17 -08:00
|
|
|
|
die("Hash length was not 32") if length($hash) != 32;
|
2016-02-26 17:27:30 +01:00
|
|
|
|
my $path = queryPathFromHashPart($hash);
|
|
|
|
|
|
|
|
|
|
if (!$path) {
|
|
|
|
|
$c->response->status(404);
|
|
|
|
|
$c->response->content_type('text/plain');
|
|
|
|
|
$c->stash->{plain}->{data} = "does not exist\n";
|
|
|
|
|
$c->forward('Hydra::View::Plain');
|
|
|
|
|
setCacheHeaders($c, 60 * 60);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$c->stash->{storePath} = $path;
|
|
|
|
|
$c->forward('Hydra::View::NARInfo');
|
|
|
|
|
}
|
2012-07-02 15:18:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2011-04-18 08:21:27 +00:00
|
|
|
|
sub logo :Local {
|
|
|
|
|
my ($self, $c) = @_;
|
2015-07-01 11:34:19 +02:00
|
|
|
|
my $path = $c->config->{hydra_logo} // die("Logo not set!");
|
2011-04-18 08:21:27 +00:00
|
|
|
|
$c->serve_static_file($path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-02-21 17:27:17 +01:00
|
|
|
|
sub evals :Local Args(0) {
|
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
|
|
|
|
|
|
$c->stash->{template} = 'evals.tt';
|
|
|
|
|
|
|
|
|
|
my $page = int($c->req->param('page') || "1") || 1;
|
|
|
|
|
|
|
|
|
|
my $resultsPerPage = 20;
|
|
|
|
|
|
|
|
|
|
my $evals = $c->model('DB::JobsetEvals');
|
|
|
|
|
|
|
|
|
|
$c->stash->{page} = $page;
|
|
|
|
|
$c->stash->{resultsPerPage} = $resultsPerPage;
|
|
|
|
|
$c->stash->{total} = $evals->search({hasnewbuilds => 1})->count;
|
2021-06-16 12:42:25 -04:00
|
|
|
|
$c->stash->{evals} = getEvals($c, $evals, ($page - 1) * $resultsPerPage, $resultsPerPage);
|
2019-02-14 01:18:31 +01:00
|
|
|
|
|
|
|
|
|
$self->status_ok($c, entity => $c->stash->{evals});
|
2013-02-21 17:27:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-07-10 15:08:34 +02:00
|
|
|
|
sub steps :Local Args(0) {
|
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
|
|
|
|
|
|
$c->stash->{template} = 'steps.tt';
|
|
|
|
|
|
|
|
|
|
my $page = int($c->req->param('page') || "1") || 1;
|
|
|
|
|
|
|
|
|
|
my $resultsPerPage = 20;
|
|
|
|
|
|
|
|
|
|
$c->stash->{page} = $page;
|
|
|
|
|
$c->stash->{resultsPerPage} = $resultsPerPage;
|
|
|
|
|
$c->stash->{steps} = [ $c->model('DB::BuildSteps')->search(
|
|
|
|
|
{ starttime => { '!=', undef },
|
|
|
|
|
stoptime => { '!=', undef }
|
|
|
|
|
},
|
|
|
|
|
{ order_by => [ "stoptime desc" ],
|
|
|
|
|
rows => $resultsPerPage,
|
|
|
|
|
offset => ($page - 1) * $resultsPerPage
|
|
|
|
|
}) ];
|
|
|
|
|
|
|
|
|
|
$c->stash->{total} = approxTableSize($c, "IndexBuildStepsOnStopTime");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-02-22 15:45:10 +01:00
|
|
|
|
sub search :Local Args(0) {
|
|
|
|
|
my ($self, $c) = @_;
|
|
|
|
|
$c->stash->{template} = 'search.tt';
|
|
|
|
|
|
|
|
|
|
my $query = trim $c->request->params->{"query"};
|
|
|
|
|
|
|
|
|
|
error($c, "Query is empty.") if $query eq "";
|
|
|
|
|
error($c, "Invalid character in query.")
|
2013-04-25 09:57:30 -04:00
|
|
|
|
unless $query =~ /^[a-zA-Z0-9_\-\/.]+$/;
|
2013-02-22 15:45:10 +01:00
|
|
|
|
|
2021-01-30 08:37:47 -05:00
|
|
|
|
my $limit = int(trim ($c->request->params->{"limit"} || "10"));
|
|
|
|
|
$c->stash->{limit} = min(50, max(1, $limit));
|
2013-02-22 16:41:42 +01:00
|
|
|
|
|
2021-01-30 11:22:29 -05:00
|
|
|
|
$c->model('DB')->schema->txn_do(sub {
|
|
|
|
|
$c->model('DB')->schema->storage->dbh->do("SET LOCAL statement_timeout = 20000");
|
|
|
|
|
$c->stash->{projects} = [ $c->model('DB::Projects')->search(
|
|
|
|
|
{ -and =>
|
|
|
|
|
[ { -or => [ name => { ilike => "%$query%" }, displayName => { ilike => "%$query%" }, description => { ilike => "%$query%" } ] }
|
|
|
|
|
, { hidden => 0 }
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{ order_by => ["name"] } ) ];
|
|
|
|
|
|
|
|
|
|
$c->stash->{jobsets} = [ $c->model('DB::Jobsets')->search(
|
|
|
|
|
{ -and =>
|
|
|
|
|
[ { -or => [ "me.name" => { ilike => "%$query%" }, "me.description" => { ilike => "%$query%" } ] }
|
|
|
|
|
, { "project.hidden" => 0, "me.hidden" => 0 }
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{ order_by => ["project", "name"], join => ["project"] } ) ];
|
|
|
|
|
|
|
|
|
|
$c->stash->{jobs} = [ $c->model('DB::Builds')->search(
|
|
|
|
|
{ "job" => { ilike => "%$query%" }
|
|
|
|
|
, "project.hidden" => 0
|
|
|
|
|
, "jobset.hidden" => 0
|
|
|
|
|
, iscurrent => 1
|
|
|
|
|
},
|
|
|
|
|
{ order_by => ["project", "jobset", "job"], join => ["project", "jobset"]
|
|
|
|
|
, rows => $c->stash->{limit} + 1
|
|
|
|
|
} )
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
# Perform build search in separate queries to prevent seq scan on buildoutputs table.
|
|
|
|
|
$c->stash->{builds} = [ $c->model('DB::Builds')->search(
|
|
|
|
|
{ "buildoutputs.path" => { ilike => "%$query%" } },
|
|
|
|
|
{ order_by => ["id desc"], join => ["buildoutputs"]
|
|
|
|
|
, rows => $c->stash->{limit}
|
|
|
|
|
} ) ];
|
|
|
|
|
|
|
|
|
|
$c->stash->{buildsdrv} = [ $c->model('DB::Builds')->search(
|
|
|
|
|
{ "drvpath" => { ilike => "%$query%" } },
|
|
|
|
|
{ order_by => ["id desc"]
|
|
|
|
|
, rows => $c->stash->{limit}
|
|
|
|
|
} ) ];
|
|
|
|
|
|
|
|
|
|
$c->stash->{resource} = { projects => $c->stash->{projects},
|
|
|
|
|
jobsets => $c->stash->{jobsets},
|
|
|
|
|
builds => $c->stash->{builds},
|
|
|
|
|
buildsdrv => $c->stash->{buildsdrv} };
|
|
|
|
|
});
|
2013-02-22 15:45:10 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-05 17:55:56 +02:00
|
|
|
|
sub serveLogFile {
|
|
|
|
|
my ($c, $logPath, $tail) = @_;
|
|
|
|
|
$c->stash->{logPath} = $logPath;
|
|
|
|
|
$c->stash->{tail} = $tail;
|
|
|
|
|
$c->forward('Hydra::View::NixLog');
|
|
|
|
|
}
|
2015-07-10 15:08:34 +02:00
|
|
|
|
|
2014-02-19 10:21:59 +00:00
|
|
|
|
sub log :Local :Args(1) {
|
2017-04-05 17:55:56 +02:00
|
|
|
|
my ($self, $c, $drvPath) = @_;
|
2014-02-19 10:21:59 +00:00
|
|
|
|
|
2017-04-05 17:55:56 +02:00
|
|
|
|
$drvPath = "/nix/store/$drvPath";
|
2014-02-19 10:21:59 +00:00
|
|
|
|
|
2017-04-05 17:55:56 +02:00
|
|
|
|
my $tail = $c->request->params->{"tail"};
|
2014-02-19 10:21:59 +00:00
|
|
|
|
|
2017-04-05 17:55:56 +02:00
|
|
|
|
die if defined $tail && $tail !~ /^[0-9]+$/;
|
2014-02-19 10:21:59 +00:00
|
|
|
|
|
2017-04-05 17:55:56 +02:00
|
|
|
|
my $logFile = findLog($c, $drvPath);
|
|
|
|
|
|
|
|
|
|
if (defined $logFile) {
|
|
|
|
|
serveLogFile($c, $logFile, $tail);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my $logPrefix = $c->config->{log_prefix};
|
|
|
|
|
|
|
|
|
|
if (defined $logPrefix) {
|
|
|
|
|
$c->res->redirect($logPrefix . "log/" . basename($drvPath));
|
|
|
|
|
} else {
|
|
|
|
|
notFound($c, "The build log of $drvPath is not available.");
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-02-22 15:45:10 +01:00
|
|
|
|
|
2008-10-28 10:19:31 +00:00
|
|
|
|
1;
|