def test_consumer_mark_event_as_failed_on_exception( self, global_consumer, handle): """ Tests that Consumer.consume marks the DB Event as failed in case there is an error in a handler. """ consumer = self.create_consumer() global_consumer.return_value = consumer @fail_event_on_handler_exception def mocked_handle(cls, msg): event = Event.get_or_create(db.session, "msg_id", "msg_id", 0) ArtifactBuild.create(db.session, event, "foo", 0) db.session.commit() cls.set_context(event) raise ValueError("Expected exception") handle.side_effect = mocked_handle msg = self._compose_state_change_msg() consumer.consume(msg) db_event = Event.get(db.session, "msg_id") for build in db_event.builds: self.assertEqual(build.state, ArtifactBuildState.FAILED.value) self.assertTrue(build.state_reason, "Failed with traceback")
def test_handle_isnt_allowed_by_internal_policy(self): event = BotasErrataShippedEvent("test_msg_id", self.botas_advisory) self.handler.handle(event) db_event = Event.get(db.session, message_id='test_msg_id') self.assertEqual(db_event.state, EventState.SKIPPED.value) self.assertTrue(db_event.state_reason.startswith( "This image rebuild is not allowed by internal policy."))
def test_handle_manual_isnt_allowed_by_internal_policy(self): event = ManualBundleRebuild("test_msg_id", [], []) self.handler.handle(event) db_event = Event.get(db.session, message_id='test_msg_id') self.assertEqual(db_event.state, EventState.SKIPPED.value) self.assertTrue( db_event.state_reason.startswith( "This image rebuild is not allowed by internal policy."))
def test_handle_no_digests_error(self): event = BotasErrataShippedEvent("test_msg_id", self.botas_advisory) self.pyxis().get_digests_by_nvrs.return_value = set() self.botas_advisory._builds = {} self.handler.handle(event) db_event = Event.get(db.session, message_id='test_msg_id') self.assertEqual(db_event.state, EventState.SKIPPED.value) self.assertTrue( db_event.state_reason.startswith("There are no digests for NVRs:"))
def test_handle_no_digests_error(self): event = BotasErrataShippedEvent("test_msg_id", self.botas_advisory) self.pyxis().get_manifest_list_digest_by_nvr.return_value = None self.botas_advisory._builds = {} self.handler.handle(event) db_event = Event.get(db.session, message_id='test_msg_id') self.assertEqual(db_event.state, EventState.SKIPPED.value) self.assertTrue( db_event.state_reason.startswith( "None of the original images have digest"))
def test_failed_to_build_images_never_built_before(self): """ This test checks that trying to build an image that was never built before (for that branch) will make the build fail. """ self.mock_find_images_to_rebuild.return_value = [self.image_a] self.mock_find_images_trees_to_rebuild = self.patcher.patch( 'find_images_trees_to_rebuild', return_value=[[self.image_a]]) event = FreshmakerAsyncManualBuildEvent( 'msg-id-123', 'another-branch', ['image-a-container']) handler = RebuildImagesOnAsyncManualBuild() handler.handle(event) db_event = Event.get(db.session, 'msg-id-123') self.assertEqual(EventState.FAILED.value, db_event.state)
def test_building_single_image(self): """ This tests the successful build of a single image """ self.mock_find_images_to_rebuild.return_value = [self.image_a] self.mock_find_images_trees_to_rebuild = self.patcher.patch( 'find_images_trees_to_rebuild', return_value=[[self.image_a]]) event = FreshmakerAsyncManualBuildEvent( 'msg-id-123', 'test_branch', ['image-a-container']) handler = RebuildImagesOnAsyncManualBuild() handler.handle(event) db_event = Event.get(db.session, 'msg-id-123') self.assertEqual(EventState.BUILDING.value, db_event.state) self.mock_get_image_builds_in_first_batch.assert_called_once_with(db.session) self.assertEqual(len(db_event.builds.all()), 1) self.mock_start_to_build_images.assert_called_once()
def test_multiple_nvrs_for_the_same_name(self): """ This test checks that when for one name more nvrs are returned by lightblue, Freshmaker will pick the one with higher nvr. """ self.mock_find_images_to_rebuild.return_value = [self.image_a, self.image_c] self.mock_find_images_trees_to_rebuild = self.patcher.patch( 'find_images_trees_to_rebuild', return_value=[[self.image_c]]) event = FreshmakerAsyncManualBuildEvent( 'msg-id-123', 'test_branch', ['image-a-container']) handler = RebuildImagesOnAsyncManualBuild() handler.handle(event) db_event = Event.get(db.session, 'msg-id-123') self.assertEqual(EventState.BUILDING.value, db_event.state) self.mock_get_image_builds_in_first_batch.assert_called_once_with(db.session) self.mock_start_to_build_images.assert_called_once() self.assertEqual(len(db_event.builds.all()), 1) self.assertEqual(db_event.builds.one().original_nvr, 'image-a-container-1.0-3')
def test_handle_get_bundle_paths(self): event = BotasErrataShippedEvent("test_msg_id", self.botas_advisory) self.pyxis().get_digests_by_nvrs.return_value = {'nvr1'} bundles = [ { "bundle_path": "some_path", "bundle_path_digest": "sha256:123123", "channel_name": "streams-1.5.x", "related_images": [ { "image": "registry/amq7/amq-streams-r-operator@sha256:111", "name": "strimzi-cluster-operator", "digest": "sha256:111" }, ], "version": "1.5.3" }, { "bundle_path": "some_path_2", "channel_name": "streams-1.5.x", "related_images": [ { "image": "registry/amq7/amq-streams-r-operator@sha256:555", "name": "strimzi-cluster-operator", "digest": "sha256:555" }, ], "version": "1.5.4" }, ] self.pyxis().filter_bundles_by_related_image_digests.return_value = bundles self.botas_advisory._builds = {} self.handler.handle(event) db_event = Event.get(db.session, message_id='test_msg_id') # should be called only with the first digest, because second one # doesn't have 'bundle_path_digest' self.pyxis().get_images_by_digests.assert_called_once_with({"sha256:123123"}) self.assertEqual(db_event.state, EventState.SKIPPED.value) self.assertTrue( db_event.state_reason.startswith("Skipping the rebuild of"))
def test_parent_if_image_without_parent(self): """ This tests if we get parent as brew build of single image to rebuild when image doesn't have "parent" key """ self.mock_find_images_to_rebuild.return_value = [self.image_f] event = FreshmakerAsyncManualBuildEvent( 'msg-id-123', 'test_branch', ['image-a-container']) find_parent_mock = MagicMock() find_parent_mock.find_parent_brew_build_nvr_from_child.return_value = 'ubi8-container-8.2-299' self.mock_lightblue.return_value = find_parent_mock RebuildImagesOnAsyncManualBuild().handle(event) db_event = Event.get(db.session, 'msg-id-123') # Check if build in DB corresponds to parent of the image build = db_event.builds.first().json() self.assertEqual(build['build_args'].get('original_parent', 0), 'ubi8-container-8.2-299') # check if we are calling Lightblue to get proper parent of image find_parent_mock.find_parent_brew_build_nvr_from_child.assert_called_once_with(self.image_f)
def test_building_sibilings(self): """ This test checks that when the users requests to rebuild 2 images that are sibilings (or other unrelated images) Freshmaker will rebuild them separately, without the need of rebuilding the parent. """ self.mock_find_images_to_rebuild.return_value = [self.image_b, self.image_d] self.find_images_trees_to_rebuild = self.patcher.patch( 'find_images_trees_to_rebuild', return_value=[ [self.image_b, self.image_a, self.image_0], [self.image_d, self.image_a, self.image_0]]) event = FreshmakerAsyncManualBuildEvent( 'msg-id-123', 'test_branch', ['image-b-container', 'image-d-container']) handler = RebuildImagesOnAsyncManualBuild() handler.handle(event) db_event = Event.get(db.session, 'msg-id-123') self.assertEqual(EventState.BUILDING.value, db_event.state) self.mock_get_image_builds_in_first_batch.assert_called_once_with(db.session) self.assertEqual(len(db_event.builds.all()), 2) self.mock_start_to_build_images.assert_called_once()
def test_related_images_are_built(self): self.mock_find_images_to_rebuild.return_value = [self.image_b, self.image_d, self.image_a] self.find_images_trees_to_rebuild = self.patcher.patch( 'find_images_trees_to_rebuild', return_value=[ [self.image_a, self.image_0], [self.image_b, self.image_a, self.image_0], [self.image_d, self.image_a, self.image_0]]) self.mock_generate_batches = self.patcher.patch('generate_batches', return_value=[ [self.image_a], [self.image_b, self.image_d] ]) event = FreshmakerAsyncManualBuildEvent( 'msg-id-123', 'test_branch', ['image-a-container', 'image-b-container', 'image-d-container']) handler = RebuildImagesOnAsyncManualBuild() handler.handle(event) db_event = Event.get(db.session, 'msg-id-123') self.assertEqual(EventState.BUILDING.value, db_event.state) self.mock_get_image_builds_in_first_batch.assert_called_once_with(db.session) self.assertEqual(len(db_event.builds.all()), 3) self.mock_start_to_build_images.assert_called_once()
def test_parent_if_image_with_parent(self): """ This tests if we get parent of single image to rebuild, when image has "parent" key as None OR as some image """ for index, image in enumerate([self.image_0, self.image_a], 1): self.mock_find_images_to_rebuild.return_value = [image] event_id = f'msg-id-{index}' event = FreshmakerAsyncManualBuildEvent( event_id, 'test_branch', ['image-a-container']) RebuildImagesOnAsyncManualBuild().handle(event) db_event = Event.get(db.session, event_id) if image['parent'] is not None: original_parent = image['parent']['brew']['build'] else: original_parent = None # Check if build in DB corresponds to parent of the image build = db_event.builds.first().json() self.assertEqual(build['build_args'].get('original_parent', 0), original_parent)
def test_multiple_bundles_to_single_related_image(self, mock_koji, get_published): event = BotasErrataShippedEvent("test_msg_id", self.botas_advisory) self.botas_advisory._builds = { "product_name": { "builds": [{ "foo-1-2.123": { "nvr": "foo-1-2.123" } }, { "bar-2-2.134": { "nvr": "bar-2-2.134" } }] } } published_nvrs = {"foo-1-2.123": "foo-1-2", "bar-2-2.134": "bar-2-2"} get_published.side_effect = lambda x: published_nvrs[x] digests_by_nvrs = { "foo-1-2": "sha256:111", "bar-2-2": "sha256:222", "foo-1-2.123": "sha256:333", "bar-2-2.134": "sha256:444", } def gmldbn(nvr, must_be_published=True): return digests_by_nvrs[nvr] self.pyxis().get_manifest_list_digest_by_nvr.side_effect = gmldbn bundles_by_related_digest = { "sha256:111": [ { "bundle_path": "bundle-a/path", "bundle_path_digest": "sha256:123123", "channel_name": "streams-1.5.x", "csv_name": "amq-streams.1.5.3", "related_images": [ { "image": "foo@sha256:111", "name": "foo", "digest": "sha256:111" }, ], "version": "1.5.3" }, { "bundle_path": "bundle-b/path", "bundle_path_digest": "sha256:023023", "channel_name": "4.5", "csv_name": "amq-streams.2.4.2", "related_images": [ { "image": "foo@sha256:111", "name": "foo", "digest": "sha256:111" }, ], "version": "2.4.2" }, ], "sha256:222": [] } self.pyxis().get_bundles_by_related_image_digest.side_effect = \ lambda x, _: bundles_by_related_digest[x] bundle_images = { "sha256:123123": [{ "brew": { "build": "foo-a-bundle-2.1-2", "nvra": "foo-a-bundle-2.1-2.amd64", "package": "foo-a-bundle", }, "repositories": [{ "content_advisory_ids": [], "manifest_list_digest": "sha256:12322", "manifest_schema2_digest": "sha256:123123", "published": True, "registry": "registry.example.com", "repository": "foo/foo-a-operator-bundle", "tags": [{ "name": "2" }, { "name": "2.1" }], }], }], "sha256:023023": [{ "brew": { "build": "foo-b-bundle-3.1-2", "nvra": "foo-b-bundle-3.1-2.amd64", "package": "foo-b-bundle", }, "repositories": [{ "content_advisory_ids": [], "manifest_list_digest": "sha256:12345", "manifest_schema2_digest": "sha256:023023", "published": True, "registry": "registry.example.com", "repository": "foo/foo-b-operator-bundle", "tags": [{ "name": "3" }, { "name": "3.1" }], }], }] } self.pyxis( ).get_images_by_digest.side_effect = lambda x: bundle_images[x] def _fake_get_auto_rebuild_tags(registry, repository): if repository == "foo/foo-a-operator-bundle": return ["2", "latest"] if repository == "foo/foo-b-operator-bundle": return ["3", "latest"] self.pyxis( ).get_auto_rebuild_tags.side_effect = _fake_get_auto_rebuild_tags koji_builds = { "foo-a-bundle-2.1-2": { "build_id": 123, "extra": { "image": { "operator_manifests": { "related_images": { "created_by_osbs": True, "pullspecs": [{ "new": "registry.example.com/foo/foo-container@sha256:111", "original": "registry.exampl.com/foo/foo-container:0.1", "pinned": True, }], } }, } }, "name": "foo-a-bundle", "nvr": "foo-a-bundle-2.1-2", }, "foo-b-bundle-3.1-2": { "build_id": 234, "extra": { "image": { "operator_manifests": { "related_images": { "created_by_osbs": True, "pullspecs": [{ "new": "registry.example.com/foo/foo-container@sha256:111", "original": "registry.exampl.com/foo/foo-container:0.1", "pinned": True, }], } }, } }, "name": "foo-b-bundle", "nvr": "foo-b-bundle-3.1-2", } } mock_koji.return_value.get_build.side_effect = lambda x: koji_builds[x] self.handler._prepare_builds = MagicMock() self.handler.start_to_build_images = MagicMock() self.handler.handle(event) db_event = Event.get(db.session, message_id='test_msg_id') self.pyxis().get_images_by_digest.assert_has_calls( [call("sha256:123123"), call("sha256:023023")], any_order=True) self.assertEqual(db_event.state, EventState.BUILDING.value)