Skip to content

Commit 98e8573

Browse files
authored
Add singularity.resourceLimits config option (#7352)
Signed-off-by: Ben Sherman <bentshermann@gmail.com>
1 parent b61eda2 commit 98e8573

11 files changed

Lines changed: 120 additions & 19 deletions

File tree

docs/reference/config.mdx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,12 @@ The amount of time the Apptainer pull can last, exceeding which the process is t
139139

140140
The registry from where Docker images are pulled. It should be only used to specify a private registry server. It should NOT include the protocol prefix i.e. `http://`.
141141

142+
##### `apptainer.resourceLimits`
143+
144+
<AddedInVersion version="26.10" />
145+
146+
Apply the task `cpus` and `memory` directives as container resource limits using the `--cpus` and `--memory` flags (default: `false`).
147+
142148
##### `apptainer.runOptions`
143149

144150
Specify extra command line options supported by `apptainer exec`.
@@ -1974,6 +1980,12 @@ The amount of time the Singularity pull can last, after which the process is ter
19741980

19751981
The registry from where Docker images are pulled. It should be only used to specify a private registry server. It should NOT include the protocol prefix i.e. `http://`.
19761982

1983+
##### `singularity.resourceLimits`
1984+
1985+
<AddedInVersion version="26.10" />
1986+
1987+
Apply the task `cpus` and `memory` directives as container resource limits using the `--cpus` and `--memory` flags (default: `false`).
1988+
19771989
##### `singularity.runOptions`
19781990

19791991
Specify extra command line options supported by `singularity exec`.

modules/nextflow/build.gradle

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,6 @@ dependencies {
9393
test {
9494
minHeapSize = "512m"
9595
maxHeapSize = "4096m"
96-
// Dev-mode plugin discovery reads plugins/*/build/resources/main/META-INF/MANIFEST.MF at
97-
// runtime. Each plugin's copyPluginManifest task (re)writes that file by truncating and
98-
// copying; when a full `gradle test` run schedules both, order this task after them so a
99-
// manifest is never read mid-write, which otherwise surfaces intermittently as the pf4j
100-
// error "Field 'id' cannot be empty". Ordering only (mustRunAfter), so running
101-
// :nextflow:test on its own does not force every plugin to build.
102-
mustRunAfter rootProject.subprojects
103-
.findAll { it.path.startsWith(':plugins:') }
104-
.collect { "${it.path}:copyPluginManifest" }
10596
}
10697

10798
application {

modules/nextflow/src/main/groovy/nextflow/container/ApptainerBuilder.groovy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ class ApptainerBuilder extends SingularityBuilder {
4848
if( config.engineOptions )
4949
this.addEngineOptions(config.engineOptions)
5050

51+
this.resourceLimits = config.resourceLimits
52+
5153
if( config.runOptions )
5254
this.addRunOptions(config.runOptions)
5355
}

modules/nextflow/src/main/groovy/nextflow/container/ApptainerConfig.groovy

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ class ApptainerConfig implements ConfigScope, ContainerConfig {
8989
""")
9090
final String registry
9191

92+
@ConfigOption
93+
@Description("""
94+
Apply the task `cpus` and `memory` directives as container resource limits using the `--cpus` and `--memory` flags (default: `false`).
95+
""")
96+
final boolean resourceLimits
97+
9298
@ConfigOption
9399
@Description("""
94100
Specify extra command line options supported by `apptainer exec`.
@@ -109,6 +115,7 @@ class ApptainerConfig implements ConfigScope, ContainerConfig {
109115
ociAutoPull = opts.ociAutoPull as boolean
110116
pullTimeout = opts.pullTimeout as Duration ?: Duration.of('20min')
111117
registry = opts.registry
118+
resourceLimits = opts.resourceLimits as boolean
112119
runOptions = opts.runOptions
113120
}
114121

modules/nextflow/src/main/groovy/nextflow/container/SingularityBuilder.groovy

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class SingularityBuilder extends ContainerBuilder<SingularityBuilder> {
4141

4242
private Boolean ociMode
4343

44+
protected boolean resourceLimits
45+
4446
SingularityBuilder(String name) {
4547
this.image = name
4648
this.homeMount = defaultHomeMount()
@@ -84,6 +86,7 @@ class SingularityBuilder extends ContainerBuilder<SingularityBuilder> {
8486
this.addEngineOptions(config.engineOptions)
8587

8688
this.ociMode = config.ociMode
89+
this.resourceLimits = config.resourceLimits
8790

8891
if( config.runOptions )
8992
this.addRunOptions(config.runOptions)
@@ -132,6 +135,13 @@ class SingularityBuilder extends ContainerBuilder<SingularityBuilder> {
132135
if( ociMode != null )
133136
result << (ociMode ? '--oci ' : '--no-oci ')
134137

138+
if( resourceLimits ) {
139+
if( cpus )
140+
result << "--cpus ${cpus} "
141+
if( memory )
142+
result << "--memory ${memory} "
143+
}
144+
135145
if( autoMounts ) {
136146
makeVolumes(mounts, result)
137147
}

modules/nextflow/src/main/groovy/nextflow/container/SingularityConfig.groovy

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ class SingularityConfig implements ConfigScope, ContainerConfig {
9595
""")
9696
final String registry
9797

98+
@ConfigOption
99+
@Description("""
100+
Apply the task `cpus` and `memory` directives as container resource limits using the `--cpus` and `--memory` flags (default: `false`).
101+
""")
102+
final boolean resourceLimits
103+
98104
@ConfigOption
99105
@Description("""
100106
Specify extra command line options supported by `singularity exec`.
@@ -116,6 +122,7 @@ class SingularityConfig implements ConfigScope, ContainerConfig {
116122
ociMode = opts.ociMode as Boolean
117123
pullTimeout = opts.pullTimeout as Duration ?: Duration.of('20min')
118124
registry = opts.registry
125+
resourceLimits = opts.resourceLimits as boolean
119126
runOptions = opts.runOptions
120127
}
121128

modules/nextflow/src/test/groovy/nextflow/container/ApptainerBuilderTest.groovy

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package nextflow.container
1919
import java.nio.file.Paths
2020

2121
import nextflow.SysEnv
22+
import nextflow.util.MemoryUnit
2223
import spock.lang.Specification
2324
import spock.lang.Unroll
2425
/**
@@ -220,6 +221,21 @@ class ApptainerBuilderTest extends Specification {
220221
cmd == 'set +u; env - PATH="$PATH" ${TMP:+APPTAINERENV_TMP="$TMP"} ${TMPDIR:+APPTAINERENV_TMPDIR="$TMPDIR"} apptainer exec --no-home --pid -B "$NXF_TASK_WORKDIR" ubuntu.img /bin/sh -c "cd \\"$NXF_TASK_WORKDIR\\"; bwa --this --that file.fastq"'
221222
}
222223

224+
def 'should emit resource limits when enabled' () {
225+
expect:
226+
new ApptainerBuilder('busybox')
227+
.setCpus(2)
228+
.setMemory(new MemoryUnit('100M'))
229+
.build()
230+
.runCommand == 'set +u; env - PATH="$PATH" ${TMP:+APPTAINERENV_TMP="$TMP"} ${TMPDIR:+APPTAINERENV_TMPDIR="$TMPDIR"} apptainer exec --no-home --pid -B "$NXF_TASK_WORKDIR" busybox'
231+
232+
new ApptainerBuilder('busybox', new ApptainerConfig(resourceLimits: true))
233+
.setCpus(2)
234+
.setMemory(new MemoryUnit('100M'))
235+
.build()
236+
.runCommand == 'set +u; env - PATH="$PATH" ${TMP:+APPTAINERENV_TMP="$TMP"} ${TMPDIR:+APPTAINERENV_TMPDIR="$TMPDIR"} apptainer exec --no-home --pid --cpus 2 --memory 100m -B "$NXF_TASK_WORKDIR" busybox'
237+
}
238+
223239
@Unroll
224240
def 'test apptainer env'() {
225241

modules/nextflow/src/test/groovy/nextflow/container/ApptainerConfigTest.groovy

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class ApptainerConfigTest extends Specification {
3333
expect:
3434
config.envWhitelist == []
3535
config.pullTimeout.toMillis() == 20 * 60 * 1000 //20 min
36+
config.resourceLimits == false
3637
}
3738

3839
def 'should create config with full map'(){
@@ -48,6 +49,7 @@ class ApptainerConfigTest extends Specification {
4849
ociAutoPull: false,
4950
pullTimeout: '50s',
5051
registry: 'http://registry.com',
52+
resourceLimits: true,
5153
runOptions: '--contain --writable'
5254
]
5355
def config = new ApptainerConfig(configMap)
@@ -61,9 +63,10 @@ class ApptainerConfigTest extends Specification {
6163
config.libraryDir == 'libraryDir'
6264
config.noHttps == false
6365
config.ociAutoPull == false
66+
config.pullTimeout.toMillis() == 50_000 // 50s
6467
config.registry == 'http://registry.com'
68+
config.resourceLimits == true
6569
config.runOptions == '--contain --writable'
66-
config.pullTimeout.toMillis() == 50_000 // 50s
6770

6871
}
6972
}

modules/nextflow/src/test/groovy/nextflow/container/SingularityBuilderTest.groovy

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package nextflow.container
1919
import java.nio.file.Paths
2020

2121
import nextflow.SysEnv
22+
import nextflow.util.MemoryUnit
2223
import spock.lang.Specification
2324
import spock.lang.Unroll
2425

@@ -221,6 +222,25 @@ class SingularityBuilderTest extends Specification {
221222
cmd == 'set +u; env - PATH="$PATH" ${TMP:+SINGULARITYENV_TMP="$TMP"} ${TMPDIR:+SINGULARITYENV_TMPDIR="$TMPDIR"} singularity exec --no-home --pid -B "$NXF_TASK_WORKDIR" ubuntu.img /bin/sh -c "cd \\"$NXF_TASK_WORKDIR\\"; bwa --this --that file.fastq"'
222223
}
223224

225+
def 'should emit resource limits when enabled' () {
226+
expect:
227+
new SingularityBuilder('busybox')
228+
.setCpus(2)
229+
.setMemory(new MemoryUnit('100M'))
230+
.build()
231+
.runCommand == 'set +u; env - PATH="$PATH" ${TMP:+SINGULARITYENV_TMP="$TMP"} ${TMPDIR:+SINGULARITYENV_TMPDIR="$TMPDIR"} singularity exec --no-home --pid -B "$NXF_TASK_WORKDIR" busybox'
232+
233+
new SingularityBuilder('busybox', new SingularityConfig(resourceLimits: true))
234+
.setCpus(2)
235+
.setMemory(new MemoryUnit('100M'))
236+
.build()
237+
.runCommand == 'set +u; env - PATH="$PATH" ${TMP:+SINGULARITYENV_TMP="$TMP"} ${TMPDIR:+SINGULARITYENV_TMPDIR="$TMPDIR"} singularity exec --no-home --pid --cpus 2 --memory 100m -B "$NXF_TASK_WORKDIR" busybox'
238+
239+
new SingularityBuilder('busybox', new SingularityConfig(resourceLimits: true))
240+
.build()
241+
.runCommand == 'set +u; env - PATH="$PATH" ${TMP:+SINGULARITYENV_TMP="$TMP"} ${TMPDIR:+SINGULARITYENV_TMPDIR="$TMPDIR"} singularity exec --no-home --pid -B "$NXF_TASK_WORKDIR" busybox'
242+
}
243+
224244
@Unroll
225245
def 'test singularity env'() {
226246

modules/nextflow/src/test/groovy/nextflow/container/SingularityConfigTest.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class SingularityConfigTest extends Specification {
3333
expect:
3434
config.envWhitelist == []
3535
config.pullTimeout.toMillis() == 20 * 60 * 1000 //20 min
36+
config.resourceLimits == false
3637
}
3738

3839
def 'should create config with full map'(){
@@ -48,6 +49,7 @@ class SingularityConfigTest extends Specification {
4849
ociAutoPull: false,
4950
pullTimeout: '50s',
5051
registry: 'http://registry.com',
52+
resourceLimits: true,
5153
runOptions: '--contain --writable'
5254
]
5355
def config = new SingularityConfig(configMap)
@@ -62,6 +64,7 @@ class SingularityConfigTest extends Specification {
6264
config.noHttps == false
6365
config.ociAutoPull == false
6466
config.registry == 'http://registry.com'
67+
config.resourceLimits == true
6568
config.runOptions == '--contain --writable'
6669
config.pullTimeout.toMillis() == 50_000 // 50s
6770

0 commit comments

Comments
 (0)