Permalink
Please sign in to comment.
Browse files
Merge pull request #186 from astiunov/master
Custom service support and some improvements
- Loading branch information...
Showing
with
349 additions
and 19 deletions.
- +8 −0 Makefile
- +1 −1 build_tools/frontend_test_normal_config.py
- +1 −0 build_tools/test_frontend.sh
- +130 −0 etc/nginx_config.conf
- +142 −0 everware/container_handler.py
- +14 −4 everware/git_processor.py
- +23 −12 everware/spawner.py
- +6 −0 everware/user_spawn_handler.py
- +1 −1 everware/user_wait_handler.py
- +19 −0 frontend_tests/normal_scenarios.py
- +2 −0 frontend_tests/test_generator.py
- +2 −1 requirements.txt
| @@ -0,0 +1,130 @@ | ||
| +user www-data; | ||
| +worker_processes 4; | ||
| +pid /run/nginx.pid; | ||
| + | ||
| +events { | ||
| + worker_connections 768; | ||
| + # multi_accept on; | ||
| +} | ||
| + | ||
| +http { | ||
| + upstream custom_service { | ||
| + server 127.0.0.1:8000; | ||
| + } | ||
| + server { | ||
| + listen %PORT%; | ||
| + server_name docker_proxy; | ||
| + | ||
| + if ($cookie_everware_custom_service_token != "%TOKEN%") { | ||
| + return 403; | ||
| + } | ||
| + | ||
| + location / { | ||
| + proxy_pass http://custom_service; | ||
| + proxy_set_header Host $host; | ||
| + proxy_set_header Upgrade $http_upgrade; | ||
| + proxy_set_header Connection "upgrade"; | ||
| + proxy_set_header X-Real-IP $remote_addr; | ||
| + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||
| + } | ||
| + | ||
| + location = /user/%USERNAME% { | ||
| + return 302 /user/%USERNAME%/; | ||
| + } | ||
| + | ||
| + location /user/%USERNAME%/ { | ||
| + proxy_pass http://custom_service/; | ||
| + proxy_set_header Host $host; | ||
| + proxy_set_header Upgrade $http_upgrade; | ||
| + proxy_set_header Connection "upgrade"; | ||
| + proxy_set_header X-Real-IP $remote_addr; | ||
| + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||
| + } | ||
| + } | ||
| + | ||
| + | ||
| + ## | ||
| + # Basic Settings | ||
| + ## | ||
| + | ||
| + sendfile on; | ||
| + tcp_nopush on; | ||
| + tcp_nodelay on; | ||
| + keepalive_timeout 65; | ||
| + types_hash_max_size 2048; | ||
| + # server_tokens off; | ||
| + | ||
| + # server_names_hash_bucket_size 64; | ||
| + # server_name_in_redirect off; | ||
| + | ||
| + include /etc/nginx/mime.types; | ||
| + default_type application/octet-stream; | ||
| + | ||
| + ## | ||
| + # Logging Settings | ||
| + ## | ||
| + | ||
| + access_log /var/log/nginx/access.log; | ||
| + error_log /var/log/nginx/error.log; | ||
| + | ||
| + ## | ||
| + # Gzip Settings | ||
| + ## | ||
| + | ||
| + gzip on; | ||
| + gzip_disable "msie6"; | ||
| + | ||
| + # gzip_vary on; | ||
| + # gzip_proxied any; | ||
| + # gzip_comp_level 6; | ||
| + # gzip_buffers 16 8k; | ||
| + # gzip_http_version 1.1; | ||
| + # gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; | ||
| + | ||
| + ## | ||
| + # nginx-naxsi config | ||
| + ## | ||
| + # Uncomment it if you installed nginx-naxsi | ||
| + ## | ||
| + | ||
| + #include /etc/nginx/naxsi_core.rules; | ||
| + | ||
| + ## | ||
| + # nginx-passenger config | ||
| + ## | ||
| + # Uncomment it if you installed nginx-passenger | ||
| + ## | ||
| + | ||
| + #passenger_root /usr; | ||
| + #passenger_ruby /usr/bin/ruby; | ||
| + | ||
| + ## | ||
| + # Virtual Host Configs | ||
| + ## | ||
| + | ||
| + include /etc/nginx/conf.d/*.conf; | ||
| + include /etc/nginx/sites-enabled/*; | ||
| +} | ||
| + | ||
| + | ||
| +#mail { | ||
| +# # See sample authentication script at: | ||
| +# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript | ||
| +# | ||
| +# # auth_http localhost/auth.php; | ||
| +# # pop3_capabilities "TOP" "USER"; | ||
| +# # imap_capabilities "IMAP4rev1" "UIDPLUS"; | ||
| +# | ||
| +# server { | ||
| +# listen localhost:110; | ||
| +# protocol pop3; | ||
| +# proxy on; | ||
| +# } | ||
| +# | ||
| +# server { | ||
| +# listen localhost:143; | ||
| +# protocol imap; | ||
| +# proxy on; | ||
| +# } | ||
| +#} | ||
| + |
| @@ -0,0 +1,142 @@ | ||
| +from dockerspawner import DockerSpawner | ||
| +from tornado import gen | ||
| +import re | ||
| +import os.path | ||
| +import sys | ||
| +import yaml | ||
| + | ||
| +class ShellCommand: | ||
| + def __init__(self, commands=[]): | ||
| + self.commands = commands | ||
| + | ||
| + def add_commands(self, commands_list): | ||
| + self.commands.extend(commands_list) | ||
| + | ||
| + def extend(self, command): | ||
| + self.add_commands(command.commands) | ||
| + | ||
| + def get_single_command(self): | ||
| + return 'bash -c "{}"' .format(' && '.join(self.commands)) | ||
| + | ||
| +def make_git_command(repourl, commit_sha): | ||
| + return ShellCommand([ | ||
| + 'apt-get install -y git', | ||
| + 'git clone {} /notebooks'.format(repourl), | ||
| + 'cd /notebooks', | ||
| + 'git reset --hard {}'.format(commit_sha) | ||
| + ]) | ||
| + | ||
| +def make_nginx_start_command(nginx_config): | ||
| + return ShellCommand([ | ||
| + 'apt-get install -y nginx', | ||
| + "python -c 'print(\\\"{}\\\")' >/etc/nginx/nginx.conf".format(nginx_config), | ||
| + 'service nginx restart', | ||
| + 'cat /etc/nginx/nginx.conf' | ||
| + ]) | ||
| + | ||
| +def make_default_start_command(env): | ||
| + return ShellCommand([ | ||
| + 'jupyterhub-singleuser --port=8888 --ip=0.0.0.0 --allow-root --user={} --cookie-name={} --base-url={} '.format( | ||
| + env['JPY_USER'], | ||
| + env['JPY_COOKIE_NAME'], | ||
| + env['JPY_BASE_URL'] | ||
| + ) + '--hub-prefix={} --hub-api-url={} --notebook-dir=/notebooks'.format( | ||
| + env['JPY_HUB_PREFIX'], | ||
| + env['JPY_HUB_API_URL'] | ||
| + ) | ||
| + ]) | ||
| + | ||
| +def make_custom_start_command(command): | ||
| + return ShellCommand([command]) | ||
| + | ||
| + | ||
| +class ContainerHandler(DockerSpawner): | ||
| + def parse_config(self, directory): | ||
| + self.everware_config = { | ||
| + 'everware_based': True | ||
| + } | ||
| + try: | ||
| + with open(os.path.join(directory, 'everware.yml')) as fin: | ||
| + try: | ||
| + self.everware_config = yaml.load(fin) | ||
| + except yaml.YAMLError as exc: | ||
| + self.log.warn('Fail reading everware.yml: {}'.format(exc)) | ||
| + except IOError: | ||
| + self.log.info('No everware.yaml in repo') | ||
| + | ||
| + @gen.coroutine | ||
| + def prepare_container(self): | ||
| + if self.everware_config.get('everware_based', True): | ||
| + return | ||
| + container = yield self.get_container() | ||
| + was_cloned = yield self._check_for_git_compatibility(container) | ||
| + if not was_cloned: | ||
| + command = make_git_command(self.repo_url_with_token, self.commit_sha) | ||
| + setup = yield self.docker( | ||
| + 'exec_create', | ||
| + container=container, | ||
| + cmd=command.get_single_command() | ||
| + ) | ||
| + output = yield self.docker('exec_start', exec_id=setup['Id']) | ||
| + | ||
| + | ||
| + @gen.coroutine | ||
| + def start(self, image=None): | ||
| + self.parse_config(self._repo_dir) | ||
| + start_command = None | ||
| + extra_create_kwargs = { | ||
| + 'ports': [self.container_port] | ||
| + } | ||
| + if not self.everware_config.get('everware_based', True): | ||
| + start_command = make_git_command(self.repo_url_with_token, self.commit_sha) | ||
| + if 'start_command' in self.everware_config: | ||
| + nginx_config = self._get_nginx_config( | ||
| + 8888, | ||
| + self.custom_service_token(), | ||
| + self.user.name | ||
| + ) | ||
| + start_command.extend(make_nginx_start_command(nginx_config)) | ||
| + start_command.extend(make_custom_start_command(self.everware_config['start_command'])) | ||
| + else: | ||
| + start_command.extend(make_default_start_command(self.get_env())) | ||
| + extra_create_kwargs.update({ | ||
| + 'command': start_command.get_single_command() | ||
| + }) | ||
| + | ||
| + extra_host_config = { | ||
| + 'port_bindings': { | ||
| + self.container_port: (self.container_ip,) | ||
| + } | ||
| + } | ||
| + ip, port = yield DockerSpawner.start(self, image, | ||
| + extra_create_kwargs=extra_create_kwargs, | ||
| + extra_host_config=extra_host_config) | ||
| + return ip, port | ||
| + | ||
| + def _encode_conf(self, s): | ||
| + return ''.join('\\x' + hex(ord(x))[2:].zfill(2) for x in s) | ||
| + | ||
| + def _get_nginx_config(self, port, token, username): | ||
| + try: | ||
| + result = '' | ||
| + with open('etc/nginx_config.conf') as fin: | ||
| + for line in fin: | ||
| + result += self._encode_conf( | ||
| + line.replace('%TOKEN%', token) | ||
| + .replace('%USERNAME%', username) | ||
| + .replace('%PORT%', str(port)) | ||
| + ) | ||
| + return result | ||
| + except OSError: | ||
| + self.log.warn('No nginx config') | ||
| + raise | ||
| + | ||
| + @gen.coroutine | ||
| + def _check_for_git_compatibility(self, container): | ||
| + setup = yield self.docker( | ||
| + 'exec_create', | ||
| + container=container, | ||
| + cmd="bash -c \"ls / | grep -E '\\bnotebooks\\b'\"" | ||
| + ) | ||
| + output = yield self.docker('exec_start', exec_id=setup['Id']) | ||
| + return output != "" |
Oops, something went wrong.
0 comments on commit
c0ed70b