diff --git a/etc/base_config.py b/etc/base_config.py index 3cfed09..a043bb1 100644 --- a/etc/base_config.py +++ b/etc/base_config.py @@ -10,6 +10,7 @@ c.Spawner.debug = True c.Spawner.start_timeout = 1000 c.Spawner.http_timeout = 60 +c.Spawner.poll_interval = 5 c.Spawner.remove_containers = True c.Spawner.tls_assert_hostname = False c.Spawner.use_docker_client_env = True diff --git a/everware/git_processor.py b/everware/git_processor.py index 6471aa9..00cfb0e 100644 --- a/everware/git_processor.py +++ b/everware/git_processor.py @@ -2,6 +2,7 @@ import git from concurrent.futures import ThreadPoolExecutor from tornado import gen +import os class GitMixin: @@ -92,6 +93,10 @@ def git(self, method, *args, **kwargs): @gen.coroutine def prepare_local_repo(self): + """Returns False if there is no Dockerfile in repo + + True otherwise + """ clone_url = self.repo_url_with_token yield self.git('clone', clone_url, self._repo_dir) repo = git.Repo(self._repo_dir) @@ -99,6 +104,18 @@ def prepare_local_repo(self): self._repo_sha = repo.rev_parse('HEAD').hexsha self._branch_name = repo.active_branch.name + dockerfile_path = os.join(self._repo_dir, 'Dockerfile') + if not os.path.isfile(dockerfile_path): + with open(dockerfile_path, 'w') as fout: + fout.writelines([ + 'FROM everware/datascience-jupyter:latest\n', + 'MAINTAINER Alexander Tiunov ' + ]) + return False + else: + return True + + @property def escaped_repo_url(self): repo_url = re.sub(r'^.+?://', '', self._processed_repo_url) diff --git a/everware/spawner.py b/everware/spawner.py index 5ecd37f..6e5a140 100755 --- a/everware/spawner.py +++ b/everware/spawner.py @@ -145,7 +145,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, @@ -166,7 +166,7 @@ def is_up(self): @gen.coroutine def get_container(self): - self.log.debug("Getting container: %s", self.container_name) + # self.log.debug("Getting container: %s", self.container_name) try: container = yield self.docker( 'inspect_container', self.container_name @@ -174,7 +174,7 @@ def get_container(self): self.container_id = container['Id'] except APIError as e: if e.response.status_code == 404: - self.log.info("Container '%s' is gone", self.container_name) + # self.log.info("Container '%s' is gone", self.container_name) container = None # my container is gone, forget my id self.container_id = '' @@ -238,7 +238,9 @@ def build_image(self): self.repo_url )) self.log.info('Cloning repo %s' % self.repo_url) - yield self.prepare_local_repo() + dockerfile_exists = yield self.prepare_local_repo() + if not dockerfile_exists: + self._add_to_log('No dockerfile. Use the default one') # use git repo URL and HEAD commit sha to derive # the image name @@ -291,6 +293,31 @@ def remove_old_container(self): except APIError as e: self.log.info("Can't erase container %s due to %s" % (self.container_name, e)) + @gen.coroutine + def wait_up(self): + # copied from jupyterhub, because if user's server didn't appear, it + # means that spawn was unsuccessful, need to set is_failed + try: + yield self.user.server.wait_up(http=True, timeout=self.http_timeout) + ip, port = yield from self.get_ip_and_port() + self.user.server.ip = ip + self.user.server.port = port + except TimeoutError: + self._is_failed = True + self._add_to_log('Server never showed up after {} seconds'.format(self.http_timeout)) + self.log.info("{user}'s server never showed up after {timeout} seconds".format( + user=self.user.name, + timeout=self.http_timeout + )) + yield self.notify_about_fail("Http timeout limit %.3f exceeded" % self.http_timeout) + raise + except Exception as e: + self._is_failed = True + message = str(e) + self._add_to_log('Something went wrong during waiting for server. Error: %s' % message) + yield self.notify_about_fail(message) + raise e + @gen.coroutine def start(self, image=None):