Skip to content

Commit

Permalink
Merge pull request #152 from parente/port-retries
Browse files Browse the repository at this point in the history
[issue: 151] Add port retries option
  • Loading branch information
Lull3rSkat3r committed Apr 7, 2016
2 parents 5369d0d + 802d731 commit 0f197ba
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 7 deletions.
16 changes: 10 additions & 6 deletions docs/source/config-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ KernelGatewayApp options
Authorization token required for all requests (KG_AUTH_TOKEN env var)
--KernelGatewayApp.base_url=<Unicode>
Default: ''
The base path on which all API resources are mounted (KG_BASE_URL env var)
The base path for mounting all API resources (KG_BASE_URL env var)
--KernelGatewayApp.config_file=<Unicode>
Default: ''
Full path of a config file.
Expand All @@ -45,8 +45,8 @@ KernelGatewayApp options
Specify a config file to load.
--KernelGatewayApp.default_kernel_name=<Unicode>
Default: ''
The default kernel name to use when spawning a kernel
(KG_DEFAULT_KERNEL_NAME env var)
The default kernel name when spawning a kernel (KG_DEFAULT_KERNEL_NAME env
var)
--KernelGatewayApp.expose_headers=<Unicode>
Default: ''
Sets the Access-Control-Expose-Headers header. (KG_EXPOSE_HEADERS env var)
Expand All @@ -58,9 +58,9 @@ KernelGatewayApp options
IP address on which to listen (KG_IP env var)
--KernelGatewayApp.list_kernels=<Bool>
Default: False
Enables listing the running kernels through /api/kernels and /api/sessions
(KG_LIST_KERNELS env var). Note: Jupyter Notebook allows this by default but
kernel gateway does not .
Permits listing of the running kernels using API endpoints /api/kernels and
/api/sessions (KG_LIST_KERNELS env var). Note: Jupyter Notebook allows this
by default but kernel gateway does not.
--KernelGatewayApp.log_datefmt=<Unicode>
Default: '%Y-%m-%d %H:%M:%S'
The date format used by logging formatters for %(asctime)s
Expand All @@ -81,6 +81,10 @@ KernelGatewayApp options
--KernelGatewayApp.port=<Int>
Default: 0
Port on which to listen (KG_PORT env var)
--KernelGatewayApp.port_retries=<Int>
Default: 0
Number of ports to try if the specified port is not available
(KG_PORT_RETRIES env var)
--KernelGatewayApp.prespawn_count=<Int>
Default: None
Number of kernels to prespawn using the default language. (KG_PRESPAWN_COUNT
Expand Down
35 changes: 34 additions & 1 deletion kernel_gateway/gatewayapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
"""Kernel Gateway Jupyter application."""

import os
import socket
import errno
import logging
import nbformat

Expand All @@ -25,6 +27,7 @@
from tornado import web
from tornado.log import enable_pretty_logging

from notebook.notebookapp import random_ports
from .services.api.handlers import default_handlers as default_api_handlers
from .services.kernels.handlers import default_handlers as default_kernel_handlers
from .services.kernelspecs.handlers import default_handlers as default_kernelspec_handlers
Expand Down Expand Up @@ -65,6 +68,14 @@ class KernelGatewayApp(JupyterApp):
@default('port')
def port_default(self):
return int(os.getenv(self.port_env, 8888))

port_retries_env = 'KG_PORT_RETRIES'
port_retries = Integer(config=True,
help="Number of ports to try if the specified port is not available (KG_PORT_RETRIES env var)"
)
@default('port_retries')
def port_retries_default(self):
return int(os.getenv(self.port_retries_env, 50))

ip_env = 'KG_IP'
ip = Unicode(config=True,
Expand Down Expand Up @@ -421,9 +432,31 @@ def init_webapp(self):
def init_http_server(self):
"""Initializes a HTTP server for the Tornado web application on the
configured interface and port.
Tries to find an open port if the one configured is not available using
the same logic as the Jupyer Notebook server.
"""
self.http_server = httpserver.HTTPServer(self.web_app)
self.http_server.listen(self.port, self.ip)

for port in random_ports(self.port, self.port_retries+1):
try:
self.http_server.listen(port, self.ip)
except socket.error as e:
if e.errno == errno.EADDRINUSE:
self.log.info('The port %i is already in use, trying another port.' % port)
continue
elif e.errno in (errno.EACCES, getattr(errno, 'WSAEACCES', errno.EACCES)):
self.log.warning("Permission to listen on port %i denied" % port)
continue
else:
raise
else:
self.port = port
break
else:
self.log.critical('ERROR: the notebook server could not be started because '
'no available port could be found.')
self.exit(1)

def start(self):
"""Starts an IO loop for the application."""
Expand Down
2 changes: 2 additions & 0 deletions kernel_gateway/tests/test_gatewayapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def test_config_env_vars(self):
"""Env vars should be honored for traitlets."""
# Environment vars are always strings
os.environ['KG_PORT'] = '1234'
os.environ['KG_PORT_RETRIES'] = '4321'
os.environ['KG_IP'] = '1.1.1.1'
os.environ['KG_AUTH_TOKEN'] = 'fake-token'
os.environ['KG_ALLOW_CREDENTIALS'] = 'true'
Expand All @@ -44,6 +45,7 @@ def test_config_env_vars(self):
app = KernelGatewayApp()

self.assertEqual(app.port, 1234)
self.assertEqual(app.port_retries, 4321)
self.assertEqual(app.ip, '1.1.1.1')
self.assertEqual(app.auth_token, 'fake-token')
self.assertEqual(app.allow_credentials, 'true')
Expand Down

0 comments on commit 0f197ba

Please sign in to comment.