diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index adbdbdda1714625cc2f934509ad32cc588b6c3fc..7c0209c6a9819e29dc83c1ce16c2cf888c90f1a3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,8 +9,8 @@ variables: CONTAINER_GALAXY_URL: "${NDIP_DOCKER_REPOSITORY}/${CI_PROJECT_PATH}" CONTAINER_GALAXY_BASE_URL: "${CONTAINER_GALAXY_URL}/base" CONTAINER_GALAXY_COMMIT_URL: "${CONTAINER_GALAXY_URL}/commit" - GALAXY_VERSION_PYTHON: 25.1.dev3+ornl - GALAXY_VERSION_DOCKER: 25.1.dev3.ornl + GALAXY_VERSION_PYTHON: 25.1.dev4+ornl + GALAXY_VERSION_DOCKER: 25.1.dev4.ornl # This import is for the func_rse_docker_* functions before_script: diff --git a/lib/galaxy/objectstore/rucio.py b/lib/galaxy/objectstore/rucio.py index b42f86dcdcdc57f4b599c65e82af0a056daa7171..5dba4560cf37a6d43685db817473654d20e3e988 100644 --- a/lib/galaxy/objectstore/rucio.py +++ b/lib/galaxy/objectstore/rucio.py @@ -165,6 +165,7 @@ class RucioBroker: temp_directory = self.extra_dirs["temp"] self.rucio_config_path = os.path.join(temp_directory, "rucio.cfg") key_for_pass = "password" + os.makedirs(temp_directory, exist_ok=True) with open(self.rucio_config_path, "w") as f: f.write( f"""[client] diff --git a/lib/galaxy/tools/__init__.py b/lib/galaxy/tools/__init__.py index 60f94fcc19af35776c8fca93e78c2b0ecaff36db..78a198a1da9b64ba6bf417a4ad1cabc97b4eaaaf 100644 --- a/lib/galaxy/tools/__init__.py +++ b/lib/galaxy/tools/__init__.py @@ -1472,6 +1472,14 @@ class Tool(UsesDictVisibleKeys, ToolParameterBundle): self.resource_requirements = resource_requirements self.javascript_requirements = javasscript_requirements self.credentials = credentials + if self.credentials: + if not self.docker_env_pass_through: + self.docker_env_pass_through = [] + for credential in self.credentials: + for secret in credential.secrets: + self.docker_env_pass_through.append(secret.inject_as_env) + for variable in credential.variables: + self.docker_env_pass_through.append(variable.inject_as_env) required_files = tool_source.parse_required_files() if required_files is None: diff --git a/lib/galaxy/workflow/modules.py b/lib/galaxy/workflow/modules.py index 54046fab5b5162c2c60f7751611104780165c628..88c5ac1e5faf267e0f5d5e55565856953e32f558 100644 --- a/lib/galaxy/workflow/modules.py +++ b/lib/galaxy/workflow/modules.py @@ -29,6 +29,7 @@ from galaxy.exceptions import ( ) from galaxy.job_execution.actions.post import ActionBox from galaxy.job_execution.compute_environment import ComputeEnvironment +from galaxy.managers.credentials import _build_user_credentials_query from galaxy.model import ( Job, PostJobAction, @@ -42,6 +43,11 @@ from galaxy.model.dataset_collections import matching from galaxy.model.dataset_collections.query import HistoryQuery from galaxy.model.dataset_collections.type_description import COLLECTION_TYPE_DESCRIPTION_FACTORY from galaxy.model.dataset_collections.types.sample_sheet_util import validate_column_definitions +from galaxy.schema.credentials import ( + CredentialsContext, + SelectedGroup, + ServiceCredentialsContext, +) from galaxy.schema.invocation import ( CancelReason, FailureReason, @@ -2504,7 +2510,7 @@ class ToolModule(WorkflowModule): for pja in step.post_job_actions: if pja.action_type == "ValidateOutputsAction": validate_outputs = True - + credentials_context = self._resolve_credentials_context(tool) execution_tracker = execute( trans=self.trans, tool=tool, @@ -2520,6 +2526,7 @@ class ToolModule(WorkflowModule): ), completed_jobs=completed_jobs, workflow_resource_parameters=resource_parameters, + credentials_context=credentials_context, ) complete = True except PartialJobExecution as pje: @@ -2583,6 +2590,38 @@ class ToolModule(WorkflowModule): self.trans, self.trans.sa_session, pja, step_inputs, step_outputs, replacement_dict ) + def _resolve_credentials_context(self, tool: "Tool") -> Optional[CredentialsContext]: + """Auto-resolve the user's current credentials for a tool in workflow execution.""" + if not tool.credentials: + return None + trans = self.trans + if not trans.user: + return None + stmt = _build_user_credentials_query( + user_id=trans.user.id, + source_type="tool", + source_id=tool.id, + current_group_only=True, + ) + results = trans.sa_session.execute(stmt).tuples().all() + if not results: + return None + encode = trans.security.encode_id + seen = {} + for user_cred, group, _cred in results: + key = (user_cred.id, user_cred.name, user_cred.version) + if key not in seen: + seen[key] = ServiceCredentialsContext( + user_credentials_id=encode(user_cred.id), + name=user_cred.name, + version=user_cred.version, + selected_group=SelectedGroup( + id=encode(group.id), + name=group.name, + ), + ) + return CredentialsContext(root=list(seen.values())) + def _handle_post_job_actions(self, step, job, replacement_dict): # Create new PJA associations with the created job, to be run on completion. # PJA Parameter Replacement (only applies to immediate actions-- rename specifically, for now) diff --git a/test/unit/tool_shed/test_files/toolshed_community_files.tgz b/test/unit/tool_shed/test_files/toolshed_community_files.tgz new file mode 100644 index 0000000000000000000000000000000000000000..af271d0219f4beb81947b62d741fef1472e713f1 Binary files /dev/null and b/test/unit/tool_shed/test_files/toolshed_community_files.tgz differ diff --git a/test/unit/tool_shed/test_shed_index.py b/test/unit/tool_shed/test_shed_index.py index 1bfce9adf3d3e42efea94b7d90ad196b737652b8..a9ac528c9561bdc9f15aedea863fd5eb7568bc64 100644 --- a/test/unit/tool_shed/test_shed_index.py +++ b/test/unit/tool_shed/test_shed_index.py @@ -3,6 +3,7 @@ import shutil import tempfile from collections import namedtuple from io import BytesIO +from pathlib import Path import pytest import requests @@ -11,7 +12,7 @@ from whoosh import index from galaxy.util.compression_utils import CompressedFile from tool_shed.util.shed_index import build_index -URL = "https://github.com/mvdbeek/toolshed-test-data/blob/master/toolshed_community_files.tgz?raw=true" +PATH = Path(__file__).resolve().parent / "test_files" / "toolshed_community_files.tgz" @pytest.fixture @@ -26,9 +27,8 @@ def whoosh_index_dir(): @pytest.fixture(scope="module") def community_file_dir(): extracted_archive_dir = tempfile.mkdtemp() - response = requests.get(URL) - response.raise_for_status() - b = BytesIO(response.content) + with open(PATH, "rb") as community_file: + b = BytesIO(community_file.read()) with CompressedFile.open_tar(b) as tar: tar.extractall(extracted_archive_dir) try: @@ -39,12 +39,16 @@ def community_file_dir(): @pytest.fixture() def community_file_structure(community_file_dir): - community = namedtuple("community", "file_path hgweb_config_dir hgweb_repo_prefix dburi") + community = namedtuple( + "community", "file_path hgweb_config_dir hgweb_repo_prefix dburi" + ) return community( file_path=os.path.join(community_file_dir, "database", "community_files"), hgweb_config_dir=community_file_dir, hgweb_repo_prefix="repos/", - dburi="sqlite:///{}".format(os.path.join(community_file_dir, "database", "community.sqlite")), + dburi="sqlite:///{}".format( + os.path.join(community_file_dir, "database", "community.sqlite") + ), )