Skip to content
74 changes: 74 additions & 0 deletions kubespawner/spawner.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import re
import string
import sys
import textwrap
import warnings
from functools import partial
from typing import Optional, Tuple, Type
Expand Down Expand Up @@ -1688,6 +1689,51 @@ def _validate_image_pull_secrets(self, proposal):
""",
)

server_spawn_launch_delay = Integer(
0,
config=True,
help="""
Time in seconds to delay a single-user server launch, which can be
useful for debugging. If set to zero, no delay will take place.
""",
)

server_spawn_launch_timer_enabled = Bool(
True,
config=True,
help="""
Enable the spawn progress counter message.
""",
)

server_spawn_launch_timer_threshold = Integer(
60,
config=True,
help="""
Time in seconds to wait before injecting a 'please be patient' message
to display to the user.
""",
)

server_spawn_launch_timer_frequency = Integer(
5,
config=True,
help="""
Sets a delay of N seconds between updates to the message buffer, so
that we don't spam the user with too many messages.
""",
)

server_spawn_launch_timer_message = Unicode(
"Server launch is taking longer than expected. Please be patient! Current time spent waiting: XXX seconds.",
config=True,
help="""
The injected timing message to display to the user. The string XXX
will be replaced by the number of seconds the spawn has taken in
self.progress().
""",
)

# deprecate redundant and inconsistent singleuser_ and user_ prefixes:
_deprecated_traits_09 = [
"singleuser_working_dir",
Expand Down Expand Up @@ -2306,6 +2352,9 @@ async def progress(self):
progress = 0
next_event = 0

# count in seconds to time single user server spawn duration
timer = 0

break_while_loop = False
while True:
# This logic avoids a race condition. self._start() will be invoked by
Expand All @@ -2322,6 +2371,23 @@ async def progress(self):
if start_future and start_future.done():
break_while_loop = True

# if the timer is greater than self.server_spawn_launch_timer_threshold
# display a message to the user with an incrementing count in seconds
if (
timer >= self.server_spawn_launch_timer_threshold
and self.server_spawn_launch_timer_enabled
):
# don't spam the user, so only update the timer message every few seconds
if timer % self.server_spawn_launch_timer_frequency == 0:
patience_message = textwrap.dedent(
self.server_spawn_launch_timer_message
)
patience_message.replace('XXX', str(timer))

yield {
'message': patience_message,
}

events = self.events
len_events = len(events)
if next_event < len_events:
Expand All @@ -2348,6 +2414,7 @@ async def progress(self):

if break_while_loop:
break
timer += 1
await asyncio.sleep(1)

async def _start_reflector(
Expand Down Expand Up @@ -2663,6 +2730,13 @@ async def _make_create_resource_request(self, kind, manifest):

async def _start(self):
"""Start the user's pod"""
# delay single user server spawn if testing
if self.server_spawn_launch_delay:
self.log.info(
"Delaying spawn launch for %s seconds.",
str(self.server_spawn_launch_delay),
)
await asyncio.sleep(self.server_spawn_launch_delay)

# load user options (including profile)
await self.load_user_options()
Expand Down