The catalyst-action-rest branch from shlevy/hydra was an exploration of using Catalyst::Action::REST to create a JSON API for hydra. This commit merges in the best bits from that experiment, with the goal that further API endpoints can be added incrementally.
In addition to migrating more endpoints, there is potential for improvement in what's already been done:
Fixes NixOS/hydra#98
Signed-off-by: Shea Levy <shea@shealevy.com>
LZVO64YG43JD7YMZSCTZNOBS5ROZA4FMPKJW2YOMHX2V5PTGBVWQC
BJAJLOT3TD2VHISB7RAF2BIZ74AP6AWB6UTGBUMPHGTIJYNOVADQC
P2NOTX5D7PII2PJOCPO265BTNZEEMI2BWA3JK7BP6VYDBSRVWHGAC
S4V64WMTM6ZPIRCUASPXKBFTY545PNMZGYNEPQGGNTXA7YJ2ZGPAC
NS5GKQHW26OSSNT722GGKKMIQLTS7UKSWG2CUSV5QYBUF3A24I7AC
PHJF4FUTRWDFKS4RTYGT2PKASDZAPPA3JY3VF2YANHFI5AUTIZAQC
FHHFNCCR2IOCRTCGF4ZLHVKAGL7E2ZN3B75E4JEMCPNB5GN4MYLQC
4HPT4SDDNU24OX2P4FDP2KXKINDCJLZEBN2VIEEKWFEVT4TNWXZAC
HZWUT4YNGQE3RSTS7XAZK4UQCUHZ5KHA7CHL2JNSXQIPPB6UGKTQC
FAUCW4NHW33VMQIEIZQFUKOEKQDR33HX3ETI2PF7X4LCV2CFV53QC
QLOLZHRXOUSNVLJG2SWVC5EISFUOUKJIT32XN6DNW4VFFJMN5PMAC
OQNFJWAGTY775SICOZKEUX42DWA4Y2BOMUZTTA5HQO3BIWAQM7ZAC
R6B5CAFF3BWJPW6I5YGKXOKKCW6S7D7AHO2HLZVFUW3NL77ZW44QC
E7M2WP7AQPTJR3DUI3ONEEG3GW36EA4CM77HTZ4KYVETJ7VA3LEAC
6K5PBUUN4GQAMOVX5BS6YYMHJ3PIF2PPZBTIEQ4R7BQNHC23GS3AC
JAH3UPWAVSHXIPNGL6PROQPZBYZHPJNFONWBDZX4HCX646USZXUQC
M6UBWKN23RAUWXCHSZOUL3YBBZWBB5II3N56CLGWRVRYVFGN3QVAC
225GEK4NN53NJ6EBDAJNECR76FILAHV7KNEG23TDQ2KYMG7NFEUAC
T4LLYESZ2HUXSLKZ6GNBLVWUVG7R5IDFHYHYO773QIZ6QTOOXR2AC
7C6HSXUQ6HFILY35VPZWCJOVBAL7IJ2QXOU2X5DTIFJ6FQ2RHOQAC
YQWH4POV22KYCCKOTZXD36QKUOWEQ2DSPYPO5DNZDZ344RI25OAAC
3PNG7NIBQQURUUPRVQXYL342OT7JUUYOMY2JJNP6YDX7SYJDZMYAC
XJFHFZCA2VXGYSRDJDLAZZX3MLRH2KDT242DHWRU5XQRJ6XIHZUQC
FPK5LF53CFUEKFYJ3IYXT4UTVC6IITWJOCFATMC4PLHEUP5SIEAAC
BTUDUY6FY6UTRZOGLSPFNA4MVTSP7JXBD44VYF6BPKNFF6Y3CY2QC
AKAZKCR6GFCZQBR2ZJSZEI3SXW4S25V7X7JGHUYNUITQQSAVAF5AC
5NO7NCKTMM5ZW7JYOETUFOSWK2ACTXWDZGJAFXZN6L3OF6BFTNOQC
PMNWRTGJ4GVSMSSAWSUD57B26PCRAHMZIQ5SIWJIK7A74ENKEQLAC
D5QIOJGPKQJIYBUCSC3MFJ3TXLPNZ2XMI37GXMFRVRFWWR2VMTFAC
BKOIYITRBRVU4D7XFAZPV5QHAPCBMIO3SBNHYJ6TVT43WR32CTHQC
PCKLFRT5IZVLG47GQQ23GBSROKUR4CUEZW4PRVGREHNDFTCZ7VBAC
EVZTBKEFHKIT6LIDRNAYFNELPYS7L2YQBLPQUB3LHLYSP3UM4OCQC
LBNVQXUBEZ45SOTGVXK5UEZXIAIZTJLWZNUYFI4JZ6J65N3KPDVQC
FU4GO5VN3PKQ3DKOHVHJTMHUKOKMKWS27TIULQ3FMEFXRED5Q44AC
6F4UNDTCAW7LYSKTUUUCX32BLAAGYPUPK2LXMMZ32Y6H3CBWP6TQC
NUIKDEHLDR4ANFZFRCWBAWZGFVMHCZ7PSMPPFVQJ3AAAAWAQQD4AC
D3DIBMOKXK2E65267BEEWQL4S4NSHGZBCY7YTU34JSEPZ7AKNBRQC
H5REHM3MZKP6SZPMSTMO7SHAVS6W756Q6P2AEJ27R4FZB2IZ2HVAC
YPDYBK5GXGNHZMJWC2EHO3T4BIMYK5LMP2G3ABGHOKGIWV4ONIYAC
36M6DGITWMQQGZ7VROEHUOY4FP7OHPJ5DXMQMHHISBIAJLQE24YAC
XDDCO6CHPWGXFV3RSMMUYY45DTXJXQNYYHUT2PCYAGRODK43MD6AC
OD5FSS5AJ3XRTV5Q4UQQF6JMBT6UUL7UUOMPPUBNOM6ZMVIHW35QC
A52HEFHQNVNF2OUSWDSUYVVXLYR2UFCGOORPCN27CJJYA4UDJA3AC
ZILILXXKP4Z64UGIXIPTQ3KXPV76LFGSXVFLMLCO2HJBCQ5GP6AQC
GJFYEU3SVP7TDSYXVZEYGKN4NVWSZX4754PPPTOYPRHUO5RMDWPQC
NEWDDAOFCDLYBXQCZNQ2GDH7HPAHVN3YRDL52ZYEMVA4YH6LBDXAC
Z6MDQIGODVE7RXX5U5D64EEC2ZDEZ36Z4ZMPFYIHWBG42IXUDYFQC
WRIU3S5EO3RB3IM5PUDNHLOOMUPD5UKWNUL4YMAKD3C6O4KELJCAC
ND75XNSQ6MWGOAOZGEBMED6QQKLZAU47TEQK3OKRSYOSAUVL6WNAC
HSVVEKTY2U5XJ4ZDHF43YEQXRPRWNZLZDDD4W7JFJ55UWD7CEFBQC
3HZY24CX4U2TO74HOY4YX3LBJIYF4DLXHCIY7J2RASAC4COMSMZAC
UWVMQIAC2HQNSG2JQOPZGUOCQ5V2JFP2F7RCTF3WJLK7NHSD5PAAC
UMFB2767NMAG46NAG3XHMYQMCLF5TMVN46SST4AM2CFUEMNZH3WQC
75XUS62YF7OK4S45RCZ5OOASXEBIEDNDBYEEMOCBDHVXV4GA3NLQC
QTC3SYBMNNTPZHQGXRMV5GU5KDODGXPIM3TIGWBAF7HDKNDKTCXQC
6QRHXIM3XHCDLSIIBEGETDV67V6LTV55QMHC64ZPBMLTAECM5N3QC
ODNCGFQ5FPKFI624BVMLW7PJ2EFJOR3TY66OCZM42UNNTWBCF2TQC
ZRRPBEI4ZSISCMWV62JGB35QNRJVA7SZUR4F52WTQRECCG2GCEAQC
LZO3C2KIACZ3HN72RBGWWIT5ED4RJMYKI3SAHXT6RIUPHDFL3STQC
OOQ2D3KCLFPYNAN253PHWLBQMB6OMO2KYQWQXLTP65SQAYZWQ5LAC
XEKWCIDR3GZECQWYUSMB2UWWP25NBJ34MFZVQVIUGT6KQOW7M6SAC
YFPZ46YK4BOI6VH2H3F757UEGEYONURUAEDAYEIBLRY33PLSSO4AC
JTRG7RDQXKPSO4ESGDLSVAT5WIFGKDL424MN6YYCVTKCOR2FTXRQC
HQGXL4MXGHICQ3IRRQUR6KQHS2RKVOTDKQ5ZETXHQ56DANR2YADQC
QMW24O5S43MYF5ZTBULUEZNDW2FKJ3QP2GL46P6B2SVGSQLVEACAC
CQTN62OHT4DY35E2MJEG7GFTVNEE5KRDMV6ASBQLBHN7BUDK7WHAC
OX6NYJDVCDX2UCJ4QQMTRJVZ535NOMCJGYCHDLCADJPQI7RMCXPQC
VS3OUK7DARCUQWR6NC2MIKHBLOVPXDUGITCNYS5VHYYGHS3OQBNQC
FYO6NECE4YJC76HQIG35NNJABODV7KNQKREK5YKZU6O2MNNSQTMAC
PVIRRARJJC2Z3PYC6XPHJUZPT5IS4IKXUUDGMXXV57PFVVFCUUMAC
6LETVKRAATQS7Z63ED6FD3IMGLYIEDBQ55DAWHTMTGAFLOTC4PMQC
RSEGBU6CIEDRNTL7R6Q7RF2IZGCN3HH6VTPGFBKD77LSHEUD6EKAC
TNMOG2ZQTXJJ6TEPEDB6D4XPIIGMAV3DUDWO33WD23CGDVT4LO2QC
U4TD3AIQXBJFFUORTMIC4IHZTVBORRKL2TZ2FSP4G665ECZOEMNAC
VH5ZABDRP565VZIG55YHNYYPST53NQ2J6YM362NSLXCAHI5WPH4AC
UXXFND4UYVY6WHWPRL3YIRKWTGFM3PR32JZCYKYNQOX7MZAFEG5AC
GNIEG2GCT6BUYHY2WXUAQVKHSYB6TVQT52O5GEY6COLKFK4ODCCQC
OR5SJ42Y6HYDJL3UTEXMINLNY2T3LYSVJ6FSYFMHP4K4W2RZAMIQC
JARRBLZDQ2JZWY7IUVPTOT7WJMBPMLFLF2MGLVGOYROAAISYGLSAC
QTFVCDIFHTF36AGU4UGNCTWSHQYTM2KERXS26TPAJK7IDJHL7BVAC
BHJ62LYETTTVB53IGPMYJ4H2BT26AKZBGDI32DIVHBX7ZMFOXILAC
A6XVP6FE3CO7BVZR23HQS2GFDNF3FK7KBDSOB4YIPRISZHEWQJKAC
2BUX775ILK47LEDXNPSVUAKVLEKG54ODCXRG3474DHY5PHJJBNBQC
KXGOUX7PH4BOXPJRYRGDS4RY7RTXYWUXCRAB5JR3BLVS62PVKOQAC
RBNQKATLSAKTGW2IRNB5CRV3SEH5F6E4BPVWX4BII7MH5TCIPINQC
RJICSUYGE5RVDXYAPCOTS436V67HB222GG6GAWEATJEQAAKDU5SAC
J5UVLXOK6EDIL5I7VKWH4V2QDS4DPD7FHRK6XBWSXFRQS4JKXFZQC
ZWCTAZGLJZQNTYWTC2XQUKMILJF6JGDL5IND6QNYWK4FIGMLRFXAC
S66BOMVUACAUDSGSDWP7ZIXVMZSQHWXOZYVTB7ILUCWZ7DDFAKVAC
JLDUSNUOOQNL63BOPXIWZOWFRQ5X35RWG33PJB3J3KMR6QR7TN7QC
XJRJ4J7M6BC433TBLWHHKX7UYYCFX6M7ZQLUEYYTREPCSM6M3RDQC
JFZNAYJXKCMXYHGCLTRH7Q6TOFGJ4BT6332GONCWVYRLNMDDG3KAC
6FRLEP4PY7HKDWDD7TWQ7HXILOWRMIKXHJRXXXS65Q5CXMQ5CSMQC
EFWN7JBV7YIHNMCA6ZGFRSHZIUQJ2EX57SWYGVT7ZRJCHPIM2R3QC
LSZLZHJYGXZTCNH4JUXU7W23MW5PBVM4OBMWRRVNEDROMIBUVQNAC
SMM4HQTPAY45254O7GQOJVLHP5LZ6BXDKGNE7IKUJHB5XKH244ZAC
MVB7RRLTOLZALMUVUNSCMXDB76QLICJAV2GYQQZO7E6S327LHMSQC
P5X4P6VKS5CJOOLJRVL66GRJLDLVC3EKAVAHP2RJOXQJ7WTYAUBQC
JM3DPYOMVNMCL5GMEYC3Y4NDRGTNIFBBFTPGPVT66GPENVPU7EVQC
PHX2HIVGHHKCAX6VNN2WXD4LRGSA74KQMJCCTMHK7HS6JPELVECAC
SJLEZFC472OWVCR7WEUUYNS6BJDDR77SHWADKDCPIS2INMTPTIVAC
Y6AHH4THYQA43V77L43YM42DYRPCMDSWLUV4NKWAQYMPL4NTUIPQC
7ZSVXUGFXOI3BOJE37SXIT6MDN6AJRBCCJNEICYDMBJF5QOEY6BAC
CLJQCY2XHIDNNMFBJ5PK3GQEN6RFALEFKXBJRWZPEIKR4PR5ZQ5AC
RFE6T5LGBFFNEPHZOPF4UNMFC2L4CGD5TPAMOXDLRPH3TZJ43UBAC
YTIDBFGUR65L6D6OKGQWTKHJJQK3NCC6WDNQFTA7E6EATVOX4Y5AC
L2E6EVE2RVFVDCUNRJ4CZYSQNS2DZUA5DTBETHBDUQUV2KQQRAOQC
DHMTPGSTO5WBG7CCOY6GO6J5FDHL6OGXBFENFTQJ7GXPAANTLIDAC
SB2V735VJ2CDHGCXRUA5FOYHDRXQFVOZ3KXC3YKXWRNW6DIX7RXQC
JJT5QG3KOCWGMOTBN7BJFCUKDCLMB4O6K2PP32KL7WKXKABSZXFAC
QQ4STW3SZRZLTYANG5QNDDL7DIPRW3HWKOEMQC6QJHTMXWJDKB7QC
ZVTSOVHNQNQCRF3N44RKDQSL3UM7HSLTAXICMWEE6EIA6SWJXZCQC
S5PV6IIMKJ7PGWIFLLXERHYF3BCP2UEGFRZEZLD6UUBLVEZXJLUAC
G2T4WAHINIIKTEKR4JBIRXO5IEKO7MPYD7U5PQ4AIX3WL54EPP3AC
5SHCWE7XPQORSOLY7HGAIK2ODKBFPY4KVXRL2W7X6D4WALU544HQC
KPZNJ33UUF6TK5OPB6K5KLM3ZEK7YV3IF32HTLJFURCO6ICDMGYAC
M552HLIAP52D42AVXVC5SGROAYN2TBCEUZOXESWEMBBUX7G3U6TAC
X27GNHDV5KPZ5GSH6DCAJMNCEMZLCP7M43JWF2X3O5QWXMOX273AC
NABL63FIFXIY2VIPFSK3RE4DLUVBVDJ7H2V6QA7LVY3RRQ3UWVGAC
MOX7XJ2E3XISXA7V7T4W6GEAGECGWBZ4PYSLTYBVVR4VAKOI33CQC
FD76WVTQR5QM6ZP7GH2R6YSBRI3LFMB54YSGHURE4RAICAKFWGOAC
RU7AQO7U4HCWJNQTR2KRGDLLG24WYD47MWIHREV6SIAPCPDQHAWQC
FHF6IZJQPUQHY5QWQYRPZVDBRLHREWRHGNKVQDT7F3GQKKLZXJKQC
FV2M6MOTAP4BJMEKU5XUDVEACWEJGEIRCCE2MRY3F6SF2SFOE3MQC
TP3PFR5KVAORTBLZI6EMZYZSQJI3BUEH3YRVTQJ5EM4M6VCSH5EAC
7Z3YOKCVJE242IDO4HQVOBBLHFOXXCQIBIKDIUXQLTU5LY5QAORQC
PZL3SZM3U3BYJX2RGYXC6NMBG7WQHFWHSYDYXZ7Q5VZA3EDYVPIQC
OEPUOUNBNTHTFZVDXREGBQCKFRCWMVP2MDVK4OA47VK2DBKEWVYAC
QL55ECJ6KMMBUOWQ6LKSOVN7L43CH4S6SPE2AQ3VX3KSGC32RP4AC
JZVRK5QJGPDT7HNQLC2UOJRG6V6QO4NJJ5V5QEMQ6VUTUIQWHG5AC
4JS4DWHDRVNPIV3QCHLVJP6ZEAO5FHOK7NQ5FGO2JVZE6BZDZAIAC
NRSKJPP4FYTEKWNLVRK2QZDL6P24VJAC6R6NUBVX77JSUEBVRISAC
KN3VYE5P2RJB3KZ355LA5C2T2D5S2IR3QZFE53AJIWUVMETEEYDAC
G2ZB6464XGPBIMSZIPSB24EIXSCCGV4XWC3IWPS2CXYPDSUZSU5QC
tests.api = genAttrs' (system:
with import <nixos/lib/testing.nix> { inherit system; };
let hydra = builtins.getAttr system build; in # build.${system}
simpleTest {
machine =
{ config, pkgs, ... }:
{ services.postgresql.enable = true;
services.postgresql.package = pkgs.postgresql92;
environment.systemPackages = [ hydra pkgs.perlPackages.LWP pkgs.perlPackages.JSON ];
virtualisation.memorySize = 2048;
};
testScript =
''
$machine->waitForJob("postgresql");
# Initialise the database and the state.
$machine->mustSucceed
( "createdb -O root hydra"
, "psql hydra -f ${hydra}/libexec/hydra/sql/hydra-postgresql.sql"
, "mkdir /var/lib/hydra"
, "echo \"insert into Users(userName, emailAddress, password) values('root', 'e.dolstra\@tudelft.nl', '\$(echo -n foobar | sha1sum | cut -c1-40)');\" | psql hydra"
, "echo \"insert into UserRoles(userName, role) values('root', 'admin');\" | psql hydra"
, "mkdir /run/jobset"
, "chmod 755 /run/jobset"
, "cp ${./tests/api-test.nix} /run/jobset/default.nix"
, "chmod 644 /run/jobset/default.nix"
);
# Start the web interface.
$machine->mustSucceed("NIX_STORE_DIR=/run/nix NIX_LOG_DIR=/run/nix/var/log/nix NIX_STATE_DIR=/run/nix/var/nix HYDRA_DATA=/var/lib/hydra HYDRA_DBI='dbi:Pg:dbname=hydra;user=root;' LOGNAME=root DBIC_TRACE=1 hydra-server -d >&2 &");
$machine->waitForOpenPort("3000");
$machine->mustSucceed("perl ${./tests/api-test.pl} >&2");
'';
});
package Hydra::Base::Controller::REST;
use strict;
use warnings;
use base 'Catalyst::Controller::REST';
__PACKAGE__->config(
map => {
'text/html' => [ 'View', 'TT' ]
},
default => 'text/html',
'stash_key' => 'resource',
);
sub begin { my ( $self, $c ) = @_; $c->forward('Hydra::Controller::Root::begin'); }
sub end { my ( $self, $c ) = @_; $c->forward('Hydra::Controller::Root::end'); }
1;
use utf8;
package Hydra::Component::ToJSON;
use strict;
use warnings;
use base 'DBIx::Class';
sub TO_JSON {
my $self = shift;
my $json = { $self->get_columns };
my $rs = $self->result_source;
my @relnames = $rs->relationships;
RELLOOP: foreach my $relname (@relnames) {
my $relinfo = $rs->relationship_info($relname);
next unless defined $relinfo->{attrs}->{accessor};
my $accessor = $relinfo->{attrs}->{accessor};
if ($accessor eq "single" and exists $self->{_relationship_data}{$relname}) {
$json->{$relname} = $self->$relname->TO_JSON;
} else {
unless (defined $self->{related_resultsets}{$relname}) {
my $cond = $relinfo->{cond};
if (ref $cond eq 'HASH') {
foreach my $k (keys %{$cond}) {
my $v = $cond->{$k};
$v =~ s/^self\.//;
next RELLOOP unless $self->has_column_loaded($v);
}
} #!!! TODO: Handle ARRAY conditions
}
if (defined $self->related_resultset($relname)->get_cache) {
if ($accessor eq "multi") {
$json->{$relname} = [ map { $_->TO_JSON } $self->$relname ];
} else {
$json->{$relname} = $self->$relname->TO_JSON;
}
}
}
}
return $json;
}
1;
$c->stash->{jobset_} = $project->jobsets->search({name => $jobsetName});
$c->stash->{jobset} = $c->stash->{jobset_}->single
or notFound($c, "Jobset $jobsetName doesn't exist.");
if ($jobset) {
$c->stash->{jobset} = $jobset;
} else {
if ($c->action->name eq "jobset" and $c->request->method eq "PUT") {
$c->stash->{jobsetName} = $jobsetName;
} else {
$self->status_not_found(
$c,
message => "Jobset $jobsetName doesn't exist."
);
$c->detach;
}
}
} else {
$self->status_not_found(
$c,
message => "Project $projectName doesn't exist."
);
$c->detach;
}
$self->status_ok(
$c,
entity => $c->stash->{jobset_}->find({}, {
columns => [
'me.name',
'me.project',
'me.errormsg',
'jobsetinputs.name',
{
'jobsetinputs.jobsetinputalts.altnr' => 'jobsetinputalts.altnr',
'jobsetinputs.jobsetinputalts.value' => 'jobsetinputalts.value'
}
],
join => { 'jobsetinputs' => 'jobsetinputalts' },
collapse => 1,
order_by => "me.name"
})
);
sub jobs_tab : Chained('jobset') PathPart('jobs-tab') Args(0) {
requireProjectOwner($c, $c->stash->{project});
if (defined $c->stash->{jobset}) {
error($c, "Cannot rename jobset `$c->stash->{params}->{oldName}' over existing jobset `$c->stash->{jobset}->name") if defined $c->stash->{params}->{oldName} and $c->stash->{params}->{oldName} ne $c->stash->{jobset}->name;
txn_do($c->model('DB')->schema, sub {
updateJobset($c, $c->stash->{jobset});
});
if ($c->req->looks_like_browser) {
$c->res->redirect($c->uri_for($self->action_for("jobset"),
[$c->stash->{project}->name, $c->stash->{jobset}->name]) . "#tabs-configuration");
} else {
$self->status_no_content($c);
}
} elsif (defined $c->stash->{params}->{oldName}) {
my $jobset = $c->stash->{project}->jobsets->find({'me.name' => $c->stash->{params}->{oldName}});
if (defined $jobset) {
txn_do($c->model('DB')->schema, sub {
updateJobset($c, $jobset);
});
my $uri = $c->uri_for($self->action_for("jobset"), [$c->stash->{project}->name, $jobset->name]);
if ($c->req->looks_like_browser) {
$c->res->redirect($uri . "#tabs-configuration");
} else {
$self->status_created(
$c,
location => "$uri",
entity => { name => $jobset->name, uri => "$uri", type => "jobset" }
);
}
} else {
$self->status_not_found(
$c,
message => "Jobset $c->stash->{params}->{oldName} doesn't exist."
);
}
} else {
my $exprType =
$c->stash->{params}->{"nixexprpath"} =~ /.scm$/ ? "guile" : "nix";
error($c, "Invalid jobset name: ‘$c->stash->{jobsetName}’") if $c->stash->{jobsetName} !~ /^$jobsetNameRE$/;
my $jobset;
txn_do($c->model('DB')->schema, sub {
# Note: $jobsetName is validated in updateProject, which will
# abort the transaction if the name isn't valid.
$jobset = $c->stash->{project}->jobsets->create(
{name => $c->stash->{jobsetName}, nixexprinput => "", nixexprpath => "", emailoverride => ""});
updateJobset($c, $jobset);
});
my $uri = $c->uri_for($self->action_for("jobset"), [$c->stash->{project}->name, $jobset->name]);
if ($c->req->looks_like_browser) {
$c->res->redirect($uri . "#tabs-configuration");
} else {
$self->status_created(
$c,
location => "$uri",
entity => { name => $jobset->name, uri => "$uri", type => "jobset" }
);
}
}
}
sub jobs_tab : Chained('jobsetChain') PathPart('jobs-tab') Args(0) {
return $c->res->redirect($c->uri_for($c->controller('Project')->action_for("view"), [$c->stash->{project}->name]));
return $c->res->redirect($c->uri_for($c->controller('Project')->action_for("project"), [$c->stash->{project}->name]));
txn_do($c->model('DB')->schema, sub {
updateJobset($c, $c->stash->{jobset});
});
$c->res->redirect($c->uri_for($self->action_for("index"),
[$c->stash->{project}->name, $c->stash->{jobset}->name]) . "#tabs-configuration");
my $newName = trim $c->stash->{params}->{name};
my $oldName = trim $c->stash->{jobset}->name;
unless ($oldName eq $newName) {
$c->stash->{params}->{oldName} = $oldName;
$c->stash->{jobsetName} = $newName;
undef $c->stash->{jobset};
}
jobset_PUT($self, $c);
}
sub checkInput {
my ($c, $baseName) = @_;
my $inputName = trim $c->request->params->{"input-$baseName-name"};
error($c, "Invalid input name: $inputName") unless $inputName =~ /^[[:alpha:]]\w*$/;
my $inputType = trim $c->request->params->{"input-$baseName-type"};
error($c, "Invalid input type: $inputType") unless
$inputType eq "svn" || $inputType eq "svn-checkout" || $inputType eq "hg" || $inputType eq "tarball" ||
$inputType eq "string" || $inputType eq "path" || $inputType eq "boolean" || $inputType eq "bzr" || $inputType eq "bzr-checkout" ||
$inputType eq "git" || $inputType eq "build" || $inputType eq "sysbuild" ;
return ($inputName, $inputType);
, enabled => defined $c->request->params->{enabled} ? 1 : 0
, enableemail => defined $c->request->params->{enableemail} ? 1 : 0
, emailoverride => trim($c->request->params->{emailoverride}) || ""
, hidden => defined $c->request->params->{visible} ? 0 : 1
, keepnr => int(trim($c->request->params->{keepnr})) || 3
, checkinterval => int(trim($c->request->params->{checkinterval}))
, enabled => defined $c->stash->{params}->{enabled} ? 1 : 0
, enableemail => defined $c->stash->{params}->{enableemail} ? 1 : 0
, emailoverride => trim($c->stash->{params}->{emailoverride}) || ""
, hidden => defined $c->stash->{params}->{visible} ? 0 : 1
, keepnr => int(trim($c->stash->{params}->{keepnr})) || 3
, checkinterval => int(trim($c->stash->{params}->{checkinterval}))
foreach my $param (keys %{$c->request->params}) {
next unless $param =~ /^input-(\w+)-name$/;
my $baseName = $1;
next if $baseName eq "template";
unless (defined $c->stash->{params}->{inputs}) {
$c->stash->{params}->{inputs} = {};
foreach my $param (keys %{$c->stash->{params}}) {
next unless $param =~ /^input-(\w+)-name$/;
my $baseName = $1;
next if $baseName eq "template";
$c->stash->{params}->{inputs}->{$c->stash->{params}->{$param}} = { type => $c->stash->{params}->{"input-$baseName-type"}, values => $c->stash->{params}->{"input-$baseName-values"} };
unless ($baseName =~ /^\d+$/) { # non-numeric base name is an existing entry
$c->stash->{params}->{inputs}->{$c->stash->{params}->{$param}}->{oldName} = $baseName;
}
}
}
my ($inputName, $inputType) = checkInput($c, $baseName);
foreach my $inputName (keys %{$c->stash->{params}->{inputs}}) {
my $inputData = $c->stash->{params}->{inputs}->{$inputName};
error($c, "Invalid input name: $inputName") unless $inputName =~ /^[[:alpha:]]\w*$/;
$inputNames{$inputName} = 1;
my $inputType = $inputData->{type};
error($c, "Invalid input type: $inputType") unless
$inputType eq "svn" || $inputType eq "svn-checkout" || $inputType eq "hg" || $inputType eq "tarball" ||
$inputType eq "string" || $inputType eq "path" || $inputType eq "boolean" || $inputType eq "bzr" || $inputType eq "bzr-checkout" ||
$inputType eq "git" || $inputType eq "build" || $inputType eq "sysbuild" ;
if ($baseName =~ /^\d+$/) { # numeric base name is auto-generated, i.e. a new entry
$input = $jobset->jobsetinputs->create(
unless (defined $inputData->{oldName}) {
$input = $jobset->jobsetinputs->update_or_create(
$c->stash->{evals} = getEvals($self, $c, $evals, ($page - 1) * $resultsPerPage, $resultsPerPage)
my $offset = ($page - 1) * $resultsPerPage;
$c->stash->{evals} = getEvals($self, $c, $evals, $offset, $resultsPerPage);
my %entity = (
evals => [ $evals->search({ 'me.hasnewbuilds' => 1 }, {
columns => [
'me.hasnewbuilds',
'me.id',
'jobsetevalinputs.name',
'jobsetevalinputs.altnr',
'jobsetevalinputs.revision',
'jobsetevalinputs.type',
'jobsetevalinputs.uri',
'jobsetevalinputs.dependency',
'jobsetevalmembers.build',
],
join => [ 'jobsetevalinputs', 'jobsetevalmembers' ],
collapse => 1,
rows => $resultsPerPage,
offset => $offset,
order_by => "me.id DESC",
}) ],
first => "?page=1",
last => "?page=" . POSIX::ceil($c->stash->{total}/$resultsPerPage)
);
if ($page > 1) {
$entity{previous} = "?page=" . ($page - 1);
}
if ($page < POSIX::ceil($c->stash->{total}/$resultsPerPage)) {
$entity{next} = "?page=" . ($page + 1);
}
$self->status_ok(
$c,
entity => \%entity
);
my $project = $c->model('DB::Projects')->find($projectName)
or notFound($c, "Project $projectName doesn't exist.");
my $project = $c->model('DB::Projects')->find($projectName, { columns => [
"me.name",
"me.displayName",
"me.description",
"me.enabled",
"me.hidden",
"me.homepage",
"owner.username",
"owner.fullname",
"views.name",
"releases.name",
"releases.timestamp",
"jobsets.name",
], join => [ 'owner', 'views', 'releases', 'jobsets' ], order_by => { -desc => "releases.timestamp" }, collapse => 1 });
$c->stash->{project} = $project;
if ($project) {
$c->stash->{project} = $project;
} else {
if ($c->action->name eq "project" and $c->request->method eq "PUT") {
$c->stash->{projectName} = $projectName;
} else {
$self->status_not_found(
$c,
message => "Project $projectName doesn't exist."
);
$c->detach;
}
}
sub edit : Chained('project') PathPart Args(0) {
if (defined $c->stash->{project}) {
error($c, "Cannot rename project `$c->stash->{params}->{oldName}' over existing project `$c->stash->{project}->name") if defined $c->stash->{params}->{oldName};
requireProjectOwner($c, $c->stash->{project});
txn_do($c->model('DB')->schema, sub {
updateProject($c, $c->stash->{project});
});
if ($c->req->looks_like_browser) {
$c->res->redirect($c->uri_for($self->action_for("project"), [$c->stash->{project}->name]) . "#tabs-configuration");
} else {
$self->status_no_content($c);
}
} elsif (defined $c->stash->{params}->{oldName}) {
my $project = $c->model('DB::Projects')->find($c->stash->{params}->{oldName});
if (defined $project) {
requireProjectOwner($c, $project);
txn_do($c->model('DB')->schema, sub {
updateProject($c, $project);
});
my $uri = $c->uri_for($self->action_for("project"), [$project->name]);
if ($c->req->looks_like_browser) {
$c->res->redirect($uri . "#tabs-configuration");
} else {
$self->status_created(
$c,
location => "$uri",
entity => { name => $project->name, uri => "$uri", type => "project" }
);
}
} else {
$self->status_not_found(
$c,
message => "Project $c->stash->{params}->{oldName} doesn't exist."
);
}
} else {
requireMayCreateProjects($c);
error($c, "Invalid project name: ‘$c->stash->{projectName}’") if $c->stash->{projectName} !~ /^$projectNameRE$/;
my $project;
txn_do($c->model('DB')->schema, sub {
# Note: $projectName is validated in updateProject,
# which will abort the transaction if the name isn't
# valid. Idem for the owner.
my $owner = $c->user->username;
$project = $c->model('DB::Projects')->create(
{name => $c->stash->{projectName}, displayname => "", owner => $owner});
updateProject($c, $project);
});
my $uri = $c->uri_for($self->action_for("project"), [$project->name]);
if ($c->req->looks_like_browser) {
$c->res->redirect($uri . "#tabs-configuration");
} else {
$self->status_created(
$c,
location => "$uri",
entity => { name => $project->name, uri => "$uri", type => "project" }
);
}
}
}
sub edit : Chained('projectChain') PathPart Args(0) {
txn_do($c->model('DB')->schema, sub {
updateProject($c, $c->stash->{project});
});
$c->res->redirect($c->uri_for($self->action_for("view"), [$c->stash->{project}->name]) . "#tabs-configuration");
my $newName = trim $c->stash->{params}->{name};
my $oldName = trim $c->stash->{project}->name;
unless ($oldName eq $newName) {
$c->stash->{params}->{oldName} = $oldName;
$c->stash->{projectName} = $newName;
undef $c->stash->{project};
}
project_PUT($self, $c);
requireMayCreateProjects($c);
my $projectName = trim $c->request->params->{name};
error($c, "Invalid project name: ‘$projectName’") if $projectName !~ /^$projectNameRE$/;
txn_do($c->model('DB')->schema, sub {
# Note: $projectName is validated in updateProject,
# which will abort the transaction if the name isn't
# valid. Idem for the owner.
my $owner = $c->check_user_roles('admin')
? trim $c->request->params->{owner} : $c->user->username;
my $project = $c->model('DB::Projects')->create(
{name => $projectName, displayname => "", owner => $owner});
updateProject($c, $project);
});
$c->stash->{projectName} = trim $c->stash->{params}->{name};
my $jobsetName = trim $c->request->params->{name};
my $exprType =
$c->request->params->{"nixexprpath"} =~ /.scm$/ ? "guile" : "nix";
error($c, "Invalid jobset name: ‘$jobsetName’") if $jobsetName !~ /^$jobsetNameRE$/;
txn_do($c->model('DB')->schema, sub {
# Note: $jobsetName is validated in updateProject, which will
# abort the transaction if the name isn't valid.
my $jobset = $c->stash->{project}->jobsets->create(
{name => $jobsetName, nixexprinput => "", nixexprpath => "", emailoverride => ""});
Hydra::Controller::Jobset::updateJobset($c, $jobset);
});
$c->res->redirect($c->uri_for($c->controller('Jobset')->action_for("index"),
[$c->stash->{project}->name, $jobsetName]));
Hydra::Controller::Jobset::jobset_PUT($self, $c);
if ($c->check_user_roles('admin')) {
$owner = trim $c->request->params->{owner};
if ($c->check_user_roles('admin') and defined $c->stash->{params}->{owner}) {
$owner = trim $c->stash->{params}->{owner};
, description => trim($c->request->params->{description})
, homepage => trim($c->request->params->{homepage})
, enabled => defined $c->request->params->{enabled} ? 1 : 0
, hidden => defined $c->request->params->{visible} ? 0 : 1
, description => trim($c->stash->{params}->{description})
, homepage => trim($c->stash->{params}->{homepage})
, enabled => defined $c->stash->{params}->{enabled} ? 1 : 0
, hidden => defined $c->stash->{params}->{visible} ? 0 : 1
$c->stash->{steps} = [ $c->model('DB::BuildSteps')->search(
{ 'me.busy' => 1, 'build.finished' => 0, 'build.busy' => 1 },
{ join => [ 'build' ]
, order_by => [ 'machine' ]
} ) ];
$self->status_ok(
$c,
entity => [ $c->model('DB::BuildSteps')->search(
{ 'me.busy' => 1, 'build.finished' => 0, 'build.busy' => 1 },
{ join => { build => [ 'project', 'job', 'jobset' ] },
columns => [
'me.machine',
'me.system',
'me.stepnr',
'me.drvpath',
'me.starttime',
'build.id',
{
'build.project.name' => 'project.name',
'build.jobset.name' => 'jobset.name',
'build.job.name' => 'job.name'
}
],
order_by => [ 'machine' ]
}
) ]
);
my $username = $c->request->params->{username} || "";
my $password = $c->request->params->{password} || "";
my $baseurl = $c->uri_for('/');
my $referer = $c->request->referer;
$c->session->{referer} = $referer if defined $referer && $referer =~ m/^($baseurl)/;
if ($username eq "" && $password eq "" && !defined $c->session->{referer}) {
my $baseurl = $c->uri_for('/');
my $referer = $c->request->referer;
$c->session->{referer} = $referer if defined $referer && $referer =~ m/^($baseurl)/;
}
$c->stash->{template} = 'login.tt';
}
sub login_POST {
my ($self, $c) = @_;
my $username;
my $password;
$username = $c->stash->{params}->{username};
$password = $c->stash->{params}->{password};
backToReferer($c) if $c->authenticate({username => $username, password => $password});
$c->stash->{errorMsg} = "Bad username or password.";
if ($c->authenticate({username => $username, password => $password})) {
if ($c->request->looks_like_browser) {
backToReferer($c);
} else {
currentUser_GET($self, $c);
}
} else {
$self->status_forbidden($c, message => "Bad username or password.");
if ($c->request->looks_like_browser) {
login_GET($self, $c);
}
}
sub currentUser :Path('/current-user') :ActionClass('REST') { }
sub currentUser_GET {
my ($self, $c) = @_;
requireLogin($c) if !$c->user_exists;
$self->status_ok(
$c,
entity => $c->model('DB::Users')->find({ 'me.username' => $c->user->username}, {
columns => [ "me.fullname", "me.emailaddress", "me.username", "userroles.role" ]
, join => [ "userroles" ]
, collapse => 1
})
);
}
if (($c->request->params->{submit} // "") eq "delete") {
$c->stash->{emailonerror} = $user->emailonerror;
}
sub edit_POST {
my ($self, $c) = @_;
my $user = $c->stash->{user};
$c->stash->{template} = 'user.tt';
$c->session->{referer} = $c->request->referer if !defined $c->session->{referer};
if (($c->stash->{params}->{submit} // "") eq "delete") {
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:byU/SLN03zNJlSFbi/3Bcg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:tKZAybbNaRIMs9n5tHkqPw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-30 16:22:11
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:UpVoKdd3OwMvlvyMjcYNVA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:V8MbzKvZNEaeHBJV67+ZMQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:KHwh/Np40jxKXc3ijMImEQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:+0LkZiaRL5tGJvbLxnwD/g
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-30 16:22:11
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:dC1yX7arRVu9K3wG9dAjCg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:A/4v3ugXYbuYoKPlOvC6mg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-30 16:36:03
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ZiA1nv73Fpp0/DTi4sLfEQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:OZsXJniZ/7EB2iSz7p5y4A
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-05-03 14:35:11
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:aYVEk+AeDsgTRi5GAqOhEw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:isCEXACY/PwkvgKHcXvAIg
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ONhBo6Xhq7uwYFdEzbp3dg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:zvun8uhxwrr7B8EsqBoCjA
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:IcSVN/tlfQQtX88Ix+aKnw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Vi1qzjW52Lnsl0JSmGzy0w
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:fx3yosWMmJ+MnvL/dSWtFA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:I4hI02FKRMkw76WV/KBocA
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:xFLnuCBAcJCg+N3b4aajZQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:qS/eiiZXmpc7KpTHdtaT7g
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:4KzXhMnUldVgNuuNXWIYjw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:28rja0vR1glJJ15hzVfjsQ
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1rjwWtZXGEowHqhfjLqjmA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:3qXfnvkOVj25W94bfhQ65w
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-05-23 16:09:46
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:JgxEaCz/TW9YKa+HavRzXw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:t2CCfUjFEz/lO4szROz1AQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ng+Q6tMX5EJMD7DxRWVy7Q
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1Dp8B58leBLh4GK0GPw2zg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:EVwSR9WBqbBdIHq1ANQMHg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ccPNQe/QnSjTAC3uGWe8Ng
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:qElGj6zzuI0xo426np3r1w
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:SlEiF8oN6FBK262uSiMKiw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:M3pNBRLfxgSScrPj1zaajA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:UUO37lIuEYm0GiR92m/fyA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:xjioYUPo6visoLAVDkDZ0Q
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:UXBzqO0vHPql4LYyXpgEQg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-05-02 14:50:55
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:q4amPCWRoWMThnRa/n/y1w
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:tsGR8MhZRIUeNwpcVczMUw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:lnA5Utkwk5WTyKA/M5mlyg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:3CRNsvd+YnZp9c80tuZREQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:zW87n6E7xWaShcFbgFkVuw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:imPoiaitrTbX0vVNlF6dPA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:OCuhmxs8pZxvmk81eVLLcQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:RffghAo9jAaqYk41y1Sdqw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:eP00w5UJp1uTtiB7D5IhTQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:7M7WPlGQT6rNHKJ+82/KSA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:UTUE3Hb89fT7prwnwwBgvQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:qISBiwvboB8dIdinaE45mg
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2012-02-29 00:47:18
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:LFD28W0GvvrOOylCM98SEQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:08/7gbEQp1TqBiWFJXVY0w
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:zg8db3Cbi0QOv+gLJqH8cQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:8cC34cEw9T3+x+7uRs4KHQ
# Created by DBIx::Class::Schema::Loader v0.07014 @ 2011-12-05 14:15:43
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:hzKzGAgAiCfU0nBOiDnjWw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:G2GAF/Rb7cRkRegH94LwIA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:KArPHyemtnm/siwE4x5mGQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:aS+ivlFpndqIv8U578zz9A
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:OAUFl/teGpfeleb6D8FPlw
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:hy3MKvFxfL+1bTc7Hcb1zA
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:cbSUw113ENPypbd/sICfgg
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:hz912vBfYw0rHslBPqJW2w
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-01-22 13:29:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Vyd2+0RAF3XGTpq3KswfAQ
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:U23GZ3k5KZk2go6j2LYLHA
[% INCLUDE menuItem uri = c.uri_for(c.controller('Project').action_for('view'), [project.name]) title = "Overview" %]
[% INCLUDE menuItem uri = c.uri_for(c.controller('Project').action_for('project'), [project.name]) title = "Overview" %]
perl -MDBIx::Class::Schema::Loader=make_schema_at,dump_to_dir:../lib -e 'make_schema_at("Hydra::Schema", { naming => { ALL => "v5" }, relationships => 1, moniker_map => sub {return "$$_";} }, ["dbi:SQLite:tmp.sqlite"])'
perl -I ../lib -MDBIx::Class::Schema::Loader=make_schema_at,dump_to_dir:../lib -e 'make_schema_at("Hydra::Schema", { naming => { ALL => "v5" }, relationships => 1, moniker_map => sub {return "$$_";}, components => [ "+Hydra::Component::ToJSON" ], }, ["dbi:SQLite:tmp.sqlite"])'
let
builder = builtins.toFile "builder.sh" ''
echo -n ${builtins.readFile ./default.nix} > $out
'';
in {
job = derivation {
name = "job";
system = builtins.currentSystem;
builder = "/bin/sh";
args = [ builder ];
};
}
use LWP::UserAgent;
use JSON;
use Test::Simple tests => 15;
my $ua = LWP::UserAgent->new;
$ua->cookie_jar({});
sub request_json {
my ($opts) = @_;
my $req = HTTP::Request->new;
$req->method($opts->{method} or "GET");
$req->uri("http://localhost:3000$opts->{uri}");
$req->header(Accept => "application/json");
$req->content(encode_json($opts->{data})) if defined $opts->{data};
my $res = $ua->request($req);
print $res->as_string();
return $res;
}
my $result = request_json({ uri => "/login", method => "POST", data => { username => "root", password => "foobar" } });
my $user = decode_json($result->content());
ok($user->{username} eq "root", "The root user is named root");
ok($user->{userroles}->[0]->{role} eq "admin", "The root user is an admin");
$user = decode_json(request_json({ uri => "/current-user" })->content());
ok($user->{username} eq "root", "The current user is named root");
ok($user->{userroles}->[0]->{role} eq "admin", "The current user is an admin");
ok(request_json({ uri => '/project/sample' })->code() == 404, "Non-existent projects don't exist");
$result = request_json({ uri => '/project/sample', method => 'PUT', data => { displayname => "Sample", enabled => "1", } });
ok($result->code() == 201, "PUTting a new project creates it");
my $project = decode_json(request_json({ uri => '/project/sample' })->content());
ok((not @{$project->{jobsets}}), "A new project has no jobsets");
$result = request_json({ uri => '/jobset/sample/default', method => 'PUT', data => { nixexprpath => "default.nix", nixexprinput => "src", inputs => { src => { type => "path", values => "/run/jobset" } }, enabled => "1", checkinterval => "3600"} });
ok($result->code() == 201, "PUTting a new jobset creates it");
my $jobset = decode_json(request_json({ uri => '/jobset/sample/default' })->content());
ok($jobset->{jobsetinputs}->[0]->{name} eq "src", "The new jobset has an 'src' input");
ok($jobset->{jobsetinputs}->[0]->{jobsetinputalts}->[0]->{value} eq "/run/jobset", "The 'src' input is in /run/jobset");
system("LOGNAME=root NIX_STORE_DIR=/run/nix/store NIX_LOG_DIR=/run/nix/var/log/nix NIX_STATE_DIR=/run/nix/var/nix HYDRA_DATA=/var/lib/hydra HYDRA_DBI='dbi:Pg:dbname=hydra;user=root;' hydra-evaluator sample default");
$result = request_json({ uri => '/jobset/sample/default/evals' });
ok($result->code() == 200, "Can get evals of a jobset");
my $evals = decode_json($result->content())->{evals};
my $eval = $evals->[0];
ok($eval->{hasnewbuilds} == 1, "The first eval of a jobset has new builds");
# Ugh, cached for 30s
sleep 30;
system("echo >> /run/jobset/default.nix; LOGNAME=root NIX_STORE_DIR=/run/nix/store NIX_LOG_DIR=/run/nix/var/log/nix NIX_STATE_DIR=/run/nix/var/nix HYDRA_DATA=/var/lib/hydra HYDRA_DBI='dbi:Pg:dbname=hydra;user=root;' hydra-evaluator sample default");
my $evals = decode_json(request_json({ uri => '/jobset/sample/default/evals' })->content())->{evals};
ok($evals->[0]->{jobsetevalinputs}->[0]->{revision} != $evals->[1]->{jobsetevalinputs}->[0]->{revision}, "Changing a jobset source changes its revision");
my $build = decode_json(request_json({ uri => "/build/" . $evals->[0]->{jobsetevalmembers}->[0]->{build} })->content());
ok($build->{job} eq "job", "The build's job name is job");
ok($build->{finished} == 0, "The build isn't finished yet");