Skip to content

Commit 926fc89

Browse files
Enable ephemeral containers
1 parent ee57f1f commit 926fc89

File tree

19 files changed

+981
-629
lines changed

19 files changed

+981
-629
lines changed

.github/workflows/ci-tests.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ jobs:
147147
python -m pip install -r docs/requirements.txt
148148
- name: "Build documentation and check for consistency"
149149
env:
150-
CHECKSUM: "6642d8d7533c7cf3ec9de7bdbf1871e3a313dd73fa6551fe3bb10e4e94e7ff08"
150+
CHECKSUM: "2c3a8784c46b5591461db3df25cf56a431086cb265173e5f648fdde3c9fc23e7"
151151
run: |
152152
cd docs
153153
HASH="$(make checksum | tail -n1)"

docs/source/connector/container.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
ContainerConnector
33
==================
44

5-
The ``ContainerConnector`` is an abstract connector that serves as a base class to implement software container connectors (e.g., :ref:`Docker <DockerConnector>`, :ref:`Docker Compose <DockerComposeConnector>`, and :ref:`Singularity <SingularityConnector>`). It extends the abstract :ref:`ConnectorWrapper <ConnectorWrapper>` interface, allowing users to spawn software containers on top of local or remote execution environments using the :ref:`stacked locations <Stacked locations>` mechanism. Plus, it prevents :ref:`BatchConnector <BatchConnector>` instances to be wrapped as inner connectors.
5+
The ``ContainerConnector`` is an abstract connector that serves as a base class to implement software container connectors (e.g., :ref:`Docker <DockerConnector>`, :ref:`Docker Compose <DockerComposeConnector>`, and :ref:`Singularity <SingularityConnector>`). It extends the abstract :ref:`ConnectorWrapper <ConnectorWrapper>` interface, allowing users to spawn software containers on top of local or remote execution environments using the :ref:`stacked locations <Stacked locations>` mechanism. Plus, it prevents :ref:`BatchConnector <BatchConnector>` instances to be wrapped as inner connectors, unless the ``ContainerConnector`` is marked as ``ephemeral``.

docs/source/connector/docker.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
DockerConnector
33
===============
44

5-
The `Docker <https://www.docker.com/>`_ connector can spawn one or more instances of a Docker container locally on the StreamFlow node. The units of deployment and binding for this connector correspond to the set of homogeneous container instances, while the unit of scheduling is the single instance. It extends the :ref:`ContainerConnector <ContainerConnector>`, which inherits from the :ref:`ConnectorWrapper <ConnectorWrapper>` interface, allowing users to spawn Docker containers on top of local or remote execution environments using the :ref:`stacked locations <Stacked locations>` mechanism.
5+
The `Docker <https://www.docker.com/>`_ connector can spawn one or more instances of a Docker container locally on the StreamFlow node. It extends the :ref:`ContainerConnector <ContainerConnector>`, which inherits from the :ref:`ConnectorWrapper <ConnectorWrapper>` interface, allowing users to spawn Docker containers on top of local or remote execution environments using the :ref:`stacked locations <Stacked locations>` mechanism. Normally, a single Docker container is reused for multiple workflow commands, reducing cold start overhead. However, when the ``ephemeral`` option is set to ``True``, a fresh container instance is spawned for each command to prevent internal state contamination. In addition, a ``ContainerConnector`` marked as ``ephemeral`` can successfully wrap :ref:`BatchConnector <BatchConnector>` instances.
66

77
.. jsonschema:: ../../../streamflow/deployment/connector/schemas/docker.json
88
:lift_description: true

docs/source/connector/singularity.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
SingularityConnector
33
=====================
44

