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
2 changes: 2 additions & 0 deletions lib/OpenQA/Worker.pm
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ has 'current_error_is_fatal';
has 'worker_hostname';
has 'isotovideo_interface_version';

sub encode_token ($entry_string) { OpenQA::Worker::Settings::encode_token(split /@/, $entry_string) }

sub new ($class, $cli_options) {
# determine instance number
my $instance_number = $cli_options->{instance};
Expand Down
30 changes: 30 additions & 0 deletions lib/OpenQA/Worker/Settings.pm
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use Mojo::File 'path';
use Mojo::URL;
use Mojo::Util 'trim';
use Config::IniFiles;
use Feature::Compat::Try;
use MIME::Base64 qw(encode_base64url decode_base64url);
use Time::Seconds;
use OpenQA::Config;
use OpenQA::Log qw(setup_log log_info);
Expand Down Expand Up @@ -45,6 +47,15 @@ sub new ($class, $instance_number = undef, $cli_options = {}) {
my @hosts = split(' ', $cli_options->{host} || $global_settings{HOST} || 'localhost');
delete $global_settings{HOST};
_read_section($cfg, $_, $webui_host_specific_settings{$_} = {}) for @hosts;
my %tokens = map {
my ($host, $key, $secret) = decode_token($_);
$host => {key => $key, secret => $secret}
} split /,/, $ENV{OPENQA_WORKER_TOKEN} // $cli_options->{token} // '';
for (keys %tokens) {
$webui_host_specific_settings{$_} ||= {};
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
$webui_host_specific_settings{$_} ||= {};

Copy link
Member Author

Choose a reason for hiding this comment

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

so you think this line isn't necessary? Isn't that needed for auto-vivification so that we can write values into the inner hash?

Copy link
Contributor

Choose a reason for hiding this comment

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

Auto-vivifaction is exactly why it isn't needed, that's why it's called "auto" :)

$webui_host_specific_settings{$_}->{apikey} = $tokens{$_}{key};
$webui_host_specific_settings{$_}->{apisecret} = $tokens{$_}{secret};
}

# Select sensible system CPU load15 threshold to prevent system overload
# based on experiences with system stability so far
Expand Down Expand Up @@ -144,4 +155,23 @@ sub has_class ($self, $worker_class) {
return exists $c->{$worker_class};
}

# Generate a length-optimized, URL escaped, base64 encoded worker token string
# with a prefix abbreviated for "openQA worker token"
sub encode_token ($host, $key, $secret) {
my ($key_bin, $secret_bin) = map { pack 'H*', $_ } ($key, $secret);
return 'oqwt-' . encode_base64url join "\x00", $host, $key_bin, $secret_bin;
}

sub decode_token ($token) {
return undef unless my $encoded_data = $token =~ s/^oqwt-//ir;
my $raw_data = decode_base64url($encoded_data);
return undef unless $raw_data;
my @parts = split /\x00/, $raw_data, 3;
return undef unless @parts == 3;
my $host = $parts[0];
my $key = uc unpack 'H*', $parts[1];
my $secret = uc unpack 'H*', $parts[2];
return ($host, $key, $secret);
}

1;
13 changes: 10 additions & 3 deletions lib/OpenQA/Worker/WebUIConnection.pm
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,19 @@
# the websocket connection to receive commands from the web UI and send the status (Mojo::Transaction::WebSockets instance)
has 'websocket_connection';

sub new ($class, $webui_host, $cli_options) {


sub new ($class, $webui_host, $args) {
$webui_host = $ENV{OPENQA_WORKER_WEBUI_HOST} // $webui_host;
my $url = $webui_host !~ '/' ? Mojo::URL->new->scheme('http')->host_port($webui_host) : Mojo::URL->new($webui_host);
my ($host, $key, $secret) = split /|/, $ENV{OPENQA_WORKER_TOKEN} if $ENV{OPENQA_WORKER_TOKEN};

Check warning on line 33 in lib/OpenQA/Worker/WebUIConnection.pm

View workflow job for this annotation

GitHub Actions / Perlcritic

Variable declared in conditional statement - severity 4

[Variables::ProhibitConditionalDeclarations] Declare variables outside of the condition
Copy link
Contributor

@perlpunk perlpunk Sep 25, 2025

Choose a reason for hiding this comment

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

perl -wE'@a = split /|/, "abc|de"; say for @a'
a
b
c
|
d
e

You want split m/\|/

Copy link
Contributor

Choose a reason for hiding this comment

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

Why not reuse the decode_token here?

$host //= $ENV{OPENQA_WORKER_HOST};
$key //= $ENV{OPENQA_WORKER_APIKEY};
$secret //= $ENV{OPENQA_WORKER_APISECRET};
my $ua = OpenQA::Client->new(
api => $url->host,
apikey => $cli_options->{apikey},
apisecret => $cli_options->{apisecret},
apikey => $ENV{OPENQA_WORKER_APIKEY} // $args->{apikey},
apisecret => $ENV{OPENQA_WORKER_APISECRET} // $args->{apisecret},
);
$ua->base_url($url);

Expand Down
16 changes: 14 additions & 2 deletions script/worker
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ specify the public key needed for API authentication

specify the secret key needed for API authentication

=item B<--token> <value>

specify a comma-separated list of tokens for C<host>@C<key>@C<secret>
combinations as alternative to the individual options. Alternatively use the
environment variable C<OPENQA_WORKER_TOKEN>.

=item B<--encode-token> HOST@KEY@SECRET

Encode a token as string for the specified host+key+secret combination

=item B<--isotovideo> PATH

path to isotovideo script, useful for running from git
Expand Down Expand Up @@ -97,11 +107,13 @@ my %options;
sub usage ($r) { require Pod::Usage; Pod::Usage::pod2usage($r) }

GetOptions(
\%options, "no-cleanup", "instance=i", "isotovideo=s", "host=s", "apikey:s",
"apisecret:s", "verbose|v|debug|d", "help|h",
\%options, "no-cleanup", "instance=i", "isotovideo=s",
"host=s", "apikey:s", "apisecret:s", "token:s",
"encode-token:s", "verbose|v|debug|d", "help|h",
) or usage(1);

usage(0) if ($options{help});
exit !OpenQA::Worker::encode_token($options{encode-token}) if $options{encode-token};
Copy link
Contributor

Choose a reason for hiding this comment

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

Unquoted string "encode" may clash with future reserved word at -e line 1.
Argument "token" isn't numeric in subtraction (-) at -e line 1.
Argument "encode" isn't numeric in subtraction (-) at -e line 1.

Copy link
Member Author

Choose a reason for hiding this comment

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

sorry, I missed some local changes for that


# count workers from 1 if not set - if tap devices are used worker would try to use tap -1
$options{instance} ||= 1;
Expand Down
4 changes: 4 additions & 0 deletions t/24-worker-overall.t
Original file line number Diff line number Diff line change
Expand Up @@ -987,5 +987,9 @@ subtest 'worker ipmi' => sub {
'ipmitool called correctly';
};

subtest 'worker token generation' => sub {
is OpenQA::Worker::encode_token('my_host@123456@789ABC'), 'owqt-', 'can generate token';
};


done_testing();
17 changes: 17 additions & 0 deletions t/24-worker-settings.t
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,23 @@ subtest 'check for local worker' => sub {
ok $settings->is_local_worker, 'considered local with localhost:9527 and remotehost being changed to ::1';
};

subtest 'worker tokens' => sub {
my $host = 'my_host';
my $key = '1234567890ABCDEF';
my $secret = '1234567890ABCDEF';
my $token = 'oqwt-bXlfaG9zdAASNFZ4kKvN7wASNFZ4kKvN7w';
is OpenQA::Worker::Settings::encode_token($host, $key, $secret), $token, 'worker can encode expected token';
is_deeply [OpenQA::Worker::Settings::decode_token($token)], [$host, $key, $secret], 'worker can decode token';
my $settings = OpenQA::Worker::Settings->new(1, {token => $token});
is_deeply(
$settings->webui_host_specific_settings->{my_host},
{apikey => $key, apisecret => $secret},
'credentials parsed correctly from optimized oqwt-token'
) or always_explain $settings->webui_host_specific_settings;
is $settings->webui_host_specific_settings->{'https://remotehost'}{HOST_SPECIFIC}, 'specific setting (remotehost)',
'other settings preserved';
};

subtest 'apply settings to app' => sub {
my ($setup_log_called, $setup_log_app);
my $mock = Test::MockModule->new('OpenQA::Worker::Settings');
Expand Down
Loading