21
21
import asyncio
22
22
import json
23
23
import os
24
+ import ssl
24
25
from os .path import abspath
25
26
from subprocess import Popen , TimeoutExpired
26
27
from urllib .parse import urlparse , urlunparse
@@ -105,6 +106,14 @@ def __init__(self, **kwargs):
105
106
if trait .metadata .get ("deprecated_in" ):
106
107
self .observe (self ._deprecated_trait , name )
107
108
super ().__init__ (** kwargs )
109
+ # not calling .start, make sure we load our initial dynamic config
110
+ # and that our static config matches what we expect
111
+ # can't await here because we're not async,
112
+ # so await in our Proxy methods
113
+ if not self .should_start :
114
+ self ._start_future = asyncio .ensure_future (self ._start_external ())
115
+ else :
116
+ self ._start_future = None
108
117
109
118
static_config = Dict ()
110
119
dynamic_config = Dict ()
@@ -346,6 +355,12 @@ async def _check_traefik_static_conf_ready():
346
355
# unexpected
347
356
self .log .error (f"Error checking for traefik static configuration { e } " )
348
357
return False
358
+ except (OSError , ssl .SSLError ) as e :
359
+ # Can occur if SSL isn't set up yet
360
+ self .log .warning (
361
+ f"SSL Error checking for traefik static configuration: { e } "
362
+ )
363
+ return False
349
364
350
365
return True
351
366
@@ -467,6 +482,15 @@ async def start(self):
467
482
self ._start_traefik ()
468
483
await self ._wait_for_static_config ()
469
484
485
+ async def _start_external (self ):
486
+ """Startup function called when `not self.should_start`
487
+
488
+ Ensures dynamic config is setup and static config is loaded
489
+ """
490
+ await self ._setup_traefik_dynamic_config ()
491
+ await self ._wait_for_static_config ()
492
+ self ._start_future = None
493
+
470
494
async def stop (self ):
471
495
"""Stop the proxy.
472
496
@@ -565,6 +589,8 @@ async def add_route(self, routespec, target, data):
565
589
The proxy implementation should also have a way to associate the fact that a
566
590
route came from JupyterHub.
567
591
"""
592
+ if self ._start_future and not self ._start_future .done ():
593
+ await self ._start_future
568
594
routespec = self .validate_routespec (routespec )
569
595
570
596
traefik_config , jupyterhub_config = self ._dynamic_config_for_route (
@@ -621,6 +647,11 @@ async def _get_jupyterhub_dynamic_config(self):
621
647
"""
622
648
raise NotImplementedError ()
623
649
650
+ async def check_routes (self , * args , ** kwargs ):
651
+ if self ._start_future and not self ._start_future .done ():
652
+ await self ._start_future
653
+ return await super ().check_routes (* args , ** kwargs )
654
+
624
655
async def get_all_routes (self ):
625
656
"""Fetch and return all the routes associated by JupyterHub from the
626
657
proxy.
@@ -636,6 +667,9 @@ async def get_all_routes(self):
636
667
'data': the attached data dict for this route (as specified in add_route)
637
668
}
638
669
"""
670
+ if self ._start_future and not self ._start_future .done ():
671
+ await self ._start_future
672
+
639
673
jupyterhub_config = await self ._get_jupyterhub_dynamic_config ()
640
674
641
675
all_routes = {}
0 commit comments