Skip to content

Commit

Permalink
Deprecations for v2 apis
Browse files Browse the repository at this point in the history
avoid unnecessary breakage by leaving old names available with deprecation warnings
  • Loading branch information
minrk committed Feb 21, 2023
1 parent 76c607b commit 10bbf3b
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 40 deletions.
39 changes: 22 additions & 17 deletions jupyterhub_traefik_proxy/consul.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@
from urllib.parse import urlparse
import base64
import string
import warnings

import escapism
from traitlets import default, observe, Any, Unicode
from traitlets import default, Any, Unicode

from .kv_proxy import TKvProxy

Expand Down Expand Up @@ -54,20 +53,26 @@ def _provider_name(self):
consul_username = Unicode(
"",
config=True,
help="URL for the consul endpoint.",
help="Usrname for accessing consul.",
)
consul_password = Unicode(
"",
config=True,
help="Password or token for accessing consul.",
)

kv_url = Unicode("DEPRECATED", config=True)
kv_username = Unicode("DEPRECATED", config=True)
kv_password = Unicode("DEPRECATED", config=True)

@observe("kv_url", "kv_username", "kv_password")
def _deprecated_config(self, change):
new_name = change.name.replace("kv_", "consul_")
warnings.warn(
f"TraefikConsulProxy.{change.name} is deprecated in 0.4. Use TraefikEtcdProxy.{new_name} = {change.new!r}"
)
setattr(self, new_name, change.new)
kv_url = Unicode("DEPRECATED", config=True).tag(
deprecated_in="0.4",
deprecated_for="consul_url",
)
kv_username = Unicode("DEPRECATED", config=True).tag(
deprecated_in="0.4",
deprecated_for="consul_username",
)
kv_password = Unicode("DEPRECATED", config=True).tag(
deprecated_in="0.4",
deprecated_for="consul_password",
)

consul = Any()

Expand All @@ -81,7 +86,7 @@ def _default_client(self):
kwargs = {
"host": consul_service.hostname,
"port": consul_service.port,
"cert": self.consul_ca_cert,
"cert": self.consul_client_ca_cert,
}
if self.consul_password:
kwargs.update({"token": self.consul_password})
Expand All @@ -96,8 +101,8 @@ def _define_kv_specific_static_config(self):
}

# FIXME: Same with the tls info
if self.consul_ca_cert:
provider_config["consul"]["tls"] = {"ca": self.consul_ca_cert}
if self.consul_client_ca_cert:
provider_config["consul"]["tls"] = {"ca": self.consul_client_ca_cert}

self.static_config.update({"providers": provider_config})

Expand Down
26 changes: 13 additions & 13 deletions jupyterhub_traefik_proxy/etcd.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,10 @@

from concurrent.futures import ThreadPoolExecutor
from urllib.parse import urlparse
import warnings

import escapism
from tornado.concurrent import run_on_executor
from traitlets import Any, default, observe, Bool, List, Unicode
from traitlets import Any, default, Bool, List, Unicode

from .kv_proxy import TKvProxy

Expand Down Expand Up @@ -97,17 +96,18 @@ def _default_executor(self):
help="Password for accessing etcd.",
)

kv_url = Unicode("DEPRECATED", config=True)
kv_username = Unicode("DEPRECATED", config=True)
kv_password = Unicode("DEPRECATED", config=True)

@observe("kv_url", "kv_username", "kv_password")
def _deprecated_config(self, change):
new_name = change.name.replace("kv_", "etcd_")
warnings.warn(
f"TraefikEtcdProxy.{change.name} is deprecated in 0.4. Use TraefikEtcdProxy.{new_name} = {change.new!r}"
)
setattr(self, new_name, change.new)
kv_url = Unicode("DEPRECATED", config=True).tag(
deprecated_in="0.4",
deprecated_for="etcd_url",
)
kv_username = Unicode("DEPRECATED", config=True).tag(
deprecated_in="0.4",
deprecated_for="etcd_username",
)
kv_password = Unicode("DEPRECATED", config=True).tag(
deprecated_in="0.4",
deprecated_for="etcd_password",
)

etcd = Any()

Expand Down
15 changes: 7 additions & 8 deletions jupyterhub_traefik_proxy/fileprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,20 @@ def _default_handler(self):
def _set_dynamic_config_file(self, change):
self.dynamic_config_handler = traefik_utils.TraefikConfigFileHandler(self.dynamic_config_file)

def __init__(self, **kwargs):
super().__init__(**kwargs)
@default("dynamic_config")
def _load_dynamic_config(self):
try:
# Load initial dynamic config from disk
self.dynamic_config = self.dynamic_config_handler.load()
dynamic_config = self.dynamic_config_handler.load()
except FileNotFoundError:
self.dynamic_config = {}
dynamic_config = {}

if not self.dynamic_config:
self.dynamic_config = {
if not dynamic_config:
dynamic_config = {
"http" : {"services": {}, "routers": {}},
"jupyter": {"routers" : {} }
}
return dynamic_config

def persist_dynamic_config(self):
"""Save the dynamic config file with the current dynamic_config"""
Expand Down Expand Up @@ -238,7 +239,6 @@ async def delete_route(self, routespec):
router_alias = traefik_utils.generate_alias(routespec, "router")

async with self.mutex:

# Pop each entry and if it's the last one, delete the key
self.dynamic_config["http"]["routers"].pop(router_alias, None)
self.dynamic_config["http"]["services"].pop(service_alias, None)
Expand Down Expand Up @@ -309,4 +309,3 @@ async def get_route(self, routespec):
routespec = self.validate_routespec(routespec)
async with self.mutex:
return self._get_route_unsafe(routespec)

47 changes: 45 additions & 2 deletions jupyterhub_traefik_proxy/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from subprocess import Popen, TimeoutExpired
from urllib.parse import urlparse

from traitlets import Any, Bool, Dict, Integer, Unicode, default
from traitlets import Any, Bool, Dict, Integer, Unicode, default, observe
from tornado.httpclient import AsyncHTTPClient

from jupyterhub.utils import exponential_backoff, url_path_join, new_token
Expand All @@ -41,6 +41,49 @@ class TraefikProxy(Proxy):
"traefik.toml", config=True, help="""traefik's static configuration file"""
)

