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..c89fe09a8bc --- /dev/null +++ b/config/plugins/interactive_environments/jupyter/config/jupyter.ini.sample @@ -0,0 +1,43 @@ +[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 = 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 + +# 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..3b0ca53e51e --- /dev/null +++ b/config/plugins/interactive_environments/jupyter/static/js/jupyter.js @@ -0,0 +1,123 @@ +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); + }); + }); +} + + +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 + */ +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..d76e2998e95 --- /dev/null +++ b/config/plugins/interactive_environments/jupyter/templates/jupyter.mako @@ -0,0 +1,70 @@ +<%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() } + + + + +
+
+ +