Skip to content

Commit c7ca466

Browse files
manabuishiimr-c
andcommitted
Check singularity image already cached
If there is already exists singularity image under cache directory , `singularity pull` causes error. To avoid error, check whether singularity image is exists or not. Co-authored-by: Michael R. Crusoe <[email protected]>
1 parent eb89df4 commit c7ca466

File tree

3 files changed

+66
-9
lines changed

3 files changed

+66
-9
lines changed

cwl_utils/docker_extract.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ def arg_parser() -> argparse.ArgumentParser:
3737
help="Specify which command to use to run OCI containers. "
3838
"Defaults to 'docker' (or 'singularity' if --singularity/-s is passed).",
3939
)
40+
parser.add_argument(
41+
"--force-download", help="Force pulling a newer container.", action="store_true"
42+
)
4043
return parser
4144

4245

@@ -64,6 +67,7 @@ def run(args: argparse.Namespace) -> List[cwl.DockerRequirement]:
6467
args.container_engine
6568
if args.container_engine is not None
6669
else "singularity",
70+
args.force_download,
6771
)
6872
else:
6973
image_puller = DockerImagePuller(
@@ -72,6 +76,7 @@ def run(args: argparse.Namespace) -> List[cwl.DockerRequirement]:
7276
args.container_engine
7377
if args.container_engine is not None
7478
else "docker",
79+
args.force_download,
7580
)
7681
image_puller.save_docker_image()
7782
return reqs

cwl_utils/image_puller.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616

1717

1818
class ImagePuller(ABC):
19-
def __init__(self, req: str, save_directory: Union[str, Path], cmd: str) -> None:
19+
def __init__(
20+
self, req: str, save_directory: Union[str, Path], cmd: str, force_pull: bool
21+
) -> None:
2022
"""Create an ImagePuller."""
2123
self.req = req
2224
self.save_directory = save_directory
2325
self.cmd = cmd
26+
self.force_pull = force_pull
2427

2528
@abstractmethod
2629
def get_image_name(self) -> str:
@@ -62,17 +65,18 @@ def save_docker_image(self) -> None:
6265
_LOGGER.info(f"Pulling {self.req} with Docker...")
6366
cmd_pull = [self.cmd, "pull", self.req]
6467
ImagePuller._run_command_pull(cmd_pull)
68+
dest = os.path.join(self.save_directory, self.get_image_name())
69+
if self.force_pull:
70+
os.remove(dest)
6571
cmd_save = [
6672
self.cmd,
6773
"save",
6874
"-o",
69-
os.path.join(self.save_directory, self.get_image_name()),
75+
dest,
7076
self.req,
7177
]
7278
subprocess.run(cmd_save, check=True) # nosec
73-
_LOGGER.info(
74-
f"Image successfully pulled: {self.save_directory}/{self.get_image_name()}"
75-
)
79+
_LOGGER.info(f"Image successfully pulled: {dest!r}.")
7680
print(self.generate_udocker_loading_command())
7781

7882

@@ -98,15 +102,27 @@ def get_image_name(self) -> str:
98102
return f"{image_name}{suffix}"
99103

100104
def save_docker_image(self) -> None:
101-
"""Pull down the Docker container image in the Singularity image format."""
105+
"""Pull down the Docker software container image and save it in the Singularity image format."""
106+
if (
107+
os.path.exists(os.path.join(self.save_directory, self.get_image_name()))
108+
and not self.force_pull
109+
):
110+
_LOGGER.info(f"Already cached {self.req} with Singularity.")
111+
return
102112
_LOGGER.info(f"Pulling {self.req} with Singularity...")
103113
cmd_pull = [
104114
self.cmd,
105115
"pull",
106-
"--name",
107-
os.path.join(self.save_directory, self.get_image_name()),
108-
f"docker://{self.req}",
109116
]
117+
if self.force_pull:
118+
cmd_pull.append("--force")
119+
cmd_pull.extend(
120+
[
121+
"--name",
122+
os.path.join(self.save_directory, self.get_image_name()),
123+
f"docker://{self.req}",
124+
]
125+
)
110126
ImagePuller._run_command_pull(cmd_pull)
111127
_LOGGER.info(
112128
f"Image successfully pulled: {self.save_directory}/{self.get_image_name()}"

tests/test_docker_extract.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,42 @@ def test_container_extraction(target: str, engine: str, tmp_path: Path) -> None:
3131
assert len(list(tmp_path.iterdir())) == 1
3232

3333

34+
@pytest.mark.parametrize(
35+
("engine"),
36+
[
37+
pytest.param("docker", marks=needs_docker),
38+
pytest.param("podman", marks=needs_podman),
39+
pytest.param("singularity", marks=needs_singularity),
40+
],
41+
)
42+
def test_container_extraction_force(engine: str, tmp_path: Path) -> None:
43+
"""Test force pull container extraction."""
44+
45+
args = [
46+
str(tmp_path),
47+
get_data("testdata/md5sum.cwl"),
48+
"--container-engine",
49+
engine,
50+
]
51+
if engine == "singularity":
52+
args.append("--singularity")
53+
reqs = run(arg_parser().parse_args(args))
54+
assert len(reqs) == 1
55+
assert len(list(tmp_path.iterdir())) == 1
56+
args = [
57+
str(tmp_path),
58+
get_data("testdata/md5sum.cwl"),
59+
"--container-engine",
60+
engine,
61+
"--force-download",
62+
]
63+
if engine == "singularity":
64+
args.append("--singularity")
65+
reqs = run(arg_parser().parse_args(args))
66+
assert len(reqs) == 1
67+
assert len(list(tmp_path.iterdir())) == 1
68+
69+
3470
@pytest.mark.parametrize(
3571
("engine"),
3672
[

0 commit comments

Comments
 (0)