Add test for proposed prod-like deployment

This commit is contained in:
mvdbeek
2025-09-21 17:01:54 +02:00
parent b204902146
commit 0c09aa9e5c
8 changed files with 97 additions and 29 deletions
+2 -1
View File
@@ -89,7 +89,8 @@ These tools can also be exported to disk and loaded like regular tools, enabling
## Security considerations
User-defined tools share the same security risks as interactive tools..
See https://training.galaxyproject.org/training-material/topics/admin/tutorials/interactive-tools/tutorial.html#securing-interactive-tools for an extended discussion.
See https://training.galaxyproject.org/training-material/topics/admin/tutorials/interactive-tools/tutorial.html#securing-interactive-tools for an extended discussion,
and see https://github.com/galaxyproject/galaxy/blob/dev/test/integration/embedded_pulsar_job_conf.yml#L29 for a simple example that uses embedded pulsar to isolate mounts and disables network access.
While the feature is in beta we recommend that only trusted users are allowed to use this feature.
## Limitations
@@ -1160,6 +1160,7 @@ tools:
# Classes can be used to map groups of tools - the current classes include
# - local (these special tools that aren't parameterized for remote execution - expression tools, upload, etc..)
# - requires_galaxy (these special tools require Galaxy's Python environment during execution)
# - user_defined (these tools are defined by the user and should be routed to an isolated job execution environment)
# If a tool matches multiple classes, it will match the first class according to the order listed
# above in this document (local, requires_galaxy).
class: local
+1 -1
View File
@@ -123,7 +123,7 @@ DEFAULT_JOB_SHELL = "/bin/bash"
DEFAULT_LOCAL_WORKERS = 4
DEFAULT_CLEANUP_JOB = "always"
VALID_TOOL_CLASSES = ["local", "requires_galaxy"]
VALID_TOOL_CLASSES = ["local", "requires_galaxy", "user_defined"]
class JobDestination(Bunch):
+1 -23
View File
@@ -33,6 +33,7 @@ from galaxy_test.base.populators import (
DatasetPopulator,
skip_without_tool,
stage_rules_example,
TOOL_WITH_SHELL_COMMAND,
)
from ._framework import ApiTestCase
@@ -54,29 +55,6 @@ MINIMAL_TOOL_NO_ID = {
"outputs": {"output1": {"format": "txt", "type": "data"}},
}
TOOL_WITH_SHELL_COMMAND = {
"id": "basecommand",
"name": "Base command tool",
"class": "GalaxyUserTool",
"container": "busybox",
"version": "1.0.0",
"shell_command": "cat '$(inputs.input.path)' > output.fastq",
"inputs": [
{
"type": "data",
"name": "input",
"format": "txt",
}
],
"outputs": [
{
"type": "data",
"from_work_dir": "output.fastq",
"name": "output",
}
],
}
class TestsTools:
dataset_populator: DatasetPopulator
@@ -3,12 +3,10 @@ from galaxy.tool_util_models import UserToolSource
from galaxy_test.base.populators import (
DatasetCollectionPopulator,
DatasetPopulator,
)
from ._framework import ApiTestCase
from .test_tools import (
TestsTools,
TOOL_WITH_SHELL_COMMAND,
)
from ._framework import ApiTestCase
from .test_tools import TestsTools
class TestUnprivilegedToolsApi(ApiTestCase, TestsTools):
+23
View File
@@ -141,6 +141,29 @@ workflow_str = resource_string(__name__, "data/test_workflow_1.ga")
# Simple workflow that takes an input and filters with random lines twice in a
# row - first grabbing 8 lines at random and then 6.
workflow_random_x2_str = resource_string(__name__, "data/test_workflow_2.ga")
# example of user defined tool
TOOL_WITH_SHELL_COMMAND = {
"id": "basecommand",
"name": "Base command tool",
"class": "GalaxyUserTool",
"container": "busybox",
"version": "1.0.0",
"shell_command": "cat '$(inputs.input.path)' > output.fastq",
"inputs": [
{
"type": "data",
"name": "input",
"format": "txt",
}
],
"outputs": [
{
"type": "data",
"from_work_dir": "output.fastq",
"name": "output",
}
],
}
DEFAULT_TIMEOUT = 60 # Secs to wait for state to turn ok
@@ -16,7 +16,15 @@ execution:
pulsar_embed:
runner: pulsar_embed
remote_metadata: true
user_defined:
runner: pulsar_embed
remote_metadata: true
docker_enabled: true
require_container: true
docker_net: "none"
tools:
- class: local
environment: local
- class: user_defined
environment: user_defined
@@ -0,0 +1,59 @@
"""Integration tests for user defined tools with Pulsar embedded runner."""
import os
from galaxy.tool_util_models import UserToolSource
from galaxy_test.api.test_tools import TestsTools
from galaxy_test.base.populators import (
DatasetPopulator,
TOOL_WITH_SHELL_COMMAND,
)
from galaxy_test.driver import integration_util
SCRIPT_DIRECTORY = os.path.abspath(os.path.dirname(__file__))
EMBEDDED_PULSAR_JOB_CONFIG_FILE = os.path.join(SCRIPT_DIRECTORY, "embedded_pulsar_job_conf.yml")
class TestUserDefinedToolRecommendedJobSetup(integration_util.IntegrationTestCase, TestsTools):
"""Exercies how user defined tools could be run in production."""
framework_tool_and_types = True
dataset_populator: DatasetPopulator
@classmethod
def handle_galaxy_config_kwds(cls, config):
super().handle_galaxy_config_kwds(config)
config["job_config_file"] = EMBEDDED_PULSAR_JOB_CONFIG_FILE
config["enable_celery_tasks"] = False
config["metadata_strategy"] = "directory"
config["admin_users"] = "udt@galaxy.org"
def setUp(self):
super().setUp()
self.dataset_populator = DatasetPopulator(self.galaxy_interactor)
def test_user_defined_runs_in_correct_destination(self):
with (
self.dataset_populator.test_history() as history_id,
self.dataset_populator.user_tool_execute_permissions(),
):
# Create a new dynamic tool.
# This is a shell command tool that will echo the input dataset.
dynamic_tool = self.dataset_populator.create_unprivileged_tool(UserToolSource(**TOOL_WITH_SHELL_COMMAND))
# Run tool.
dataset = self.dataset_populator.new_dataset(history_id=history_id, content="abc")
response = self._run(
history_id=history_id,
tool_uuid=dynamic_tool["uuid"],
inputs={"input": {"src": "hda", "id": dataset["id"]}},
wait_for_job=True,
assert_ok=True,
)
output_content = self.dataset_populator.get_history_dataset_content(history_id)
assert output_content == "abc\n"
with self._different_user(email="udt@galaxy.org"):
destination_params = self._get(f"/api/jobs/{response['jobs'][0]['id']}/destination_params").json()
assert destination_params["Runner"] == "pulsar_embed"
assert destination_params["require_container"]