Hello community,

here is the log from the commit of package openQA for openSUSE:Factory checked 
in at 2018-03-11 15:25:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/openQA (Old)
 and      /work/SRC/openSUSE:Factory/.openQA.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "openQA"

Sun Mar 11 15:25:54 2018 rev:15 rq:585204 version:4.5.1520515419.c3df6f91

Changes:
--------
--- /work/SRC/openSUSE:Factory/openQA/openQA.changes    2018-03-07 
10:39:35.027860334 +0100
+++ /work/SRC/openSUSE:Factory/.openQA.new/openQA.changes       2018-03-11 
15:25:55.161777283 +0100
@@ -1,0 +2,22 @@
+Sat Mar 10 05:29:00 UTC 2018 - co...@suse.com
+
+- Update to version 4.5.1520515419.c3df6f91:
+  * Update the chromedriver url from 2.33 to 2.36
+  * Allow saving needles for OS with dots in the version
+  * docs: Clarify some serial terminal terminology and other elaboration 
(#1596)
+  * Do not reset the api version just because the worker is offline
+  * Fix scheduler comment - now default MAX_JOB_ALLOCATION is 80 (#1595)
+  * Always retry until chunk is uploaded and retrials exhausted (#1594)
+  * Refactor and add error test
+  * PARALLEL_CLUSTER is no more
+  * Adapt scheduler_full test
+  * Avoid to re-consider the jobs that are being allocating in a round
+  * Do not take account of spaces in PARALLEL_WITH
+  * Exclude allocating jobs from prefer_parallel cut
+  * Consider allocating only when we have them
+  * Bump MAX_JOB_ALLOCATION default to 80
+  * Exclude allocated when blocking dependencies
+  * Consider the jobs allocated during the round as running in prefer_parallel
+  * Prevent cluster tests to be allocated if their group is not allocated
+
+-------------------------------------------------------------------

Old:
----
  openQA-4.5.1520100590.2279151e.tar.xz

New:
----
  openQA-4.5.1520515419.c3df6f91.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ openQA.spec ++++++
--- /var/tmp/diff_new_pack.BxImoJ/_old  2018-03-11 15:25:55.989747580 +0100
+++ /var/tmp/diff_new_pack.BxImoJ/_new  2018-03-11 15:25:56.001747149 +0100
@@ -36,7 +36,7 @@
 # runtime requirements that also the testsuite needs
 %define t_requires perl(DBD::Pg) perl(DBIx::Class) perl(Config::IniFiles) 
perl(SQL::Translator) perl(Date::Format) perl(File::Copy::Recursive) 
perl(DateTime::Format::Pg) perl(Net::OpenID::Consumer) 
perl(Mojolicious::Plugin::RenderFile) perl(Mojolicious::Plugin::AssetPack) 
perl(aliased) perl(Config::Tiny) perl(DBIx::Class::DynamicDefault) 
perl(DBIx::Class::Schema::Config) perl(DBIx::Class::Storage::Statistics) 
perl(IO::Socket::SSL) perl(Data::Dump) perl(DBIx::Class::OptimisticLocking) 
perl(Text::Markdown) perl(Net::DBus) perl(IPC::Run) perl(Archive::Extract) 
perl(CSS::Minifier::XS) perl(JavaScript::Minifier::XS) perl(Time::ParseDate) 
perl(Sort::Versions) perl(Mojo::RabbitMQ::Client) perl(BSD::Resource) 
perl(Cpanel::JSON::XS) perl(Pod::POM) perl(Mojo::IOLoop::ReadWriteProcess)
 Name:           openQA
-Version:        4.5.1520100590.2279151e
+Version:        4.5.1520515419.c3df6f91
 Release:        0
 Summary:        The openQA web-frontend, scheduler and tools
 License:        GPL-2.0+


++++++ openQA-4.5.1520100590.2279151e.tar.xz -> 
openQA-4.5.1520515419.c3df6f91.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-4.5.1520100590.2279151e/.travis.yml 
new/openQA-4.5.1520515419.c3df6f91/.travis.yml
--- old/openQA-4.5.1520100590.2279151e/.travis.yml      2018-03-03 
19:09:50.000000000 +0100
+++ new/openQA-4.5.1520515419.c3df6f91/.travis.yml      2018-03-08 
14:23:39.000000000 +0100
@@ -32,7 +32,7 @@
   - cpanm local::lib
   - eval "$(perl -Mlocal::lib=${HOME}/perl_modules)"
   - mkdir -p $HOME/chrome
-  - if ! test -f $HOME/chrome/chromedriver; then curl -s 
https://chromedriver.storage.googleapis.com/2.33/chromedriver_linux64.zip | 
funzip - > $HOME/chrome/chromedriver; fi
+  - if ! test -f $HOME/chrome/chromedriver; then curl -s 
https://chromedriver.storage.googleapis.com/2.36/chromedriver_linux64.zip | 
funzip - > $HOME/chrome/chromedriver; fi
   - chmod a+x $HOME/chrome/chromedriver
   - export PATH=$PATH:$HOME/chrome
 install:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-4.5.1520100590.2279151e/docs/WritingTests.asciidoc 
new/openQA-4.5.1520515419.c3df6f91/docs/WritingTests.asciidoc
--- old/openQA-4.5.1520100590.2279151e/docs/WritingTests.asciidoc       
2018-03-03 19:09:50.000000000 +0100
+++ new/openQA-4.5.1520515419.c3df6f91/docs/WritingTests.asciidoc       
2018-03-08 14:23:39.000000000 +0100
@@ -688,10 +688,17 @@
 serial port for getting raw text output from the SUT. This is primarily
 implemented with VNC and so I will referrer to it as the VNC text console.
 
-The second method uses a single serial port for both input and output. The SUT
-attaches a TTY to the serial port and all communication is text based. This is
-called the serial terminal console (or the virtio console, see implementation
-section for details).
+The serial port device which is used with the VNC text console is the default
+virtual serial port device in QEMU (i.e. the device configured with the
++-serial+ command line option). I will refer to this as the "default serial
+port". OpenQA currently only uses this serial port for one way communication
+from the SUT to the host.
+
+The second method uses another serial port for both input and output. The SUT
+attaches a TTY to the serial port which os-autoinst logs into. All
+communication is therefor text based, similar to if you SSH'd into a remote
+machine. This is called the serial terminal console (or the virtio console,
+see implementation section for details).
 
 The VNC text console is very slow and expensive relative to the serial
 terminal console, but allows you to continue using +assert_screen+ and is more
@@ -723,7 +730,7 @@
 
 Note that +root-console+ is defined by the distribution, so on different
 distributions or operating systems this can vary. There are also many utility
-functions that wrap +select_console+, so check your distributions utility
+functions that wrap +select_console+, so check your distribution's utility
 library before using it directly.
 
 ====
@@ -765,11 +772,11 @@
 Usually OpenQA controls the system under test using VNC. This allows the use
 of both graphical and text based consoles. Key presses are sent individually
 as VNC commands and output is returned in the form of screen images and text
-output from the SUT's serial port.
+output from the SUT's default serial port.
 
-Sending key presses over VNC is very slow so for tests which send a lot of
-text commands it is much faster to use a serial port for both sending and
-receiving TTY commands.
+Sending key presses over VNC is very slow, so for tests which send a lot of
+text commands it is much faster to use a serial port for both sending shell
+commands and received program output.
 
 Communicating entirely using text also means that you no longer have to worry
 about your needles being invalidated due to a font change or similar. It is
@@ -788,22 +795,34 @@
 select_console('root-virtio-terminal');  # Selects a virtio based serial 
terminal
 
--------------------------------------------------------------------------------
 
-Changing input and output to a serial terminal has the side effect of changing
-where +wait_serial+ reads output from. On a QEMU VM +wait_serial+ usually
-reads from an emulated serial port which is also where the kernel log is
-usually output to.
+The above code will cause +type_string+ and +wait_serial+ to write and read
+from a virtio serial port. A distribution specific call back will be made
+which allows os-autoinst to log into a serial terminal session running on the
+SUT. Once +select_console+ returns you should be logged into a TTY as root.
+
+If you are struggling to visualise what is happening, imagine SSH-ing into a
+remote machine as root, you can then type in commands and read the results as
+if you were sat at that computer. What we are doing is much simpler than using
+an SSH connection (it is more like using GNU +screen+ with a serial port), but
+the end result looks quite similar.
+
+As mentioned above, changing input and output to a serial terminal has the
+effect of changing where +wait_serial+ reads output from. On a QEMU VM
++wait_serial+ usually reads from the default serial port which is also where
+the kernel log is usually output to.
 
 When switching to a virtio based serial terminal, +wait_serial+ will then read
-from the virtio serial port instead. However the emulated serial port still
+from a virtio serial port instead. However the default serial port still
 exists and can receive output. Some utility library functions are hard coded
-to redirect output to the emulated serial port and expect that +wait_serial+
+to redirect output to the default serial port and expect that +wait_serial+
 will be able to read it. Usually it is not too difficult to fix the utility
-function, you just need to remove some redirection in the shell command.
+function, you just need to remove some redirection from the relevant shell
+command.
 
 Another common problem is that some library or utility function tries to take
 a screen shot. The hard part is finding what takes the screen shot, but then
 it is just a simple case of checking +is_serial_terminal+ and not taking the
-screen shot if we are on a serial terminal.
+screen shot if we are on a serial terminal console.
 
 Distributions usually wrap +select_console+, so instead of using it directly,
 you can use something like the following which is from the OpenSUSE test
@@ -816,18 +835,18 @@
 }
 
--------------------------------------------------------------------------------
 
-This selects the virtio based serial terminal if possible. If it is available
-then it returns true. It is also possible to check if the current console is a
-serial terminal by calling +is_serial_terminal+.
+This selects the virtio based serial terminal console if possible. If it is
+available then it returns true. It is also possible to check if the current
+console is a serial terminal by calling +is_serial_terminal+.
 
 Once you have selected a serial terminal, the video feed will disappear from
 the live view, however at the bottom of the live screen there is a separate
 text feed. After the test has finished you can view the serial log(s) in the
-assets tab. You will probably have two serial logs; +serial0.txt+ and
-+serial_terminal.txt+.
+assets tab. You will probably have two serial logs; +serial0.txt+ which is
+written from the default serial port and +serial_terminal.txt+.
 
-Now that you are on a serial terminal everything will start to go a lot
-faster. So much faster in fact that race conditions become a big
+Now that you are on a serial terminal console everything will start to go a
+lot faster. So much faster in fact that race conditions become a big
 issue. Generally these can be avoided by using the higher level functions such
 as +script_run+ and +script_output+.
 
@@ -863,7 +882,7 @@
         wait_serial($cmd_text, undef, 0, no_regex => 1);           #Step 3
         type_string("\n");                                                 
#Step 4
 } else {
-        # The slow path
+        # None serial terminal console code (e.g. the VNC console)
 }
 my $test_log = wait_serial(qr/$fin_msg\d+/, $timeout, 0, record_output => 1); 
#Step 5
 
--------------------------------------------------------------------------------
@@ -874,9 +893,15 @@
 would be 'echo' which is used by +script_run+ to print a 'finished' message.
 
 It is possible that echo was able to print the finish message, but was then
-suspended by the OS before it could exit. In which case the test script is able
-to race ahead and start sending input which will just be dropped by echo once
-it is resumed. Waiting for the shell prompt stops this from happening.
+suspended by the OS before it could exit. In which case the test script is
+able to race ahead and start sending input to echo which was intended for the
+shell. Waiting for the shell prompt stops this from happening.
+
+INFO: It appears that echo does not read STDIN in this case, and so the input
+will stay inside STDIN's buffer and be read by the shell (Bash). Unfortunately
+this results in the input being displayed twice: once by the terminal's echo
+(explained later) and once by Bash. Depending on your configuration the
+behavior could be completely different
 
 The function +serial_term_prompt+ is a distribution specific function which
 returns the characters previously set as the shell prompt (e.g. export PS1="#
@@ -922,6 +947,45 @@
 after it, so that Step 5 can be deferred to a later time. In theory this
 allows the test script to perform some other work while the SUT is busy.
 
+==== Sending signals - ctrl-c and ctrl-d
+
+On a VNC based console you simply use +send_key+ like follows.
+
+[source,perl]
+--------------------------------------------------------------------------------
+send_key('ctrl-c');
+--------------------------------------------------------------------------------
+
+This usually (see termios(3)) has the effect of sending SIGINT to whatever
+command is running. Most commands terminate upon receiving this signal (see
+signal(7)).
+
+On a serial terminal console the +send_key+ command is not implemented (see
+implementation section). So instead the following can be done to achieve the
+same effect.
+
+[source,perl]
+--------------------------------------------------------------------------------
+type_string('', terminate_with => 'ETX');
+--------------------------------------------------------------------------------
+
+The ETX ASCII code means End of Text and usually results in SIGINT being
+raised. In fact pressing +ctrl-c+ may just be translated into ETX, so you
+might consider this a more direct method. Also you can use 'EOT' to do the
+same thing as pressing +ctrl-d+.
+
+You also have the option of using Perl's control character escape sequences in
+the first argument to +type_string+. So you can also send ETX with:
+
+[source,perl]
+--------------------------------------------------------------------------------
+type_string("\cC");
+--------------------------------------------------------------------------------
+
+The +terminate_with+ parameter just exists to display intention. It is also
+possible to send any character using the hex code like '\x0f' which may have
+the effect of pressing the magic SysRq key if you are lucky.
+
 ==== The virtio serial terminal implementation
 
 The os-autoinst package supports several types of 'consoles' of which the
@@ -952,6 +1016,13 @@
 string and use the Perl library function +index+ to search for a match in the
 ring buffer.
 
+The +send_key+ function is not implemented for the serial terminal console
+because the OpenQA console implementation would need to map key actions like
++ctrl-c+ to a character and then send that character. This may mislead some
+people into thinking they are actually sending +ctrl-c+ to the SUT and also
+requires OpenQA to choose what character +ctrl-c+ represents which varies
+across terminal configurations.
+
 Very little of the code (perhaps none) is specific to a virtio based serial
 terminal and can be reused with a physical serial port, SSH socket, IPMI or
 some other text based interface. It is called the virtio console because the
@@ -965,3 +1036,4 @@
 the SUSE distribution's serial terminal utility library). However some
 programs ignore this, but piping there output into +tee+ is usually enough to
 stop them outputting non-printable characters.
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-4.5.1520100590.2279151e/lib/OpenQA/Client/Upload.pm 
new/openQA-4.5.1520515419.c3df6f91/lib/OpenQA/Client/Upload.pm
--- old/openQA-4.5.1520100590.2279151e/lib/OpenQA/Client/Upload.pm      
2018-03-03 19:09:50.000000000 +0100
+++ new/openQA-4.5.1520515419.c3df6f91/lib/OpenQA/Client/Upload.pm      
2018-03-08 14:23:39.000000000 +0100
@@ -57,18 +57,24 @@
         my $res;
         my $done = 0;
         do {
-            $res = $self->client->start(
-                $self->_build_post(
-                    "$uri/artefact" => {
-                        file => {filename => $file_name, file => 
Mojo::Asset::Memory->new->add_chunk($_->serialize)},
-                        asset => $opts->{asset},
-                    }));
-            $self->emit('upload_chunk.response' => $res);
-            $done = 1 if $res && $res->res->json && $res->res->json->{status} 
&& $res->res->json->{status} eq 'ok';
-            $trial = 0 if (!$res->res->is_server_error && $res->error);
-            $trial-- if $trial > 0;
+            local $@;
+            eval {
+                $res = $self->client->start(
+                    $self->_build_post(
+                        "$uri/artefact" => {
+                            file =>
+                              {filename => $file_name, file => 
Mojo::Asset::Memory->new->add_chunk($_->serialize)},
+                            asset => $opts->{asset},
+                        }));
+                $self->emit('upload_chunk.response' => $res);
+                $done = 1
+                  if $res && $res->res->json && exists 
$res->res->json->{status} && $res->res->json->{status} eq 'ok';
+            };
             $self->emit('upload_chunk.fail' => $res => $_) if $done == 0;
-            $e = $res->res if $trial == 0 && $done == 0;
+
+            $trial-- if $trial > 0;
+            $self->emit('upload_chunk.request_err' => $res => $@) if $@;
+            $e = $@ || $res if $trial == 0 && $done == 0;
         } until ($trial == 0 || $done);
 
         $failed++ if $trial == 0 && $done == 0;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-4.5.1520100590.2279151e/lib/OpenQA/Scheduler/Scheduler.pm 
new/openQA-4.5.1520515419.c3df6f91/lib/OpenQA/Scheduler/Scheduler.pm
--- old/openQA-4.5.1520100590.2279151e/lib/OpenQA/Scheduler/Scheduler.pm        
2018-03-03 19:09:50.000000000 +0100
+++ new/openQA-4.5.1520515419.c3df6f91/lib/OpenQA/Scheduler/Scheduler.pm        
2018-03-08 14:23:39.000000000 +0100
@@ -96,9 +96,16 @@
 # Jobs API
 #
 sub _prefer_parallel {
-    my ($available_cond) = @_;
+    my ($available_cond, $allocating) = @_;
     my $running = schema->resultset("Jobs")->search(
-        {
+        @$allocating > 0 ?
+          {
+            -or => [
+                id    => {-in => $allocating},
+                state => OpenQA::Schema::Result::Jobs::RUNNING,
+            ],
+          }
+        : {
             state => OpenQA::Schema::Result::Jobs::RUNNING,
         })->get_column('id')->as_query;
 
@@ -117,8 +124,10 @@
 
     my $available_children = $children->search(
         {
-            child_job_id => $available_cond
-        });
+            -and => [
+                child_job_id => $available_cond,
+                child_job_id => {-not_in => $allocating},
+            ]});
 
     # we have scheduled children that are not blocked
     return ({'-in' => 
$available_children->get_column('child_job_id')->as_query})
@@ -130,6 +139,7 @@
             dependency   => OpenQA::Schema::Result::JobDependencies::PARALLEL,
             child_job_id => {-in => 
$children->get_column('child_job_id')->as_query},
             state        => OpenQA::Schema::Result::Jobs::SCHEDULED,
+            child_job_id => {-not_in => $allocating},
         },
         {
             join => 'parent',
@@ -246,6 +256,7 @@
                     # by checking workers capabilities
                     my @possible_jobs = job_grab(
                         workerid     => $w->id(),
+                        allocating   => [keys %$allocating],
                         blocking     => 0,
                         allocate     => 0,
                         jobs         => 
OpenQA::Scheduler::MAX_JOB_ALLOCATION(),
@@ -286,6 +297,9 @@
       if (OpenQA::Scheduler::MAX_JOB_ALLOCATION() > 0
         && scalar(@allocated_jobs) > OpenQA::Scheduler::MAX_JOB_ALLOCATION());
 
+    # We filter after, or we risk to cut jobs that meant to be parallel later
+    @allocated_jobs = filter_jobs(@allocated_jobs);
+
     my @successfully_allocated;
 
     foreach my $allocated (@allocated_jobs) {
@@ -466,7 +480,7 @@
 }
 
 sub _build_search_query {
-    my $worker  = shift;
+    my ($worker, $allocating, $allocate) = @_;
     my $blocked = schema->resultset("JobDependencies")->search(
         {
             -or => [
@@ -477,7 +491,8 @@
                 -and => {
                     dependency => 
OpenQA::Schema::Result::JobDependencies::PARALLEL,
                     state      => OpenQA::Schema::Result::Jobs::SCHEDULED,
-                },
+                    (parent_job_id => {-not_in => $allocating}) x 
!!(@$allocating > 0),
+                }
             ],
         },
         {
@@ -489,6 +504,7 @@
             -not_in => $blocked->get_column('child_job_id')->as_query
         },
     );
+    push @available_cond, {-not_in => $allocating} if @$allocating > 0;
 
     # Don't kick off jobs if GRU task they depend on is running
     my $waiting_jobs = 
schema->resultset("GruDependencies")->get_column('job_id')->as_query;
@@ -516,7 +532,7 @@
         push @available_cond, {-not_in => $not_applying_jobs->as_query};
     }
 
-    my $preferred_parallel = _prefer_parallel(\@available_cond);
+    my $preferred_parallel = _prefer_parallel(\@available_cond, $allocating);
     push @available_cond, $preferred_parallel if $preferred_parallel;
     return @available_cond;
 }
@@ -558,6 +574,48 @@
     return 0;
 }
 
