From: Dario Faccin Date: Mon, 3 Jul 2023 07:18:04 +0000 (+0200) Subject: OSMENG-1096 OSMENG-1097: Delete application and check application is X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=83789290dc8c31202db2ea17d7bb10af85fba54d;p=osm%2FLCM.git OSMENG-1096 OSMENG-1097: Delete application and check application is removed Change-Id: I76b1c08e987038b0bf48cafc7a258e05d8129553 Signed-off-by: Dario Faccin --- diff --git a/osm_lcm/nglcm.py b/osm_lcm/nglcm.py index 38015bf..926fe79 100644 --- a/osm_lcm/nglcm.py +++ b/osm_lcm/nglcm.py @@ -36,6 +36,8 @@ from osm_lcm.temporal.juju_paas_activities import ( CheckCharmStatusImpl, DeployCharmImpl, TestVimConnectivityImpl, + RemoveCharmImpl, + CheckCharmIsRemovedImpl, ) from osm_lcm.temporal.lcm_activities import NsLcmNoOpImpl, UpdateNsLcmOperationStateImpl @@ -184,6 +186,8 @@ class NGLcm: DeployCharmImpl(paas_connector_instance), CheckCharmStatusImpl(paas_connector_instance), TestVimConnectivityImpl(paas_connector_instance), + RemoveCharmImpl(paas_connector_instance), + CheckCharmIsRemovedImpl(paas_connector_instance), UpdateVimOperationStateImpl(self.db), UpdateVimStateImpl(self.db), DeleteVimRecordImpl(self.db), diff --git a/osm_lcm/temporal/juju_paas_activities.py b/osm_lcm/temporal/juju_paas_activities.py index 8b51f24..cb96fd9 100644 --- a/osm_lcm/temporal/juju_paas_activities.py +++ b/osm_lcm/temporal/juju_paas_activities.py @@ -24,8 +24,10 @@ from n2vc.config import EnvironConfig from osm_common.temporal.activities.paas import ( TestVimConnectivity, CheckCharmStatus, + CheckCharmIsRemoved, CreateModel, DeployCharm, + RemoveCharm, ) from osm_common.temporal.dataclasses_common import ( CharmInfo, @@ -266,3 +268,38 @@ class CharmInfoUtils: if filtered_image: return filtered_image.get("image"), filtered_image.get("version") return None, None + + +class RemoveCharmImpl(RemoveCharm): + @activity.defn(name=RemoveCharm.__name__) + async def __call__(self, activity_input: RemoveCharm.Input) -> None: + app_name = activity_input.application_name + model_name = activity_input.model_name + force_remove = activity_input.force_remove + controller: Controller = await self.juju_controller._get_controller( + activity_input.vim_uuid + ) + model = await controller.get_model(model_name) + if app_name not in model.applications: + return + await model.remove_application( + app_name=app_name, + block_until_done=False, + force=force_remove, + no_wait=force_remove, + ) + + +class CheckCharmIsRemovedImpl(CheckCharmIsRemoved): + @activity.defn(name=CheckCharmIsRemoved.__name__) + async def __call__(self, activity_input: CheckCharmIsRemoved.Input) -> None: + controller = await self.juju_controller._get_controller(activity_input.vim_uuid) + model = await controller.get_model(activity_input.model_name) + app_name = activity_input.application_name + + removed = False + while not removed: + activity.heartbeat() + await asyncio.sleep(activity_input.poll_interval) + if app_name not in model.applications: + removed = True diff --git a/osm_lcm/tests/test_juju_paas_activities.py b/osm_lcm/tests/test_juju_paas_activities.py index bac5619..0f24e82 100644 --- a/osm_lcm/tests/test_juju_paas_activities.py +++ b/osm_lcm/tests/test_juju_paas_activities.py @@ -30,6 +30,8 @@ from osm_lcm.temporal.juju_paas_activities import ( DeployCharmImpl, CreateModelImpl, CheckCharmStatusImpl, + CheckCharmIsRemovedImpl, + RemoveCharmImpl, ) from osm_common.temporal.workflows.vdu import VduInstantiateWorkflow from osm_common.temporal.dataclasses_common import CharmInfo, VduComputeConstraints @@ -447,3 +449,100 @@ class TestTestVimConnectivity(TestJujuPaasActivitiesBase): self.test_vim_connectivity, self.test_vim_connectivity_input, ) + + +class TestRemoveCharm(TestJujuPaasActivitiesBase): + app_name = "my_app_name" + + def setUp(self) -> None: + super().setUp() + self.remove_charm = RemoveCharmImpl(self.juju_paas) + + @parameterized.expand([False, True]) + async def test_remove_charm__application_exists__is_forced(self, is_forced): + remove_charm_input = RemoveCharmImpl.Input( + vim_uuid=vim_content["_id"], + application_name=self.app_name, + model_name=namespace, + force_remove=is_forced, + ) + self.add_application(self.app_name) + await self.env.run( + self.remove_charm, + remove_charm_input, + ) + self.model.remove_application.assert_called_once_with( + app_name=self.app_name, + block_until_done=False, + force=is_forced, + no_wait=is_forced, + ) + + async def test_remove_charm__app_does_not_exist(self): + remove_charm_input = RemoveCharmImpl.Input( + vim_uuid=vim_content["_id"], + application_name=self.app_name, + model_name=namespace, + force_remove=False, + ) + await self.env.run( + self.remove_charm, + remove_charm_input, + ) + self.model.remove_application.assert_not_called() + + async def test_remove_charm__juju_error_occurred__app_is_not_removed(self): + remove_charm_input = RemoveCharmImpl.Input( + vim_uuid=vim_content["_id"], + application_name=self.app_name, + model_name=namespace, + force_remove=False, + ) + self.add_application(self.app_name) + self.controller.get_model.side_effect = JujuError() + with self.assertRaises(JujuError): + await self.env.run( + self.remove_charm, + remove_charm_input, + ) + self.model.remove_application.assert_not_called() + + +class TestCheckCharmIsRemoved(TestJujuPaasActivitiesBase): + def setUp(self) -> None: + super().setUp() + self.env.on_heartbeat = self.on_heartbeat + self.heartbeat_count = 0 + self.heartbeat_maximum = 5 + self.add_application("application") + self.check_charm_is_removed = CheckCharmIsRemovedImpl(self.juju_paas) + + def on_heartbeat(self, *args, **kwargs): + self.heartbeat_count += 1 + if self.heartbeat_count > self.heartbeat_maximum: + self.env.cancel() + + async def test_check_is_charm_removed__nominal_case(self): + arg = CheckCharmIsRemovedImpl.Input( + application_name=self.application_name, + model_name="model", + vim_uuid="vim-uuid", + poll_interval=0, + ) + + type(self.model).applications = mock.PropertyMock( + side_effect=[self.model.applications, {}] + ) + + await self.env.run(self.check_charm_is_removed.__call__, arg) + + async def test_check_is_charm_removed__cancel(self): + arg = CheckCharmIsRemovedImpl.Input( + application_name=self.application_name, + model_name="model", + vim_uuid="vim-uuid", + poll_interval=0, + ) + + with self.assertRaises(asyncio.exceptions.CancelledError): + await self.env.run(self.check_charm_is_removed.__call__, arg)