Skip to content

Commit a825a8f

Browse files
committed
convert cwlprov:image
1 parent d052f77 commit a825a8f

3 files changed

Lines changed: 64 additions & 1 deletion

File tree

src/runcrate/convert.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
from rocrate.rocrate import ROCrate
3636

3737
from .constants import PROFILES_BASE, PROFILES_VERSION, TERMS_NAMESPACE
38-
from .utils import as_list
38+
from .utils import as_list, parse_img
3939

4040

4141
WORKFLOW_BASENAME = "packed.cwl"
@@ -61,6 +61,8 @@
6161

6262
WROC_PROFILE_VERSION = "1.0"
6363

64+
DOCKER_IMG_TYPE = "https://w3id.org/ro/terms/workflow-run#DockerImage"
65+
6466

6567
def convert_cwl_type(cwl_type):
6668
if isinstance(cwl_type, list):
@@ -503,9 +505,24 @@ def to_wf_p(k):
503505
action["endTime"] = activity.end().time.isoformat()
504506
action["object"] = self.add_action_params(crate, activity, to_wf_p, "usage")
505507
action["result"] = self.add_action_params(crate, activity, to_wf_p, "generation")
508+
self.add_container_images(crate, action, activity)
506509
for job in activity.steps():
507510
self.add_action(crate, job, parent_instrument=instrument)
508511

512+
def add_container_images(self, crate, action, activity):
513+
images = set()
514+
for assoc in activity.association():
515+
for agent in activity.provenance.prov_doc.get_record(assoc.agent_id):
516+
images |= agent.get_attribute("cwlprov:image")
517+
for im in images:
518+
properties = parse_img(im)
519+
properties.update({
520+
"@type": "ContainerImage",
521+
"additionalType": {"@id": DOCKER_IMG_TYPE}
522+
})
523+
roc_img = crate.add(ContextEntity(crate, properties=properties))
524+
action.append_to("containerImage", roc_img, compact=True)
525+
509526
def add_action_params(self, crate, activity, to_wf_p, ptype="usage"):
510527
action_params = []
511528
all_roles = set()

src/runcrate/utils.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,39 @@ def as_list(value):
1717
if isinstance(value, list):
1818
return value
1919
return [value]
20+
21+
22+
def parse_img_name(img_name):
23+
parts = img_name.split("/")
24+
if len(parts) == 3:
25+
registry = parts[0]
26+
name = "/".join(parts[1:])
27+
else:
28+
registry = "docker.io"
29+
name = "/".join(parts)
30+
return registry, name
31+
32+
33+
def parse_img(img_str):
34+
"""\
35+
Parse image string following the docker pull syntax NAME[:TAG|@DIGEST].
36+
CWL's DockerRequirement also accepts HTTP URLs for docker load.
37+
"""
38+
parsed = {}
39+
if img_str.startswith("http://") or img_str.startswith("https://"):
40+
return img_str
41+
parts = img_str.rsplit("@", 1)
42+
if len(parts) == 2:
43+
parsed["registry"], parsed["name"] = parse_img_name(parts[0])
44+
algo, digest = parts[1].split(":", 1)
45+
assert algo == "sha256"
46+
parsed[algo] = digest
47+
return parsed
48+
parts = img_str.rsplit(":", 1)
49+
if len(parts) == 2:
50+
parsed["registry"], parsed["name"] = parse_img_name(parts[0])
51+
parsed["tag"] = parts[1]
52+
return parsed
53+
assert len(parts) == 1
54+
parsed["registry"], parsed["name"] = parse_img_name(parts[0])
55+
return parsed

tests/test_cwlprov_crate_builder.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,16 @@ def test_revsort(data_dir, tmpdir):
182182
metadata = json.load(f)
183183
context = metadata['@context']
184184
assert TERMS_NAMESPACE in context
185+
# Docker image
186+
for action in crate.get_by_type("CreateAction"):
187+
if action is wf_action:
188+
continue
189+
assert "containerImage" in action
190+
img = action["containerImage"]
191+
assert img.type == "ContainerImage"
192+
assert img["additionalType"] == "https://w3id.org/ro/terms/workflow-run#DockerImage"
193+
assert img["name"] == "debian"
194+
assert img["tag"] == "8"
185195

186196

187197
def test_no_input(data_dir, tmpdir):

0 commit comments

Comments
 (0)