+
+sub filter_jobs {
+    my @jobs          = @_;
+    my @filtered_jobs = @jobs;
+    my @delete;
+    my $allocated_tests;
+    my @k = qw(ARCH DISTRI BUILD FLAVOR);
+
+    # TODO: @jobs's jobs needs to be a schema again
+    # previously we didn't needed schema after scheduling phase
+    # so we stripped down to what we needed
+
+    try {
+        # Build adjacent list with the tests that would have been assigned
+        $allocated_tests->{$_->{test} . join(".", @{$_->{settings}}{@k})}++ 
for @jobs;
+
+        foreach my $j (@jobs) {
+
+            # Filter by PARALLEL_CLUSTER
+            # next unless exists $j->{settings}->{PARALLEL_CLUSTER};
+
+            my $dep = schema->resultset("Jobs")->search({id => 
$j->{id},})->first->dependencies;
+
+            # Get dependencies - do not map with dbix, no oneline fun :(
+            my @dep_tests;
+            push(@dep_tests, schema->resultset("Jobs")->search({id => 
$_,})->first->TEST)
+              for (@{$dep->{children}->{Parallel}}, 
@{$dep->{parents}->{Parallel}});
+
+            # Filter if dependencies are not in the same allocation round
+            @filtered_jobs = grep { $_->{id} ne $j->{id} } @filtered_jobs
+              if grep { s/^\s+|\s+$//g; !exists $allocated_tests->{$_ . 
join(".", @{$j->{settings}}{@k})} } ## no critic
+              (@dep_tests, exists $j->{settings}->{PARALLEL_WITH} ? split(/,/, 
$j->{settings}->{PARALLEL_WITH}) : ());
+        }
+    }
+    catch {
+        log_debug("Failed job filtering, error: " . $_);
+        @filtered_jobs = @jobs;
+    };
+
+    return @filtered_jobs;
+}
+
 =head2 job_grab
 
 Search for matching jobs corresponding to a given worker id.
