Update gateway support with recent changes

Convey notebook working directory to the gateway (nb2kg pr-21)
Support retrieval of kernelspec resources from the gateway (nb2kg pr-23)
pull/4431/head
Kevin Bates 7 years ago
parent d145301b55
commit 46bcf78d63

@ -3,8 +3,9 @@
import os
import logging
import mimetypes
from ..base.handlers import IPythonHandler
from ..base.handlers import APIHandler, IPythonHandler
from ..utils import url_path_join
from tornado import gen, web
@ -200,8 +201,26 @@ class GatewayWebSocketClient(LoggingConfigurable):
self._disconnect()
class GatewayResourceHandler(APIHandler):
"""Retrieves resources for specific kernelspec definitions from kernel/enterprise gateway."""
@web.authenticated
@gen.coroutine
def get(self, kernel_name, path, include_body=True):
ksm = self.kernel_spec_manager
kernel_spec_res = yield ksm.get_kernel_spec_resource(kernel_name, path)
if kernel_spec_res is None:
self.log.warning("Kernelspec resource '{}' for '{}' not found. Gateway may not support"
" resource serving.".format(path, kernel_name))
else:
self.set_header("Content-Type", mimetypes.guess_type(path)[0])
self.finish(kernel_spec_res)
from ..services.kernels.handlers import _kernel_id_regex
from ..services.kernelspecs.handlers import kernel_name_regex
default_handlers = [
(r"/api/kernels/%s/channels" % _kernel_id_regex, WebSocketChannelsHandler),
(r"/kernelspecs/%s/(?P<path>.*)" % kernel_name_regex, GatewayResourceHandler),
]

