Skip to content

Commit 0d1fb3f

Browse files
denis-yuentetron
authored andcommitted
WIP Fix resolver for imports (#1093)
* Fix resolver for TRS
1 parent 6e9f82a commit 0d1fb3f

File tree

7 files changed

+212
-6
lines changed

7 files changed

+212
-6
lines changed

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ include tests/wf/*
99
include tests/override/*
1010
include tests/checker_wf/*
1111
include tests/subgraph/*
12+
include tests/trs/*
1213
include cwltool/schemas/v1.0/*.yml
1314
include cwltool/schemas/v1.0/*.yml
1415
include cwltool/schemas/v1.0/*.md

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ Running tests locally
498498

499499
- Running basic tests ``(/tests)``:
500500

501-
To run the basis tests after installing `cwltool` execute the following:
501+
To run the basic tests after installing `cwltool` execute the following:
502502

503503
.. code:: bash
504504

cwltool/resolver.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,17 @@
1313
else:
1414
from pathlib import Path
1515

16+
if not getattr(__builtins__, "WindowsError", None):
17+
class WindowsError(OSError): pass
1618

1719
def resolve_local(document_loader, uri):
1820
pathpart, frag = urllib.parse.urldefrag(uri)
19-
pathobj = Path(pathpart).resolve()
21+
22+
try:
23+
pathobj = Path(pathpart).resolve()
24+
except (WindowsError, OSError):
25+
_logger.debug("local resolver could not resolve %s", uri)
26+
return None
2027

2128
if pathobj.is_file():
2229
if frag:
@@ -48,19 +55,35 @@ def tool_resolver(document_loader, uri):
4855

4956

5057
ga4gh_tool_registries = ["https://dockstore.org/api"]
51-
GA4GH_TRS = "{0}/api/ga4gh/v2/tools/{1}/versions/{2}/plain-CWL/descriptor"
58+
# in the TRS registry, a primary descriptor can be reached at {0}/api/ga4gh/v2/tools/{1}/versions/{2}/plain-CWL/descriptor
59+
# The primary descriptor is a CommandLineTool in the case that the files endpoint only describes one file
60+
# When the primary descriptor is a Workflow, files need to be imported without stripping off "descriptor", looking at the files endpoint is a workaround
61+
# tested with TRS version 2.0.0-beta.2
62+
# TODO not stripping off "descriptor" when looking for local imports would also work https://github.com/ga4gh/tool-registry-service-schemas/blob/2.0.0-beta.2/src/main/resources/swagger/ga4gh-tool-discovery.yaml#L273
63+
GA4GH_TRS_FILES = "{0}/api/ga4gh/v2/tools/{1}/versions/{2}/CWL/files"
64+
GA4GH_TRS_PRIMARY_DESCRIPTOR = "{0}/api/ga4gh/v2/tools/{1}/versions/{2}/plain-CWL/descriptor/{3}"
65+
5266

5367
def resolve_ga4gh_tool(document_loader, uri):
5468
path, version = uri.partition(":")[::2]
5569
if not version:
5670
version = "latest"
5771
for reg in ga4gh_tool_registries:
58-
ds = GA4GH_TRS.format(reg, urllib.parse.quote(path, ""),
59-
urllib.parse.quote(version, ""))
72+
ds = GA4GH_TRS_FILES.format(reg, urllib.parse.quote(path, ""), urllib.parse.quote(version, ""))
6073
try:
74+
_logger.debug("Head path is %s", ds)
6175
resp = document_loader.session.head(ds)
6276
resp.raise_for_status()
63-
return ds
77+
78+
_logger.debug("Passed head path of %s", ds)
79+
80+
resp = document_loader.session.get(ds)
81+
for file_listing in resp.json():
82+
if file_listing.get('file_type') == 'PRIMARY_DESCRIPTOR':
83+
primary_path = file_listing.get('path')
84+
ds2 = GA4GH_TRS_PRIMARY_DESCRIPTOR.format(reg, urllib.parse.quote(path, ""), urllib.parse.quote(version, ""), urllib.parse.quote(primary_path, ""))
85+
_logger.debug("Resolved %s", ds2)
86+
return ds2
6487
except Exception:
6588
pass
6689
return None

tests/test_trs.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
from __future__ import absolute_import
2+
3+
import mock
4+
5+
from cwltool.main import main
6+
from .util import get_data
7+
8+
def mocked_requests_head(*args):
9+
10+
class MockResponse:
11+
def __init__(self, json_data, status_code, raise_for_status=None):
12+
self.json_data = json_data
13+
self.status_code = status_code
14+
self.raise_for_status = mock.Mock()
15+
self.raise_for_status.side_effect = raise_for_status
16+
17+
def json(self):
18+
return self.json_data
19+
20+
return MockResponse(None, 200)
21+
22+
23+
def mocked_requests_get(*args):
24+
25+
class MockResponse:
26+
def __init__(self, json_data, status_code, raise_for_status=None):
27+
self.json_data = json_data
28+
self.text = json_data
29+
self.status_code = status_code
30+
self.raise_for_status = mock.Mock()
31+
self.raise_for_status.side_effect = raise_for_status
32+
33+
def json(self):
34+
return self.json_data
35+
36+
if args[0] == 'https://dockstore.org/api/api/ga4gh/v2/tools/quay.io%2Fbriandoconnor%2Fdockstore-tool-md5sum/versions/1.0.4/CWL/files':
37+
return MockResponse(
38+
[{"file_type": "CONTAINERFILE", "path": "Dockerfile"}, {"file_type": "PRIMARY_DESCRIPTOR", "path": "Dockstore.cwl"},
39+
{"file_type": "TEST_FILE", "path": "test.json"}], 200)
40+
elif args[0] == 'https://dockstore.org/api/api/ga4gh/v2/tools/quay.io%2Fbriandoconnor%2Fdockstore-tool-md5sum/versions/1.0.4/plain-CWL/descriptor/Dockstore.cwl':
41+
string = open(get_data("tests/trs/Dockstore.cwl"), "r").read()
42+
return MockResponse(string, 200)
43+
elif args[0] == 'https://dockstore.org/api/api/ga4gh/v2/tools/%23workflow%2Fgithub.com%2Fdockstore-testing%2Fmd5sum-checker/versions/develop/plain-CWL/descriptor/md5sum-tool.cwl':
44+
string = open(get_data("tests/trs/md5sum-tool.cwl"), "r").read()
45+
return MockResponse(string, 200)
46+
elif args[0] == 'https://dockstore.org/api/api/ga4gh/v2/tools/%23workflow%2Fgithub.com%2Fdockstore-testing%2Fmd5sum-checker/versions/develop/plain-CWL/descriptor/md5sum-workflow.cwl':
47+
string = open(get_data("tests/trs/md5sum-workflow.cwl"), "r").read()
48+
return MockResponse(string, 200)
49+
elif args[
50+
0] == 'https://dockstore.org/api/api/ga4gh/v2/tools/%23workflow%2Fgithub.com%2Fdockstore-testing%2Fmd5sum-checker/versions/develop/CWL/files':
51+
return MockResponse(
52+
[{"file_type": "TEST_FILE", "path": "md5sum-input-cwl.json"}, {"file_type": "SECONDARY_DESCRIPTOR", "path": "md5sum-tool.cwl"},
53+
{"file_type": "PRIMARY_DESCRIPTOR", "path": "md5sum-workflow.cwl"}], 200)
54+
55+
print ("A mocked call to TRS missed, target was %s", args[0])
56+
return MockResponse(None, 404)
57+
58+
59+
@mock.patch('requests.Session.head', side_effect=mocked_requests_head)
60+
@mock.patch('requests.Session.get', side_effect=mocked_requests_get)
61+
def test_tool_trs_template(mock_head, mock_get):
62+
params = ["--make-template", r"quay.io/briandoconnor/dockstore-tool-md5sum:1.0.4"]
63+
return_value = main(params)
64+
mock_head.assert_called()
65+
mock_get.assert_called()
66+
assert return_value == 0
67+
68+
69+
@mock.patch('requests.Session.head', side_effect=mocked_requests_head)
70+
@mock.patch('requests.Session.get', side_effect=mocked_requests_get)
71+
def test_workflow_trs_template(mock_head, mock_get):
72+
params = ["--make-template", r"#workflow/github.com/dockstore-testing/md5sum-checker:develop"]
73+
return_value = main(params)
74+
mock_head.assert_called()
75+
mock_get.assert_called()
76+
assert return_value == 0

tests/trs/Dockstore.cwl

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/usr/bin/env cwl-runner
2+
3+
class: CommandLineTool
4+
id: Md5sum
5+
label: Simple md5sum tool
6+
cwlVersion: v1.0
7+
8+
$namespaces:
9+
dct: http://purl.org/dc/terms/
10+
foaf: http://xmlns.com/foaf/0.1/
11+
12+
doc: |
13+
[![Docker Repository on Quay.io](https://quay.io/repository/briandoconnor/dockstore-tool-md5sum/status "Docker Repository on Quay.io")](https://quay.io/repository/briandoconnor/dockstore-tool-md5sum)
14+
[![Build Status](https://travis-ci.org/briandoconnor/dockstore-tool-md5sum.svg)](https://travis-ci.org/briandoconnor/dockstore-tool-md5sum)
15+
A very, very simple Docker container for the md5sum command. See the [README](https://github.com/briandoconnor/dockstore-tool-md5sum/blob/master/README.md) for more information.
16+
17+
18+
#dct:creator:
19+
# '@id': http://orcid.org/0000-0002-7681-6415
20+
# foaf:name: Brian O'Connor
21+
# foaf:mbox: [email protected]
22+
23+
requirements:
24+
- class: DockerRequirement
25+
dockerPull: quay.io/briandoconnor/dockstore-tool-md5sum:1.0.4
26+
- class: InlineJavascriptRequirement
27+
28+
hints:
29+
- class: ResourceRequirement
30+
# The command really requires very little resources.
31+
coresMin: 1
32+
ramMin: 1024
33+
outdirMin: 1024
34+
35+
inputs:
36+
input_file:
37+
type: File
38+
inputBinding:
39+
position: 1
40+
doc: The file that will have its md5sum calculated.
41+
42+
outputs:
43+
output_file:
44+
type: File
45+
format: http://edamontology.org/data_3671
46+
outputBinding:
47+
glob: md5sum.txt
48+
doc: A text file that contains a single line that is the md5sum of the input file.
49+
50+
baseCommand: [/bin/my_md5sum]

tests/trs/md5sum-tool.cwl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/env cwl-runner
2+
3+
class: CommandLineTool
4+
id: Md5sum
5+
label: Simple md5sum tool
6+
cwlVersion: v1.0
7+
8+
$namespaces:
9+
dct: http://purl.org/dc/terms/
10+
foaf: http://xmlns.com/foaf/0.1/
11+
12+
requirements:
13+
- class: DockerRequirement
14+
dockerPull: quay.io/agduncan94/my-md5sum
15+
- class: InlineJavascriptRequirement
16+
17+
hints:
18+
- class: ResourceRequirement
19+
# The command really requires very little resources.
20+
coresMin: 1
21+
ramMin: 1024
22+
outdirMin: 512
23+
24+
inputs:
25+
input_file:
26+
type: File
27+
inputBinding:
28+
position: 1
29+
doc: The file that will have its md5sum calculated.
30+
31+
outputs:
32+
output_file:
33+
type: File
34+
format: http://edamontology.org/data_3671
35+
outputBinding:
36+
glob: md5sum.txt
37+
doc: A text file that contains a single line that is the md5sum of the input file.
38+
39+
baseCommand: [/bin/my_md5sum]

tests/trs/md5sum-workflow.cwl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
cwlVersion: v1.0
2+
class: Workflow
3+
4+
inputs:
5+
input_file: File
6+
7+
outputs:
8+
output_file:
9+
type: File
10+
outputSource: md5sum/output_file
11+
12+
steps:
13+
md5sum:
14+
run: md5sum-tool.cwl
15+
in:
16+
input_file: input_file
17+
out: [output_file]

0 commit comments

Comments
 (0)