@@ -587,11 +645,12 @@
 =cut
 
 sub job_grab {
-    my %args     = @_;
-    my $workerid = $args{workerid};
-    my $blocking = int($args{blocking} || 0);
-    my $allocate = int($args{allocate} || 0);
-    my $job_n    = $args{jobs} // 0;
+    my %args       = @_;
+    my $workerid   = $args{workerid};
+    my $blocking   = int($args{blocking} || 0);
+    my $allocate   = int($args{allocate} || 0);
+    my $job_n      = $args{jobs} // 0;
+    my $allocating = $args{allocating} // [];
 
     my $worker;
     my $attempt = 0;
@@ -644,7 +703,7 @@
                     my $search = schema->resultset("Jobs")->search(
                         {
                             state => OpenQA::Schema::Result::Jobs::SCHEDULED,
-                            id    => [_build_search_query($worker)],
+                            id    => [_build_search_query($worker, 
$allocating, $allocate)],
                         },
                         {order_by => {-asc => [qw(priority id)]}});
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-4.5.1520100590.2279151e/lib/OpenQA/Scheduler.pm 
new/openQA-4.5.1520515419.c3df6f91/lib/OpenQA/Scheduler.pm
--- old/openQA-4.5.1520100590.2279151e/lib/OpenQA/Scheduler.pm  2018-03-03 
19:09:50.000000000 +0100
+++ new/openQA-4.5.1520515419.c3df6f91/lib/OpenQA/Scheduler.pm  2018-03-08 
14:23:39.000000000 +0100
@@ -27,8 +27,8 @@
 
 use OpenQA::Utils 'log_debug';
 
-# How many jobs to allocate in one tick. Defaults to 50 ( set it to 0 for as 
much as possible)
-use constant MAX_JOB_ALLOCATION => $ENV{OPENQA_SCHEDULER_MAX_JOB_ALLOCATION} 
// 50;
+# How many jobs to allocate in one tick. Defaults to 80 ( set it to 0 for as 
much as possible)
+use constant MAX_JOB_ALLOCATION => $ENV{OPENQA_SCHEDULER_MAX_JOB_ALLOCATION} 
// 80;
 
 # How many attempts have to be performed to find a job before assuming there 
