Permalink
...
Checking mergeability…
Don’t worry, you can still create the pull request.
Comparing changes
Open a pull request
- 2 commits
- 3 files changed
- 0 commit comments
- 1 contributor
Unified
Split
Showing
with
32 additions
and 3 deletions.
- +1 −0 etc/base_config.py
- +19 −3 everware/spawner.py
- +12 −0 everware/user_spawn_handler.py
View
1
etc/base_config.py
| @@ -10,6 +10,7 @@ | ||
| c.Spawner.debug = True | ||
| c.Spawner.start_timeout = 1000 | ||
| c.Spawner.http_timeout = 60 | ||
| +c.Spawner.max_users_running = 60 | ||
| c.Spawner.remove_containers = True | ||
| c.Spawner.tls_assert_hostname = False | ||
| c.Spawner.use_docker_client_env = True | ||
View
22
everware/spawner.py
| @@ -18,6 +18,7 @@ | ||
| import ssl | ||
| import json | ||
| import os | ||
| +import sys | ||
| from .image_handler import ImageHandler | ||
| from .git_processor import GitMixin | ||
| @@ -29,6 +30,10 @@ | ||
| class CustomDockerSpawner(DockerSpawner, GitMixin, EmailNotificator): | ||
| + max_users_running = Integer( | ||
| + config=True | ||
| + ) | ||
| + | ||
| def __init__(self, **kwargs): | ||
| self._user_log = [] | ||
| self._is_failed = False | ||
| @@ -144,7 +149,7 @@ def options_from_form(self, formdata): | ||
| def form_repo_url(self): | ||
| """Repository URL as submitted by the user.""" | ||
| return self.user_options.get('repo_url', '') | ||
| - | ||
| + | ||
| @property | ||
| def container_name(self): | ||
| return "{}-{}".format(self.container_prefix, | ||
| @@ -283,6 +288,12 @@ def remove_old_container(self): | ||
| except APIError as e: | ||
| self.log.info("Can't erase container %s due to %s" % (self.container_name, e)) | ||
| + def are_too_many_users(self): | ||
| + if not hasattr(self, 'max_users_running'): | ||
| + return False | ||
| + running_users = self.user_options.get('running_users', -1) | ||
| + self.log.info('{} running_users'.format(running_users)) | ||
| + return running_users >= self.max_users_running | ||
| @gen.coroutine | ||
| def start(self, image=None): | ||
| @@ -292,6 +303,8 @@ def start(self, image=None): | ||
| self._is_building = True | ||
| self._is_empty = False | ||
| try: | ||
| + if self.are_too_many_users(): | ||
| + raise Exception('Sorry, there are too many users') | ||
| f = self.build_image() | ||
| image_name = yield gen.with_timeout( | ||
| timedelta(seconds=self.start_timeout), | ||
| @@ -327,7 +340,10 @@ def start(self, image=None): | ||
| message = "Container doesn't have jupyter-singleuser inside" | ||
| elif 'Cannot locate specified Dockerfile' in message: | ||
| message = "Your repo doesn't include Dockerfile" | ||
| - self._add_to_log('Something went wrong during building. Error: %s' % message) | ||
| + if message.startswith('Sorry'): | ||
| + self._add_to_log(message) | ||
| + else: | ||
| + self._add_to_log('Something went wrong during building. Error: %s' % message) | ||
| yield self.notify_about_fail(message) | ||
| raise e | ||
| finally: | ||
| @@ -379,7 +395,7 @@ def stop(self, now=False): | ||
| "Removing container %s (id: %s)", | ||
| self.container_name, self.container_id[:7]) | ||
| # remove the container, as well as any associated volumes | ||
| - yield self.docker('remove_container', self.container_id, v=True) | ||
| + yield self.docker('remove_container', self.container_id, v=True, force=True) | ||
| self.clear_state() | ||
View
12
everware/user_spawn_handler.py
| @@ -6,6 +6,14 @@ | ||
| class SpawnHandler(default_handlers.SpawnHandler): | ||
| + @gen.coroutine | ||
| + def count_running_users(self): | ||
| + running_users = 0 | ||
| + for user_id, user in self.users.items(): | ||
| + if user.running: | ||
| + running_users += 1 | ||
| + return running_users | ||
| + | ||
| def _render_form(self, message=''): | ||
| user = self.get_current_user() | ||
| metrica = MetricaIdsMixin() | ||
| @@ -22,9 +30,13 @@ def _render_form(self, message=''): | ||
| @gen.coroutine | ||
| def _spawn(self, user, form_options): | ||
| + running_users = yield self.count_running_users() | ||
| self.redirect('/user/%s' % user.name) | ||
| try: | ||
| options = user.spawner.options_from_form(form_options) | ||
| + options.update({ | ||
| + 'running_users': running_users | ||
| + }) | ||
| yield self.spawn_single_user(user, options=options) | ||
| # if user set another access token (for example he logged with github | ||
| # and clones from bitbucket) | ||