import json import os from urllib.parse import urlencode, urljoin import docker from binderhub.launcher import Launcher from jinja2 import Environment, PackageLoader from pkg_resources import resource_filename, resource_stream from ruamel.yaml import YAML from tornado import ioloop, web yaml = YAML() def _get_docker_images(): client = docker.from_env() image_tags = [ tag for image in client.images.list(filters={"dangling": False}) for tag in image.tags ] return set(image_tags) # Read gallery.yaml on each spawn. If this gets too expensive, cache it here # Only keep the examples that have a corresponding Docker image def get_gallery(): with resource_stream(__name__, 'gallery.yaml') as f: gallery = yaml.load(f) images = _get_docker_images() gallery['examples'] = { name: ex for name, ex in gallery.get('examples', {}).items() if f"{name}:latest" in images } return gallery 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.uri, examples=gallery.get('examples', []), static_url=self.static_url )) 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( f'{example_name}:latest', 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(): service_prefix = os.environ['JUPYTERHUB_SERVICE_PREFIX'] app_settings = { 'static_path': os.path.join(os.path.dirname(__file__), 'static'), 'static_url_prefix': f'{service_prefix}/static/' } return web.Application([ (rf'{service_prefix}?', GalleryHandler), (rf'{service_prefix}static/(.*)', web.StaticFileHandler, { 'path': app_settings['static_path'] }) ], **app_settings) 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()