From d8055506c855ff95a918a30c5dd2d2672bbf407c Mon Sep 17 00:00:00 2001 From: Jan Kaluža Date: Feb 06 2019 06:49:44 +0000 Subject: Merge #360 `Add `internal.UpdateDBOnODCSComposeFail` handler to mark ArtifactBuilds as failed in case of ODCS compose failure.` --- diff --git a/freshmaker/handlers/internal/__init__.py b/freshmaker/handlers/internal/__init__.py index e814291..17571d6 100644 --- a/freshmaker/handlers/internal/__init__.py +++ b/freshmaker/handlers/internal/__init__.py @@ -22,3 +22,4 @@ from .update_db_on_advisory_change import UpdateDBOnAdvisoryChange # noqa from .update_db_on_module_build import UpdateDBOnModuleBuild # noqa from .generate_advisory_signed_event_on_rpm_sign import GenerateAdvisorySignedEventOnRPMSign # noqa +from .update_db_on_odcs_compose_fail import UpdateDBOnODCSComposeFail # noqa diff --git a/freshmaker/handlers/internal/update_db_on_odcs_compose_fail.py b/freshmaker/handlers/internal/update_db_on_odcs_compose_fail.py new file mode 100644 index 0000000..1942679 --- /dev/null +++ b/freshmaker/handlers/internal/update_db_on_odcs_compose_fail.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017 Red Hat, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from freshmaker import db +from freshmaker.events import ODCSComposeStateChangeEvent +from freshmaker.models import ArtifactBuild, ArtifactBuildCompose, Compose +from freshmaker.handlers import BaseHandler, fail_event_on_handler_exception +from freshmaker.types import ArtifactBuildState +from odcs.common.types import COMPOSE_STATES + + +class UpdateDBOnODCSComposeFail(BaseHandler): + """ + Marks the ArtifactBuild as FAILED in case the ODCS compose on which the + ArtifactBuild depends is moved to "failed" state. + """ + + name = "UpdateDBOnODCSComposeFail" + order = 0 + + def can_handle(self, event): + if not isinstance(event, ODCSComposeStateChangeEvent): + return False + return event.compose["state"] == COMPOSE_STATES["failed"] + + @fail_event_on_handler_exception + def handle(self, event): + # Get all the builds waiting for this compose. + builds_with_compose = db.session.query(ArtifactBuild).join( + ArtifactBuildCompose).join(Compose) + builds_with_compose = builds_with_compose.filter( + Compose.odcs_compose_id == event.compose["id"], + ArtifactBuildCompose.compose_id == Compose.id) + + for build in builds_with_compose: + build.transition( + ArtifactBuildState.FAILED.value, + "ODCS compose %r is in failed state." % event.compose["id"]) diff --git a/freshmaker/models.py b/freshmaker/models.py index 6bfad66..17b730e 100644 --- a/freshmaker/models.py +++ b/freshmaker/models.py @@ -572,6 +572,7 @@ class ArtifactBuild(FreshmakerBase): "build_id": self.build_id, "url": build_url, "build_args": build_args, + "odcs_composes": [rel.compose.odcs_compose_id for rel in self.composes], } def get_root_dep_on(self): diff --git a/tests/handlers/internal/test_update_db_on_odcs_compose_fail.py b/tests/handlers/internal/test_update_db_on_odcs_compose_fail.py new file mode 100644 index 0000000..1da39cd --- /dev/null +++ b/tests/handlers/internal/test_update_db_on_odcs_compose_fail.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019 Red Hat, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Written by Jan Kaluza + +from freshmaker import db +from freshmaker.models import ( + Event, EventState, EVENT_TYPES, + ArtifactBuild, ArtifactType, ArtifactBuildState, ArtifactBuildCompose, + Compose +) +from freshmaker.events import ErrataAdvisoryRPMsSignedEvent +from freshmaker.handlers.internal import UpdateDBOnODCSComposeFail +from freshmaker.events import ODCSComposeStateChangeEvent +from tests import helpers +from odcs.common.types import COMPOSE_STATES + + +class TestUpdateDBOnODCSComposeFail(helpers.ModelsTestCase): + + def setUp(self): + super(TestUpdateDBOnODCSComposeFail, self).setUp() + self.db_event = self._create_test_event( + "msg-1", "search-key-1", "build-1", 1) + # Create another DB event, build and compose just to have more data + # in database. + self.db_event_2 = self._create_test_event( + "msg-2", "search-key-2", "another-build-1", 2) + + def _create_test_event(self, event_id, search_key, build_name, compose_id): + db_event = Event.create( + db.session, event_id, search_key, + EVENT_TYPES[ErrataAdvisoryRPMsSignedEvent], + state=EventState.INITIALIZED, + released=False) + build_1 = ArtifactBuild.create( + db.session, db_event, build_name, ArtifactType.IMAGE, + state=ArtifactBuildState.PLANNED) + compose_1 = Compose(odcs_compose_id=compose_id) + db.session.add(compose_1) + db.session.commit() + db.session.add(ArtifactBuildCompose( + build_id=build_1.id, compose_id=compose_1.id)) + db.session.commit() + return db_event + + def test_cannot_handle_if_compose_is_not_failed(self): + event = ODCSComposeStateChangeEvent( + 'msg-id', {'id': 1, 'state': COMPOSE_STATES["done"]} + ) + handler = UpdateDBOnODCSComposeFail() + can_handle = handler.can_handle(event) + self.assertFalse(can_handle) + + def test_can_handle(self): + event = ODCSComposeStateChangeEvent( + 'msg-id', {'id': 1, 'state': COMPOSE_STATES["failed"]} + ) + handler = UpdateDBOnODCSComposeFail() + can_handle = handler.can_handle(event) + self.assertTrue(can_handle) + + def test_handle_mark_build_as_failed(self): + event = ODCSComposeStateChangeEvent( + 'msg-id', {'id': 1, 'state': COMPOSE_STATES["failed"]} + ) + handler = UpdateDBOnODCSComposeFail() + handler.handle(event) + + db.session.refresh(self.db_event) + build = self.db_event.builds[0] + self.assertEqual(build.state, ArtifactBuildState.FAILED.value) + self.assertEqual(build.state_reason, + "ODCS compose 1 is in failed state.") + + db.session.refresh(self.db_event_2) + build = self.db_event_2.builds[0] + self.assertEqual(build.state, ArtifactBuildState.PLANNED.value)