Permalink
Please sign in to comment.
Browse files
Merge pull request #205 from StrausMG/byor
Bring Your Own Resources
- Loading branch information...
Showing
with
192 additions
and 7 deletions.
- +1 −0 .gitignore
- +5 −0 etc/byor_config.py
- +2 −1 everware/__init__.py
- +88 −0 everware/byor_spawner.py
- +11 −6 everware/spawner.py
- +85 −0 share/static/html/_byor_options_form.html
| @@ -0,0 +1,5 @@ | ||
| +c = get_config() | ||
| +load_subconfig('etc/base_config.py') | ||
| +load_subconfig('etc/github_auth.py') | ||
| + | ||
| +c.JupyterHub.spawner_class = 'everware.ByorDockerSpawner' |
| @@ -0,0 +1,88 @@ | ||
| +from os.path import join as pjoin | ||
| + | ||
| +import docker | ||
| +from docker.errors import DockerException | ||
| +from traitlets import Int | ||
| +from tornado import gen | ||
| + | ||
| +from .spawner import CustomDockerSpawner | ||
| + | ||
| + | ||
| +class ByorDockerSpawner(CustomDockerSpawner): | ||
| + def __init__(self, **kwargs): | ||
| + CustomDockerSpawner.__init__(self, **kwargs) | ||
| + self._byor_client = None | ||
| + if self.options_form == self._options_form_default(): | ||
| + with open(pjoin(self.config['JupyterHub']['template_paths'][0], | ||
| + '_byor_options_form.html')) as form: | ||
| + ByorDockerSpawner.options_form = form.read() | ||
| + | ||
| + @property | ||
| + def client(self): | ||
| + if self._byor_client is not None: | ||
| + return self._byor_client | ||
| + return super(ByorDockerSpawner, self).client | ||
| + | ||
| + @property | ||
| + def byor_is_used(self): | ||
| + return self.user_options.get('byor_is_needed', False) | ||
| + | ||
| + def _reset_byor(self): | ||
| + self.container_ip = str(self.__class__.container_ip) | ||
| + self._byor_client = None | ||
| + | ||
| + byor_timeout = Int(20, min=1, config=True, | ||
| + help='Timeout for connection to BYOR Docker daemon') | ||
| + | ||
| + def options_from_form(self, formdata): | ||
| + options = {} | ||
| + options['byor_is_needed'] = formdata.pop('byor_is_needed', [''])[0].strip() == 'on' | ||
| + for field in ('byor_docker_ip', 'byor_docker_port'): | ||
| + options[field] = formdata.pop(field, [''])[0].strip() | ||
| + options.update( | ||
| + super(ByorDockerSpawner, self).options_from_form(formdata) | ||
| + ) | ||
| + return options | ||
| + | ||
| + @gen.coroutine | ||
| + def _configure_byor(self): | ||
| + """Configure BYOR settings or reset them if BYOR is not needed.""" | ||
| + if not self.byor_is_used: | ||
| + self._reset_byor() | ||
| + return | ||
| + byor_ip = self.user_options['byor_docker_ip'] | ||
| + byor_port = self.user_options['byor_docker_port'] | ||
| + try: | ||
| + # version='auto' causes a connection to the daemon. | ||
| + # That's why the method must be a coroutine. | ||
| + self._byor_client = docker.Client('{}:{}'.format(byor_ip, byor_port), | ||
| + version='auto', | ||
| + timeout=self.byor_timeout) | ||
| + except DockerException as e: | ||
| + self._is_failed = True | ||
| + message = str(e) | ||
| + if 'ConnectTimeoutError' in message: | ||
| + log_message = 'Connection to the Docker daemon took too long (> {} secs)'.format( | ||
| + self.byor_timeout | ||
| + ) | ||
| + notification_message = 'BYOR timeout limit {} exceeded'.format(self.byor_timeout) | ||
| + else: | ||
| + log_message = "Failed to establish connection with the Docker daemon" | ||
| + notification_message = log_message | ||
| + self._add_to_log(log_message, level=2) | ||
| + yield self.notify_about_fail(notification_message) | ||
| + self._is_building = False | ||
| + raise | ||
| + | ||
| + self.container_ip = byor_ip | ||
| + | ||
| + @gen.coroutine | ||
| + def _prepare_for_start(self): | ||
| + super(ByorDockerSpawner, self)._prepare_for_start() | ||
| + yield self._configure_byor() | ||
| + | ||
| + @gen.coroutine | ||
| + def start(self, image=None): | ||
| + yield self._prepare_for_start() | ||
| + ip_port = yield self._start(image) | ||
| + return ip_port |
| @@ -0,0 +1,85 @@ | ||
| +<div style="margin-bottom: 0px;"> | ||
| +<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" style="width: 50%;"> | ||
| +<input | ||
| + id="repository_input" | ||
| + type="text" | ||
| + autocapitalize="off" | ||
| + autocorrect="off" | ||
| + name="repository_url" | ||
| + tabindex="1" | ||
| + autofocus="autofocus" | ||
| + class="mdl-textfield__input" | ||
| +style="margin-bottom: 3px;" /> | ||
| +<label class="mdl-textfield__label" for="repository_input">Git repository</label> | ||
| +</div> | ||
| + | ||
| +<label for="need_remove" class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" > | ||
| +<input type="checkbox" | ||
| + name="need_remove" | ||
| + class="mdl-checkbox__input" | ||
| + id="need_remove" | ||
| + checked /> | ||
| +<span class="mdl-checkbox__label">Remove previous container if it exists</span> | ||
| +</label> | ||
| + | ||
| +<label for="byor_is_needed" class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" > | ||
| +<input type="checkbox" | ||
| + name="byor_is_needed" | ||
| + class="mdl-checkbox__input" | ||
| + id="byor_is_needed" | ||
| + unchecked /> | ||
| +<span class="mdl-checkbox__label">I want to run the repository on my own server</span> | ||
| +</label> | ||
| +<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> | ||
| +<script> | ||
| +window.onload = function () { | ||
| + byor_is_needed = document.getElementById("byor_is_needed"); | ||
| + byor_input = document.getElementById("byor_input"); | ||
| + if (byor_is_needed.checked) { | ||
| + byor_input.style.display = "inline"; | ||
| + } else { | ||
| + byor_input.style.display = "none"; | ||
| + } | ||
| +} | ||
| +$('#byor_is_needed').on('change', function() { | ||
| + $('#byor_input').toggle(speed='normal'); | ||
| +}); | ||
| +</script> | ||
| + | ||
| +<div id='byor_input' style="display: none;"> | ||
| +<p style="margin-bottom: 0px;"> | ||
| + For a successful run <a href="https://www.docker.com/" target="_black">Docker</a> | ||
| + must be installed on your server.<br /> | ||
| + Enter IP address and port of the Docker daemon running on your server.<br /> | ||
| + (Click <a href="https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-socket-option" | ||
| + target="_blank"> | ||
| + here</a> to learn how to run Docker daemon on a particular port) | ||
| +</p> | ||
| +<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" | ||
| + style="width: 9%; margin-right: 20px;"> | ||
| + <input | ||
| + id="byor_docker_ip" | ||
| + type="text" | ||
| + autocapitalize="off" | ||
| + autocorrect="off" | ||
| + name="byor_docker_ip" | ||
| + tabindex="1" | ||
| + autofocus="autofocus" | ||
| + class="mdl-textfield__input"/> | ||
| + <label class="mdl-textfield__label" for="byor_docker_ip">ip</label> | ||
| +</div> | ||
| +<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" | ||
| + style="width: 9%;"> | ||
| + <input | ||
| + id="byor_docker_port" | ||
| + type="text" | ||
| + autocapitalize="off" | ||
| + autocorrect="off" | ||
| + name="byor_docker_port" | ||
| + tabindex="1" | ||
| + autofocus="autofocus" | ||
| + class="mdl-textfield__input"/> | ||
| + <label class="mdl-textfield__label" for="byor_docker_port">port</label> | ||
| +</div> | ||
| +</div> | ||
| +</div> |
0 comments on commit
7e9dead