Skip to content

Commit 27ecb89

Browse files
Refactor K8s support as core plug-in (#5965)
Signed-off-by: Paolo Di Tommaso <[email protected]> Signed-off-by: Ben Sherman <[email protected]> Co-authored-by: Ben Sherman <[email protected]>
1 parent fac2e4b commit 27ecb89

File tree

59 files changed

+254
-58
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+254
-58
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
25.02.3-edge
1+
25.03.0-edge

docs/plugins.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ The following functionalities are provided via plugin components, and they make
1414
- `nf-azure`: Support for Microsoft Azure.
1515
- `nf-cloudcache`: Support for the cloud cache (see `NXF_CLOUDCACHE_PATH` under {ref}`config-env-vars`).
1616
- `nf-console`: Implement Nextflow [REPL console](https://www.nextflow.io/blog/2015/introducing-nextflow-console.html).
17+
- `nf-k8s`: Support for Kubernetes.
1718
- `nf-google`: Support for Google Cloud.
1819
- `nf-tower`: Support for [Seqera Platform](https://seqera.io) (formerly Tower Cloud).
1920
- `nf-wave`: Support for [Wave containers](https://seqera.io/wave/) service.

modules/nextflow/build.gradle

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@ dependencies {
4848
api 'jline:jline:2.9'
4949
api 'org.pf4j:pf4j:3.12.0'
5050
api 'dev.failsafe:failsafe:3.1.0'
51-
api 'org.bouncycastle:bcprov-ext-jdk18on:1.78.1'
52-
api 'org.bouncycastle:bcpkix-jdk18on:1.78.1'
5351
api 'io.seqera:lib-trace:0.1.0'
5452

5553
testImplementation 'org.subethamail:subethasmtp:3.1.7'

modules/nextflow/src/main/groovy/nextflow/cli/CmdKubeRun.groovy

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import com.beust.jcommander.Parameters
2121
import groovy.transform.CompileStatic
2222
import groovy.util.logging.Slf4j
2323
import nextflow.exception.AbortOperationException
24-
import nextflow.k8s.K8sDriverLauncher
24+
import nextflow.plugin.Plugins
25+
import org.pf4j.ExtensionPoint
2526
/**
2627
* Extends `run` command to support Kubernetes deployment
2728
*
@@ -32,6 +33,10 @@ import nextflow.k8s.K8sDriverLauncher
3233
@Parameters(commandDescription = "Execute a workflow in a Kubernetes cluster (experimental)")
3334
class CmdKubeRun extends CmdRun {
3435

36+
interface KubeCommand extends ExtensionPoint {
37+
int run(CmdKubeRun cmd, String pipeline, List<String> args)
38+
}
39+
3540
static private String POD_NAME = /[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/
3641

3742
/**
@@ -76,7 +81,7 @@ class CmdKubeRun extends CmdRun {
7681
runName = runName.replace('_','-')
7782
}
7883

79-
protected boolean background() { launcher.options.background }
84+
boolean background() { launcher.options.background }
8085

8186
protected hasAnsiLogFlag() { launcher.options.hasAnsiLogFlag() }
8287

@@ -93,9 +98,11 @@ class CmdKubeRun extends CmdRun {
9398
headImage = podImage
9499
}
95100
checkRunName()
96-
final driver = new K8sDriverLauncher(cmd: this, runName: runName, headImage: headImage, background: background(), headCpus: headCpus, headMemory: headMemory, headPreScript: headPreScript, plugins: plugins)
97-
driver.run(pipeline, scriptArgs)
98-
final status = driver.shutdown()
101+
Plugins.init()
102+
Plugins.start('nf-k8s')
103+
// load the command operations
104+
final command = Plugins.getExtension(KubeCommand)
105+
final status = command.run(this, pipeline, scriptArgs)
99106
System.exit(status)
100107
}
101108

modules/nextflow/src/main/groovy/nextflow/executor/ExecutorFactory.groovy

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import groovy.transform.PackageScope
2222
import groovy.util.logging.Slf4j
2323
import nextflow.Session
2424
import nextflow.executor.local.LocalExecutor
25-
import nextflow.k8s.K8sExecutor
2625
import nextflow.script.BodyDef
2726
import nextflow.script.ProcessConfig
2827
import nextflow.script.ScriptType
@@ -57,7 +56,6 @@ class ExecutorFactory {
5756
'crg': CrgExecutor,
5857
'bsc': LsfExecutor,
5958
'condor': CondorExecutor,
60-
'k8s': K8sExecutor,
6159
'nqsii': NqsiiExecutor,
6260
'moab': MoabExecutor,
6361
'oar': OarExecutor,

modules/nextflow/src/main/groovy/nextflow/processor/TaskConfig.groovy

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import nextflow.exception.ProcessUnrecoverableException
3030
import nextflow.executor.BashWrapperBuilder
3131
import nextflow.executor.res.AcceleratorResource
3232
import nextflow.executor.res.DiskResource
33-
import nextflow.k8s.model.PodOptions
3433
import nextflow.script.TaskClosure
3534
import nextflow.util.CmdLineHelper
3635
import nextflow.util.CmdLineOptionMap
@@ -499,10 +498,6 @@ class TaskConfig extends LazyMap implements Cloneable {
499498
get('retryCount') as Integer ?: 0
500499
}
501500

502-
PodOptions getPodOptions() {
503-
new PodOptions((List)get('pod'))
504-
}
505-
506501
AcceleratorResource getAccelerator() {
507502
final value = get('accelerator')
508503
if( value instanceof Number )

modules/nextflow/src/main/resources/META-INF/plugins-info.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ [email protected]
44
55
66
7+
78
89

modules/nextflow/src/test/groovy/nextflow/executor/ExecutorFactoryTest.groovy

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import nextflow.executor.local.LocalExecutor
2020
import spock.lang.Specification
2121

2222
import nextflow.Session
23-
import nextflow.k8s.K8sExecutor
2423
import nextflow.script.ProcessConfig
2524
import nextflow.processor.TaskHandler
2625
import nextflow.processor.TaskMonitor
@@ -50,7 +49,6 @@ class ExecutorFactoryTest extends Specification {
5049
factory.getExecutorClass('pbs') == PbsExecutor
5150
factory.getExecutorClass('slurm') == SlurmExecutor
5251
factory.getExecutorClass('condor') == CondorExecutor
53-
factory.getExecutorClass('k8s') == K8sExecutor
5452
factory.getExecutorClass('x') == XExecutor // <-- this is loaded by the name
5553

5654
when:

modules/nextflow/src/test/groovy/nextflow/processor/TaskConfigTest.groovy

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
*/
1616

1717
package nextflow.processor
18+
1819
import java.nio.file.Paths
1920

2021
import nextflow.exception.FailedGuardException
2122
import nextflow.exception.ProcessUnrecoverableException
22-
import nextflow.k8s.model.PodOptions
2323
import nextflow.script.BaseScript
2424
import nextflow.script.ProcessConfig
2525
import nextflow.script.TaskClosure
@@ -545,9 +545,9 @@ class TaskConfigTest extends Specification {
545545
[secret: 'foo', mountPath: '/this'],
546546
[secret: 'bar', env: 'BAR_XXX'] ]
547547

548-
process.createTaskConfig().getPodOptions() == new PodOptions([
549-
[secret: 'foo', mountPath: '/this'],
550-
[secret: 'bar', env: 'BAR_XXX'] ])
548+
process.createTaskConfig().get('pod') == [
549+
[secret: 'foo', mountPath: '/this'],
550+
[secret: 'bar', env: 'BAR_XXX'] ]
551551
}
552552

553553
def 'should get gpu resources' () {

modules/nf-commons/src/main/nextflow/plugin/PluginsFacade.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,9 @@ class PluginsFacade implements PluginStateListener {
416416
if( executor == 'azurebatch' || workDir?.startsWith('az://') || bucketDir?.startsWith('az://') )
417417
plugins << defaultPlugins.getPlugin('nf-azure')
418418

419+
if( executor == 'k8s' )
420+
plugins << defaultPlugins.getPlugin('nf-k8s')
421+
419422
if( Bolts.navigate(config, 'weblog.enabled'))
420423
plugins << new PluginSpec('nf-weblog')
421424

modules/nf-commons/src/test/nextflow/plugin/PluginsFacadeTest.groovy

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,8 @@ class PluginsFacadeTest extends Specification {
183183
'nf-amazon': new PluginSpec('nf-amazon', '0.1.0'),
184184
'nf-google': new PluginSpec('nf-google', '0.1.0'),
185185
'nf-azure': new PluginSpec('nf-azure', '0.1.0'),
186-
'nf-tower': new PluginSpec('nf-tower', '0.1.0')
186+
'nf-tower': new PluginSpec('nf-tower', '0.1.0'),
187+
'nf-k8s': new PluginSpec('nf-k8s', '0.1.0')
187188
])
188189
and:
189190
def handler = new PluginsFacade(defaultPlugins: defaults)
@@ -210,6 +211,12 @@ class PluginsFacadeTest extends Specification {
210211
!plugins.find { it.id == 'nf-amazon' }
211212
plugins.find { it.id == 'nf-azure' }
212213

214+
when:
215+
plugins = handler.defaultPluginsConf([process:[executor: 'k8s']])
216+
then:
217+
plugins.find { it.id == 'nf-k8s' }
218+
!plugins.find { it.id == 'nf-amazon' }
219+
213220
when:
214221
plugins = handler.defaultPluginsConf([:])
215222
then:

plugins/nf-k8s/build.gradle

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2019, Google Inc
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
apply plugin: 'java'
17+
apply plugin: 'java-test-fixtures'
18+
apply plugin: 'idea'
19+
apply plugin: 'groovy'
20+
21+
sourceSets {
22+
main.java.srcDirs = []
23+
main.groovy.srcDirs = ['src/main']
24+
main.resources.srcDirs = ['src/resources']
25+
test.groovy.srcDirs = ['src/test']
26+
test.java.srcDirs = []
27+
test.resources.srcDirs = []
28+
}
29+
30+
configurations {
31+
// see https://docs.gradle.org/4.1/userguide/dependency_management.html#sub:exclude_transitive_dependencies
32+
runtimeClasspath.exclude group: 'org.slf4j', module: 'slf4j-api'
33+
}
34+
35+
dependencies {
36+
compileOnly project(':nextflow')
37+
compileOnly 'org.slf4j:slf4j-api:2.0.16'
38+
compileOnly 'org.pf4j:pf4j:3.12.0'
39+
40+
api 'org.bouncycastle:bcprov-ext-jdk18on:1.78.1'
41+
api 'org.bouncycastle:bcpkix-jdk18on:1.78.1'
42+
43+
testImplementation(testFixtures(project(":nextflow")))
44+
testImplementation "org.apache.groovy:groovy:4.0.26"
45+
testImplementation "org.apache.groovy:groovy-nio:4.0.26"
46+
}
47+
48+
test {
49+
useJUnitPlatform()
50+
}

plugins/nf-k8s/changelog.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
nf-k8s changelog
2+
===================
3+
1.0.0 - 13 Apr 2025
4+
- Refactor Kubernetes support as a core plugin

modules/nextflow/src/main/groovy/nextflow/k8s/K8sConfig.groovy renamed to plugins/nf-k8s/src/main/nextflow/k8s/K8sConfig.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013-2024, Seqera Labs
2+
* Copyright 2013-2025, Seqera Labs
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

modules/nextflow/src/main/groovy/nextflow/k8s/K8sDriverLauncher.groovy renamed to plugins/nf-k8s/src/main/nextflow/k8s/K8sDriverLauncher.groovy

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013-2024, Seqera Labs
2+
* Copyright 2013-2025, Seqera Labs
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -38,7 +38,6 @@ import nextflow.k8s.model.PodEnv
3838
import nextflow.k8s.model.PodMountConfig
3939
import nextflow.k8s.model.PodSpecBuilder
4040
import nextflow.k8s.model.ResourceType
41-
import nextflow.plugin.Plugins
4241
import nextflow.scm.AssetManager
4342
import nextflow.scm.ProviderConfig
4443
import nextflow.util.ConfigHelper
@@ -272,7 +271,6 @@ class K8sDriverLauncher {
272271

273272
if( !interactive && !pipelineName.startsWith('/') && !cmd.remoteProfile && !cmd.runRemoteConfig ) {
274273
// -- check and parse project remote config
275-
Plugins.init()
276274
final pipelineConfig = new AssetManager(pipelineName, cmd) .getConfigFile()
277275
builder.setUserConfigFiles(pipelineConfig)
278276
}

modules/nextflow/src/main/groovy/nextflow/k8s/K8sExecutor.groovy renamed to plugins/nf-k8s/src/main/nextflow/k8s/K8sExecutor.groovy

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013-2024, Seqera Labs
2+
* Copyright 2013-2025, Seqera Labs
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package nextflow.k8s
1818

1919
import groovy.transform.CompileStatic
2020
import groovy.transform.Memoized
21-
import groovy.transform.PackageScope
2221
import groovy.util.logging.Slf4j
2322
import nextflow.executor.Executor
2423
import nextflow.fusion.FusionHelper
@@ -29,6 +28,8 @@ import nextflow.processor.TaskPollingMonitor
2928
import nextflow.processor.TaskRun
3029
import nextflow.util.Duration
3130
import nextflow.util.ServiceName
31+
import org.pf4j.ExtensionPoint
32+
3233
/**
3334
* Implement the Kubernetes executor
3435
*
@@ -37,7 +38,7 @@ import nextflow.util.ServiceName
3738
@Slf4j
3839
@CompileStatic
3940
@ServiceName('k8s')
40-
class K8sExecutor extends Executor {
41+
class K8sExecutor extends Executor implements ExtensionPoint {
4142

4243
/**
4344
* The Kubernetes HTTP client
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2013-2025, Seqera Labs
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package nextflow.k8s
18+
19+
import groovy.transform.CompileStatic
20+
import nextflow.plugin.BasePlugin
21+
import org.pf4j.PluginWrapper
22+
23+
/**
24+
* Kubernetes plugin entry point
25+
*
26+
* @author Paolo Di Tommaso <[email protected]>
27+
*/
28+
@CompileStatic
29+
class K8sPlugin extends BasePlugin {
30+
31+
K8sPlugin(PluginWrapper wrapper) {
32+
super(wrapper)
33+
}
34+
}

modules/nextflow/src/main/groovy/nextflow/k8s/K8sTaskHandler.groovy renamed to plugins/nf-k8s/src/main/nextflow/k8s/K8sTaskHandler.groovy

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013-2024, Seqera Labs
2+
* Copyright 2013-2025, Seqera Labs
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -276,10 +276,13 @@ class K8sTaskHandler extends TaskHandler implements FusionAwareTask {
276276
// merge the pod options provided in the k8s config
277277
// with the ones in process config
278278
def opt1 = k8sConfig.getPodOptions()
279-
def opt2 = task.getConfig().getPodOptions()
279+
def opt2 = taskPodOptions()
280280
return opt1 + opt2
281281
}
282282

283+
protected PodOptions taskPodOptions() {
284+
new PodOptions((List)task.getConfig().get('pod'))
285+
}
283286

284287
protected Map<String,String> getLabels(TaskRun task) {
285288
final result = new LinkedHashMap<String,String>(10)

modules/nextflow/src/main/groovy/nextflow/k8s/K8sWrapperBuilder.groovy renamed to plugins/nf-k8s/src/main/nextflow/k8s/K8sWrapperBuilder.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013-2024, Seqera Labs
2+
* Copyright 2013-2025, Seqera Labs
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

0 commit comments

Comments
 (0)