Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable declarative projects. #277

Merged
merged 2 commits into from
May 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/lib/Hydra/Controller/Jobset.pm
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ sub jobset_PUT {

requireProjectOwner($c, $c->stash->{project});

if (length($c->stash->{project}->declfile)) {
error($c, "can't modify jobset of declarative project", 403);
}

if (defined $c->stash->{jobset}) {
txn_do($c->model('DB')->schema, sub {
updateJobset($c, $c->stash->{jobset});
Expand Down Expand Up @@ -88,6 +92,10 @@ sub jobset_DELETE {

requireProjectOwner($c, $c->stash->{project});

if (length($c->stash->{project}->declfile)) {
error($c, "can't modify jobset of declarative project", 403);
}

txn_do($c->model('DB')->schema, sub {
$c->stash->{jobset}->jobsetevals->delete;
$c->stash->{jobset}->builds->delete;
Expand Down
12 changes: 12 additions & 0 deletions src/lib/Hydra/Controller/Project.pm
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,19 @@ sub updateProject {
, enabled => defined $c->stash->{params}->{enabled} ? 1 : 0
, hidden => defined $c->stash->{params}->{visible} ? 0 : 1
, owner => $owner
, declfile => trim($c->stash->{params}->{declfile})
, decltype => trim($c->stash->{params}->{decltype})
, declvalue => trim($c->stash->{params}->{declvalue})
});
if (length($project->declfile)) {
$project->jobsets->update_or_create(
{ name=> ".jobsets"
, nixexprinput => ""
, nixexprpath => ""
, emailoverride => ""
, triggertime => time
});
}
}


Expand Down
65 changes: 64 additions & 1 deletion src/lib/Hydra/Helper/AddBuilds.pm
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ use Hydra::Helper::CatalystUtils;
our @ISA = qw(Exporter);
our @EXPORT = qw(
fetchInput evalJobs checkBuild inputsToArgs
restartBuild getPrevJobsetEval
restartBuild getPrevJobsetEval updateDeclarativeJobset
handleDeclarativeJobsetBuild
);


Expand Down Expand Up @@ -467,4 +468,66 @@ sub checkBuild {
};


sub updateDeclarativeJobset {
my ($db, $project, $jobsetName, $declSpec) = @_;

my @allowed_keys = qw(
enabled
hidden
description
nixexprinput
nixexprpath
checkinterval
schedulingshares
enableemail
emailoverride
keepnr
);
my %update = ( name => $jobsetName );
foreach my $key (@allowed_keys) {
$update{$key} = $declSpec->{$key};
delete $declSpec->{$key};
}
txn_do($db, sub {
my $jobset = $project->jobsets->update_or_create(\%update);
$jobset->jobsetinputs->delete;
while ((my $name, my $data) = each %{$declSpec->{"inputs"}}) {
my $input = $jobset->jobsetinputs->create(
{ name => $name,
type => $data->{type},
emailresponsible => $data->{emailresponsible}
});
$input->jobsetinputalts->create({altnr => 0, value => $data->{value}});
}
delete $declSpec->{"inputs"};
die "invalid keys in declarative specification file\n" if (%{$declSpec});
});
};


sub handleDeclarativeJobsetBuild {
my ($db, $project, $build) = @_;

eval {
my $id = $build->id;
die "Declarative jobset build $id failed" unless $build->buildstatus == 0;
my $declPath = ($build->buildoutputs)[0]->path;
my $declText = read_file($declPath)
or die "Couldn't read declarative specification file $declPath: $!";
my $declSpec = decode_json($declText);
txn_do($db, sub {
my @kept = keys %$declSpec;
push @kept, ".jobsets";
$project->jobsets->search({ name => { "not in" => \@kept } })->update({ enabled => 0, hidden => 1 });
while ((my $jobsetName, my $spec) = each %$declSpec) {
updateDeclarativeJobset($db, $project, $jobsetName, $spec);
}
});
};
$project->jobsets->find({ name => ".jobsets" })->update({ errormsg => $@, errortime => time, fetcherrormsg => undef })
if defined $@;

};


1;
2 changes: 1 addition & 1 deletion src/lib/Hydra/Helper/CatalystUtils.pm
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ Readonly our $inputNameRE => "(?:[A-Za-z_][A-Za-z0-9-_]*)";

sub parseJobsetName {
my ($s) = @_;
$s =~ /^($projectNameRE):($jobsetNameRE)$/ or die "invalid jobset specifier ‘$s’\n";
$s =~ /^($projectNameRE):(\.?$jobsetNameRE)$/ or die "invalid jobset specifier ‘$s’\n";
return ($1, $2);
}

Expand Down
25 changes: 23 additions & 2 deletions src/lib/Hydra/Schema/Projects.pm
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,21 @@ __PACKAGE__->table("Projects");
data_type: 'text'
is_nullable: 1

=head2 declfile

data_type: 'text'
is_nullable: 1

=head2 decltype

data_type: 'text'
is_nullable: 1

=head2 declvalue

data_type: 'text'
is_nullable: 1

=cut

__PACKAGE__->add_columns(
Expand All @@ -90,6 +105,12 @@ __PACKAGE__->add_columns(
{ data_type => "text", is_foreign_key => 1, is_nullable => 0 },
"homepage",
{ data_type => "text", is_nullable => 1 },
"declfile",
{ data_type => "text", is_nullable => 1 },
"decltype",
{ data_type => "text", is_nullable => 1 },
"declvalue",
{ data_type => "text", is_nullable => 1 },
);

=head1 PRIMARY KEY
Expand Down Expand Up @@ -282,8 +303,8 @@ Composing rels: L</projectmembers> -> username
__PACKAGE__->many_to_many("usernames", "projectmembers", "username");


# Created by DBIx::Class::Schema::Loader v0.07043 @ 2015-07-30 16:52:20
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:67kWIE0IGmEJTvOIATAKaw
# Created by DBIx::Class::Schema::Loader v0.07043 @ 2016-03-11 10:39:17
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1ats3brIVhRTWLToIYSoaQ

my %hint = (
columns => [
Expand Down
19 changes: 19 additions & 0 deletions src/root/edit-project.tt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,25 @@
</div>
</div>

<div class="control-group">
<label class="control-label">Declarative spec file</label>
<div class="controls">
<div class="input-append">
<input type="text" class="span3" name="declfile" [% HTML.attributes(value => project.declfile) %]/>
</div>
<span class="help-inline">(Leave blank for non-declarative project configuration)</span>
</div>
</div>

<div class="control-group">
<label class="control-label">Declarative input type</label>
<div class="controls">
[% INCLUDE renderSelection param="decltype" options=inputTypes edit=1 curValue=project.decltype %]
value
<input style="width: 70%" type="text" [% HTML.attributes(value => project.declvalue, name => "declvalue") %]/>
</div>
</div>

<div class="form-actions">
<button id="submit-project" type="submit" class="btn btn-primary">
<i class="icon-ok icon-white"></i>
Expand Down
2 changes: 2 additions & 0 deletions src/root/jobset.tt
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
[% UNLESS project.declfile %]
[% INCLUDE menuItem title="Edit configuration" icon="icon-edit" uri=c.uri_for(c.controller('Jobset').action_for('edit'), c.req.captures) %]
[% INCLUDE menuItem title="Delete this jobset" icon="icon-trash" uri="javascript:deleteJobset()" %]
[% INCLUDE menuItem title="Clone this jobset" uri=c.uri_for(c.controller('Jobset').action_for('edit'), c.req.captures, { cloneJobset => 1 }) %]
[% END %]
[% INCLUDE menuItem title="Evaluate this jobset" uri="javascript:confirmEvaluateJobset()" %]
</ul>
</li>
Expand Down
2 changes: 2 additions & 0 deletions src/root/project.tt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
<ul class="dropdown-menu">
[% INCLUDE menuItem title="Edit configuration" icon="icon-edit" uri=c.uri_for(c.controller('Project').action_for('edit'), c.req.captures) %]
[% INCLUDE menuItem title="Delete this project" icon="icon-trash" uri="javascript:deleteProject()" %]
[% UNLESS project.declfile %]
[% INCLUDE menuItem title="Create jobset" icon="icon-plus" uri=c.uri_for(c.controller('Project').action_for('create_jobset'), c.req.captures) %]
[% END %]
[% INCLUDE menuItem title="Create release" icon="icon-plus" uri=c.uri_for(c.controller('Project').action_for('create_release'), c.req.captures) %]
</ul>
</li>
Expand Down
25 changes: 25 additions & 0 deletions src/script/hydra-evaluator
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use Data::Dump qw(dump);
use Try::Tiny;
use Net::Statsd;
use Time::HiRes qw(clock_gettime CLOCK_REALTIME);
use JSON;
use File::Slurp;

STDOUT->autoflush();
STDERR->autoflush(1);
Expand Down Expand Up @@ -100,7 +102,25 @@ sub permute {
sub checkJobsetWrapped {
my ($jobset) = @_;
my $project = $jobset->project;
my $jobsetsJobset = length($project->declfile) && $jobset->name eq ".jobsets";
my $inputInfo = {};
if ($jobsetsJobset) {
my @declInputs = fetchInput($plugins, $db, $project, $jobset, "decl", $project->decltype, $project->declvalue, 0);
my $declInput = @declInputs[0] or die "cannot find the input containing the declarative project specification\n";
die "multiple alternatives for the input containing the declarative project specificaiton are not supported\n"
if scalar @declInputs != 1;
my $declFile = $declInput->{storePath} . "/" . $project->declfile;
my $declText = read_file($declFile)
or die "Couldn't read declarative specification file $declFile: $!\n";
my $declSpec;
eval {
$declSpec = decode_json($declText);
};
die "Declarative specification file $declFile not valid JSON: $@\n" if $@;
updateDeclarativeJobset($db, $project, ".jobsets", $declSpec);
$jobset->discard_changes;
$inputInfo->{"declInput"} = [ $declInput ];
}
my $exprType = $jobset->nixexprpath =~ /.scm$/ ? "guile" : "nix";

# Fetch all values for all inputs.
Expand Down Expand Up @@ -143,6 +163,11 @@ sub checkJobsetWrapped {
my ($jobs, $nixExprInput) = evalJobs($inputInfo, $exprType, $jobset->nixexprinput, $jobset->nixexprpath);
my $evalStop = clock_gettime(CLOCK_REALTIME);

if ($jobsetsJobset) {
my @keys = keys %$jobs;
die "The .jobsets jobset must only have a single job named 'jobsets'"
unless (scalar @keys) == 1 && $keys[0] eq "jobsets";
}
Net::Statsd::timing("hydra.evaluator.eval_time", int(($evalStop - $evalStart) * 1000));

if ($dryRun) {
Expand Down
6 changes: 6 additions & 0 deletions src/script/hydra-notify
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use utf8;
use Hydra::Plugin;
use Hydra::Helper::Nix;
use Hydra::Helper::PluginHooks;
use Hydra::Helper::AddBuilds;

STDERR->autoflush(1);
binmode STDERR, ":encoding(utf8)";
Expand All @@ -21,6 +22,11 @@ my $buildId = shift @ARGV or die;
my $build = $db->resultset('Builds')->find($buildId)
or die "build $buildId does not exist\n";
if ($cmd eq "build-finished") {
my $project = $build->project;
my $jobset = $build->jobset;
if (length($project->declfile) && $jobset->name eq ".jobsets" && $build->iscurrent) {
handleDeclarativeJobsetBuild($db, $project, $build);
}
my @dependents;
foreach my $id (@ARGV) {
my $dep = $db->resultset('Builds')->find($id)
Expand Down
3 changes: 3 additions & 0 deletions src/sql/hydra.sql
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ create table Projects (
hidden integer not null default 0,
owner text not null,
homepage text, -- URL for the project
declfile text, -- File containing declarative jobset specification
decltype text, -- Type of the input containing declarative jobset specification
declvalue text, -- Value of the input containing declarative jobset specification
foreign key (owner) references Users(userName) on update cascade
);

Expand Down
4 changes: 4 additions & 0 deletions src/sql/upgrade-48.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Add declarative fields to Projects
alter table Projects add column declfile text;
alter table Projects add column decltype text;
alter table Projects add column declvalue text;