Merge remote-tracking branch 'origin/master' into flake

This commit is contained in:
Eelco Dolstra 2019-11-07 18:42:15 +01:00
commit 55b0afa08f
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
7 changed files with 96 additions and 6 deletions

View file

@ -77,6 +77,7 @@
LWP LWP
LWPProtocolHttps LWPProtocolHttps
NetAmazonS3 NetAmazonS3
NetPrometheus
NetStatsd NetStatsd
PadWalker PadWalker
Readonly Readonly

View file

@ -504,7 +504,7 @@ sub restart : Chained('buildChain') PathPart Args(0) {
sub cancel : Chained('buildChain') PathPart Args(0) { sub cancel : Chained('buildChain') PathPart Args(0) {
my ($self, $c) = @_; my ($self, $c) = @_;
my $build = $c->stash->{build}; my $build = $c->stash->{build};
requireProjectOwner($c, $build->project); requireCancelBuildPrivileges($c, $build->project);
my $n = cancelBuilds($c->model('DB')->schema, $c->model('DB::Builds')->search({ id => $build->id })); my $n = cancelBuilds($c->model('DB')->schema, $c->model('DB::Builds')->search({ id => $build->id }));
error($c, "This build cannot be cancelled.") if $n != 1; error($c, "This build cannot be cancelled.") if $n != 1;
$c->flash->{successMsg} = "Build has been cancelled."; $c->flash->{successMsg} = "Build has been cancelled.";
@ -540,7 +540,7 @@ sub bump : Chained('buildChain') PathPart('bump') {
my $build = $c->stash->{build}; my $build = $c->stash->{build};
requireProjectOwner($c, $build->project); # FIXME: require admin? requireBumpPrivileges($c, $build->project);
$c->model('DB')->schema->txn_do(sub { $c->model('DB')->schema->txn_do(sub {
$build->update({globalpriority => time()}); $build->update({globalpriority => time()});

View file

@ -179,7 +179,7 @@ sub create_jobset : Chained('evalChain') PathPart('create-jobset') Args(0) {
sub cancel : Chained('evalChain') PathPart('cancel') Args(0) { sub cancel : Chained('evalChain') PathPart('cancel') Args(0) {
my ($self, $c) = @_; my ($self, $c) = @_;
requireProjectOwner($c, $c->stash->{eval}->project); requireCancelBuildPrivileges($c, $c->stash->{eval}->project);
my $n = cancelBuilds($c->model('DB')->schema, $c->stash->{eval}->builds); my $n = cancelBuilds($c->model('DB')->schema, $c->stash->{eval}->builds);
$c->flash->{successMsg} = "$n builds have been cancelled."; $c->flash->{successMsg} = "$n builds have been cancelled.";
$c->res->redirect($c->uri_for($c->controller('JobsetEval')->action_for('view'), $c->req->captures)); $c->res->redirect($c->uri_for($c->controller('JobsetEval')->action_for('view'), $c->req->captures));
@ -210,7 +210,7 @@ sub restart_failed : Chained('evalChain') PathPart('restart-failed') Args(0) {
sub bump : Chained('evalChain') PathPart('bump') Args(0) { sub bump : Chained('evalChain') PathPart('bump') Args(0) {
my ($self, $c) = @_; my ($self, $c) = @_;
requireProjectOwner($c, $c->stash->{eval}->project); # FIXME: require admin? requireBumpPrivileges($c, $c->stash->{eval}->project); # FIXME: require admin?
my $builds = $c->stash->{eval}->builds->search({ finished => 0 }); my $builds = $c->stash->{eval}->builds->search({ finished => 0 });
my $n = $builds->count(); my $n = $builds->count();
$c->model('DB')->schema->txn_do(sub { $c->model('DB')->schema->txn_do(sub {

View file

@ -6,6 +6,7 @@ use warnings;
use base 'Hydra::Base::Controller::ListBuilds'; use base 'Hydra::Base::Controller::ListBuilds';
use Hydra::Helper::Nix; use Hydra::Helper::Nix;
use Hydra::Helper::CatalystUtils; use Hydra::Helper::CatalystUtils;
use Hydra::View::TT;
use Digest::SHA1 qw(sha1_hex); use Digest::SHA1 qw(sha1_hex);
use Nix::Store; use Nix::Store;
use Nix::Config; use Nix::Config;
@ -13,6 +14,7 @@ use Encode;
use File::Basename; use File::Basename;
use JSON; use JSON;
use List::MoreUtils qw{any}; use List::MoreUtils qw{any};
use Net::Prometheus;
# Put this controller at top-level. # Put this controller at top-level.
__PACKAGE__->config->{namespace} = ''; __PACKAGE__->config->{namespace} = '';
@ -200,6 +202,49 @@ sub machines :Local Args(0) {
$self->status_ok($c, entity => $c->stash->{machines}); $self->status_ok($c, entity => $c->stash->{machines});
} }
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');
}
# Hydra::Base::Controller::ListBuilds needs this. # Hydra::Base::Controller::ListBuilds needs this.
sub get_builds : Chained('/') PathPart('') CaptureArgs(0) { sub get_builds : Chained('/') PathPart('') CaptureArgs(0) {

View file

@ -13,6 +13,8 @@ our @EXPORT = qw(
searchBuildsAndEvalsForJobset searchBuildsAndEvalsForJobset
error notFound gone accessDenied error notFound gone accessDenied
forceLogin requireUser requireProjectOwner requireRestartPrivileges requireAdmin requirePost isAdmin isProjectOwner forceLogin requireUser requireProjectOwner requireRestartPrivileges requireAdmin requirePost isAdmin isProjectOwner
requireBumpPrivileges
requireCancelBuildPrivileges
trim trim
getLatestFinishedEval getFirstEval getLatestFinishedEval getFirstEval
paramToList paramToList
@ -181,6 +183,48 @@ sub isProjectOwner {
defined $c->model('DB::ProjectMembers')->find({ project => $project, userName => $c->user->username })); defined $c->model('DB::ProjectMembers')->find({ project => $project, userName => $c->user->username }));
} }
sub hasCancelBuildRole {
my ($c) = @_;
return $c->user_exists && $c->check_user_roles('cancel-build');
}
sub mayCancelBuild {
my ($c, $project) = @_;
return
$c->user_exists &&
(isAdmin($c) ||
hasCancelBuildRole($c) ||
isProjectOwner($c, $project));
}
sub requireCancelBuildPrivileges {
my ($c, $project) = @_;
requireUser($c);
accessDenied($c, "Only the project members, administrators, and accounts with cancel-build privileges can perform this operation.")
unless mayCancelBuild($c, $project);
}
sub hasBumpJobsRole {
my ($c) = @_;
return $c->user_exists && $c->check_user_roles('bump-to-front');
}
sub mayBumpJobs {
my ($c, $project) = @_;
return
$c->user_exists &&
(isAdmin($c) ||
hasBumpJobsRole($c) ||
isProjectOwner($c, $project));
}
sub requireBumpPrivileges {
my ($c, $project) = @_;
requireUser($c);
accessDenied($c, "Only the project members, administrators, and accounts with bump-to-front privileges can perform this operation.")
unless mayBumpJobs($c, $project);
}
sub hasRestartJobsRole { sub hasRestartJobsRole {
my ($c) = @_; my ($c) = @_;
return $c->user_exists && $c->check_user_roles('restart-jobs'); return $c->user_exists && $c->check_user_roles('restart-jobs');

View file

@ -81,6 +81,8 @@
[% INCLUDE roleoption role="admin" %] [% INCLUDE roleoption role="admin" %]
[% INCLUDE roleoption role="create-projects" %] [% INCLUDE roleoption role="create-projects" %]
[% INCLUDE roleoption role="restart-jobs" %] [% INCLUDE roleoption role="restart-jobs" %]
[% INCLUDE roleoption role="bump-to-front" %]
[% INCLUDE roleoption role="cancel-build" %]
</select> </select>
</div> </div>
</div> </div>

View file

@ -440,8 +440,6 @@ sub checkBuild {
# new build to be scheduled if the meta.maintainers field is # new build to be scheduled if the meta.maintainers field is
# changed? # changed?
if (defined $prevEval) { if (defined $prevEval) {
# Only check one output: if it's the same, the other will be as well.
my $firstOutput = $outputNames[0];
my ($prevBuild) = $prevEval->builds->search( my ($prevBuild) = $prevEval->builds->search(
# The "project" and "jobset" constraints are # The "project" and "jobset" constraints are
# semantically unnecessary (because they're implied by # semantically unnecessary (because they're implied by