Francois Gouget : testbot: Make it possible to restart a failed job.

Alexandre Julliard julliard at winehq.org
Tue Jan 22 12:57:44 CST 2013


Module: tools
Branch: master
Commit: b8fbfb378bbaa359068131d038280b5a1d05f2c7
URL:    http://source.winehq.org/git/tools.git/?a=commit;h=b8fbfb378bbaa359068131d038280b5a1d05f2c7

Author: Francois Gouget <fgouget at codeweavers.com>
Date:   Tue Jan 22 04:15:50 2013 +0100

testbot: Make it possible to restart a failed job.

Mostly this is so that jobs that failed because of a transient
WineTestBot error can be restarted by the administrator.

---

 testbot/bin/Engine.pl                    |   31 +++++++++++++
 testbot/lib/WineTestBot/Engine/Notify.pm |   22 ++++++++-
 testbot/lib/WineTestBot/Jobs.pm          |   38 +++++++++++++++
 testbot/web/JobDetails.pl                |   73 ++++++++++++++++++++++++++++--
 4 files changed, 158 insertions(+), 6 deletions(-)

diff --git a/testbot/bin/Engine.pl b/testbot/bin/Engine.pl
index e233e80..3d8f159 100755
--- a/testbot/bin/Engine.pl
+++ b/testbot/bin/Engine.pl
@@ -171,6 +171,36 @@ sub HandleJobCancel
   return "1OK";
 }
 
