diff --git a/dask-gateway-server/dask_gateway_server/backends/kubernetes/controller.py b/dask-gateway-server/dask_gateway_server/backends/kubernetes/controller.py index 297de496..d72a5c6b 100644 --- a/dask-gateway-server/dask_gateway_server/backends/kubernetes/controller.py +++ b/dask-gateway-server/dask_gateway_server/backends/kubernetes/controller.py @@ -9,7 +9,7 @@ from base64 import b64encode from aiohttp import web -from traitlets import Unicode, Integer, Float, List, validate +from traitlets import Unicode, Integer, Float, List, Dict, validate from traitlets.config import catch_config_error from kubernetes_asyncio import client, config @@ -285,6 +285,17 @@ def _validate_proxy_prefix(self, proposal): config=True, ) + proxy_web_tls_domains = Dict( + help="""The traefik TLS domains to use when creating IngressRoute. + + This is only required when the web entrypoint is `websecure` i.e. + the dashboard is hosted via https. + + Example: + { 'main': 'my.secure.domain.com' } + """, + config=True, + ) _log_formatter_cls = LogFormatter # Fail if the config file errors @@ -1230,7 +1241,7 @@ def make_service(self, cluster_name): def make_ingressroute(self, cluster_name, namespace): route = f"{self.proxy_prefix}/clusters/{namespace}.{cluster_name}/" - return { + route_resource = { "apiVersion": "traefik.containo.us/v1alpha1", "kind": "IngressRoute", "metadata": { @@ -1257,6 +1268,13 @@ def make_ingressroute(self, cluster_name, namespace): }, } + if self.proxy_web_tls_domains: + route_resource["spec"]["tls"] = { + "options": {"domains": self.proxy_web_tls_domains} + } + + return route_resource + def make_ingressroutetcp(self, cluster_name, namespace): return { "apiVersion": "traefik.containo.us/v1alpha1", diff --git a/tests/kubernetes/test_methods.py b/tests/kubernetes/test_methods.py index a8969e5b..8a6a7e43 100644 --- a/tests/kubernetes/test_methods.py +++ b/tests/kubernetes/test_methods.py @@ -269,6 +269,41 @@ def test_make_ingressroute(): route["match"] == f"PathPrefix(`/foo/bar/clusters/{namespace}.{cluster_name}/`)" ) + tls_options = ingress["spec"].get("tls") + assert not tls_options + + +def test_make_ingressroute_with_tls_domain(): + middlewares = [{"name": "my-middleware"}] + domains = {"main": "my-main.domain.com", "sub": "sub.my-main.domain.com"} + + controller = KubeController( + gateway_instance="instance-1234", + api_url="http://example.com/api", + proxy_prefix="/foo/bar", + proxy_web_middlewares=middlewares, + proxy_web_tls_domains=domains, + ) + + cluster_name = "c1234" + namespace = "mynamespace" + + ingress = controller.make_ingressroute(cluster_name, namespace) + + labels = ingress["metadata"]["labels"] + assert labels["gateway.dask.org/instance"] == "instance-1234" + assert labels["gateway.dask.org/cluster"] == cluster_name + assert labels["app.kubernetes.io/component"] == "dask-scheduler" + + route = ingress["spec"]["routes"][0] + assert route["middlewares"] == middlewares + assert ( + route["match"] == f"PathPrefix(`/foo/bar/clusters/{namespace}.{cluster_name}/`)" + ) + + tls_options = ingress["spec"]["tls"]["options"] + assert tls_options["domains"] == domains + def test_make_ingressroutetcp(): controller = KubeController(