Skip to content

Commit 83f2260

Browse files
committed
ensure dynamic config is loaded when proxy is external
remove unnecessary dynamic config from tests
1 parent b99c2f3 commit 83f2260

File tree

4 files changed

+54
-20
lines changed

4 files changed

+54
-20
lines changed

jupyterhub_traefik_proxy/proxy.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import asyncio
2222
import json
2323
import os
24+
import ssl
2425
from os.path import abspath
2526
from subprocess import Popen, TimeoutExpired
2627
from urllib.parse import urlparse, urlunparse
@@ -105,6 +106,14 @@ def __init__(self, **kwargs):
105106
if trait.metadata.get("deprecated_in"):
106107
self.observe(self._deprecated_trait, name)
107108
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
108117

109118
static_config = Dict()
110119
dynamic_config = Dict()
@@ -346,6 +355,12 @@ async def _check_traefik_static_conf_ready():
346355
# unexpected
347356
self.log.error(f"Error checking for traefik static configuration {e}")
348357
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
349364

350365
return True
351366

@@ -467,6 +482,15 @@ async def start(self):
467482
self._start_traefik()
468483
await self._wait_for_static_config()
469484

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+
470494
async def stop(self):
471495
"""Stop the proxy.
472496
@@ -565,6 +589,8 @@ async def add_route(self, routespec, target, data):
565589
The proxy implementation should also have a way to associate the fact that a
566590
route came from JupyterHub.
567591
"""
592+
if self._start_future and not self._start_future.done():
593+
await self._start_future
568594
routespec = self.validate_routespec(routespec)
569595

570596
traefik_config, jupyterhub_config = self._dynamic_config_for_route(
@@ -621,6 +647,11 @@ async def _get_jupyterhub_dynamic_config(self):
621647
"""
622648
raise NotImplementedError()
623649

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+
624655
async def get_all_routes(self):
625656
"""Fetch and return all the routes associated by JupyterHub from the
626657
proxy.
@@ -636,6 +667,9 @@ async def get_all_routes(self):
636667
'data': the attached data dict for this route (as specified in add_route)
637668
}
638669
"""
670+
if self._start_future and not self._start_future.done():
671+
await self._start_future
672+
639673
jupyterhub_config = await self._get_jupyterhub_dynamic_config()
640674

641675
all_routes = {}

tests/config_files/dynamic_config/dynamic_conf.toml

Lines changed: 0 additions & 11 deletions
This file was deleted.

tests/config_files/traefik.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ level = "debug"
77
providersThrottleDuration = "0s"
88

99
[providers.file]
10-
directory = "./tests/config_files/dynamic_config"
10+
directory = "/tmp/jupyterhub-traefik-proxy-test"
1111
watch = true
1212

1313
[entryPoints.websecure]

tests/conftest.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,17 @@ def pytest_configure(config):
7878
config.addinivalue_line("markers", "slow: marks tests as slow.")
7979

8080

81+
@pytest.fixture
82+
def dynamic_config_dir():
83+
# matches traefik.toml
84+
path = Path("/tmp/jupyterhub-traefik-proxy-test")
85+
if path.exists():
86+
shutil.rmtree(path)
87+
path.mkdir()
88+
yield path
89+
shutil.rmtree(path)
90+
91+
8192
@pytest.fixture
8293
async def no_auth_consul_proxy(launch_consul):
8394
"""
@@ -181,9 +192,9 @@ def traitlets_log():
181192

182193
# There must be a way to parameterise this to run on both yaml and toml files?
183194
@pytest.fixture
184-
async def file_proxy_toml():
195+
async def file_proxy_toml(dynamic_config_dir):
185196
"""Fixture returning a configured TraefikFileProviderProxy"""
186-
dynamic_config_file = os.path.join(config_files, "dynamic_config", "rules.toml")
197+
dynamic_config_file = str(dynamic_config_dir / "rules.toml")
187198
static_config_file = "traefik.toml"
188199
proxy = _file_proxy(
189200
dynamic_config_file, static_config_file=static_config_file, should_start=True
@@ -194,8 +205,8 @@ async def file_proxy_toml():
194205

195206

196207
@pytest.fixture
197-
async def file_proxy_yaml():
198-
dynamic_config_file = os.path.join(config_files, "dynamic_config", "rules.yaml")
208+
async def file_proxy_yaml(dynamic_config_dir):
209+
dynamic_config_file = str(dynamic_config_dir / "rules.yaml")
199210
static_config_file = "traefik.yaml"
200211
proxy = _file_proxy(
201212
dynamic_config_file, static_config_file=static_config_file, should_start=True
@@ -218,16 +229,16 @@ def _file_proxy(dynamic_config_file, **kwargs):
218229

219230

220231
@pytest.fixture
221-
async def external_file_proxy_yaml(launch_traefik_file):
222-
dynamic_config_file = os.path.join(config_files, "dynamic_config", "rules.yaml")
232+
async def external_file_proxy_yaml(launch_traefik_file, dynamic_config_dir):
233+
dynamic_config_file = str(dynamic_config_dir / "rules.yaml")
223234
proxy = _file_proxy(dynamic_config_file, should_start=False)
224235
yield proxy
225236
os.remove(dynamic_config_file)
226237

227238

228239
@pytest.fixture
229-
async def external_file_proxy_toml(launch_traefik_file):
230-
dynamic_config_file = os.path.join(config_files, "dynamic_config", "rules.toml")
240+
async def external_file_proxy_toml(launch_traefik_file, dynamic_config_dir):
241+
dynamic_config_file = str(dynamic_config_dir / "rules.toml")
231242
proxy = _file_proxy(dynamic_config_file, should_start=False)
232243
yield proxy
233244
os.remove(dynamic_config_file)

0 commit comments

Comments
 (0)