diff --git a/module_build_service/scheduler/producer.py b/module_build_service/scheduler/producer.py index 0aa68bf3..6df26cc5 100644 --- a/module_build_service/scheduler/producer.py +++ b/module_build_service/scheduler/producer.py @@ -52,6 +52,7 @@ class MBSProducer(PollingProducer): self.trigger_new_repo_when_stalled(conf, session) self.delete_old_koji_targets(conf, session) self.cleanup_stale_failed_builds(conf, session) + self.sync_koji_build_tags(conf, session) except Exception: msg = 'Error in poller execution:' log.exception(msg) @@ -371,3 +372,54 @@ class MBSProducer(PollingProducer): ) build.transition(config, state=models.BUILD_STATES["failed"], state_reason=state_reason) session.commit() + + def sync_koji_build_tags(self, config, session): + """ + Method checking the "tagged" and "tagged_in_final" attributes of + "complete" ComponentBuilds in the current batch of module builds + in "building" state against the Koji. + + In case the Koji shows the build as tagged/tagged_in_final, + fake "tagged" message is added to work queue. + """ + if conf.system != 'koji': + return + + koji_session = KojiModuleBuilder.get_session(conf, login=False) + + module_builds = models.ModuleBuild.by_state(session, "build") + for module_build in module_builds: + complete_components = module_build.current_batch( + koji.BUILD_STATES['COMPLETE']) + for c in complete_components: + # In case the component is tagged in the build tag and + # also tagged in the final tag (or it is build_time_only + # and therefore should not be tagged in final tag), skip it. + if c.tagged and (c.tagged_in_final or c.build_time_only): + continue + + log.info("%r: Component %r is complete, but not tagged in the " + "final and/or build tags.", module_build, c) + + # Check in which tags the component is tagged. + tag_dicts = koji_session.listTags(c.nvr) + tags = [tag_dict["name"] for tag_dict in tag_dicts] + + # If it is tagged in final tag, but MBS does not think so, + # schedule fake message. + if not c.tagged_in_final and module_build.koji_tag in tags: + msg = module_build_service.messaging.KojiTagChange( + 'sync_koji_build_tags_fake_message', + module_build.koji_tag, c.package, c.nvr) + log.info(" Scheduling faked event %r" % msg) + module_build_service.scheduler.consumer.work_queue_put(msg) + + # If it is tagged in the build tag, but MBS does not think so, + # schedule fake message. + build_tag = module_build.koji_tag + "-build" + if not c.tagged and build_tag in tags: + msg = module_build_service.messaging.KojiTagChange( + 'sync_koji_build_tags_fake_message', + build_tag, c.package, c.nvr) + log.info(" Scheduling faked event %r" % msg) + module_build_service.scheduler.consumer.work_queue_put(msg) diff --git a/tests/test_scheduler/test_poller.py b/tests/test_scheduler/test_poller.py index eb815cf9..e4031b34 100644 --- a/tests/test_scheduler/test_poller.py +++ b/tests/test_scheduler/test_poller.py @@ -25,6 +25,7 @@ from tests import reuse_component_init_data, db, clean_database import mock import koji from module_build_service.scheduler.producer import MBSProducer +from module_build_service.messaging import KojiTagChange import six.moves.queue as queue from datetime import datetime, timedelta @@ -567,3 +568,65 @@ class TestPoller: module = models.ModuleBuild.query.filter_by(state=4).all() assert len(module) == 1 assert module[0].id == 2 + + @pytest.mark.parametrize('tagged, tagged_in_final', ([True, False], [True, False])) + @patch("module_build_service.builder.KojiModuleBuilder.KojiClientSession") + def test_sync_koji_build_tags( + self, ClientSession, create_builder, global_consumer, dbg, + tagged, tagged_in_final): + module_build_2 = models.ModuleBuild.query.filter_by(id=2).one() + + # Only module build 1's build target should be deleted. + module_build_2.koji_tag = 'module-tag1' + module_build_2.state = models.BUILD_STATES['build'] + c = module_build_2.current_batch()[0] + c.state = koji.BUILD_STATES["COMPLETE"] + c.tagged_in_final = False + c.tagged = False + db.session.commit() + db.session.refresh(module_build_2) + + koji_session = ClientSession.return_value + # No created module build has any of these tags. + ret = [] + + if tagged: + ret.append( + { + 'id': 1, + 'name': module_build_2.koji_tag + "-build" + }, + ) + if tagged_in_final: + ret.append( + { + 'id': 2, + 'name': module_build_2.koji_tag + }, + ) + koji_session.listTags.return_value = ret + + consumer = mock.MagicMock() + consumer.incoming = queue.Queue() + global_consumer.return_value = consumer + hub = mock.MagicMock() + poller = MBSProducer(hub) + + assert consumer.incoming.qsize() == 0 + poller.sync_koji_build_tags(conf, db.session) + assert consumer.incoming.qsize() == 2 - len(ret) + + expected_msg_tags = [] + if tagged: + expected_msg_tags.append(module_build_2.koji_tag + "-build") + if tagged_in_final: + expected_msg_tags.append(module_build_2.koji_tag) + + assert len(expected_msg_tags) == consumer.incoming.qsize() + + for i in range(consumer.incoming.qsize()): + msg = consumer.incoming.get() + assert isinstance(msg, KojiTagChange) + assert msg.artifact == c.package + assert msg.nvr == c.nvr + assert msg.tag in expected_msg_tags