Skip to content

Commit

Permalink
nomad secrets, add get set and list commands
Browse files Browse the repository at this point in the history
Signed-off-by: Jorge Aguilera <[email protected]>
  • Loading branch information
jagedn committed Jul 25, 2024
1 parent dd47cde commit b36638a
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 11 deletions.
25 changes: 24 additions & 1 deletion plugins/nf-nomad/src/main/nextflow/nomad/NomadPlugin.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -31,7 +34,8 @@ import org.pf4j.PluginWrapper
* @author : matthdsm <[email protected]>
*/
@CompileStatic
class NomadPlugin extends BasePlugin {
@Slf4j
class NomadPlugin extends BasePlugin implements PluginAbstractExec{

NomadPlugin(PluginWrapper wrapper) {
super(wrapper)
Expand All @@ -42,4 +46,23 @@ class NomadPlugin extends BasePlugin {
private static void addCustomDirectives() {
ProcessConfig.DIRECTIVES.addAll(TaskDirectives.ALL)
}

@Override
List<String> getCommands() {
return ['secrets']
}

@Override
int exec(String cmd, List<String> args) {
return switch (cmd){
case 'secrets'-> secrets(args.first(), args.drop(1))
default -> -1
}
}

int secrets(String action, List<String>args){
NomadSecretCmd nomadSecretCmd = new NomadSecretCmd()
nomadSecretCmd.runCommand( session.config , action, args)
return 0
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class NomadJobOpts{
List<String> datacenters
String region
String namespace
String secretsPath
String dockerVolume
JobVolume[] volumeSpec
JobAffinity affinitySpec
Expand Down Expand Up @@ -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){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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}"

Expand All @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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<String> 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)
}
}
Original file line number Diff line number Diff line change
@@ -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<String> 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<String> listSecretsNames() {
service.variablesList
}

void setSecret(String name, String value) {
service.setVariableValue(name, value)
}

void deleteSecret(String name){
service.deleteVariable(name)
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package nextflow.nomad
package nextflow.nomad.secrets

import groovy.util.logging.Slf4j
import nextflow.plugin.Priority
Expand All @@ -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<String> 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<String> 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
}

Expand Down
1 change: 1 addition & 0 deletions validation/secrets/nextflow.config
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ nomad {
jobs {
deleteOnCompletion = false
volume = { type "host" name "scratchdir" }
namespace = 'ns-qa'
}

}
Expand Down

0 comments on commit b36638a

Please sign in to comment.