toml_static_config_file = Unicode(
config=True,
help="Deprecated. Use static_config_file",
).tag(
deprecated_in="0.4",
deprecated_for="static_config_file",
)

def _deprecated_trait(self, change):
"""observer for deprecated traits"""
trait = change.owner.traits()[change.name]
old_attr = change.name
new_attr = trait.metadata["deprecated_for"]
version = trait.metadata["deprecated_in"]
if "." in new_attr:
new_cls_attr = new_attr
new_attr = new_attr.rsplit(".", 1)[1]
else:
new_cls_attr = f"{self.__class__.__name__}.{new_attr}"

new_value = getattr(self, new_attr)
if new_value != change.new:
# only warn if different
# protects backward-compatible config from warnings
# if they set the same value under both names
message = "{cls}.{old} is deprecated in {cls} {version}, use {new} instead".format(
cls=self.__class__.__name__,
old=old_attr,
new=new_cls_attr,
version=version,
)
self.log.warning(message)

setattr(self, new_attr, change.new)

def __init__(self, **kwargs):
# observe deprecated config names in oauthenticator
for name, trait in self.class_traits().items():
deprecated_in = trait.metadata.get("deprecated_in")
if trait.metadata.get("deprecated_in"):
self.observe(self._deprecated_trait, name)
super().__init__(**kwargs)

static_config = Dict()
dynamic_config = Dict()

Expand Down Expand Up @@ -71,7 +114,7 @@ class TraefikProxy(Proxy):
)

provider_name = Unicode(
config=True, help="""The provider name that Traefik expects, e.g. file, consul, etcd"""
help="""The provider name that Traefik expects, e.g. file, consul, etcd"""
)

is_https = Bool(
Expand Down
16 changes: 16 additions & 0 deletions jupyterhub_traefik_proxy/toml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

from traitlets import Unicode
from .fileprovider import TraefikFileProviderProxy


class TraefikTomlProxy(TraefikFileProviderProxy):
"""Deprecated alias for file provider"""
toml_dynamic_config_file = Unicode(
config=True,
).tag(
deprecated_in="0.4",
deprecated_for="TraefikFileProvider.dynamic_config_file",
)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.log.warning("TraefikTomlProxy is deprecated in jupyterhub-traefik-proxy 0.4. Use `c.JupyterHub.proxy_class = 'traefik_file'")
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def run(self):
"traefik_consul = jupyterhub_traefik_proxy.consul:TraefikConsulProxy",
"traefik_etcd = jupyterhub_traefik_proxy.etcd:TraefikEtcdProxy",
"traefik_file = jupyterhub_traefik_proxy.fileprovider:TraefikFileProviderProxy",
"traefik_toml = jupyterhub_traefik_proxy.toml:TraefikTomlProxy",
]
},
)
60 changes: 60 additions & 0 deletions tests/test_deprecations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from traitlets.config import Config

from jupyterhub_traefik_proxy.toml import TraefikTomlProxy
from jupyterhub_traefik_proxy.etcd import TraefikEtcdProxy
from jupyterhub_traefik_proxy.consul import TraefikConsulProxy

def test_toml_deprecation(caplog):
cfg = Config()
cfg.TraefikTomlProxy.toml_static_config_file = 'deprecated-static.toml'
cfg.TraefikTomlProxy.toml_dynamic_config_file = 'deprecated-dynamic.toml'
p = TraefikTomlProxy(config=cfg)
assert p.static_config_file == 'deprecated-static.toml'

assert p.dynamic_config_file == 'deprecated-dynamic.toml'

log = '\n'.join([
record.msg
for record in caplog.records
])
assert 'TraefikFileProvider.dynamic_config_file instead' in log
assert 'static_config_file instead' in log

def test_etcd_deprecation(caplog):
cfg = Config()
cfg.TraefikEtcdProxy.kv_url = "http://1.2.3.4:12345"
cfg.TraefikEtcdProxy.kv_username = "user"
cfg.TraefikEtcdProxy.kv_password = "pass"

p = TraefikEtcdProxy(config=cfg)
assert p.etcd_url=="http://1.2.3.4:12345"
assert p.etcd_username == "user"
assert p.etcd_password == "pass"

log = '\n'.join([
record.msg
for record in caplog.records
])
assert 'TraefikEtcdProxy.etcd_url instead' in log
assert 'TraefikEtcdProxy.etcd_username instead' in log
assert 'TraefikEtcdProxy.etcd_password instead' in log


def test_consul_deprecation(caplog):
cfg = Config()
cfg.TraefikConsulProxy.kv_url = "http://1.2.3.4:12345"
cfg.TraefikConsulProxy.kv_username = "user"
cfg.TraefikConsulProxy.kv_password = "pass"

p = TraefikConsulProxy(config=cfg)
assert p.consul_url=="http://1.2.3.4:12345"
assert p.consul_username == "user"
assert p.consul_password == "pass"

log = '\n'.join([
record.msg
for record in caplog.records
])
assert 'TraefikConsulProxy.consul_url instead' in log
assert 'TraefikConsulProxy.consul_username instead' in log
assert 'TraefikConsulProxy.consul_password instead' in log

0 comments on commit 10bbf3b

Please sign in to comment.