5-
The `Singularity <https://sylabs.io/singularity>`_ connector can spawn one or more instances of a Singularity container locally on the StreamFlow node. The units of deployment and binding for this connector correspond to the set of homogeneous container instances, while the unit of scheduling is the single instance. It extends the :ref:`ContainerConnector <ContainerConnector>`, which inherits from the :ref:`ConnectorWrapper <ConnectorWrapper>` interface, allowing users to spawn Singularity containers on top of local or remote execution environments using the :ref:`stacked locations <Stacked locations>` mechanism.
5+
The `Singularity <https://sylabs.io/singularity>`_ connector can spawn one or more instances of a Singularity container locally on the StreamFlow node. It extends the :ref:`ContainerConnector <ContainerConnector>`, which inherits from the :ref:`ConnectorWrapper <ConnectorWrapper>` interface, allowing users to spawn Singularity containers on top of local or remote execution environments using the :ref:`stacked locations <Stacked locations>` mechanism. Normally, a single Singularity instance is reused for multiple workflow commands, reducing cold start overhead. However, when the ``ephemeral`` option is set to ``True``, a fresh container instance is spawned for each command to prevent internal state contamination. In addition, a ``ContainerConnector`` marked as ``ephemeral`` can successfully wrap :ref:`BatchConnector <BatchConnector>` instances.
66

77
.. jsonschema:: ../../../streamflow/deployment/connector/schemas/singularity.json
88
:lift_description: true

docs/source/cwl/docker-requirement.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ By default, StreamFlow automatically maps a step with the ``DockerRequirement``
4343
type: docker
4444
config:
4545
image: node:slim
46+
ephemeral: true
4647
4748
StreamFlow also supports the possibility to map a CWL ``DockerRequirement`` onto different types of connectors through the :ref:`CWLDockerTranslator <CWLDockerTranslator>` extension point. In particular, the ``docker`` section of a workflow configuration can bind each step or subworkflow to a specific translator type, making it possible to convert a pure CWL workflow with ``DockerRequirement`` features into a hybrid workflow. The available translator types are: ``docker``, ``kubernetes``, ``none`` and ``singularity``.
4849

docs/source/cwl/docker/docker.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
DockerCWLDockerTranslator
33
=========================
44

5-
The Docker :ref:`CWLDockerTranslator <CWLDockerTranslator>` instantiates a :ref:`DockerConnector <DockerConnector>` instance with the given configuration for every CWL :ref:`DockerRequirement <CWL Docker Requirement>` specification in the selected subworkflow.
5+
The Docker :ref:`CWLDockerTranslator <CWLDockerTranslator>` instantiates a :ref:`DockerConnector <DockerConnector>` instance with the given configuration for every CWL :ref:`DockerRequirement <CWL Docker Requirement>` specification in the selected subworkflow. Note that the resulting ``DockerConnector`` instance spawns ``ephemeral`` containers, making it able to wrap also :ref:`BatchConnector <BatchConnector>` instances for HPC deployments.
66

77
.. jsonschema:: ../../../../streamflow/cwl/requirement/docker/schemas/docker.json
88
:lift_description: true

docs/source/cwl/docker/kubernetes.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
KubernetesCWLDockerTranslator
33
=============================
44

5-
The Kubernetes :ref:`CWLDockerTranslator <CWLDockerTranslator>` instantiates a :ref:`KubernetesConnector <KubernetesConnector>` instance with the given configuration for every CWL :ref:`DockerRequirement <CWL Docker Requirement>` specification in the selected subworkflow.
5+
The Kubernetes :ref:`CWLDockerTranslator <CWLDockerTranslator>` instantiates a :ref:`KubernetesConnector <KubernetesConnector>` instance with the given configuration for every CWL :ref:`DockerRequirement <CWL Docker Requirement>` specification in the selected subworkflow. Note that, unlike other ``CWLDockerTranslator`` classes, the ``KubernetesConnector`` does not support ``ephemeral`` containers.
66

77
.. jsonschema:: ../../../../streamflow/cwl/requirement/docker/schemas/kubernetes.json
88
:lift_description: true

docs/source/cwl/docker/singularity.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
SingularityCWLDockerTranslator
33
==============================
44

