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" : "" ;
2022-01-05 14:49:18 -05:00
$ c - > stash - > { tracker } = defined $ c - > config - > { tracker } ? $ c - > config - > { tracker } : "" ;
2013-02-27 18:33:47 +01:00
$ 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 (
2022-01-09 11:40:17 -05:00
"select jobsets.project as project, jobsets.name as jobset, count(*) as queued, min(timestamp) as oldest, max(timestamp) as newest from Builds " .
"join Jobsets jobsets on jobsets.id = builds.jobset_id " .
"where finished = 0 group by jobsets.project, jobsets.name order by queued desc" ,
2016-03-08 19:44:51 +01:00
{ 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 (
2022-01-09 11:40:24 -05:00
"select build, stepnr, s.system as system, s.drvpath as drvpath, machine, s.starttime as starttime, jobsets.project, jobsets.name, job, s.busy as busy " .
"from BuildSteps s " .
"join Builds b on s.build = b.id " .
"join Jobsets jobsets on jobsets.id = b.jobset_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
} ,
2022-01-09 10:14:24 -05:00
{
order_by = > [ "jobset.project" , "jobset.name" , "job" ] ,
join = > { "jobset" = > "project" } ,
rows = > $ c - > stash - > { limit } + 1
2021-01-30 11:22:29 -05:00
} )
] ;
# 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 ;