From 86c301dcda11350162059a5b8aa70609b9be1a3b Mon Sep 17 00:00:00 2001 From: uruwhy <58484522+uruwhy@users.noreply.github.com> Date: Thu, 9 Oct 2025 19:24:01 +0000 Subject: [PATCH 1/7] set finished and cleanup state properly for operations --- app/objects/c_operation.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index 38d8688da..a76c127ad 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -266,7 +266,6 @@ async def wait_for_links_completion(self, link_ids): async def is_closeable(self): if await self.is_finished() or self.auto_close: - self.state = self.states['FINISHED'] return True return False @@ -427,12 +426,18 @@ async def _cleanup_operation(self, services): self.add_link(link) cleanup_count += 1 if cleanup_count: + self.state = self.states['CLEANUP'] + logging.debug('Starting operation cleanup') await self._safely_handle_cleanup(cleanup_count) + logging.debug('Completed operation cleanup') + else: + self.state = self.states['FINISHED'] async def _safely_handle_cleanup(self, cleanup_link_count): try: await asyncio.wait_for(self.wait_for_completion(), timeout=self.base_timeout + self.link_timeout * cleanup_link_count) + self.state = self.states['FINISHED'] except asyncio.TimeoutError: logging.warning(f"[OPERATION] - unable to close {self.name} cleanly due to timeout. Forcibly terminating.") self.state = self.states['OUT_OF_TIME'] From f99dbff9f437beced64364c38375fcf8ff1c2e6f Mon Sep 17 00:00:00 2001 From: Daniel Matthews <58484522+uruwhy@users.noreply.github.com> Date: Fri, 10 Oct 2025 08:14:57 -0400 Subject: [PATCH 2/7] remove unnecessary lines --- app/objects/c_operation.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index a76c127ad..90ee63e88 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -430,14 +430,11 @@ async def _cleanup_operation(self, services): logging.debug('Starting operation cleanup') await self._safely_handle_cleanup(cleanup_count) logging.debug('Completed operation cleanup') - else: - self.state = self.states['FINISHED'] async def _safely_handle_cleanup(self, cleanup_link_count): try: await asyncio.wait_for(self.wait_for_completion(), timeout=self.base_timeout + self.link_timeout * cleanup_link_count) - self.state = self.states['FINISHED'] except asyncio.TimeoutError: logging.warning(f"[OPERATION] - unable to close {self.name} cleanly due to timeout. Forcibly terminating.") self.state = self.states['OUT_OF_TIME'] From 0cac55761dca2751d65191e0660a03d87826786d Mon Sep 17 00:00:00 2001 From: Daniel Matthews <58484522+uruwhy@users.noreply.github.com> Date: Fri, 10 Oct 2025 08:16:19 -0400 Subject: [PATCH 3/7] include operation ID in cleanup log msg --- app/objects/c_operation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index 90ee63e88..608a009f6 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -427,9 +427,9 @@ async def _cleanup_operation(self, services): cleanup_count += 1 if cleanup_count: self.state = self.states['CLEANUP'] - logging.debug('Starting operation cleanup') + logging.debug(f'Starting cleanup for operation {self.id}') await self._safely_handle_cleanup(cleanup_count) - logging.debug('Completed operation cleanup') + logging.debug(f'Completed cleanup for operation {self.id}') async def _safely_handle_cleanup(self, cleanup_link_count): try: From b8afcb879234bd060e0b50ea81e3ae48236dca09 Mon Sep 17 00:00:00 2001 From: Daniel Matthews <58484522+uruwhy@users.noreply.github.com> Date: Fri, 10 Oct 2025 08:17:24 -0400 Subject: [PATCH 4/7] simplify condition --- app/objects/c_operation.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/objects/c_operation.py b/app/objects/c_operation.py index 608a009f6..4c7ea94fd 100644 --- a/app/objects/c_operation.py +++ b/app/objects/c_operation.py @@ -265,9 +265,7 @@ async def wait_for_links_completion(self, link_ids): break async def is_closeable(self): - if await self.is_finished() or self.auto_close: - return True - return False + return await self.is_finished() or self.auto_close async def is_finished(self): if self.state in [self.states['FINISHED'], self.states['OUT_OF_TIME'], self.states['CLEANUP']] \ From b433650602a72793b75bf245418832e35d613476 Mon Sep 17 00:00:00 2001 From: uruwhy <58484522+uruwhy@users.noreply.github.com> Date: Fri, 10 Oct 2025 19:40:12 +0000 Subject: [PATCH 5/7] add unit tests for cleanup status --- tests/objects/test_operation.py | 40 +++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/tests/objects/test_operation.py b/tests/objects/test_operation.py index 357acc62b..13400c18a 100644 --- a/tests/objects/test_operation.py +++ b/tests/objects/test_operation.py @@ -10,6 +10,7 @@ from app.objects.c_operation import Operation from app.objects.secondclass.c_link import Link from app.service.interfaces.i_event_svc import EventServiceInterface +from app.service.interfaces.i_planning_svc import PlanningServiceInterface from app.utility.base_service import BaseService from app.objects.c_source import Source from app.objects.c_planner import Planner @@ -139,6 +140,33 @@ async def fire_event(self, exchange=None, queue=None, timestamp=True, **callback BaseService.remove_service('event_svc') +@pytest.fixture +def fake_planning_svc(event_loop, make_test_link, test_agent): + class FakePlanningService(BaseService, PlanningServiceInterface): + def __init__(self): + self.fired = {} + + async def get_cleanup_links(self, operation, agent): + cleanup_link = make_test_link(135, link_paw=test_agent.paw, link_cleanup=1, link_status=0) + return [cleanup_link] + + def get_links(self, operation, buckets, agent, trim): + pass + + def generate_and_trim_links(self, agent, operation, abilities, trim): + pass + + def sort_links(self, links): + pass + + service = FakePlanningService() + service.add_service('planning_svc', service) + + yield service + + BaseService.remove_service('planning_svc') + + @pytest.fixture def test_ability(ability, executor): return ability(ability_id='123', executors=[executor(name='psh', platform='windows')]) @@ -146,9 +174,9 @@ def test_ability(ability, executor): @pytest.fixture def make_test_link(test_ability): - def _make_link(link_id, link_paw='123456', link_status=-3): + def _make_link(link_id, link_paw='123456', link_status=-3, link_cleanup=0): return Link(command='', paw=link_paw, ability=test_ability, id=link_id, executor=next(test_ability.executors), - status=link_status) + status=link_status, cleanup=link_cleanup) return _make_link @@ -597,3 +625,11 @@ async def test_add_ignored_link(self, make_test_link, operation_agent): assert op.ignored_links assert test_link.id in op.ignored_links assert len(op.ignored_links) == 1 + + async def test_operation_cleanup_status(self, fake_planning_svc, operation_agent): + services = {'planning_svc': fake_planning_svc} + op = Operation(name='test with cleanup', agents=[operation_agent], state='running') + assert op.state == 'running' + assert await op.is_closeable() + await op._cleanup_operation(services) + assert op.state == 'cleanup' From b829e89edafe934ab4e943a57483ff63938f9906 Mon Sep 17 00:00:00 2001 From: Daniel Matthews <58484522+uruwhy@users.noreply.github.com> Date: Fri, 10 Oct 2025 16:04:11 -0400 Subject: [PATCH 6/7] Update tox.ini --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 863e00242..4ccc02843 100644 --- a/tox.ini +++ b/tox.ini @@ -26,7 +26,7 @@ deps = allowlist_externals = mkdir commands = mkdir -p plugins/magma/dist/assets - coverage run -p -m pytest --tb=short --asyncio-mode=auto tests + coverage run -p -m pytest --tb=short --asyncio-mode=auto tests -vv [testenv:style] deps = pre-commit From 28968e74120ed14046d677dcd069a7ed6663c136 Mon Sep 17 00:00:00 2001 From: Daniel Matthews <58484522+uruwhy@users.noreply.github.com> Date: Tue, 14 Oct 2025 09:37:46 -0400 Subject: [PATCH 7/7] Update quality.yml --- .github/workflows/quality.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index ee8817a7f..3d5404b3a 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -58,9 +58,6 @@ jobs: TOXENV: ${{ matrix.toxenv }} run: tox - - name: Override Coverage Source Path for Sonar - run: sed -i "s#/home/runner/work/caldera/caldera#/github/workspace#g" /home/runner/work/caldera/caldera/coverage.xml - # --- Sonar scan for pushes and same-repo PRs only --- - name: SonarQube Scan if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false }}