Skip to content

Commit 88aae11

Browse files
authored
Merge pull request #4431 from kevin-bates/apply-nb2kg-updates
Update gateway support with recent changes
2 parents 859ae0a + 46bcf78 commit 88aae11

File tree

3 files changed

+80
-15
lines changed

3 files changed

+80
-15
lines changed

notebook/gateway/handlers.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33

44
import os
55
import logging
6+
import mimetypes
67

7-
from ..base.handlers import IPythonHandler
8+
from ..base.handlers import APIHandler, IPythonHandler
89
from ..utils import url_path_join
910

1011
from tornado import gen, web
@@ -200,8 +201,26 @@ def on_close(self):
200201
self._disconnect()
201202

202203

204+
class GatewayResourceHandler(APIHandler):
205+
"""Retrieves resources for specific kernelspec definitions from kernel/enterprise gateway."""
206+
207+
@web.authenticated
208+
@gen.coroutine
209+
def get(self, kernel_name, path, include_body=True):
210+
ksm = self.kernel_spec_manager
211+
kernel_spec_res = yield ksm.get_kernel_spec_resource(kernel_name, path)
212+
if kernel_spec_res is None:
213+
self.log.warning("Kernelspec resource '{}' for '{}' not found. Gateway may not support"
214+
" resource serving.".format(path, kernel_name))
215+
else:
216+
self.set_header("Content-Type", mimetypes.guess_type(path)[0])
217+
self.finish(kernel_spec_res)
218+
219+
203220
from ..services.kernels.handlers import _kernel_id_regex
221+
from ..services.kernelspecs.handlers import kernel_name_regex
204222

205223
default_handlers = [
206224
(r"/api/kernels/%s/channels" % _kernel_id_regex, WebSocketChannelsHandler),
225+
(r"/kernelspecs/%s/(?P<path>.*)" % kernel_name_regex, GatewayResourceHandler),
207226
]

notebook/gateway/managers.py

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,16 @@ def _kernels_endpoint_default(self):
9090
def _kernelspecs_endpoint_default(self):
9191
return os.environ.get(self.kernelspecs_endpoint_env, self.kernelspecs_endpoint_default_value)
9292