5-
The Singularity :ref:`CWLDockerTranslator <CWLDockerTranslator>` instantiates a :ref:`SingularityConnector <SingularityConnector>` instance with the given configuration for every CWL :ref:`DockerRequirement <CWL Docker Requirement>` specification in the selected subworkflow.
5+
The Singularity :ref:`CWLDockerTranslator <CWLDockerTranslator>` instantiates a :ref:`SingularityConnector <SingularityConnector>` instance with the given configuration for every CWL :ref:`DockerRequirement <CWL Docker Requirement>` specification in the selected subworkflow. Note that the resulting ``SingularityConnector`` instance spawns ``ephemeral`` containers, making it able to wrap also :ref:`BatchConnector <BatchConnector>` instances for HPC deployments.
66

77
.. jsonschema:: ../../../../streamflow/cwl/requirement/docker/schemas/docker.json
88
:lift_description: true

docs/source/ext/cwl-docker-translator.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ Implementations
2828
=================================================== ================================================================
2929
Type Class
3030
=================================================== ================================================================
31-
:ref:`docker <DockerCWLDockerTranslator>` streamflow.cwl.requirement.docker.DockerCWLDockerTranslator
32-
:ref:`kubernetes <KubernetesCWLDockerTranslator>` streamflow.cwl.requirement.docker.KubernetesCWLDockerTranslator
33-
:ref:`singularity <SingularityCWLDockerTranslator>` streamflow.cwl.requirement.docker.SingularityCWLDockerTranslator
31+
:ref:`docker <DockerCWLDockerTranslator>` streamflow.cwl.requirement.docker.docker.DockerCWLDockerTranslator
32+
:ref:`kubernetes <KubernetesCWLDockerTranslator>` streamflow.cwl.requirement.docker.kubernetes.KubernetesCWLDockerTranslator
33+
:ref:`none <NoContainerCWLDockerTranslator>` streamflow.cwl.requirement.docker.nocontainer.NoContainerCWLDockerTranslator
34+
:ref:`singularity <SingularityCWLDockerTranslator>` streamflow.cwl.requirement.docker.singularity.SingularityCWLDockerTranslator
3435
=================================================== ================================================================