is nothing to be scheduled. Defaults to 1
 use constant FIND_JOB_ATTEMPTS => $ENV{OPENQA_SCHEDULER_FIND_JOB_ATTEMPTS} // 
1;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-4.5.1520100590.2279151e/lib/OpenQA/WebAPI/Controller/Step.pm 
new/openQA-4.5.1520515419.c3df6f91/lib/OpenQA/WebAPI/Controller/Step.pm
--- old/openQA-4.5.1520100590.2279151e/lib/OpenQA/WebAPI/Controller/Step.pm     
2018-03-03 19:09:50.000000000 +0100
+++ new/openQA-4.5.1520515419.c3df6f91/lib/OpenQA/WebAPI/Controller/Step.pm     
2018-03-08 14:23:39.000000000 +0100
@@ -453,7 +453,8 @@
     $validation->required('json');
     $validation->required('imagename')->like(qr/^[^.\/][^\/]{3,}\.png$/);
     $validation->optional('imagedistri')->like(qr/^[^.\/]+$/);
-    $validation->optional('imageversion')->like(qr/^[^.\/]+$/);
+    $validation->optional('imageversion')->like(qr/^(?!.*([.])\1+).*$/);
+    $validation->optional('imageversion')->like(qr/^[^\/]+$/);
     $validation->required('needlename')->like(qr/^[^.\/][^\/]{3,}$/);
 
     if ($validation->has_error) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-4.5.1520100590.2279151e/lib/OpenQA/WebSockets/Server.pm 
