From patchwork Thu Apr 25 15:46:07 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Opdenacker X-Patchwork-Id: 42873 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id AFBE2C4345F for ; Thu, 25 Apr 2024 15:46:31 +0000 (UTC) Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) by mx.groups.io with SMTP id smtpd.web10.20470.1714059982554882079 for ; Thu, 25 Apr 2024 08:46:23 -0700 Authentication-Results: mx.groups.io; dkim=pass header.i=@bootlin.com header.s=gm1 header.b=oAGthv44; spf=pass (domain: bootlin.com, ip: 217.70.183.201, mailfrom: michael.opdenacker@bootlin.com) Received: by mail.gandi.net (Postfix) with ESMTPSA id 7FDF11BF205; Thu, 25 Apr 2024 15:46:20 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1714059980; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=Llzh4UvCU3IY+ojozqkCRrlLRE52Eg3/OWivl8TwuDo=; b=oAGthv44/gLcH+tA7sEEpsDqtYM+J1dk4+DOlTLi1v8zBCnlmItOvjbSkUQJq/9+lah6jM yFtbN+9/PL6hvlDI7aofQ0zeWfDZhIoiJAvR/q7ekypNdiPXaSYwzdd1tVXFwEHy4g8eSc 5T+St52BiTBGxWgzL5FJUKx9CnUPfbb902K4Jtc5lZQJNIj0Z0omHWryQ3C1uaK6Qp4Ctv 88L8MNH1iE8jzNkMnP8/xknJJTvPUlTMRtlDcdXcqVIWxexJLMKZ5dzf4IWWkNE25NGlhs MaGjYWvtKQhXwcYhK42rCLeQ5+D9rwaN5jjPVFpAAGuTqNmWxACn8e9AesgikQ== From: michael.opdenacker@bootlin.com To: openembedded-core@lists.openembedded.org Cc: Michael Opdenacker , Richard Purdie , Thomas Petazzoni , Bruce Ashfield Subject: [RFC][PATCH] oeqa/runtime/cases: new image_upgrade test Date: Thu, 25 Apr 2024 17:46:07 +0200 Message-Id: <20240425154607.566716-1-michael.opdenacker@bootlin.com> X-Mailer: git-send-email 2.34.1 MIME-Version: 1.0 X-GND-Sasl: michael.opdenacker@bootlin.com List-Id: X-Webhook-Received: from li982-79.members.linode.com [45.33.32.79] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Thu, 25 Apr 2024 15:46:31 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-core/message/198704 From: Michael Opdenacker New oe-selftest and associated "testimage" test to check that generated package feeds can be used to update the latest image built by the Yocto Project autobuilder. Currently, only the "core-image-full-cmdline" image with IPK packages and the "poky-altcfg" distro is supported. Test it by running: oe-selftest -r image_upgrade Signed-off-by: Michael Opdenacker Suggested-by: Richard Purdie CC: Thomas Petazzoni CC: Bruce Ashfield --- Tested on the latest master against yocto-5.0_M3 Interested in your feedback about which settings (such as IMAGE) can be hardcoded in the scripts or provided by the calling environment. --- .../lib/oeqa/runtime/cases/opkg_sysupgrade.py | 68 ++++++++++ meta/lib/oeqa/selftest/cases/image_upgrade.py | 124 ++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 meta/lib/oeqa/runtime/cases/opkg_sysupgrade.py create mode 100755 meta/lib/oeqa/selftest/cases/image_upgrade.py diff --git a/meta/lib/oeqa/runtime/cases/opkg_sysupgrade.py b/meta/lib/oeqa/runtime/cases/opkg_sysupgrade.py new file mode 100644 index 0000000000..05b5847b4a --- /dev/null +++ b/meta/lib/oeqa/runtime/cases/opkg_sysupgrade.py @@ -0,0 +1,68 @@ +# +# Copyright OpenEmbedded Contributors +# +# Test that generated ipk packages can be used to upgrade +# an older image version. +# +# This is done by the meta/lib/oeqa/selftest/cases/image_upgrade.py oe-selftest +# replacing the newly generated image by an older image +# generated by the Yocto Project autobuilder. +# +# Here, we replace the package feeds in our image by our own +# +# This test is not meant to be used as a regular "testimage" test +# run on the fresh image. +# +# SPDX-License-Identifier: MIT +# + +import os +from oeqa.utils.httpserver import HTTPService +from oeqa.runtime.case import OERuntimeTestCase +from oeqa.core.decorator.data import skipIfNotDataVar, skipIfNotFeature, skipIfFeature +from oeqa.runtime.decorator.package import OEHasPackage + +class OpkgSysUpgradeTest(OERuntimeTestCase): + + def pkg(self, command, expected = 0): + command = 'opkg %s' % command + status, output = self.target.run(command, 1500) + message = os.linesep.join([command, output]) + self.assertEqual(status, expected, message) + return output + +class OpkgRepoTest(OpkgSysUpgradeTest): + + @classmethod + def setUp(cls): + service_repo = os.path.join(cls.tc.td['DEPLOY_DIR_IPK']) + cls.repo_server = HTTPService(service_repo, + '0.0.0.0', port=cls.tc.target.server_port, + logger=cls.tc.logger) + cls.repo_server.start() + + @classmethod + def tearDown(cls): + cls.repo_server.stop() + + def setup_source_config_for_package_install(self): + source_server = 'http://%s:%s' % (self.tc.target.server_ip, self.repo_server.port) + sourceslist_dir = '/etc/opkg' + pkgarch = self.tc.td["TUNE_PKGARCH"] + machinedir = self.tc.td["MACHINE"].replace("-", "_") + self.target.run('cd %s; echo src/gz all %s/all > base-feeds.conf' % (sourceslist_dir, source_server)) + self.target.run('cd %s; echo src/gz %s %s/%s >> base-feeds.conf' % (sourceslist_dir, pkgarch, source_server, pkgarch)) + self.target.run('cd %s; echo src/gz %s %s/%s >> base-feeds.conf' % (sourceslist_dir, machinedir, source_server, machinedir)) + + @skipIfNotFeature('package-management', + 'Test requires package-management to be in IMAGE_FEATURES') + @skipIfNotDataVar('IMAGE_PKGTYPE', 'ipk', + 'IPK is not the primary package manager') + @skipIfFeature('read-only-rootfs', + 'Test does not work with read-only-rootfs in IMAGE_FEATURES') + @OEHasPackage(['opkg']) + def test_opkg_system_upgrade_from_repo(self): + self.setup_source_config_for_package_install() + self.pkg('update') + self.pkg('upgrade') + diff --git a/meta/lib/oeqa/selftest/cases/image_upgrade.py b/meta/lib/oeqa/selftest/cases/image_upgrade.py new file mode 100755 index 0000000000..3e26c7eed0 --- /dev/null +++ b/meta/lib/oeqa/selftest/cases/image_upgrade.py @@ -0,0 +1,124 @@ +# +# Copyright OpenEmbedded Contributors +# +# SPDX-License-Identifier: MIT +# + +import os +import subprocess +from oeqa.selftest.case import OESelftestTestCase +from oeqa.utils.commands import bitbake, runCmd, get_bb_var +from oeqa.core.decorator.data import skipIfNotQemu + +basepath = os.path.abspath(os.path.dirname(__file__) + '/../../../../../') + +# Version string utilities copied from yocto-autobuilder-helper/scripts/utils.py + +def get_string_from_version(version, milestone=None, rc=None): + """ Point releases finishing by 0 (e.g 4.0.0, 4.1.0) do no exists, + those are major releases + """ + if len(version) == 3 and version[-1] == 0: + version = version[:-1] + + result = ".".join(list(map(str, version))) + if milestone: + result += "_M" + str(milestone) + if rc: + result += ".rc" + str(rc) + return result + +def get_tag_from_version(version, milestone): + if not milestone: + return "yocto-" + get_string_from_version(version, milestone) + return get_string_from_version(version, milestone) + +def get_version_from_string(raw_version): + """ Get version as list of int from raw_version. + + Raw version _can_ be prefixed by "yocto-", + Raw version _can_ be suffixed by "_MX" + Raw version _can_ be suffixed by ".rcY" + """ + version = None + milestone = None + rc = None + if raw_version[:6] == "yocto-": + raw_version = raw_version[6:] + raw_version = raw_version.split(".") + if raw_version[-1][:2] == "rc": + rc = int(raw_version[-1][-1]) + raw_version = raw_version[:-1] + if raw_version[-1][-3:-1] == "_M": + milestone = int(raw_version[-1][-1]) + raw_version = raw_version[:-1] + [raw_version[-1][:-3]] + version = list(map(int, raw_version)) + """ Point releases finishing by 0 (e.g 4.0.0, 4.1.0) do no exists, + those are major releases + """ + if len(version) == 3 and version[-1] == 0: + version = version[:-1] + return version, milestone, rc + +def get_latest_image_url(machine, machine_variant, image_file): + + """Returns the URL of the latest generated image for the current branch""" + + baseversion, milestone, _ = get_version_from_string(subprocess.check_output(["git", "describe", "--abbrev=0"], cwd=basepath).decode('utf-8').strip()) + tag = get_tag_from_version(baseversion, milestone) + downloads_base = "https://downloads.yoctoproject.org/releases/yocto/" + + if milestone is not None: + downloads_base += "milestones/yocto-%s" % tag + else: + downloads_base += tag + + return "%s/machines/qemu/%s%s/%s" % (downloads_base, machine, machine_variant, image_file) + +class ImageIpkUpgrade(OESelftestTestCase): + + @skipIfNotQemu() + def test_image_upgrade(self): + """ + Summary: Test that generated ipk packages can + be used to upgrade an older image version. + This is done by generating an image but then replacing it + by an older image shared by the Yocto Project autobuilder. + We then run QEMU on the old image and replace the original + original package feeds by our own. + """ + + image = 'core-image-full-cmdline' + machine = get_bb_var("MACHINE") + + features = 'DISTRO = "poky-altcfg"\n' + features += 'EXTRA_IMAGE_FEATURES += "package-management"\n' + features += 'PACKAGE_CLASSES = "package_ipk"\n' + features += 'IMAGE_CLASSES += "testimage"\n' + features += 'TEST_SUITES="opkg_sysupgrade"\n' + self.write_config(features) + + # Need to build a full image to build the .json file needed by QEMU. + # Therefore, it is not sufficient to run only "package_write_ipk" for the image. + + self.logger.info("Generating '%s' and package index for latest commit in this branch..." % image) + bitbake(image) + bitbake('package-index') + + # Download previously generated image + + image_file = '%s-%s.rootfs.ext4' % (image, machine) + image_path = '%s/tmp/deploy/images/%s/%s' % (self.builddir, machine, image_file) + machine_variant = '-alt' + + os.remove(image_path) + image_url = get_latest_image_url(machine, machine_variant, image_file) + self.logger.info("Downloading image: %s..." % image_url) + cmd = 'wget -O %s %s' % (image_path, image_url) + result = runCmd(cmd) + self.assertEqual(0, result.status, cmd + ' returned a non 0 status: %s' % result.output) + + # Now run the upgrade tests on the old image + + self.logger.info("Running upgrade tests on the downloaded image, using the package feeds generated here...") + bitbake(image + ' -c testimage')