From c08fc6ce1e76ef87cc322fc7880cd435af4bf949 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 8 Jul 2013 23:54:40 +0200 Subject: [PATCH 01/50] Add basic Persona support This allows users to sign in to Hydra using Mozilla Persona accounts. When a user first sign in, a row in the Users table for the given Persona identity (an email address) is created automatically. To do: figure out how to deal with legacy accounts. --- src/lib/Hydra/Controller/Root.pm | 2 +- src/lib/Hydra/Controller/User.pm | 39 ++++++++++++++++++++++++++++++++ src/root/layout.tt | 38 ++++++++++++++++++++++++++++++- src/root/login.tt | 4 ++-- src/root/topbar.tt | 10 +++++--- 5 files changed, 86 insertions(+), 7 deletions(-) diff --git a/src/lib/Hydra/Controller/Root.pm b/src/lib/Hydra/Controller/Root.pm index 52ffa653..7f1a28ae 100644 --- a/src/lib/Hydra/Controller/Root.pm +++ b/src/lib/Hydra/Controller/Root.pm @@ -135,7 +135,7 @@ sub machines :Local Args(0) { { order_by => 'stoptime desc', rows => 1 }); ${$machines}{$m}{'idle'} = $idle ? $idle->stoptime : 0; } - + $c->stash->{machines} = $machines; $c->stash->{steps} = [ $c->model('DB::BuildSteps')->search( { finished => 0, 'me.busy' => 1, 'build.busy' => 1, }, diff --git a/src/lib/Hydra/Controller/User.pm b/src/lib/Hydra/Controller/User.pm index 84ec88df..3b9efb0a 100644 --- a/src/lib/Hydra/Controller/User.pm +++ b/src/lib/Hydra/Controller/User.pm @@ -8,6 +8,8 @@ use Crypt::RandPasswd; use Digest::SHA1 qw(sha1_hex); use Hydra::Helper::Nix; use Hydra::Helper::CatalystUtils; +use LWP::UserAgent; +use JSON; __PACKAGE__->config->{namespace} = ''; @@ -70,6 +72,43 @@ sub logout_GET { } +sub persona_login :Path('/persona-login') Args(0) { + my ($self, $c) = @_; + $c->stash->{json} = {}; + die if $c->request->method ne "POST"; + + my $assertion = $c->req->params->{assertion} or die; + + my $ua = new LWP::UserAgent; + my $response = $ua->post( + 'https://verifier.login.persona.org/verify', + { assertion => $assertion, + audience => "http://localhost:3000/" + }); + Catalyst::Exception->throw("Did not get a response from Persona.") unless $response->is_success; + + my $d = decode_json($response->decoded_content) or die; + Catalyst::Exception->throw("Persona says: $d->{reason}") if $d->{status} ne "okay"; + + my $email = $d->{email} or die; + + my $user = $c->find_user({ username => $email }); + + if (!$user) { + $c->model('DB::Users')->create( + { username => $email + , password => "!" + , emailaddress => $email, + }); + $user = $c->find_user({ username => $email }) or die; + } + + $c->set_authenticated($user); + + $c->stash->{json}->{result} = "ok"; +} + + sub captcha :Local Args(0) { my ($self, $c) = @_; $c->create_captcha(); diff --git a/src/root/layout.tt b/src/root/layout.tt index 2337599b..3357726b 100644 --- a/src/root/layout.tt +++ b/src/root/layout.tt @@ -11,6 +11,8 @@ Hydra - [% HTML.escape(title) %] + + @@ -95,13 +97,47 @@ Hydra [% HTML.escape(version) %] (using [% HTML.escape(nixVersion) %]). [% IF c.user_exists %] - You are logged in as [% c.user.username %]. + You are logged in as [% HTML.escape(c.user.username) %]. [% END %] + + + + diff --git a/src/root/login.tt b/src/root/login.tt index 5d69f3ea..bf3ad616 100644 --- a/src/root/login.tt +++ b/src/root/login.tt @@ -13,8 +13,7 @@ You can logout here. c.uri_for('/register') %]">register first.

--> -
- + [% END %] diff --git a/src/root/topbar.tt b/src/root/topbar.tt index fd924615..609fa3c1 100644 --- a/src/root/topbar.tt +++ b/src/root/topbar.tt @@ -1,6 +1,6 @@ [% BLOCK menuItem %]
  • - [% title %] + uri) %]>[% title %]
  • [% END %] @@ -198,9 +198,13 @@ [% IF c.user_exists %] [% INCLUDE menuItem uri = c.uri_for(c.controller('User').action_for('edit'), [c.user.username]) title = "Preferences" %] - [% INCLUDE menuItem uri = c.uri_for(c.controller('Root').action_for('logout')) title = "Sign out" %] +
  • + Sign out +
  • [% ELSE %] - [% INCLUDE menuItem uri = c.uri_for(c.controller('Root').action_for('login')) title = "Sign in" %] +
  • + Sign in +
  • [% END %] From 13f0b54c496e9625def069680c4de638401e4110 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 9 Jul 2013 00:23:48 +0200 Subject: [PATCH 02/50] Set the character set --- src/root/layout.tt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/root/layout.tt b/src/root/layout.tt index 3357726b..78152407 100644 --- a/src/root/layout.tt +++ b/src/root/layout.tt @@ -11,6 +11,7 @@ Hydra - [% HTML.escape(title) %] + From 12ca755beaef0403d80c71dc7cb35aedb389e37c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 9 Jul 2013 12:02:15 +0200 Subject: [PATCH 03/50] Don't redirect to /login if authentication is required We can just serve the login page in place. --- src/lib/Hydra/Controller/User.pm | 16 +++++++++++++--- src/lib/Hydra/Helper/CatalystUtils.pm | 16 ++++++---------- src/root/layout.tt | 11 +++++------ 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/lib/Hydra/Controller/User.pm b/src/lib/Hydra/Controller/User.pm index 3b9efb0a..9dd90b0b 100644 --- a/src/lib/Hydra/Controller/User.pm +++ b/src/lib/Hydra/Controller/User.pm @@ -75,7 +75,7 @@ sub logout_GET { sub persona_login :Path('/persona-login') Args(0) { my ($self, $c) = @_; $c->stash->{json} = {}; - die if $c->request->method ne "POST"; + requirePost($c); my $assertion = $c->req->params->{assertion} or die; @@ -85,10 +85,10 @@ sub persona_login :Path('/persona-login') Args(0) { { assertion => $assertion, audience => "http://localhost:3000/" }); - Catalyst::Exception->throw("Did not get a response from Persona.") unless $response->is_success; + error($c, "Did not get a response from Persona.") unless $response->is_success; my $d = decode_json($response->decoded_content) or die; - Catalyst::Exception->throw("Persona says: $d->{reason}") if $d->{status} ne "okay"; + error($c, "Persona says: $d->{reason}") if $d->{status} ne "okay"; my $email = $d->{email} or die; @@ -106,6 +106,16 @@ sub persona_login :Path('/persona-login') Args(0) { $c->set_authenticated($user); $c->stash->{json}->{result} = "ok"; + $c->flash->{flashMsg} = "You are now signed in as " . $email . ""; +} + + +sub persona_logout :Path('/persona-logout') Args(0) { + my ($self, $c) = @_; + $c->stash->{json} = {}; + requirePost($c); + $c->flash->{flashMsg} = "You are no longer signed in." if $c->user_exists(); + $c->logout; } diff --git a/src/lib/Hydra/Helper/CatalystUtils.pm b/src/lib/Hydra/Helper/CatalystUtils.pm index a13f2570..2cbb601a 100644 --- a/src/lib/Hydra/Helper/CatalystUtils.pm +++ b/src/lib/Hydra/Helper/CatalystUtils.pm @@ -89,7 +89,8 @@ sub getPreviousSuccessfulBuild { sub error { - my ($c, $msg) = @_; + my ($c, $msg, $status) = @_; + $c->response->status($status) if defined $status; $c->error($msg); $c->detach; # doesn't return } @@ -97,8 +98,7 @@ sub error { sub notFound { my ($c, $msg) = @_; - $c->response->status(404); - error($c, $msg); + error($c, $msg, 404); } @@ -113,8 +113,7 @@ sub backToReferer { sub requireLogin { my ($c) = @_; $c->session->{referer} = $c->request->uri; - $c->response->redirect($c->uri_for('/login')); - $c->detach; # doesn't return + error($c, "This page requires you to sign in.", 403); } @@ -130,24 +129,21 @@ sub requireProjectOwner { requireLogin($c) if !$c->user_exists; - error($c, "Only the project members or administrators can perform this operation.") + error($c, "Only the project members or administrators can perform this operation.", 403) unless isProjectOwner($c, $project); } sub isAdmin { my ($c) = @_; - return $c->user_exists && $c->check_user_roles('admin'); } sub requireAdmin { my ($c) = @_; - requireLogin($c) if !$c->user_exists; - - error($c, "Only administrators can perform this operation.") + error($c, "Only administrators can perform this operation.", 403) unless isAdmin($c); } diff --git a/src/root/layout.tt b/src/root/layout.tt index 78152407..66da558d 100644 --- a/src/root/layout.tt +++ b/src/root/layout.tt @@ -121,12 +121,11 @@ .fail(function() { bootbox.alert("Server request failed!"); }); }, onlogout: function() { - $.ajax({ - type: 'POST', - url: '/logout', - success: function(res, status, xhr) { window.location.reload(); }, - error: function(xhr, status, err) { alert("Logout failure: " + err); } - }); + $.post("[% c.uri_for('/persona-logout') %]") + .done(function(data) { + window.location.reload(); + }) + .fail(function() { bootbox.alert("Server request failed!"); }); } }); From 08f3bb78c481deee9b4fe1507860620167ae3872 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 9 Jul 2013 12:05:25 +0200 Subject: [PATCH 04/50] Use the alert class for flash messages --- src/root/layout.tt | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/root/layout.tt b/src/root/layout.tt index 66da558d..7030447b 100644 --- a/src/root/layout.tt +++ b/src/root/layout.tt @@ -68,21 +68,6 @@
    - [% IF flashMsg %] -
    -

    [% flashMsg %]

    - [% END %] - - [% IF successMsg %] -
    -

    [% successMsg %]

    - [% END %] - - [% IF errorMsg %] -
    -

    Error: [% errorMsg %]

    - [% END %] - [% IF !hideHeader %]
    + [% IF !create && user.password != '!' %]
    @@ -47,6 +48,7 @@
    + [% END %] - [% END %] From 486a84486637235d8ec7fe3f3c8ec12b8e0e88cb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 9 Jul 2013 14:05:04 +0200 Subject: [PATCH 10/50] Fix audience URL --- src/lib/Hydra/Controller/User.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Hydra/Controller/User.pm b/src/lib/Hydra/Controller/User.pm index 6a11c231..cb852830 100644 --- a/src/lib/Hydra/Controller/User.pm +++ b/src/lib/Hydra/Controller/User.pm @@ -75,7 +75,7 @@ sub persona_login :Path('/persona-login') Args(0) { my $response = $ua->post( 'https://verifier.login.persona.org/verify', { assertion => $assertion, - audience => "http://localhost:3000/" + audience => $c->uri_for('/') }); error($c, "Did not get a response from Persona.") unless $response->is_success; From ccd2292458aab921c40ab0fad6c025a033f09f6e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 9 Jul 2013 14:39:28 +0200 Subject: [PATCH 11/50] Link to both the Persona and legacy sign in --- src/root/layout.tt | 2 +- src/root/login.tt | 5 ----- src/root/topbar.tt | 14 +++++++++++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/root/layout.tt b/src/root/layout.tt index ed255e05..0229c229 100644 --- a/src/root/layout.tt +++ b/src/root/layout.tt @@ -136,7 +136,7 @@ }); $("#persona-signin").click(function() { - navigator.id.request(); + navigator.id.request({ siteName: 'Hydra' }); }); $("#persona-signout").click(function() { diff --git a/src/root/login.tt b/src/root/login.tt index d26cb4d6..c2806600 100644 --- a/src/root/login.tt +++ b/src/root/login.tt @@ -7,11 +7,6 @@ You are already signed in as [% HTML.escape(c.user.username) %].

    [% ELSE %] - -
    diff --git a/src/root/topbar.tt b/src/root/topbar.tt index 609fa3c1..153b0b1a 100644 --- a/src/root/topbar.tt +++ b/src/root/topbar.tt @@ -202,9 +202,17 @@ Sign out [% ELSE %] -
  • - Sign in -
  • + [% WRAPPER makeSubMenu title="Sign in" %] +
  • + + Sign in with Persona + +
  • +
  • +
  • + Sign in with a Hydra user name +
  • + [% END %] [% END %] From 14b9b201e14d3939e9e3afc912686ce048dd37e0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 5 Nov 2013 11:18:44 +0100 Subject: [PATCH 12/50] Don't show a password reset button for Persona accounts --- src/root/user.tt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/root/user.tt b/src/root/user.tt index edd279a3..781500aa 100644 --- a/src/root/user.tt +++ b/src/root/user.tt @@ -73,7 +73,7 @@
    - [% INCLUDE roleoption role="admin" %] [% INCLUDE roleoption role="create-project" %] @@ -101,7 +101,7 @@ [%IF create %]Create[% ELSE %]Apply changes[% END %] - [% IF !create && c.check_user_roles('admin') %] + [% IF !create && c.check_user_roles('admin') && user.password != '!' %]
    - [% IF !create && user.password != '!' %] + [% IF !create && user.type == 'hydra' %]
    @@ -101,7 +101,7 @@ [%IF create %]Create[% ELSE %]Apply changes[% END %] - [% IF !create && c.check_user_roles('admin') && user.password != '!' %] + [% IF !create && c.check_user_roles('admin') && user.type == 'hydra' %]
    - [% IF !create && user.type == 'hydra' %] + [% IF create || user.type == 'hydra' %]
    @@ -50,26 +50,22 @@
    [% END %] - - [% IF !create %] -
    -
    - -
    +
    +
    +
    - [% END %] +
    - [% IF !create %] + [% IF !create || c.check_user_roles('admin') %]
    @@ -81,7 +77,7 @@
    [% END %] - [% IF create %] + [% IF create && !c.check_user_roles('admin') %]
    CAPTCHA From bd927eba6615a24587ee0ec566e34ee3972362dd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 5 Nov 2013 13:13:02 +0100 Subject: [PATCH 15/50] Make sign in a modal dialog box rather than a separate page --- src/lib/Hydra/Controller/User.pm | 36 +++++++---------------------- src/root/layout.tt | 39 ++++++++++++++++++++++++++++++++ src/root/login.tt | 36 ----------------------------- src/root/topbar.tt | 2 +- 4 files changed, 48 insertions(+), 65 deletions(-) delete mode 100644 src/root/login.tt diff --git a/src/lib/Hydra/Controller/User.pm b/src/lib/Hydra/Controller/User.pm index 04109f11..49f67974 100644 --- a/src/lib/Hydra/Controller/User.pm +++ b/src/lib/Hydra/Controller/User.pm @@ -18,39 +18,19 @@ __PACKAGE__->config->{namespace} = ''; sub login :Local :Args(0) :ActionClass('REST::ForBrowsers') { } -sub login_GET { - my ($self, $c) = @_; - - 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; + my $username = $c->stash->{params}->{username} // ""; + my $password = $c->stash->{params}->{password} // ""; - $username = $c->stash->{params}->{username}; - $password = $c->stash->{params}->{password}; + error($c, "You must specify a user name.") if $username eq ""; + error($c, "You must specify a password.") if $password eq ""; - if ($username && $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); - } - } - } + accessDenied($c, "Bad username or password.") + if !$c->authenticate({username => $username, password => $password}); + + $self->status_ok($c, entity => { }); } diff --git a/src/root/layout.tt b/src/root/layout.tt index 70236b17..2237e79b 100644 --- a/src/root/layout.tt +++ b/src/root/layout.tt @@ -146,6 +146,45 @@ [% END %] + [% IF !c.user_exists %] + + + + [% END %] + diff --git a/src/root/login.tt b/src/root/login.tt deleted file mode 100644 index 9963c497..00000000 --- a/src/root/login.tt +++ /dev/null @@ -1,36 +0,0 @@ -[% WRAPPER layout.tt title="Sign in" %] -[% PROCESS common.tt %] - -[% IF c.user_exists %] -

    -You are already signed in as [% HTML.escape(c.user.username) %]. -

    -[% ELSE %] - -
    - -
    -
    - -
    - -
    -
    - -
    - -
    - -
    -
    - -
    - -
    -
    - -
    - -[% END %] - -[% END %] diff --git a/src/root/topbar.tt b/src/root/topbar.tt index a5f7c3c1..a48038d5 100644 --- a/src/root/topbar.tt +++ b/src/root/topbar.tt @@ -128,7 +128,7 @@
  • - Sign in with a Hydra account + Sign in with a Hydra account
  • [% END %] [% END %] From bdd6e99fbddb3b751f7137dad677f0b0a93051b1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 5 Nov 2013 14:02:04 +0100 Subject: [PATCH 16/50] Clean up user editing --- src/lib/Hydra/Controller/User.pm | 124 +++++++++++++++---------------- src/root/static/js/common.js | 6 ++ src/root/user.tt | 81 ++++++++++++-------- 3 files changed, 118 insertions(+), 93 deletions(-) diff --git a/src/lib/Hydra/Controller/User.pm b/src/lib/Hydra/Controller/User.pm index 49f67974..e96796fb 100644 --- a/src/lib/Hydra/Controller/User.pm +++ b/src/lib/Hydra/Controller/User.pm @@ -16,7 +16,7 @@ use HTML::Entities; __PACKAGE__->config->{namespace} = ''; -sub login :Local :Args(0) :ActionClass('REST::ForBrowsers') { } +sub login :Local :Args(0) :ActionClass('REST') { } sub login_POST { my ($self, $c) = @_; @@ -30,11 +30,11 @@ sub login_POST { accessDenied($c, "Bad username or password.") if !$c->authenticate({username => $username, password => $password}); - $self->status_ok($c, entity => { }); + $self->status_no_content($c); } -sub logout :Local :Args(0) :ActionClass('REST::ForBrowsers') { } +sub logout :Local :Args(0) :ActionClass('REST') { } sub logout_POST { my ($self, $c) = @_; @@ -106,9 +106,13 @@ sub register :Local Args(0) { accessDenied($c, "User registration is currently not implemented.") unless isAdmin($c); - $c->stash->{template} = 'user.tt'; - $c->stash->{create} = 1; - return if $c->request->method ne "POST"; + if ($c->request->method eq "GET") { + $c->stash->{template} = 'user.tt'; + $c->stash->{create} = 1; + return; + } + + die unless $c->request->method eq "PUT"; my $userName = trim $c->req->params->{username}; $c->stash->{username} = $userName; @@ -138,13 +142,16 @@ sub register :Local Args(0) { } $c->flash->{successMsg} = "User $userName has been created."; - backToReferer($c); + $self->status_no_content($c); } sub updatePreferences { my ($c, $user) = @_; + my $fullName = trim($c->req->params->{fullname} // ""); + error($c, "Your must specify your full name.") if $fullName eq ""; + my $password = trim($c->req->params->{password} // ""); if ($user->type eq "hydra" && ($user->password eq "!" || $password ne "")) { error($c, "You must specify a password of at least 6 characters.") @@ -156,9 +163,6 @@ sub updatePreferences { setPassword($user, $password); } - my $fullName = trim($c->req->params->{fullname} // ""); - error($c, "Your must specify your full name.") if $fullName eq ""; - my $emailAddress = trim($c->req->params->{emailaddress} // ""); # FIXME: validate email address? @@ -185,8 +189,7 @@ sub currentUser_GET { requireUser($c); - $self->status_ok( - $c, + $self->status_ok($c, entity => $c->model("DB::Users")->find($c->user->username) ); } @@ -205,61 +208,18 @@ sub user :Chained('/') PathPart('user') CaptureArgs(1) { } -sub deleteUser { - my ($self, $c, $user) = @_; - my ($project) = $c->model('DB::Projects')->search({ owner => $user->username }); - error($c, "User " . $user->username . " is still owner of project " . $project->name . ".") - if defined $project; - $c->logout() if $user->username eq $c->user->username; - $user->delete; -} - - -sub edit :Chained('user') :Args(0) :ActionClass('REST::ForBrowsers') { } +sub edit :Chained('user') :PathPart('') :Args(0) :ActionClass('REST::ForBrowsers') { } sub edit_GET { my ($self, $c) = @_; - - my $user = $c->stash->{user}; - $c->stash->{template} = 'user.tt'; - - $c->session->{referer} = $c->request->referer if !defined $c->session->{referer}; - - $c->stash->{fullname} = $user->fullname; - - $c->stash->{emailonerror} = $user->emailonerror; } -sub edit_POST { +sub edit_PUT { 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") { - deleteUser($self, $c, $user); - backToReferer($c); - } - if (($c->stash->{params}->{submit} // "") eq "reset-password") { - error($c, "This user's password cannot be reset.") if $user->type ne "hydra"; - $c->stash->{json} = {}; - error($c, "No email address is set for this user.") - unless $user->emailaddress; - my $password = Crypt::RandPasswd->word(8,10); - setPassword($user, $password); - sendEmail($c, - $user->emailaddress, - "Hydra password reset", - "Hi,\n\n". - "Your password has been reset. Your new password is '$password'.\n\n". - "You can change your password at " . $c->uri_for($self->action_for('edit'), [$user->username]) . ".\n\n". - "With regards,\n\nHydra.\n" - ); return; } @@ -267,12 +227,50 @@ sub edit_POST { updatePreferences($c, $user); }); - if ($c->request->looks_like_browser) { - $c->flash->{successMsg} = "Your preferences have been updated."; - backToReferer($c); - } else { - $self->status_no_content($c); - } + $c->flash->{successMsg} = "Your preferences have been updated."; + $self->status_no_content($c); +} + +sub edit_DELETE { + my ($self, $c) = @_; + my $user = $c->stash->{user}; + + my ($project) = $c->model('DB::Projects')->search({ owner => $user->username }); + error($c, "User " . $user->username . " is still owner of project " . $project->name . ".") + if defined $project; + + $c->logout() if $user->username eq $c->user->username; + + $user->delete; + + $c->flash->{successMsg} = "The user has been deleted."; + $self->status_no_content($c); +} + + +sub reset_password :Chained('user') :PathPart('reset-password') :Args(0) { + my ($self, $c) = @_; + my $user = $c->stash->{user}; + + requirePost($c); + + error($c, "This user's password cannot be reset.") if $user->type ne "hydra"; + error($c, "No email address is set for this user.") + unless $user->emailaddress; + + my $password = Crypt::RandPasswd->word(8,10); + setPassword($user, $password); + sendEmail($c, + $user->emailaddress, + "Hydra password reset", + "Hi,\n\n". + "Your password has been reset. Your new password is '$password'.\n\n". + "You can change your password at " . $c->uri_for($self->action_for('edit'), [$user->username]) . ".\n\n". + "With regards,\n\nHydra.\n" + ); + + $c->flash->{successMsg} = "A new password has been sent to ${\$user->emailaddress}."; + $self->status_no_content($c); } diff --git a/src/root/static/js/common.js b/src/root/static/js/common.js index 891fd4e6..784c2ed3 100644 --- a/src/root/static/js/common.js +++ b/src/root/static/js/common.js @@ -145,3 +145,9 @@ function redirectJSON(args) { }; return requestJSON(args); }; + +function backToReferrer() { + // FIXME: should only do this if the referrer is another Hydra + // page. + window.location = document.referrer; +} diff --git a/src/root/user.tt b/src/root/user.tt index 0d80e828..8c70736c 100644 --- a/src/root/user.tt +++ b/src/root/user.tt @@ -1,4 +1,4 @@ -[% WRAPPER layout.tt title=(create ? "Register new user" : "Editing user $user.username") %] +[% WRAPPER layout.tt title=(create ? "Add new user" : "Editing user $user.username") %] [% PROCESS common.tt %] [% BLOCK roleoption %] @@ -14,7 +14,7 @@ >[% role %] [% END %] -
    +
    @@ -30,7 +30,7 @@
    - fullname) %]/> + create ? '' : user.fullname) %]/>
    @@ -60,7 +60,7 @@
    @@ -93,44 +93,21 @@ [% END %]
    - [% IF !create && c.check_user_roles('admin') && user.type == 'hydra' %] - - [% END %] [% IF !create %] - - [% END %]
    @@ -138,4 +115,48 @@ + + [% END %] From 3c981bed022ff9d0becd4768861374b179242dfd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 5 Nov 2013 14:10:20 +0100 Subject: [PATCH 17/50] Slight cleanup in the Persona sign in code --- src/lib/Hydra/Controller/User.pm | 3 +-- src/root/layout.tt | 14 ++++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/lib/Hydra/Controller/User.pm b/src/lib/Hydra/Controller/User.pm index e96796fb..96dd3e30 100644 --- a/src/lib/Hydra/Controller/User.pm +++ b/src/lib/Hydra/Controller/User.pm @@ -46,7 +46,6 @@ sub logout_POST { sub persona_login :Path('/persona-login') Args(0) { my ($self, $c) = @_; - $c->stash->{json} = {}; requirePost($c); my $assertion = $c->req->params->{assertion} or die; @@ -78,7 +77,7 @@ sub persona_login :Path('/persona-login') Args(0) { $c->set_authenticated($user); - $c->stash->{json}->{result} = "ok"; + $self->status_no_content($c); $c->flash->{successMsg} = "You are now signed in as " . encode_entities($email) . "."; } diff --git a/src/root/layout.tt b/src/root/layout.tt index 2237e79b..fb4a2531 100644 --- a/src/root/layout.tt +++ b/src/root/layout.tt @@ -124,14 +124,12 @@ navigator.id.watch({ loggedInUser: [% c.user_exists ? '"' _ HTML.escape(c.user.username) _ '"' : "null" %], onlogin: function(assertion) { - $.post("[% c.uri_for('/persona-login') %]", { assertion: assertion }) - .done(function(data) { - if (data.error) - bootbox.alert("Login failed: " + data.error); - else - window.location.reload(); - }) - .fail(function() { bootbox.alert("Server request failed!"); }); + requestJSON({ + url: "[% c.uri_for('/persona-login') %]", + data: "assertion=" + assertion, + type: 'POST', + success: function(data) { window.location.reload(); } + }); }, onlogout: doLogout }); From b53bd868fbd196cb3554d7344aa87eaf96fba6a2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 5 Nov 2013 14:35:49 +0100 Subject: [PATCH 18/50] Add a "My jobs" tab to the dashboard It shows all current jobs of which the user is a maintainer. --- src/lib/Hydra/Controller/User.pm | 15 +++++++++++++++ src/root/dashboard.tt | 7 ++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/lib/Hydra/Controller/User.pm b/src/lib/Hydra/Controller/User.pm index 96dd3e30..ca626077 100644 --- a/src/lib/Hydra/Controller/User.pm +++ b/src/lib/Hydra/Controller/User.pm @@ -288,4 +288,19 @@ sub dashboard :Chained('user') :Args(0) { } +sub my_jobs_tab :Chained('user') :PathPart('my-jobs-tab') :Args(0) { + my ($self, $c) = @_; + $c->stash->{template} = 'dashboard-my-jobs-tab.tt'; + + die unless $c->stash->{user}->emailaddress; + + # Get all current builds of which this user is a maintainer. + $c->stash->{builds} = [$c->model('DB::Builds')->search( + { iscurrent => 1 + , maintainers => { ilike => "%" . $c->stash->{user}->emailaddress . "%" } + }, + { order_by => ["project", "jobset", "job"] })]; +} + + 1; diff --git a/src/root/dashboard.tt b/src/root/dashboard.tt index d33eb887..2e30ea4b 100644 --- a/src/root/dashboard.tt +++ b/src/root/dashboard.tt @@ -3,6 +3,7 @@
    @@ -31,12 +32,16 @@ [% ELSE %] -
    You have no starred jobs. You can add them by visiting a job page and clicking on the ☆ icon.
    +
    You have no starred jobs. You + can add them by visiting a job page and clicking on the ☆ + icon.
    [% END %]
    + [% INCLUDE makeLazyTab tabName="tabs-my-jobs" uri=c.uri_for(c.controller('User').action_for('my_jobs_tab'), [user.username]) %] +
    [% END %] From fc827a003a44540b80cf31ce009721897d931191 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 5 Nov 2013 14:40:40 +0100 Subject: [PATCH 19/50] Be paranoid about the Persona email address --- src/lib/Hydra/Controller/User.pm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/Hydra/Controller/User.pm b/src/lib/Hydra/Controller/User.pm index ca626077..4b6033a8 100644 --- a/src/lib/Hydra/Controller/User.pm +++ b/src/lib/Hydra/Controller/User.pm @@ -63,6 +63,10 @@ sub persona_login :Path('/persona-login') Args(0) { my $email = $d->{email} or die; + # Be paranoid about the email address format, since we do use it + # in URLs. + die "Illegal email address." unless $email =~ /^[a-zA-Z0-9\.\-\_]+@[a-zA-Z0-9\.\-\_]+$/; + my $user = $c->find_user({ username => $email }); if (!$user) { From 27b795f46e78fe031692833e4f2aabc90aabfdbb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 5 Nov 2013 14:46:20 +0100 Subject: [PATCH 20/50] Doh --- src/root/dashboard-my-jobs-tab.tt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/root/dashboard-my-jobs-tab.tt diff --git a/src/root/dashboard-my-jobs-tab.tt b/src/root/dashboard-my-jobs-tab.tt new file mode 100644 index 00000000..9e15cd7d --- /dev/null +++ b/src/root/dashboard-my-jobs-tab.tt @@ -0,0 +1,17 @@ +[% PROCESS common.tt %] + +[% IF builds.size == 0 %] + +
    You are not the maintainer of any + job. You can become a maintainer by setting the + meta.maintainer field of a job to [% + HTML.escape(user.emailaddress) %].
    + +[% ELSE %] + +

    Below is the most recent build of every job of which you + ([% HTML.escape(user.emailaddress) %]) are a maintainer.

    + + [% INCLUDE renderBuildList %] + +[% END %] From 589271bd72a833fd153ff390882b977e66f0d047 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 5 Nov 2013 14:53:52 +0100 Subject: [PATCH 21/50] In the "My jobs" tab, ignore disabled projects/jobsets --- src/lib/Hydra/Controller/User.pm | 6 +++++- src/root/dashboard-my-jobs-tab.tt | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib/Hydra/Controller/User.pm b/src/lib/Hydra/Controller/User.pm index 4b6033a8..7ddfb6e8 100644 --- a/src/lib/Hydra/Controller/User.pm +++ b/src/lib/Hydra/Controller/User.pm @@ -302,8 +302,12 @@ sub my_jobs_tab :Chained('user') :PathPart('my-jobs-tab') :Args(0) { $c->stash->{builds} = [$c->model('DB::Builds')->search( { iscurrent => 1 , maintainers => { ilike => "%" . $c->stash->{user}->emailaddress . "%" } + , "project.enabled" => 1 + , "jobset.enabled" => 1 }, - { order_by => ["project", "jobset", "job"] })]; + { order_by => ["project", "jobset", "job"] + , join => ["project", "jobset"] + })]; } diff --git a/src/root/dashboard-my-jobs-tab.tt b/src/root/dashboard-my-jobs-tab.tt index 9e15cd7d..6dee16b5 100644 --- a/src/root/dashboard-my-jobs-tab.tt +++ b/src/root/dashboard-my-jobs-tab.tt @@ -9,7 +9,7 @@ [% ELSE %] -

    Below is the most recent build of every job of which you +

    Below are the most recent builds of the [% builds.size %] jobs of which you ([% HTML.escape(user.emailaddress) %]) are a maintainer.

    [% INCLUDE renderBuildList %] From ae85d2df23f66fa6b1600d972fc341bcbb85d613 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 5 Nov 2013 16:05:29 +0100 Subject: [PATCH 22/50] Add a "My jobsets" tab to the dashboard --- src/lib/Hydra/Controller/User.pm | 16 ++++++++ src/lib/Hydra/Helper/Nix.pm | 16 ++++++-- src/root/common.tt | 61 ++++++++++++++++++++++++++++ src/root/dashboard-my-jobs-tab.tt | 2 +- src/root/dashboard-my-jobsets-tab.tt | 12 ++++++ src/root/dashboard.tt | 2 + src/root/project.tt | 59 +-------------------------- 7 files changed, 105 insertions(+), 63 deletions(-) create mode 100644 src/root/dashboard-my-jobsets-tab.tt diff --git a/src/lib/Hydra/Controller/User.pm b/src/lib/Hydra/Controller/User.pm index 7ddfb6e8..66b645b2 100644 --- a/src/lib/Hydra/Controller/User.pm +++ b/src/lib/Hydra/Controller/User.pm @@ -311,4 +311,20 @@ sub my_jobs_tab :Chained('user') :PathPart('my-jobs-tab') :Args(0) { } +sub my_jobsets_tab :Chained('user') :PathPart('my-jobsets-tab') :Args(0) { + my ($self, $c) = @_; + $c->stash->{template} = 'dashboard-my-jobsets-tab.tt'; + + my $jobsets = $c->model('DB::Jobsets')->search( + { "project.enabled" => 1, "me.enabled" => 1, + , owner => $c->stash->{user}->username + }, + { order_by => ["project", "name"] + , join => ["project"] + }); + + $c->stash->{jobsets} = [jobsetOverview_($c, $jobsets)]; +} + + 1; diff --git a/src/lib/Hydra/Helper/Nix.pm b/src/lib/Hydra/Helper/Nix.pm index a7fa06ed..76e10b47 100644 --- a/src/lib/Hydra/Helper/Nix.pm +++ b/src/lib/Hydra/Helper/Nix.pm @@ -17,7 +17,8 @@ our @EXPORT = qw( getPrimaryBuildsForView getPrimaryBuildTotal getViewResult getLatestSuccessfulViewResult - jobsetOverview removeAsciiEscapes getDrvLogPath findLog logContents + jobsetOverview jobsetOverview_ + removeAsciiEscapes getDrvLogPath findLog logContents getMainOutput getEvals getMachines pathIsInsidePrefix @@ -173,9 +174,9 @@ sub findLastJobForBuilds { } -sub jobsetOverview { - my ($c, $project) = @_; - return $project->jobsets->search( isProjectOwner($c, $project) ? {} : { hidden => 0 }, +sub jobsetOverview_ { + my ($c, $jobsets) = @_; + return $jobsets->search({}, { order_by => "name" , "+select" => [ "(select count(*) from Builds as a where a.finished = 0 and me.project = a.project and me.name = a.jobset and a.isCurrent = 1)" @@ -188,6 +189,13 @@ sub jobsetOverview { } +sub jobsetOverview { + my ($c, $project) = @_; + my $jobsets = $project->jobsets->search(isProjectOwner($c, $project) ? {} : { hidden => 0 }); + return jobsetOverview_($c, $jobsets); +} + + sub getViewResult { my ($primaryBuild, $jobs, $finished) = @_; diff --git a/src/root/common.tt b/src/root/common.tt index 0b0b7472..056c9f7e 100644 --- a/src/root/common.tt +++ b/src/root/common.tt @@ -464,4 +464,65 @@ BLOCK makeStar %] [% IF starred; "★"; ELSE; "☆"; END %] [% END; + +BLOCK renderJobsetOverview %] + + + + + + + + + + + + [% FOREACH j IN jobsets %] + [% successrate = 0 %] + + + + + + [% IF j.get_column('nrtotal') > 0 %] + [% successrate = ( j.get_column('nrsucceeded') / j.get_column('nrtotal') )*100 %] + [% IF j.get_column('nrscheduled') > 0 %] + [% class = 'label' %] + [% ELSIF successrate < 25 %] + [% class = 'label label-important' %] + [% ELSIF successrate < 75 %] + [% class = 'label label-warning' %] + [% ELSIF successrate <= 100 %] + [% class = 'label label-success' %] + [% END %] + [% END %] + + + + [% END %] + +
    NameDescriptionLast evaluatedSuccess
    + [% IF j.get_column('nrscheduled') > 0 %] + Scheduled + [% ELSIF j.get_column('nrfailed') == 0 %] + Succeeded + [% ELSIF j.get_column('nrfailed') > 0 && j.get_column('nrsucceeded') > 0 %] + Some Failed + [% ELSE %] + All Failed + [% END %] + [% IF showProject; INCLUDE renderFullJobsetName project=j.get_column('project') jobset=j.name inRow=1; ELSE; INCLUDE renderJobsetName project=j.get_column('project') jobset=j.name inRow=1; END %][% HTML.escape(j.description) %][% IF j.lastcheckedtime; INCLUDE renderDateTime timestamp = j.lastcheckedtime; ELSE; "-"; END %][% successrate FILTER format('%d') %]% + [% IF j.get_column('nrsucceeded') > 0 %] + [% j.get_column('nrsucceeded') %] + [% END %] + [% IF j.get_column('nrfailed') > 0 %] + [% j.get_column('nrfailed') %] + [% END %] + [% IF j.get_column('nrscheduled') > 0 %] + [% j.get_column('nrscheduled') %] + [% END %] +
    +[% END; + + %] diff --git a/src/root/dashboard-my-jobs-tab.tt b/src/root/dashboard-my-jobs-tab.tt index 6dee16b5..a1e82612 100644 --- a/src/root/dashboard-my-jobs-tab.tt +++ b/src/root/dashboard-my-jobs-tab.tt @@ -11,7 +11,7 @@

    Below are the most recent builds of the [% builds.size %] jobs of which you ([% HTML.escape(user.emailaddress) %]) are a maintainer.

    - + [% INCLUDE renderBuildList %] [% END %] diff --git a/src/root/dashboard-my-jobsets-tab.tt b/src/root/dashboard-my-jobsets-tab.tt new file mode 100644 index 00000000..5d857236 --- /dev/null +++ b/src/root/dashboard-my-jobsets-tab.tt @@ -0,0 +1,12 @@ +[% PROCESS common.tt %] + +[% IF builds.size == 0 %] + +
    You are not the owner of any + jobset.
    + +[% ELSE %] + + [% INCLUDE renderJobsetOverview showProject=1 %] + +[% END %] diff --git a/src/root/dashboard.tt b/src/root/dashboard.tt index 2e30ea4b..07e48eb5 100644 --- a/src/root/dashboard.tt +++ b/src/root/dashboard.tt @@ -4,6 +4,7 @@
    @@ -41,6 +42,7 @@
    [% INCLUDE makeLazyTab tabName="tabs-my-jobs" uri=c.uri_for(c.controller('User').action_for('my_jobs_tab'), [user.username]) %] + [% INCLUDE makeLazyTab tabName="tabs-my-jobsets" uri=c.uri_for(c.controller('User').action_for('my_jobsets_tab'), [user.username]) %]
    diff --git a/src/root/project.tt b/src/root/project.tt index d88474dd..4e2dbd8f 100644 --- a/src/root/project.tt +++ b/src/root/project.tt @@ -30,64 +30,7 @@
    [% IF project.jobsets %]

    This project has the following jobsets:

    - - - - - - - - - - - - - [% FOREACH j IN jobsets %] - [% successrate = 0 %] - - - - - - [% IF j.get_column('nrtotal') > 0 %] - [% successrate = ( j.get_column('nrsucceeded') / j.get_column('nrtotal') )*100 %] - [% IF j.get_column('nrscheduled') > 0 %] - [% class = 'label' %] - [% ELSIF successrate < 25 %] - [% class = 'label label-important' %] - [% ELSIF successrate < 75 %] - [% class = 'label label-warning' %] - [% ELSIF successrate <= 100 %] - [% class = 'label label-success' %] - [% END %] - [% END %] - - - - [% END %] - -
    IdDescriptionLast evaluatedSuccess
    - [% IF j.get_column('nrscheduled') > 0 %] - Scheduled - [% ELSIF j.get_column('nrfailed') == 0 %] - Succeeded - [% ELSIF j.get_column('nrfailed') > 0 && j.get_column('nrsucceeded') > 0 %] - Some Failed - [% ELSE %] - All Failed - [% END %] - [% INCLUDE renderJobsetName project=project.name jobset=j.name inRow=1 %][% HTML.escape(j.description) %][% IF j.lastcheckedtime; INCLUDE renderDateTime timestamp = j.lastcheckedtime; ELSE; "-"; END %][% successrate FILTER format('%d') %]% - [% IF j.get_column('nrsucceeded') > 0 %] - [% j.get_column('nrsucceeded') %] - [% END %] - [% IF j.get_column('nrfailed') > 0 %] - [% j.get_column('nrfailed') %] - [% END %] - [% IF j.get_column('nrscheduled') > 0 %] - [% j.get_column('nrscheduled') %] - [% END %] -
    - + [% INCLUDE renderJobsetOverview %] [% ELSE %]

    No jobsets have been defined yet.

    [% END %] From 8a0200edd296d3157c16ee383f65ff614741655f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 5 Nov 2013 16:13:09 +0100 Subject: [PATCH 23/50] Doh --- src/root/dashboard-my-jobsets-tab.tt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/root/dashboard-my-jobsets-tab.tt b/src/root/dashboard-my-jobsets-tab.tt index 5d857236..ab4fff0c 100644 --- a/src/root/dashboard-my-jobsets-tab.tt +++ b/src/root/dashboard-my-jobsets-tab.tt @@ -1,6 +1,6 @@ [% PROCESS common.tt %] -[% IF builds.size == 0 %] +[% IF jobsets.size == 0 %]
    You are not the owner of any jobset.
    From 7a18e5f0c19c39187f8a24e129764160190a5862 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 11:41:55 +0000 Subject: [PATCH 24/50] Remove unused file --- src/script/hydra-create | 60 ----------------------------------------- 1 file changed, 60 deletions(-) delete mode 100755 src/script/hydra-create diff --git a/src/script/hydra-create b/src/script/hydra-create deleted file mode 100755 index d05c0c6c..00000000 --- a/src/script/hydra-create +++ /dev/null @@ -1,60 +0,0 @@ -#! @perl@ - -use strict; -use warnings; - -use Catalyst::ScriptRunner; -Catalyst::ScriptRunner->run('Hydra', 'Create'); - -1; - -=head1 NAME - -hydra_create.pl - Create a new Catalyst Component - -=head1 SYNOPSIS - -hydra_create.pl [options] model|view|controller name [helper] [options] - - Options: - --force don't create a .new file where a file to be created exists - --mechanize use Test::WWW::Mechanize::Catalyst for tests if available - --help display this help and exits - - Examples: - hydra_create.pl controller My::Controller - hydra_create.pl -mechanize controller My::Controller - hydra_create.pl view My::View - hydra_create.pl view HTML TT - hydra_create.pl model My::Model - hydra_create.pl model SomeDB DBIC::Schema MyApp::Schema create=dynamic\ - dbi:SQLite:/tmp/my.db - hydra_create.pl model AnotherDB DBIC::Schema MyApp::Schema create=static\ - [Loader opts like db_schema, naming] dbi:Pg:dbname=foo root 4321 - [connect_info opts like quote_char, name_sep] - - See also: - perldoc Catalyst::Manual - perldoc Catalyst::Manual::Intro - perldoc Catalyst::Helper::Model::DBIC::Schema - perldoc Catalyst::Model::DBIC::Schema - perldoc Catalyst::View::TT - -=head1 DESCRIPTION - -Create a new Catalyst Component. - -Existing component files are not overwritten. If any of the component files -to be created already exist the file will be written with a '.new' suffix. -This behavior can be suppressed with the C<-force> option. - -=head1 AUTHORS - -Catalyst Contributors, see Catalyst.pm - -=head1 COPYRIGHT - -This library is free software. You can redistribute it and/or modify -it under the same terms as Perl itself. - -=cut From 3315d1ea51019706366e0087c72a337c0f7596c8 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 11:42:49 +0000 Subject: [PATCH 25/50] Remove obsolete hydra-control script --- src/script/Makefile.am | 1 - src/script/hydra-control | 42 ---------------------------------------- 2 files changed, 43 deletions(-) delete mode 100755 src/script/hydra-control diff --git a/src/script/Makefile.am b/src/script/Makefile.am index 3994684c..508e5fe1 100644 --- a/src/script/Makefile.am +++ b/src/script/Makefile.am @@ -1,6 +1,5 @@ EXTRA_DIST = \ $(distributable_scripts) \ - hydra-control \ hydra-eval-guile-jobs.in distributable_scripts = \ diff --git a/src/script/hydra-control b/src/script/hydra-control deleted file mode 100755 index 7349e303..00000000 --- a/src/script/hydra-control +++ /dev/null @@ -1,42 +0,0 @@ -#! /bin/sh - -action="$1" - -if test -z "$HYDRA_DATA"; then - echo "Error: \$HYDRA_DATA is not set"; - exit 1 -fi - -if test "$action" = "start"; then - - hydra-server > $HYDRA_DATA/server.log 2>&1 & - echo $! > $HYDRA_DATA/server.pid - - hydra-evaluator > $HYDRA_DATA/evaluator.log 2>&1 & - echo $! > $HYDRA_DATA/evaluator.pid - - hydra-queue-runner > $HYDRA_DATA/queue-runner.log 2>&1 & - echo $! > $HYDRA_DATA/queue_runner.pid - -elif test "$action" = "stop"; then - - kill $(cat $HYDRA_DATA/server.pid) - kill $(cat $HYDRA_DATA/evaluator.pid) - kill $(cat $HYDRA_DATA/queue_runner.pid) - -elif test "$action" = "status"; then - - echo -n "Hydra web server... " - (kill -0 $(cat $HYDRA_DATA/server.pid) 2> /dev/null && echo "ok") || echo "not running" - - echo -n "Hydra evaluator... " - (kill -0 $(cat $HYDRA_DATA/evaluator.pid) 2> /dev/null && echo "ok") || echo "not running" - - echo -n "Hydra queue runner... " - (kill -0 $(cat $HYDRA_DATA/queue_runner.pid) 2> /dev/null && echo "ok") || echo "not running" - - -else - echo "Syntax: $0 [start|stop|status]" - exit 1 -fi From 55f9d2393397449c587837e541a7befadc31d0a4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 12:28:45 +0000 Subject: [PATCH 26/50] Add a command `hydra-create-user' for managing user accounts --- doc/manual/installation.xml | 17 +++---- src/script/Makefile.am | 1 + src/script/hydra-create-user | 88 ++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 8 deletions(-) create mode 100755 src/script/hydra-create-user diff --git a/doc/manual/installation.xml b/doc/manual/installation.xml index a1c410d5..4d1c6b2a 100644 --- a/doc/manual/installation.xml +++ b/doc/manual/installation.xml @@ -163,15 +163,16 @@ hydra-init - To add a user root with - admin privileges, execute: - -echo "INSERT INTO Users(userName, emailAddress, password) VALUES ('root', 'some@email.adress.com', '$(echo -n foobar | sha1sum | cut -c1-40)');" | psql hydra -echo "INSERT INTO UserRoles(userName, role) values('root', 'admin');" | psql hydra + To create projects, you need to create a user with + admin privileges. This can be done using + the command hydra-create-user: - For SQLite the same commands can be used, with psql - hydra replaced by sqlite3 - /path/to/hydra.sqlite. + +$ hydra-create-user alice --full-name 'Alice Q. User' \ + --email-address 'alice@example.org' --password foobar --role admin + + + Additional users can be created through the web interface. diff --git a/src/script/Makefile.am b/src/script/Makefile.am index 508e5fe1..c05c9a12 100644 --- a/src/script/Makefile.am +++ b/src/script/Makefile.am @@ -10,6 +10,7 @@ distributable_scripts = \ hydra-server \ hydra-update-gc-roots \ hydra-s3-backup-collect-garbage \ + hydra-create-user \ nix-prefetch-git \ nix-prefetch-bzr \ nix-prefetch-hg diff --git a/src/script/hydra-create-user b/src/script/hydra-create-user new file mode 100755 index 00000000..1fcf4728 --- /dev/null +++ b/src/script/hydra-create-user @@ -0,0 +1,88 @@ +#! /var/run/current-system/sw/bin/perl -w + +use strict; +use Hydra::Schema; +use Hydra::Helper::Nix; +use Hydra::Model::DB; +use Getopt::Long qw(:config gnu_getopt); +use Digest::SHA1 qw(sha1_hex); + +sub showHelp { + print < \$renameFrom, + "type=s" => \$type, + "full-name=s" => \$fullName, + "email-address=s" => \$emailAddress, + "password=s" => \$password, + "wipe-roles" => \$wipeRoles, + "role=s" => \@roles, + "help" => sub { showHelp() } + ) or exit 1; + +die "$0: one user name required\n" if scalar @ARGV != 1; +my $userName = $ARGV[0]; + +die "$0: type must be `hydra' or `persona'\n" + if defined $type && $type ne "hydra" && $type ne "persona"; + +my $db = Hydra::Model::DB->new(); + +txn_do($db, sub { + my $user = $db->resultset('Users')->find({ username => $renameFrom // $userName }); + if ($renameFrom) { + die "$0: user `$renameFrom' does not exist\n" unless $user; + $user->update({ username => $userName }); + } elsif ($user) { + print STDERR "updating existing user `$userName'\n"; + } else { + print STDERR "creating new user `$userName'\n"; + $user = $db->resultset('Users')->create( + { username => $userName, type => "hydra", emailaddress => "", password => "!" }); + } + + die "$0: Persona user names must be email addresses\n" + if $user->type eq "persona" && $userName !~ /\@/; + + $user->update({ type => $type }) if defined $type; + + $user->update({ fullname => $fullName eq "" ? undef : $fullName }) if defined $fullName; + + if ($user->type eq "persona") { + die "$0: Persona accounts do not have an explicitly set email address.\n" + if defined $emailAddress; + die "$0: Persona accounts do not have a password.\n" + if defined $password; + $user->update({ emailaddress => $userName, password => "!" }); + } else { + $user->update({ emailaddress => $emailAddress }) if defined $emailAddress; + $user->update({ password => sha1_hex($password) }) if defined $password; + } + + $user->userroles->delete if $wipeRoles; + $user->userroles->update_or_create({ role => $_ }) foreach @roles; +}); From 8928c35ecd7d68e4536381fb6c44a7ca4f4e9cde Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 13:32:56 +0100 Subject: [PATCH 27/50] Slight layout tweak --- src/root/layout.tt | 8 ++------ src/root/static/css/hydra.css | 18 +++++++++--------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/root/layout.tt b/src/root/layout.tt index fb4a2531..074e4809 100644 --- a/src/root/layout.tt +++ b/src/root/layout.tt @@ -65,28 +65,24 @@
    - [% IF flashMsg %] -
    [% flashMsg %]
    [% END %] [% IF successMsg %] -
    [% successMsg %]
    [% END %] [% IF errorMsg %] -
    Error: [% errorMsg %]
    [% END %] [% IF !hideHeader %] [% ELSE %] -
    + [% IF first %]
    [% first = 0; END; %] [% END %] [% content %] diff --git a/src/root/static/css/hydra.css b/src/root/static/css/hydra.css index 4fac2c3c..b369f3e0 100644 --- a/src/root/static/css/hydra.css +++ b/src/root/static/css/hydra.css @@ -2,6 +2,9 @@ body { padding-top: 40px; } + div.alert { + margin-top: 1em; + } } .logo { @@ -68,16 +71,13 @@ h3 { } div.page-header { - margin-top: 0em; + margin-top: 1em; margin-bottom: 1em; -} - -div.page-header h1 { - margin-bottom: 0em; -} - -div.page-header h1 small { - font-size: 45%; + padding-top: 0em; + padding-bottom: 0.5em; + color: #999999; + font-size: 123.809%; + font-weight: normal; } .shell-prompt { From d118a575e19e0b6d0c9f71e9d393e4e05cbe0fa0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 14:11:56 +0100 Subject: [PATCH 28/50] Use page-header on the front page --- src/root/overview.tt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/root/overview.tt b/src/root/overview.tt index 9b31aeb5..ebc4cea7 100644 --- a/src/root/overview.tt +++ b/src/root/overview.tt @@ -13,7 +13,7 @@
    [% END %] -

    Projects

    + [% IF projects.size != 0 %] From b44baf374666897976204b4a24e39b569dbd1f9e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 14:38:36 +0100 Subject: [PATCH 29/50] Add a flag to enable Persona support It's disabled by default. To enable, add the following to hydra.conf: enable_persona = 1 --- src/lib/Hydra/Controller/Root.pm | 1 + src/lib/Hydra/Controller/User.pm | 2 ++ src/root/layout.tt | 15 +++++++++------ src/root/static/css/hydra.css | 2 +- src/root/static/js/common.js | 1 + src/root/topbar.tt | 22 ++++++++++++++-------- 6 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/lib/Hydra/Controller/Root.pm b/src/lib/Hydra/Controller/Root.pm index cbdba121..9ca2c81a 100644 --- a/src/lib/Hydra/Controller/Root.pm +++ b/src/lib/Hydra/Controller/Root.pm @@ -24,6 +24,7 @@ sub begin :Private { $c->stash->{tracker} = $ENV{"HYDRA_TRACKER"}; $c->stash->{flashMsg} = $c->flash->{flashMsg}; $c->stash->{successMsg} = $c->flash->{successMsg}; + $c->stash->{personaEnabled} = $c->config->{enable_persona} // "0" eq "1"; if (scalar(@args) == 0 || $args[0] ne "static") { $c->stash->{nrRunningBuilds} = $c->model('DB::Builds')->search({ finished => 0, busy => 1 }, {})->count(); diff --git a/src/lib/Hydra/Controller/User.pm b/src/lib/Hydra/Controller/User.pm index 66b645b2..024ddd2f 100644 --- a/src/lib/Hydra/Controller/User.pm +++ b/src/lib/Hydra/Controller/User.pm @@ -48,6 +48,8 @@ sub persona_login :Path('/persona-login') Args(0) { my ($self, $c) = @_; requirePost($c); + error($c, "Persona support is not enabled.") unless $c->stash->{personaEnabled}; + my $assertion = $c->req->params->{assertion} or die; my $ua = new LWP::UserAgent; diff --git a/src/root/layout.tt b/src/root/layout.tt index 074e4809..933fe893 100644 --- a/src/root/layout.tt +++ b/src/root/layout.tt @@ -101,11 +101,13 @@ @@ -124,7 +126,8 @@ url: "[% c.uri_for('/persona-login') %]", data: "assertion=" + assertion, type: 'POST', - success: function(data) { window.location.reload(); } + success: function(data) { window.location.reload(); }, + postError: function() { navigator.id.logout(); } }); }, onlogout: doLogout diff --git a/src/root/static/css/hydra.css b/src/root/static/css/hydra.css index b369f3e0..6f65c649 100644 --- a/src/root/static/css/hydra.css +++ b/src/root/static/css/hydra.css @@ -107,4 +107,4 @@ td.nowrap { .star:hover { cursor: pointer; -} \ No newline at end of file +} diff --git a/src/root/static/js/common.js b/src/root/static/js/common.js index 784c2ed3..68a65d2a 100644 --- a/src/root/static/js/common.js +++ b/src/root/static/js/common.js @@ -135,6 +135,7 @@ function requestJSON(args) { bootbox.alert("Server error: " + escapeHTML(data.responseText)); else bootbox.alert("Unknown server error!"); + if (args.postError) args.postError(data); }; return $.ajax(args); }; diff --git a/src/root/topbar.tt b/src/root/topbar.tt index 80895e67..6e3cd264 100644 --- a/src/root/topbar.tt +++ b/src/root/topbar.tt @@ -123,15 +123,21 @@ Sign out [% ELSE %] - [% WRAPPER makeSubMenu title="Sign in" %] + [% IF personaEnabled %] + [% WRAPPER makeSubMenu title="Sign in" %] +
  • + + Sign in with Persona + +
  • +
  • +
  • + Sign in with a Hydra account +
  • + [% END %] + [% ELSE %]
  • - - Sign in with Persona - -
  • -
  • -
  • - Sign in with a Hydra account + Sign in
  • [% END %] [% END %] From 3c45992182b4a4e82f4c06dbb3b009de75d4e6d2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 14:40:30 +0100 Subject: [PATCH 30/50] Make the logo configurable via hydra.conf --- src/lib/Hydra/Controller/Root.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Hydra/Controller/Root.pm b/src/lib/Hydra/Controller/Root.pm index 9ca2c81a..f8ac88ed 100644 --- a/src/lib/Hydra/Controller/Root.pm +++ b/src/lib/Hydra/Controller/Root.pm @@ -20,7 +20,7 @@ sub begin :Private { $c->stash->{version} = $ENV{"HYDRA_RELEASE"} || ""; $c->stash->{nixVersion} = $ENV{"NIX_RELEASE"} || ""; $c->stash->{curTime} = time; - $c->stash->{logo} = $ENV{"HYDRA_LOGO"} ? "/logo" : ""; + $c->stash->{logo} = $c->config->{hydra_logo} // ($ENV{"HYDRA_LOGO"} ? "/logo" : ""); $c->stash->{tracker} = $ENV{"HYDRA_TRACKER"}; $c->stash->{flashMsg} = $c->flash->{flashMsg}; $c->stash->{successMsg} = $c->flash->{successMsg}; From df1a33c63f8cec1ef734b5b2a508623ea15f3e4d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 14:50:21 +0100 Subject: [PATCH 31/50] Fix layout at all page sizes --- src/root/layout.tt | 2 ++ src/root/static/css/hydra.css | 15 +++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/root/layout.tt b/src/root/layout.tt index 933fe893..bee4d8a7 100644 --- a/src/root/layout.tt +++ b/src/root/layout.tt @@ -65,6 +65,8 @@
    +
    + [% IF flashMsg %]
    [% flashMsg %]
    [% END %] diff --git a/src/root/static/css/hydra.css b/src/root/static/css/hydra.css index 6f65c649..59ef91c1 100644 --- a/src/root/static/css/hydra.css +++ b/src/root/static/css/hydra.css @@ -1,9 +1,12 @@ -@media (min-width: 768px) { - body { - padding-top: 40px; - } - div.alert { - margin-top: 1em; +div.skip-topbar { + padding-top: 40px; + margin-bottom: 1.5em; +} + +@media (max-width: 979px) { + div.skip-topbar { + padding-top: 0; + margin-bottom: 0; } } From 33f01da50779f34598dba068067c7f956c9122f6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 15:10:26 +0100 Subject: [PATCH 32/50] Fix hydra_logo setting --- src/lib/Hydra/Controller/Root.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/Hydra/Controller/Root.pm b/src/lib/Hydra/Controller/Root.pm index f8ac88ed..3071bb8c 100644 --- a/src/lib/Hydra/Controller/Root.pm +++ b/src/lib/Hydra/Controller/Root.pm @@ -20,7 +20,7 @@ sub begin :Private { $c->stash->{version} = $ENV{"HYDRA_RELEASE"} || ""; $c->stash->{nixVersion} = $ENV{"NIX_RELEASE"} || ""; $c->stash->{curTime} = time; - $c->stash->{logo} = $c->config->{hydra_logo} // ($ENV{"HYDRA_LOGO"} ? "/logo" : ""); + $c->stash->{logo} = ($c->config->{hydra_logo} // $ENV{"HYDRA_LOGO"}) ? "/logo" : ""; $c->stash->{tracker} = $ENV{"HYDRA_TRACKER"}; $c->stash->{flashMsg} = $c->flash->{flashMsg}; $c->stash->{successMsg} = $c->flash->{successMsg}; @@ -274,7 +274,7 @@ sub narinfo :LocalRegex('^([a-z0-9]+).narinfo$') :Args(0) { sub logo :Local { my ($self, $c) = @_; - my $path = $ENV{"HYDRA_LOGO"} or die("Logo not set!"); + my $path = $c->config->{hydra_logo} // $ENV{"HYDRA_LOGO"} // die("Logo not set!"); $c->serve_static_file($path); } From 5723fda7fc1b3a246a8f5e287e0e01e3fae71fcd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 15:14:31 +0100 Subject: [PATCH 33/50] Keep session state across hydra-server restarts --- src/lib/Hydra.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/Hydra.pm b/src/lib/Hydra.pm index 42da5f65..edcecb4d 100644 --- a/src/lib/Hydra.pm +++ b/src/lib/Hydra.pm @@ -54,7 +54,8 @@ __PACKAGE__->config( }, 'Plugin::Session' => { expires => 3600 * 24 * 2, - storage => Hydra::Model::DB::getHydraPath . "/session_data" + storage => Hydra::Model::DB::getHydraPath . "/session_data", + unlink_on_exit => 0 }, 'Plugin::AccessLog' => { formatter => { From e8d92445d7835e290e34b37a1e1ca86f8a79fe6a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 15:15:35 +0100 Subject: [PATCH 34/50] Keep sessions for a week --- src/lib/Hydra.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Hydra.pm b/src/lib/Hydra.pm index edcecb4d..6bef5cd8 100644 --- a/src/lib/Hydra.pm +++ b/src/lib/Hydra.pm @@ -53,7 +53,7 @@ __PACKAGE__->config( expose_stash => 'json' }, 'Plugin::Session' => { - expires => 3600 * 24 * 2, + expires => 3600 * 24 * 7, storage => Hydra::Model::DB::getHydraPath . "/session_data", unlink_on_exit => 0 }, From 60e7e4fbe9fb415c3744f3a97522f1f1e728e35a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 15:20:30 +0100 Subject: [PATCH 35/50] Remove obsolete/broken timeline feature --- src/lib/Hydra/Controller/Root.pm | 14 ------- src/root/timeline.tt | 64 -------------------------------- 2 files changed, 78 deletions(-) delete mode 100644 src/root/timeline.tt diff --git a/src/lib/Hydra/Controller/Root.pm b/src/lib/Hydra/Controller/Root.pm index 3071bb8c..ad34ae40 100644 --- a/src/lib/Hydra/Controller/Root.pm +++ b/src/lib/Hydra/Controller/Root.pm @@ -76,20 +76,6 @@ sub queue_GET { } -sub timeline :Local { - my ($self, $c) = @_; - my $pit = time(); - $c->stash->{pit} = $pit; - $pit = $pit-(24*60*60)-1; - - $c->stash->{template} = 'timeline.tt'; - $c->stash->{builds} = [ $c->model('DB::Builds')->search - ( { finished => 1, stoptime => { '>' => $pit } } - , { order_by => ["starttime"] } - ) ]; -} - - sub status :Local :Args(0) :ActionClass('REST') { } sub status_GET { diff --git a/src/root/timeline.tt b/src/root/timeline.tt deleted file mode 100644 index 2f665ad0..00000000 --- a/src/root/timeline.tt +++ /dev/null @@ -1,64 +0,0 @@ -[% USE date %] - -[% WRAPPER layout.tt title="Timeline" %] - -[% PROCESS common.tt %] - - - - - - - -
    - - -[% END %] From 8c2acdd29406eab82dd76eb39e44705cab50986c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 14:29:55 +0000 Subject: [PATCH 36/50] make clean: Delete darcs-repo --- tests/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Makefile.am b/tests/Makefile.am index ab848d85..f5f88488 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -27,7 +27,7 @@ TESTS = \ clean: chmod -R a+w nix || true - rm -rf db.sqlite data nix git-repo hg-repo svn-repo svn-checkout svn-checkout-repo bzr-repo bzr-checkout-repo + rm -rf db.sqlite data nix git-repo hg-repo svn-repo svn-checkout svn-checkout-repo bzr-repo bzr-checkout-repo darcs-repo rm -f .*-state check_SCRIPTS = db.sqlite repos From bb2976693c126473b74fd0ec48881b73d7444af2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 15:37:06 +0100 Subject: [PATCH 37/50] Use hydra-init / hydra-create-user in the tests --- release.nix | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/release.nix b/release.nix index 2be29519..5c3757ab 100644 --- a/release.nix +++ b/release.nix @@ -160,18 +160,20 @@ in rec { }; testScript = + let dbi = "dbi:Pg:dbname=hydra;user=root;"; in '' $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" + ( "createdb -O root hydra" + , "HYDRA_DBI='${dbi}' hydra-init" + , "HYDRA_DBI='${dbi}' hydra-init" # again to test idempotence , "mkdir /var/lib/hydra" ); # Start the web interface. - $machine->mustSucceed("HYDRA_DATA=/var/lib/hydra HYDRA_DBI='dbi:Pg:dbname=hydra;user=hydra;' hydra-server >&2 &"); + $machine->mustSucceed("HYDRA_DATA=/var/lib/hydra HYDRA_DBI='${dbi}' hydra-server >&2 &"); $machine->waitForOpenPort("3000"); ''; }); @@ -190,16 +192,16 @@ in rec { }; testScript = + let dbi = "dbi:Pg:dbname=hydra;user=root;"; in '' $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" + , "HYDRA_DBI='${dbi}' hydra-init" + , "HYDRA_DBI='${dbi}' hydra-create-user root --email-address 'e.dolstra\@tudelft.nl' --password foobar --role admin" , "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" @@ -207,7 +209,7 @@ in rec { ); # 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->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}' LOGNAME=root DBIC_TRACE=1 hydra-server -d >&2 &"); $machine->waitForOpenPort("3000"); $machine->mustSucceed("perl ${./tests/api-test.pl} >&2"); From 4fb3d67f49668bf960fd0026a5f13ed5e72c61ca Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 16:10:27 +0100 Subject: [PATCH 38/50] Make /login give a proper JSON response again --- src/lib/Hydra/Controller/User.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Hydra/Controller/User.pm b/src/lib/Hydra/Controller/User.pm index 024ddd2f..6a1a8552 100644 --- a/src/lib/Hydra/Controller/User.pm +++ b/src/lib/Hydra/Controller/User.pm @@ -30,7 +30,7 @@ sub login_POST { accessDenied($c, "Bad username or password.") if !$c->authenticate({username => $username, password => $password}); - $self->status_no_content($c); + currentUser_GET($self, $c); } From 64cbdddea8934732e58e8b44e81289bce0fecf23 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 16:10:47 +0100 Subject: [PATCH 39/50] Test whether incorrect passwords are rejected --- tests/api-test.pl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/api-test.pl b/tests/api-test.pl index f9068dd4..efbbf0fc 100644 --- a/tests/api-test.pl +++ b/tests/api-test.pl @@ -1,6 +1,6 @@ use LWP::UserAgent; use JSON; -use Test::Simple tests => 16; +use Test::Simple tests => 17; my $ua = LWP::UserAgent->new; $ua->cookie_jar({}); @@ -17,6 +17,9 @@ sub request_json { return $res; } +my $result = request_json({ uri => "/login", method => "POST", data => { username => "root", password => "wrong" } }); +ok($result->code() == 403, "Incorrect password rejected."); + my $result = request_json({ uri => "/login", method => "POST", data => { username => "root", password => "foobar" } }); my $user = decode_json($result->content()); From 9dc3311ff7c3c81bc6391a3bca3694dd06d26a73 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 16:15:53 +0100 Subject: [PATCH 40/50] hydra-module.nix: Don't create an admin user It's the user's job to create users with the appropriate roles. --- hydra-module.nix | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hydra-module.nix b/hydra-module.nix index 1b8d8663..e5d12c32 100644 --- a/hydra-module.nix +++ b/hydra-module.nix @@ -194,12 +194,6 @@ in mv ${baseDir}/.pgpass-tmp ${baseDir}/.pgpass fi ${pkgs.shadow}/bin/su hydra -c ${cfg.hydra}/bin/hydra-init - ${config.services.postgresql.package}/bin/psql hydra << EOF - BEGIN; - INSERT INTO Users(userName, emailAddress, password) VALUES ('admin', '${cfg.notificationSender}', '$(echo -n $pass | sha1sum | cut -c1-40)'); - INSERT INTO UserRoles(userName, role) values('admin', 'admin'); - COMMIT; - EOF ''; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; From 6fc59aa0dac16e7a6fe6d876f58fac0a27fc6ed2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 16:17:11 +0100 Subject: [PATCH 41/50] hydra-module.nix: Remove the useWAL option This is not a Hydra option but a Nix option so it shouldn't be here. --- hydra-module.nix | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/hydra-module.nix b/hydra-module.nix index e5d12c32..2fa27629 100644 --- a/hydra-module.nix +++ b/hydra-module.nix @@ -18,7 +18,7 @@ let OPENSSL_X509_CERT_FILE = "/etc/ssl/certs/ca-bundle.crt"; GIT_SSL_CAINFO = "/etc/ssl/certs/ca-bundle.crt"; }; - + serverEnv = env // { HYDRA_LOGO = if cfg.logo != null then cfg.logo else ""; HYDRA_TRACKER = cfg.tracker; @@ -108,13 +108,6 @@ in ''; }; - useWAL = mkOption { - default = true; - description = '' - Whether to use SQLite's Write-Ahead Logging, which may improve performance. - ''; - }; - debugServer = mkOption { default = false; type = types.bool; @@ -163,12 +156,10 @@ in build-cache-failure = true build-poll-interval = 10 - + # Online log compression makes it impossible to get the tail of # builds that are in progress. build-compress-log = false - - use-sqlite-wal = ${if cfg.useWAL then "true" else "false"} ''; systemd.services."hydra-init" = @@ -198,7 +189,7 @@ in serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; }; - + systemd.services."hydra-server" = { wantedBy = [ "multi-user.target" ]; wants = [ "hydra-init.service" ]; From ed69106c3454addfd51ad94a30478d0ee0debc07 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 16:34:16 +0100 Subject: [PATCH 42/50] hydra-module.nix: Give types to options Also, rename the "service.hydra.hydra" option to "service.hydra.package" to be consistent with NixOS option naming conventions. --- hydra-module.nix | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/hydra-module.nix b/hydra-module.nix index 2fa27629..455526ee 100644 --- a/hydra-module.nix +++ b/hydra-module.nix @@ -31,6 +31,7 @@ in services.hydra = rec { enable = mkOption { + type = types.bool; default = false; description = '' Whether to run Hydra services. @@ -38,27 +39,28 @@ in }; dbi = mkOption { + type = types.string; default = "dbi:Pg:dbname=hydra;host=localhost;user=hydra;"; - example = "dbi:SQLite:/home/hydra/db/hydra.sqlite"; description = '' The DBI string for Hydra database connection. ''; }; - hydra = mkOption { + package = mkOption { + type = types.path; #default = pkgs.hydra; - description = '' - Location of hydra - ''; + description = "The Hydra package."; }; hydraURL = mkOption { + type = types.str; description = '' The base URL for the Hydra webserver instance. Used for links in emails. ''; }; listenHost = mkOption { + type = types.str; default = "*"; example = "localhost"; description = '' @@ -68,6 +70,7 @@ in }; port = mkOption { + type = types.int; default = 3000; description = '' TCP port the web server should listen to. @@ -75,26 +78,30 @@ in }; minimumDiskFree = mkOption { + type = types.int; default = 5; description = '' - Threshold of minimum disk space (G) to determine if queue runner should run or not. + Threshold of minimum disk space (GiB) to determine if queue runner should run or not. ''; }; minimumDiskFreeEvaluator = mkOption { + type = types.int; default = 2; description = '' - Threshold of minimum disk space (G) to determine if evaluator should run or not. + Threshold of minimum disk space (GiB) to determine if evaluator should run or not. ''; }; notificationSender = mkOption { + type = types.str; description = '' Sender email address used for email notifications. ''; }; tracker = mkOption { + type = types.str; default = ""; description = '' Piece of HTML that is included on all pages. @@ -102,6 +109,7 @@ in }; logo = mkOption { + type = types.nullOr types.str; default = null; description = '' File name of an alternate logo to be displayed on the web pages. @@ -109,8 +117,8 @@ in }; debugServer = mkOption { - default = false; type = types.bool; + default = false; description = "Whether to run the server in debug mode"; }; @@ -135,7 +143,7 @@ in max_servers 25 ''; - environment.systemPackages = [ cfg.hydra ]; + environment.systemPackages = [ cfg.package ]; users.extraUsers.hydra = { description = "Hydra"; @@ -184,7 +192,7 @@ in chmod 600 ${baseDir}/.pgpass-tmp mv ${baseDir}/.pgpass-tmp ${baseDir}/.pgpass fi - ${pkgs.shadow}/bin/su hydra -c ${cfg.hydra}/bin/hydra-init + ${pkgs.shadow}/bin/su hydra -c ${cfg.package}/bin/hydra-init ''; serviceConfig.Type = "oneshot"; serviceConfig.RemainAfterExit = true; @@ -196,7 +204,7 @@ in after = [ "hydra-init.service" ]; environment = serverEnv; serviceConfig = - { ExecStart = "@${cfg.hydra}/bin/hydra-server hydra-server -f -h '${cfg.listenHost}' --max_spare_servers 5 --max_servers 25 --max_requests 100${optionalString cfg.debugServer " -d"}"; + { ExecStart = "@${cfg.package}/bin/hydra-server hydra-server -f -h '${cfg.listenHost}' --max_spare_servers 5 --max_servers 25 --max_requests 100${optionalString cfg.debugServer " -d"}"; User = "hydra"; Restart = "always"; }; @@ -209,8 +217,8 @@ in path = [ pkgs.nettools ]; environment = env; serviceConfig = - { ExecStartPre = "${cfg.hydra}/bin/hydra-queue-runner --unlock"; - ExecStart = "@${cfg.hydra}/bin/hydra-queue-runner hydra-queue-runner"; + { ExecStartPre = "${cfg.package}/bin/hydra-queue-runner --unlock"; + ExecStart = "@${cfg.package}/bin/hydra-queue-runner hydra-queue-runner"; User = "hydra"; Restart = "always"; }; @@ -223,7 +231,7 @@ in path = [ pkgs.nettools ]; environment = env; serviceConfig = - { ExecStart = "@${cfg.hydra}/bin/hydra-evaluator hydra-evaluator"; + { ExecStart = "@${cfg.package}/bin/hydra-evaluator hydra-evaluator"; User = "hydra"; Restart = "always"; }; @@ -234,7 +242,7 @@ in after = [ "hydra-init.service" ]; environment = env; serviceConfig = - { ExecStart = "@${cfg.hydra}/bin/hydra-update-gc-roots hydra-update-gc-roots"; + { ExecStart = "@${cfg.package}/bin/hydra-update-gc-roots hydra-update-gc-roots"; User = "hydra"; }; }; From 1f6304c876513107196c890a20b6902e31eb9e18 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 17:07:25 +0100 Subject: [PATCH 43/50] hydra-module.nix: Don't use a password If PostgreSQL is running on the same system, then the "hydra" user can can connect without a password (via Unix domain socket authentication), so no need to set up a password. If PostgreSQL is on another machine, then creating a user/database won't work anyway. --- hydra-module.nix | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/hydra-module.nix b/hydra-module.nix index 455526ee..31b07b44 100644 --- a/hydra-module.nix +++ b/hydra-module.nix @@ -40,7 +40,8 @@ in dbi = mkOption { type = types.string; - default = "dbi:Pg:dbname=hydra;host=localhost;user=hydra;"; + default = "dbi:Pg:dbname=hydra;user=hydra;"; + example = "dbi:Pg:dbname=hydra;host=postgres.example.org;user=foo;"; description = '' The DBI string for Hydra database connection. ''; @@ -179,19 +180,13 @@ in mkdir -p ${baseDir}/data chown hydra ${baseDir}/data ln -sf ${hydraConf} ${baseDir}/data/hydra.conf - pass=$(HOME=/root ${pkgs.openssl}/bin/openssl rand -base64 32) - if [ ! -f ${baseDir}/.pgpass ]; then - ${config.services.postgresql.package}/bin/psql postgres << EOF - CREATE USER hydra PASSWORD '$pass'; - EOF + ${optionalString (cfg.dbi == "dbi:Pg:dbname=hydra;user=hydra;") '' + if ! [ -e ${baseDir}/.db-created ]; then + ${config.services.postgresql.package}/bin/createuser hydra ${config.services.postgresql.package}/bin/createdb -O hydra hydra - cat > ${baseDir}/.pgpass-tmp << EOF - localhost:*:hydra:hydra:$pass - EOF - chown hydra ${baseDir}/.pgpass-tmp - chmod 600 ${baseDir}/.pgpass-tmp - mv ${baseDir}/.pgpass-tmp ${baseDir}/.pgpass - fi + touch ${baseDir}/.db-created + fi + ''} ${pkgs.shadow}/bin/su hydra -c ${cfg.package}/bin/hydra-init ''; serviceConfig.Type = "oneshot"; From f4845cde0001dd3e87618a5b8812203f80e2ca55 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 17:09:24 +0100 Subject: [PATCH 44/50] hydra-module.nix: Make services require hydra-init --- hydra-module.nix | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hydra-module.nix b/hydra-module.nix index 31b07b44..44e0edec 100644 --- a/hydra-module.nix +++ b/hydra-module.nix @@ -195,7 +195,7 @@ in systemd.services."hydra-server" = { wantedBy = [ "multi-user.target" ]; - wants = [ "hydra-init.service" ]; + requires = [ "hydra-init.service" ]; after = [ "hydra-init.service" ]; environment = serverEnv; serviceConfig = @@ -207,7 +207,7 @@ in systemd.services."hydra-queue-runner" = { wantedBy = [ "multi-user.target" ]; - wants = [ "hydra-init.service" ]; + requires = [ "hydra-init.service" ]; after = [ "hydra-init.service" "network.target" ]; path = [ pkgs.nettools ]; environment = env; @@ -221,7 +221,7 @@ in systemd.services."hydra-evaluator" = { wantedBy = [ "multi-user.target" ]; - wants = [ "hydra-init.service" ]; + requires = [ "hydra-init.service" ]; after = [ "hydra-init.service" "network.target" ]; path = [ pkgs.nettools ]; environment = env; @@ -233,7 +233,7 @@ in }; systemd.services."hydra-update-gc-roots" = - { wants = [ "hydra-init.service" ]; + { requires = [ "hydra-init.service" ]; after = [ "hydra-init.service" ]; environment = env; serviceConfig = From 243b05f1e7d3ccc40167b32c6c9e20b2695cda32 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 17:19:57 +0100 Subject: [PATCH 45/50] hydra-module.nix: Use -p instead of HYDRA_PORT --- hydra-module.nix | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hydra-module.nix b/hydra-module.nix index 44e0edec..56d705a7 100644 --- a/hydra-module.nix +++ b/hydra-module.nix @@ -14,7 +14,6 @@ let HYDRA_DBI = cfg.dbi; HYDRA_CONFIG = "${baseDir}/data/hydra.conf"; HYDRA_DATA = "${baseDir}/data"; - HYDRA_PORT = "${toString cfg.port}"; OPENSSL_X509_CERT_FILE = "/etc/ssl/certs/ca-bundle.crt"; GIT_SSL_CAINFO = "/etc/ssl/certs/ca-bundle.crt"; }; @@ -199,7 +198,10 @@ in after = [ "hydra-init.service" ]; environment = serverEnv; serviceConfig = - { ExecStart = "@${cfg.package}/bin/hydra-server hydra-server -f -h '${cfg.listenHost}' --max_spare_servers 5 --max_servers 25 --max_requests 100${optionalString cfg.debugServer " -d"}"; + { ExecStart = + "@${cfg.package}/bin/hydra-server hydra-server -f -h '${cfg.listenHost}' " + + "-p ${toString cfg.port} --max_spare_servers 5 --max_servers 25 " + + "--max_requests 100 ${optionalString cfg.debugServer "-d"}"; User = "hydra"; Restart = "always"; }; From d710c1e4438623baeb0e910a928fe0604935ce63 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 17:27:36 +0100 Subject: [PATCH 46/50] hydra-module.nix: Fix setting the logo --- hydra-module.nix | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hydra-module.nix b/hydra-module.nix index 56d705a7..146df7af 100644 --- a/hydra-module.nix +++ b/hydra-module.nix @@ -19,14 +19,14 @@ let }; serverEnv = env // - { HYDRA_LOGO = if cfg.logo != null then cfg.logo else ""; - HYDRA_TRACKER = cfg.tracker; + { HYDRA_TRACKER = cfg.tracker; } // (optionalAttrs cfg.debugServer { DBIC_TRACE = 1; }); in { ###### interface options = { + services.hydra = rec { enable = mkOption { @@ -109,7 +109,7 @@ in }; logo = mkOption { - type = types.nullOr types.str; + type = types.nullOr types.path; default = null; description = '' File name of an alternate logo to be displayed on the web pages. @@ -135,12 +135,16 @@ in ###### implementation config = mkIf cfg.enable { + services.hydra.extraConfig = '' using_frontend_proxy 1 base_uri ${cfg.hydraURL} notification_sender ${cfg.notificationSender} max_servers 25 + ${optionalString (cfg.logo != null) '' + hydra_logo ${cfg.logo} + ''} ''; environment.systemPackages = [ cfg.package ]; From 2614fe21fb9b075bf3ff227f7df1e2c9ea9f47fa Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 17:29:22 +0100 Subject: [PATCH 47/50] hydra-module.nix: Use startAt --- hydra-module.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hydra-module.nix b/hydra-module.nix index 146df7af..6fd1fd9f 100644 --- a/hydra-module.nix +++ b/hydra-module.nix @@ -246,6 +246,7 @@ in { ExecStart = "@${cfg.package}/bin/hydra-update-gc-roots hydra-update-gc-roots"; User = "hydra"; }; + startAt = "02:15"; }; services.cron.systemCronJobs = @@ -272,7 +273,6 @@ in in [ "*/5 * * * * root ${checkSpace} &> ${baseDir}/data/checkspace.log" "15 5 * * * root ${compressLogs} &> ${baseDir}/data/compress.log" - "15 2 * * * root ${pkgs.systemd}/bin/systemctl start hydra-update-gc-roots.service" ]; }; } From ed9d57d0a49a9397807352bcda4b85c1d0f9596a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 17:29:34 +0100 Subject: [PATCH 48/50] hydra-module.nix: More paranoid permissions on the data directory --- hydra-module.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hydra-module.nix b/hydra-module.nix index 6fd1fd9f..a667bd4d 100644 --- a/hydra-module.nix +++ b/hydra-module.nix @@ -180,7 +180,7 @@ in after = [ "postgresql.service" ]; environment = env; script = '' - mkdir -p ${baseDir}/data + mkdir -m 0700 -p ${baseDir}/data chown hydra ${baseDir}/data ln -sf ${hydraConf} ${baseDir}/data/hydra.conf ${optionalString (cfg.dbi == "dbi:Pg:dbname=hydra;user=hydra;") '' From 8b67ad87872726b7e7ec3face1328bb913795651 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 17:32:50 +0100 Subject: [PATCH 49/50] hydra-module.nix: Export the HYDRA_* environment variables This is useful if you want to run hydra-* commands manually. --- hydra-module.nix | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/hydra-module.nix b/hydra-module.nix index a667bd4d..8034ef77 100644 --- a/hydra-module.nix +++ b/hydra-module.nix @@ -9,14 +9,17 @@ let hydraConf = pkgs.writeScript "hydra.conf" cfg.extraConfig; - env = - { NIX_REMOTE = "daemon"; - HYDRA_DBI = cfg.dbi; + hydraEnv = + { HYDRA_DBI = cfg.dbi; HYDRA_CONFIG = "${baseDir}/data/hydra.conf"; HYDRA_DATA = "${baseDir}/data"; + }; + + env = + { NIX_REMOTE = "daemon"; OPENSSL_X509_CERT_FILE = "/etc/ssl/certs/ca-bundle.crt"; GIT_SSL_CAINFO = "/etc/ssl/certs/ca-bundle.crt"; - }; + } // hydraEnv; serverEnv = env // { HYDRA_TRACKER = cfg.tracker; @@ -149,6 +152,8 @@ in environment.systemPackages = [ cfg.package ]; + environment.variables = hydraEnv; + users.extraUsers.hydra = { description = "Hydra"; home = baseDir; From c92410c147120b67989408ff1e7fc4650c3ba3f2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 6 Nov 2013 18:10:52 +0100 Subject: [PATCH 50/50] Use hydra-module.nix in the tests --- release.nix | 85 ++++++++++++++++++++++------------------------- tests/api-test.pl | 6 ++-- 2 files changed, 42 insertions(+), 49 deletions(-) diff --git a/release.nix b/release.nix index 5c3757ab..6004c88d 100644 --- a/release.nix +++ b/release.nix @@ -8,6 +8,23 @@ let genAttrs' = pkgs.lib.genAttrs [ "x86_64-linux" ]; + hydraServer = hydraPkg: + { config, pkgs, ... }: + { imports = [ ./hydra-module.nix ]; + + virtualisation.memorySize = 1024; + + services.hydra.enable = true; + services.hydra.package = hydraPkg; + services.hydra.hydraURL = "http://hydra.example.org"; + services.hydra.notificationSender = "admin@hydra.example.org"; + + services.postgresql.enable = true; + services.postgresql.package = pkgs.postgresql92; + + environment.systemPackages = [ pkgs.perlPackages.LWP pkgs.perlPackages.JSON ]; + }; + in rec { tarball = @@ -150,69 +167,45 @@ in rec { tests.install = genAttrs' (system: with import { 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 ]; - }; - + machine = hydraServer (builtins.getAttr system build); # build.${system} testScript = - let dbi = "dbi:Pg:dbname=hydra;user=root;"; in '' - $machine->waitForJob("postgresql"); - - # Initialise the database and the state. - $machine->mustSucceed - ( "createdb -O root hydra" - , "HYDRA_DBI='${dbi}' hydra-init" - , "HYDRA_DBI='${dbi}' hydra-init" # again to test idempotence - , "mkdir /var/lib/hydra" - ); - - # Start the web interface. - $machine->mustSucceed("HYDRA_DATA=/var/lib/hydra HYDRA_DBI='${dbi}' hydra-server >&2 &"); + $machine->waitForJob("hydra-init"); + $machine->waitForJob("hydra-server"); + $machine->waitForJob("hydra-evaluator"); + $machine->waitForJob("hydra-queue-runner"); $machine->waitForOpenPort("3000"); + $machine->succeed("curl --fail http://localhost:3000/"); ''; }); tests.api = genAttrs' (system: with import { 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 = 2047; - boot.kernelPackages = pkgs.linuxPackages_3_10; - }; - + machine = hydraServer (builtins.getAttr system build); # build.${system} testScript = let dbi = "dbi:Pg:dbname=hydra;user=root;"; in '' - $machine->waitForJob("postgresql"); + $machine->waitForJob("hydra-init"); - # Initialise the database and the state. - $machine->mustSucceed - ( "createdb -O root hydra" - , "HYDRA_DBI='${dbi}' hydra-init" - , "HYDRA_DBI='${dbi}' hydra-create-user root --email-address 'e.dolstra\@tudelft.nl' --password foobar --role admin" - , "mkdir /var/lib/hydra" - , "mkdir /run/jobset" - , "chmod 755 /run/jobset" + # Create an admin account and some other state. + $machine->succeed + ( "su hydra -c \"hydra-create-user root --email-address 'e.dolstra\@tudelft.nl' --password foobar --role admin\"" + , "mkdir /run/jobset /tmp/nix" + , "chmod 755 /run/jobset /tmp/nix" , "cp ${./tests/api-test.nix} /run/jobset/default.nix" , "chmod 644 /run/jobset/default.nix" + , "chown -R hydra /run/jobset /tmp/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}' LOGNAME=root DBIC_TRACE=1 hydra-server -d >&2 &"); + # Start the web interface with some weird settings. + $machine->succeed("systemctl stop hydra-server hydra-evaluator hydra-queue-runner"); + $machine->mustSucceed("su hydra -c 'NIX_STORE_DIR=/tmp/nix/store NIX_LOG_DIR=/tmp/nix/var/log/nix NIX_STATE_DIR=/tmp/nix/var/nix DBIC_TRACE=1 hydra-server -d' >&2 &"); $machine->waitForOpenPort("3000"); - $machine->mustSucceed("perl ${./tests/api-test.pl} >&2"); + # Run the API tests. + $machine->mustSucceed("su hydra -c 'perl ${./tests/api-test.pl}' >&2"); ''; }); @@ -238,7 +231,7 @@ in rec { $machine->waitForJob("postgresql"); # Initialise the database and the state. - $machine->mustSucceed + $machine->succeed ( "createdb -O root hydra" , "psql hydra -f ${hydra}/libexec/hydra/sql/hydra-postgresql.sql" , "mkdir /var/lib/hydra" @@ -248,10 +241,10 @@ in rec { ); # start fakes3 - $machine->mustSucceed("fakes3 --root /tmp/s3 --port 80 &>/dev/null &"); + $machine->succeed("fakes3 --root /tmp/s3 --port 80 &>/dev/null &"); $machine->waitForOpenPort("80"); - $machine->mustSucceed("cd /tmp && LOGNAME=root AWS_ACCESS_KEY_ID=foo AWS_SECRET_ACCESS_KEY=bar HYDRA_DBI='dbi:Pg:dbname=hydra;user=root;' HYDRA_CONFIG=${./tests/s3-backup-test.config} perl -I ${hydra}/libexec/hydra/lib -I ${hydra.perlDeps}/lib/perl5/site_perl ./s3-backup-test.pl >&2"); + $machine->succeed("cd /tmp && LOGNAME=root AWS_ACCESS_KEY_ID=foo AWS_SECRET_ACCESS_KEY=bar HYDRA_DBI='dbi:Pg:dbname=hydra;user=root;' HYDRA_CONFIG=${./tests/s3-backup-test.config} perl -I ${hydra}/libexec/hydra/lib -I ${hydra.perlDeps}/lib/perl5/site_perl ./s3-backup-test.pl >&2"); ''; }); } diff --git a/tests/api-test.pl b/tests/api-test.pl index efbbf0fc..c71f7909 100644 --- a/tests/api-test.pl +++ b/tests/api-test.pl @@ -49,7 +49,7 @@ ok(exists $jobset->{jobsetinputs}->{"my-src"}, "The new jobset has a 'my-src' in ok($jobset->{jobsetinputs}->{"my-src"}->{jobsetinputalts}->[0] eq "/run/jobset", "The 'my-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"); +system("NIX_STORE_DIR=/tmp/nix/store NIX_LOG_DIR=/tmp/nix/var/log/nix NIX_STATE_DIR=/tmp/nix/var/nix 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}; @@ -58,11 +58,11 @@ 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"); +system("echo >> /run/jobset/default.nix; NIX_STORE_DIR=/tmp/nix/store NIX_LOG_DIR=/tmp/nix/var/log/nix NIX_STATE_DIR=/tmp/nix/var/nix hydra-evaluator sample default"); my $evals = decode_json(request_json({ uri => '/jobset/sample/default/evals' })->content())->{evals}; ok($evals->[0]->{jobsetevalinputs}->{"my-src"}->{revision} != $evals->[1]->{jobsetevalinputs}->{"my-src"}->{revision}, "Changing a jobset source changes its revision"); my $build = decode_json(request_json({ uri => "/build/" . $evals->[0]->{builds}->[0] })->content()); ok($build->{job} eq "job", "The build's job name is job"); ok($build->{finished} == 0, "The build isn't finished yet"); -ok($build->{buildoutputs}->{out}->{path} =~ /^\/run\/nix\/store\/[a-zA-Z0-9]{32}-job$/, "The build's outpath is in the nix store and named 'job'"); +ok($build->{buildoutputs}->{out}->{path} =~ /^\/tmp\/nix\/store\/[a-zA-Z0-9]{32}-job$/, "The build's outpath is in the nix store and named 'job'");