action #181895
opencoordination #162539: [saga][epic] future ideas version for version control features within openQA
coordination #181886: [epic] Good git defaults for new instances while keeping compatibility for old instances
openQA: script dump / load issues: loading job templates is broken
0%
Description
Observation¶
job templates are never dumped unless --group and --full are set, $options{group}
magically gets defined halfway through dump-templates
lruzicka reported to me today that template loading is broken in current openQA, and boy, did this turn out to be a rabbit hole.
The initial issue is actually simple enough. https://github.com/os-autoinst/openQA/pull/5973 broke job template loading completely, because it made openqa-load-templates invariably pass the data to the API as JSON, but the JobTemplate controller's create
method cannot work with JSON, it heavily uses Mojolicious validation stuff (via the validation
default helper that it has), and that does not work with JSON, only with "GET and POST parameters extracted from the query string and application/x-www-form-urlencoded or multipart/form-data message body", per https://docs.mojolicious.org/Mojolicious/Plugin/DefaultHelpers#validation . One way to fix this is simple enough - change openqa-load-templates to use form => \%param
for the JobTemplates table like it used to, not json => \%param
.
But then I got curious and wondered why the test suite never caught this, and that turns out to be a heck of a rabbit hole!
The answer turns out to be: the test suite works by doing dump-templates and then load-templates on the dumped data, but dump-templates has been broken for several years, it will never dump job templates. It's broken in two interesting ways.
The first relates to the --full
arg. This is meant to only be relevant if you also pass --group
; it's documented as "when dumping a specific job group also dump related test suites, products and machines". But during refactors over the years, this has broken. In handle_row
we do this:
if ($table eq 'JobTemplates') {
return undef if $options{group} && $r->{group_name} && !$options{group}->{$r->{group_name}};
return undef unless $options{product}->{product_key($r->{product})};
}
But the only code that ever sets $options{product}->{product_key($r->{product})}
is gated behind a check for $options{full}
, above:
if ($tables{JobTemplates} && $options{full}) {
for my $r (@{$result{JobTemplates}}) {
next if $options{group} && !$options{group}->{$r->{group_name}};
$options{test}->{$r->{test_suite}->{name}} = 1;
$options{machine}->{$r->{machine}->{name}} = 1;
$options{product}->{product_key($r->{product})} = 1;
}
}
...so we will never dump any JobTemplates unless --full
is passed. So I thought we could fix this by doing:
if ($table eq 'JobTemplates' && $options{group}) {
return undef if $r->{group_name} && !$options{group}->{$r->{group_name}};
return undef unless $options{product}->{product_key($r->{product})};
}
but even this does not work, for a surprising reason. If you do not pass --group
, $options{group}
is undefined (as you'd expect) until partway through the script, but after this bit, it is magically defined:
if ($tables{'JobGroups'}) {
my $group = (keys %{$options{group}})[0];
once that keys
call has happened, $options{group}
is defined even if it wasn't before. You can prove that with this test script:
#!/bin/perl
my %options;
defined $options{group} ? print "defined\n" : print "undefined\n";
keys %{$options{group}};
defined $options{group} ? print "defined\n" : print "undefined\n";
...so in fact, once we reach the meat of dump-templates, $options{group}
is always defined. This has the effect that we never dump job templates unless --group
and --full
are both passed, because we will always hit the check for $options{product}->{product_key($r->{product})}
in handle_row
(even with my fix), but we will never set it unless --group
is passed, because we'll always bail out of the block that tries to set it, here:
next if $options{group} && !$options{group}->{$r->{group_name}};
It looks like the call to keys
that introduces this breakage was added in 323246dc9f597c0efa137e910ad038594489ce83 , "Add YAML support to {dump,load}_templates".
So, in the test suite, when we dump the templates, we get a file with empty JobTemplates
, so we don't catch the 'loading job templates is broken' bug when we load it back, because we don't try and load any. The test suite unfortunately does not check that the dumped file contains everything it should, or that everything expected is loaded back (it only explicitly checks that the expected number of JobGroups got loaded).
I'm writing all this up because my brain is too frazzled to decide how to fix it today, so I have a reference to get started on it tomorrow.