Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow InteractiveTools to use path-based proxying when requires_domain=False #8596

Merged
merged 6 commits into from
Jan 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions config/galaxy.yml.interactivetools
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,27 @@ uwsgi:

interactivetools_map: database/interactivetools_map.sqlite
python-raw: scripts/interactivetools/key_type_token_mapping.py
route-host: ^([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)-([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)\.([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)\.(interactivetool\.localhost:8080)$ goto:interactivetool
route-run: goto:endendend
route-label: interactivetool
route-host: ^([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)-([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)\.([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)\.(interactivetool\.localhost:8080)$ goto:itdomain
route-run: goto:itdomainend
route-label: itdomain
route-host: ^([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)-([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)\.([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)\.(interactivetool\.localhost:8080)$ rpcvar:TARGET_HOST rtt_key_type_token_mapper_cached $1 $3 $2 $4 $0 5
route-if-not: empty:${TARGET_HOST} httpdumb:${TARGET_HOST}
route: .* break:404 Not Found
route-label: endendend
route-label: itdomainend
# Path-based is currently less functional and less tested than domain-based
route: ^(/interactivetool/access/)([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)(()|(/.*)*)$ goto:itpath
route-run: goto:itpathend
route-label: itpath
route: ^/interactivetool/access/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)(()|/.*)$ rpcvar:TARGET_HOST rtt_key_type_token_mapper_cached $2 $1 $3 $4 $0 5
route-if: empty:${TARGET_HOST} goto:itpathfail
route: ^/interactivetool/access/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)/([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)((()|/.*)*)$ rewrite:$4
route-if: empty:${PATH_INFO} addvar:PATH_INFO=/
route-run: seturi:${PATH_INFO}
route-if-not: empty:${QUERY_STRING} seturi:${PATH_INFO}?${QUERY_STRING}
route-if-not: empty:${TARGET_HOST} httpdumb:${TARGET_HOST}
route-label: itpathfail
route: .* break:404 Not Found
route-label: itpathend


galaxy:
Expand Down
9 changes: 5 additions & 4 deletions lib/galaxy/managers/interactivetool.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def __init__(self, app):
def create_entry_points(self, job, tool, entry_points=None, flush=True):
entry_points = entry_points or tool.ports
for entry in entry_points:
ep = self.model.InteractiveToolEntryPoint(job=job, tool_port=entry['port'], entry_url=entry['url'], name=entry['name'])
ep = self.model.InteractiveToolEntryPoint(job=job, tool_port=entry['port'], entry_url=entry['url'], name=entry['name'], requires_domain=entry['requires_domain'])
self.sa_session.add(ep)
if flush:
self.sa_session.flush()
Expand Down Expand Up @@ -258,9 +258,10 @@ def target_if_active(self, trans, entry_point):
entry_point_encoded_id = trans.security.encode_id(entry_point.id)
entry_point_class = entry_point.__class__.__name__.lower()
entry_point_prefix = self.app.config.interactivetools_prefix
interactivetools_proxy_host = self.app.config.interactivetools_proxy_host or request_host
rval = '{}//{}-{}.{}.{}.{}/'.format(protocol, entry_point_encoded_id,
entry_point.token, entry_point_class, entry_point_prefix, interactivetools_proxy_host)
if entry_point.requires_domain:
rval = f'{protocol}//{entry_point_encoded_id}-{entry_point.token}.{entry_point_class}.{entry_point_prefix}.{request_host}/'
else:
rval = self.app.url_for(f'/{entry_point_prefix}/access/{entry_point_class}/{entry_point_encoded_id}/{entry_point.token}/')
if entry_point.entry_url:
rval = '{}/{}'.format(rval.rstrip('/'), entry_point.entry_url.lstrip('/'))
return rval
Expand Down
3 changes: 2 additions & 1 deletion lib/galaxy/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1675,7 +1675,7 @@ class InteractiveToolEntryPoint(Dictifiable, RepresentById):
dict_element_visible_keys = ['id', 'name', 'active', 'created_time', 'modified_time']

def __init__(self, job=None, name=None, token=None, tool_port=None, host=None, port=None, protocol=None,
entry_url=None, info=None, configured=False, deleted=False):
entry_url=None, requires_domain=True, info=None, configured=False, deleted=False):
self.job = job
self.name = name
if not token:
Expand All @@ -1686,6 +1686,7 @@ def __init__(self, job=None, name=None, token=None, tool_port=None, host=None, p
self.port = port
self.protocol = protocol
self.entry_url = entry_url
self.requires_domain = requires_domain
self.info = info or {}
self.configured = configured
self.deleted = deleted
Expand Down
1 change: 1 addition & 0 deletions lib/galaxy/model/mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,7 @@
Column("port", Integer),
Column("protocol", TEXT),
Column("entry_url", TEXT),
Column("requires_domain", Boolean, default=True),
Column("info", JSONType, nullable=True),
Column("configured", Boolean, default=False),
Column("deleted", Boolean, default=False),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""
Adds requires_domain column to InteractiveTools Entry Point (interactivetool_entry_point).
"""
import datetime
import logging

from sqlalchemy import (
Boolean,
Column,
MetaData,
)

from galaxy.model.migrate.versions.util import (
add_column,
drop_column
)

log = logging.getLogger(__name__)
now = datetime.datetime.utcnow
metadata = MetaData()


def upgrade(migrate_engine):
print(__doc__)
metadata.bind = migrate_engine
metadata.reflect()

requires_domain = Column("requires_domain", Boolean, default=None)
add_column(requires_domain, 'interactivetool_entry_point', metadata)


def downgrade(migrate_engine):
metadata.bind = migrate_engine
metadata.reflect()

drop_column('requires_domain', 'interactivetool_entry_point', metadata)
2 changes: 0 additions & 2 deletions lib/galaxy/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2656,8 +2656,6 @@ class InteractiveTool(Tool):
def __init__(self, config_file, tool_source, app, **kwd):
assert app.config.interactivetools_enable, ValueError('Trying to load an InteractiveTool, but InteractiveTools are not enabled.')
super().__init__(config_file, tool_source, app, **kwd)
for port in self.ports:
assert port.get('requires_domain', None), ValueError('InteractiveTools currently only work when requires_domain is True for each entry_point.')

def __remove_interactivetool_by_job(self, job):
if job:
Expand Down
4 changes: 2 additions & 2 deletions lib/galaxy/tools/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,9 +399,9 @@ def __populate_interactivetools(self, param_dict):
it = []
for ep in getattr(self.tool, 'ports', []):
ep_dict = {}
for key in 'port', 'name', 'url':
for key in 'port', 'name', 'url', 'requires_domain':
val = ep.get(key, None)
if val is not None:
if val is not None and not isinstance(val, bool):
val = fill_template(val, context=param_dict, python_template_version=self.tool.python_template_version)
clean_val = []
for line in val.split('\n'):
Expand Down