Skip to content

Commit 59702e9

Browse files
authored
Merge pull request #66 from simleo/container_img
Add ContainerImage
2 parents d052f77 + 8a56a22 commit 59702e9

File tree

6 files changed

+108
-3
lines changed

6 files changed

+108
-3
lines changed

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
bdbag>=1.4.1
22
click~=8.1
3-
cwl-utils>=0.27
3+
cwl-utils==0.29
44
cwlprov==0.1.1
55
networkx==3.1
66
prov>=1.5.1

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ python_requires=>=3.8, <4
3232
install_requires=
3333
bdbag>=1.4.1
3434
click~=8.1
35-
cwl-utils>=0.27
35+
cwl-utils==0.29
3636
cwlprov==0.1.1
3737
networkx==3.1
3838
prov>=1.5.1

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):

tests/test_utils.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright 2023 CRS4.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from runcrate.utils import parse_img
16+
17+
18+
def test_parse_img():
19+
assert parse_img("python") == {
20+
"registry": "docker.io",
21+
"name": "python"
22+
}
23+
assert parse_img("python:3.12") == {
24+
"registry": "docker.io",
25+
"name": "python",
26+
"tag": "3.12"
27+
}
28+
assert parse_img("josiah/python:3.11") == {
29+
"registry": "docker.io",
30+
"name": "josiah/python",
31+
"tag": "3.11"
32+
}
33+
assert parse_img("quay.io/josiah/python:3.11") == {
34+
"registry": "quay.io",
35+
"name": "josiah/python",
36+
"tag": "3.11"
37+
}
38+
assert parse_img("python@sha256:7b8d65a924f596eb65306214f559253c468336bcae09fd575429774563460caf") == {
39+
"registry": "docker.io",
40+
"name": "python",
41+
"sha256": "7b8d65a924f596eb65306214f559253c468336bcae09fd575429774563460caf"
42+
}

0 commit comments

Comments
 (0)