From c8ae015327c9a098c03205f21562907833e24fbb Mon Sep 17 00:00:00 2001 From: Theofilos Manitaras Date: Fri, 30 Sep 2022 12:13:35 +0200 Subject: [PATCH 1/2] Add support for the Apptainer container platform --- reframe/core/containers.py | 14 ++++++++++++-- unittests/test_containers.py | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/reframe/core/containers.py b/reframe/core/containers.py index 15be3766f8..2493b5093e 100644 --- a/reframe/core/containers.py +++ b/reframe/core/containers.py @@ -243,6 +243,7 @@ class Singularity(ContainerPlatform): def __init__(self): super().__init__() self.with_cuda = False + self._launch_command = 'singularity' def emit_prepare_commands(self, stagedir): return [] @@ -259,10 +260,19 @@ def launch_command(self, stagedir): run_opts += self.options if self.command: - return (f'singularity exec {" ".join(run_opts)} ' + return (f'{self._launch_command} exec {" ".join(run_opts)} ' f'{self.image} {self.command}') - return f'singularity run {" ".join(run_opts)} {self.image}' + return f'{self._launch_command} run {" ".join(run_opts)} {self.image}' + + +class Apptainer(Singularity): + '''Container platform backend for running containers with `Apptainer + `__.''' + + def __init__(self): + super().__init__() + self._launch_command = 'apptainer' class ContainerPlatformField(fields.TypedField): diff --git a/unittests/test_containers.py b/unittests/test_containers.py index ea7134652e..6ee7cbbe15 100644 --- a/unittests/test_containers.py +++ b/unittests/test_containers.py @@ -13,7 +13,8 @@ 'Sarus', 'Sarus+nocommand', 'Sarus+nopull', 'Sarus+mpi', 'Sarus+load', 'Shifter', 'Shifter+nocommand', 'Shifter+mpi', 'Shifter+nopull', 'Shifter+load', - 'Singularity', 'Singularity+nocommand', 'Singularity+cuda' + 'Singularity', 'Singularity+nocommand', 'Singularity+cuda', + 'Apptainer', 'Apptainer+nocommand', 'Apptainer+cuda' ]) def container_variant(request): return request.param @@ -101,7 +102,7 @@ def expected_cmd_mount_points(container_variant): '--mount=type=bind,source="/path/two",destination="/two" ' '--mount=type=bind,source="/foo",destination="/rfm_workdir" ' 'load/library/image:tag cmd') - elif container_variant in {'Singularity', 'Singularity+nopull'}: + elif container_variant in {'Singularity'}: return ('singularity exec -B"/path/one:/one" ' '-B"/path/two:/two" -B"/foo:/rfm_workdir" ' '--pwd /rfm_workdir image:tag cmd') @@ -113,6 +114,19 @@ def expected_cmd_mount_points(container_variant): return ('singularity run -B"/path/one:/one" ' '-B"/path/two:/two" -B"/foo:/rfm_workdir" ' '--pwd /rfm_workdir image:tag') + elif container_variant in {'Apptainer'}: + return ('apptainer exec -B"/path/one:/one" ' + '-B"/path/two:/two" -B"/foo:/rfm_workdir" ' + '--pwd /rfm_workdir image:tag cmd') + elif container_variant == 'Apptainer+cuda': + return ('apptainer exec -B"/path/one:/one" ' + '-B"/path/two:/two" -B"/foo:/rfm_workdir" --nv ' + '--pwd /rfm_workdir image:tag cmd') + elif container_variant == 'Apptainer+nocommand': + return ('apptainer run -B"/path/one:/one" ' + '-B"/path/two:/two" -B"/foo:/rfm_workdir" ' + '--pwd /rfm_workdir image:tag') + @pytest.fixture @@ -189,6 +203,15 @@ def expected_cmd_run_opts(container_variant): elif container_variant == 'Singularity+nocommand': return ('singularity run -B"/path/one:/one" -B"/foo:/rfm_workdir" ' '--foo --bar image:tag') + elif container_variant in {'Apptainer'}: + return ('apptainer exec -B"/path/one:/one" -B"/foo:/rfm_workdir" ' + '--foo --bar image:tag cmd') + elif container_variant == 'Apptainer+cuda': + return ('apptainer exec -B"/path/one:/one" -B"/foo:/rfm_workdir" ' + '--nv --foo --bar image:tag cmd') + elif container_variant == 'Apptainer+nocommand': + return ('apptainer run -B"/path/one:/one" -B"/foo:/rfm_workdir" ' + '--foo --bar image:tag') def test_mount_points(container_platform, expected_cmd_mount_points): @@ -245,6 +268,9 @@ def expected_run_with_commands(container_variant_noopt): elif container_variant_noopt == 'Singularity': return ("singularity exec -B\"/foo:/rfm_workdir\" " "--foo image:tag bash -c 'cmd1; cmd2'") + elif container_variant_noopt == 'Apptainer': + return ("apptainer exec -B\"/foo:/rfm_workdir\" " + "--foo image:tag bash -c 'cmd1; cmd2'") @pytest.fixture @@ -267,6 +293,9 @@ def expected_run_with_workdir(container_variant_noopt): elif container_variant_noopt == 'Singularity': return ('singularity exec -B\"/foo:/rfm_workdir\" --pwd foodir ' '--foo image:tag cmd1') + elif container_variant_noopt == 'Apptainer': + return ('apptainer exec -B\"/foo:/rfm_workdir\" --pwd foodir ' + '--foo image:tag cmd1') def test_run_with_workdir(container_platform_with_opts, From ab52dab52adaa91a943b38e999857ec5f9bdcd1a Mon Sep 17 00:00:00 2001 From: Theofilos Manitaras Date: Mon, 3 Oct 2022 11:42:35 +0200 Subject: [PATCH 2/2] Address PR comments --- docs/config_reference.rst | 1 + reframe/core/containers.py | 6 +++++- unittests/test_containers.py | 8 ++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/config_reference.rst b/docs/config_reference.rst index 356a97cec9..d03e882f53 100644 --- a/docs/config_reference.rst +++ b/docs/config_reference.rst @@ -467,6 +467,7 @@ ReFrame can launch containerized applications, but you need to configure properl The type of the container platform. Available values are the following: + - ``Apptainer``: The `Apptainer `__ container runtime. - ``Docker``: The `Docker `__ container runtime. - ``Sarus``: The `Sarus `__ container runtime. - ``Shifter``: The `Shifter `__ container runtime. diff --git a/reframe/core/containers.py b/reframe/core/containers.py index 2493b5093e..6182b3d817 100644 --- a/reframe/core/containers.py +++ b/reframe/core/containers.py @@ -268,7 +268,11 @@ def launch_command(self, stagedir): class Apptainer(Singularity): '''Container platform backend for running containers with `Apptainer - `__.''' + `__. + + .. versionadded:: 4.0.0 + + ''' def __init__(self): super().__init__() diff --git a/unittests/test_containers.py b/unittests/test_containers.py index 6ee7cbbe15..b4d30724ff 100644 --- a/unittests/test_containers.py +++ b/unittests/test_containers.py @@ -102,7 +102,7 @@ def expected_cmd_mount_points(container_variant): '--mount=type=bind,source="/path/two",destination="/two" ' '--mount=type=bind,source="/foo",destination="/rfm_workdir" ' 'load/library/image:tag cmd') - elif container_variant in {'Singularity'}: + elif container_variant == 'Singularity': return ('singularity exec -B"/path/one:/one" ' '-B"/path/two:/two" -B"/foo:/rfm_workdir" ' '--pwd /rfm_workdir image:tag cmd') @@ -114,7 +114,7 @@ def expected_cmd_mount_points(container_variant): return ('singularity run -B"/path/one:/one" ' '-B"/path/two:/two" -B"/foo:/rfm_workdir" ' '--pwd /rfm_workdir image:tag') - elif container_variant in {'Apptainer'}: + elif container_variant == 'Apptainer': return ('apptainer exec -B"/path/one:/one" ' '-B"/path/two:/two" -B"/foo:/rfm_workdir" ' '--pwd /rfm_workdir image:tag cmd') @@ -194,7 +194,7 @@ def expected_cmd_run_opts(container_variant): '--mount=type=bind,source="/path/one",destination="/one" ' '--mount=type=bind,source="/foo",destination="/rfm_workdir" ' '--mpi --foo --bar image:tag cmd') - elif container_variant in {'Singularity'}: + elif container_variant == 'Singularity': return ('singularity exec -B"/path/one:/one" -B"/foo:/rfm_workdir" ' '--foo --bar image:tag cmd') elif container_variant == 'Singularity+cuda': @@ -203,7 +203,7 @@ def expected_cmd_run_opts(container_variant): elif container_variant == 'Singularity+nocommand': return ('singularity run -B"/path/one:/one" -B"/foo:/rfm_workdir" ' '--foo --bar image:tag') - elif container_variant in {'Apptainer'}: + elif container_variant == 'Apptainer': return ('apptainer exec -B"/path/one:/one" -B"/foo:/rfm_workdir" ' '--foo --bar image:tag cmd') elif container_variant == 'Apptainer+cuda':