Skip to content

Commit 1fcdcef

Browse files
raballewclaude
andcommitted
fix: release lease on unused timeout when hooks are configured
When a lease times out without any client connection and hooks are configured, _cleanup_after_lease skipped the afterLease hook (correct) but also skipped calling _request_lease_release (bug). This left the exporter permanently stuck in LeaseReady status because the controller was never notified that the lease should be freed. Add _request_lease_release() call in the else branch of _cleanup_after_lease so the controller always frees the lease, regardless of whether the afterLease hook ran. Fixes: #237 Generated-By: Forge/20260416_202053_681470_86fec9bd_i237 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3e37577 commit 1fcdcef

2 files changed

Lines changed: 57 additions & 0 deletions

File tree

python/packages/jumpstarter/jumpstarter/exporter/exporter.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,7 @@ async def _cleanup_after_lease(self, lease_scope: LeaseContext) -> None:
622622
await self._report_status(ExporterStatus.AVAILABLE, "Available for new lease")
623623
else:
624624
logger.debug("Exporter is shutting down, skipping AVAILABLE status report")
625+
await self._request_lease_release()
625626
if not lease_scope.after_lease_hook_done.is_set():
626627
lease_scope.after_lease_hook_done.set()
627628
else:

python/packages/jumpstarter/jumpstarter/exporter/exporter_test.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,62 @@ async def track_status(status, message=""):
184184
assert ExporterStatus.AVAILABLE in statuses
185185
assert lease_ctx.after_lease_hook_done.is_set()
186186

187+
async def test_unused_lease_with_hooks_calls_request_lease_release(self):
188+
"""When a lease ends with no client and hooks are configured,
189+
_request_lease_release must still be called so the controller
190+
frees the lease. This prevents the exporter from getting stuck
191+
in LeaseReady status permanently."""
192+
from jumpstarter.config.exporter import HookConfigV1Alpha1, HookInstanceConfigV1Alpha1
193+
from jumpstarter.exporter.hooks import HookExecutor
194+
195+
lease_ctx = make_lease_context(client_name="")
196+
lease_ctx.before_lease_hook.set()
197+
198+
hook_config = HookConfigV1Alpha1(
199+
after_lease=HookInstanceConfigV1Alpha1(script="echo cleanup", timeout=10),
200+
)
201+
hook_executor = HookExecutor(config=hook_config)
202+
203+
exporter = make_exporter(lease_ctx, hook_executor)
204+
205+
await exporter._cleanup_after_lease(lease_ctx)
206+
207+
exporter._request_lease_release.assert_awaited_once()
208+
209+
async def test_unused_lease_without_hooks_calls_request_lease_release(self):
210+
"""When a lease ends with no client and no hooks configured,
211+
_request_lease_release must be called so the controller frees
212+
the lease."""
213+
lease_ctx = make_lease_context(client_name="")
214+
lease_ctx.before_lease_hook.set()
215+
216+
exporter = make_exporter(lease_ctx)
217+
218+
await exporter._cleanup_after_lease(lease_ctx)
219+
220+
exporter._request_lease_release.assert_awaited_once()
221+
222+
async def test_unused_lease_during_shutdown_still_releases(self):
223+
"""When a lease ends with no client during exporter shutdown,
224+
_request_lease_release must still be called even though AVAILABLE
225+
status is not reported."""
226+
lease_ctx = make_lease_context(client_name="")
227+
lease_ctx.before_lease_hook.set()
228+
229+
statuses = []
230+
231+
async def track_status(status, message=""):
232+
statuses.append(status)
233+
234+
exporter = make_exporter(lease_ctx)
235+
exporter._stop_requested = True
236+
exporter._report_status = AsyncMock(side_effect=track_status)
237+
238+
await exporter._cleanup_after_lease(lease_ctx)
239+
240+
exporter._request_lease_release.assert_awaited_once()
241+
assert ExporterStatus.AVAILABLE not in statuses
242+
187243
async def test_new_lease_after_unused_timeout_recovery(self):
188244
"""After recovering from unused lease timeout, a new lease
189245
can be accepted and processed."""

0 commit comments

Comments
 (0)