CheckCharmStatusImpl,
DeployCharmImpl,
TestVimConnectivityImpl,
+ RemoveCharmImpl,
+ CheckCharmIsRemovedImpl,
)
from osm_lcm.temporal.lcm_activities import NsLcmNoOpImpl, UpdateNsLcmOperationStateImpl
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),
from osm_common.temporal.activities.paas import (
TestVimConnectivity,
CheckCharmStatus,
+ CheckCharmIsRemoved,
CreateModel,
DeployCharm,
+ RemoveCharm,
)
from osm_common.temporal.dataclasses_common import (
CharmInfo,
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
DeployCharmImpl,
CreateModelImpl,
CheckCharmStatusImpl,
+ CheckCharmIsRemovedImpl,
+ RemoveCharmImpl,
)
from osm_common.temporal.workflows.vdu import VduInstantiateWorkflow
from osm_common.temporal.dataclasses_common import CharmInfo, VduComputeConstraints
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)