@ -91,6 +91,16 @@ class GatewayClient(SingletonConfigurable):
def _kernelspecs_endpoint_default(self):
return os.environ.get(self.kernelspecs_endpoint_env, self.kernelspecs_endpoint_default_value)
kernelspecs_resource_endpoint_default_value = '/kernelspecs'
kernelspecs_resource_endpoint_env = 'JUPYTER_GATEWAY_KERNELSPECS_RESOURCE_ENDPOINT'
kernelspecs_resource_endpoint = Unicode(default_value=kernelspecs_resource_endpoint_default_value, config=True,
help="""The gateway endpoint for accessing kernelspecs resources
(JUPYTER_GATEWAY_KERNELSPECS_RESOURCE_ENDPOINT env var)""")
@default('kernelspecs_resource_endpoint')
def _kernelspecs_resource_endpoint_default(self):
return os.environ.get(self.kernelspecs_resource_endpoint_env, self.kernelspecs_resource_endpoint_default_value)
connect_timeout_default_value = 20.0
connect_timeout_env = 'JUPYTER_GATEWAY_CONNECT_TIMEOUT'
connect_timeout = Float(default_value=connect_timeout_default_value, config=True,
@ -333,6 +343,11 @@ class GatewayKernelManager(MappingKernelManager):
kernel_env = {k: v for (k, v) in dict(os.environ).items() if k.startswith('KERNEL_')
or k in GatewayClient.instance().env_whitelist.split(",")}
# Convey the full path to where this notebook file is located.
if path is not None and kernel_env.get('KERNEL_WORKING_DIR') is None:
kernel_env['KERNEL_WORKING_DIR'] = kwargs['cwd']
json_body = json_encode({'name': kernel_name, 'env': kernel_env})
response = yield gateway_request(kernel_url, method='POST', body=json_body)
@ -467,7 +482,10 @@ class GatewayKernelSpecManager(KernelSpecManager):
def __init__(self, **kwargs):
super(GatewayKernelSpecManager, self).__init__(**kwargs)
self.base_endpoint = url_path_join(GatewayClient.instance().url, GatewayClient.instance().kernelspecs_endpoint)
self.base_endpoint = url_path_join(GatewayClient.instance().url,
GatewayClient.instance().kernelspecs_endpoint)
self.base_resource_endpoint = url_path_join(GatewayClient.instance().url,
GatewayClient.instance().kernelspecs_resource_endpoint)
def _get_kernelspecs_endpoint_url(self, kernel_name=None):
"""Builds a url for the kernels endpoint
@ -498,14 +516,7 @@ class GatewayKernelSpecManager(KernelSpecManager):
notebook_default=km.default_kernel_name))
km.default_kernel_name = remote_default_kernel_name
# gateway doesn't support resources (requires transfer for use by NB client)
# so add `resource_dir` to each kernelspec and value of 'not supported in gateway mode'
remote_kspecs = fetched_kspecs.get('kernelspecs')
for kernel_name, kspec_info in remote_kspecs.items():
if not kspec_info.get('resource_dir'):
kspec_info['resource_dir'] = 'not supported in gateway mode'
remote_kspecs[kernel_name].update(kspec_info)
raise gen.Return(remote_kspecs)
@gen.coroutine
@ -540,9 +551,32 @@ class GatewayKernelSpecManager(KernelSpecManager):
raise
else:
kernel_spec = json_decode(response.body)
# Convert to instance of Kernelspec
kspec_instance = self.kernel_spec_class(resource_dir=u'', **kernel_spec['spec'])
raise gen.Return(kspec_instance)
raise gen.Return(kernel_spec)
@gen.coroutine
def get_kernel_spec_resource(self, kernel_name, path):
"""Get kernel spec for kernel_name.
Parameters
----------
kernel_name : str
The name of the kernel.
path : str
The name of the desired resource
"""
kernel_spec_resource_url = url_path_join(self.base_resource_endpoint, str(kernel_name), str(path))
self.log.debug("Request kernel spec resource '{}' at: {}".format(path, kernel_spec_resource_url))
try:
response = yield gateway_request(kernel_spec_resource_url, method='GET')
except HTTPError as error:
if error.code == 404:
kernel_spec_resource = None
else:
raise
else:
kernel_spec_resource = response.body
raise gen.Return(kernel_spec_resource)
class GatewaySessionManager(SessionManager):

@ -16,6 +16,7 @@ from tornado import web, gen
from ...base.handlers import APIHandler
from ...utils import url_path_join, url_unescape
def kernelspec_model(handler, name, spec_dict, resource_dir):
"""Load a KernelSpec by name and return the REST API model"""
d = {
@ -45,6 +46,12 @@ def kernelspec_model(handler, name, spec_dict, resource_dir):
)
return d
def is_kernelspec_model(spec_dict):
"""Returns True if spec_dict is already in proper form. This will occur when using a gateway."""
return isinstance(spec_dict, dict) and 'name' in spec_dict and 'spec' in spec_dict and 'resources' in spec_dict
class MainKernelSpecHandler(APIHandler):
@web.authenticated
@ -58,8 +65,10 @@ class MainKernelSpecHandler(APIHandler):
kspecs = yield gen.maybe_future(ksm.get_all_specs())
for kernel_name, kernel_info in kspecs.items():
try:
d = kernelspec_model(self, kernel_name, kernel_info['spec'],
kernel_info['resource_dir'])
if is_kernelspec_model(kernel_info):
d = kernel_info
else:
d = kernelspec_model(self, kernel_name, kernel_info['spec'], kernel_info['resource_dir'])
except Exception:
self.log.error("Failed to load kernel spec: '%s'", kernel_name, exc_info=True)
continue
@ -79,7 +88,10 @@ class KernelSpecHandler(APIHandler):
spec = yield gen.maybe_future(ksm.get_kernel_spec(kernel_name))
except KeyError:
raise web.HTTPError(404, u'Kernel spec %s not found' % kernel_name)
model = kernelspec_model(self, kernel_name, spec.to_dict(), spec.resource_dir)
if is_kernelspec_model(spec):
model = spec
else:
model = kernelspec_model(self, kernel_name, spec.to_dict(), spec.resource_dir)
self.set_header("Content-Type", 'application/json')
self.finish(json.dumps(model))

Loading…
Cancel
Save