Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package openQA for openSUSE:Factory checked in at 2026-06-03 20:28:51 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/openQA (Old) and /work/SRC/openSUSE:Factory/.openQA.new.1937 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "openQA" Wed Jun 3 20:28:51 2026 rev:850 rq:1357006 version:5.1780410522.ad58d974 Changes: -------- --- /work/SRC/openSUSE:Factory/openQA/openQA.changes 2026-06-02 16:03:50.049722615 +0200 +++ /work/SRC/openSUSE:Factory/.openQA.new.1937/openQA.changes 2026-06-03 20:30:45.101930987 +0200 @@ -1,0 +2,22 @@ +Tue Jun 02 15:54:51 UTC 2026 - [email protected] + +- Update to version 5.1780410522.ad58d974: + * feat(tests): enable parallel-safe fullstack and developer mode tests + * fix: ignore transient 'database is locked' errors in cache service + * git subrepo pull (merge) --force external/os-autoinst-common + * Revert "fix: avoid permission denied on cgroup creation for v2" + * feat: add run-test-env target to start all local services + * fix: add functional API keys for local test environment + * fix: use absolute paths for local test environment + * feat: Improve CLI retry error feedback with detailed messages + * fix: avoid permission denied on cgroup creation for v2 + * ci: Auto-import keys to avoid interactive prompt for devel_openQA + * docs: require atomic commits in agent guidelines + * fix: include file paths in worker logging errors + * chore(AGENTS.md): add rules for ensuring valid commits + * fix: only report job limit in UI when globally reached + * docs: Clarify wording in "Spawning multiple jobs …" + * docs: Fix wrapping in "Medium Types" section + * docs: Fix wrapping and use of blank lines in "Spawning multiple jobs …" + +------------------------------------------------------------------- Old: ---- openQA-5.1780322162.c1836389.obscpio New: ---- openQA-5.1780410522.ad58d974.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ openQA-client-test.spec ++++++ --- /var/tmp/diff_new_pack.9FQARl/_old 2026-06-03 20:30:48.318064179 +0200 +++ /var/tmp/diff_new_pack.9FQARl/_new 2026-06-03 20:30:48.322064346 +0200 @@ -18,7 +18,7 @@ %define short_name openQA-client Name: %{short_name}-test -Version: 5.1780322162.c1836389 +Version: 5.1780410522.ad58d974 Release: 0 Summary: Test package for %{short_name} License: GPL-2.0-or-later ++++++ openQA-devel-test.spec ++++++ --- /var/tmp/diff_new_pack.9FQARl/_old 2026-06-03 20:30:48.362066002 +0200 +++ /var/tmp/diff_new_pack.9FQARl/_new 2026-06-03 20:30:48.362066002 +0200 @@ -18,7 +18,7 @@ %define short_name openQA-devel Name: %{short_name}-test -Version: 5.1780322162.c1836389 +Version: 5.1780410522.ad58d974 Release: 0 Summary: Test package for %{short_name} License: GPL-2.0-or-later ++++++ openQA-test.spec ++++++ --- /var/tmp/diff_new_pack.9FQARl/_old 2026-06-03 20:30:48.406067824 +0200 +++ /var/tmp/diff_new_pack.9FQARl/_new 2026-06-03 20:30:48.410067990 +0200 @@ -18,7 +18,7 @@ %define short_name openQA Name: %{short_name}-test -Version: 5.1780322162.c1836389 +Version: 5.1780410522.ad58d974 Release: 0 Summary: Test package for openQA License: GPL-2.0-or-later ++++++ openQA-worker-test.spec ++++++ --- /var/tmp/diff_new_pack.9FQARl/_old 2026-06-03 20:30:48.442069315 +0200 +++ /var/tmp/diff_new_pack.9FQARl/_new 2026-06-03 20:30:48.446069481 +0200 @@ -18,7 +18,7 @@ %define short_name openQA-worker Name: %{short_name}-test -Version: 5.1780322162.c1836389 +Version: 5.1780410522.ad58d974 Release: 0 Summary: Test package for %{short_name} License: GPL-2.0-or-later ++++++ openQA.spec ++++++ --- /var/tmp/diff_new_pack.9FQARl/_old 2026-06-03 20:30:48.494071469 +0200 +++ /var/tmp/diff_new_pack.9FQARl/_new 2026-06-03 20:30:48.498071635 +0200 @@ -104,7 +104,7 @@ %define devel_requires %devel_no_selenium_requires chromedriver Name: openQA -Version: 5.1780322162.c1836389 +Version: 5.1780410522.ad58d974 Release: 0 Summary: Framework for automated system-level testing (web-frontend, scheduler and tools) Group: Development/Tools/Other ++++++ openQA-5.1780322162.c1836389.obscpio -> openQA-5.1780410522.ad58d974.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/AGENTS.md new/openQA-5.1780410522.ad58d974/AGENTS.md --- old/openQA-5.1780322162.c1836389/AGENTS.md 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/AGENTS.md 2026-06-02 16:28:42.000000000 +0200 @@ -14,13 +14,15 @@ - `cover -report text -select_re 'path/to/modified_file'`: Generate and view a fast text-based coverage report restricted to the specific file you changed (run this after generating the coverage database). +- `make test-gitlint`: Must pass after creating or amending git commits. - `make test`: Full test suite. ## Conventions - Commits: [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format. Include - motivation and details for "feat" commits. + motivation and details for "feat" commits. Atomic commits are REQUIRED: split + refactorings, bug fixes, and features into separate commits. - Documentation: Markdown (`.md`). ## Agent Guidelines @@ -42,4 +44,5 @@ git rm on this directory or delete files from there. - Never run git clean or any command that deletes unversioned files. Ask the user for confirmation. -- Commit message format: 50/80 rule, 80-char limit, wrap in single quotes. +- Commit message format: 50/80 rule. You MUST explicitly insert physical + newlines to hard-wrap the body text at 80 characters. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/Makefile new/openQA-5.1780410522.ad58d974/Makefile --- old/openQA-5.1780322162.c1836389/Makefile 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/Makefile 2026-06-02 16:28:42.000000000 +0200 @@ -308,13 +308,24 @@ test -d $(TEST_PG_PATH) && (pg_ctl -D $(TEST_PG_PATH) -s status >&/dev/null || pg_ctl -D $(TEST_PG_PATH) -s start) || ./t/test_postgresql $(TEST_PG_PATH) define RUN_SERVICE_TEST_ENV - OPENQA_BASEDIR=t/data \ + OPENQA_BASEDIR="$(CURDIR)/t/data" \ + OPENQA_CONFIG="$(CURDIR)/t/data" \ OPENQA_DATABASE=test \ OPENQA_WEBUI_MODE=test \ TEST_PG="DBI:Pg:dbname=openqa_test;host=$(TEST_PG_PATH)" \ $(1) endef +.PHONY: run-test-env +run-test-env: setup-database ## Run all local test services (webui, websockets, scheduler, worker, gru) + trap 'kill 0' SIGINT SIGTERM; \ + $(call RUN_SERVICE_TEST_ENV,script/openqa-webui-daemon) & \ + $(call RUN_SERVICE_TEST_ENV,script/openqa-websockets-daemon) & \ + $(call RUN_SERVICE_TEST_ENV,script/openqa-scheduler-daemon) & \ + $(call RUN_SERVICE_TEST_ENV,script/worker) & \ + $(call RUN_SERVICE_TEST_ENV,script/openqa-gru) & \ + wait + .PHONY: run-webui-test-env run-webui-test-env: setup-database ## Run a local web UI instance using a test environment $(call RUN_SERVICE_TEST_ENV,script/openqa-webui-daemon) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/docs/UsersGuide.md new/openQA-5.1780410522.ad58d974/docs/UsersGuide.md --- old/openQA-5.1780322162.c1836389/docs/UsersGuide.md 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/docs/UsersGuide.md 2026-06-02 16:28:42.000000000 +0200 @@ -122,9 +122,11 @@ - `PROMO` marks the promotional product. - `RESCUECD` is set to 1 for rescue CD images. -The version of a medium can be set to `*`. Then this medium is considered if a product is scheduled with a `VERSION` parameter that does not match any other +The version of a medium can be set to `*`. Then this medium is considered if a +product is scheduled with a `VERSION` parameter that does not match any other medium. This allows having only one medium per version. Note that having a -medium with a concrete version and one with `*` at the same time is usually not a good idea as you will likely run into the scheduling error `no templates found` +medium with a concrete version and one with `*` at the same time is usually not +a good idea as you will likely run into the scheduling error `no templates found` `for product …` (unless you actually have job templates for all these medium types). @@ -1245,58 +1247,58 @@ details about these tables. Alternatively, these settings can be supplied via a [YAML document](UsersGuide.md#defining-test-scenarios-in-yaml). -Additionally to the necessary template matching parameters `DISTRI`, `VERSION`, -`FLAVOR` and `ARCH` more parameters can be specified. Those additional -parameters will be added as jobs settings in all triggered jobs. - -If there is no medium type matching the specified `VERSION`, the lookup falls back to matching any medium with version `*`. Check out -[the section about medium types](UsersGuide.md#medium_types_products) -for details. +The necessary parameters `DISTRI`, `VERSION`, `FLAVOR` and `ARCH` are matched +against medium types (product definitions). Jobs will be generated for the +matching medium types based on the relevant job templates for each job group. + +If there is no medium type matching the specified `VERSION`, the lookup falls +back to matching any medium with version `*`. Check out +[the section about medium types](UsersGuide.md#medium_types_products) for +details. The parameters `MACHINE` and `TEST` additionally act as filters and `TEST` -supports multiple comma-separated values. So adding e.g. `TEST=foo,bar` will only consider the test suites `foo` and `bar`. +supports multiple comma-separated values. So adding e.g. `TEST=foo,bar` will +only consider the test suites `foo` and `bar`. + +It is also possible to add additional parameters like `FOO=bar`. These +parameters won't enable any special behavior but they will be added as jobs +settings in all triggered jobs. There are also special parameters which only have an influence on the way the triggering itself is done. These parameters all start with a leading underscore but are set as request parameters in the same way as the other parameters. - - - - The following scheduling parameters exist - - \_OBSOLETE -Obsolete jobs in older builds with same DISTRI and VERSION -(The default behavior is not obsoleting). With this option jobs which are currently pending, +Obsolete jobs in older builds with same DISTRI and VERSION. (The default +behavior is not obsoleting). With this option jobs which are currently pending, for example scheduled or running, are cancelled when a new medium is triggered. \_DEPRIORITIZEBUILD -Setting this switch to '1' will deprioritize the -unfinished jobs of old builds, and it will obsolete the jobs once the -configurable limit of the priority value is reached. +Setting this switch to '1' will deprioritize the unfinished jobs of old builds, +and it will obsolete the jobs once the configurable limit of the priority value +is reached. \_DEPRIORITIZE_LIMIT -The configurable limit of priority value up to which -jobs should be deprioritized. Needs `_DEPRIORITIZEBUILD`. Defaults to 100. +The configurable limit of priority value up to which jobs should be +deprioritized. Needs `_DEPRIORITIZEBUILD`. Defaults to 100. \_ONLY_OBSOLETE_SAME_BUILD Only obsolete (or deprioritize) jobs for the same BUILD. -This is useful for cases where a new build appearing does not necessarily -mean existing jobs for earlier builds with the same DISTRI and VERSION are -no longer interesting, but you still want to be able to re-submit jobs for a -build and have existing jobs for the exact same build obsoleted. Needs `_OBSOLETE`. +This is useful for cases where a new build appearing does not necessarily mean +existing jobs for earlier builds with the same DISTRI and VERSION are no longer +interesting, but you still want to be able to re-submit jobs for a build and +have existing jobs for the exact same build obsoleted. Needs `_OBSOLETE`. \_SKIP_CHAINED_DEPS -Do not schedule parent test suites which are specified in `START_AFTER_TEST` -or `START_DIRECTLY_AFTER_TEST`. +Do not schedule parent test suites which are specified in `START_AFTER_TEST` or +`START_DIRECTLY_AFTER_TEST`. \_INCLUDE_CHILDREN -Include children that would otherwise not be considered when -filtering test suites via the `TEST` parameter. +Include children that would otherwise not be considered when filtering test +suites via the `TEST` parameter. \_GROUP Job templates **not** matching the given group name are ignored. Does **not** @@ -1314,9 +1316,7 @@ Those parameters can be used to store additional information about the scheduled product itself, e.g. the URL of a web page with more context. - - -Example for `_DEPRIORITIZEBUILD` and `_DEPRIORITIZE_LIMIT`. +Example for `_DEPRIORITIZEBUILD` and `_DEPRIORITIZE_LIMIT`: ```sh openqa-cli api -X POST isos async=0 ISO=my_iso.iso DISTRI=my_distri \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/external/os-autoinst-common/.github/workflows/test.yaml new/openQA-5.1780410522.ad58d974/external/os-autoinst-common/.github/workflows/test.yaml --- old/openQA-5.1780322162.c1836389/external/os-autoinst-common/.github/workflows/test.yaml 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/external/os-autoinst-common/.github/workflows/test.yaml 2026-06-02 16:28:42.000000000 +0200 @@ -9,7 +9,7 @@ contents: read runs-on: ubuntu-latest container: - image: perldocker/perl-tester + image: registry.opensuse.org/devel/openqa/containers/os-autoinst_dev steps: - uses: actions/checkout@v6 - run: make test-t diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/external/os-autoinst-common/.gitrepo new/openQA-5.1780410522.ad58d974/external/os-autoinst-common/.gitrepo --- old/openQA-5.1780322162.c1836389/external/os-autoinst-common/.gitrepo 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/external/os-autoinst-common/.gitrepo 2026-06-02 16:28:42.000000000 +0200 @@ -6,7 +6,7 @@ [subrepo] remote = [email protected]:os-autoinst/os-autoinst-common.git branch = master - commit = 0e48179240f9383db02b76a265e15f865e632ff3 - parent = 005599846d8beb42cb523c87d2a20c0693f8da43 + commit = 0a185ba03e7a44fa2353016d7702190acfdaab5d + parent = 93fed5d3ab0eba22d8cea9f9685904ee8a612823 method = merge cmdver = 0.4.6 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/external/os-autoinst-common/cpanfile new/openQA-5.1780410522.ad58d974/external/os-autoinst-common/cpanfile --- old/openQA-5.1780322162.c1836389/external/os-autoinst-common/cpanfile 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/external/os-autoinst-common/cpanfile 2026-06-02 16:28:42.000000000 +0200 @@ -8,6 +8,7 @@ requires 'Storable', '>= 3.06'; on 'test' => sub { + requires 'Test::Compile'; requires 'Test::Most'; requires 'Test::Warnings'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/external/os-autoinst-common/dependencies.yaml new/openQA-5.1780410522.ad58d974/external/os-autoinst-common/dependencies.yaml --- old/openQA-5.1780322162.c1836389/external/os-autoinst-common/dependencies.yaml 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/external/os-autoinst-common/dependencies.yaml 2026-06-02 16:28:42.000000000 +0200 @@ -31,5 +31,6 @@ perl(Devel::Cover::Report::Codecov): test_requires: + perl(Test::Compile): perl(Test::Most): perl(Test::Warnings): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/external/os-autoinst-common/lib/OpenQA/Test/TimeLimit.pm new/openQA-5.1780410522.ad58d974/external/os-autoinst-common/lib/OpenQA/Test/TimeLimit.pm --- old/openQA-5.1780322162.c1836389/external/os-autoinst-common/lib/OpenQA/Test/TimeLimit.pm 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/external/os-autoinst-common/lib/OpenQA/Test/TimeLimit.pm 2026-06-02 16:28:42.000000000 +0200 @@ -4,23 +4,30 @@ package OpenQA::Test::TimeLimit; use Test::Most; -my $SCALE_FACTOR = $ENV{OPENQA_TEST_TIMEOUT_SCALE_FACTOR} // 1; +my $SCALE_FACTOR; + +sub _calculate_scale_factor { + return $SCALE_FACTOR if defined $SCALE_FACTOR; + $SCALE_FACTOR = $ENV{OPENQA_TEST_TIMEOUT_SCALE_FACTOR} // 1; + $SCALE_FACTOR *= $ENV{OPENQA_TEST_TIMEOUT_SCALE_COVER} // 3 if Devel::Cover->can('report'); + $SCALE_FACTOR *= $ENV{OPENQA_TEST_TIMEOUT_SCALE_CI} // 2 if $ENV{CI}; + $SCALE_FACTOR *= 5 if $ENV{HARNESS_IS_PARALLEL}; + return $SCALE_FACTOR; +} sub import { my ($package, $limit) = @_; - die "$package: Need argument on import, e.g. use: use OpenQA::Test::TimeLimit '42';" unless $limit; + _calculate_scale_factor(); + return unless $limit; # disable timeout if requested by ENV variable or running within debugger return if ($ENV{OPENQA_TEST_TIMEOUT_DISABLE} or $INC{'perl5db.pl'}); - $SCALE_FACTOR *= $ENV{OPENQA_TEST_TIMEOUT_SCALE_COVER} // 3 if Devel::Cover->can('report'); - $SCALE_FACTOR *= $ENV{OPENQA_TEST_TIMEOUT_SCALE_CI} // 2 if $ENV{CI}; - $SCALE_FACTOR *= 5 if $ENV{HARNESS_IS_PARALLEL}; $limit *= $SCALE_FACTOR; $SIG{ALRM} = sub { BAIL_OUT "test '$0' exceeds runtime limit of '$limit' seconds\n" }; alarm $limit; } sub scale_timeout { - return $_[0] * $SCALE_FACTOR; + return $_[0] * _calculate_scale_factor(); } 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/lib/OpenQA/CacheService.pm new/openQA-5.1780410522.ad58d974/lib/OpenQA/CacheService.pm --- old/openQA-5.1780322162.c1836389/lib/OpenQA/CacheService.pm 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/lib/OpenQA/CacheService.pm 2026-06-02 16:28:42.000000000 +0200 @@ -54,6 +54,7 @@ 'reply.exception' => sub ($c, $error) { $error = $c->$code($error)->stash('exception'); return unless $error =~ qr/(database|no such (table|column))/; + return if $error =~ qr/database is locked/i; my $app = $c->app; $app->exit_code(1); # ensure the return code is non-zero diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/lib/OpenQA/Command.pm new/openQA-5.1780410522.ad58d974/lib/OpenQA/Command.pm --- old/openQA-5.1780322162.c1836389/lib/OpenQA/Command.pm 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/lib/OpenQA/Command.pm 2026-06-02 16:28:42.000000000 +0200 @@ -130,8 +130,13 @@ my $res_code = $new_tx->res->code // 0; return $self->handle_result($new_tx, $tx) if $res_code !~ /^(50[23]|0)$/ || $retries <= 0; my $waited = time - $start; + my $error = $new_tx->error; + my $error_msg = $error ? " ($error->{message})" : ''; + my $url = $new_tx->req->url; + my $port = $url->port // ($url->scheme && $url->scheme eq 'https' ? 443 : 80); + my $target = ($url->host || 'localhost') . ":$port"; print STDERR encode('UTF-8', -"Request failed, hit error $res_code, retrying up to $retries more times after waiting … (delay: $delay; waited ${waited}s)\n" +"Request to $target failed, hit error $res_code$error_msg, retrying up to $retries more times after waiting … (delay: $delay; waited ${waited}s)\n" ); sleep $delay; $delay *= $factor; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/lib/OpenQA/Scheduler/DynamicLimit.pm new/openQA-5.1780410522.ad58d974/lib/OpenQA/Scheduler/DynamicLimit.pm --- old/openQA-5.1780322162.c1836389/lib/OpenQA/Scheduler/DynamicLimit.pm 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/lib/OpenQA/Scheduler/DynamicLimit.pm 2026-06-02 16:28:42.000000000 +0200 @@ -60,23 +60,18 @@ # DynamicLimit to the config key names. sub _extract_config ($config) { my $defaults = DEFAULTS(); + my %cfg = map { $_ => $config->{$_} // $defaults->{$_} } keys %$defaults; return { - threshold => _resolve_threshold( - $config->{dynamic_job_limit_load_threshold} // $defaults->{dynamic_job_limit_load_threshold}, - $config->{dynamic_job_limit_load_threshold_factor} // $defaults->{dynamic_job_limit_load_threshold_factor} - ), - critical => _resolve_threshold( - $config->{dynamic_job_limit_load_critical} // $defaults->{dynamic_job_limit_load_critical}, - $config->{dynamic_job_limit_load_critical_factor} // $defaults->{dynamic_job_limit_load_critical_factor} - ), - step => $config->{dynamic_job_limit_step} // $defaults->{dynamic_job_limit_step}, - min => $config->{dynamic_job_limit_min} // $defaults->{dynamic_job_limit_min}, - max => $config->{max_running_jobs} // $defaults->{max_running_jobs}, - interval => $config->{dynamic_job_limit_interval} // $defaults->{dynamic_job_limit_interval}, - scale_up_hysteresis => $config->{dynamic_job_limit_scale_up_hysteresis} - // $defaults->{dynamic_job_limit_scale_up_hysteresis}, - fast_ramp_up_load_factor => $config->{dynamic_job_limit_fast_ramp_up_load_factor} - // $defaults->{dynamic_job_limit_fast_ramp_up_load_factor}, + threshold => + _resolve_threshold($cfg{dynamic_job_limit_load_threshold}, $cfg{dynamic_job_limit_load_threshold_factor}), + critical => + _resolve_threshold($cfg{dynamic_job_limit_load_critical}, $cfg{dynamic_job_limit_load_critical_factor}), + step => $cfg{dynamic_job_limit_step}, + min => $cfg{dynamic_job_limit_min}, + max => $cfg{max_running_jobs}, + interval => $cfg{dynamic_job_limit_interval}, + scale_up_hysteresis => $cfg{dynamic_job_limit_scale_up_hysteresis}, + fast_ramp_up_load_factor => $cfg{dynamic_job_limit_fast_ramp_up_load_factor}, }; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/lib/OpenQA/WebAPI/Controller/Test.pm new/openQA-5.1780410522.ad58d974/lib/OpenQA/WebAPI/Controller/Test.pm --- old/openQA-5.1780322162.c1836389/lib/OpenQA/WebAPI/Controller/Test.pm 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/lib/OpenQA/WebAPI/Controller/Test.pm 2026-06-02 16:28:42.000000000 +0200 @@ -343,14 +343,26 @@ $job_data; } @jobs; my %response = (data => \@running); - my $config = OpenQA::App->singleton->config->{scheduler}; + my $app = OpenQA::App->singleton; + my $config = $app->config->{scheduler}; my $max_running = $config->{max_running_jobs}; + my $has_filters + = $self->get_match_param + || $self->param('comment') + || $self->param('groupid') + || %{$self->every_key_value_param('job_setting')}; + my $global_running + = $has_filters + ? $self->schema->resultset('Jobs')->count({state => [OpenQA::Jobs::Constants::EXECUTION_STATES]}) + : scalar @jobs; if ($config->{dynamic_job_limit_enabled}) { - my $effective = OpenQA::App->singleton->dynamic_limit->current_limit($config); - $response{dynamic_job_limit} = $effective if $effective >= 0 && @running >= $effective; - $response{max_running_jobs} = $max_running if $max_running >= 0; + my $effective = $app->dynamic_limit->current_limit($config); + if ($effective >= 0 && $global_running >= $effective) { + $response{dynamic_job_limit} = $effective; + $response{max_running_jobs} = $max_running if $max_running >= 0; + } } - elsif ($max_running >= 0 && @running >= $max_running) { + elsif ($max_running >= 0 && $global_running >= $max_running) { $response{max_running_jobs} = $max_running; } $self->render(json => \%response); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/lib/OpenQA/Worker/Job.pm new/openQA-5.1780410522.ad58d974/lib/OpenQA/Worker/Job.pm --- old/openQA-5.1780322162.c1836389/lib/OpenQA/Worker/Job.pm 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/lib/OpenQA/Worker/Job.pm 2026-06-02 16:28:42.000000000 +0200 @@ -234,10 +234,11 @@ # ensure log files are empty/removed if (my $pooldir = $worker->pool_directory) { - open my $fd, '>', "$pooldir/worker-log.txt" or log_error("Could not open worker log: $!"); + open my $fd, '>', "$pooldir/worker-log.txt" + or log_error("Could not open worker log '$pooldir/worker-log.txt': $!"); foreach my $file (qw(serial0.txt autoinst-log.txt serial_terminal.txt)) { next unless -e "$pooldir/$file"; - unlink "$pooldir/$file" or log_error("Could not unlink '$file': $!"); + unlink "$pooldir/$file" or log_error("Could not unlink '$pooldir/$file': $!"); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/t/26-dynamic-job-limit.t new/openQA-5.1780410522.ad58d974/t/26-dynamic-job-limit.t --- old/openQA-5.1780322162.c1836389/t/26-dynamic-job-limit.t 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/t/26-dynamic-job-limit.t 2026-06-02 16:28:42.000000000 +0200 @@ -116,7 +116,7 @@ test_dynamic_limit(%$_) for @cases; -subtest 'auto-detects threshold from nproc when configured as 0' => sub { +subtest 'auto-detect threshold (nproc * factor) and fast ramp-up' => sub { my $mock_dl = Test::MockModule->new('OpenQA::Scheduler::DynamicLimit'); $mock_dl->mock(_nproc => sub { 4 }); # 4 CPUs; threshold = 4*0.85 = 3.4; load 1.0 < 3.4*0.3=1.02 => fast up test_dynamic_limit( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/t/33-developer_mode.t new/openQA-5.1780410522.ad58d974/t/33-developer_mode.t --- old/openQA-5.1780322162.c1836389/t/33-developer_mode.t 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/t/33-developer_mode.t 2026-06-02 16:28:42.000000000 +0200 @@ -9,6 +9,7 @@ use Test::Most; use Test::Warnings ':report_warnings'; +use Mojo::IOLoop::Server; BEGIN { # require the scheduler to be fixed in its actions since tests depends on timing @@ -17,12 +18,15 @@ # ensure the web socket connection won't timeout $ENV{MOJO_INACTIVITY_TIMEOUT} = 10 * 60; + + $ENV{OPENQA_BASE_PORT} ||= Mojo::IOLoop::Server->generate_port; } use FindBin; use lib "$FindBin::Bin/lib", "$FindBin::Bin/../external/os-autoinst-common/lib"; use Mojo::Base -signatures; -use OpenQA::Test::TimeLimit '60'; +use Mojo::IOLoop::Server; +use OpenQA::Test::TimeLimit '200'; use Test::Mojo; use IO::Socket::INET; use Mojo::File 'path'; @@ -261,7 +265,7 @@ subtest 'initial state of UI controls' => sub { wait_for_session_info(qr/owned by Demo/, 'user displayed'); - element_visible('#developer-vnc-notice', qr/.*VNC.*91.*/); + element_visible('#developer-vnc-notice', qr/.*VNC.*\d{4}.*/); element_visible('#developer-panel .card-header', qr/paused/); }; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/t/data/client.conf new/openQA-5.1780410522.ad58d974/t/data/client.conf --- old/openQA-5.1780322162.c1836389/t/data/client.conf 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/t/data/client.conf 2026-06-02 16:28:42.000000000 +0200 @@ -1,3 +1,7 @@ [testapi] key = PERCIVALKEY02 secret = PERCIVALSECRET02 + +[localhost] +key = 1234567890ABCDEF +secret = 1234567890ABCDEF diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/t/data/workers.ini new/openQA-5.1780410522.ad58d974/t/data/workers.ini --- old/openQA-5.1780322162.c1836389/t/data/workers.ini 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/t/data/workers.ini 2026-06-02 16:28:42.000000000 +0200 @@ -7,6 +7,6 @@ # the value set to 0 prevents jobs from being blocked in ci CRITICAL_LOAD_AVG_THRESHOLD = 0 -[1] +[1-99] WORKER_CLASS = qemu_i386,qemu_x86_64 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/t/full-stack.t new/openQA-5.1780410522.ad58d974/t/full-stack.t --- old/openQA-5.1780322162.c1836389/t/full-stack.t 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/t/full-stack.t 2026-06-02 16:28:42.000000000 +0200 @@ -9,6 +9,7 @@ # execution) use Test::Most; +use Mojo::IOLoop::Server; BEGIN { # require the scheduler to be fixed in its actions since tests depends on timing @@ -19,6 +20,8 @@ $ENV{MOJO_INACTIVITY_TIMEOUT} = 10 * 60; $ENV{OS_AUTOINST_STORAGE_KEEP_FREE_RATIO} = 0; + + $ENV{OPENQA_BASE_PORT} ||= Mojo::IOLoop::Server->generate_port; } use Test::Warnings ':report_warnings'; @@ -26,6 +29,7 @@ use List::Util (); use Test::Mojo; use Test::MockModule; +use Mojo::IOLoop::Server; use autodie ':all'; use IO::Socket::INET; use POSIX '_exit'; @@ -49,6 +53,7 @@ use File::Path qw(make_path remove_tree); use Module::Load::Conditional 'can_load'; use OpenQA::Test::Utils + qw(MAX_WORKER_INSTANCES), qw(create_websocket_server create_live_view_handler setup_share_dir), qw(cache_minion_worker cache_worker_service setup_fullstack_temp_dir), qw(start_worker stop_service wait_for_or_bail_out); @@ -152,9 +157,11 @@ assign_jobs $worker_class; } +my $instance = ($$ % MAX_WORKER_INSTANCES) + 1; + sub logfile ($job_id, $filename) { my $log = path($resultdir, '00000', sprintf "%08d-$job_name", $job_id)->child($filename); - return -e $log ? $log : path("$resultdir/../pool/1/")->child($filename); + return -e $log ? $log : path("$resultdir/../pool/$instance/")->child($filename); } sub print_log ($job_id) { @@ -306,7 +313,7 @@ # Ensure fullstack tests run even under high load. CRITICAL_LOAD_AVG_THRESHOLD = 0 -[1] +[1-99] WORKER_CLASS = qemu_i386,qemu_x86_64 [http://localhost:$mojoport] @@ -347,7 +354,7 @@ ok !-d path($cache_location, 'test_directory'), 'Directory within cache, not present after deploy'; ok !-e $cache_location->child('test.file'), 'File within cache, not present after deploy'; - my $link = path($ENV{OPENQA_BASEDIR}, 'openqa', 'pool', '1')->child('Core-7.2.iso'); + my $link = path($ENV{OPENQA_BASEDIR}, 'openqa', 'pool', $instance)->child('Core-7.2.iso'); wait_for_or_bail_out { -e $link } 'finished download'; my $cached = $cache_location->child('localhost', 'Core-7.2.iso'); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/t/lib/OpenQA/SeleniumTest.pm new/openQA-5.1780410522.ad58d974/t/lib/OpenQA/SeleniumTest.pm --- old/openQA-5.1780322162.c1836389/t/lib/OpenQA/SeleniumTest.pm 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/t/lib/OpenQA/SeleniumTest.pm 2026-06-02 16:28:42.000000000 +0200 @@ -25,6 +25,7 @@ use OpenQA::WebAPI; use OpenQA::Log 'log_info'; use OpenQA::Utils; +use OpenQA::Test::TimeLimit (); use OpenQA::Test::Utils qw(wait_for); use POSIX '_exit'; @@ -91,7 +92,7 @@ push @{$opts{extra_capabilities}{$_}{args}}, qw(--headless --disable-gpu --no-sandbox) for @chrome_option_keys; } - my $startup_timeout = $ENV{OPENQA_SELENIUM_TEST_STARTUP_TIMEOUT} // 10; + my $startup_timeout = OpenQA::Test::TimeLimit::scale_timeout($ENV{OPENQA_SELENIUM_TEST_STARTUP_TIMEOUT} // 10); $_DRIVER = Test::Selenium::Chrome->new(%opts, startup_timeout => $startup_timeout); $_DRIVER->{is_wd3} = 0; # ensure the Selenium::Remote::Driver instance uses JSON Wire protocol enable_timeout; @@ -142,7 +143,7 @@ sub wait_for_ajax (%args) { my $check_interval = $args{interval} || 0.25; - my $timeout = $args{timeout} // 30; + my $timeout = OpenQA::Test::TimeLimit::scale_timeout($args{timeout} // 30); my $slept = 0; my $msg = $args{msg} ? (': ' . $args{msg}) : ''; @@ -300,7 +301,7 @@ } sub wait_until ($check_function, $check_description, $timeout = undef, $check_interval = undef) { - $timeout //= 100; + $timeout = OpenQA::Test::TimeLimit::scale_timeout($timeout // 100); $check_interval //= .1; while (1) { if ($check_function->()) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/t/lib/OpenQA/Test/Utils.pm new/openQA-5.1780410522.ad58d974/t/lib/OpenQA/Test/Utils.pm --- old/openQA-5.1780322162.c1836389/t/lib/OpenQA/Test/Utils.pm 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/t/lib/OpenQA/Test/Utils.pm 2026-06-02 16:28:42.000000000 +0200 @@ -12,8 +12,9 @@ use Config::IniFiles; use Data::Dumper 'Dumper'; use OpenQA::App; -use OpenQA::Constants 'DEFAULT_WORKER_TIMEOUT'; +use OpenQA::Constants qw(DEFAULT_WORKER_TIMEOUT); use OpenQA::Log qw(log_error log_info log_debug); + use OpenQA::Utils 'service_port'; use OpenQA::WebSockets; use OpenQA::WebSockets::Client; @@ -33,9 +34,12 @@ use Mojo::Server::Daemon; use Mojo::IOLoop::Server; use Test::MockModule; +use OpenQA::Test::TimeLimit (); use Feature::Compat::Try; use Time::HiRes 'sleep'; +use constant MAX_WORKER_INSTANCES => 64; + BEGIN { if (!$ENV{MOJO_HOME}) { # override default home as Mojo gets it wrong for our sub apps @@ -49,6 +53,7 @@ } our @EXPORT_OK = qw( + MAX_WORKER_INSTANCES setup_mojo_app_with_default_worker_timeout create_user_for_workers create_webapi create_websocket_server create_scheduler create_live_view_handler unresponsive_worker broken_worker rejective_worker setup_share_dir setup_fullstack_temp_dir run_gru_job @@ -422,7 +427,8 @@ $ENV{OPENQA_WORKER_CONNECT_RETRIES} = 1; # enable additional diagnostics for serialization errors $ENV{DEBUG_JSON} = 1; - my @cmd = ('perl', './script/worker', "--isotovideo=$isotovideo_path", '--verbose'); + my $instance = ($$ % MAX_WORKER_INSTANCES) + 1; + my @cmd = ('perl', './script/worker', "--instance=$instance", "--isotovideo=$isotovideo_path", '--verbose'); push @cmd, @$connect_args; start \@cmd; } @@ -567,7 +573,7 @@ sub wait_for ($function, $description, $args = {}) { # `&*;*` allows calling it like `wait_for { 1 } 'foo'` - my $timeout = $args->{timeout} // 60; + my $timeout = OpenQA::Test::TimeLimit::scale_timeout($args->{timeout} // 60); my $interval = $args->{interval} // $ENV{OPENQA_TEST_WAIT_INTERVAL} // .1; note "Waiting for '$description' to become available (timeout: $timeout)"; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/t/ui/01-list.t new/openQA-5.1780410522.ad58d974/t/ui/01-list.t --- old/openQA-5.1780322162.c1836389/t/ui/01-list.t 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/t/ui/01-list.t 2026-06-02 16:28:42.000000000 +0200 @@ -210,16 +210,57 @@ $t->get_ok('/tests/list_running_ajax')->status_is(200); +my $app = OpenQA::App->singleton; +my $scheduler_config = $app->config->{scheduler}; + subtest 'running jobs list with static limit' => sub { - local OpenQA::App->singleton->config->{scheduler}->{dynamic_job_limit_enabled} = 0; - local OpenQA::App->singleton->config->{scheduler}->{max_running_jobs} = 2; + local $scheduler_config->{dynamic_job_limit_enabled} = 0; + local $scheduler_config->{max_running_jobs} = 2; $t->get_ok('/tests/list_running_ajax')->status_is(200); my $json = $t->tx->res->json; is $json->{max_running_jobs}, 2, 'max_running_jobs included in response when static limit reached'; }; +subtest 'running jobs list limit info based on global count' => sub { + my $dl = $app->dynamic_limit; + + local $scheduler_config->{dynamic_job_limit_enabled} = 1; + local $scheduler_config->{dynamic_job_limit_min} = 3; + local $scheduler_config->{max_running_jobs} = 10; + local $scheduler_config->{dynamic_job_limit_load_threshold} = 100; + $dl->effective_limit(3); + $dl->block_adjustment_for(9999); + + # Global count is 3 (99961, 99963, 99970). Filter by groupid=1001 (99963). + $t->get_ok('/tests/list_running_ajax?groupid=1001')->status_is(200); + my $json = $t->tx->res->json; + is scalar @{$json->{data}}, 1, 'only one job matches filter (group 1001)'; + is $json->{dynamic_job_limit}, 3, + 'dynamic_job_limit included based on global count even when filtered list is smaller'; + + # Global count (3) is below limit (4). + local $scheduler_config->{dynamic_job_limit_min} = 4; + $dl->effective_limit(4); + $t->get_ok('/tests/list_running_ajax')->status_is(200); + $json = $t->tx->res->json; + ok !exists $json->{dynamic_job_limit}, 'dynamic_job_limit omitted when global count is below limit'; + ok !exists $json->{max_running_jobs}, + 'max_running_jobs omitted when below dynamic limit to avoid misleading "limited by server config"'; + + subtest 'static limit behavior when dynamic limit is disabled' => sub { + local $scheduler_config->{dynamic_job_limit_enabled} = 0; + local $scheduler_config->{max_running_jobs} = 3; + $t->get_ok('/tests/list_running_ajax')->status_is(200); + is $t->tx->res->json->{max_running_jobs}, 3, 'max_running_jobs included when reached'; + + local $scheduler_config->{max_running_jobs} = 4; + $t->get_ok('/tests/list_running_ajax')->status_is(200); + ok !exists $t->tx->res->json->{max_running_jobs}, 'max_running_jobs omitted when below limit'; + }; +}; + subtest 'all tests server-side limit has precedence over user-specified limit' => sub { - my $limits = OpenQA::App->singleton->config->{misc_limits}; + my $limits = $app->config->{misc_limits}; $limits->{all_tests_max_finished_jobs} = 5; # set low low maximum limit $limits->{all_tests_default_finished_jobs} = 2; # set low default limit diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/tools/ci/build-docs new/openQA-5.1780410522.ad58d974/tools/ci/build-docs --- old/openQA-5.1780322162.c1836389/tools/ci/build-docs 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/tools/ci/build-docs 2026-06-02 16:28:42.000000000 +0200 @@ -2,7 +2,7 @@ if [[ "${CIRCLE_JOB}" != *nightly ]]; then bash -xe tools/generate-documentation else - zypper -n install hub + zypper -n --gpg-auto-import-keys install hub export PULL_REQUEST_USER=${CIRCLE_PROJECT_USERNAME:-os-autoinst} export PUBLISH=1 bash -xe tools/generate-documentation https://[email protected]/os-autoinst-bot/openQA.git gh-pages-"$(date +%y%m%d%H%M%S)" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/openQA-5.1780322162.c1836389/tools/ci/prepare_dependency_pr.sh new/openQA-5.1780410522.ad58d974/tools/ci/prepare_dependency_pr.sh --- old/openQA-5.1780322162.c1836389/tools/ci/prepare_dependency_pr.sh 2026-06-01 15:56:02.000000000 +0200 +++ new/openQA-5.1780410522.ad58d974/tools/ci/prepare_dependency_pr.sh 2026-06-02 16:28:42.000000000 +0200 @@ -18,7 +18,7 @@ echo -n "$new_sha" > "$sha_file" -sudo zypper -n install git hub curl +sudo zypper -n --gpg-auto-import-keys install git hub curl hub --version git add "$sha_file" git commit -m "$msg" ++++++ openQA.obsinfo ++++++ --- /var/tmp/diff_new_pack.9FQARl/_old 2026-06-03 20:31:02.414648657 +0200 +++ /var/tmp/diff_new_pack.9FQARl/_new 2026-06-03 20:31:02.426649155 +0200 @@ -1,5 +1,5 @@ name: openQA -version: 5.1780322162.c1836389 -mtime: 1780322162 -commit: c18363899cbe127225717f0b5bed136d8ddcc8f7 +version: 5.1780410522.ad58d974 +mtime: 1780410522 +commit: ad58d974d8900660683f812119313c523a41f530
