Skip to content

Commit

Permalink
Merge pull request #4431 from kevin-bates/apply-nb2kg-updates
Browse files Browse the repository at this point in the history
Update gateway support with recent changes
  • Loading branch information
minrk authored Mar 6, 2019
2 parents 859ae0a + 46bcf78 commit 88aae11
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 15 deletions.
21 changes: 20 additions & 1 deletion notebook/gateway/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -200,8 +201,26 @@ def on_close(self):
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),
]
56 changes: 45 additions & 11 deletions notebook/gateway/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,16 @@ def _kernels_endpoint_default(self):
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,
Expand Down Expand Up @@ -332,6 +342,11 @@ def start_kernel(self, kernel_id=None, path=None, **kwargs):

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)
Expand Down Expand Up @@ -466,7 +481,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
Expand Down Expand Up @@ -497,14 +515,7 @@ def get_all_specs(self):
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
Expand Down Expand Up @@ -539,9 +550,32 @@ def get_kernel_spec(self, kernel_name, **kwargs):
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):
Expand Down
18 changes: 15 additions & 3 deletions notebook/services/kernelspecs/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
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 = {
Expand Down Expand Up @@ -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
Expand All @@ -58,8 +65,10 @@ def get(self):
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
Expand All @@ -79,7 +88,10 @@ def get(self, kernel_name):
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))

Expand Down

0 comments on commit 88aae11

Please sign in to comment.