pungi/tests/test_osbs_phase.py
Lubomír Sedlář ff5a7e6377 Make python3-mock dependency optional
https://fedoraproject.org/wiki/Changes/RemovePythonMockUsage

Prefer using unittest.mock to a standalone package. The separate
packages should only really be needed on Python 2.7 these days.

The test requirements file is updated to only require mock on old
Python, and the dependency is removed from setup.py to avoid issues
there.

Relates: https://src.fedoraproject.org/rpms/pungi/pull-request/9

Signed-off-by: Lubomír Sedlář <lsedlar@redhat.com>
2024-01-26 09:45:19 +01:00

616 lines
22 KiB
Python

# -*- coding: utf-8 -*-
try:
from unittest import mock
except ImportError:
import mock
import json
import copy
import os
from tests import helpers
from pungi import checks
from pungi.phases import osbs
class OSBSPhaseTest(helpers.PungiTestCase):
@mock.patch("pungi.phases.osbs.ThreadPool")
def test_run(self, ThreadPool):
cfg = helpers.IterableMock()
compose = helpers.DummyCompose(self.topdir, {"osbs": {"^Everything$": cfg}})
pool = ThreadPool.return_value
phase = osbs.OSBSPhase(compose, None, None)
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))],
)
@mock.patch("pungi.phases.osbs.ThreadPool")
def test_skip_without_config(self, ThreadPool):
compose = helpers.DummyCompose(self.topdir, {})
compose.just_phases = None
compose.skip_phases = []
phase = osbs.OSBSPhase(compose, None, None)
self.assertTrue(phase.skip())
@mock.patch("pungi.phases.osbs.ThreadPool")
def test_request_push(self, ThreadPool):
compose = helpers.DummyCompose(self.topdir, {"osbs": {"^Everything$": {}}})
compose.just_phases = None
compose.skip_phases = []
compose.notifier = mock.Mock()
phase = osbs.OSBSPhase(compose, None, None)
phase.start()
phase.stop()
phase.pool.registries = {"foo": "bar"}
phase.request_push()
with open(os.path.join(self.topdir, "logs/global/osbs-registries.json")) as f:
data = json.load(f)
self.assertEqual(data, phase.pool.registries)
self.assertEqual(
compose.notifier.call_args_list,
[],
)
TASK_RESULT = {
"koji_builds": ["54321"],
"repositories": [
"registry.example.com:8888/rcm/buildroot:f24-docker-candidate-20160617141632",
],
}
BUILD_INFO = {
"completion_time": "2016-06-17 18:25:30",
"completion_ts": 1466187930.0,
"creation_event_id": 13227702,
"creation_time": "2016-06-17 18:25:57.611172",
"creation_ts": 1466187957.61117,
"epoch": None,
"extra": {"container_koji_task_id": "12345", "image": {}},
"id": 54321,
"name": "my-name",
"nvr": "my-name-1.0-1",
"owner_id": 3436,
"owner_name": "osbs",
"package_id": 50072,
"package_name": "my-name",
"release": "1",
"source": "git://example.com/repo?#BEEFCAFE",
"start_time": "2016-06-17 18:16:37",
"start_ts": 1466187397.0,
"state": 1,
"task_id": None,
"version": "1.0",
"volume_id": 0,
"volume_name": "DEFAULT",
}
ARCHIVES = [
{
"build_id": 54321,
"buildroot_id": 2955357,
"checksum": "a2922842dc80873ac782da048c54f6cc",
"checksum_type": 0,
"extra": {
"docker": {
"id": "408c4cd37a87a807bec65dd13b049a32fe090d2fa1a8e891f65e3e3e683996d7", # noqa: E501
"parent_id": "6c3a84d798dc449313787502060b6d5b4694d7527d64a7c99ba199e3b2df834e", # noqa: E501
"repositories": ["registry.example.com:8888/rcm/buildroot:1.0-1"],
},
"image": {"arch": "x86_64"},
},
"filename": "docker-image-408c4cd37a87a807bec65dd13b049a32fe090d2fa1a8e891f65e3e3e683996d7.x86_64.tar.gz", # noqa: E501
"id": 1436049,
"metadata_only": False,
"size": 174038795,
"type_description": "Tar file",
"type_extensions": "tar tar.gz tar.bz2 tar.xz",
"type_id": 4,
"type_name": "tar",
}
]
METADATA = {
"Server": {
"x86_64": [
{
"name": "my-name",
"version": "1.0",
"release": "1",
"nvr": "my-name-1.0-1",
"creation_time": BUILD_INFO["creation_time"],
"filename": ARCHIVES[0]["filename"],
"size": ARCHIVES[0]["size"],
"docker": {
"id": "408c4cd37a87a807bec65dd13b049a32fe090d2fa1a8e891f65e3e3e683996d7", # noqa: E501
"parent_id": "6c3a84d798dc449313787502060b6d5b4694d7527d64a7c99ba199e3b2df834e", # noqa: E501
"repositories": ["registry.example.com:8888/rcm/buildroot:1.0-1"],
},
"image": {"arch": "x86_64"},
"checksum": ARCHIVES[0]["checksum"],
}
]
}
}
RPMS = []
SCRATCH_TASK_RESULT = {
"koji_builds": [],
"repositories": [
"registry.example.com:8888/rcm/buildroot:f24-docker-candidate-20160617141632",
],
}
SCRATCH_METADATA = {
"Server": {
"scratch": [
{
"koji_task": 12345,
"repositories": [
"registry.example.com:8888/rcm/buildroot:f24-docker-candidate-20160617141632", # noqa: E501
],
}
]
}
}
class OSBSThreadTest(helpers.PungiTestCase):
def setUp(self):
super(OSBSThreadTest, self).setUp()
self.pool = mock.Mock(registries={})
self.t = osbs.OSBSThread(self.pool)
self.compose = helpers.DummyCompose(
self.topdir,
{
"koji_profile": "koji",
"koji_cache": "/tmp",
"translate_paths": [(self.topdir, "http://root")],
},
)
def _setupMock(self, KojiWrapper, scratch=False):
self.wrapper = KojiWrapper.return_value
self.wrapper.koji_proxy.buildContainer.return_value = 12345
if scratch:
self.wrapper.koji_proxy.getTaskResult.return_value = SCRATCH_TASK_RESULT
else:
self.wrapper.koji_proxy.getTaskResult.return_value = TASK_RESULT
self.wrapper.koji_proxy.getBuild.return_value = BUILD_INFO
self.wrapper.koji_proxy.listArchives.return_value = ARCHIVES
self.wrapper.koji_proxy.listRPMs.return_value = RPMS
self.wrapper.koji_proxy.getLatestBuilds.return_value = [
mock.Mock(),
mock.Mock(),
]
self.wrapper.koji_proxy.getNextRelease.return_value = 3
self.wrapper.watch_task.return_value = 0
def _assertCorrectMetadata(self, scratch=False):
self.maxDiff = None
if scratch:
metadata = copy.deepcopy(SCRATCH_METADATA)
metadata["Server"]["scratch"][0]["compose_id"] = self.compose.compose_id
metadata["Server"]["scratch"][0]["koji_task"] = 12345
else:
metadata = copy.deepcopy(METADATA)
metadata["Server"]["x86_64"][0]["compose_id"] = self.compose.compose_id
metadata["Server"]["x86_64"][0]["koji_task"] = 12345
self.assertEqual(self.compose.containers_metadata, metadata)
def _assertCorrectCalls(self, opts, setupCalls=None, scratch=False):
setupCalls = setupCalls or []
options = {
"yum_repourls": [
"http://root/work/global/tmp-Server/compose-rpms-Server-1.repo"
]
}
if scratch:
options["scratch"] = True
options.update(opts)
expect_calls = [mock.call.login()] + setupCalls
expect_calls.extend(
[
mock.call.koji_proxy.buildContainer(
"git://example.com/repo?#BEEFCAFE",
"f24-docker-candidate",
options,
priority=None,
),
mock.call.save_task_id(12345),
mock.call.watch_task(
12345, self.topdir + "/logs/global/osbs/Server-1-watch-task.log"
),
mock.call.koji_proxy.getTaskResult(12345),
]
)
if not scratch:
expect_calls.extend(
[
mock.call.koji_proxy.getBuild(54321),
mock.call.koji_proxy.listArchives(54321, type="image"),
mock.call.koji_proxy.listRPMs(imageID=1436049),
]
)
self.assertEqual(self.wrapper.mock_calls, expect_calls)
def _assertRepoFile(self, variants=None, gpgkey=None):
variants = variants or ["Server"]
for variant in variants:
with open(
self.topdir
+ "/work/global/tmp-%s/compose-rpms-%s-1.repo" % (variant, variant)
) as f:
lines = f.read().split("\n")
self.assertIn(
"baseurl=http://root/compose/%s/$basearch/os" % variant, lines
)
if gpgkey:
self.assertIn("gpgcheck=1", lines)
self.assertIn("gpgkey=%s" % gpgkey, lines)
def _assertConfigCorrect(self, cfg):
config = copy.deepcopy(self.compose.conf)
config["osbs"] = {"^Server$": cfg}
self.assertEqual(([], []), checks.validate(config, offline=True))
def _assertConfigMissing(self, cfg, key):
config = copy.deepcopy(self.compose.conf)
config["osbs"] = {"^Server$": cfg}
errors, warnings = checks.validate(config, offline=True)
self.assertIn(
"Failed validation in osbs.^Server$: %r is not valid under any of the given schemas" # noqa: E501
% cfg,
errors,
)
self.assertIn(" Possible reason: %r is a required property" % key, errors)
self.assertEqual([], warnings)
@mock.patch("pungi.phases.osbs.get_file_from_scm")
@mock.patch("pungi.phases.osbs.kojiwrapper.KojiWrapper")
def test_minimal_run(self, KojiWrapper, get_file_from_scm):
cfg = {
"url": "git://example.com/repo?#BEEFCAFE",
"target": "f24-docker-candidate",
"git_branch": "f24-docker",
}
self._setupMock(KojiWrapper)
self._assertConfigCorrect(cfg)
self.t.process((self.compose, self.compose.variants["Server"], cfg), 1)
self._assertCorrectCalls({"git_branch": "f24-docker"})
self._assertCorrectMetadata()
self._assertRepoFile()
@mock.patch("pungi.phases.osbs.get_file_from_scm")
@mock.patch("pungi.phases.osbs.kojiwrapper.KojiWrapper")
def test_run_failable(self, KojiWrapper, get_file_from_scm):
cfg = {
"url": "git://example.com/repo?#BEEFCAFE",
"target": "f24-docker-candidate",
"git_branch": "f24-docker",
"failable": ["*"],
}
self._setupMock(KojiWrapper)
self._assertConfigCorrect(cfg)
self.t.process((self.compose, self.compose.variants["Server"], cfg), 1)
self._assertCorrectCalls({"git_branch": "f24-docker"})
self._assertCorrectMetadata()
self._assertRepoFile()
@mock.patch("pungi.phases.osbs.get_file_from_scm")
@mock.patch("pungi.phases.osbs.kojiwrapper.KojiWrapper")
def test_run_with_more_args(self, KojiWrapper, get_file_from_scm):
cfg = {
"url": "git://example.com/repo?#BEEFCAFE",
"target": "f24-docker-candidate",
"git_branch": "f24-docker",
"name": "my-name",
"version": "1.0",
}
self._setupMock(KojiWrapper)
self._assertConfigCorrect(cfg)
self.t.process((self.compose, self.compose.variants["Server"], cfg), 1)
self._assertCorrectCalls(
{"name": "my-name", "version": "1.0", "git_branch": "f24-docker"}
)
self._assertCorrectMetadata()
self._assertRepoFile()
@mock.patch("pungi.phases.osbs.get_file_from_scm")
@mock.patch("pungi.phases.osbs.kojiwrapper.KojiWrapper")
def test_run_with_extra_repos(self, KojiWrapper, get_file_from_scm):
cfg = {
"url": "git://example.com/repo?#BEEFCAFE",
"target": "f24-docker-candidate",
"git_branch": "f24-docker",
"name": "my-name",
"version": "1.0",
"repo": [
"Everything",
"http://pkgs.example.com/my.repo",
"/extra/repo",
"http://cts.localhost/$COMPOSE_ID/repo",
],
}
self.compose.conf["translate_paths"].append(("/extra", "http://example.com"))
self._setupMock(KojiWrapper)
self._assertConfigCorrect(cfg)
self.t.process((self.compose, self.compose.variants["Server"], cfg), 1)
options = {
"name": "my-name",
"version": "1.0",
"git_branch": "f24-docker",
"yum_repourls": [
"http://root/work/global/tmp-Server/compose-rpms-Server-1.repo",
"http://root/work/global/tmp-Everything/compose-rpms-Everything-1.repo",
"http://pkgs.example.com/my.repo",
"http://root/work/global/tmp/compose-rpms-local-1.repo",
"http://cts.localhost/%s/repo" % self.compose.compose_id,
],
}
self._assertCorrectCalls(options)
self._assertCorrectMetadata()
self._assertRepoFile(["Server", "Everything"])
with open(
os.path.join(self.topdir, "work/global/tmp/compose-rpms-local-1.repo")
) as f:
self.assertIn("baseurl=http://example.com/repo\n", f)
@mock.patch("pungi.phases.osbs.get_file_from_scm")
@mock.patch("pungi.phases.osbs.kojiwrapper.KojiWrapper")
def test_run_with_extra_repos_with_cts(self, KojiWrapper, get_file_from_scm):
cfg = {
"url": "git://example.com/repo?#BEEFCAFE",
"target": "f24-docker-candidate",
"git_branch": "f24-docker",
"name": "my-name",
"version": "1.0",
"repo": [
"Everything",
],
}
self.compose.conf["cts_url"] = "http://cts.localhost"
self._setupMock(KojiWrapper)
self._assertConfigCorrect(cfg)
self.t.process((self.compose, self.compose.variants["Server"], cfg), 1)
cts_url = "http://cts.localhost/api/1/composes/%s" % self.compose.compose_id
options = {
"name": "my-name",
"version": "1.0",
"git_branch": "f24-docker",
"yum_repourls": [
"%s/repo/?variant=Server" % cts_url,
"%s/repo/?variant=Everything" % cts_url,
],
}
self._assertCorrectCalls(options)
self._assertCorrectMetadata()
@mock.patch("pungi.phases.osbs.get_file_from_scm")
@mock.patch("pungi.phases.osbs.kojiwrapper.KojiWrapper")
def test_run_with_deprecated_registry(self, KojiWrapper, get_file_from_scm):
cfg = {
"url": "git://example.com/repo?#BEEFCAFE",
"target": "f24-docker-candidate",
"git_branch": "f24-docker",
"name": "my-name",
"version": "1.0",
"repo": ["Everything", "http://pkgs.example.com/my.repo"],
"registry": {"foo": "bar"},
}
self._setupMock(KojiWrapper)
self._assertConfigCorrect(cfg)
self.t.process((self.compose, self.compose.variants["Server"], cfg), 1)
options = {
"name": "my-name",
"version": "1.0",
"git_branch": "f24-docker",
"yum_repourls": [
"http://root/work/global/tmp-Server/compose-rpms-Server-1.repo",
"http://root/work/global/tmp-Everything/compose-rpms-Everything-1.repo",
"http://pkgs.example.com/my.repo",
],
}
self._assertCorrectCalls(options)
self._assertCorrectMetadata()
self._assertRepoFile(["Server", "Everything"])
self.assertEqual(self.t.pool.registries, {"my-name-1.0-1": {"foo": "bar"}})
@mock.patch("pungi.phases.osbs.get_file_from_scm")
@mock.patch("pungi.phases.osbs.kojiwrapper.KojiWrapper")
def test_run_with_registry(self, KojiWrapper, get_file_from_scm):
cfg = {
"url": "git://example.com/repo?#BEEFCAFE",
"target": "f24-docker-candidate",
"git_branch": "f24-docker",
"name": "my-name",
"version": "1.0",
"repo": ["Everything", "http://pkgs.example.com/my.repo"],
}
self.compose.conf["osbs_registries"] = {"my-name-1.0-*": [{"foo": "bar"}]}
self._setupMock(KojiWrapper)
self._assertConfigCorrect(cfg)
self.t.process((self.compose, self.compose.variants["Server"], cfg), 1)
options = {
"name": "my-name",
"version": "1.0",
"git_branch": "f24-docker",
"yum_repourls": [
"http://root/work/global/tmp-Server/compose-rpms-Server-1.repo",
"http://root/work/global/tmp-Everything/compose-rpms-Everything-1.repo",
"http://pkgs.example.com/my.repo",
],
}
self._assertCorrectCalls(options)
self._assertCorrectMetadata()
self._assertRepoFile(["Server", "Everything"])
self.assertEqual(self.t.pool.registries, {"my-name-1.0-1": [{"foo": "bar"}]})
@mock.patch("pungi.phases.osbs.get_file_from_scm")
@mock.patch("pungi.phases.osbs.kojiwrapper.KojiWrapper")
def test_run_with_extra_repos_in_list(self, KojiWrapper, get_file_from_scm):
cfg = {
"url": "git://example.com/repo?#BEEFCAFE",
"target": "f24-docker-candidate",
"git_branch": "f24-docker",
"name": "my-name",
"version": "1.0",
"repo": ["Everything", "Client", "http://pkgs.example.com/my.repo"],
}
self._assertConfigCorrect(cfg)
self._setupMock(KojiWrapper)
self.t.process((self.compose, self.compose.variants["Server"], cfg), 1)
options = {
"name": "my-name",
"version": "1.0",
"git_branch": "f24-docker",
"yum_repourls": [
"http://root/work/global/tmp-Server/compose-rpms-Server-1.repo",
"http://root/work/global/tmp-Everything/compose-rpms-Everything-1.repo",
"http://root/work/global/tmp-Client/compose-rpms-Client-1.repo",
"http://pkgs.example.com/my.repo",
],
}
self._assertCorrectCalls(options)
self._assertCorrectMetadata()
self._assertRepoFile(["Server", "Everything", "Client"])
@mock.patch("pungi.phases.osbs.get_file_from_scm")
@mock.patch("pungi.phases.osbs.kojiwrapper.KojiWrapper")
def test_run_with_gpgkey_enabled(self, KojiWrapper, get_file_from_scm):
gpgkey = "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"
cfg = {
"url": "git://example.com/repo?#BEEFCAFE",
"target": "f24-docker-candidate",
"git_branch": "f24-docker",
"name": "my-name",
"version": "1.0",
"repo": ["Everything", "Client", "http://pkgs.example.com/my.repo"],
"gpgkey": gpgkey,
}
self._assertConfigCorrect(cfg)
self._setupMock(KojiWrapper)
self.t.process((self.compose, self.compose.variants["Server"], cfg), 1)
self._assertRepoFile(["Server", "Everything", "Client"], gpgkey=gpgkey)
@mock.patch("pungi.phases.osbs.kojiwrapper.KojiWrapper")
def test_run_with_extra_repos_missing_variant(self, KojiWrapper):
cfg = {
"url": "git://example.com/repo?#BEEFCAFE",
"target": "f24-docker-candidate",
"git_branch": "f24-docker",
"name": "my-name",
"version": "1.0",
"repo": "Gold",
}
self._assertConfigCorrect(cfg)
self._setupMock(KojiWrapper)
with self.assertRaises(RuntimeError) as ctx:
self.t.process((self.compose, self.compose.variants["Server"], cfg), 1)
self.assertIn("no variant Gold", str(ctx.exception))
def test_run_with_missing_url(self):
cfg = {
"target": "f24-docker-candidate",
"git_branch": "f24-docker",
"name": "my-name",
}
self._assertConfigMissing(cfg, "url")
def test_run_with_missing_target(self):
cfg = {
"url": "git://example.com/repo?#BEEFCAFE",
"git_branch": "f24-docker",
"name": "my-name",
}
self._assertConfigMissing(cfg, "target")
def test_run_with_missing_git_branch(self):
cfg = {
"url": "git://example.com/repo?#BEEFCAFE",
"target": "f24-docker-candidate",
}
self._assertConfigMissing(cfg, "git_branch")
@mock.patch("pungi.phases.osbs.get_file_from_scm")
@mock.patch("pungi.phases.osbs.kojiwrapper.KojiWrapper")
def test_failing_task(self, KojiWrapper, get_file_from_scm):
cfg = {
"url": "git://example.com/repo?#BEEFCAFE",
"target": "fedora-24-docker-candidate",
"git_branch": "f24-docker",
}
self._assertConfigCorrect(cfg)
self._setupMock(KojiWrapper)
self.wrapper.watch_task.return_value = 1
with self.assertRaises(RuntimeError) as ctx:
self.t.process((self.compose, self.compose.variants["Server"], cfg), 1)
self.assertRegex(str(ctx.exception), r"task 12345 failed: see .+ for details")
@mock.patch("pungi.phases.osbs.get_file_from_scm")
@mock.patch("pungi.phases.osbs.kojiwrapper.KojiWrapper")
def test_failing_task_with_failable(self, KojiWrapper, get_file_from_scm):
cfg = {
"url": "git://example.com/repo?#BEEFCAFE",
"target": "fedora-24-docker-candidate",
"git_branch": "f24-docker",
"failable": ["*"],
}
self._assertConfigCorrect(cfg)
self._setupMock(KojiWrapper)
self.wrapper.watch_task.return_value = 1
self.t.process((self.compose, self.compose.variants["Server"], cfg), 1)
@mock.patch("pungi.phases.osbs.get_file_from_scm")
@mock.patch("pungi.phases.osbs.kojiwrapper.KojiWrapper")
def test_scratch_metadata(self, KojiWrapper, get_file_from_scm):
cfg = {
"url": "git://example.com/repo?#BEEFCAFE",
"target": "f24-docker-candidate",
"git_branch": "f24-docker",
"scratch": True,
}
self._setupMock(KojiWrapper, scratch=True)
self._assertConfigCorrect(cfg)
self.t.process((self.compose, self.compose.variants["Server"], cfg), 1)
self._assertCorrectCalls({"git_branch": "f24-docker"}, scratch=True)
self._assertCorrectMetadata(scratch=True)
self._assertRepoFile()