From 484578936bc7ee04c6b28cb848772a4b6ded2b03 Mon Sep 17 00:00:00 2001 From: Eric Rasche Date: Wed, 23 Sep 2015 11:24:37 -0500 Subject: [PATCH 1/9] Refactor to use -P This is a major refactoring to use the -P option of docker which should simplify some of our deployment like running docker containers on another host. In support of #372 --- .../ipython/templates/ipython.mako | 10 +- .../rstudio/templates/rstudio.mako | 17 ++-- .../web/base/interactive_environments.py | 91 +++++++++++++++---- 3 files changed, 86 insertions(+), 32 deletions(-) diff --git a/config/plugins/interactive_environments/ipython/templates/ipython.mako b/config/plugins/interactive_environments/ipython/templates/ipython.mako index 118d72baf2f..8edd23dc423 100644 --- a/config/plugins/interactive_environments/ipython/templates/ipython.mako +++ b/config/plugins/interactive_environments/ipython/templates/ipython.mako @@ -33,18 +33,16 @@ if hda.datatype.__class__.__name__ != "Ipynb": else: shutil.copy( hda.file_name, empty_nb_path ) +# Add all environment variables collected from Galaxy's IE infrastructure +ie_request.launch(env_override={ + 'notebook_password': PASSWORD, +}) ## General IE specific # Access URLs for the notebook from within galaxy. notebook_access_url = ie_request.url_template('${PROXY_URL}/ipython/${PORT}/notebooks/ipython_galaxy_notebook.ipynb') notebook_login_url = ie_request.url_template('${PROXY_URL}/ipython/${PORT}/login?next=%2Fipython%2F${PORT}%2Ftree') - -# Add all environment variables collected from Galaxy's IE infrastructure -ie_request.launch(env_override={ - 'notebook_password': PASSWORD, -}) - %> diff --git a/config/plugins/interactive_environments/rstudio/templates/rstudio.mako b/config/plugins/interactive_environments/rstudio/templates/rstudio.mako index 0fc4d2a4ba2..c9cdbed7e49 100644 --- a/config/plugins/interactive_environments/rstudio/templates/rstudio.mako +++ b/config/plugins/interactive_environments/rstudio/templates/rstudio.mako @@ -12,14 +12,6 @@ temp_dir = ie_request.temp_dir PASSWORD = ie_request.notebook_pw USERNAME = "galaxy" -## General IE specific -# Access URLs for the notebook from within galaxy. -# TODO: Make this work without pointing directly to IE. Currently does not work -# through proxy. -notebook_pubkey_url = ie_request.url_template('${PROXY_URL}/rstudio/${PORT}/auth-public-key') -notebook_access_url = ie_request.url_template('${PROXY_URL}/rstudio/${PORT}/') -notebook_login_url = ie_request.url_template('${PROXY_URL}/rstudio/${PORT}/auth-do-sign-in') - # Did the user give us an RData file? if hda.datatype.__class__.__name__ == "RData": shutil.copy( hda.file_name, os.path.join(temp_dir, '.RData') ) @@ -29,6 +21,15 @@ ie_request.launch(env_override={ 'notebook_password': PASSWORD, 'cors_origin': ie_request.attr.proxy_url, }) + +## General IE specific +# Access URLs for the notebook from within galaxy. +# TODO: Make this work without pointing directly to IE. Currently does not work +# through proxy. +notebook_pubkey_url = ie_request.url_template('${PROXY_URL}/rstudio/${PORT}/auth-public-key') +notebook_access_url = ie_request.url_template('${PROXY_URL}/rstudio/${PORT}/') +notebook_login_url = ie_request.url_template('${PROXY_URL}/rstudio/${PORT}/auth-do-sign-in') + %> diff --git a/lib/galaxy/web/base/interactive_environments.py b/lib/galaxy/web/base/interactive_environments.py index b2964e35fbb..f81f1ce3e31 100644 --- a/lib/galaxy/web/base/interactive_environments.py +++ b/lib/galaxy/web/base/interactive_environments.py @@ -1,10 +1,11 @@ import ConfigParser import os +import json import stat import random import tempfile -from subprocess import Popen, PIPE +from subprocess import Popen, PIPE, check_output from galaxy.util.bunch import Bunch from galaxy import web @@ -40,11 +41,6 @@ class InteractiveEnviornmentRequest(object): self.load_deploy_config() self.attr.docker_hostname = self.attr.viz_config.get("docker", "docker_hostname") - self.attr.proxy_request = trans.app.proxy_manager.setup_proxy( - trans, host=self.attr.docker_hostname - ) - self.attr.proxy_url = self.attr.proxy_request[ 'proxy_url' ] - self.attr.PORT = self.attr.proxy_request[ 'proxied_port' ] # Generate per-request passwords the IE plugin can use to configure # the destination container. @@ -102,7 +98,7 @@ class InteractiveEnviornmentRequest(object): 'history_id': self.attr.history_id, 'api_key': api_key, 'remote_host': request.remote_addr, - 'docker_port': self.attr.PORT, + # DOCKER_PORT is NO LONGER AVAILABLE. All IEs must update. 'cors_origin': request.host_url, } @@ -144,7 +140,6 @@ class InteractiveEnviornmentRequest(object): There are several variables accessible to the user: - ${PROXY_URL} will be replaced with dynamically create proxy - - ${PORT} will be replaced with the port the docker image is attached to """ # Figure out our substitutions @@ -156,18 +151,12 @@ class InteractiveEnviornmentRequest(object): else: protocol = 'http' - if not self.attr.APACHE_URLS: - # If they are not using apache URLs, that implies there's a port attached to the host - # string, thus we replace just the first instance of host that we see. - url_template = url_template.replace('${HOST}', '${HOST}:${PORT}', 1) - url_template = url_template.replace('${PROTO}', protocol) \ .replace('${HOST}', self.attr.HOST) # Only the following replacements are used with Galaxy dynamic proxy # URLs - url = url_template.replace('${PROXY_URL}', str(self.attr.proxy_url)) \ - .replace('${PORT}', str(self.attr.PORT)) + url = url_template.replace('${PROXY_URL}', str(self.attr.proxy_url)) return url def volume(self, host_path, container_path, **kwds): @@ -188,13 +177,11 @@ class InteractiveEnviornmentRequest(object): # Then we format in the entire docker command in place of # {docker_args}, so as to let the admin not worry about which args are # getting passed - command = command.format(docker_args='{command_inject} {environment} -d -p {port_ext}:{port_int} -v "{temp_dir}:/import/" {volume_str} {image}') + command = command.format(docker_args='{command_inject} {environment} -d -P -v "{temp_dir}:/import/" {volume_str} {image}') # Once that's available, we format again with all of our arguments command = command.format( command_inject=self.attr.viz_config.get("docker", "command_inject"), environment=env_str, - port_ext=self.attr.PORT, - port_int=self.attr.docker_port, temp_dir=temp_dir, volume_str=volume_str, image=self.attr.viz_config.get("docker", "image") @@ -212,5 +199,73 @@ class InteractiveEnviornmentRequest(object): stdout, stderr = p.communicate() if p.returncode != 0 or len(stderr): log.error( "%s\n%s" % (stdout, stderr) ) + return None else: log.debug( "Container id: %s" % stdout) + port_mappings = self.get_proxied_ports(stdout) + if len(port_mappings) > 1: + log.warning("Don't know how to handle proxies to containers with multiple exposed ports. Arbitrarily choosing first") + elif len(port_mappings) == 0: + log.warning("No exposed ports to map! Images MUST EXPOSE") + return None + # Fetch the first port_mapping + (service, host_ip, host_port) = port_mappings[0] + + # Now we configure our proxy_requst object and we manually specify + # the port to map to and ensure the proxy is available. + self.attr.proxy_request = self.trans.app.proxy_manager.setup_proxy( + self.trans, + host=self.attr.docker_hostname, + port=host_port, + ) + # These variables then become available for use in templating URLs + self.attr.proxy_url = self.attr.proxy_request[ 'proxy_url' ] + # Commented out because it needs to be documented and visible that + # this variable was moved here. Usually would remove commented + # code, but again, needs to be clear where this went. Remove at a + # later time. + # + # PORT is no longer exposed internally. All requests are forced to + # go through the proxy we ship. + # self.attr.PORT = self.attr.proxy_request[ 'proxied_port' ] + + def get_proxied_ports(self, container_id): + """Run docker inspect on a container to figure out which ports were + mapped where. + + :type container_id: str + :param container_id: a docker container ID + + :returns: a list of triples containing (internal_port, external_ip, + external_port), of which the ports are probably the only + useful information. + + Someday code that calls this should be refactored whenever we get + containers with multiple ports working. + """ + command = self.attr.viz_config.get("docker", "command") + command.replace( + "run {docker_args}", + "inspect %s" % container_id + ) + output = check_output(command) + inspect_data = json.loads(output) + # [{ + # "NetworkSettings" : { + # "Ports" : { + # "3306/tcp" : [ + # { + # "HostIp" : "127.0.0.1", + # "HostPort" : "3306" + # } + # ] + mappings = [] + port_mappings = inspect_data[0]['NetworkSettings']['Ports'] + for port_name in port_mappings: + for binding in port_mappings[port_name]: + mappings.append(( + port_name.replace('/tcp', '').replace('/udp', ''), + port_mappings[port_name][binding]['HostIp'], + port_mappings[port_name][binding]['HostPort'] + )) + return mappings From c9e9c95514e50f9a260b5ed342a715d2846fd4fe Mon Sep 17 00:00:00 2001 From: Eric Rasche Date: Wed, 23 Sep 2015 12:35:00 -0500 Subject: [PATCH 2/9] Correct small logic bugs, add logging --- lib/galaxy/web/base/interactive_environments.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/galaxy/web/base/interactive_environments.py b/lib/galaxy/web/base/interactive_environments.py index 2c1b3c3b71d..fb906fd67d2 100644 --- a/lib/galaxy/web/base/interactive_environments.py +++ b/lib/galaxy/web/base/interactive_environments.py @@ -246,11 +246,15 @@ class InteractiveEnviornmentRequest(object): containers with multiple ports working. """ command = self.attr.viz_config.get("docker", "command") - command.replace( + command = command.replace( "run {docker_args}", "inspect %s" % container_id ) - output = check_output(command) + log.info("Inspecting docker container {0} with command [{1}]".format( + container_id, + command + )) + output = check_output(command, shell=True) inspect_data = json.loads(output) # [{ # "NetworkSettings" : { @@ -267,7 +271,7 @@ class InteractiveEnviornmentRequest(object): for binding in port_mappings[port_name]: mappings.append(( port_name.replace('/tcp', '').replace('/udp', ''), - port_mappings[port_name][binding]['HostIp'], - port_mappings[port_name][binding]['HostPort'] + binding['HostIp'], + binding['HostPort'] )) return mappings From a02df53f3356a03f3784070a4c50accfbc00187f Mon Sep 17 00:00:00 2001 From: Eric Rasche Date: Wed, 23 Sep 2015 12:51:37 -0500 Subject: [PATCH 3/9] Correct IPython notebooks to new URL --- .../interactive_environments/ipython/templates/ipython.mako | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/plugins/interactive_environments/ipython/templates/ipython.mako b/config/plugins/interactive_environments/ipython/templates/ipython.mako index 8edd23dc423..4d8f780592f 100644 --- a/config/plugins/interactive_environments/ipython/templates/ipython.mako +++ b/config/plugins/interactive_environments/ipython/templates/ipython.mako @@ -40,8 +40,8 @@ ie_request.launch(env_override={ ## General IE specific # Access URLs for the notebook from within galaxy. -notebook_access_url = ie_request.url_template('${PROXY_URL}/ipython/${PORT}/notebooks/ipython_galaxy_notebook.ipynb') -notebook_login_url = ie_request.url_template('${PROXY_URL}/ipython/${PORT}/login?next=%2Fipython%2F${PORT}%2Ftree') +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=%2Fipython%2Ftree') %> From a5b296b8ecd262d58a17049f5a9aeeecb7649621 Mon Sep 17 00:00:00 2001 From: Eric Rasche Date: Wed, 23 Sep 2015 12:52:13 -0500 Subject: [PATCH 4/9] Include user email Fixes bgruening/docker-ipython-notebook#51 --- lib/galaxy/web/base/interactive_environments.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/galaxy/web/base/interactive_environments.py b/lib/galaxy/web/base/interactive_environments.py index fb906fd67d2..772037993e5 100644 --- a/lib/galaxy/web/base/interactive_environments.py +++ b/lib/galaxy/web/base/interactive_environments.py @@ -102,6 +102,7 @@ class InteractiveEnviornmentRequest(object): 'remote_host': request.remote_addr, # DOCKER_PORT is NO LONGER AVAILABLE. All IEs must update. 'cors_origin': request.host_url, + 'user_email': self.trans.user.email, } if self.attr.viz_config.has_option("docker", "galaxy_url"): From 06a67c8c109dcf59425b53b80dcb5419f5e814af Mon Sep 17 00:00:00 2001 From: Eric Rasche Date: Wed, 23 Sep 2015 15:02:02 -0500 Subject: [PATCH 5/9] Add dynamic_proxy_prefix configuration Normally the proxy binds to :8800 and runs containers under :8800/ipython/... which is great... until you have an upstream proxy like apache wrapping that at the url FQDN/ipython and suddenly your cookies aren't available because they're specific to /galaxy. Thus, when an upstream proxy is in use, we correct the proxy's path to: with :8800/{cookie_path}/gie_proxy/ipython/... which behaves much more nicely with upstraem proxies. --- config/galaxy.ini.sample | 6 ++++++ lib/galaxy/config.py | 1 + lib/galaxy/web/proxy/__init__.py | 8 +++++--- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/config/galaxy.ini.sample b/config/galaxy.ini.sample index 5dadaaa29b4..787962d2f95 100644 --- a/config/galaxy.ini.sample +++ b/config/galaxy.ini.sample @@ -574,6 +574,12 @@ nglims_config_file = tool-data/nglims.yaml # nodejs to wrap connections in SSL). #dynamic_proxy_external_proxy=False +# Additionally, when the dynamic proxy is proxied by an upstream server, you'll +# want to specify a prefixed URL so both Galaxy and the proxy reside under the +# same path that your cookies are under. This will result in a url like +# https://FQDN/galaxy-prefix/gie_proxy for proxying +#dynamic_proxy_prefix=gie_proxy + # -- Logging and Debugging # If True, Galaxy will attempt to configure a simple root logger if a diff --git a/lib/galaxy/config.py b/lib/galaxy/config.py index 28acdc42262..286ca8b38ef 100644 --- a/lib/galaxy/config.py +++ b/lib/galaxy/config.py @@ -443,6 +443,7 @@ class Configuration( object ): self.dynamic_proxy_bind_port = int( kwargs.get( "dynamic_proxy_bind_port", "8800" ) ) self.dynamic_proxy_bind_ip = kwargs.get( "dynamic_proxy_bind_ip", "0.0.0.0" ) self.dynamic_proxy_external_proxy = string_as_bool( kwargs.get( "dynamic_proxy_external_proxy", "False" ) ) + self.dynamic_proxy_prefix = kwargs.get( "dynamic_proxy_prefix", "gie_proxy" ) # Default chunk size for chunkable datatypes -- 64k self.display_chunk_size = int( kwargs.get( 'display_chunk_size', 65536) ) diff --git a/lib/galaxy/web/proxy/__init__.py b/lib/galaxy/web/proxy/__init__.py index 30738ad6c11..fda64907c60 100644 --- a/lib/galaxy/web/proxy/__init__.py +++ b/lib/galaxy/web/proxy/__init__.py @@ -17,7 +17,9 @@ SECURE_COOKIE = "galaxysession" class ProxyManager(object): def __init__( self, config ): - for option in [ "manage_dynamic_proxy", "dynamic_proxy_bind_port", "dynamic_proxy_bind_ip", "dynamic_proxy_debug", "dynamic_proxy_external_proxy" ]: + for option in ["manage_dynamic_proxy", "dynamic_proxy_bind_port", + "dynamic_proxy_bind_ip", "dynamic_proxy_debug", + "dynamic_proxy_external_proxy", "dynamic_proxy_prefix"]: setattr( self, option, getattr( config, option ) ) self.launch_by = "node" # TODO: Support docker if self.manage_dynamic_proxy: @@ -44,9 +46,9 @@ class ProxyManager(object): host = host[0:host.index(':')] scheme = trans.request.scheme if not self.dynamic_proxy_external_proxy: - proxy_url = '%s://%s:%d' % (scheme, host, self.dynamic_proxy_bind_port) + proxy_url = '%s://%s:%d/%s' % (scheme, host, self.dynamic_proxy_bind_port, self.dynamic_proxy_prefix) else: - proxy_url = '%s://%s' % (scheme, host) + proxy_url = '%s://%s/%s' % (scheme, host, self.dynamic_proxy_prefix) return { 'proxy_url': proxy_url, 'proxied_port': proxy_requests.port, From b4bbd1c2f161959fabdb62962aa63fb630c1ca2d Mon Sep 17 00:00:00 2001 From: Eric Rasche Date: Wed, 23 Sep 2015 16:12:01 -0500 Subject: [PATCH 6/9] Make use of proxy path in appropriate places --- .../ipython/templates/ipython.mako | 2 +- .../web/base/interactive_environments.py | 33 +++++++++++-------- lib/galaxy/web/proxy/__init__.py | 6 ++-- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/config/plugins/interactive_environments/ipython/templates/ipython.mako b/config/plugins/interactive_environments/ipython/templates/ipython.mako index 4d8f780592f..2e87c5ae221 100644 --- a/config/plugins/interactive_environments/ipython/templates/ipython.mako +++ b/config/plugins/interactive_environments/ipython/templates/ipython.mako @@ -41,7 +41,7 @@ ie_request.launch(env_override={ ## 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=%2Fipython%2Ftree') +notebook_login_url = ie_request.url_template('${PROXY_URL}/ipython/login?next=${PROXY_PREFIX}%2Fipython%2Ftree') %> diff --git a/lib/galaxy/web/base/interactive_environments.py b/lib/galaxy/web/base/interactive_environments.py index ed0779882dd..f2f64c2cddd 100644 --- a/lib/galaxy/web/base/interactive_environments.py +++ b/lib/galaxy/web/base/interactive_environments.py @@ -56,6 +56,14 @@ class InteractiveEnviornmentRequest(object): log.error( "Could not change permissions of tmpdir %s" % self.temp_dir ) # continue anyway + # This duplicates the logic in the proxy manager + if self.attr.galaxy_config.dynamic_proxy_external_proxy: + self.attr.proxy_prefix = '%s/%s' % ( + self.attr.galaxy_config.cookie_path, + self.attr.galaxy_config.dynamic_proxy_prefix) + else: + self.attr.proxy_prefix = '' + def load_deploy_config(self, default_dict={}): # For backwards compat, any new variables added to the base .ini file # will need to be recorded here. The ConfigParser doesn't provide a @@ -103,6 +111,7 @@ class InteractiveEnviornmentRequest(object): # DOCKER_PORT is NO LONGER AVAILABLE. All IEs must update. 'cors_origin': request.host_url, 'user_email': self.trans.user.email, + 'proxy_prefix': self.attr.proxy_prefix, } if self.attr.viz_config.has_option("docker", "galaxy_url"): @@ -137,18 +146,15 @@ class InteractiveEnviornmentRequest(object): return "false" def url_template(self, url_template): + """Process a URL template + + There are several variables accessible to the user: + + - ${PROXY_URL} will be replaced with the dynamically create proxy's url + - ${PROXY_PREFIX} will be replaced with the prefix that may occur """ - Process a URL template - - There are several variables accessible to the user: - - - ${PROXY_URL} will be replaced with dynamically create proxy - """ - # Figure out our substitutions - # Next several lines for older style replacements (not used with Galaxy dynamic # proxy) - if self.attr.SSL_URLS: protocol = 'https' else: @@ -159,7 +165,8 @@ class InteractiveEnviornmentRequest(object): # Only the following replacements are used with Galaxy dynamic proxy # URLs - url = url_template.replace('${PROXY_URL}', str(self.attr.proxy_url)) + url = url_template.replace('${PROXY_URL}', str(self.attr.proxy_url)) \ + .replace('${PROXY_PREFIX}', str(self.attr.proxy_prefix.replace('/', '%2F'))) return url def volume(self, host_path, container_path, **kwds): @@ -220,6 +227,7 @@ class InteractiveEnviornmentRequest(object): self.trans, host=self.attr.docker_hostname, port=host_port, + proxy_prefix=self.attr.proxy_prefix, ) # These variables then become available for use in templating URLs self.attr.proxy_url = self.attr.proxy_request[ 'proxy_url' ] @@ -247,10 +255,7 @@ class InteractiveEnviornmentRequest(object): containers with multiple ports working. """ command = self.attr.viz_config.get("docker", "command") - command = command.replace( - "run {docker_args}", - "inspect %s" % container_id - ) + command = command.format(docker_args="inspect %s" % container_id) log.info("Inspecting docker container {0} with command [{1}]".format( container_id, command diff --git a/lib/galaxy/web/proxy/__init__.py b/lib/galaxy/web/proxy/__init__.py index fda64907c60..a23b42a285c 100644 --- a/lib/galaxy/web/proxy/__init__.py +++ b/lib/galaxy/web/proxy/__init__.py @@ -31,7 +31,7 @@ class ProxyManager(object): def shutdown( self ): self.lazy_process.shutdown() - def setup_proxy( self, trans, host=DEFAULT_PROXY_TO_HOST, port=None ): + def setup_proxy( self, trans, host=DEFAULT_PROXY_TO_HOST, port=None, proxy_prefix="" ): if self.manage_dynamic_proxy: log.info("Attempting to start dynamic proxy process") self.lazy_process.start_process() @@ -46,9 +46,9 @@ class ProxyManager(object): host = host[0:host.index(':')] scheme = trans.request.scheme if not self.dynamic_proxy_external_proxy: - proxy_url = '%s://%s:%d/%s' % (scheme, host, self.dynamic_proxy_bind_port, self.dynamic_proxy_prefix) + proxy_url = '%s://%s:%d' % (scheme, host, self.dynamic_proxy_bind_port) else: - proxy_url = '%s://%s/%s' % (scheme, host, self.dynamic_proxy_prefix) + proxy_url = '%s://%s%s' % (scheme, host, proxy_prefix) return { 'proxy_url': proxy_url, 'proxied_port': proxy_requests.port, From 4a4a7ca51b195ac9d9a718448d65c89c02ae118c Mon Sep 17 00:00:00 2001 From: Eric Rasche Date: Wed, 23 Sep 2015 16:23:54 -0500 Subject: [PATCH 7/9] Update URLs for rstudio --- .../rstudio/templates/rstudio.mako | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/config/plugins/interactive_environments/rstudio/templates/rstudio.mako b/config/plugins/interactive_environments/rstudio/templates/rstudio.mako index c9cdbed7e49..9d35421ba5a 100644 --- a/config/plugins/interactive_environments/rstudio/templates/rstudio.mako +++ b/config/plugins/interactive_environments/rstudio/templates/rstudio.mako @@ -19,16 +19,15 @@ if hda.datatype.__class__.__name__ == "RData": ie_request.launch(env_override={ 'notebook_username': USERNAME, 'notebook_password': PASSWORD, - 'cors_origin': ie_request.attr.proxy_url, }) ## General IE specific # Access URLs for the notebook from within galaxy. # TODO: Make this work without pointing directly to IE. Currently does not work # through proxy. -notebook_pubkey_url = ie_request.url_template('${PROXY_URL}/rstudio/${PORT}/auth-public-key') -notebook_access_url = ie_request.url_template('${PROXY_URL}/rstudio/${PORT}/') -notebook_login_url = ie_request.url_template('${PROXY_URL}/rstudio/${PORT}/auth-do-sign-in') +notebook_pubkey_url = ie_request.url_template('${PROXY_URL}/rstudio/auth-public-key') +notebook_access_url = ie_request.url_template('${PROXY_URL}/rstudio/') +notebook_login_url = ie_request.url_template('${PROXY_URL}/rstudio/auth-do-sign-in') %> From e8eb46b65d76a2b7645aa325e321d135df02e802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gr=C3=BCning?= Date: Thu, 24 Sep 2015 16:00:50 +0200 Subject: [PATCH 8/9] Use a backport for check_output that is not part of python 2.6 --- .../web/base/interactive_environments.py | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/galaxy/web/base/interactive_environments.py b/lib/galaxy/web/base/interactive_environments.py index f2f64c2cddd..baffdf539b9 100644 --- a/lib/galaxy/web/base/interactive_environments.py +++ b/lib/galaxy/web/base/interactive_environments.py @@ -5,7 +5,8 @@ import json import stat import random import tempfile -from subprocess import Popen, PIPE, check_output +import subprocess +from subprocess import Popen, PIPE from galaxy.util.bunch import Bunch from galaxy import web @@ -16,6 +17,32 @@ import logging log = logging.getLogger(__name__) +# Python 2.6 does not support the check_output command. +# As a workaround we use a backport from https://gist.github.com/edufelipe/1027906 +# until we do not support Python 2.6 anymore. + +if "check_output" not in dir( subprocess ): + def check_output(*popenargs, **kwargs): + r"""Run command with arguments and return its output as a byte string. + Backported from Python 2.7 as it's implemented as pure python on stdlib. + >>> check_output(['/usr/bin/python', '--version']) + Python 2.6.2 + """ + process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + error = subprocess.CalledProcessError(retcode, cmd) + error.output = output + raise error + return output +else: + from subprocess import check_output + + class InteractiveEnviornmentRequest(object): def __init__(self, trans, plugin): From 5f15b0d94c4a9703315f96fe3ce63b9b3906ea84 Mon Sep 17 00:00:00 2001 From: Eric Rasche Date: Thu, 24 Sep 2015 10:30:13 -0500 Subject: [PATCH 9/9] Replace backported one with just the code used Sorry bjoern, ripped out what you'd added and just used the code because the doctest was failing for some unknown reason and we didn't ever need to reuse the function, so ... easier this way --- .../web/base/interactive_environments.py | 37 ++++--------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/lib/galaxy/web/base/interactive_environments.py b/lib/galaxy/web/base/interactive_environments.py index baffdf539b9..27f42f2a0cd 100644 --- a/lib/galaxy/web/base/interactive_environments.py +++ b/lib/galaxy/web/base/interactive_environments.py @@ -5,7 +5,6 @@ import json import stat import random import tempfile -import subprocess from subprocess import Popen, PIPE from galaxy.util.bunch import Bunch @@ -17,32 +16,6 @@ import logging log = logging.getLogger(__name__) -# Python 2.6 does not support the check_output command. -# As a workaround we use a backport from https://gist.github.com/edufelipe/1027906 -# until we do not support Python 2.6 anymore. - -if "check_output" not in dir( subprocess ): - def check_output(*popenargs, **kwargs): - r"""Run command with arguments and return its output as a byte string. - Backported from Python 2.7 as it's implemented as pure python on stdlib. - >>> check_output(['/usr/bin/python', '--version']) - Python 2.6.2 - """ - process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) - output, unused_err = process.communicate() - retcode = process.poll() - if retcode: - cmd = kwargs.get("args") - if cmd is None: - cmd = popenargs[0] - error = subprocess.CalledProcessError(retcode, cmd) - error.output = output - raise error - return output -else: - from subprocess import check_output - - class InteractiveEnviornmentRequest(object): def __init__(self, trans, plugin): @@ -287,8 +260,14 @@ class InteractiveEnviornmentRequest(object): container_id, command )) - output = check_output(command, shell=True) - inspect_data = json.loads(output) + + p = Popen(command, stdout=PIPE, stderr=PIPE, close_fds=True, shell=True) + stdout, stderr = p.communicate() + if p.returncode != 0 or len(stderr): + log.error( "%s\n%s" % (stdout, stderr) ) + return None + + inspect_data = json.loads(stdout) # [{ # "NetworkSettings" : { # "Ports" : {