koji_retired_packages: modify koji session and make it testable locally

Signed-off-by: Lenka Segura <lsegura@redhat.com>
This commit is contained in:
Lenka Segura 2024-07-11 15:09:30 +02:00
parent a6dfc2e084
commit ca595a3846
3 changed files with 124 additions and 28 deletions

View file

@ -3,7 +3,7 @@ Unit tests for `toddler.plugins.koji_block_retired`
"""
import logging
from unittest.mock import MagicMock
from unittest import mock
import koji
import pytest
@ -56,7 +56,7 @@ class TestProcess:
config = {"key": "value"}
message = "Example message"
self.toddler_cls.process_block_retired = MagicMock()
self.toddler_cls.process_block_retired = mock.MagicMock()
self.toddler_cls.process(config, message)
self.toddler_cls.process_block_retired.assert_called_once_with(config, message)
@ -70,16 +70,18 @@ class TestProcessBlockRetired:
def setup_method(self):
"""Initialize toddler."""
self.toddler_cls = koji_block_retired.KojiBlockRetired()
self.toddler_cls.koji_session = MagicMock()
self.toddler_cls.koji_session = mock.MagicMock()
def test_no_dead_package_file(self, caplog):
"""
Assert that if no dead package was added the plugin stops.
"""
caplog.set_level(logging.INFO)
message = MagicMock()
message = mock.MagicMock()
message.body = {"commit": {"stats": {"files": {}}}}
self.toddler_cls.process_block_retired({}, message)
config = mock.MagicMock()
config = {"profile": "stg"}
self.toddler_cls.process_block_retired(config, message)
assert caplog.records[-1].message == "No dead.package in the commit, bailing"
def test_dead_package_not_added(self, caplog):
@ -87,13 +89,15 @@ class TestProcessBlockRetired:
Assert if in commit dead package wasn't added the plugin stops.
"""
caplog.set_level(logging.INFO)
message = MagicMock()
message = mock.MagicMock()
message.body = {
"commit": {
"stats": {"files": {"dead.package": {"additions": 0, "deletions": 1}}}
}
}
self.toddler_cls.process_block_retired({}, message)
config = mock.MagicMock()
config = {"profile": "stg"}
self.toddler_cls.process_block_retired(config, message)
assert caplog.records[-1].message == "dead.package file was not added, bailing"
def test_dead_package_added_to_main_branch(self, caplog):
@ -101,7 +105,8 @@ class TestProcessBlockRetired:
Assert that that main branch will be changed to rawhide tag
"""
caplog.set_level(logging.INFO)
message = MagicMock()
message = mock.MagicMock()
message.body = {
"commit": {
"stats": {"files": {"dead.package": {"additions": 1, "deletions": 0}}},
@ -110,7 +115,9 @@ class TestProcessBlockRetired:
"namespace": "example_ns",
}
}
self.toddler_cls.process_block_retired({}, message)
config = mock.MagicMock()
config = {"profile": "stg"}
self.toddler_cls.process_block_retired(config, message)
self.toddler_cls.koji_session.packageListBlock.assert_called_once_with(
taginfo="rawhide",
pkginfo="example-repo",
@ -121,7 +128,7 @@ class TestProcessBlockRetired:
Assert that method will process correctly with different branches.
"""
caplog.set_level(logging.INFO)
message = MagicMock()
message = mock.MagicMock()
message.body = {
"commit": {
"stats": {"files": {"dead.package": {"additions": 1, "deletions": 0}}},
@ -130,7 +137,9 @@ class TestProcessBlockRetired:
"namespace": "example_ns",
}
}
self.toddler_cls.process_block_retired({}, message)
config = mock.MagicMock()
config = {"profile": "stg"}
self.toddler_cls.process_block_retired(config, message)
self.toddler_cls.koji_session.packageListBlock.assert_called_once_with(
taginfo="f38",
pkginfo="example-repo",
@ -140,7 +149,7 @@ class TestProcessBlockRetired:
"""
Assert that Koji generic error will be handled correctly.
"""
message = MagicMock()
message = mock.MagicMock()
message.body = {
"commit": {
"stats": {"files": {"dead.package": {"additions": 1, "deletions": 0}}},
@ -149,6 +158,13 @@ class TestProcessBlockRetired:
"namespace": "example_ns",
}
}
self.toddler_cls.koji_session.packageListBlock.side_effect = koji.GenericError
self.toddler_cls.process_block_retired({}, message)
assert caplog.records[-1].message == "Failed to process Koji block retired"
config = mock.MagicMock()
config = {"profile": "stg"}
self.toddler_cls.koji_session.packageListBlock.side_effect = koji.GenericError(
"Failed"
)
with pytest.raises(koji.GenericError):
self.toddler_cls.process_block_retired(config, message)
assert (
caplog.records[-1].message == "Failed to process Koji block retired: Failed"
)

View file

@ -176,6 +176,10 @@ email_overrides_url = "https://pagure.io/fedora-infra/ansible/raw/master/f/roles
[consumer_config.clean_retired_packages]
pdc_active_branches = "https://pdc.fedoraproject.org/extras/active_branches.json"
[consumer_config.koji_block_retired]
# Use "stg" for testing, "koji" for prod
profile = "stg"
[consumer_config.packagers_without_bugzilla]
ignorable_namespaces = ["tests"]

View file

@ -4,17 +4,18 @@ When a branch is retired, automatically blocks the package on Koji
Authors: Anton Medvedev <amedvede@redhat.com>
"""
import argparse
import json
import logging
import sys
from fedora_messaging_git_hook_messages import CommitV1
import koji
import toml
from ..base import ToddlerBase
# Koji hub url for creating ClientSession
KOJIHUB_URL = "https://koji.fedoraproject.org/kojihub"
# Koji hub staging url for creating ClientSession
KOJIHUB_STG_URL = "https://koji.stg.fedoraproject.org/kojihub"
_log = logging.getLogger(__name__)
@ -32,15 +33,15 @@ class KojiBlockRetired(ToddlerBase):
def __init__(self):
self.koji_session = None
def _create_session(self, profile):
"""Makes a koji session, that handles logging in"""
koji_module = koji.get_profile_module(profile)
self.koji_session = koji_module.ClientSession(koji_module.config.server)
def accepts_topic(self, topic):
"""Returns a boolean whether this toddler is interested in messages
from this specific topic.
"""
if topic.startswith("org.fedoraproject.stg"):
self.koji_session = koji.ClientSession(KOJIHUB_STG_URL)
else:
self.koji_session = koji.ClientSession(KOJIHUB_URL)
return topic.startswith("org.fedoraproject.") and topic.endswith("git.receive")
def process(self, config, message):
@ -55,6 +56,8 @@ class KojiBlockRetired(ToddlerBase):
"""
msg = message.body
profile = config["profile"]
# If there is no dead.package file in commit, then it can be ignored
if "dead.package" not in msg["commit"]["stats"]["files"]:
_log.info("No dead.package in the commit, bailing")
@ -77,8 +80,81 @@ class KojiBlockRetired(ToddlerBase):
_log.info("Processing Koji block retired for %s", repo)
if self.koji_session is None:
self._create_session(profile)
try:
self.koji_session.packageListBlock(taginfo=branch_name, pkginfo=repo)
except koji.GenericError:
_log.exception("Failed to process Koji block retired")
return
except (koji.GenericError, koji.ActionNotAllowed) as e:
_log.exception(f"Failed to process Koji block retired: {e}")
raise
def get_arguments(args):
"""Load and parse the CLI arguments."""
parser = argparse.ArgumentParser(
description="Checks that packagers have a valid bugzilla account"
)
parser.add_argument(
"conf",
help="Configuration file",
)
parser.add_argument(
"-m",
dest="message",
help="In case you want a certain message to be processed "
"for local testing, pass it as a file",
)
log_level_group = parser.add_mutually_exclusive_group()
log_level_group.add_argument(
"-q",
"--quiet",
action="store_const",
dest="log_level",
const=logging.WARNING,
default=logging.INFO,
help="Be less talkative",
)
log_level_group.add_argument(
"--debug",
action="store_const",
dest="log_level",
const=logging.DEBUG,
help="Enable debugging output",
)
return parser.parse_args(args)
def main(args):
"""Schedule the first test and run the scheduler."""
args = get_arguments(args)
logging.StreamHandler(stream=sys.stdout)
config = toml.load(args.conf)
logging.basicConfig(level=args.log_level)
msg_file = args.message
# For local testing: In case you want to run a specific message
if msg_file:
# Either from a file
with open(msg_file, "r") as stream:
msg = json.load(stream)
commit_msg = CommitV1(body=msg["body"], headers=msg["headers"], topic=msg["topic"])
else:
# Or edit this dict to match your needs
body = {"commit": {"branch": "rawhide", "stats": {"files": {}}}, "repo": "repo"}
commit_msg = CommitV1(body=body)
logging.StreamHandler(stream=sys.stdout)
KojiBlockRetired().process(
config=config.get("consumer_config", {}).get("koji_block_retired", {}),
message=commit_msg,
)
if __name__ == "__main__": # pragma: no cover
try:
main(sys.argv[1:])
except KeyboardInterrupt:
pass