Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/OpenQA/Schema/Result/Jobs.pm
Original file line number Diff line number Diff line change
Expand Up @@ -994,7 +994,7 @@ sub auto_duplicate ($self, $args = {}) {
# report status back to GitHub for affected scheduled products
my $scheduled_products = $rsource->schema->resultset('ScheduledProducts');
my %related_scheduled_products = (id => {-in => [keys %related_scheduled_product_ids]});
$_->report_status_to_github for $scheduled_products->search(\%related_scheduled_products);
$_->report_status_to_git for $scheduled_products->search(\%related_scheduled_products);

my $clone_id = $clones->{$job_id}->{clone};
my $dup = $rsource->resultset->find($clone_id);
Expand Down Expand Up @@ -2158,7 +2158,7 @@ sub done ($self, %args) {

# report back to GitHub if this job is part of a CI check which has concluded with this job
if (my $sp_id = $self->related_scheduled_product_id) {
$self->result_source->schema->resultset('ScheduledProducts')->find($sp_id)->report_status_to_github;
$self->result_source->schema->resultset('ScheduledProducts')->find($sp_id)->report_status_to_git;
}

# enqueue the finalize job only after stopping the cluster so in case the job should be restarted the cluster
Expand Down
12 changes: 6 additions & 6 deletions lib/OpenQA/Schema/Result/ScheduledProducts.pm
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ sub set_done ($self, $result) {
}
else {
$self->update({status => SCHEDULED, results => $result}); # … SCHEDULED if remained SCHEDULING
$self->report_status_to_github;
$self->report_status_to_git;
}
}

Expand Down Expand Up @@ -932,16 +932,16 @@ sub _format_check_description ($verb, $count, $total) {
return "All $total openQA jobs $verb";
}

sub report_status_to_github ($self, $callback = undef) {
sub report_status_to_git ($self, $callback = undef) {
my $id = $self->id;
my $settings = $self->{_settings} // $self->settings;
return undef unless my $github_statuses_url = $settings->{GITHUB_STATUSES_URL};
return undef unless $self->webhook_id;
my $vcs = OpenQA::VcsProvider->new(type => $self->webhook_id, app => OpenQA::App->singleton);
$vcs->read_settings($settings) or return undef;
my ($state, $verb, $count, $total) = $self->state_for_ci_status;
return undef unless $state;
my $vcs = OpenQA::VcsProvider->new(app => OpenQA::App->singleton);
my $base_url = $settings->{CI_TARGET_URL};
my %params = (state => $state, description => _format_check_description($verb, $count, $total));
$vcs->report_status_to_github($github_statuses_url, \%params, $id, $base_url, $callback);
$vcs->report_status_to_git(\%params, $id, $callback);
}

sub cancel ($self, $reason = undef) {
Expand Down
2 changes: 1 addition & 1 deletion lib/OpenQA/Setup.pm
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ sub read_config ($app) {
'assets/storage_duration' => {
# intentionally left blank for overview
},
secrets => {github_token => ''},
secrets => {github_token => '', gitea_token => ''},
# allow dynamic config keys based on job results
hooks => {},
influxdb => {
Expand Down
35 changes: 8 additions & 27 deletions lib/OpenQA/VcsProvider.pm
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,15 @@

package OpenQA::VcsProvider;

use Mojo::Base -base, -signatures;
use Mojo::JSON qw(encode_json);
use Mojo::URL;
use Mojo::Base -signatures;
use OpenQA::VcsProvider::GitHub;
use OpenQA::VcsProvider::Gitea;

has 'app';

sub report_status_to_github ($self, $statuses_url, $params, $scheduled_product_id, $base_url, $callback = undef) {
$params->{context} //= 'openqa';
$params->{description} //= 'openQA test run';
$params->{target_url} //= "$base_url/admin/productlog?id=$scheduled_product_id"
if $scheduled_product_id && $base_url;

my $url = Mojo::URL->new($statuses_url);
my $app = $self->app;
my $ua = $app->ua;
my $tx = $ua->build_tx(POST => $url);
my $req = $tx->req;
my $headers = $req->headers;
my $github_token = $app->config->{secrets}->{github_token};
my $json = encode_json($params);
$req->body($json);
$headers->content_type('application/json');
$headers->content_length(length $json);
$headers->header(Accept => 'application/vnd.github+json');
$headers->header(Authorization => "Bearer $github_token");
$headers->header('X-GitHub-Api-Version' => '2022-11-28');
$ua->start($tx, $callback);
return $tx;
sub new ($class, %args) {
my $type = delete $args{type};
my ($provider) = split m/:/, $type;
$class = {gh => 'GitHub', gitea => 'Gitea'}->{$provider} or return undef;
return "OpenQA::VcsProvider::$class"->new(%args);
}

1;
49 changes: 49 additions & 0 deletions lib/OpenQA/VcsProvider/Base.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright SUSE LLC
# SPDX-License-Identifier: GPL-2.0-or-later

package OpenQA::VcsProvider::Base;

use Mojo::Base -base, -signatures;
use Mojo::JSON qw(encode_json);
use Mojo::URL;

has 'app';
has 'base_url';
has 'statuses_url';

sub add_params ($self, $params, $scheduled_product_id) {
$params->{context} //= 'openqa';
$params->{description} //= 'openQA test run';
my $base_url = $self->base_url;
$params->{target_url} //= "$base_url/admin/productlog?id=$scheduled_product_id"
if $scheduled_product_id && $base_url;
}

sub create_request ($self, $params) {
my $app = $self->app;
my $ua = $app->ua;
# TODO Note that anyone who can create an openQA job can set settings
# with a webhook id and a statuses URL. Maybe we should configure the
# base url for each git provider and double check the url, because
# we are making an API request with a token to an otherwise unchecked URL
Comment on lines +25 to +28
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just commenting to be able to link to this more easily

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should definitely add such a check.

my $url = Mojo::URL->new($self->statuses_url);
my $tx = $ua->build_tx(POST => $url);
my $req = $tx->req;
my $json = encode_json($params);
$req->body($json);
my $headers = $req->headers;
$headers->content_type('application/json');
$headers->content_length(length $json);

return $tx;
}

sub report_status_to_git ($self, $params, $scheduled_product_id, $callback = undef) {
$self->add_params($params, $scheduled_product_id);

my $tx = $self->create_request($params);
$self->app->ua->start($tx, $callback);
return $tx;
}

1;
27 changes: 27 additions & 0 deletions lib/OpenQA/VcsProvider/GitHub.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright SUSE LLC
# SPDX-License-Identifier: GPL-2.0-or-later

package OpenQA::VcsProvider::GitHub;

use Mojo::Base 'OpenQA::VcsProvider::Base', -signatures;

sub read_settings ($self, $settings) {
$self->statuses_url($settings->{GITHUB_STATUSES_URL});
$self->base_url($settings->{CI_TARGET_URL});
return undef unless $self->statuses_url;
return 1;
}

sub create_request ($self, $params) {
my $tx = $self->SUPER::create_request($params);

my $headers = $tx->req->headers;
my $github_token = $self->app->config->{secrets}->{github_token};
$headers->header(Accept => 'application/vnd.github+json');
$headers->header(Authorization => "Bearer $github_token");
$headers->header('X-GitHub-Api-Version' => '2022-11-28');

return $tx;
}

1;
27 changes: 27 additions & 0 deletions lib/OpenQA/VcsProvider/Gitea.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Copyright SUSE LLC
# SPDX-License-Identifier: GPL-2.0-or-later

package OpenQA::VcsProvider::Gitea;

use Mojo::Base 'OpenQA::VcsProvider::Base', -signatures;

sub read_settings ($self, $settings) {
$self->statuses_url($settings->{GITEA_STATUSES_URL});
$self->base_url($settings->{CI_TARGET_URL});
return undef unless $self->statuses_url;
return 1;
}

sub create_request ($self, $params) {
my $tx = $self->SUPER::create_request($params);

my $headers = $tx->req->headers;
# TODO there might be more than one gitea server -> add sections?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just commenting to be able to link to this more easily

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably need to be able to configure one token per instance, so yes we probably need multiple sections.

my $token = $self->app->config->{secrets}->{gitea_token};
$headers->header(Accept => 'application/json');
$headers->header(Authorization => "Bearer $token");

return $tx;
}

1;
3 changes: 2 additions & 1 deletion lib/OpenQA/WebAPI/Controller/API/V1/Iso.pm
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ sub create {

my $params = $self->req->params->to_hash;
my $async = delete $params->{async}; # whether to run the operation as a Minion job
my $webhook_id = delete $params->{webhook_id};
my $scheduled_product_clone_id
= delete $params->{scheduled_product_clone_id}; # ID of a previous product to clone settings from
my $log = $self->app->log;
Expand Down Expand Up @@ -192,7 +193,7 @@ sub create {
return undef unless $self->validate_download_parameters(\%params);

# add entry to ScheduledProducts table and log event
my $scheduled_product = $scheduled_products->create_with_event(\%params, $self->current_user);
my $scheduled_product = $scheduled_products->create_with_event(\%params, $self->current_user, $webhook_id);
my $scheduled_product_id = $scheduled_product->id;

# only spawn Minion job and return IDs if async flag has been passed
Expand Down
6 changes: 4 additions & 2 deletions lib/OpenQA/WebAPI/Controller/API/V1/Webhook.pm
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ sub product ($self) {

# create scheduled product and enqueue minion job with parameter
my $scheduled_product = $scheduled_products->create_with_event(\%params, $self->current_user, $webhook_id);
my $vcs = OpenQA::VcsProvider->new(app => $app);
my $vcs = OpenQA::VcsProvider->new(type => $webhook_id, app => $app);
my $cb = sub ($ua, $tx, @) {
if (my $err = $tx->error) {
$scheduled_product->delete;
Expand All @@ -153,7 +153,9 @@ sub product ($self) {
return $self->render(json => $scheduled_product->enqueue_minion_job(\%params));
};
$scheduled_product->discard_changes; # load value of columns that have a default value
my $tx = $vcs->report_status_to_github($statuses_url, {state => 'pending'}, $scheduled_product->id, $base_url, $cb);
$vcs->statuses_url($statuses_url);
$vcs->base_url($base_url);
my $tx = $vcs->report_status_to_git({state => 'pending'}, $scheduled_product->id, $cb);
}

1;
26 changes: 20 additions & 6 deletions t/16-utils-vcs-provider.t
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,26 @@

use Test::Most;

use Mojo::Base -signatures;
use FindBin;
use lib "$FindBin::Bin/lib", "$FindBin::Bin/../external/os-autoinst-common/lib";
require OpenQA::Test::Database;
use OpenQA::Test::TimeLimit '5';
use OpenQA::VcsProvider;
use OpenQA::VcsProvider::GitHub;
use OpenQA::VcsProvider::Gitea;
use Test::Mojo;
use Test::Warnings ':report_warnings';

my $schema = OpenQA::Test::Database->new->create(fixtures_glob => '03-users.pl');
my $t = Test::Mojo->new('OpenQA::WebAPI');

subtest 'reporting status to GitHub' => sub {
sub test_report_status_to_git ($app, $statuses_url_key, $webhook_id) {
# avoid making an actual query to GitHub; this test just checks whether an expected request would have been done
my $app = $t->app;
$app->config->{secrets}->{github_token} = 'some-token';

my $git = OpenQA::VcsProvider->new(app => $app);
my $git = OpenQA::VcsProvider->new(app => $app, type => $webhook_id);
my $url = 'http://127.0.0.1/repos/some/repo/statuses/some-sha';
my $tx = $git->report_status_to_github($url, {state => 'pending'}, '42', 'https://openqa.opensuse.org');
$git->read_settings({$statuses_url_key => $url, CI_TARGET_URL => 'https://openqa.opensuse.org'});
my $tx = $git->report_status_to_git({state => 'pending'}, '42');
my $req = $tx->req;
is $req->method, 'POST', 'method';
is $req->url, $url, 'URL';
Expand All @@ -37,4 +38,17 @@ subtest 'reporting status to GitHub' => sub {
ok $tx->is_finished, 'transaction has finished (and thus was started in first place)';
};


subtest 'reporting status to GitHub' => sub {
my $app = $t->app;
$app->config->{secrets}->{github_token} = 'some-token';
test_report_status_to_git($app, 'GITHUB_STATUSES_URL', 'gh:pr:123');
};

subtest 'reporting status to Gitea' => sub {
my $app = $t->app;
$app->config->{secrets}->{gitea_token} = 'some-token';
test_report_status_to_git($app, 'GITEA_STATUSES_URL', 'gitea:pr:123');
};

done_testing();
8 changes: 4 additions & 4 deletions t/api/16-webhooks.t
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ subtest 'failure when reporting status to GitHub' => sub {
};

# mock reporting back to GitHub
my $vcs_mock = Test::MockModule->new('OpenQA::VcsProvider');
my $vcs_mock = Test::MockModule->new('OpenQA::VcsProvider::GitHub');
my $minion_job_id;
my $status_reports = 0;
my $expected_sha = '04a3f669ea13a4aa7cbd4569f578a66f7403c43d';
Expand All @@ -100,10 +100,10 @@ my $expected_statuses_url = "https://127.0.0.1/repos/os-autoinst/openQA/statuses
my $expected_ci_check_state = 'pending';
my $expected_ci_check_desc = undef;
$vcs_mock->redefine(
report_status_to_github =>
sub ($self, $statuses_url, $params, $scheduled_product_id, $base_url_from_req, $callback) {
report_status_to_git =>
sub ($self, $params, $scheduled_product_id, $callback) {
my $tx = $ua->build_tx(POST => 'http://dummy');
is $statuses_url, $expected_statuses_url, 'URL from webhook payload used for reporting back';
is $self->statuses_url, $expected_statuses_url, 'URL from webhook payload used for reporting back';
is $params->{state}, $expected_ci_check_state, "check reported to be $expected_ci_check_state";
is $params->{description}, $expected_ci_check_desc, 'check description';
++$status_reports;
Expand Down
Loading