Permalink
Browse files

Saving states

  • Loading branch information...
1 parent c0ed70b commit fd801dd63f1ef3f6cc327ef5af5d155e6bec0fc5 @vchagaev vchagaev committed May 14, 2017
Showing with 53 additions and 3 deletions.
  1. +2 −1 bower.json
  2. +3 −0 etc/base_config.py
  3. +25 −0 everware/home_handler.py
  4. +9 −1 everware/spawner.py
  5. +13 −1 share/static/html/home.html
  6. +1 −0 share/static/html/page.html
View
@@ -7,6 +7,7 @@
"jquery": "components/jquery#~2.0", "jquery": "components/jquery#~2.0",
"moment": "~2.7", "moment": "~2.7",
"requirejs": "~2.1", "requirejs": "~2.1",
- "material-design-lite": "^1.1.3" + "material-design-lite": "^1.1.3",
+ "clipboard": "^1.6.1"
} }
} }
View
@@ -14,6 +14,9 @@
c.Spawner.remove_containers = True c.Spawner.remove_containers = True
c.Spawner.tls_assert_hostname = False c.Spawner.tls_assert_hostname = False
c.Spawner.use_docker_client_env = True c.Spawner.use_docker_client_env = True
+# give users an opportunity to restore any images via docker or not:
+# c.Spawner.user_images_check = False
+
# c.Authenticator.admin_users = {'anaderi', 'astiunov'} # c.Authenticator.admin_users = {'anaderi', 'astiunov'}
# The docker containers need access to the Hub API, so the default # The docker containers need access to the Hub API, so the default
View
@@ -8,6 +8,7 @@
from . import __version__ from . import __version__
from .github_agent import * from .github_agent import *
from .metrica import MetricaIdsMixin from .metrica import MetricaIdsMixin
+from datetime import datetime
@gen.coroutine @gen.coroutine
def is_repository_changed(user): def is_repository_changed(user):
@@ -43,7 +44,9 @@ def get(self):
do_fork = self.get_argument('do_fork', False) do_fork = self.get_argument('do_fork', False)
do_push = self.get_argument('do_push', False) do_push = self.get_argument('do_push', False)
+ do_commit = self.get_argument('do_commit', False)
notify_message = self.get_argument('message', '') notify_message = self.get_argument('message', '')
+ notify_url_to_image = self.get_argument('url_with_image', '')
if repourl: if repourl:
self.redirect(url_concat( self.redirect(url_concat(
url_path_join(self.hub.server.base_url, 'spawn'), all_arguments url_path_join(self.hub.server.base_url, 'spawn'), all_arguments
@@ -58,6 +61,27 @@ def get(self):
branch_name = user.spawner.branch_name branch_name = user.spawner.branch_name
commit_sha = user.spawner.commit_sha commit_sha = user.spawner.commit_sha
repo_url = user.spawner.repo_url repo_url = user.spawner.repo_url
+
+ if user.running and do_commit:
+ spawner = user.spawner
+ image_tag = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
+ image_name = 'everware_image/' + spawner.escaped_name + '/' + spawner.escaped_repo_url + '_' + spawner.container_id
+ host_with_protocol = self.request.protocol + '://' + self.request.host
+ url_with_image = url_concat(host_with_protocol + '/hub/spawn', dict(repourl='docker:' + image_name + ':' + image_tag))
+
+ self.log.info('Will commit %s' % url_with_image)
+
+ spawner.client.commit(
+ container=spawner.container_id,
+ repository=image_name,
+ tag=image_tag,
+ message='Commit from control panel',
+ author=spawner.escaped_name
+ )
+
+ self.redirect(url_concat('/hub/home', dict(url_with_image=url_with_image)))
+ return
+
if user.running and getattr(user, 'login_service', '') == 'github': if user.running and getattr(user, 'login_service', '') == 'github':
if do_fork: if do_fork:
self.log.info('Will fork %s' % user.spawner.repo_url) self.log.info('Will fork %s' % user.spawner.repo_url)
@@ -104,6 +128,7 @@ def get(self):
branch_name=branch_name, branch_name=branch_name,
commit_sha=commit_sha, commit_sha=commit_sha,
notify_message=notify_message, notify_message=notify_message,
+ notify_url_to_image=notify_url_to_image,
version=__version__, version=__version__,
g_analitics_id=g_id, g_analitics_id=g_id,
ya_metrica_id=ya_id ya_metrica_id=ya_id
View
@@ -14,7 +14,8 @@
from traitlets import ( from traitlets import (
Integer, Integer,
Unicode, Unicode,
- Int + Int,
+ Bool
) )
from tornado import gen from tornado import gen
from tornado.httpclient import HTTPError from tornado.httpclient import HTTPError
@@ -251,12 +252,19 @@ def wait_up(self):
yield self.notify_about_fail(message) yield self.notify_about_fail(message)
raise e raise e
+ user_images_check = Bool(default_value=True, config=True, help="If True, users will be able restore only own images")
@gen.coroutine @gen.coroutine
def build_image(self): def build_image(self):
"""download the repo and build a docker image if needed""" """download the repo and build a docker image if needed"""
if self.form_repo_url.startswith('docker:'): if self.form_repo_url.startswith('docker:'):
image_name = self.form_repo_url.replace('docker:', '') image_name = self.form_repo_url.replace('docker:', '')
+
+ if image_name.startswith('everware_image') and not self.user.admin and self.user_images_check:
+ images_user = image_name.split('/')[1]
+ if self.escaped_name != images_user:
+ raise Exception('Access denied. Image %s is not yours.' % image_name)
+
image = yield self.get_image(image_name) image = yield self.get_image(image_name)
if image is None: if image is None:
raise Exception('Image %s doesn\'t exist' % image_name) raise Exception('Image %s doesn\'t exist' % image_name)
@@ -21,6 +21,7 @@
{% else %} {% else %}
<p>You don't have a repository with the same name. Do you want to <a id="push" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored small-button" href="/hub/home?do_fork=1">fork</a> it?</p> <p>You don't have a repository with the same name. Do you want to <a id="push" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored small-button" href="/hub/home?do_fork=1">fork</a> it?</p>
{% endif %} {% endif %}
+ <p>Also you can save current state. You will be able easily restore your work later. Just <a id="commit" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--colored small-button" href="/hub/home?do_commit=1">commit</a> it!</p>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@@ -70,12 +71,23 @@
> >
{{notify_message}} {{notify_message}}
</div> </div>
+ {% if notify_url_to_image %}
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" style="width: 700px">
+ <input class="mdl-textfield__input" type="text" id="notify-url" value="{{notify_url_to_image}}" readonly>
+ <label class="mdl-textfield__label" for="notify-url">Your link to restore state</label>
+ </div>
+ <button class="mdl-button mdl-js-button mdl-button--icon notify-url--button notify-url__button" data-clipboard-target="#notify-url" title="Copy to clipboard">
+ <i class="material-icons">content_copy</i>
+ </button>
+ {% endif %}
</div> </div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script type="text/javascript"> <script type="text/javascript">
- require(["home"]); + require(["home", "clipboard"], function (home, Clipboard) {
+ new Clipboard('.notify-url__button');
+ });
</script> </script>
{% endblock %} {% endblock %}
@@ -56,6 +56,7 @@ <h4 class="modal-title" id="{{key}}-label">{{title}}</h4>
jquery: '../components/jquery/jquery.min', jquery: '../components/jquery/jquery.min',
bootstrap: '../components/bootstrap/js/bootstrap.min', bootstrap: '../components/bootstrap/js/bootstrap.min',
moment: "../components/moment/moment", moment: "../components/moment/moment",
+ clipboard: "../components/clipboard/dist/clipboard.min",
}, },
shim: { shim: {
bootstrap: { bootstrap: {

0 comments on commit fd801dd

Please sign in to comment.