From 9a76a7e63229471ad035f44b2c43a7732f0a83e2 Mon Sep 17 00:00:00 2001 From: Jorge Aguilera Date: Mon, 22 Jul 2024 13:09:36 +0200 Subject: [PATCH 01/14] first iteration in secrets implementation Signed-off-by: Jorge Aguilera --- .../gradle/plugins/GenerateIdxTask.groovy | 1 + .../gradle/plugins/SourcesMatcher.groovy | 5 ++ .../main/nextflow/nomad/NomadPlugin.groovy | 2 + .../nextflow/nomad/NomadSecretProvider.groovy | 60 +++++++++++++++++++ .../nomad/executor/NomadService.groovy | 19 +++++- .../nomad/executor/TaskDirectives.groovy | 5 +- validation/secrets/main.nf | 20 +++++++ validation/secrets/nextflow.config | 21 +++++++ 8 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 plugins/nf-nomad/src/main/nextflow/nomad/NomadSecretProvider.groovy create mode 100644 validation/secrets/main.nf create mode 100644 validation/secrets/nextflow.config diff --git a/buildSrc/src/main/groovy/nextflow/gradle/plugins/GenerateIdxTask.groovy b/buildSrc/src/main/groovy/nextflow/gradle/plugins/GenerateIdxTask.groovy index 4b8708f..289f475 100644 --- a/buildSrc/src/main/groovy/nextflow/gradle/plugins/GenerateIdxTask.groovy +++ b/buildSrc/src/main/groovy/nextflow/gradle/plugins/GenerateIdxTask.groovy @@ -34,6 +34,7 @@ abstract class GenerateIdxTask extends DefaultTask{ def matcher = new SourcesMatcher(project) def extensionsClassName = matcher.pluginExtensions + extensionsClassName += matcher.providers def traceClassName = matcher.traceObservers def all = extensionsClassName+traceClassName output.text = all.join('\n') diff --git a/buildSrc/src/main/groovy/nextflow/gradle/plugins/SourcesMatcher.groovy b/buildSrc/src/main/groovy/nextflow/gradle/plugins/SourcesMatcher.groovy index 1865b93..84237bd 100644 --- a/buildSrc/src/main/groovy/nextflow/gradle/plugins/SourcesMatcher.groovy +++ b/buildSrc/src/main/groovy/nextflow/gradle/plugins/SourcesMatcher.groovy @@ -21,6 +21,11 @@ class SourcesMatcher { findSources(/class (\w+) extends Executor implements ExtensionPoint/) } + List getProviders(){ + return findSources(/class (\w+) implements (.+)Provider/) + } + + List getTraceObservers(){ return findSources(/class (\w+) implements TraceObserverFactory/) } diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy index 5772093..5273ae8 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy @@ -21,6 +21,7 @@ import groovy.transform.CompileStatic import nextflow.nomad.executor.TaskDirectives import nextflow.plugin.BasePlugin import nextflow.script.ProcessConfig +import nextflow.secret.SecretsLoader import org.pf4j.PluginWrapper /** @@ -35,6 +36,7 @@ class NomadPlugin extends BasePlugin { NomadPlugin(PluginWrapper wrapper) { super(wrapper) addCustomDirectives() + SecretsLoader.instance.reset() } private static void addCustomDirectives() { diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/NomadSecretProvider.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/NomadSecretProvider.groovy new file mode 100644 index 0000000..668b846 --- /dev/null +++ b/plugins/nf-nomad/src/main/nextflow/nomad/NomadSecretProvider.groovy @@ -0,0 +1,60 @@ +package nextflow.nomad + +import groovy.util.logging.Slf4j +import nextflow.plugin.Priority +import nextflow.secret.Secret +import nextflow.secret.SecretsProvider + +@Slf4j +@Priority(-100) // high priority +class NomadSecretProvider implements SecretsProvider, Closeable{ + + @Override + void close() throws IOException { + } + + @Override + boolean activable() { + return true + } + + @Override + SecretsProvider load() { + this + } + + @Override + Secret getSecret(String name) { + log.error("NomadSecretProvider can't get secret, use nomad cli or disable it") + null + } + + @Override + String getSecretsEnv(List secretNames) { + log.error("NomadSecretProvider can't get secret, use nomad cli or disable it") + null + } + + @Override + String getSecretsEnv() { + log.error("NomadSecretProvider can't get secret, use nomad cli or disable it") + null + } + + @Override + void putSecret(String name, String value) { + throw new UnsupportedOperationException("NomadSecretProvider can't put secret, use nomad cli") + } + + @Override + void removeSecret(String name) { + throw new UnsupportedOperationException("NomadSecretProvider can't remove secret, use nomad cli") + } + + @Override + Set listSecretsNames() { + log.error("NomadSecretProvider can't get secret, use nomad cli or disable it") + null + } + +} diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy index 21cc80c..5325304 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy @@ -20,6 +20,7 @@ package nextflow.nomad.executor import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import io.nomadproject.client.ApiClient +import io.nomadproject.client.ApiException import io.nomadproject.client.api.JobsApi import io.nomadproject.client.model.* import nextflow.nomad.models.ConstraintsBuilder @@ -110,6 +111,9 @@ class NomadService implements Closeable{ try { JobRegisterResponse jobRegisterResponse = jobsApi.registerJob(jobRegisterRequest, config.jobOpts().region, config.jobOpts().namespace, null, null) jobRegisterResponse.evalID + } catch( ApiException apiException){ + log.debug("[NOMAD] Failed to submit ${job.name} -- Cause: ${apiException.responseBody ?: apiException}", apiException) + throw new ProcessSubmitException("[NOMAD] Failed to submit ${job.name} -- Cause: ${apiException.responseBody ?: apiException}", apiException) } catch (Throwable e) { log.debug("[NOMAD] Failed to submit ${job.name} -- Cause: ${e.message ?: e}", e) throw new ProcessSubmitException("[NOMAD] Failed to submit ${job.name} -- Cause: ${e.message ?: e}", e) @@ -186,7 +190,7 @@ class NomadService implements Closeable{ affinity(task, taskDef) constraint(task, taskDef) constraints(task, taskDef) - + secrets(task, taskDef) return taskDef } @@ -276,7 +280,18 @@ class NomadService implements Closeable{ taskDef } - + protected Task secrets(TaskRun task, Task taskDef){ + def secrets = task.processor?.config?.get(TaskDirectives.SECRETS) + if( secrets ){ + Template template = new Template(envvars: true, destPath: "/secrets/nf-nomad") + String tmpl = secrets.collect{ String name-> + "${name}={{ with nomadVar \"secrets/${name}\" }}{{ .${name} }}{{ end }}" + }.join('\n').stripIndent() + template.embeddedTmpl(tmpl) + taskDef.addTemplatesItem(template) + } + taskDef + } protected Job assignDatacenters(TaskRun task, Job job){ def datacenters = task.processor?.config?.get(TaskDirectives.DATACENTERS) diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/executor/TaskDirectives.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/executor/TaskDirectives.groovy index edc1722..e72c381 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/executor/TaskDirectives.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/executor/TaskDirectives.groovy @@ -6,8 +6,11 @@ class TaskDirectives { public static final String CONSTRAINTS = "constraints" + public static final String SECRETS = "secret" + public static final List ALL = [ DATACENTERS, - CONSTRAINTS + CONSTRAINTS, + SECRETS ] } diff --git a/validation/secrets/main.nf b/validation/secrets/main.nf new file mode 100644 index 0000000..3304707 --- /dev/null +++ b/validation/secrets/main.nf @@ -0,0 +1,20 @@ +#!/usr/bin/env nextflow + +process sayHello { + container 'ubuntu:20.04' + secret 'MY_ACCESS_KEY' + secret 'MY_SECRET_KEY' + + input: + val x + output: + stdout + + """ + echo $x world! the access \$MY_ACCESS_KEY and the secret \$MY_SECRET_KEY + """ +} + +workflow { + Channel.of('Bonjour', 'Ciao', 'Hello', 'Hola') | sayHello | view +} diff --git a/validation/secrets/nextflow.config b/validation/secrets/nextflow.config new file mode 100644 index 0000000..d35b509 --- /dev/null +++ b/validation/secrets/nextflow.config @@ -0,0 +1,21 @@ +plugins { + id "nf-nomad@${System.getenv("NOMAD_PLUGIN_VERSION") ?: "latest"}" +} + +process { + executor = "nomad" +} + +nomad { + + client { + address = "http://localhost:4646" + } + + jobs { + deleteOnCompletion = false + volume = { type "host" name "scratchdir" } + } + +} + From dd47cde27ba0cff79b10d78c6daba0d32dffaa20 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Wed, 24 Jul 2024 05:03:23 +0200 Subject: [PATCH 02/14] tweak the start-nomad script for different platforms [ci skip] --- validation/start-nomad.sh | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/validation/start-nomad.sh b/validation/start-nomad.sh index 215506f..39b9474 100755 --- a/validation/start-nomad.sh +++ b/validation/start-nomad.sh @@ -2,7 +2,14 @@ set -ue NOMAD_VERSION="1.8.1" -NOMAD_PLATFORM="linux_amd64" +NOMAD_PLATFORM=${NOMAD_PLATFORM:-linux_amd64} + +## Available platforms +#- "linux_amd64" +#- "linux_arm64" +#- "darwin_amd64" +#- "darwin_arm64" +#- "windows_amd64" SECURE=0 [[ "$@" =~ '--secure' ]] && SECURE=1 @@ -10,7 +17,8 @@ SECURE=0 if [ ! -f ./nomad ]; then curl -O "https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_${NOMAD_PLATFORM}.zip" unzip nomad_${NOMAD_VERSION}_${NOMAD_PLATFORM}.zip - rm -f nomad_${NOMAD_VERSION}_${NOMAD_PLATFORM}.zip + rm -f nomad_${NOMAD_VERSION}_${NOMAD_PLATFORM}.zip LICENSE.txt + chmod +x ./nomad fi mkdir -p nomad_temp @@ -53,7 +61,7 @@ if [ "$SECURE" == 0 ]; then # basic nomad cluter ../nomad agent -config server.conf -config client.conf -config server-custom.conf -config client-custom.conf else - # secured nomad cluster +# secured nomad cluster ../nomad agent -config server.conf -config client.conf -config server-custom.conf -config client-custom.conf & cd .. ./wait-nomad.sh @@ -62,4 +70,4 @@ NOMAD_TOKEN=$(nomad acl bootstrap | awk '/^Secret ID/ {print $4}') export NOMAD_TOKEN echo New super token generated. echo export NOMAD_TOKEN=$NOMAD_TOKEN -fi +fi \ No newline at end of file From b36638a723dc65e3ae686b7ebfa2dd9767e3bd95 Mon Sep 17 00:00:00 2001 From: Jorge Aguilera Date: Thu, 25 Jul 2024 21:30:59 +0200 Subject: [PATCH 03/14] nomad secrets, add get set and list commands Signed-off-by: Jorge Aguilera --- .../main/nextflow/nomad/NomadPlugin.groovy | 25 +++++- .../nextflow/nomad/config/NomadJobOpts.groovy | 3 + .../nomad/executor/NomadService.groovy | 55 ++++++++++++- .../nomad/secrets/NomadSecretCmd.groovy | 79 +++++++++++++++++++ .../{ => secrets}/NomadSecretProvider.groovy | 14 ++-- validation/secrets/nextflow.config | 1 + 6 files changed, 166 insertions(+), 11 deletions(-) create mode 100644 plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretCmd.groovy rename plugins/nf-nomad/src/main/nextflow/nomad/{ => secrets}/NomadSecretProvider.groovy (61%) diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy index 5273ae8..55512ba 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy @@ -18,6 +18,9 @@ package nextflow.nomad import groovy.transform.CompileStatic +import groovy.util.logging.Slf4j +import nextflow.cli.PluginAbstractExec +import nextflow.nomad.secrets.NomadSecretCmd import nextflow.nomad.executor.TaskDirectives import nextflow.plugin.BasePlugin import nextflow.script.ProcessConfig @@ -31,7 +34,8 @@ import org.pf4j.PluginWrapper * @author : matthdsm */ @CompileStatic -class NomadPlugin extends BasePlugin { +@Slf4j +class NomadPlugin extends BasePlugin implements PluginAbstractExec{ NomadPlugin(PluginWrapper wrapper) { super(wrapper) @@ -42,4 +46,23 @@ class NomadPlugin extends BasePlugin { private static void addCustomDirectives() { ProcessConfig.DIRECTIVES.addAll(TaskDirectives.ALL) } + + @Override + List getCommands() { + return ['secrets'] + } + + @Override + int exec(String cmd, List args) { + return switch (cmd){ + case 'secrets'-> secrets(args.first(), args.drop(1)) + default -> -1 + } + } + + int secrets(String action, Listargs){ + NomadSecretCmd nomadSecretCmd = new NomadSecretCmd() + nomadSecretCmd.runCommand( session.config , action, args) + return 0 + } } diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/config/NomadJobOpts.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/config/NomadJobOpts.groovy index a31dd3f..2c70e8c 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/config/NomadJobOpts.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/config/NomadJobOpts.groovy @@ -40,6 +40,7 @@ class NomadJobOpts{ List datacenters String region String namespace + String secretsPath String dockerVolume JobVolume[] volumeSpec JobAffinity affinitySpec @@ -76,6 +77,8 @@ class NomadJobOpts{ this.affinitySpec = parseAffinity(nomadJobOpts) this.constraintSpec = parseConstraint(nomadJobOpts) this.constraintsSpec = parseConstraints(nomadJobOpts) + + this.secretsPath = nomadJobOpts.secretsPath ?: sysEnv.get('NOMAD_SECRETS_PATH') ?: "secrets/nf-nomad" } JobVolume[] parseVolumes(Map nomadJobOpts){ diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy index 5325304..0d01c0f 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy @@ -22,6 +22,7 @@ import groovy.util.logging.Slf4j import io.nomadproject.client.ApiClient import io.nomadproject.client.ApiException import io.nomadproject.client.api.JobsApi +import io.nomadproject.client.api.VariablesApi import io.nomadproject.client.model.* import nextflow.nomad.models.ConstraintsBuilder import nextflow.nomad.models.JobConstraints @@ -44,8 +45,9 @@ import java.nio.file.Path class NomadService implements Closeable{ NomadConfig config - + ApiClient apiClient JobsApi jobsApi + VariablesApi variablesApi NomadService(NomadConfig config) { this.config = config @@ -55,7 +57,7 @@ class NomadService implements Closeable{ final READ_TIMEOUT_MILLISECONDS = 60000 final WRITE_TIMEOUT_MILLISECONDS = 60000 - ApiClient apiClient = new ApiClient( connectTimeout: CONNECTION_TIMEOUT_MILLISECONDS, readTimeout: READ_TIMEOUT_MILLISECONDS, writeTimeout: WRITE_TIMEOUT_MILLISECONDS) + apiClient = new ApiClient( connectTimeout: CONNECTION_TIMEOUT_MILLISECONDS, readTimeout: READ_TIMEOUT_MILLISECONDS, writeTimeout: WRITE_TIMEOUT_MILLISECONDS) apiClient.basePath = config.clientOpts().address log.debug "[NOMAD] Client Address: ${config.clientOpts().address}" @@ -64,6 +66,7 @@ class NomadService implements Closeable{ apiClient.apiKey = config.clientOpts().token } this.jobsApi = new JobsApi(apiClient) + this.variablesApi = new VariablesApi(apiClient) } protected Resources getResources(TaskRun task) { @@ -284,8 +287,9 @@ class NomadService implements Closeable{ def secrets = task.processor?.config?.get(TaskDirectives.SECRETS) if( secrets ){ Template template = new Template(envvars: true, destPath: "/secrets/nf-nomad") + String secretPath = config.jobOpts().secretsPath String tmpl = secrets.collect{ String name-> - "${name}={{ with nomadVar \"secrets/${name}\" }}{{ .${name} }}{{ end }}" + "${name}={{ with nomadVar \"$secretPath/${name}\" }}{{ .${name} }}{{ end }}" }.join('\n').stripIndent() template.embeddedTmpl(tmpl) taskDef.addTemplatesItem(template) @@ -380,4 +384,49 @@ class NomadService implements Closeable{ throw new ProcessSubmitException("[NOMAD] Failed to get alloactions ${jobId} -- Cause: ${e.message ?: e}", e) } } + + String getVariableValue(String key){ + getVariableValue(config.jobOpts().secretsPath, key) + } + + String getVariableValue(String path, String key){ + var variable = variablesApi.getVariableQuery("$path/$key", + config.jobOpts().region, + config.jobOpts().namespace, + null, null, null, null, null, null, null) + variable?.items?.find{ it.key == key }?.value + } + + void setVariableValue(String key, String value){ + setVariableValue(config.jobOpts().secretsPath, key, value) + } + + void setVariableValue(String path, String key, String value){ + var content = Map.of(key,value) + var variable = new Variable(path: path, items: content) + variablesApi.postVariable("$path/$key", variable, + config.jobOpts().region, + config.jobOpts().namespace, + null, null, null) + } + + List getVariablesList(){ + var listRequest = variablesApi.getVariablesListRequest( + config.jobOpts().region, + config.jobOpts().namespace, + null, null, null, null, null, null, null) + listRequest.collect{ it.path} + } + + void deleteVariable(String key){ + deleteVariable(config.jobOpts().secretsPath, key) + } + + void deleteVariable(String path, String key){ + var variable = new Variable( items: Map.of(key, "")) + variablesApi.deleteVariable("$path/$key", variable, + config.jobOpts().region, + config.jobOpts().namespace, + null, null, null) + } } diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretCmd.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretCmd.groovy new file mode 100644 index 0000000..0ae2ba9 --- /dev/null +++ b/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretCmd.groovy @@ -0,0 +1,79 @@ +package nextflow.nomad.secrets + +import groovy.util.logging.Slf4j +import nextflow.exception.AbortOperationException +import nextflow.nomad.config.NomadConfig +import nextflow.nomad.executor.NomadService +import nextflow.plugin.Priority +import nextflow.secret.Secret +import nextflow.secret.SecretImpl +import nextflow.secret.SecretsProvider + +@Slf4j +class NomadSecretCmd { + + protected NomadService service + protected NomadConfig nomadConfig + + int runCommand(Map config, String action, List args){ + nomadConfig = new NomadConfig((config.nomad ?: Collections.emptyMap()) as Map) + service = new NomadService(nomadConfig) + return switch (action){ + case 'get' ->execGetSecretNames(args.removeAt(0).toString()) + case 'set' ->execSetSecretNames(args.removeAt(0).toString(),args.removeAt(0).toString()) + case 'list'->execListSecretsNames() + case 'delete'->execDeleteSecretNames(args.removeAt(0).toString()) + default -> -1 + } + } + + int execListSecretsNames(){ + def list = listSecretsNames() + println list.join('\n') + return 0 + } + + int execGetSecretNames(String name){ + if(!name){ + throw new AbortOperationException("Wrong number of arguments") + } + def secret = getSecret(name) + println secret + return 0 + } + + int execSetSecretNames(String name, String value){ + if(!name){ + throw new AbortOperationException("Wrong number of arguments") + } + setSecret(name, value) + return 0 + } + + int execDeleteSecretNames(String name){ + if(!name){ + throw new AbortOperationException("Wrong number of arguments") + } + deleteSecret(name) + return 0 + } + + String getSecret(String name) { + String value = service.getVariableValue(name) + if( !value ) + throw new AbortOperationException("Missing secret name") + value + } + + Set listSecretsNames() { + service.variablesList + } + + void setSecret(String name, String value) { + service.setVariableValue(name, value) + } + + void deleteSecret(String name){ + service.deleteVariable(name) + } +} diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/NomadSecretProvider.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretProvider.groovy similarity index 61% rename from plugins/nf-nomad/src/main/nextflow/nomad/NomadSecretProvider.groovy rename to plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretProvider.groovy index 668b846..9e4bb45 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/NomadSecretProvider.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretProvider.groovy @@ -1,4 +1,4 @@ -package nextflow.nomad +package nextflow.nomad.secrets import groovy.util.logging.Slf4j import nextflow.plugin.Priority @@ -25,35 +25,35 @@ class NomadSecretProvider implements SecretsProvider, Closeable{ @Override Secret getSecret(String name) { - log.error("NomadSecretProvider can't get secret, use nomad cli or disable it") + log.debug("NomadSecretProvider can't get secret, use nomad cli or nextflow plugin nf-nomad:secrets") null } @Override String getSecretsEnv(List secretNames) { - log.error("NomadSecretProvider can't get secret, use nomad cli or disable it") + log.debug("NomadSecretProvider can't get secret, use nomad cli or nextflow plugin nf-nomad:secrets") null } @Override String getSecretsEnv() { - log.error("NomadSecretProvider can't get secret, use nomad cli or disable it") + log.debug("NomadSecretProvider can't get secret, use nomad cli or nextflow plugin nf-nomad:secrets") null } @Override void putSecret(String name, String value) { - throw new UnsupportedOperationException("NomadSecretProvider can't put secret, use nomad cli") + throw new UnsupportedOperationException("NomadSecretProvider can't put secret, use nomad cli or nextflow plugin nf-nomad:secrets") } @Override void removeSecret(String name) { - throw new UnsupportedOperationException("NomadSecretProvider can't remove secret, use nomad cli") + throw new UnsupportedOperationException("NomadSecretProvider can't remove secret, use nomad cli or nextflow plugin nf-nomad:secrets") } @Override Set listSecretsNames() { - log.error("NomadSecretProvider can't get secret, use nomad cli or disable it") + log.debug("NomadSecretProvider can't get secret, use nomad cli or nextflow plugin nf-nomad:secrets") null } diff --git a/validation/secrets/nextflow.config b/validation/secrets/nextflow.config index d35b509..6dc2be9 100644 --- a/validation/secrets/nextflow.config +++ b/validation/secrets/nextflow.config @@ -15,6 +15,7 @@ nomad { jobs { deleteOnCompletion = false volume = { type "host" name "scratchdir" } + namespace = 'ns-qa' } } From e95d874a17b1c07d171127c97eeabeede38754e6 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Fri, 26 Jul 2024 11:05:36 +0200 Subject: [PATCH 04/14] delete nomad with stop command [ci skip] --- validation/stop-nomad.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/validation/stop-nomad.sh b/validation/stop-nomad.sh index 85f2ae1..714b400 100755 --- a/validation/stop-nomad.sh +++ b/validation/stop-nomad.sh @@ -5,4 +5,5 @@ sleep 1 df -h --output=target | grep nf-task | xargs sudo umount pkill -9 nomad sleep 1 -rm -rf nomad_temp \ No newline at end of file +rm -rf nomad_temp +rm ./nomad From d4e7015702efe622df53bef15cdc96530bb3d35b Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Fri, 26 Jul 2024 11:38:23 +0200 Subject: [PATCH 05/14] tweak the sun-nomadlab config to accommodate variables [ci skip] --- validation/sun-nomadlab/nextflow.config | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/validation/sun-nomadlab/nextflow.config b/validation/sun-nomadlab/nextflow.config index eed649f..1f59479 100644 --- a/validation/sun-nomadlab/nextflow.config +++ b/validation/sun-nomadlab/nextflow.config @@ -30,18 +30,20 @@ nomad { } jobs { + namespace = 'nf-nomad' deleteOnCompletion = false - constraints = { - - node { - unique = [name: "nomad03"] - } - - attr { - unique = [hostname:'nomad03'] - //raw 'platform.aws.instance-type', '=', 'm4.xlarge' - } + volume = { type "host" name "scratch" } + +// constraints = { +// +// node { +// unique = [name: "nomad03"] +// } +// +// attr { +// unique = [hostname:'nomad03'] +// //raw 'platform.aws.instance-type', '=', 'm4.xlarge' +// } } } -} From 38721e9decfa618a06ac6a48425d5a5015c6470a Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Fri, 26 Jul 2024 11:59:26 +0200 Subject: [PATCH 06/14] update the sun-nomadlab config for config level secret vars [ci skip] --- validation/sun-nomadlab/nextflow.config | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/validation/sun-nomadlab/nextflow.config b/validation/sun-nomadlab/nextflow.config index 1f59479..ed6b547 100644 --- a/validation/sun-nomadlab/nextflow.config +++ b/validation/sun-nomadlab/nextflow.config @@ -7,8 +7,14 @@ process { } aws { +# From Nomad variables secret store + # accessKey = secrets.SUN_NOMADLAB_MINIO_ACCESS_KEY + # secretKey = secrets.SUN_NOMADLAB_MINIO_SECRET_KEY + +# From local Nextflow secret store accessKey = secrets.SUN_NOMADLAB_ACCESS_KEY secretKey = secrets.SUN_NOMADLAB_SECRET_KEY + client { endpoint = 'http://100.119.165.23:9000' } From b39b0795cd4a92158493fe7e8fcb6c66684b6ca4 Mon Sep 17 00:00:00 2001 From: Jorge Aguilera Date: Sat, 27 Jul 2024 11:22:00 +0200 Subject: [PATCH 07/14] enable nomad secrets via nextflow config if false use Local implementation Signed-off-by: Jorge Aguilera --- .../gradle/plugins/SourcesMatcher.groovy | 2 +- .../nextflow/nomad/config/NomadJobOpts.groovy | 16 ++++-- .../nomad/config/NomadSecretOpts.groovy | 13 +++++ .../nomad/executor/NomadService.groovy | 26 ++++----- .../nomad/secrets/NomadSecretProvider.groovy | 54 +++++++++++++------ validation/secrets/main.nf | 3 ++ validation/secrets/nextflow.config | 4 ++ 7 files changed, 85 insertions(+), 33 deletions(-) create mode 100644 plugins/nf-nomad/src/main/nextflow/nomad/config/NomadSecretOpts.groovy diff --git a/buildSrc/src/main/groovy/nextflow/gradle/plugins/SourcesMatcher.groovy b/buildSrc/src/main/groovy/nextflow/gradle/plugins/SourcesMatcher.groovy index 84237bd..1beb1cc 100644 --- a/buildSrc/src/main/groovy/nextflow/gradle/plugins/SourcesMatcher.groovy +++ b/buildSrc/src/main/groovy/nextflow/gradle/plugins/SourcesMatcher.groovy @@ -22,7 +22,7 @@ class SourcesMatcher { } List getProviders(){ - return findSources(/class (\w+) implements (.+)Provider/) + return findSources(/implements SecretsProvider/) } diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/config/NomadJobOpts.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/config/NomadJobOpts.groovy index 2c70e8c..78ce744 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/config/NomadJobOpts.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/config/NomadJobOpts.groovy @@ -40,7 +40,6 @@ class NomadJobOpts{ List datacenters String region String namespace - String secretsPath String dockerVolume JobVolume[] volumeSpec JobAffinity affinitySpec @@ -48,6 +47,8 @@ class NomadJobOpts{ JobConstraints constraintsSpec + NomadSecretOpts secretOpts + NomadJobOpts(Map nomadJobOpts, Map env=null){ assert nomadJobOpts!=null @@ -77,8 +78,7 @@ class NomadJobOpts{ this.affinitySpec = parseAffinity(nomadJobOpts) this.constraintSpec = parseConstraint(nomadJobOpts) this.constraintsSpec = parseConstraints(nomadJobOpts) - - this.secretsPath = nomadJobOpts.secretsPath ?: sysEnv.get('NOMAD_SECRETS_PATH') ?: "secrets/nf-nomad" + this.secretOpts = parseSecrets(nomadJobOpts) } JobVolume[] parseVolumes(Map nomadJobOpts){ @@ -161,4 +161,14 @@ class NomadJobOpts{ null } } + + NomadSecretOpts parseSecrets(Map nomadJobOpts){ + if (nomadJobOpts.secrets && nomadJobOpts.secrets instanceof Map) { + def secretOpts = new NomadSecretOpts(nomadJobOpts.secrets as Map) + secretOpts + }else{ + null + } + } + } \ No newline at end of file diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/config/NomadSecretOpts.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/config/NomadSecretOpts.groovy new file mode 100644 index 0000000..5f0bfb2 --- /dev/null +++ b/plugins/nf-nomad/src/main/nextflow/nomad/config/NomadSecretOpts.groovy @@ -0,0 +1,13 @@ +package nextflow.nomad.config + +class NomadSecretOpts { + + final Boolean enable + final String path + + NomadSecretOpts(Map map){ + this.enable = map.containsKey('enable') ? map.get('enable') as boolean : false + this.path = map.path ?: "secrets/nf-nomad" + } + +} diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy index 0d01c0f..2b54f87 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy @@ -284,15 +284,17 @@ class NomadService implements Closeable{ } protected Task secrets(TaskRun task, Task taskDef){ - def secrets = task.processor?.config?.get(TaskDirectives.SECRETS) - if( secrets ){ - Template template = new Template(envvars: true, destPath: "/secrets/nf-nomad") - String secretPath = config.jobOpts().secretsPath - String tmpl = secrets.collect{ String name-> - "${name}={{ with nomadVar \"$secretPath/${name}\" }}{{ .${name} }}{{ end }}" - }.join('\n').stripIndent() - template.embeddedTmpl(tmpl) - taskDef.addTemplatesItem(template) + if( config.jobOpts()?.secretOpts?.enable) { + def secrets = task.processor?.config?.get(TaskDirectives.SECRETS) + if (secrets) { + Template template = new Template(envvars: true, destPath: "/secrets/nf-nomad") + String secretPath = config.jobOpts()?.secretOpts?.path + String tmpl = secrets.collect { String name -> + "${name}={{ with nomadVar \"$secretPath/${name}\" }}{{ .${name} }}{{ end }}" + }.join('\n').stripIndent() + template.embeddedTmpl(tmpl) + taskDef.addTemplatesItem(template) + } } taskDef } @@ -386,7 +388,7 @@ class NomadService implements Closeable{ } String getVariableValue(String key){ - getVariableValue(config.jobOpts().secretsPath, key) + getVariableValue(config.jobOpts().secretOpts?.path, key) } String getVariableValue(String path, String key){ @@ -398,7 +400,7 @@ class NomadService implements Closeable{ } void setVariableValue(String key, String value){ - setVariableValue(config.jobOpts().secretsPath, key, value) + setVariableValue(config.jobOpts().secretOpts?.path, key, value) } void setVariableValue(String path, String key, String value){ @@ -419,7 +421,7 @@ class NomadService implements Closeable{ } void deleteVariable(String key){ - deleteVariable(config.jobOpts().secretsPath, key) + deleteVariable(config.jobOpts().secretOpts?.path, key) } void deleteVariable(String path, String key){ diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretProvider.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretProvider.groovy index 9e4bb45..815c8d5 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretProvider.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretProvider.groovy @@ -1,60 +1,80 @@ package nextflow.nomad.secrets import groovy.util.logging.Slf4j +import nextflow.Global +import nextflow.nomad.config.NomadConfig +import nextflow.nomad.executor.NomadService import nextflow.plugin.Priority +import nextflow.secret.LocalSecretsProvider import nextflow.secret.Secret +import nextflow.secret.SecretImpl import nextflow.secret.SecretsProvider @Slf4j @Priority(-100) // high priority -class NomadSecretProvider implements SecretsProvider, Closeable{ +class NomadSecretProvider extends LocalSecretsProvider implements SecretsProvider { - @Override - void close() throws IOException { - } + NomadConfig config @Override - boolean activable() { - return true + LocalSecretsProvider load() { + return super.load() } - @Override - SecretsProvider load() { - this + protected boolean isEnabled(){ + if( !config ){ + config = new NomadConfig(Global.config?.nomad as Map ?: Map.of()) + } + config?.jobOpts()?.secretOpts?.enable } @Override Secret getSecret(String name) { - log.debug("NomadSecretProvider can't get secret, use nomad cli or nextflow plugin nf-nomad:secrets") - null + if( !this.enabled ) { + return super.getSecret(name) + } + NomadService service = new NomadService(config) + String value = service.getVariableValue(name) + return new SecretImpl(name: name, value: value) } @Override String getSecretsEnv(List secretNames) { - log.debug("NomadSecretProvider can't get secret, use nomad cli or nextflow plugin nf-nomad:secrets") + if( !this.enabled ) { + return super.getSecretsEnv(secretNames) + } null } @Override String getSecretsEnv() { - log.debug("NomadSecretProvider can't get secret, use nomad cli or nextflow plugin nf-nomad:secrets") + if( !this.enabled ) { + return super.getSecretsEnv() + } null } @Override void putSecret(String name, String value) { - throw new UnsupportedOperationException("NomadSecretProvider can't put secret, use nomad cli or nextflow plugin nf-nomad:secrets") + if( !this.enabled ) { + super.putSecret(name, value) + } } @Override void removeSecret(String name) { - throw new UnsupportedOperationException("NomadSecretProvider can't remove secret, use nomad cli or nextflow plugin nf-nomad:secrets") + if( !this.enabled ) { + super.removeSecret(name) + } } @Override Set listSecretsNames() { - log.debug("NomadSecretProvider can't get secret, use nomad cli or nextflow plugin nf-nomad:secrets") - null + if( !this.enabled ) { + return super.listSecretsNames() + } + NomadService service = new NomadService(config) + service.variablesList as Set } } diff --git a/validation/secrets/main.nf b/validation/secrets/main.nf index 3304707..82259b8 100644 --- a/validation/secrets/main.nf +++ b/validation/secrets/main.nf @@ -18,3 +18,6 @@ process sayHello { workflow { Channel.of('Bonjour', 'Ciao', 'Hello', 'Hola') | sayHello | view } +workflow.onComplete { + println("The secret is: ${secrets.MY_ACCESS_KEY}") +} \ No newline at end of file diff --git a/validation/secrets/nextflow.config b/validation/secrets/nextflow.config index 6dc2be9..3fb29f7 100644 --- a/validation/secrets/nextflow.config +++ b/validation/secrets/nextflow.config @@ -16,6 +16,10 @@ nomad { deleteOnCompletion = false volume = { type "host" name "scratchdir" } namespace = 'ns-qa' + + secrets { + enable = true //if false then use LocalSecretsProvider implementation + } } } From e4de78b19b4bf33db4f08ba1030ed796e67aeb13 Mon Sep 17 00:00:00 2001 From: Jorge Aguilera Date: Sat, 27 Jul 2024 11:33:17 +0200 Subject: [PATCH 08/14] add test Signed-off-by: Jorge Aguilera --- .../executor/NomadSecretServiceSpec.groovy | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 plugins/nf-nomad/src/test/nextflow/nomad/executor/NomadSecretServiceSpec.groovy diff --git a/plugins/nf-nomad/src/test/nextflow/nomad/executor/NomadSecretServiceSpec.groovy b/plugins/nf-nomad/src/test/nextflow/nomad/executor/NomadSecretServiceSpec.groovy new file mode 100644 index 0000000..96ea68c --- /dev/null +++ b/plugins/nf-nomad/src/test/nextflow/nomad/executor/NomadSecretServiceSpec.groovy @@ -0,0 +1,120 @@ +/* + * Copyright 2023-, Stellenbosch University, South Africa + * Copyright 2024, Evaluacion y Desarrollo de Negocios, Spain + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nextflow.nomad.executor + +import groovy.json.JsonOutput +import groovy.json.JsonSlurper +import nextflow.executor.Executor +import nextflow.nomad.config.NomadConfig +import nextflow.processor.TaskBean +import nextflow.processor.TaskConfig +import nextflow.processor.TaskProcessor +import nextflow.processor.TaskRun +import nextflow.script.ProcessConfig +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import spock.lang.Specification + +import java.nio.file.Files +import java.nio.file.Path + +/** + * Unit test for Nomad Service for Secrets requests + * + * Validate requests using a Mock WebServer + * + * @author : Jorge Aguilera + */ +class NomadSecretServiceSpec extends Specification{ + + MockWebServer mockWebServer + + def setup() { + mockWebServer = new MockWebServer() + mockWebServer.start() + } + + def cleanup() { + mockWebServer.shutdown() + } + + void "should request a variable"(){ + given: + def config = new NomadConfig( + client:[ + address : "http://${mockWebServer.hostName}:${mockWebServer.port}" + ], + jobs: [ + secrets:[ + enable: true, + path: 'test' + ] + ] + ) + def service = new NomadService(config) + + when: + mockWebServer.enqueue(new MockResponse() + .addHeader("Content-Type", "application/json")); + + service.getVariableValue("MySecret") + def recordedRequest = mockWebServer.takeRequest(); + + then: + recordedRequest.method == "GET" + recordedRequest.path == "/v1/var/test%2FMySecret" + + when: + mockWebServer.enqueue(new MockResponse() + .addHeader("Content-Type", "application/json")); + + service.getVariableValue("another", "MySecret") + def recordedRequest2 = mockWebServer.takeRequest(); + + then: + recordedRequest2.method == "GET" + recordedRequest2.path == "/v1/var/another%2FMySecret" + } + + void "should set a variable"(){ + given: + def config = new NomadConfig( + client:[ + address : "http://${mockWebServer.hostName}:${mockWebServer.port}" + ], + jobs: [ + secrets:[ + enable: true, + path: 'test' + ] + ] + ) + def service = new NomadService(config) + + when: + mockWebServer.enqueue(new MockResponse() + .addHeader("Content-Type", "application/json")); + + service.setVariableValue("MySecret", "MyValue") + def recordedRequest = mockWebServer.takeRequest(); + + then: + recordedRequest.method == "POST" + recordedRequest.path == "/v1/var/test%2FMySecret" + } + +} From 8b2fbf952fb1190c7af1b6846f1ae6eb51146895 Mon Sep 17 00:00:00 2001 From: Jorge Aguilera Date: Sat, 27 Jul 2024 20:08:26 +0200 Subject: [PATCH 09/14] fix small bug Signed-off-by: Jorge Aguilera --- plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy index 55512ba..2753979 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy @@ -63,6 +63,5 @@ class NomadPlugin extends BasePlugin implements PluginAbstractExec{ int secrets(String action, Listargs){ NomadSecretCmd nomadSecretCmd = new NomadSecretCmd() nomadSecretCmd.runCommand( session.config , action, args) - return 0 } } From 1cee20642dcf0783addf5897284cff4a9bbefda6 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 29 Jul 2024 08:20:37 +0200 Subject: [PATCH 10/14] improve local testing env [ci skip] --- .../nf-nomad/src/resources/META-INF/MANIFEST.MF | 2 +- settings.gradle | 2 -- validation/secrets/nextflow.config | 2 +- validation/sun-nomadlab/nextflow.config | 15 +++++++++------ validation/wait-nomad.sh | 1 + 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/plugins/nf-nomad/src/resources/META-INF/MANIFEST.MF b/plugins/nf-nomad/src/resources/META-INF/MANIFEST.MF index 6fe64c3..da26157 100644 --- a/plugins/nf-nomad/src/resources/META-INF/MANIFEST.MF +++ b/plugins/nf-nomad/src/resources/META-INF/MANIFEST.MF @@ -1,6 +1,6 @@ Manifest-Version: 1.0 Plugin-Id: nf-nomad -Plugin-Version: 0.1.0 +Plugin-Version: 0.1.2 Plugin-Class: nextflow.nomad.NomadPlugin Plugin-Provider: nextflow Plugin-Requires: >=24.01.0-edge diff --git a/settings.gradle b/settings.gradle index 832c3d6..42569db 100644 --- a/settings.gradle +++ b/settings.gradle @@ -18,5 +18,3 @@ rootProject.name = 'nf-nomad' include 'plugins' include('plugins:nf-nomad') - -//includeBuild('_resources/nextflow') diff --git a/validation/secrets/nextflow.config b/validation/secrets/nextflow.config index 3fb29f7..924ee98 100644 --- a/validation/secrets/nextflow.config +++ b/validation/secrets/nextflow.config @@ -15,7 +15,7 @@ nomad { jobs { deleteOnCompletion = false volume = { type "host" name "scratchdir" } - namespace = 'ns-qa' + namespace = 'nf-nomad' secrets { enable = true //if false then use LocalSecretsProvider implementation diff --git a/validation/sun-nomadlab/nextflow.config b/validation/sun-nomadlab/nextflow.config index ed6b547..b718fef 100644 --- a/validation/sun-nomadlab/nextflow.config +++ b/validation/sun-nomadlab/nextflow.config @@ -7,13 +7,13 @@ process { } aws { -# From Nomad variables secret store - # accessKey = secrets.SUN_NOMADLAB_MINIO_ACCESS_KEY - # secretKey = secrets.SUN_NOMADLAB_MINIO_SECRET_KEY +// From Nomad variables secret store + accessKey = secrets.SUN_NOMADLAB_MINIO_ACCESS_KEY + secretKey = secrets.SUN_NOMADLAB_MINIO_SECRET_KEY -# From local Nextflow secret store - accessKey = secrets.SUN_NOMADLAB_ACCESS_KEY - secretKey = secrets.SUN_NOMADLAB_SECRET_KEY +// From local Nextflow secret store + // accessKey = secrets.SUN_NOMADLAB_ACCESS_KEY + // secretKey = secrets.SUN_NOMADLAB_SECRET_KEY client { endpoint = 'http://100.119.165.23:9000' @@ -37,6 +37,9 @@ nomad { jobs { namespace = 'nf-nomad' + secrets { + enable = true + } deleteOnCompletion = false volume = { type "host" name "scratch" } diff --git a/validation/wait-nomad.sh b/validation/wait-nomad.sh index aa94154..e2ded0d 100755 --- a/validation/wait-nomad.sh +++ b/validation/wait-nomad.sh @@ -5,5 +5,6 @@ until curl --output /dev/null --silent --fail http://localhost:4646/v1/status/le sleep 5 done +./nomad namespace apply -description "local-nomadlab" nf-nomad ./nomad node drain -disable $(./nomad node status -quiet) sleep 3 From ccb80bd9e4b8a1c51eca47b8250a9e9441d601f0 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 29 Jul 2024 08:35:41 +0200 Subject: [PATCH 11/14] test with sun-nomadlab and localsecretstore [ci skip] --- validation/start-nomad.sh | 1 + validation/sun-nomadlab/nextflow.config | 8 ++++---- validation/wait-nomad.sh | 1 - 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/validation/start-nomad.sh b/validation/start-nomad.sh index 39b9474..e63aa8d 100755 --- a/validation/start-nomad.sh +++ b/validation/start-nomad.sh @@ -64,6 +64,7 @@ else # secured nomad cluster ../nomad agent -config server.conf -config client.conf -config server-custom.conf -config client-custom.conf & cd .. +#./nomad namespace apply -description "local-nomadlab" nf-nomad ./wait-nomad.sh sleep 3 NOMAD_TOKEN=$(nomad acl bootstrap | awk '/^Secret ID/ {print $4}') diff --git a/validation/sun-nomadlab/nextflow.config b/validation/sun-nomadlab/nextflow.config index b718fef..c13a897 100644 --- a/validation/sun-nomadlab/nextflow.config +++ b/validation/sun-nomadlab/nextflow.config @@ -8,12 +8,12 @@ process { aws { // From Nomad variables secret store - accessKey = secrets.SUN_NOMADLAB_MINIO_ACCESS_KEY - secretKey = secrets.SUN_NOMADLAB_MINIO_SECRET_KEY +// accessKey = secrets.SUN_NOMADLAB_MINIO_ACCESS_KEY +// secretKey = secrets.SUN_NOMADLAB_MINIO_SECRET_KEY // From local Nextflow secret store - // accessKey = secrets.SUN_NOMADLAB_ACCESS_KEY - // secretKey = secrets.SUN_NOMADLAB_SECRET_KEY + accessKey = secrets.SUN_NOMADLAB_ACCESS_KEY + secretKey = secrets.SUN_NOMADLAB_SECRET_KEY client { endpoint = 'http://100.119.165.23:9000' diff --git a/validation/wait-nomad.sh b/validation/wait-nomad.sh index e2ded0d..aa94154 100755 --- a/validation/wait-nomad.sh +++ b/validation/wait-nomad.sh @@ -5,6 +5,5 @@ until curl --output /dev/null --silent --fail http://localhost:4646/v1/status/le sleep 5 done -./nomad namespace apply -description "local-nomadlab" nf-nomad ./nomad node drain -disable $(./nomad node status -quiet) sleep 3 From 95e35c2aa4756ade2205cb678939cd81dc9e3080 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 29 Jul 2024 09:04:29 +0200 Subject: [PATCH 12/14] rename enable -> enabled to comply with standard --- .../main/nextflow/nomad/config/NomadSecretOpts.groovy | 4 ++-- .../main/nextflow/nomad/executor/NomadService.groovy | 2 +- .../nextflow/nomad/secrets/NomadSecretProvider.groovy | 2 +- validation/secrets/nextflow.config | 2 +- validation/sun-nomadlab/nextflow.config | 11 ++++++----- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/config/NomadSecretOpts.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/config/NomadSecretOpts.groovy index 5f0bfb2..ee6749e 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/config/NomadSecretOpts.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/config/NomadSecretOpts.groovy @@ -2,11 +2,11 @@ package nextflow.nomad.config class NomadSecretOpts { - final Boolean enable + final Boolean enabled final String path NomadSecretOpts(Map map){ - this.enable = map.containsKey('enable') ? map.get('enable') as boolean : false + this.enabled = map.containsKey('enabled') ? map.get('enabled') as boolean : false this.path = map.path ?: "secrets/nf-nomad" } diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy index 2b54f87..080e172 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy @@ -284,7 +284,7 @@ class NomadService implements Closeable{ } protected Task secrets(TaskRun task, Task taskDef){ - if( config.jobOpts()?.secretOpts?.enable) { + if( config.jobOpts()?.secretOpts?.enabled) { def secrets = task.processor?.config?.get(TaskDirectives.SECRETS) if (secrets) { Template template = new Template(envvars: true, destPath: "/secrets/nf-nomad") diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretProvider.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretProvider.groovy index 815c8d5..80d4196 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretProvider.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretProvider.groovy @@ -25,7 +25,7 @@ class NomadSecretProvider extends LocalSecretsProvider implements SecretsProvide if( !config ){ config = new NomadConfig(Global.config?.nomad as Map ?: Map.of()) } - config?.jobOpts()?.secretOpts?.enable + config?.jobOpts()?.secretOpts?.enabled } @Override diff --git a/validation/secrets/nextflow.config b/validation/secrets/nextflow.config index 924ee98..93e1219 100644 --- a/validation/secrets/nextflow.config +++ b/validation/secrets/nextflow.config @@ -18,7 +18,7 @@ nomad { namespace = 'nf-nomad' secrets { - enable = true //if false then use LocalSecretsProvider implementation + enabled = true //if false then use LocalSecretsProvider implementation } } diff --git a/validation/sun-nomadlab/nextflow.config b/validation/sun-nomadlab/nextflow.config index c13a897..7c3e535 100644 --- a/validation/sun-nomadlab/nextflow.config +++ b/validation/sun-nomadlab/nextflow.config @@ -8,12 +8,12 @@ process { aws { // From Nomad variables secret store -// accessKey = secrets.SUN_NOMADLAB_MINIO_ACCESS_KEY -// secretKey = secrets.SUN_NOMADLAB_MINIO_SECRET_KEY + accessKey = secrets.SUN_NOMADLAB_MINIO_ACCESS_KEY + secretKey = secrets.SUN_NOMADLAB_MINIO_SECRET_KEY // From local Nextflow secret store - accessKey = secrets.SUN_NOMADLAB_ACCESS_KEY - secretKey = secrets.SUN_NOMADLAB_SECRET_KEY +// accessKey = secrets.SUN_NOMADLAB_ACCESS_KEY +// secretKey = secrets.SUN_NOMADLAB_SECRET_KEY client { endpoint = 'http://100.119.165.23:9000' @@ -38,8 +38,9 @@ nomad { jobs { namespace = 'nf-nomad' secrets { - enable = true + enabled = false } + deleteOnCompletion = false volume = { type "host" name "scratch" } From 4321d22171ae6b49b8e9e31537de7b7e68829a04 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 29 Jul 2024 09:29:15 +0200 Subject: [PATCH 13/14] use the functional config for sun-nomadlab [ci skip] --- validation/sun-nomadlab/nextflow.config | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/validation/sun-nomadlab/nextflow.config b/validation/sun-nomadlab/nextflow.config index 7c3e535..5e5de70 100644 --- a/validation/sun-nomadlab/nextflow.config +++ b/validation/sun-nomadlab/nextflow.config @@ -8,12 +8,12 @@ process { aws { // From Nomad variables secret store - accessKey = secrets.SUN_NOMADLAB_MINIO_ACCESS_KEY - secretKey = secrets.SUN_NOMADLAB_MINIO_SECRET_KEY +// accessKey = secrets.SUN_NOMADLAB_MINIO_ACCESS_KEY +// secretKey = secrets.SUN_NOMADLAB_MINIO_SECRET_KEY // From local Nextflow secret store -// accessKey = secrets.SUN_NOMADLAB_ACCESS_KEY -// secretKey = secrets.SUN_NOMADLAB_SECRET_KEY + accessKey = secrets.SUN_NOMADLAB_ACCESS_KEY + secretKey = secrets.SUN_NOMADLAB_SECRET_KEY client { endpoint = 'http://100.119.165.23:9000' @@ -38,22 +38,22 @@ nomad { jobs { namespace = 'nf-nomad' secrets { - enabled = false + enabled = true } deleteOnCompletion = false volume = { type "host" name "scratch" } -// constraints = { -// -// node { -// unique = [name: "nomad03"] -// } -// + constraints = { // attr { // unique = [hostname:'nomad03'] // //raw 'platform.aws.instance-type', '=', 'm4.xlarge' -// } + + node { + unique = [name: "nomad03"] + } + } } +} From 0efa7198fa71bd9bae54985ab17a1d86fc3811b2 Mon Sep 17 00:00:00 2001 From: Abhinav Sharma Date: Mon, 29 Jul 2024 09:34:50 +0200 Subject: [PATCH 14/14] update authors [ci skip] --- plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy | 2 +- .../main/nextflow/nomad/secrets/NomadSecretProvider.groovy | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy index 2753979..9c6d243 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy @@ -31,7 +31,7 @@ import org.pf4j.PluginWrapper * Nextflow plugin for Nomad executor * * @author Abhinav Sharma - * @author : matthdsm + * @author Jorge Aguilera */ @CompileStatic @Slf4j diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretProvider.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretProvider.groovy index 80d4196..0375ec2 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretProvider.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/secrets/NomadSecretProvider.groovy @@ -10,6 +10,11 @@ import nextflow.secret.Secret import nextflow.secret.SecretImpl import nextflow.secret.SecretsProvider +/** + * Custom Nextflow CLI commands for nf-nomad + * + * @author Jorge Aguilera + */ @Slf4j @Priority(-100) // high priority class NomadSecretProvider extends LocalSecretsProvider implements SecretsProvider {