diff --git a/lib/galaxy/jobs/__init__.py b/lib/galaxy/jobs/__init__.py index 82107f16094..7329f95546f 100644 --- a/lib/galaxy/jobs/__init__.py +++ b/lib/galaxy/jobs/__init__.py @@ -1030,6 +1030,7 @@ class JobWrapper(object, HasResourceParameters): dataset_assoc.dataset.dataset.state = dataset_assoc.dataset.dataset.states.PAUSED dataset_assoc.dataset.info = message self.sa_session.add(dataset_assoc.dataset) + log.debug("Pausing Job '%d', %s", job.id, message) job.set_state(job.states.PAUSED) self.sa_session.add(job) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 225c8205499..68610b8eabd 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -3139,6 +3139,13 @@ class DatasetCollection(object, Dictifiable, UsesAnnotations): object_session(self).flush() return new_collection + def replace_failed_elements(self, replacements): + for element in self.elements: + if element.element_object in replacements: + if element.element_type == 'hda': + element.hda = replacements[element.element_object] + # TODO: handle the case where elements are collections + def set_from_dict(self, new_data): # Nothing currently editable in this class. return {} diff --git a/lib/galaxy/tools/actions/__init__.py b/lib/galaxy/tools/actions/__init__.py index 737ca465804..a0088e8344e 100644 --- a/lib/galaxy/tools/actions/__init__.py +++ b/lib/galaxy/tools/actions/__init__.py @@ -509,6 +509,8 @@ class DefaultToolAction(object): # Duplicate PJAs before remap. for pjaa in old_job.post_job_actions: current_job.add_post_job_action(pjaa.post_job_action) + remapped_hdas = {} + input_hdcas = set() for jtod in old_job.output_datasets: for (job_to_remap, jtid) in [(jtid.job, jtid) for jtid in jtod.dataset.dependent_jobs]: if (trans.user is not None and job_to_remap.user_id == trans.user.id) or ( @@ -520,6 +522,9 @@ class DefaultToolAction(object): hda.state = hda.states.NEW hda.info = None input_values = dict([(p.name, json.loads(p.value)) for p in job_to_remap.parameters]) + remapped_hdas[jtod.dataset] = out_data[jtod.name] + for jtidca in job_to_remap.input_dataset_collections: + input_hdcas.add(jtidca.dataset_collection) old_dataset_id = jtod.dataset_id new_dataset_id = out_data[jtod.name].id input_values = update_dataset_ids(input_values, {old_dataset_id: new_dataset_id}, src='hda') @@ -530,6 +535,12 @@ class DefaultToolAction(object): log.info('Job %s input HDA %s remapped to new HDA %s' % (job_to_remap.id, jtod.dataset.id, jtid.dataset.id)) trans.sa_session.add(job_to_remap) trans.sa_session.add(jtid) + for hdca in input_hdcas: + hdca.collection.replace_failed_elements(remapped_hdas) + if hdca.implicit_collection_jobs: + for job in hdca.implicit_collection_jobs.jobs: + if job.job_id == old_job.id: + job.job_id = current_job.id jtod.dataset.visible = False trans.sa_session.add(jtod) except Exception: diff --git a/test/api/test_workflows.py b/test/api/test_workflows.py index 5f470071492..edf397cedf2 100644 --- a/test/api/test_workflows.py +++ b/test/api/test_workflows.py @@ -723,6 +723,54 @@ steps: assert_ok=False) assert unpaused_dataset['state'] == 'ok' + @skip_without_tool("fail_identifier") + @skip_without_tool("identifier_multiple_in_conditional") + def test_workflow_resume_with_mapped_over_input(self): + with self.dataset_populator.test_history() as history_id: + job_summary = self._run_jobs(""" +class: GalaxyWorkflow +steps: + - label: input_datasets + type: input_collection + - label: fail_identifier_1 + tool_id: fail_identifier + state: + input1: + $link: input_datasets + failbool: true + - tool_id: identifier_collection + state: + input1: + $link: fail_identifier_1#out_file1 +test_data: + input_datasets: + type: list + elements: + - identifier: fail + value: 1.fastq + type: File + - identifier: success + value: 1.fastq + type: File +""", history_id=history_id, assert_ok=False, wait=False) + self.wait_for_invocation_and_jobs(history_id, job_summary.workflow_id, job_summary.invocation_id, assert_ok=False) + history_contents = self.dataset_populator._get_contents_request(history_id=history_id).json() + paused_dataset = history_contents[-1] + failed_dataset = self.dataset_populator.get_history_dataset_details(history_id, hid=5, assert_ok=False) + assert paused_dataset['state'] == 'paused', paused_dataset + assert failed_dataset['state'] == 'error', failed_dataset + inputs = {"input1": {'values': [{'src': 'hda', + 'id': history_contents[0]['id']}] + }, + "failbool": "false", + "rerun_remap_job_id": failed_dataset['creating_job']} + self.dataset_populator.run_tool(tool_id='fail_identifier', + inputs=inputs, + history_id=history_id, + assert_ok=True) + unpaused_dataset = self.dataset_populator.get_history_dataset_details(history_id, wait=True, assert_ok=False) + assert unpaused_dataset['state'] == 'ok' + @skip_without_tool("collection_creates_pair") def test_workflow_run_output_collection_mapping(self): workflow_id = self._upload_yaml_workflow(""" diff --git a/test/base/populators.py b/test/base/populators.py index c046a78bf2a..c8388d06bcc 100644 --- a/test/base/populators.py +++ b/test/base/populators.py @@ -275,19 +275,19 @@ class BaseDatasetPopulator(object): data = {} if filename: data["filename"] = filename - display_response = self.__get_contents_request(history_id, "/%s/display" % dataset_id, data=data) + display_response = self._get_contents_request(history_id, "/%s/display" % dataset_id, data=data) assert display_response.status_code == 200, display_response.content return display_response.content def get_history_dataset_details(self, history_id, **kwds): dataset_id = self.__history_content_id(history_id, **kwds) - details_response = self.__get_contents_request(history_id, "/datasets/%s" % dataset_id) + details_response = self._get_contents_request(history_id, "/datasets/%s" % dataset_id) assert details_response.status_code == 200 return details_response.json() def get_history_collection_details(self, history_id, **kwds): hdca_id = self.__history_content_id(history_id, **kwds) - details_response = self.__get_contents_request(history_id, "/dataset_collections/%s" % hdca_id) + details_response = self._get_contents_request(history_id, "/dataset_collections/%s" % hdca_id) assert details_response.status_code == 200, details_response.content return details_response.json() @@ -320,7 +320,7 @@ class BaseDatasetPopulator(object): history_content_id = kwds["dataset"]["id"] else: hid = kwds.get("hid", None) # If not hid, just grab last dataset - history_contents = self.__get_contents_request(history_id).json() + history_contents = self._get_contents_request(history_id).json() if hid: history_content_id = None for history_item in history_contents: @@ -333,7 +333,7 @@ class BaseDatasetPopulator(object): history_content_id = history_contents[-1]["id"] return history_content_id - def __get_contents_request(self, history_id, suffix="", data={}): + def _get_contents_request(self, history_id, suffix="", data={}): url = "histories/%s/contents" % history_id if suffix: url = "%s%s" % (url, suffix) diff --git a/test/functional/tools/fail_identifier.xml b/test/functional/tools/fail_identifier.xml new file mode 100644 index 00000000000..c9f4aac2aa4 --- /dev/null +++ b/test/functional/tools/fail_identifier.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + diff --git a/test/functional/tools/samples_tool_conf.xml b/test/functional/tools/samples_tool_conf.xml index 5bd62a7cbb8..a92448627e5 100644 --- a/test/functional/tools/samples_tool_conf.xml +++ b/test/functional/tools/samples_tool_conf.xml @@ -96,6 +96,7 @@ +