diff --git a/lib/galaxy/jobs/__init__.py b/lib/galaxy/jobs/__init__.py
index 0120d44cc16..870e2b28b86 100644
--- a/lib/galaxy/jobs/__init__.py
+++ b/lib/galaxy/jobs/__init__.py
@@ -2455,6 +2455,10 @@ class ComputeEnvironment(object):
def tmp_directory(self):
"""Temp directory of target job - none if HOME should not be set."""
+ @abstractmethod
+ def galaxy_url(self):
+ """URL to access Galaxy API from for this compute environment."""
+
class SimpleComputeEnvironment(object):
@@ -2525,6 +2529,9 @@ class SharedComputeEnvironment(SimpleComputeEnvironment):
def tmp_directory(self):
return self.job_wrapper.tmp_directory()
+ def galaxy_url(self):
+ return self.job_wrapper.get_destination_configuration("galaxy_infrastructure_url")
+
class NoopQueue(object):
"""
diff --git a/lib/galaxy/jobs/runners/condor.py b/lib/galaxy/jobs/runners/condor.py
index 2ddb4030bc3..2a887bd87ab 100644
--- a/lib/galaxy/jobs/runners/condor.py
+++ b/lib/galaxy/jobs/runners/condor.py
@@ -176,7 +176,7 @@ class CondorJobRunner(AsynchronousJobRunner):
job_id = cjs.job_id
galaxy_id_tag = cjs.job_wrapper.get_id_tag()
try:
- if os.stat(cjs.user_log).st_size == cjs.user_log_size:
+ if cjs.job_wrapper.tool.tool_type != 'interactive' and os.stat(cjs.user_log).st_size == cjs.user_log_size:
new_watched.append(cjs)
continue
s1, s4, s7, s5, s9, log_size = summarize_condor_log(cjs.user_log, job_id)
diff --git a/lib/galaxy/jobs/runners/pulsar.py b/lib/galaxy/jobs/runners/pulsar.py
index fa73cdcdc05..3e208bc9b53 100644
--- a/lib/galaxy/jobs/runners/pulsar.py
+++ b/lib/galaxy/jobs/runners/pulsar.py
@@ -1025,6 +1025,9 @@ class PulsarComputeEnvironment(ComputeEnvironment):
# meantime.
return None
+ def galaxy_url(self):
+ return self.job_wrapper.get_destination_configuration("galaxy_infrastructure_url")
+
class UnsupportedPulsarException(Exception):
diff --git a/lib/galaxy/managers/library_datasets.py b/lib/galaxy/managers/library_datasets.py
index 2f844e57c15..3b52b6a6227 100644
--- a/lib/galaxy/managers/library_datasets.py
+++ b/lib/galaxy/managers/library_datasets.py
@@ -139,7 +139,7 @@ class LibraryDatasetsManager(datasets.DatasetAssociationManager):
val = validation.validate_and_sanitize_basestring(key, val)
validated_payload[key] = val
if key in ('tags'):
- val = validation.validate_and_sanitize_basestring_list(key, val)
+ val = validation.validate_and_sanitize_basestring_list(key, util.listify(val))
validated_payload[key] = val
return validated_payload
diff --git a/lib/galaxy/tool_util/parser/xml.py b/lib/galaxy/tool_util/parser/xml.py
index ecfaf88cce1..b453f1c82ca 100644
--- a/lib/galaxy/tool_util/parser/xml.py
+++ b/lib/galaxy/tool_util/parser/xml.py
@@ -138,9 +138,17 @@ class XmlToolSource(ToolSource):
environment_variables = []
for environment_variable_el in environment_variables_el.findall("environment_variable"):
+ template = environment_variable_el.text
+ inject = environment_variable_el.get("inject")
+ if inject:
+ assert not template, "Cannot specify inject and environment variable template."
+ assert inject in ["api_key"]
+ if template:
+ assert not inject, "Cannot specify inject and environment variable template."
definition = {
"name": environment_variable_el.get("name"),
- "template": environment_variable_el.text,
+ "template": template,
+ "inject": inject,
"strip": string_as_bool(environment_variable_el.get("strip", False)),
}
environment_variables.append(
diff --git a/lib/galaxy/tool_util/xsd/galaxy.xsd b/lib/galaxy/tool_util/xsd/galaxy.xsd
index 8946797a945..05f7355b97b 100644
--- a/lib/galaxy/tool_util/xsd/galaxy.xsd
+++ b/lib/galaxy/tool_util/xsd/galaxy.xsd
@@ -4676,6 +4676,21 @@ variable instead of shell variable.
```
+### inject
+
+The Galaxy user's API key can be injected into an environment variable by setting ``inject``
+attribute to ``api_key`` (e.g. ``inject="api_key"``).
+
+```xml
+
+
+
+```
+
+The framework allows setting this via environment variable and not via templating variables
+in order to discourage setting the actual values of these keys as command line arguments.
+On shared systems this provides some security by preventing a simple process listing command
+from exposing keys.
]]>
@@ -4686,15 +4701,27 @@ variable instead of shell variable.
define.
+
+
+ Special variable to inject into the environment variable. Currently 'api_key' is the only option and will cause the user's API key to be injected via this environment variable.
+
+
-
- Whether to strip leading and trailing whitespace from the calculated value before exporting the environment variable.
-
+
+ Whether to strip leading and trailing whitespace from the calculated value before exporting the environment variable.
+
-
+
+
+
+
+
+
+
+
) which may not be the same value
# as self.tool_data_path, we'll parse the path to get the filename and see if it is
# in self.tool_data_path.
file_path, file_name = os.path.split(filename)
- if file_path and file_path != self.tool_data_path:
+ if file_path != self.tool_data_path:
corrected_filename = os.path.join(self.tool_data_path, file_name)
if self.tool_data_path_files.exists(corrected_filename):
filename = corrected_filename
found = True
+ elif not from_shed_config and self.tool_data_path_files.exists("%s.sample" % corrected_filename):
+ log.info("Could not find tool data %s, reading sample" % corrected_filename)
+ filename = "%s.sample" % corrected_filename
+ found = True
errors = []
if found:
diff --git a/lib/galaxy/tools/evaluation.py b/lib/galaxy/tools/evaluation.py
index 04b0876c698..cd03a0fee80 100644
--- a/lib/galaxy/tools/evaluation.py
+++ b/lib/galaxy/tools/evaluation.py
@@ -69,7 +69,7 @@ class ToolEvaluator(object):
incoming = self.tool.params_from_strings(incoming, self.app)
# Full parameter validation
- request_context = WorkRequestContext(app=self.app, user=job.history and job.history.user, history=job.history)
+ request_context = WorkRequestContext(app=self.app, user=self._user, history=self._history)
def validate_inputs(input, value, context, **kwargs):
value = input.from_json(value, request_context, context)
@@ -135,7 +135,9 @@ class ToolEvaluator(object):
param_dict["input"] = input
param_dict['__datatypes_config__'] = param_dict['GALAXY_DATATYPES_CONF_FILE'] = os.path.join(job_working_directory, 'registry.xml')
-
+ if self._history:
+ param_dict['__history_id__'] = self.app.security.encode_id(self._history.id)
+ param_dict['__galaxy_url__'] = self.compute_environment.galaxy_url()
param_dict.update(self.tool.template_macro_params)
# All parameters go into the param_dict
param_dict.update(incoming)
@@ -536,9 +538,19 @@ class ToolEvaluator(object):
directory = self.local_working_directory
environment_variable = environment_variable_def.copy()
environment_variable_template = environment_variable_def["template"]
+ inject = environment_variable_def.get("inject")
+ if inject == "api_key":
+ if self._user:
+ from galaxy.managers import api_keys
+ environment_variable_template = api_keys.ApiKeyManager(self.app).get_or_create_api_key(self._user)
+ else:
+ environment_variable_template = ""
+ is_template = False
+ else:
+ is_template = True
fd, config_filename = tempfile.mkstemp(dir=directory)
os.close(fd)
- self.__write_workdir_file(config_filename, environment_variable_template, param_dict, strip=environment_variable_def.get("strip", False))
+ self.__write_workdir_file(config_filename, environment_variable_template, param_dict, is_template=is_template, strip=environment_variable_def.get("strip", False))
config_file_basename = os.path.basename(config_filename)
# environment setup in job file template happens before `cd $working_directory`
environment_variable["value"] = '`cat "$_GALAXY_JOB_DIR/%s"`' % config_file_basename
@@ -624,3 +636,12 @@ class ToolEvaluator(object):
compat.
"""
return self.compute_environment.sep().join(args)
+
+ @property
+ def _history(self):
+ return self.job.history
+
+ @property
+ def _user(self):
+ history = self._history
+ return history and history.user
diff --git a/lib/galaxy/webapps/galaxy/controllers/user.py b/lib/galaxy/webapps/galaxy/controllers/user.py
index 8e204ff1c24..56394bcf968 100644
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -227,7 +227,7 @@ class User(BaseUIController, UsesFormDefinitionsMixin, CreatesApiKeysMixin):
# while sometimes, so we don't want to block on logout.
send_local_control_task(trans.app,
"recalculate_user_disk_usage",
- {"user_id": trans.security.encode_id(trans.user.id)})
+ kwargs={"user_id": trans.security.encode_id(trans.user.id)})
# Since logging an event requires a session, we'll log prior to ending the session
trans.log_event("User logged out")
trans.handle_user_logout(logout_all=logout_all)
diff --git a/test/functional/tools/environment_variables_inject.xml b/test/functional/tools/environment_variables_inject.xml
new file mode 100644
index 00000000000..7b8cb5b6955
--- /dev/null
+++ b/test/functional/tools/environment_variables_inject.xml
@@ -0,0 +1,42 @@
+
+
+
+
+ $__history_id__
+ $__galaxy_url__
+
+
+ echo "\$GX_API" > '$out_file_api_key';
+ echo "\$GX_URL" > '$out_file_galaxy_url';
+ echo "\$GX_HISTORY_ID" > '$out_file_history_id'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/functional/tools/interactivetool_simple.xml b/test/functional/tools/interactivetool_simple.xml
index 54820286ef4..63e4c4b410e 100644
--- a/test/functional/tools/interactivetool_simple.xml
+++ b/test/functional/tools/interactivetool_simple.xml
@@ -1,4 +1,4 @@
-
+
galaxy/test-http-example:0.1
diff --git a/test/functional/tools/interactivetool_two_entry_points.xml b/test/functional/tools/interactivetool_two_entry_points.xml
index 8693360840a..ffb59974df8 100644
--- a/test/functional/tools/interactivetool_two_entry_points.xml
+++ b/test/functional/tools/interactivetool_two_entry_points.xml
@@ -1,4 +1,4 @@
-
+
galaxy/test-http-example:0.1
diff --git a/test/functional/tools/samples_tool_conf.xml b/test/functional/tools/samples_tool_conf.xml
index a12e997bce9..a867645e74f 100644
--- a/test/functional/tools/samples_tool_conf.xml
+++ b/test/functional/tools/samples_tool_conf.xml
@@ -8,6 +8,7 @@
+
diff --git a/test/unit/tool_util/test_parsing.py b/test/unit/tool_util/test_parsing.py
index 434e631ecf4..da646d19894 100644
--- a/test/unit/tool_util/test_parsing.py
+++ b/test/unit/tool_util/test_parsing.py
@@ -618,6 +618,16 @@ class CollectionOutputYamlTestCase(BaseLoaderTestCase):
assert len(output_collections) == 1
+class EnvironmentVariablesTestCase(BaseLoaderTestCase):
+ source_file_name = os.path.join(galaxy_directory(), "test/functional/tools/environment_variables.xml")
+ source_contents = None
+
+ def test_tests(self):
+ tests_dict = self._tool_source.parse_tests_to_dict()
+ tests = tests_dict["tests"]
+ assert len(tests) == 1
+
+
class ExpectationsTestCase(BaseLoaderTestCase):
source_file_name = os.path.join(galaxy_directory(), "test/functional/tools/detect_errors.xml")
source_contents = None
diff --git a/test/unit/tools/test_evaluation.py b/test/unit/tools/test_evaluation.py
index db94baf4cca..fc06b2782dd 100644
--- a/test/unit/tools/test_evaluation.py
+++ b/test/unit/tools/test_evaluation.py
@@ -33,6 +33,7 @@ from ..tools_support import UsesApp
# To Test:
# - param_file handling.
TEST_TOOL_DIRECTORY = "/path/to/the/tool"
+TEST_GALAXY_URL = "http://mycool.galaxyproject.org:8456"
class ToolEvaluatorTestCase(TestCase, UsesApp):
@@ -42,6 +43,7 @@ class ToolEvaluatorTestCase(TestCase, UsesApp):
self.tool = MockTool(self.app)
self.job = Job()
self.job.history = History()
+ self.job.history.id = 42
self.job.parameters = [JobParameter(name="thresh", value="4")]
self.evaluator = ToolEvaluator(self.app, self.tool, self.job, self.test_directory)
@@ -65,6 +67,18 @@ class ToolEvaluatorTestCase(TestCase, UsesApp):
command_line, extra_filenames, _ = self.evaluator.build()
self.assertEqual(command_line, "prog1 4 5")
+ def test_eval_galaxy_url(self):
+ self.tool._command_line = "prog1 $__galaxy_url__"
+ self._set_compute_environment()
+ command_line, extra_filenames, _ = self.evaluator.build()
+ self.assertEqual(command_line, "prog1 %s" % TEST_GALAXY_URL)
+
+ def test_eval_history_id(self):
+ self.tool._command_line = "prog1 '$__history_id__'"
+ self._set_compute_environment()
+ command_line, extra_filenames, _ = self.evaluator.build()
+ self.assertEqual(command_line, "prog1 '%s'" % self.app.security.encode_id(42))
+
def test_conditional_evaluation(self):
select_xml = XML('''''')
parameter = SelectToolParameter(self.tool, select_xml)
@@ -264,6 +278,9 @@ class TestComputeEnvironment(SimpleComputeEnvironment):
def tool_directory(self):
return TEST_TOOL_DIRECTORY
+ def galaxy_url(self):
+ return TEST_GALAXY_URL
+
class MockTool(object):
diff --git a/tools/interactive/interactivetool_askomics.xml b/tools/interactive/interactivetool_askomics.xml
index c1ce9d3bd11..b5f0b4d56a4 100644
--- a/tools/interactive/interactivetool_askomics.xml
+++ b/tools/interactive/interactivetool_askomics.xml
@@ -10,15 +10,8 @@
- ${__app__.config.galaxy_infrastructure_url}
-
- #if $__user__:
- #for $api_key in $__user__.api_keys:
- ${api_key.key}
- #break
- #end for
- #end if
-
+ $__galaxy_url__
+
- ${__app__.security.encode_id($jupyter_notebook.history_id)}
- ${__app__.config.galaxy_infrastructure_url}
+ $__history_id__
+ $__galaxy_url__
8080
- ${__app__.config.galaxy_infrastructure_url}
-
- #if $__user__:
- #for $api_key in $__user__.api_keys:
- ${api_key.key}
- #break
- #end for
- #end if
-
+ $__galaxy_url__
+