new/openQA-4.5.1520515419.c3df6f91/lib/OpenQA/WebSockets/Server.pm
--- old/openQA-4.5.1520100590.2279151e/lib/OpenQA/WebSockets/Server.pm  
2018-03-03 19:09:50.000000000 +0100
+++ new/openQA-4.5.1520515419.c3df6f91/lib/OpenQA/WebSockets/Server.pm  
2018-03-08 14:23:39.000000000 +0100
@@ -191,9 +191,6 @@
     $dt->subtract(seconds => (WORKERS_CHECKER_THRESHOLD + 20));
     $worker->{db}->update({t_updated => $dt});
     $worker->{socket} = undef;
-    # remove the version as we don't know if the worker is updated while 
shutdown
-    $worker->{db}->set_property('WEBSOCKET_API_VERSION',        '');
-    $worker->{db}->set_property('ISOTOVIDEO_INTERFACE_VERSION', '');
 }
 
 sub _message {
@@ -206,14 +203,14 @@
         return;
     }
     unless (ref($json) eq 'HASH') {
-        log_error(sprintf('Received unexpected WS message "%s from worker %u', 
Dumper($json), $worker->id));
+        log_error(sprintf('Received unexpected WS message "%s from worker %u', 
Dumper($json), $worker->{id}));
         $ws->finish("1003", "Received unexpected data from worker, forcing 
close");
         return;
     }
 
     # This is to make sure that no worker can skip the _registration.
     if (($worker->{db}->get_websocket_api_version() || 0) != 
WEBSOCKET_API_VERSION) {
-        log_warning("Received a message from an incompatible worker");
+        log_warning("Received a message from an incompatible worker " . 
$worker->{id});
         $ws->tx->send({json => {type => 'incompatible'}});
         $ws->finish("1008",
             "Connection terminated from WebSocket server - incompatible 
communication protocol version");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/openQA-4.5.1520100590.2279151e/lib/OpenQA/Worker/Jobs.pm 
new/openQA-4.5.1520515419.c3df6f91/lib/OpenQA/Worker/Jobs.pm
--- old/openQA-4.5.1520100590.2279151e/lib/OpenQA/Worker/Jobs.pm        
2018-03-03 19:09:50.000000000 +0100
+++ new/openQA-4.5.1520515419.c3df6f91/lib/OpenQA/Worker/Jobs.pm        
2018-03-08 14:23:39.000000000 +0100
@@ -222,16 +222,21 @@
         'upload_chunk.fail' => sub {
             my ($self, $res, $chunk) = @_;
             log_error(
-                "Upload failed for chunk " . $chunk->index . ": " . $e->body,
+                "Upload failed for chunk " . $chunk->index,
                 channels => ['autoinst', 'worker'],
                 default  => 1
             );
+            sleep 5;    # do not choke webui
         });
 
     $client->upload->once(
         'upload_chunk.error' => sub {
             $e = pop();
-            log_error($e->body, channels => ['autoinst', 'worker'], default => 
1);
+            log_error(
+                "Upload failed, and all retry attempts have been exhausted",
+                channels => ['autoinst', 'worker'],
+                default  => 1
+            );
         });
 
     local $@;
@@ -244,13 +249,11 @@
                 chunk_size => $chunk_size
             });
     };
