From f4bd3c6f58f38e4baca642083341693cd744de30 Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Thu, 18 Apr 2024 14:42:38 -0700 Subject: [PATCH] Add a container build test This test, much like _live_build or _installer_build, builds a container image in a way intended to be as similar as possible to how official compose images are built. The purpose of the test is to make sure updates do not break official container image builds. At the end of the test, we also check that the built container is functional (at least, that we can run a 'hello world' command in it). This can't really be rolled into podman.pm because that test is more about testing podman itself, and it's just a one- liner here anyway. We also run the 'if any packages from the update are installed, are they the versions from the update?' check inside the container, which required giving that check the ability to 'wrap' the rpm commands to run inside a container. Signed-off-by: Adam Williamson --- lib/utils.pm | 8 ++- templates-updates.fif.json | 15 ++++ tests/_container_build_kiwi.pm | 124 +++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 tests/_container_build_kiwi.pm diff --git a/lib/utils.pm b/lib/utils.pm index b435520b..906e3303 100644 --- a/lib/utils.pm +++ b/lib/utils.pm @@ -1230,6 +1230,7 @@ sub advisory_check_nonmatching_packages { # older version from the frozen release repo my %args = ( fatal => 1, + wrapper => "", @_ ); # can't do anything useful when testing a side tag @@ -1241,6 +1242,9 @@ sub advisory_check_nonmatching_packages { # unnecessarily in post_fail_hook return if (get_var("_ACNMP_DONE")); script_run 'touch /tmp/installedupdatepkgs.txt'; + my $rpmcmd = "rpm"; + my $wrapper = $args{wrapper}; + $rpmcmd = "$wrapper rpm" if ($wrapper); # this creates /tmp/installedupdatepkgs.txt as a sorted list of installed # packages with the same name as packages from the update, in the same form # as /mnt/updatepkgs.txt. The '--last | head -1' tries to handle the @@ -1254,7 +1258,7 @@ sub advisory_check_nonmatching_packages { # (we need four to reach bash, and half of them get eaten by perl or # something along the way). Yes, it only works with *single* quotes. Yes, # I hate escaping - script_run 'for pkg in $(cat /mnt/updatepkgnames.txt); do rpm -q $pkg && rpm -q $pkg --last | head -1 | cut -d" " -f1 | sed -e \'s,\^,\\\\\\\\^,g\' | xargs rpm -q --qf "%{SOURCERPM} %{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE}\n" >> /tmp/installedupdatepkgs.txt; done', timeout => 180; + script_run 'for pkg in $(cat /mnt/updatepkgnames.txt); do ' . $rpmcmd . ' -q $pkg && ' . $rpmcmd . ' -q $pkg --last | head -1 | cut -d" " -f1 | sed -e \'s,\^,\\\\\\\\^,g\' | xargs ' . $rpmcmd . ' -q --qf "%{SOURCERPM} %{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE}\n" >> /tmp/installedupdatepkgs.txt; done', timeout => 180; script_run 'sort -u -o /tmp/installedupdatepkgs.txt /tmp/installedupdatepkgs.txt'; # for debugging, may as well always upload these, can't hurt anything upload_logs "/tmp/installedupdatepkgs.txt", failok => 1; @@ -1274,7 +1278,7 @@ sub advisory_check_nonmatching_packages { my $message = "Package(s) from update not installed when it should have been! See script output"; $message = "Script failed unexpectedly!" if ($ret == 1); if ($args{fatal}) { - set_var("_ACNMP_DONE", "1"); + set_var("_ACNMP_DONE", "1") unless $wrapper; die $message; } else { diff --git a/templates-updates.fif.json b/templates-updates.fif.json index 863c7868..d7b3005a 100644 --- a/templates-updates.fif.json +++ b/templates-updates.fif.json @@ -335,6 +335,21 @@ "fedora-updates-workstation-x86_64-*-64bit": 5 } }, + "container_build_kiwi": { + "profiles": { + "fedora-updates-container-x86_64-*-64bit": 5 + }, + "settings": { + "BOOTFROM": "c", + "HDD_1": "disk_f%VERSION%_minimal_4_%ARCH%.qcow2", + "HDDSIZEGB_3": "25", + "MAX_JOB_TIME": "10800", + "+NUMDISKS": "3", + "POSTINSTALL": "_container_build_kiwi", + "ROOT_PASSWORD": "weakpassword", + "USER_LOGIN": "false" + } + }, "desktop_background": { "profiles": { "fedora-updates-kde-x86_64-*-64bit": 5, diff --git a/tests/_container_build_kiwi.pm b/tests/_container_build_kiwi.pm new file mode 100644 index 00000000..aec7f041 --- /dev/null +++ b/tests/_container_build_kiwi.pm @@ -0,0 +1,124 @@ +use base "installedtest"; +use strict; +use testapi; +use utils; + +sub run { + my $self = shift; + my $version = get_var("VERSION"); + # we didn't use kiwi before F40, and I don't really want to write + # an imgfac test for a release that will be dead in 6 months + # FIXME drop when F39 is EOL + if ($version < 40) { + record_info('notvalid', "this test cannot be run on Fedora < 40"); + return; + } + my $rawrel = get_var("RAWREL"); + my $branch; + my $repoxml; + my $releasever; + my $mockver; + if ($version eq $rawrel) { + $branch = "main"; + $repoxml = "repositories/core-rawhide.xml"; + $releasever = "Rawhide"; + $mockver = "rawhide"; + } + else { + $branch = "f${version}"; + $repoxml = "repositories/core-nonrawhide.xml"; + $releasever = $version; + $mockver = $version; + } + my $arch = get_var("ARCH"); + my $tag = get_var("TAG"); + my $workarounds = get_workarounds; + if (get_var("NUMDISKS") > 2) { + # put /var/lib/mock on the third disk, so we don't run out of + # space on the main disk. The second disk will have already + # been claimed for the update repo. + assert_script_run "echo 'type=83' | sfdisk /dev/vdc"; + assert_script_run "mkfs.ext4 /dev/vdc1"; + assert_script_run "echo '/dev/vdc1 /var/lib/mock ext4 defaults 1 2' >> /etc/fstab"; + assert_script_run "mkdir -p /var/lib/mock"; + assert_script_run "mount /var/lib/mock"; + } + # install the tools we need + assert_script_run "dnf -y install mock git", 300; + # base mock config on original + assert_script_run "echo \"include('/etc/mock/fedora-${mockver}-${arch}.cfg')\" > /etc/mock/openqa.cfg"; + # make the side and workarounds repos and the serial device available inside the mock root + assert_script_run 'echo "config_opts[\'plugin_conf\'][\'bind_mount_enable\'] = True" >> /etc/mock/openqa.cfg'; + assert_script_run 'echo "config_opts[\'plugin_conf\'][\'bind_mount_opts\'][\'dirs\'].append((\'/mnt/update_repo\', \'/mnt/update_repo\'))" >> /etc/mock/openqa.cfg' unless ($tag); + assert_script_run 'echo "config_opts[\'plugin_conf\'][\'bind_mount_opts\'][\'dirs\'].append((\'/mnt/workarounds_repo\', \'/mnt/workarounds_repo\'))" >> /etc/mock/openqa.cfg' if ($workarounds); + assert_script_run 'echo "config_opts[\'plugin_conf\'][\'bind_mount_opts\'][\'dirs\'].append((\'/dev/' . $serialdev . '\', \'/dev/' . $serialdev . '\'))" >> /etc/mock/openqa.cfg'; + my $repos = 'config_opts[\'dnf.conf\'] += \"\"\"\n'; + # add the update repo or tag repo to the config + $repos .= '[advisory]\nname=Advisory repo\nbaseurl=file:///mnt/update_repo\nenabled=1\nmetadata_expire=3600\ngpgcheck=0\n' unless ($tag); + $repos .= '[openqa-testtag]\nname=Tag test repo\nbaseurl=https://kojipkgs.fedoraproject.org/repos/' . "${tag}/latest/${arch}" . '\nenabled=1\nmetadata_expire=3600\ngpgcheck=0\n' if ($tag); + # and the workaround repo + $repos .= '\n[workarounds]\nname=Workarounds repo\nbaseurl=file:///mnt/workarounds_repo\nenabled=1\nmetadata_expire=3600\ngpgcheck=0\n' if ($workarounds); + # also the buildroot repo, for Rawhide + if ($version eq $rawrel) { + $repos .= '\n[koji-rawhide]\nname=Buildroot repo\nbaseurl=https://kojipkgs.fedoraproject.org/repos/f' . $version . '-build/latest/\$basearch/\nenabled=1\nmetadata_expire=3600\ngpgcheck=0\nskip_if_unavailable=1\n'; + } + $repos .= '\"\"\"'; + assert_script_run 'printf "' . $repos . '" >> /etc/mock/openqa.cfg'; + # replace metalink with mirrorlist so we don't get slow mirrors + repos_mirrorlist "/etc/mock/templates/*.tpl"; + # upload the config so we can check it's OK + upload_logs "/etc/mock/openqa.cfg"; + # now check out the fedora kiwi descriptions + assert_script_run 'git clone https://pagure.io/fedora-kiwi-descriptions.git'; + assert_script_run 'cd fedora-kiwi-descriptions'; + assert_script_run "git checkout ${branch}"; + # correct the GPG key paths in the repositories and swap metalink + # to mirrorlist + assert_script_run 'sed -i -e "s,/usr/share/distribution-gpg-keys/fedora,/etc/pki/rpm-gpg,g" ' . $repoxml; + repos_mirrorlist $repoxml; + # now add the side repo or tag repo to the appropriate repo XML + assert_script_run 'printf "$(head -n -1 ' . $repoxml . ')\n \n \n \n\n" > ' . $repoxml unless ($tag); + assert_script_run 'printf "$(head -n -1 ' . $repoxml . ')\n \n \n \n\n" > ' . $repoxml if ($tag); + # and the workarounds repo + assert_script_run 'printf "$(head -n -1 ' . $repoxml . ')\n \n \n \n\n" > ' . $repoxml if ($workarounds); + # and the buildroot repo, for Rawhide + assert_script_run 'printf "$(head -n -1 ' . $repoxml . ')\n \n \n \n\n" > ' . $repoxml if ($version eq $rawrel); + # upload the repositories XML so we can check it + # NOTE: koji kiwi plugin does much more futzing around with the XML + # it flattens includes, fiddles with the repos, and and messes with + # preferences a bit. see + # KiwiCreateImageTask.prepareDescription. but we do our own repo + # stuff above, the preference stuff is unnecessary on Fedora, and + # the flattening is unnecessary outside Koji + upload_logs "$repoxml"; + assert_script_run "cd .."; + # now install the tools into the mock + assert_script_run "mock -r openqa --install kiwi-cli kiwi-systemdeps", 900; + # now copy the descriptions in + assert_script_run "mock -r openqa --isolation=simple --copyin fedora-kiwi-descriptions /fedora-kiwi-descriptions"; + # PULL SOME LEVERS! PULL SOME LEVERS! + assert_script_run "mock -r openqa --enable-network --chroot \"kiwi-ng --profile Container-Base-Generic --debug --logfile /tmp/image-root.log system build --description /fedora-kiwi-descriptions/ --target-dir /builddir/result/image\"", 7200; + unless (script_run "mock -r openqa --isolation=simple --copyout /tmp/image-root.log .", 90) { + upload_logs "image-root.log"; + } + assert_script_run "mock -r openqa --isolation=simple --copyout /builddir/result/image/Fedora.${arch}-${releasever}.oci.tar.xz .", 180; + upload_asset "./Fedora.${arch}-${releasever}.oci.tar.xz"; + + # load and test that we can use the built container + assert_script_run "podman load -i ./Fedora.${arch}-${releasever}.oci.tar.xz"; + my $imgspec = "localhost/fedora:${mockver}"; + validate_script_output "podman run ${imgspec} echo Hello-World", sub { m/Hello-World/ }; + # do advisory_check_nonmatching_packages inside the container + advisory_check_nonmatching_packages(wrapper => "podman run ${imgspec}"); + # wipe the temp file so it doesn't interfere with the same check + # on the host + assert_script_run "rm -f /tmp/installedupdatepkgs.txt"; +} + +sub test_flags { + return {fatal => 1}; +} + +1; + +# vim: set sw=4 et: