a97488721d
The osbuild Koji plugin supports passing customizations for an image
build. This is also supported in the Koji CLI plugin. Some teams want to
pass image customizations for images built as part of Pungi composes.
Extend the osbuild phase to support passing customizations in the Pungi
configuration.
Merges: https://pagure.io/pungi/pull-request/1733
Signed-off-by: Tomáš Hozza <thozza@redhat.com>
(cherry picked from commit e738f65458
)
858 lines
29 KiB
Python
858 lines
29 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from unittest import mock
|
|
|
|
import os
|
|
import shutil
|
|
import tempfile
|
|
import unittest
|
|
|
|
import koji as orig_koji
|
|
|
|
from tests import helpers
|
|
from pungi import compose
|
|
from pungi.phases import osbuild
|
|
from pungi.checks import validate
|
|
|
|
|
|
class OSBuildPhaseHelperFuncsTest(unittest.TestCase):
|
|
@mock.patch("pungi.compose.ComposeInfo")
|
|
def setUp(self, ci):
|
|
self.tmp_dir = tempfile.mkdtemp()
|
|
conf = {"translate_paths": [(self.tmp_dir, "http://example.com")]}
|
|
ci.return_value.compose.respin = 0
|
|
ci.return_value.compose.id = "RHEL-8.0-20180101.n.0"
|
|
ci.return_value.compose.date = "20160101"
|
|
ci.return_value.compose.type = "nightly"
|
|
ci.return_value.compose.type_suffix = ".n"
|
|
ci.return_value.compose.label = "RC-1.0"
|
|
ci.return_value.compose.label_major_version = "1"
|
|
|
|
compose_dir = os.path.join(self.tmp_dir, ci.return_value.compose.id)
|
|
self.compose = compose.Compose(conf, compose_dir)
|
|
server_variant = mock.Mock(uid="Server", type="variant")
|
|
client_variant = mock.Mock(uid="Client", type="variant")
|
|
self.compose.all_variants = {
|
|
"Server": server_variant,
|
|
"Client": client_variant,
|
|
}
|
|
|
|
def tearDown(self):
|
|
shutil.rmtree(self.tmp_dir)
|
|
|
|
def test__get_repo_urls(self):
|
|
repos = [
|
|
"http://example.com/repo",
|
|
"Server",
|
|
{
|
|
"baseurl": "Client",
|
|
"package_sets": ["build"],
|
|
},
|
|
{
|
|
"baseurl": "ftp://example.com/linux/repo",
|
|
"package_sets": ["build"],
|
|
},
|
|
]
|
|
|
|
expect = [
|
|
"http://example.com/repo",
|
|
"http://example.com/RHEL-8.0-20180101.n.0/compose/Server/$basearch/os",
|
|
{
|
|
"baseurl": "http://example.com/RHEL-8.0-20180101.n.0/compose/Client/"
|
|
+ "$basearch/os",
|
|
"package_sets": ["build"],
|
|
},
|
|
{
|
|
"baseurl": "ftp://example.com/linux/repo",
|
|
"package_sets": ["build"],
|
|
},
|
|
]
|
|
|
|
self.assertEqual(
|
|
osbuild.OSBuildPhase._get_repo_urls(self.compose, repos), expect
|
|
)
|
|
|
|
|
|
class OSBuildPhaseTest(helpers.PungiTestCase):
|
|
@mock.patch("pungi.phases.osbuild.ThreadPool")
|
|
def test_run(self, ThreadPool):
|
|
cfg = {
|
|
"name": "test-image",
|
|
"distro": "rhel-8",
|
|
"version": "1",
|
|
"target": "image-target",
|
|
"arches": ["x86_64"],
|
|
"failable": ["x86_64"],
|
|
"image_types": ["qcow2"],
|
|
}
|
|
compose = helpers.DummyCompose(
|
|
self.topdir, {"osbuild": {"^Everything$": [cfg]}}
|
|
)
|
|
|
|
self.assertValidConfig(compose.conf)
|
|
|
|
pool = ThreadPool.return_value
|
|
|
|
phase = osbuild.OSBuildPhase(compose)
|
|
phase.run()
|
|
|
|
self.assertEqual(len(pool.add.call_args_list), 1)
|
|
self.assertEqual(
|
|
pool.queue_put.call_args_list,
|
|
[
|
|
mock.call(
|
|
(
|
|
compose,
|
|
compose.variants["Everything"],
|
|
cfg,
|
|
["x86_64"],
|
|
"1",
|
|
None,
|
|
"image-target",
|
|
[self.topdir + "/compose/Everything/$arch/os"],
|
|
["x86_64"],
|
|
),
|
|
),
|
|
],
|
|
)
|
|
|
|
@mock.patch("pungi.phases.osbuild.ThreadPool")
|
|
def test_run_with_global_options(self, ThreadPool):
|
|
cfg = {
|
|
"name": "test-image",
|
|
"distro": "rhel-8",
|
|
"image_types": ["qcow2"],
|
|
}
|
|
compose = helpers.DummyCompose(
|
|
self.topdir,
|
|
{
|
|
"osbuild": {"^Everything$": [cfg]},
|
|
"osbuild_target": "image-target",
|
|
"osbuild_version": "1",
|
|
"osbuild_release": "2",
|
|
},
|
|
)
|
|
|
|
self.assertValidConfig(compose.conf)
|
|
|
|
pool = ThreadPool.return_value
|
|
|
|
phase = osbuild.OSBuildPhase(compose)
|
|
phase.run()
|
|
|
|
self.assertEqual(len(pool.add.call_args_list), 1)
|
|
self.assertEqual(
|
|
pool.queue_put.call_args_list,
|
|
[
|
|
mock.call(
|
|
(
|
|
compose,
|
|
compose.variants["Everything"],
|
|
cfg,
|
|
sorted(compose.variants["Everything"].arches),
|
|
"1",
|
|
"2",
|
|
"image-target",
|
|
[self.topdir + "/compose/Everything/$arch/os"],
|
|
[],
|
|
),
|
|
),
|
|
],
|
|
)
|
|
|
|
@mock.patch("pungi.phases.osbuild.ThreadPool")
|
|
def test_skip_without_config(self, ThreadPool):
|
|
compose = helpers.DummyCompose(self.topdir, {})
|
|
compose.just_phases = None
|
|
compose.skip_phases = []
|
|
phase = osbuild.OSBuildPhase(compose)
|
|
self.assertTrue(phase.skip())
|
|
|
|
def test_fail_multiple_image_types(self):
|
|
cfg = {
|
|
"name": "test-image",
|
|
"distro": "rhel-8",
|
|
# more than one image type is not allowed
|
|
"image_types": ["qcow2", "rhel-ec2"],
|
|
}
|
|
compose = helpers.DummyCompose(
|
|
self.topdir,
|
|
{
|
|
"osbuild": {"^Everything$": [cfg]},
|
|
"osbuild_target": "image-target",
|
|
"osbuild_version": "1",
|
|
"osbuild_release": "2",
|
|
},
|
|
)
|
|
self.assertNotEqual(validate(compose.conf), ([], []))
|
|
|
|
@mock.patch("pungi.phases.osbuild.ThreadPool")
|
|
def test_run_with_customizations(self, ThreadPool):
|
|
cfg = {
|
|
"name": "test-image",
|
|
"distro": "rhel-8",
|
|
"image_types": ["qcow2"],
|
|
"customizations": {"installation_device": "/dev/sda"},
|
|
}
|
|
compose = helpers.DummyCompose(
|
|
self.topdir,
|
|
{
|
|
"osbuild": {"^Everything$": [cfg]},
|
|
"osbuild_target": "image-target",
|
|
"osbuild_version": "1",
|
|
"osbuild_release": "2",
|
|
},
|
|
)
|
|
|
|
self.assertValidConfig(compose.conf)
|
|
|
|
pool = ThreadPool.return_value
|
|
|
|
phase = osbuild.OSBuildPhase(compose)
|
|
phase.run()
|
|
|
|
self.assertEqual(len(pool.add.call_args_list), 1)
|
|
self.assertEqual(
|
|
pool.queue_put.call_args_list,
|
|
[
|
|
mock.call(
|
|
(
|
|
compose,
|
|
compose.variants["Everything"],
|
|
cfg,
|
|
sorted(compose.variants["Everything"].arches),
|
|
"1",
|
|
"2",
|
|
"image-target",
|
|
[self.topdir + "/compose/Everything/$arch/os"],
|
|
[],
|
|
),
|
|
),
|
|
],
|
|
)
|
|
|
|
@mock.patch("pungi.phases.osbuild.ThreadPool")
|
|
def test_rich_repos(self, ThreadPool):
|
|
repo = {"baseurl": "http://example.com/repo", "package_sets": ["build"]}
|
|
cfg = {
|
|
"name": "test-image",
|
|
"distro": "rhel-8",
|
|
"version": "1",
|
|
"target": "image-target",
|
|
"arches": ["x86_64"],
|
|
"image_types": ["qcow2"],
|
|
"repo": [repo],
|
|
}
|
|
compose = helpers.DummyCompose(
|
|
self.topdir, {"osbuild": {"^Everything$": [cfg]}}
|
|
)
|
|
|
|
self.assertValidConfig(compose.conf)
|
|
|
|
pool = ThreadPool.return_value
|
|
|
|
phase = osbuild.OSBuildPhase(compose)
|
|
phase.run()
|
|
|
|
self.assertEqual(len(pool.add.call_args_list), 1)
|
|
self.assertEqual(
|
|
pool.queue_put.call_args_list,
|
|
[
|
|
mock.call(
|
|
(
|
|
compose,
|
|
compose.variants["Everything"],
|
|
cfg,
|
|
["x86_64"],
|
|
"1",
|
|
None,
|
|
"image-target",
|
|
[repo, self.topdir + "/compose/Everything/$arch/os"],
|
|
[],
|
|
),
|
|
),
|
|
],
|
|
)
|
|
|
|
|
|
class RunOSBuildThreadTest(helpers.PungiTestCase):
|
|
def setUp(self):
|
|
super(RunOSBuildThreadTest, self).setUp()
|
|
self.pool = mock.Mock()
|
|
self.t = osbuild.RunOSBuildThread(self.pool)
|
|
self.compose = helpers.DummyCompose(
|
|
self.topdir,
|
|
{
|
|
"koji_profile": "koji",
|
|
"koji_cache": "/tmp",
|
|
"translate_paths": [(self.topdir, "http://root")],
|
|
},
|
|
)
|
|
|
|
def make_fake_watch(self, retval):
|
|
def inner(task_id, log_file):
|
|
with open(log_file, "w") as f:
|
|
f.write("Creating compose: test-image-1-1 1234\n")
|
|
return retval
|
|
|
|
return inner
|
|
|
|
@mock.patch("pungi.util.get_file_size", new=lambda fp: 65536)
|
|
@mock.patch("pungi.util.get_mtime", new=lambda fp: 1024)
|
|
@mock.patch("pungi.phases.osbuild.Linker")
|
|
@mock.patch("pungi.phases.osbuild.kojiwrapper.KojiWrapper")
|
|
def test_process(self, KojiWrapper, Linker):
|
|
cfg = {"name": "test-image", "distro": "rhel-8", "image_types": ["qcow2"]}
|
|
build_id = 5678
|
|
koji = KojiWrapper.return_value
|
|
koji.watch_task.side_effect = self.make_fake_watch(0)
|
|
koji.koji_proxy.osbuildImage.return_value = 1234
|
|
koji.koji_proxy.getTaskResult.return_value = {
|
|
"composer": {"server": "https://composer.osbuild.org", "id": ""},
|
|
"koji": {"build": build_id},
|
|
}
|
|
koji.koji_proxy.getBuild.return_value = {
|
|
"build_id": build_id,
|
|
"name": "test-image",
|
|
"version": "1",
|
|
"release": "1",
|
|
}
|
|
koji.koji_proxy.listArchives.return_value = [
|
|
{
|
|
"extra": {"image": {"arch": "aarch64"}},
|
|
"filename": "disk.aarch64.qcow2",
|
|
"type_name": "qcow2",
|
|
},
|
|
{
|
|
"extra": {"image": {"arch": "x86_64"}},
|
|
"filename": "disk.x86_64.qcow2",
|
|
"type_name": "qcow2",
|
|
},
|
|
]
|
|
koji.koji_module.pathinfo = orig_koji.pathinfo
|
|
|
|
self.t.process(
|
|
(
|
|
self.compose,
|
|
self.compose.variants["Everything"],
|
|
cfg,
|
|
["aarch64", "x86_64"],
|
|
"1", # version
|
|
"15", # release
|
|
"image-target",
|
|
[
|
|
self.topdir + "/compose/Everything/$arch/os",
|
|
{
|
|
"baseurl": self.topdir + "/compose/Everything/$arch/os",
|
|
"package_sets": ["build"],
|
|
},
|
|
],
|
|
["x86_64"],
|
|
),
|
|
1,
|
|
)
|
|
|
|
# Verify two Koji instances were created.
|
|
self.assertEqual(len(KojiWrapper.call_args), 2)
|
|
# Verify correct calls to Koji
|
|
self.assertEqual(
|
|
koji.mock_calls,
|
|
[
|
|
mock.call.login(),
|
|
mock.call.koji_proxy.osbuildImage(
|
|
"test-image",
|
|
"1",
|
|
"rhel-8",
|
|
["qcow2"],
|
|
"image-target",
|
|
["aarch64", "x86_64"],
|
|
opts={
|
|
"release": "15",
|
|
"repo": [
|
|
self.topdir + "/compose/Everything/$arch/os",
|
|
{
|
|
"baseurl": self.topdir + "/compose/Everything/$arch/os",
|
|
"package_sets": ["build"],
|
|
},
|
|
],
|
|
},
|
|
),
|
|
mock.call.save_task_id(1234),
|
|
mock.call.watch_task(1234, mock.ANY),
|
|
mock.call.koji_proxy.getTaskResult(1234),
|
|
mock.call.koji_proxy.getBuild(build_id),
|
|
mock.call.koji_proxy.listArchives(buildID=build_id),
|
|
],
|
|
)
|
|
|
|
# Assert there are 2 images added to manifest and the arguments are sane
|
|
self.assertEqual(
|
|
self.compose.im.add.call_args_list,
|
|
[
|
|
mock.call(arch="aarch64", variant="Everything", image=mock.ANY),
|
|
mock.call(arch="x86_64", variant="Everything", image=mock.ANY),
|
|
],
|
|
)
|
|
for call in self.compose.im.add.call_args_list:
|
|
_, kwargs = call
|
|
image = kwargs["image"]
|
|
self.assertEqual(kwargs["variant"], "Everything")
|
|
self.assertIn(kwargs["arch"], ("aarch64", "x86_64"))
|
|
self.assertEqual(kwargs["arch"], image.arch)
|
|
self.assertEqual(
|
|
"Everything/%(arch)s/images/disk.%(arch)s.qcow2" % {"arch": image.arch},
|
|
image.path,
|
|
)
|
|
self.assertEqual("qcow2", image.format)
|
|
self.assertEqual("qcow2", image.type)
|
|
self.assertEqual("Everything", image.subvariant)
|
|
|
|
self.assertTrue(
|
|
os.path.isdir(self.topdir + "/compose/Everything/aarch64/images")
|
|
)
|
|
self.assertTrue(
|
|
os.path.isdir(self.topdir + "/compose/Everything/x86_64/images")
|
|
)
|
|
|
|
self.assertEqual(
|
|
Linker.return_value.mock_calls,
|
|
[
|
|
mock.call.link(
|
|
"/mnt/koji/packages/test-image/1/1/images/disk.%(arch)s.qcow2"
|
|
% {"arch": arch},
|
|
self.topdir
|
|
+ "/compose/Everything/%(arch)s/images/disk.%(arch)s.qcow2"
|
|
% {"arch": arch},
|
|
link_type="hardlink-or-copy",
|
|
)
|
|
for arch in ["aarch64", "x86_64"]
|
|
],
|
|
)
|
|
|
|
@mock.patch("pungi.util.get_file_size", new=lambda fp: 65536)
|
|
@mock.patch("pungi.util.get_mtime", new=lambda fp: 1024)
|
|
@mock.patch("pungi.phases.osbuild.Linker")
|
|
@mock.patch("pungi.phases.osbuild.kojiwrapper.KojiWrapper")
|
|
def test_process_ostree(self, KojiWrapper, Linker):
|
|
cfg = {
|
|
"name": "test-image",
|
|
"distro": "rhel-8",
|
|
"image_types": ["edge-raw-disk"],
|
|
"ostree_url": "http://edge.example.com/repo",
|
|
"ostree_ref": "test/iot",
|
|
"ostree_parent": "test/iot-parent",
|
|
}
|
|
build_id = 5678
|
|
koji = KojiWrapper.return_value
|
|
koji.watch_task.side_effect = self.make_fake_watch(0)
|
|
koji.koji_proxy.osbuildImage.return_value = 1234
|
|
koji.koji_proxy.getTaskResult.return_value = {
|
|
"composer": {"server": "https://composer.osbuild.org", "id": ""},
|
|
"koji": {"build": build_id},
|
|
}
|
|
koji.koji_proxy.getBuild.return_value = {
|
|
"build_id": build_id,
|
|
"name": "test-image",
|
|
"version": "1",
|
|
"release": "1",
|
|
}
|
|
koji.koji_proxy.listArchives.return_value = [
|
|
{
|
|
"extra": {"image": {"arch": "aarch64"}},
|
|
"filename": "image.aarch64.raw.xz",
|
|
"type_name": "raw-xz",
|
|
},
|
|
{
|
|
"extra": {"image": {"arch": "x86_64"}},
|
|
"filename": "image.x86_64.raw.xz",
|
|
"type_name": "raw-xz",
|
|
},
|
|
]
|
|
koji.koji_module.pathinfo = orig_koji.pathinfo
|
|
|
|
self.t.process(
|
|
(
|
|
self.compose,
|
|
self.compose.variants["Everything"],
|
|
cfg,
|
|
["aarch64", "x86_64"],
|
|
"1", # version
|
|
"15", # release
|
|
"image-target",
|
|
[self.topdir + "/compose/Everything/$arch/os"],
|
|
["x86_64"],
|
|
),
|
|
1,
|
|
)
|
|
|
|
# Verify two Koji instances were created.
|
|
self.assertEqual(len(KojiWrapper.call_args), 2)
|
|
# Verify correct calls to Koji
|
|
self.assertEqual(
|
|
koji.mock_calls,
|
|
[
|
|
mock.call.login(),
|
|
mock.call.koji_proxy.osbuildImage(
|
|
"test-image",
|
|
"1",
|
|
"rhel-8",
|
|
["edge-raw-disk"],
|
|
"image-target",
|
|
["aarch64", "x86_64"],
|
|
opts={
|
|
"release": "15",
|
|
"repo": [self.topdir + "/compose/Everything/$arch/os"],
|
|
"ostree": {
|
|
"url": "http://edge.example.com/repo",
|
|
"ref": "test/iot",
|
|
"parent": "test/iot-parent",
|
|
},
|
|
},
|
|
),
|
|
mock.call.save_task_id(1234),
|
|
mock.call.watch_task(1234, mock.ANY),
|
|
mock.call.koji_proxy.getTaskResult(1234),
|
|
mock.call.koji_proxy.getBuild(build_id),
|
|
mock.call.koji_proxy.listArchives(buildID=build_id),
|
|
],
|
|
)
|
|
|
|
# Assert there are 2 images added to manifest and the arguments are sane
|
|
self.assertEqual(
|
|
self.compose.im.add.call_args_list,
|
|
[
|
|
mock.call(arch="aarch64", variant="Everything", image=mock.ANY),
|
|
mock.call(arch="x86_64", variant="Everything", image=mock.ANY),
|
|
],
|
|
)
|
|
for call in self.compose.im.add.call_args_list:
|
|
_, kwargs = call
|
|
image = kwargs["image"]
|
|
self.assertEqual(kwargs["variant"], "Everything")
|
|
self.assertIn(kwargs["arch"], ("aarch64", "x86_64"))
|
|
self.assertEqual(kwargs["arch"], image.arch)
|
|
self.assertEqual(
|
|
"Everything/%(arch)s/images/image.%(arch)s.raw.xz"
|
|
% {"arch": image.arch},
|
|
image.path,
|
|
)
|
|
self.assertEqual("raw.xz", image.format)
|
|
self.assertEqual("raw-xz", image.type)
|
|
self.assertEqual("Everything", image.subvariant)
|
|
|
|
self.assertTrue(
|
|
os.path.isdir(self.topdir + "/compose/Everything/aarch64/images")
|
|
)
|
|
self.assertTrue(
|
|
os.path.isdir(self.topdir + "/compose/Everything/x86_64/images")
|
|
)
|
|
|
|
self.assertEqual(
|
|
Linker.return_value.mock_calls,
|
|
[
|
|
mock.call.link(
|
|
"/mnt/koji/packages/test-image/1/1/images/image.%(arch)s.raw.xz"
|
|
% {"arch": arch},
|
|
self.topdir
|
|
+ "/compose/Everything/%(arch)s/images/image.%(arch)s.raw.xz"
|
|
% {"arch": arch},
|
|
link_type="hardlink-or-copy",
|
|
)
|
|
for arch in ["aarch64", "x86_64"]
|
|
],
|
|
)
|
|
|
|
@mock.patch("pungi.util.get_file_size", new=lambda fp: 65536)
|
|
@mock.patch("pungi.util.get_mtime", new=lambda fp: 1024)
|
|
@mock.patch("pungi.phases.osbuild.Linker")
|
|
@mock.patch("pungi.phases.osbuild.kojiwrapper.KojiWrapper")
|
|
def test_process_upload_options(self, KojiWrapper, Linker):
|
|
cfg = {
|
|
"name": "test-image",
|
|
"distro": "rhel-8",
|
|
"image_types": ["rhel-ec2"],
|
|
"upload_options": {
|
|
"region": "us-east-1",
|
|
"share_with_accounts": ["123456789012"],
|
|
},
|
|
}
|
|
build_id = 5678
|
|
koji = KojiWrapper.return_value
|
|
koji.watch_task.side_effect = self.make_fake_watch(0)
|
|
koji.koji_proxy.osbuildImage.return_value = 1234
|
|
koji.koji_proxy.getTaskResult.return_value = {
|
|
"composer": {"server": "https://composer.osbuild.org", "id": ""},
|
|
"koji": {"build": build_id},
|
|
}
|
|
koji.koji_proxy.getBuild.return_value = {
|
|
"build_id": build_id,
|
|
"name": "test-image",
|
|
"version": "1",
|
|
"release": "1",
|
|
}
|
|
koji.koji_proxy.listArchives.return_value = [
|
|
{
|
|
"extra": {"image": {"arch": "x86_64"}},
|
|
"filename": "image.raw.xz",
|
|
"type_name": "raw-xz",
|
|
}
|
|
]
|
|
koji.koji_module.pathinfo = orig_koji.pathinfo
|
|
|
|
self.t.process(
|
|
(
|
|
self.compose,
|
|
self.compose.variants["Everything"],
|
|
cfg,
|
|
["x86_64"],
|
|
"1", # version
|
|
"15", # release
|
|
"image-target",
|
|
[self.topdir + "/compose/Everything/$arch/os"],
|
|
["x86_64"],
|
|
),
|
|
1,
|
|
)
|
|
|
|
# Verify two Koji instances were created.
|
|
self.assertEqual(len(KojiWrapper.call_args), 2)
|
|
# Verify correct calls to Koji
|
|
self.assertEqual(
|
|
koji.mock_calls,
|
|
[
|
|
mock.call.login(),
|
|
mock.call.koji_proxy.osbuildImage(
|
|
"test-image",
|
|
"1",
|
|
"rhel-8",
|
|
["rhel-ec2"],
|
|
"image-target",
|
|
["x86_64"],
|
|
opts={
|
|
"release": "15",
|
|
"repo": [self.topdir + "/compose/Everything/$arch/os"],
|
|
"upload_options": {
|
|
"region": "us-east-1",
|
|
"share_with_accounts": ["123456789012"],
|
|
},
|
|
},
|
|
),
|
|
mock.call.save_task_id(1234),
|
|
mock.call.watch_task(1234, mock.ANY),
|
|
mock.call.koji_proxy.getTaskResult(1234),
|
|
mock.call.koji_proxy.getBuild(build_id),
|
|
mock.call.koji_proxy.listArchives(buildID=build_id),
|
|
],
|
|
)
|
|
|
|
# Assert there is one image added to manifest and the arguments are sane
|
|
self.assertEqual(
|
|
self.compose.im.add.call_args_list,
|
|
[
|
|
mock.call(arch="x86_64", variant="Everything", image=mock.ANY),
|
|
],
|
|
)
|
|
for call in self.compose.im.add.call_args_list:
|
|
_, kwargs = call
|
|
image = kwargs["image"]
|
|
self.assertEqual(kwargs["variant"], "Everything")
|
|
self.assertIn(kwargs["arch"], ("x86_64"))
|
|
self.assertEqual(kwargs["arch"], image.arch)
|
|
self.assertEqual(
|
|
"Everything/x86_64/images/image.raw.xz",
|
|
image.path,
|
|
)
|
|
self.assertEqual("raw.xz", image.format)
|
|
self.assertEqual("raw-xz", image.type)
|
|
self.assertEqual("Everything", image.subvariant)
|
|
|
|
self.assertTrue(
|
|
os.path.isdir(self.topdir + "/compose/Everything/x86_64/images")
|
|
)
|
|
|
|
self.assertEqual(
|
|
Linker.return_value.mock_calls,
|
|
[
|
|
mock.call.link(
|
|
"/mnt/koji/packages/test-image/1/1/images/image.raw.xz",
|
|
self.topdir + "/compose/Everything/x86_64/images/image.raw.xz",
|
|
link_type="hardlink-or-copy",
|
|
)
|
|
],
|
|
)
|
|
|
|
@mock.patch("pungi.util.get_file_size", new=lambda fp: 65536)
|
|
@mock.patch("pungi.util.get_mtime", new=lambda fp: 1024)
|
|
@mock.patch("pungi.phases.osbuild.Linker")
|
|
@mock.patch("pungi.phases.osbuild.kojiwrapper.KojiWrapper")
|
|
def test_process_without_release(self, KojiWrapper, Linker):
|
|
cfg = {"name": "test-image", "distro": "rhel-8", "image_types": ["qcow2"]}
|
|
build_id = 5678
|
|
koji = KojiWrapper.return_value
|
|
koji.watch_task.side_effect = self.make_fake_watch(0)
|
|
koji.koji_proxy.osbuildImage.return_value = 1234
|
|
koji.koji_proxy.getTaskResult.return_value = {
|
|
"composer": {"server": "https://composer.osbuild.org", "id": ""},
|
|
"koji": {"build": build_id},
|
|
}
|
|
koji.koji_proxy.getBuild.return_value = {
|
|
"build_id": build_id,
|
|
"name": "test-image",
|
|
"version": "1",
|
|
"release": "1",
|
|
}
|
|
koji.koji_proxy.listArchives.return_value = [
|
|
{
|
|
"extra": {"image": {"arch": "aarch64"}},
|
|
"filename": "disk.aarch64.qcow2",
|
|
"type_name": "qcow2",
|
|
},
|
|
{
|
|
"extra": {"image": {"arch": "x86_64"}},
|
|
"filename": "disk.x86_64.qcow2",
|
|
"type_name": "qcow2",
|
|
},
|
|
]
|
|
koji.koji_module.pathinfo = orig_koji.pathinfo
|
|
|
|
self.t.process(
|
|
(
|
|
self.compose,
|
|
self.compose.variants["Everything"],
|
|
cfg,
|
|
["aarch64", "x86_64"],
|
|
"1",
|
|
None,
|
|
"image-target",
|
|
[self.topdir + "/compose/Everything/$arch/os"],
|
|
["x86_64"],
|
|
),
|
|
1,
|
|
)
|
|
|
|
# Verify two Koji instances were created.
|
|
self.assertEqual(len(KojiWrapper.call_args), 2)
|
|
# Verify correct calls to Koji
|
|
self.assertEqual(
|
|
koji.mock_calls,
|
|
[
|
|
mock.call.login(),
|
|
mock.call.koji_proxy.osbuildImage(
|
|
"test-image",
|
|
"1",
|
|
"rhel-8",
|
|
["qcow2"],
|
|
"image-target",
|
|
["aarch64", "x86_64"],
|
|
opts={"repo": [self.topdir + "/compose/Everything/$arch/os"]},
|
|
),
|
|
mock.call.save_task_id(1234),
|
|
mock.call.watch_task(1234, mock.ANY),
|
|
mock.call.koji_proxy.getTaskResult(1234),
|
|
mock.call.koji_proxy.getBuild(build_id),
|
|
mock.call.koji_proxy.listArchives(buildID=build_id),
|
|
],
|
|
)
|
|
|
|
# Assert there are 2 images added to manifest and the arguments are sane
|
|
self.assertEqual(
|
|
self.compose.im.add.call_args_list,
|
|
[
|
|
mock.call(arch="aarch64", variant="Everything", image=mock.ANY),
|
|
mock.call(arch="x86_64", variant="Everything", image=mock.ANY),
|
|
],
|
|
)
|
|
for call in self.compose.im.add.call_args_list:
|
|
_, kwargs = call
|
|
image = kwargs["image"]
|
|
self.assertEqual(kwargs["variant"], "Everything")
|
|
self.assertIn(kwargs["arch"], ("aarch64", "x86_64"))
|
|
self.assertEqual(kwargs["arch"], image.arch)
|
|
self.assertEqual(
|
|
"Everything/%(arch)s/images/disk.%(arch)s.qcow2" % {"arch": image.arch},
|
|
image.path,
|
|
)
|
|
self.assertEqual("qcow2", image.format)
|
|
self.assertEqual("qcow2", image.type)
|
|
self.assertEqual("Everything", image.subvariant)
|
|
|
|
self.assertTrue(
|
|
os.path.isdir(self.topdir + "/compose/Everything/aarch64/images")
|
|
)
|
|
self.assertTrue(
|
|
os.path.isdir(self.topdir + "/compose/Everything/x86_64/images")
|
|
)
|
|
|
|
self.assertEqual(
|
|
Linker.return_value.mock_calls,
|
|
[
|
|
mock.call.link(
|
|
"/mnt/koji/packages/test-image/1/1/images/disk.%(arch)s.qcow2"
|
|
% {"arch": arch},
|
|
self.topdir
|
|
+ "/compose/Everything/%(arch)s/images/disk.%(arch)s.qcow2"
|
|
% {"arch": arch},
|
|
link_type="hardlink-or-copy",
|
|
)
|
|
for arch in ["aarch64", "x86_64"]
|
|
],
|
|
)
|
|
|
|
@mock.patch("pungi.phases.osbuild.kojiwrapper.KojiWrapper")
|
|
def test_task_fails(self, KojiWrapper):
|
|
cfg = {"name": "test-image", "distro": "rhel-8", "image_types": ["qcow2"]}
|
|
koji = KojiWrapper.return_value
|
|
koji.watch_task.side_effect = self.make_fake_watch(1)
|
|
koji.koji_proxy.osbuildImage.return_value = 1234
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
self.t.process(
|
|
(
|
|
self.compose,
|
|
self.compose.variants["Everything"],
|
|
cfg,
|
|
["aarch64", "x86_64"],
|
|
"1",
|
|
None,
|
|
"image-target",
|
|
[self.topdir + "/compose/Everything/$arch/os"],
|
|
False,
|
|
),
|
|
1,
|
|
)
|
|
|
|
@mock.patch("pungi.phases.osbuild.kojiwrapper.KojiWrapper")
|
|
def test_task_fails_but_is_failable(self, KojiWrapper):
|
|
cfg = {
|
|
"name": "test-image",
|
|
"distro": "rhel-8",
|
|
"image_types": ["qcow2"],
|
|
"failable": ["x86_65"],
|
|
}
|
|
koji = KojiWrapper.return_value
|
|
koji.watch_task.side_effect = self.make_fake_watch(1)
|
|
koji.koji_proxy.osbuildImage.return_value = 1234
|
|
|
|
self.t.process(
|
|
(
|
|
self.compose,
|
|
self.compose.variants["Everything"],
|
|
cfg,
|
|
["aarch64", "x86_64"],
|
|
"1",
|
|
None,
|
|
"image-target",
|
|
[self.topdir + "/compose/Everything/$arch/os"],
|
|
True,
|
|
),
|
|
1,
|
|
)
|
|
|
|
self.assertFalse(
|
|
os.path.isdir(self.topdir + "/compose/Everything/aarch64/images")
|
|
)
|
|
self.assertFalse(
|
|
os.path.isdir(self.topdir + "/compose/Everything/x86_64/images")
|
|
)
|
|
self.assertEqual(len(self.compose.im.add.call_args_list), 0)
|