+    log_error($@, channels => ['autoinst', 'worker'], default => 1) if $@;
 
-    $client->upload->unsubscribe('upload_chunk.response');
-    $client->upload->unsubscribe('upload_chunk.start');
-    $client->upload->unsubscribe('upload_chunk.finish');
-    $client->upload->unsubscribe('upload_chunk.prepare');
-    $client->upload->unsubscribe('upload_chunk.error');
-    $client->upload->unsubscribe('upload_chunk.fail');
+    $client->upload->unsubscribe($_)
+      for qw(upload_chunk.request_err upload_chunk.error upload_chunk.fail),
+      qw( upload_chunk.response upload_chunk.start upload_chunk.finish 
upload_chunk.prepare);
 
     return 0 if $@ || $e;
     return 1;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-4.5.1520100590.2279151e/t/05-scheduler-full.t 
new/openQA-4.5.1520515419.c3df6f91/t/05-scheduler-full.t
--- old/openQA-4.5.1520100590.2279151e/t/05-scheduler-full.t    2018-03-03 
19:09:50.000000000 +0100
+++ new/openQA-4.5.1520515419.c3df6f91/t/05-scheduler-full.t    2018-03-08 
14:23:39.000000000 +0100
@@ -246,12 +246,15 @@
         }
         is $dup->state, OpenQA::Schema::Result::Jobs::SCHEDULED, "Job(" . 
$dup->id . ") back in scheduled state";
     }
-    dead_workers($schema);
     kill_service($_, 1) for @workers;
+    dead_workers($schema);
 
     @workers = ();
 
     push(@workers, unstable_worker($k->key, $k->secret, 
"http://localhost:$mojoport";, $_, 3)) for (1 .. 30);
+    my $i = 5;
+    wait_for_worker($schema, ++$i) for 0 .. 12;
+    trigger_capture_event_loop($reactor);
 
     ($allocated, $failures, $no_actions) = scheduler_step($reactor); # Will 
try to allocate to previous worker and fail!
     is @$allocated, 0, "All failed allocation on second step - workers were 
killed";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-4.5.1520100590.2279151e/t/10-jobs.t 
new/openQA-4.5.1520515419.c3df6f91/t/10-jobs.t
--- old/openQA-4.5.1520100590.2279151e/t/10-jobs.t      2018-03-03 
19:09:50.000000000 +0100
+++ new/openQA-4.5.1520515419.c3df6f91/t/10-jobs.t      2018-03-08 
14:23:39.000000000 +0100
@@ -624,4 +624,138 @@
     like($messages[2], qr/result: dead_children/, 'dead children match 
exception');
 };
 
