Skip to content

Commit

Permalink
Merge pull request nolar#853 from nolar/daemon-stopper-not-set
Browse files Browse the repository at this point in the history
Stop daemons on operator exit
  • Loading branch information
nolar authored Oct 26, 2021
2 parents 229544b + 2aa35b2 commit 7d3fd65
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 8 deletions.
3 changes: 1 addition & 2 deletions kopf/_cogs/aiokits/aiotasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,7 @@ async def close(self) -> None:
Stop accepting new tasks and cancel all running/pending ones.
"""

# Ensure that all pending coros are awaited -- to prevent RuntimeWarnings/ResourceWarnings.
# But do it via the normal flow, i.e. without exceeding the limit of the scheduler (if any).
# Running tasks are cancelled here. Pending tasks are cancelled at actual spawning.
self._closed = True
for task in self._running_tasks:
task.cancel()
Expand Down
1 change: 1 addition & 0 deletions kopf/_core/engines/daemons.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ async def daemon_killer(
settings=settings,
daemon=daemon,
reason=stoppers.DaemonStoppingReason.OPERATOR_EXITING))
await scheduler.wait() # prevent insta-cancelling our own coros (daemon stoppers).
await scheduler.close()


Expand Down
10 changes: 5 additions & 5 deletions tests/handling/daemons/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import asyncio
import contextlib
import time
import unittest.mock

Expand Down Expand Up @@ -90,12 +91,11 @@ async def background_daemon_killer(settings, memories, operator_paused):
"""
task = asyncio.create_task(daemon_killer(
settings=settings, memories=memories, operator_paused=operator_paused))
yield
task.cancel()
try:
yield task

with contextlib.suppress(asyncio.CancelledError):
task.cancel()
await task
except asyncio.CancelledError:
pass


@pytest.fixture()
Expand Down
38 changes: 37 additions & 1 deletion tests/handling/daemons/test_daemon_termination.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import asyncio
import contextlib
import logging

import pytest

import kopf


async def test_daemon_exits_gracefully_and_instantly_on_termination_request(
async def test_daemon_exits_gracefully_and_instantly_on_resource_deletion(
settings, resource, dummy, simulate_cycle,
caplog, assert_logs, k8s_mocked, frozen_time, mocker, timer):
caplog.set_level(logging.DEBUG)
Expand Down Expand Up @@ -39,6 +40,41 @@ async def fn(**kwargs):
assert k8s_mocked.patch.call_args_list[0][1]['payload']['metadata']['finalizers'] == []


async def test_daemon_exits_gracefully_and_instantly_on_operator_exiting(
settings, resource, dummy, simulate_cycle, background_daemon_killer,
caplog, assert_logs, k8s_mocked, frozen_time, mocker, timer):
caplog.set_level(logging.DEBUG)

# A daemon-under-test.
@kopf.daemon(*resource, id='fn')
async def fn(**kwargs):
dummy.kwargs = kwargs
dummy.steps['called'].set()
await kwargs['stopped'].wait()

# 0th cycle: trigger spawning and wait until ready. Assume the finalizers are already added.
finalizer = settings.persistence.finalizer
event_object = {'metadata': {'finalizers': [finalizer]}}
await simulate_cycle(event_object)
await dummy.steps['called'].wait()

# 1st stage: trigger termination due to operator exiting.
mocker.resetall()
background_daemon_killer.cancel()

# Check that the daemon has exited near-instantly, with no delays.
with timer:
await dummy.wait_for_daemon_done()

assert timer.seconds < 0.01 # near-instantly
assert k8s_mocked.sleep.call_count == 0
assert k8s_mocked.patch.call_count == 0

# To prevent double-cancelling of the scheduler's system tasks in the fixture, let them finish:
with contextlib.suppress(asyncio.CancelledError):
await background_daemon_killer


@pytest.mark.usefixtures('background_daemon_killer')
async def test_daemon_exits_gracefully_and_instantly_on_operator_pausing(
settings, memories, resource, dummy, simulate_cycle, conflicts_found,
Expand Down

0 comments on commit 7d3fd65

Please sign in to comment.