From 47a44202673839fe1ff00408c50c533ddd823a27 Mon Sep 17 00:00:00 2001 From: Bjoern Gruening Date: Fri, 25 Dec 2015 22:38:32 +0100 Subject: [PATCH 1/3] Add Jupyter Interactive Environment --- .../jupyter/config/jupyter.ini.sample | 44 +++++++++ .../jupyter/config/jupyter.xml | 16 ++++ .../jupyter/static/js/jupyter.js | 85 +++++++++++++++++ .../jupyter/templates/jupyter.mako | 92 +++++++++++++++++++ 4 files changed, 237 insertions(+) create mode 100644 config/plugins/interactive_environments/jupyter/config/jupyter.ini.sample create mode 100644 config/plugins/interactive_environments/jupyter/config/jupyter.xml create mode 100644 config/plugins/interactive_environments/jupyter/static/js/jupyter.js create mode 100644 config/plugins/interactive_environments/jupyter/templates/jupyter.mako diff --git a/config/plugins/interactive_environments/jupyter/config/jupyter.ini.sample b/config/plugins/interactive_environments/jupyter/config/jupyter.ini.sample new file mode 100644 index 00000000000..47dfa13e85c --- /dev/null +++ b/config/plugins/interactive_environments/jupyter/config/jupyter.ini.sample @@ -0,0 +1,44 @@ +[main] +# Following options are ignored if using the Galaxy dynamic proxy but +# are useful if mapping a range of ports for environment consumption. +#password_auth = False +#ssl = False + +[docker] +# Command to launch docker container. For example `sudo docker` or `docker-lxc`. +# If you need to use a command like `sg` you can do that here, just be sure to +# wrap all of the docker portion in single quotes. E.g. `sg 'docker' 'docker {docker_args}'` +# +# It is recommended that you use command_inject if you need to inject +# additional parameters. This command string is re-used for a `docker inspect` +# command and will likely cause errors if it is extensively modified, past the +# usual group/sudo changes. +#command = docker {docker_args} + +# The docker image name that should be started. +image = jupyter +#bgruening/docker-ipython-notebook:15.10.1 + +# Additional arguments that are passed to the `docker run` command. +command_inject = --sig-proxy=true -e DEBUG=false -e DEFAULT_CONTAINER_RUNTIME=120 + +# URL to access the Galaxy API with from the spawn Docker containter, if empty +# this falls back to galaxy.ini's galaxy_infrastructure_url and finally to the +# Docker host of the spawned container if that is also not set. +#galaxy_url = + +# The Docker hostname. It can be useful to run the Docker daemon on a different +# host than Galaxy. +#docker_hostname = localhost + +# Try to set the tempdirectory to world execute - this can fix the issue +# where 'sudo docker' is not able to mount the folder otherwise. +# "finalize namespace chdir to /import permission denied" +#wx_tempdir = False + +# Overwride the IE tempdirectory. This can be useful if you regular tempdir is +# located on an NFS share, which does not work well as Docker volume. In this case +# you can have a shared sshfs share which you can use as temporary directory to +# share data between the IE and Galaxy. +#docker_galaxy_temp_dir = None + diff --git a/config/plugins/interactive_environments/jupyter/config/jupyter.xml b/config/plugins/interactive_environments/jupyter/config/jupyter.xml new file mode 100644 index 00000000000..e1c8bd0040c --- /dev/null +++ b/config/plugins/interactive_environments/jupyter/config/jupyter.xml @@ -0,0 +1,16 @@ + + + + + + HistoryDatasetAssociation + tabular.Tabular + data.Text + dataset_id + + + + dataset_id + + jupyter.mako + diff --git a/config/plugins/interactive_environments/jupyter/static/js/jupyter.js b/config/plugins/interactive_environments/jupyter/static/js/jupyter.js new file mode 100644 index 00000000000..61936f3d89f --- /dev/null +++ b/config/plugins/interactive_environments/jupyter/static/js/jupyter.js @@ -0,0 +1,85 @@ +function message_failed_auth(password){ + toastr.info( + "Automatic authorization failed. You can manually login with:
" + password + "
More details ...", + "Please login manually", + {'closeButton': true, 'timeOut': 100000, 'tapToDismiss': false} + ); +} + +function message_failed_connection(){ + toastr.error( + "Could not connect to Jupyter Notebook. Please contact your administrator. More details ...", + "Security warning", + {'closeButton': true, 'timeOut': 20000, 'tapToDismiss': true} + ); +} + +function message_no_auth(){ + // No longer a security issue, proxy validates Galaxy session token. + /* + toastr.warning( + "IPython Notebook was lunched without authentication. This is a security issue. More details ...", + "Security warning", + {'closeButton': true, 'timeOut': 20000, 'tapToDismiss': false} + ); + */ +} + + +/** + * Load an interactive environment (IE) from a remote URL + * @param {String} password: password used to authenticate to the remote resource + * @param {String} notebook_login_url: URL that should be POSTed to for login + * @param {String} notebook_access_url: the URL embeded in the page and loaded + * + */ +function load_notebook(password, notebook_login_url, notebook_access_url){ + $( document ).ready(function() { + // Test notebook_login_url for accessibility, executing the login+load function whenever + // we've successfully connected to the IE. + test_ie_availability(notebook_login_url, function(){ + _handle_notebook_loading(password, notebook_login_url, notebook_access_url); + }); + }); +} + +/** + * Must be implemented by IEs + */ +function _handle_notebook_loading(password, notebook_login_url, notebook_access_url){ + if ( ie_password_auth ) { + // Make an AJAX POST + $.ajax({ + type: "POST", + // to the Login URL + url: notebook_login_url, + // With our password + data: { + 'password': password + }, + xhrFields: { + withCredentials: true + }, + // If that is successful, load the notebook + success: function(){ + append_notebook(notebook_access_url); + }, + error: function(jqxhr, status, error){ + if(ie_password_auth){ + // Failure happens due to CORS + message_failed_auth(password); + append_notebook(notebook_access_url); + }else{ + message_failed_connection(); + // Do we want to try and load the notebook anyway? Just in case? + append_notebook(notebook_access_url); + } + } + }); + } + else { + // Not using password auth, just embed it to avoid content-origin issues. + message_no_auth(); + append_notebook(notebook_access_url); + } +} diff --git a/config/plugins/interactive_environments/jupyter/templates/jupyter.mako b/config/plugins/interactive_environments/jupyter/templates/jupyter.mako new file mode 100644 index 00000000000..ab0822bcd07 --- /dev/null +++ b/config/plugins/interactive_environments/jupyter/templates/jupyter.mako @@ -0,0 +1,92 @@ +<%namespace name="ie" file="ie.mako" /> + +<% +import os +import shutil +import hashlib + +# Sets ID and sets up a lot of other variables +ie_request.load_deploy_config() +ie_request.attr.docker_port = 8888 +ie_request.attr.import_volume = False + +if ie_request.attr.PASSWORD_AUTH: + m = hashlib.sha1() + m.update( ie_request.notebook_pw + ie_request.notebook_pw_salt ) + PASSWORD = 'sha1:%s:%s' % (ie_request.notebook_pw_salt, m.hexdigest()) +else: + PASSWORD = "none" + +## Jupyter Notbook Specific +if hda.datatype.__class__.__name__ == "Ipynb": + DATASET_HID = hda.hid +else: + DATASET_HID = None + +# Add all environment variables collected from Galaxy's IE infrastructure +ie_request.launch(env_override={ + 'notebook_password': PASSWORD, + 'dataset_hid': DATASET_HID, +}) + +## General IE specific +# Access URLs for the notebook from within galaxy. +notebook_access_url = ie_request.url_template('${PROXY_URL}/ipython/notebooks/ipython_galaxy_notebook.ipynb') +notebook_login_url = ie_request.url_template('${PROXY_URL}/ipython/login?next=${PROXY_PREFIX}%2Fipython%2Ftree') + +%> + + +${ ie.load_default_js() } + + + + +
+
+ + From 38d40e7af11525348bb54bad3a3a8496539a2a3a Mon Sep 17 00:00:00 2001 From: Bjoern Gruening Date: Sun, 27 Dec 2015 01:46:50 +0100 Subject: [PATCH 2/3] move keep_alive into jupyter.js --- .../jupyter/static/js/jupyter.js | 38 +++++++++++++++++++ .../jupyter/templates/jupyter.mako | 38 ++++--------------- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/config/plugins/interactive_environments/jupyter/static/js/jupyter.js b/config/plugins/interactive_environments/jupyter/static/js/jupyter.js index 61936f3d89f..3b0ca53e51e 100644 --- a/config/plugins/interactive_environments/jupyter/static/js/jupyter.js +++ b/config/plugins/interactive_environments/jupyter/static/js/jupyter.js @@ -43,6 +43,44 @@ function load_notebook(password, notebook_login_url, notebook_access_url){ }); } + +function keep_alive(){ + /** + * This is needed to keep the container alive. If the user leaves this site + * this function is not constantly pinging the container, the container will + * terminate itself. + */ + + var request_count = 0; + interval = setInterval(function(){ + $.ajax({ + url: notebook_access_url, + xhrFields: { + withCredentials: true + }, + type: "GET", + timeout: 500, + success: function(){ + console.log("Connected to IE, returning"); + }, + error: function(jqxhr, status, error){ + request_count++; + console.log("Request " + request_count); + if(request_count > 30){ + clearInterval(interval); + clear_main_area(); + toastr.error( + "Could not connect to IE, contact your administrator", + "Error", + {'closeButton': true, 'timeOut': 20000, 'tapToDismiss': false} + ); + } + } + }); + }, 30000); +} + + /** * Must be implemented by IEs */ diff --git a/config/plugins/interactive_environments/jupyter/templates/jupyter.mako b/config/plugins/interactive_environments/jupyter/templates/jupyter.mako index ab0822bcd07..d76e2998e95 100644 --- a/config/plugins/interactive_environments/jupyter/templates/jupyter.mako +++ b/config/plugins/interactive_environments/jupyter/templates/jupyter.mako @@ -47,6 +47,12 @@ var notebook_login_url = '${ notebook_login_url }'; var notebook_access_url = '${ notebook_access_url }'; ${ ie.plugin_require_config() } +// Keep container running +requirejs(['interactive_environments', 'plugin/jupyter'], function(){ + keep_alive(); +}); + + // Load notebook requirejs(['interactive_environments', 'plugin/jupyter'], function(){ @@ -54,36 +60,8 @@ requirejs(['interactive_environments', 'plugin/jupyter'], function(){ }); -// This is needed to keep the container alive. If the user leaves this site -// this function is not constantly pinging the container, the container will -// terminate itself. -var request_count = 0; -interval = setInterval(function(){ - $.ajax({ - url: notebook_access_url, - xhrFields: { - withCredentials: true - }, - type: "GET", - timeout: 500, - success: function(){ - console.log("Connected to IE, returning"); - }, - error: function(jqxhr, status, error){ - request_count++; - console.log("Request " + request_count); - if(request_count > 30){ - clearInterval(interval); - clear_main_area(); - toastr.error( - "Could not connect to IE, contact your administrator", - "Error", - {'closeButton': true, 'timeOut': 20000, 'tapToDismiss': false} - ); - } - } - }); -}, 30000); + +
From 6ac01675f3f37a6af1d498b96cad753a03a8a776 Mon Sep 17 00:00:00 2001 From: Bjoern Gruening Date: Sun, 27 Dec 2015 01:49:38 +0100 Subject: [PATCH 3/3] Change default image name to a versioned tagged Docker release. --- .../interactive_environments/jupyter/config/jupyter.ini.sample | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config/plugins/interactive_environments/jupyter/config/jupyter.ini.sample b/config/plugins/interactive_environments/jupyter/config/jupyter.ini.sample index 47dfa13e85c..c89fe09a8bc 100644 --- a/config/plugins/interactive_environments/jupyter/config/jupyter.ini.sample +++ b/config/plugins/interactive_environments/jupyter/config/jupyter.ini.sample @@ -16,8 +16,7 @@ #command = docker {docker_args} # The docker image name that should be started. -image = jupyter -#bgruening/docker-ipython-notebook:15.10.1 +image = bgruening/docker-jupyter-notebook:16.01 # Additional arguments that are passed to the `docker run` command. command_inject = --sig-proxy=true -e DEBUG=false -e DEFAULT_CONTAINER_RUNTIME=120