+subtest 'job PARALLEL_WITH' => sub {
+
+    use OpenQA::Scheduler::Scheduler;
+    use OpenQA::Schema::Result::JobDependencies;
+
+    my %_settings = %settings;
+    $_settings{TEST} = 'A';
+    #  $_settings{PARALLEL_WITH}    = 'B,C,D';
+    my $jobA = _job_create(\%_settings);
+
+    %_settings                = %settings;
+    $_settings{TEST}          = 'B';
+    $_settings{PARALLEL_WITH} = 'A,C,D';
+    my $jobB = _job_create(\%_settings);
+
+    %_settings                = %settings;
+    $_settings{TEST}          = 'C';
+    $_settings{PARALLEL_WITH} = 'A,B,D';
+    my $jobC = _job_create(\%_settings);
+
+    %_settings                = %settings;
+    $_settings{TEST}          = 'D';
+    $_settings{PARALLEL_WITH} = 'A,B, C ';    # Let's commit an error on 
purpose :)
+    my $jobD = _job_create(\%_settings);
+
+    %_settings                = %settings;
+    $_settings{TEST}          = 'E';
+    $_settings{PARALLEL_WITH} = 'A,B,C';
+    my $jobE = _job_create(\%_settings);
+
+    %_settings                   = %settings;
+    $_settings{TEST}             = 'H';
+    $_settings{PARALLEL_WITH}    = 'B,C,D';
+    $_settings{PARALLEL_CLUSTER} = '1';
+    my $jobH = _job_create(\%_settings);
+
+    $jobA->children->create(
+        {
+            child_job_id => $jobB->id,
+            dependency   => OpenQA::Schema::Result::JobDependencies->PARALLEL,
+        });
+    $jobA->children->create(
+        {
+            child_job_id => $jobC->id,
+            dependency   => OpenQA::Schema::Result::JobDependencies->PARALLEL,
+        });
+    $jobA->children->create(
+        {
+            child_job_id => $jobD->id,
+            dependency   => OpenQA::Schema::Result::JobDependencies->PARALLEL,
+        });
+    $jobA->children->create(
+        {
+            child_job_id => $jobE->id,
+            dependency   => OpenQA::Schema::Result::JobDependencies->PARALLEL,
+        });
+    %_settings = %settings;
+    $_settings{TEST} = 'F';
+    my $jobF = _job_create(\%_settings);
+
+    %_settings = %settings;
+    $_settings{TEST} = 'G';
+    my $jobG = _job_create(\%_settings);
+
+    # A wants to be in the cluster while being assigned!
+    my @res = OpenQA::Scheduler::Scheduler::filter_jobs($jobA->to_hash, 
$jobF->to_hash);
+    is @res, 1;
+    is $res[0]->{id}, $jobF->id() or die diag explain $jobA->to_hash;
+
+    # All are going
+    @res = OpenQA::Scheduler::Scheduler::filter_jobs($jobA->to_hash, 
$jobB->to_hash, $jobC->to_hash, $jobD->to_hash,
+        $jobE->to_hash, $jobF->to_hash);
+    is @res, 6;
+
+    # Only F doesn't belong to any cluster
+    @res = OpenQA::Scheduler::Scheduler::filter_jobs($jobA->to_hash, 
$jobC->to_hash, $jobD->to_hash, $jobE->to_hash,
+        $jobF->to_hash);
+    is @res, 1;
+    is $res[0]->{id}, $jobF->id();
+
+    # Only E and F doesn't care about clusters, and we are not about to start D
+    @res = OpenQA::Scheduler::Scheduler::filter_jobs($jobA->to_hash, 
$jobC->to_hash, $jobB->to_hash, $jobE->to_hash,
+        $jobF->to_hash);
+    is @res, 2;
+    is $res[0]->{id}, $jobE->id();
+    is $res[1]->{id}, $jobF->id();
+
+    # Only E and F doesn't care about clusters, and we are not about to start D
+    # G does care about clusters, but didn't specified PARALLEL_WITH tests to 
rely upon
+    @res = OpenQA::Scheduler::Scheduler::filter_jobs($jobA->to_hash, 
$jobC->to_hash, $jobB->to_hash, $jobE->to_hash,
+        $jobF->to_hash, $jobG->to_hash);
+    is @res, 3;
+    is $res[0]->{id}, $jobE->id();
+    is $res[1]->{id}, $jobF->id();
+    is $res[2]->{id}, $jobG->id();
+
+    # All are going
+    @res = OpenQA::Scheduler::Scheduler::filter_jobs(
+        $jobA->to_hash, $jobB->to_hash, $jobC->to_hash, $jobD->to_hash,
+        $jobE->to_hash, $jobF->to_hash, $jobG->to_hash
+    );
+    is @res, 7;
+
+    # just few that requires cluster are going, but since they want to be 
together, they are not
+    @res = OpenQA::Scheduler::Scheduler::filter_jobs($jobA->to_hash, 
$jobB->to_hash, $jobC->to_hash);
+    is @res, 0;
+
+    # just few that requires cluster are going, but since they want to be 
together, they are not
+    @res = OpenQA::Scheduler::Scheduler::filter_jobs($jobH->to_hash, 
$jobC->to_hash);
+    is @res, 0;
+
+    # just few that requires cluster are going, but since they want to be 
together, they are not
+    @res = OpenQA::Scheduler::Scheduler::filter_jobs($jobH->to_hash, 
$jobC->to_hash, $jobB->to_hash);
+    is @res, 0;
+
+    # just few that requires cluster are going, but since they want to be 
together, they are not
+    @res = OpenQA::Scheduler::Scheduler::filter_jobs($jobH->to_hash, 
$jobB->to_hash, $jobD->to_hash);
+    is @res, 0;
+
+    # A requires E, so it is not going
+    @res = OpenQA::Scheduler::Scheduler::filter_jobs($jobH->to_hash, 
$jobC->to_hash, $jobB->to_hash, $jobA->to_hash,
+        $jobD->to_hash);
+    is @res, 4 or die diag explain \@res;
+
+
+    @res = OpenQA::Scheduler::Scheduler::filter_jobs($jobH->to_hash, 
$jobC->to_hash, $jobE->to_hash, $jobB->to_hash,
+        $jobA->to_hash, $jobD->to_hash);
+    is @res, 6 or die diag explain \@res;
+
+    my @e = ([], [qw(a b c)], [qw(d e f)]);
+    @res = OpenQA::Scheduler::Scheduler::filter_jobs(@e);
+    is_deeply \@res, \@e or die diag explain \@res;
+};
+
 done_testing();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/openQA-4.5.1520100590.2279151e/t/32-openqa_client.t 
