Skip to content

Commit

Permalink
Merge pull request #17 from voila-gallery/service
Browse files Browse the repository at this point in the history
Add a gallery JupyterHub service
  • Loading branch information
jtpio authored Jun 13, 2019
2 parents 160eea0 + 04bc922 commit ed7c9a6
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 37 deletions.
9 changes: 8 additions & 1 deletion tljh-voila-gallery/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,12 @@
"console_scripts": ["build-gallery-images = tljh_voila_gallery.build_images:main"]
},
packages=find_packages(),
include_package_data=True
include_package_data=True,
install_requires=[
# These get installed into the hub environment
'dockerspawner',
'jupyter-repo2docker',
'binderhub',
'nullauthenticator'
]
)
66 changes: 37 additions & 29 deletions tljh-voila-gallery/tljh_voila_gallery/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import socket
import sys
import os
import jinja2
from pkg_resources import resource_stream, resource_filename
Expand Down Expand Up @@ -29,26 +30,41 @@ def options_form(spawner):

@hookimpl
def tljh_custom_jupyterhub_config(c):
# Since dockerspawner isn't available at import time
from dockerspawner import DockerSpawner
from nullauthenticator import NullAuthenticator
class GallerySpawner(DockerSpawner):
def options_from_form(self, formdata):
options = {}
if 'example' in formdata:
options['example'] = formdata['example'][0]
return options
# FIXME: What to do about idle culling?!
cmd = 'jupyter-notebook'

events = False

def get_args(self):
args = [
'--ip=0.0.0.0',
'--port=%i' % self.port,
'--NotebookApp.base_url=%s' % self.server.base_url,
'--NotebookApp.token=%s' % self.user_options['token'],
'--NotebookApp.trust_xheaders=True',
]
return args + self.args

def start(self):
gallery = get_gallery()
examples = gallery['examples']
chosen_example = self.user_options['example']
assert chosen_example in examples
self.default_url = examples[chosen_example]['url']
self.image = examples[chosen_example]['image']
if 'token' not in self.user_options:
raise web.HTTPError(400, "token required")
if 'image' not in self.user_options:
raise web.HTTPError(400, "image required")
self.image = self.user_options['image']
return super().start()


class GalleryAuthenticator(NullAuthenticator):
auto_login = True

def login_url(self, base_url):
return '/services/gallery'

c.JupyterHub.spawner_class = GallerySpawner
c.JupyterHub.authenticator_class = 'tmpauthenticator.TmpAuthenticator'
c.JupyterHub.authenticator_class = GalleryAuthenticator

c.JupyterHub.hub_connect_ip = socket.gethostname()

Expand All @@ -57,23 +73,15 @@ def start(self):

# rm containers when they stop
c.DockerSpawner.remove = True
# Disabled until we fix it in TmpAuthenticator
# c.TmpAuthenticator.force_new_server = True

c.DockerSpawner.cmd = ['jupyterhub-singleuser']

# Override JupyterHub template
c.JupyterHub.template_paths = [TEMPLATES_PATH]

c.Spawner.options_form = options_form


@hookimpl
def tljh_extra_hub_pip_packages():
return [
'dockerspawner',
'git+https://github.com/jupyter/repo2docker.git@f19e159dfe1006dbd82c7728e15cdd19751e8aec'
]
c.JupyterHub.services = [{
'name': 'gallery',
'admin': True,
'url': 'http://127.0.0.1:9888',
'command': [
sys.executable, '-m', 'tljh_voila_gallery.gallery'
]
}]

@hookimpl
def tljh_extra_apt_packages():
Expand Down
62 changes: 62 additions & 0 deletions tljh-voila-gallery/tljh_voila_gallery/gallery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import os
from tornado import ioloop, web
from pkg_resources import resource_stream, resource_filename
from ruamel.yaml import YAML
from jinja2 import PackageLoader, Environment
import json
from binderhub.launcher import Launcher
from urllib.parse import urljoin, urlencode

yaml = YAML()

# Read gallery.yaml on each spawn. If this gets too expensive, cache it here
def get_gallery():
with resource_stream(__name__, 'gallery.yaml') as f:
return yaml.load(f)

templates = Environment(loader=PackageLoader('tljh_voila_gallery', 'templates'))

class GalleryHandler(web.RequestHandler):
def get(self):
gallery_template = templates.get_template('gallery-examples.html')
gallery = get_gallery()

self.write(gallery_template.render(
url=self.request.full_url(),
examples=gallery.get('examples', [])
))

async def post(self):
gallery = get_gallery()

example_name = self.get_body_argument('example')

example = gallery['examples'][example_name]


launcher = Launcher(
hub_api_token=os.environ['JUPYTERHUB_API_TOKEN'],
hub_url=os.environ['JUPYTERHUB_BASE_URL']
)
response = await launcher.launch(
example['image'],
launcher.unique_name_from_repo(example['repo_url'])
)
redirect_url = urljoin(
response['url'],
example['url']
) + '?' + urlencode({'token': response['token']})
self.redirect(redirect_url)


def make_app():
return web.Application([
(os.environ['JUPYTERHUB_SERVICE_PREFIX'] + '/?', GalleryHandler),
])

if __name__ == "__main__":
if not os.environ['JUPYTERHUB_API_URL'].endswith('/'):
os.environ['JUPYTERHUB_API_URL'] = os.environ['JUPYTERHUB_API_URL'] + '/'
app = make_app()
app.listen(9888)
ioloop.IOLoop.current().start()
6 changes: 3 additions & 3 deletions tljh-voila-gallery/tljh_voila_gallery/gallery.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ examples:
title: country-indicators
description: Explore the correlations between indicators of development using matplotlib and ipywidgets
image: country-indicators:latest
url: /voila/render/index.ipynb
url: voila/render/index.ipynb
repo_url: https://github.com/voila-gallery/voila-gallery-country-indicators
image_url: https://i.imgur.com/IeG75O3.png
gaussian-density:
title: gaussian-density
description: Explore the normal distribution interactively with bqplot
image: gaussian-density:latest
url: /voila/render/index.ipynb
url: voila/render/index.ipynb
repo_url: https://github.com/voila-gallery/gaussian-density
image_url: https://i.imgur.com/J1Mj6rc.png
render-stl:
title: render-stl
description: Explore STL files with ipyvolume
image: render-stl:latest
url: /voila/render/index.ipynb
url: voila/render/index.ipynb
repo_url: https://github.com/voila-gallery/render-stl
image_url: https://github.com/voila-gallery/render-stl/raw/master/thumbnail.png
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
{% extends "page.html" %}

{% block body %}
<form enctype="multipart/form-data" id="spawn_form" action="{{url}}" method="post" role="form">
<div class="row">
{% for example_name, info in examples.items() %}
<div class="col-md-4">
Expand All @@ -18,3 +22,7 @@ <h4>{{ info.title }}</h4>
</div>
{% endfor %}
</div>

</form>

{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,8 @@ <h1 class="jumbotron-heading">Voila Dashboards Gallery</h1>

<div class="album py-5 bg-light">
<div class="container">

<form enctype="multipart/form-data" id="spawn_form" action="{{url}}" method="post" role="form">
{{spawner_options_form | safe}}
</form>
{% block body %}
{% endblock %}
</div>
</div>

Expand Down

0 comments on commit ed7c9a6

Please sign in to comment.