streamflow/cwl/requirement/docker/docker.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ def __init__(
4848
entrypoint: str | None = None,
4949
env: MutableSequence[str] | None = None,
5050
envFile: MutableSequence[str] | None = None,
51+
ephemeral: bool = True,
5152
expose: MutableSequence[str] | None = None,
5253
gpus: MutableSequence[str] | None = None,
5354
groupAdd: MutableSequence[str] | None = None,
@@ -140,6 +141,7 @@ def __init__(
140141
self.entrypoint: str | None = entrypoint
141142
self.env: MutableSequence[str] | None = env
142143
self.envFile: MutableSequence[str] | None = envFile
144+
self.ephemeral: bool = ephemeral
143145
self.expose: MutableSequence[str] | None = expose
144146
self.gpus: MutableSequence[str] | None = gpus
145147
self.groupAdd: MutableSequence[str] | None = groupAdd
@@ -218,14 +220,10 @@ def get_target(
218220
volume = list(self.volume) if self.volume else []
219221
volume.append(f"{target.workdir}:/tmp/streamflow")
220222
if output_directory is not None:
221-
if target.deployment.type == "local":
222-
volume.append(
223-
f"{os.path.join(target.workdir, utils.random_name())}:{output_directory}"
224-
)
225-
else:
226-
volume.append(
227-
f"{posixpath.join(target.workdir, utils.random_name())}:{output_directory}"
228-
)
223+
path_processor = os.path if target.deployment.type == "local" else posixpath
224+
volume.append(
225+
f"{path_processor.join(target.workdir, utils.random_name())}:{output_directory}"
226+
)
229227
return Target(
230228
deployment=DeploymentConfig(
231229
name=utils.random_name(),
@@ -264,6 +262,7 @@ def get_target(
264262
"entrypoint": self.entrypoint,
265263
"env": self.env,
266264
"envFile": self.envFile,
265+
"ephemeral": self.ephemeral,
267266
"expose": self.expose,
268267
"gpus": self.gpus,
269268
"groupAdd": self.groupAdd,
@@ -321,6 +320,7 @@ def get_target(
321320
"volume": volume,
322321
"volumeDriver": self.volumeDriver,
323322
"volumesFrom": self.volumesFrom,
323+
"workdir": self.workdir,
324324
},
325325
workdir="/tmp/streamflow", # nosec
326326
wraps=(

streamflow/cwl/requirement/docker/schemas/docker.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"items": {
6161
"type": "string"
6262
},
63-
"description": "Command to run when deploying the container"
63+
"description": "Command to run when deploying the container. Not supported for ephemeral containers"
6464
},
6565
"cpuPeriod": {
6666
"type": "integer",
@@ -199,6 +199,11 @@
199199
"uniqueItems": true,
200200
"description": "Read in a file of environment variables"
201201
},
202+
"ephemeral": {
203+
"type": "boolean",
204+
"default": true,
205+
"description": "Invoke a fresh container instance for every command in the workflow"
206+
},
202207
"expose": {
203208
"type": "array",
204209
"items": {

streamflow/cwl/requirement/docker/schemas/singularity.json

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
"type": "string",
1616
"description": "Apply cgroups from file for container processes (root only)"
1717
},
18+
"arch": {
19+
"type": "string",
20+
"description": "architecture to pull from library"
21+
},
1822
"bind": {
1923
"type": "array",
2024
"items": {
@@ -36,7 +40,7 @@
3640
},
3741
"boot": {
3842
"type": "boolean",
39-
"description": "Execute /sbin/init to boot container (root only)"
43+
"description": "Execute /sbin/init to boot container (root only). Not supported for ephemeral containers"
4044
},
4145
"cleanenv": {
4246
"type": "boolean",
@@ -48,7 +52,7 @@
4852
"items": {
4953
"type": "string"
5054
},
51-
"description": "Command to run when deploying the container"
55+
"description": "Command to run when deploying the container. Not supported for ephemeral containers"
5256
},
5357
"compat": {
5458
"type": "boolean",
@@ -65,7 +69,8 @@
6569
},
6670
"cpuShares": {
6771
"type": "integer",
68-
"description": "CPU shares for container (default -1)"
72+
"description": "CPU shares for container",
73+
"default": -1
6974
},
7075
"cpus": {
7176
"type": "string",
@@ -85,7 +90,7 @@
8590
},
8691
"dns": {
8792
"type": "string",
88-
"description": "List of DNS server separated by commas to add in resolv.conf"
93+
"description": "List of DNS server separated by commas to add in resolv.conf. Not supported for ephemeral containers"
8994
},
9095
"dockerHost": {
9196
"type": "string",
@@ -107,6 +112,11 @@
107112
"type": "string",
108113
"description": "Pass environment variables from file to contained process"
109114
},
115+
"ephemeral": {
116+
"type": "boolean",
117+
"default": true,
118+
"description": "Invoke a fresh container instance for every command in the workflow"
119+
},
110120
"fakeroot": {
111121
"type": "boolean",
112122
"description": "Run container in new user namespace as uid 0"
@@ -230,12 +240,17 @@
230240
},
231241
"pidFile": {
232242
"type": "string",
233-
"description": "Write instance PID to the file with the given name"
243+
"description": "Write instance PID to the file with the given name. Not supported for ephemeral containers"
234244
},
235245
"pidsLimit": {
236246
"type": "integer",
237247
"description": "Limit number of container PIDs, use -1 for unlimited"
238248
},
249+
"pullDir": {
250+
"type": "string",
251+
"description": "Download images to the specific directory",
252+
"default": "The target workdir"
253+
},
239254
"rocm": {
240255
"type": "boolean",
241256
"description": "Enable experimental ROCM support"

streamflow/cwl/requirement/docker/singularity.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def __init__(
2020
addCaps: str | None = None,
2121
allowSetuid: bool = False,
2222
applyCgroups: str | None = None,
23+
arch: str | None = None,
2324
bind: MutableSequence[str] | None = None,
2425
blkioWeight: int | None = None,
2526
blkioWeightDevice: MutableSequence[str] | None = None,
@@ -39,6 +40,7 @@ def __init__(
3940
dropCaps: str | None = None,
4041
env: MutableSequence[str] | None = None,
4142
envFile: str | None = None,
43+
ephemeral: bool = True,
4244
fakeroot: bool = False,
4345
fusemount: MutableSequence[str] | None = None,
4446
home: str | None = None,
@@ -66,6 +68,7 @@ def __init__(
6668
pemPath: str | None = None,
6769
pidFile: str | None = None,
6870
pidsLimit: int | None = None,
71+
pullDir: str | None = None,
6972
rocm: bool = False,
7073
scratch: MutableSequence[str] | None = None,
7174
security: MutableSequence[str] | None = None,
@@ -79,6 +82,7 @@ def __init__(
7982
self.addCaps: str | None = addCaps
8083
self.allowSetuid: bool = allowSetuid
8184
self.applyCgroups: str | None = applyCgroups
85+
self.arch: str | None = arch
8286
self.bind: MutableSequence[str] | None = bind
8387
self.blkioWeight: int | None = blkioWeight
8488
self.blkioWeightDevice: MutableSequence[str] | None = blkioWeightDevice
@@ -98,6 +102,7 @@ def __init__(
98102
self.dockerHost: str | None = dockerHost
99103
self.env: MutableSequence[str] | None = env
100104
self.envFile: str | None = envFile
105+
self.ephemeral: bool = ephemeral
101106
self.fakeroot: bool = fakeroot
102107
self.fusemount: MutableSequence[str] | None = fusemount
103108
self.home: str | None = home
@@ -125,6 +130,7 @@ def __init__(
125130
self.pemPath: str | None = pemPath
126131
self.pidFile: str | None = pidFile
127132
self.pidsLimit: int | None = pidsLimit
133+
self.pullDir: str | None = pullDir
128134
self.rocm: bool = rocm
129135
self.scratch: MutableSequence[str] | None = scratch
130136
self.security: MutableSequence[str] | None = security
@@ -154,14 +160,10 @@ def get_target(
154160
bind = list(self.bind) if self.bind else []
155161
bind.append(f"{target.workdir}:/tmp/streamflow")
156162
if output_directory is not None:
157-
if target.deployment.type == "local":
158-
bind.append(
159-
f"{os.path.join(target.workdir, utils.random_name())}:{output_directory}"
160-
)
161-
else:
162-
bind.append(
163-
f"{posixpath.join(target.workdir, utils.random_name())}:{output_directory}"
164-
)
163+
path_processor = os.path if target.deployment.type == "local" else posixpath
164+
bind.append(
165+
f"{path_processor.join(target.workdir, utils.random_name())}:{output_directory}"
166+
)
165167
return Target(
166168
deployment=DeploymentConfig(
167169
name=utils.random_name(),
@@ -171,6 +173,7 @@ def get_target(
171173
"addCaps": self.addCaps,
172174
"allowSetuid": self.allowSetuid,
173175
"applyCgroups": self.applyCgroups,
176+
"arch": self.arch,
174177
"bind": bind,
175178
"blkioWeight": self.blkioWeight,
176179
"blkioWeightDevice": self.blkioWeightDevice,
@@ -190,6 +193,7 @@ def get_target(
190193
"dockerHost": self.dockerHost,
191194
"env": self.env,
192195
"envFile": self.envFile,
196+
"ephemeral": self.ephemeral,
193197
"fakeroot": self.fakeroot,
194198
"fusemount": self.fusemount,
195199
"home": self.home,
@@ -217,12 +221,16 @@ def get_target(
217221
"pemPath": self.pemPath,
218222
"pidFile": self.pidFile,
219223
"pidsLimit": self.pidsLimit,
224+
"pullDir": (
225+
self.pullDir if self.pullDir is not None else target.workdir
226+
),
220227
"rocm": self.rocm,
221228
"scratch": self.scratch,
222229
"security": self.security,
223230
"transferBufferSize": self.transferBufferSize,
224231
"userns": self.userns,
225232
"uts": self.uts,
233+
"workdir": self.workdir,
226234
"writable": self.writable,
227235
"writableTmpfs": self.writableTmpfs,
228236
},

0 commit comments

Comments
 (0)