93+
kernelspecs_resource_endpoint_default_value = '/kernelspecs'
94+
kernelspecs_resource_endpoint_env = 'JUPYTER_GATEWAY_KERNELSPECS_RESOURCE_ENDPOINT'
95+
kernelspecs_resource_endpoint = Unicode(default_value=kernelspecs_resource_endpoint_default_value, config=True,
96+
help="""The gateway endpoint for accessing kernelspecs resources
97+
(JUPYTER_GATEWAY_KERNELSPECS_RESOURCE_ENDPOINT env var)""")
98+
99+
@default('kernelspecs_resource_endpoint')
100+
def _kernelspecs_resource_endpoint_default(self):
101+
return os.environ.get(self.kernelspecs_resource_endpoint_env, self.kernelspecs_resource_endpoint_default_value)
102+
93103
connect_timeout_default_value = 20.0
94104
connect_timeout_env = 'JUPYTER_GATEWAY_CONNECT_TIMEOUT'
95105
connect_timeout = Float(default_value=connect_timeout_default_value, config=True,
@@ -332,6 +342,11 @@ def start_kernel(self, kernel_id=None, path=None, **kwargs):
332342

333343
kernel_env = {k: v for (k, v) in dict(os.environ).items() if k.startswith('KERNEL_')
334344
or k in GatewayClient.instance().env_whitelist.split(",")}
345+
346+
# Convey the full path to where this notebook file is located.
347+
if path is not None and kernel_env.get('KERNEL_WORKING_DIR') is None:
348+
kernel_env['KERNEL_WORKING_DIR'] = kwargs['cwd']
349+
335350
json_body = json_encode({'name': kernel_name, 'env': kernel_env})
336351

337352
response = yield gateway_request(kernel_url, method='POST', body=json_body)
@@ -466,7 +481,10 @@ class GatewayKernelSpecManager(KernelSpecManager):
466481

467482
def __init__(self, **kwargs):
468483
super(GatewayKernelSpecManager, self).__init__(**kwargs)
469-
self.base_endpoint = url_path_join(GatewayClient.instance().url, GatewayClient.instance().kernelspecs_endpoint)
484+
self.base_endpoint = url_path_join(GatewayClient.instance().url,
485+
GatewayClient.instance().kernelspecs_endpoint)
486+
self.base_resource_endpoint = url_path_join(GatewayClient.instance().url,
487+
GatewayClient.instance().kernelspecs_resource_endpoint)
470488

471489
def _get_kernelspecs_endpoint_url(self, kernel_name=None):
472490
"""Builds a url for the kernels endpoint
@@ -497,14 +515,7 @@ def get_all_specs(self):
497515
notebook_default=km.default_kernel_name))
498516
km.default_kernel_name = remote_default_kernel_name
499517

500-
# gateway doesn't support resources (requires transfer for use by NB client)
501-
# so add `resource_dir` to each kernelspec and value of 'not supported in gateway mode'
502518
remote_kspecs = fetched_kspecs.get('kernelspecs')
503-
for kernel_name, kspec_info in remote_kspecs.items():
504-
if not kspec_info.get('resource_dir'):
505-
kspec_info['resource_dir'] = 'not supported in gateway mode'
506-
remote_kspecs[kernel_name].update(kspec_info)
507-
508519
raise gen.Return(remote_kspecs)
509520

510521
@gen.coroutine
@@ -539,9 +550,32 @@ def get_kernel_spec(self, kernel_name, **kwargs):
539550
raise
540551
else:
541552
kernel_spec = json_decode(response.body)
542-
# Convert to instance of Kernelspec
543-
kspec_instance = self.kernel_spec_class(resource_dir=u'', **kernel_spec['spec'])
544-
raise gen.Return(kspec_instance)
553+
554+
raise gen.Return(kernel_spec)
555+
556+
@gen.coroutine
557+
def get_kernel_spec_resource(self, kernel_name, path):
558+
"""Get kernel spec for kernel_name.
559+
560+
Parameters
561+
----------
562+
kernel_name : str
563+
The name of the kernel.
564+
path : str
565+
The name of the desired resource
566+
"""
567+
kernel_spec_resource_url = url_path_join(self.base_resource_endpoint, str(kernel_name), str(path))
568+
self.log.debug("Request kernel spec resource '{}' at: {}".format(path, kernel_spec_resource_url))
569+
try:
570+
response = yield gateway_request(kernel_spec_resource_url, method='GET')
571+
except HTTPError as error:
572+
if error.code == 404:
573+
kernel_spec_resource = None
574+
else:
575+
raise
576+
else:
577+
kernel_spec_resource = response.body
578+
raise gen.Return(kernel_spec_resource)
545579

546580

547581
class GatewaySessionManager(SessionManager):

notebook/services/kernelspecs/handlers.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from ...base.handlers import APIHandler
1717
from ...utils import url_path_join, url_unescape
1818

19+
1920
def kernelspec_model(handler, name, spec_dict, resource_dir):
2021
"""Load a KernelSpec by name and return the REST API model"""
2122
d = {
@@ -45,6 +46,12 @@ def kernelspec_model(handler, name, spec_dict, resource_dir):
4546
)
4647
return d
4748

49+
50+
def is_kernelspec_model(spec_dict):
51+
"""Returns True if spec_dict is already in proper form. This will occur when using a gateway."""
52+
return isinstance(spec_dict, dict) and 'name' in spec_dict and 'spec' in spec_dict and 'resources' in spec_dict
53+
54+
4855
class MainKernelSpecHandler(APIHandler):
4956

5057
@web.authenticated
@@ -58,8 +65,10 @@ def get(self):
5865
kspecs = yield gen.maybe_future(ksm.get_all_specs())
5966
for kernel_name, kernel_info in kspecs.items():
6067
try:
61-
d = kernelspec_model(self, kernel_name, kernel_info['spec'],
62-
kernel_info['resource_dir'])
68+
if is_kernelspec_model(kernel_info):
69+
d = kernel_info
70+
else:
71+
d = kernelspec_model(self, kernel_name, kernel_info['spec'], kernel_info['resource_dir'])
6372
except Exception:
6473
self.log.error("Failed to load kernel spec: '%s'", kernel_name, exc_info=True)
6574
continue
@@ -79,7 +88,10 @@ def get(self, kernel_name):
7988
spec = yield gen.maybe_future(ksm.get_kernel_spec(kernel_name))
8089
except KeyError:
8190
raise web.HTTPError(404, u'Kernel spec %s not found' % kernel_name)
82-
model = kernelspec_model(self, kernel_name, spec.to_dict(), spec.resource_dir)
91+
if is_kernelspec_model(spec):
92+
model = spec
93+
else:
94+
model = kernelspec_model(self, kernel_name, spec.to_dict(), spec.resource_dir)
8395
self.set_header("Content-Type", 'application/json')
8496
self.finish(json.dumps(model))
8597

0 commit comments

Comments
 (0)