new/openQA-4.5.1520515419.c3df6f91/t/32-openqa_client.t
--- old/openQA-4.5.1520100590.2279151e/t/32-openqa_client.t     2018-03-03 
19:09:50.000000000 +0100
+++ new/openQA-4.5.1520515419.c3df6f91/t/32-openqa_client.t     2018-03-08 
14:23:39.000000000 +0100
@@ -209,8 +209,10 @@
     $t->ua->upload->once(
         'upload_chunk.response' => sub { my ($self, $response) = @_; delete 
$response->res->json->{status}; $fired++; }
     );
-    $t->ua->upload->on('upload_chunk.fail'     => sub { $fail_chunk++ });
-    $t->ua->upload->on('upload_chunk.response' => sub { $responses++; });
+    $t->ua->upload->on('upload_chunk.fail'         => sub { $fail_chunk++ });
+    $t->ua->upload->on('upload_chunk.response'     => sub { $responses++; });
+    $t->ua->upload->on('upload_chunk.request_fail' => sub { use Data::Dump 
'pp'; diag pp(@_) });
+
     local $@;
     eval {
         $t->ua->upload->asset(
@@ -265,7 +267,7 @@
     $t->ua->upload->on(
         'upload_chunk.error' => sub {
             $errored++;
-            is(pop()->json->{status}, 'foobar', 'Error message status is 
correct');
+            is(pop()->res->json->{status}, 'foobar', 'Error message status is 
correct');
         });
 
     local $@;
@@ -278,7 +280,7 @@
 
     is $errored, 1, 'Upload errors';
 
-    ok !$@, 'No function errors';
+    ok !$@, 'No function errors' or die diag $@;
 
     ok(!-d $chunkdir, 'Chunk directory should not exist anymore');
 
@@ -287,6 +289,53 @@
     my $ret = 
$t->get_ok('/api/v1/assets/other/00099963-hdd_image5.xml')->status_is(404);
 };
 
+subtest 'upload internal errors' => sub {
+
+    use File::Temp;
+    my ($fh, $filename) = File::Temp::tempfile(UNLINK => 1);
+    syswrite($fh, "B");
+    seek($fh, 20 * 1024 * 1024, 0);    # create 200MB quick
+    syswrite($fh, "V");
+    close($fh);
+    my $sum = OpenQA::File::_file_digest($filename);
+
+    my $client = OpenQA::Client->new(apikey => 'PERCIVALKEY02', apisecret => 
'PERCIVALSECRET02')
+      ->ioloop(Mojo::IOLoop->singleton);
+    $client->base_url($base_url);
+
+    my $app = $t->app;
+    $t->ua($client);
+
+    $t->app($app);
+
+    my $chunkdir = 
't/data/openqa/share/factory/tmp/other/00099963-hdd_image6.xml.CHUNKS/';
+    my $rp       = "t/data/openqa/share/factory/other/00099963-hdd_image6.xml";
+
+    # Moar Sabotage!
+    my $fail_chunk;
+    my $e;
+    $t->ua->upload->on('upload_chunk.response' => sub { die("Subdly") });
+    $t->ua->upload->on('upload_chunk.request_err' => sub { $fail_chunk++; $e = 
pop(); });
+
+    local $@;
+    eval {
+        $t->ua->upload->asset(
+            99963 => {chunk_size => $chunk_size, file => $filename, name => 
'hdd_image6.xml', asset => 'other'});
+    };
+
+    is $fail_chunk, 5, 'All chunk failed, but we did not recovered :(';
+
+    like $e, qr/Subdly/;
+    ok !$@, 'No function errors' or die diag $@;
+
+    ok(!-d $chunkdir, 'Chunk directory should not exist anymore');
+
+    ok(!-e $rp, 'Asset does not exists after upload');
+
+    my $ret = 
$t->get_ok('/api/v1/assets/other/00099963-hdd_image6.xml')->status_is(404);
+};
+
+
 done_testing();
 
 1;


Reply via email to