From 4904382d9c509457003cd446219baf5c6c8db5d3 Mon Sep 17 00:00:00 2001 From: Marius Kittler Date: Tue, 20 Feb 2024 15:29:07 +0100 Subject: [PATCH] Split monitoring of jobs via `openqa-cli` into a separate command This allows to monitor a set of job which will be useful for further CI-related use cases like cloning existing jobs and monitoring them (see https://progress.opensuse.org/issues/130934). Of course the existing `openqa-cli schedule` command will continue to support the `--monitor` parameter. --- lib/OpenQA/CLI/monitor.pm | 73 ++++++++++++++++++++++++++++++++++++++ lib/OpenQA/CLI/schedule.pm | 33 ++--------------- t/43-cli-schedule.t | 22 ++++++++++-- 3 files changed, 94 insertions(+), 34 deletions(-) create mode 100644 lib/OpenQA/CLI/monitor.pm diff --git a/lib/OpenQA/CLI/monitor.pm b/lib/OpenQA/CLI/monitor.pm new file mode 100644 index 000000000000..99ab53e2c558 --- /dev/null +++ b/lib/OpenQA/CLI/monitor.pm @@ -0,0 +1,73 @@ +# Copyright SUSE LLC +# SPDX-License-Identifier: GPL-2.0-or-later + +package OpenQA::CLI::monitor; +use OpenQA::Jobs::Constants; +use Mojo::Base 'OpenQA::Command', -signatures; +use Mojo::Util qw(encode getopt); + +has description => 'Monitors a set of jobs'; +has usage => sub { shift->extract_usage }; + +sub _monitor_jobs ($self, $client, $poll_interval, $job_ids, $job_results) { + while (@$job_results < @$job_ids) { + my $job_id = $job_ids->[@$job_results]; + my $tx = $client->build_tx(GET => $self->url_for("experimental/jobs/$job_id/status"), {}); + my $res = $self->retry_tx($client, $tx); + return $res if $res != 0; + my $job = $tx->res->json; + my $job_state = $job->{state} // NONE; + if (OpenQA::Jobs::Constants::meta_state($job_state) eq OpenQA::Jobs::Constants::FINAL) { + push @$job_results, $job->{result} // NONE; + next; + } + print encode('UTF-8', "Job state of job ID $job_id: $job_state, waiting …\n"); + sleep $poll_interval; + } +} + +sub _compute_return_code ($self, $job_results) { + for my $job_result (@$job_results) { + return 2 unless OpenQA::Jobs::Constants::is_ok_result($job_result); + } + return 0; +} + +sub _monitor_and_return ($self, $client, $poll_interval, $job_ids) { + my @job_results; + my $monitor_res = $self->_monitor_jobs($client, $poll_interval // 10, $job_ids, \@job_results); + return $monitor_res if $monitor_res != 0; + return $self->_compute_return_code(\@job_results); +} + +sub command ($self, @args) { + die $self->usage unless getopt \@args, 'i|poll-interval=i' => \my $poll_interval; + @args = $self->decode_args(@args); + $self->_monitor_and_return($self->client($self->url_for('tests')), $poll_interval, \@args); +} + +1; + +=encoding utf8 + +=head1 SYNOPSIS + + Usage: openqa-cli monitor [OPTIONS] [job_id…] + + Options: + --apibase API base, defaults to /api/v1 + --apikey API key + --apisecret API secret + --host Target host, defaults to http://localhost + -h, --help Show this summary of available options + --osd Set target host to http://openqa.suse.de + --o3 Set target host to https://openqa.opensuse.org + --name Name of this client, used by openQA to + identify different clients via User-Agent + header, defaults to "openqa-cli" + -i, --poll-interval Specifies the poll interval + -p, --pretty Pretty print JSON content + -q, --quiet Do not print error messages to STDERR + -v, --verbose Print HTTP response headers + +=cut diff --git a/lib/OpenQA/CLI/schedule.pm b/lib/OpenQA/CLI/schedule.pm index e506d1b2ee70..df38cddc33fc 100644 --- a/lib/OpenQA/CLI/schedule.pm +++ b/lib/OpenQA/CLI/schedule.pm @@ -2,8 +2,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later package OpenQA::CLI::schedule; -use OpenQA::Jobs::Constants; -use Mojo::Base 'OpenQA::Command', -signatures; +use Mojo::Base 'OpenQA::CLI::monitor', -signatures; use Mojo::Util qw(encode getopt); use Term::ANSIColor qw(colored); @@ -29,30 +28,6 @@ sub _create_jobs ($self, $client, $args, $param_file, $job_ids) { return 1; } -sub _monitor_jobs ($self, $client, $poll_interval, $job_ids, $job_results) { - while (@$job_results < @$job_ids) { - my $job_id = $job_ids->[@$job_results]; - my $tx = $client->build_tx(GET => $self->url_for("experimental/jobs/$job_id/status"), {}); - my $res = $self->retry_tx($client, $tx); - return $res if $res != 0; - my $job = $tx->res->json; - my $job_state = $job->{state} // NONE; - if (OpenQA::Jobs::Constants::meta_state($job_state) eq OpenQA::Jobs::Constants::FINAL) { - push @$job_results, $job->{result} // NONE; - next; - } - print encode('UTF-8', "Job state of job ID $job_id: $job_state, waiting …\n"); - sleep $poll_interval; - } -} - -sub _compute_return_code ($self, $job_results) { - for my $job_result (@$job_results) { - return 2 unless OpenQA::Jobs::Constants::is_ok_result($job_result); - } - return 0; -} - sub command ($self, @args) { die $self->usage unless getopt \@args, @@ -66,11 +41,7 @@ sub command ($self, @args) { my @job_ids; my $create_res = $self->_create_jobs($client, \@args, \@param_file, \@job_ids); return $create_res if $create_res != 0 || !$monitor; - - my @job_results; - my $monitor_res = $self->_monitor_jobs($client, $poll_interval // 10, \@job_ids, \@job_results); - return $monitor_res if $monitor_res != 0; - return $self->_compute_return_code(\@job_results); + return $self->_monitor_and_return($client, $poll_interval, \@job_ids); } 1; diff --git a/t/43-cli-schedule.t b/t/43-cli-schedule.t index 8a8cec233f8a..138913408937 100644 --- a/t/43-cli-schedule.t +++ b/t/43-cli-schedule.t @@ -10,6 +10,7 @@ use lib "$FindBin::Bin/lib", "$FindBin::Bin/../external/os-autoinst-common/lib"; use OpenQA::Test::TimeLimit '7'; use OpenQA::CLI; +use OpenQA::CLI::monitor; use OpenQA::CLI::schedule; use OpenQA::Jobs::Constants; use OpenQA::Test::Case; @@ -18,9 +19,10 @@ use Mojo::File qw(tempdir tempfile); use Test::Output qw(combined_like); use Test::MockModule; -my $schedule = OpenQA::CLI::schedule->new; $ENV{OPENQA_CLI_RETRIES} = 0; -OpenQA::Test::Case->new->init_data(fixtures_glob => '03-users.pl'); + +my $schema = OpenQA::Test::Case->new->init_data(fixtures_glob => '03-users.pl'); +my $schedule = OpenQA::CLI::schedule->new; # change API to simulate job state/result changes my $job_controller_mock = Test::MockModule->new('OpenQA::WebAPI::Controller::API::V1::Job'); @@ -50,7 +52,8 @@ subtest 'unknown options' => sub { }; # define different sets of CLI args to be used in further tests -my @options = ('--apikey', 'ARTHURKEY01', '--apisecret', 'EXCALIBUR', '--host', $host, '-m', '-i', 0); +my @basic_options = ('--apikey', 'ARTHURKEY01', '--apisecret', 'EXCALIBUR', '--host', $host, '-i', 0); +my @options = (@basic_options, '-m'); my @scenarios = ('--param-file', "SCENARIO_DEFINITIONS_YAML=$FindBin::Bin/data/09-schedule_from_file.yaml"); my @settings1 = (qw(DISTRI=example VERSION=0 FLAVOR=DVD ARCH=x86_64 TEST=simple_boot)); my @settings2 = (qw(DISTRI=opensuse VERSION=13.1 FLAVOR=DVD ARCH=i586 BUILD=0091 TEST=autoyast_btrfs)); @@ -85,4 +88,17 @@ subtest 'scheduling and monitoring set of two jobs' => sub { is $res, 2, 'non-zero return-code if at least one job is not ok'; }; +subtest 'monitor jobs as a separate command' => sub { + my $res; + my $monitor = OpenQA::CLI::monitor->new; + my $jobs = $schema->resultset('Jobs'); + $jobs->create({id => $_, TEST => "test-$_"}) for (100 .. 103); + @job_mock_results = (PASSED, SOFTFAILED); + combined_like { $res = $monitor->run(@basic_options, 100, 101) } qr/100.*101/s, 'status logged (passing case)'; + is $res, 0, 'zero return-code if all jobs ok'; + @job_mock_results = (PASSED, FAILED); + combined_like { $res = $monitor->run(@basic_options, 102, 103) } qr/102.*103/s, 'status logged (failing case)'; + is $res, 2, 'none-zero return-code if one job failed'; +}; + done_testing();