+sub HandleJobRestart
+{
+  my $JobKey = $_[0];
+
+  my $Job = CreateJobs()->GetItem($JobKey);
+  if (! $Job)
+  {
+    LogMsg "JobRestart for nonexistent job $JobKey\n";
+    return "0Job $JobKey not found";
+  }
+  # We've already determined that JobKey is valid, untaint it
+  $JobKey =~ m/^(.*)$/;
+  $JobKey = $1;
+
+  my $ErrMessage = $Job->Restart();
+  if (defined($ErrMessage))
+  {
+    LogMsg "Restart problem: $ErrMessage\n";
+    return "0$ErrMessage";
+  }
+
+  $ErrMessage = ScheduleJobs();
+  if (defined($ErrMessage))
+  {
+    LogMsg "Scheduling problem in HandleJobRestart: $ErrMessage\n";
+  }
+
+  return "1OK";
+}
+
 sub HandleTaskComplete
 {
   my $ErrMessage = ScheduleJobs();
@@ -452,6 +482,7 @@ my %Handlers=(
     "foundwinetestupdate"      => \&HandleFoundWinetestUpdate,
     "getscreenshot"            => \&HandleGetScreenshot,
     "jobcancel"                => \&HandleJobCancel,
+    "jobrestart"               => \&HandleJobRestart,
     "jobstatuschange"          => \&HandleJobStatusChange,
     "jobsubmit"                => \&HandleJobSubmit,
     "ping"                     => \&HandlePing,
diff --git a/testbot/lib/WineTestBot/Engine/Notify.pm b/testbot/lib/WineTestBot/Engine/Notify.pm
index 6bbec87..b836751 100644
--- a/testbot/lib/WineTestBot/Engine/Notify.pm
+++ b/testbot/lib/WineTestBot/Engine/Notify.pm
@@ -33,8 +33,9 @@ use vars qw (@ISA @EXPORT @EXPORT_OK $RunningInEngine);
 
 require Exporter;
 @ISA = qw(Exporter);
- at EXPORT = qw(&PingEngine &JobSubmit &JobStatusChange &JobCancel &TaskComplete
-             &VMStatusChange &ExpectWinetestUpdate &FoundWinetestUpdate
+ at EXPORT = qw(&PingEngine &JobSubmit &JobStatusChange &JobCancel &JobRestart
+             &TaskComplete &VMStatusChange
+             &ExpectWinetestUpdate &FoundWinetestUpdate
              &WinePatchMLSubmission &WinePatchWebNotification
              &WinePatchWebSubmission &GetScreenshot);
 @EXPORT_OK = qw($RunningInEngine);
@@ -132,6 +133,23 @@ sub JobCancel
   return substr($Reply, 1);
 }
 
+sub JobRestart
+{
+  my $JobKey = $_[0];
+
+  my $Reply = SendCmdReceiveReply("jobrestart $JobKey\n");
+  if (length($Reply) < 1)
+  {
+    return "Unrecognized reply received from engine";
+  }
+  if (substr($Reply, 0, 1) eq "1")
+  {
+    return undef;
+  }
+
+  return substr($Reply, 1);
+}
+
 sub TaskComplete
 {
   my ($JobKey, $StepKey, $TaskKey) = @_;
diff --git a/testbot/lib/WineTestBot/Jobs.pm b/testbot/lib/WineTestBot/Jobs.pm
index 269643d..66f87ba 100644
--- a/testbot/lib/WineTestBot/Jobs.pm
+++ b/testbot/lib/WineTestBot/Jobs.pm
@@ -50,6 +50,7 @@ A Job is composed of multiple WineTestBot::Step objects.
 
 =cut
 
+use WineTestBot::Config;
 use WineTestBot::Branches;
 use WineTestBot::Engine::Notify;
 use WineTestBot::WineTestBotObjects;
@@ -205,6 +206,43 @@ sub Cancel
   return undef;
 }
 
+sub Restart
+{
+  my $self = shift;
+
+  if ($self->Status ne "failed" && $self->Status ne "completed")
+  {
+    return "Only completed/failed jobs can be restarted";
+  }
+
+  my $JobDir = "$DataDir/jobs/" . $self->Id;
+  my $FirstStep = 1;
+  my $Steps = $self->Steps;
+  my @SortedSteps = sort { $a->No <=> $b->No } @{$Steps->GetItems()};
+  foreach my $Step (@SortedSteps)
+  {
+    my $Tasks = $Step->Tasks;
+    foreach my $Task (@{$Tasks->GetItems()})
+    {
+      if ($FirstStep)
+      {
+        # The first step contains the patch or test executable
+        # so only delete its task folders
+        system("rm", "-rf", "$JobDir/" . $Step->No . "/" . $Task->No);
+      }
+      $Task->Status("queued");
+    }
+    # Subsequent steps only contain files generated by the previous steps
+    system("rm", "-rf", "$JobDir/" . $Step->No) if (!$FirstStep);
+    $FirstStep = undef;
+    $Step->Status("queued");
+  }
+  $self->Status("queued");
+  $self->Save(); # Save it all
+
+  return undef;
+}
+
 sub GetEMailRecipient
 {
   my $self = shift;
diff --git a/testbot/web/JobDetails.pl b/testbot/web/JobDetails.pl
index 2ca81b8..b191327 100644
--- a/testbot/web/JobDetails.pl
+++ b/testbot/web/JobDetails.pl
@@ -27,6 +27,7 @@ use WineTestBot::Config;
 use WineTestBot::Jobs;
 use WineTestBot::StepsTasks;
 use WineTestBot::Engine::Notify;
+use WineTestBot::Log;
 
 @JobDetailsPage::ISA = qw(ObjectModel::CGI::CollectionPage);
 
@@ -112,13 +113,40 @@ sub CanCancel
   return undef;
 }
 
-sub GetActions
+sub CanRestart
 {
   my $self = shift;
 
-  my $ErrMessage = $self->CanCancel();
+  my $Job = CreateJobs()->GetItem($self->{JobId});
+  my $Status = $Job->Status;
+  if ($Status ne "failed")
+  {
+    return "Job did not fail";
+  }
 
-  return defined($ErrMessage) ? [] : ["Cancel job"];
+  my $Session = $self->GetCurrentSession();
+  if (! defined($Session))
+  {
+    return "You are not authorized to restart this job";
+  }
+  my $CurrentUser = $Session->User;
+  if (! $CurrentUser->HasRole("admin") &&
+      $Job->User->GetKey() ne $CurrentUser->GetKey()) # FIXME: Admin only?
+  {
+    return "You are not authorized to restart this job";
+  }
+
+  return undef;
+}
+
+sub GetActions
+{
+  my $self = shift;
+
+  # These are mutually exclusive
+  return ["Cancel job"] if (!defined $self->CanCancel());
+  return ["Restart job"] if (!defined $self->CanRestart());
+  return [];
 }
 
 sub OnCancel
@@ -142,6 +170,27 @@ sub OnCancel
   return 1;
 }
 
+sub OnRestart
+{
+  my $self = shift;
+
+  my $ErrMessage = $self->CanRestart();
+  if (defined($ErrMessage))
+  {
+    $self->{ErrMessage} = $ErrMessage;
+    return !1;
+  }
+
+  $ErrMessage = JobRestart($self->{JobId});
+  if (defined($ErrMessage))
+  {
+    $self->{ErrMessage} = $ErrMessage;
+    return !1;
+  }
+
+  return 1;
+}
+
 sub OnAction
 {
   my $self = shift;
@@ -151,6 +200,10 @@ sub OnAction
   {
     return $self->OnCancel();
   }
+  elsif ($Action eq "Restart job")
+  {
+    return $self->OnRestart();
+  }
 
   return $self->SUPER::OnAction(@_);
 }
@@ -189,7 +242,19 @@ sub GenerateBody
   {
     print "<h1>" . $self->GetTitle() . "</h1>\n";
     print "<div class='Content'>\n";
-    print "<p>Job will be cancelled.</p>\n";
+    my $Action = $self->GetParam("Action");
+    if ($Action eq "Cancel job")
+    {
+      print "<p>Job will be cancelled.</p>\n";
+    }
+    elsif ($Action eq "Restart job")
+    {
+      print "<p>Job will be restarted.</p>\n";
+    }
+    else
+    {
+      print "<p>Unknown action $Action.</p>\n";
+    }
     print "</div>\n";
     return;
   }




More information about the wine-cvs mailing list