diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/NomadConfig.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/NomadConfig.groovy index 0716b1f..961b859 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/NomadConfig.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/NomadConfig.groovy @@ -35,10 +35,12 @@ class NomadConfig { final NomadClientOpts clientOpts final NomadJobOpts jobOpts + final NomadDebug debug NomadConfig(Map nomadConfigMap) { clientOpts = new NomadClientOpts((nomadConfigMap?.client ?: Collections.emptyMap()) as Map) jobOpts = new NomadJobOpts((nomadConfigMap?.jobs ?: Collections.emptyMap()) as Map) + debug = new NomadDebug((nomadConfigMap?.debug ?: Collections.emptyMap()) as Map) } class NomadClientOpts{ @@ -129,4 +131,15 @@ class NomadConfig { } } + static class NomadDebug { + + @Delegate + Map<String,Object> target + + NomadDebug(Map<String,Object> debug) { + this.target = debug ?: Collections.<String,Object>emptyMap() + } + + boolean getJson() { Boolean.valueOf( target.json as String ) } + } } 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 0061488..8aa1495 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadService.groovy @@ -38,6 +38,9 @@ import nextflow.nomad.NomadConfig import nextflow.nomad.config.VolumeSpec import nextflow.processor.TaskRun import nextflow.util.MemoryUnit +import org.yaml.snakeyaml.Yaml + +import java.nio.file.Path /** * Nomad Service @@ -90,7 +93,7 @@ class NomadService implements Closeable{ void close() throws IOException { } - String submitTask(String id, TaskRun task, List<String> args, Map<String, String>env){ + String submitTask(String id, TaskRun task, List<String> args, Map<String, String>env, Path saveJsonPath=null){ Job job = new Job(); job.ID = id job.name = task.name @@ -102,6 +105,14 @@ class NomadService implements Closeable{ JobRegisterRequest jobRegisterRequest = new JobRegisterRequest(); jobRegisterRequest.setJob(job); + + if( saveJsonPath ) try { + saveJsonPath.text = job.toString() + } + catch( Exception e ) { + log.debug "WARN: unable to save request json -- cause: ${e.message ?: e}" + } + JobRegisterResponse jobRegisterResponse = jobsApi.registerJob(jobRegisterRequest, config.jobOpts.region, config.jobOpts.namespace, null, null) jobRegisterResponse.evalID } diff --git a/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadTaskHandler.groovy b/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadTaskHandler.groovy index d449f2a..59d4cf1 100644 --- a/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadTaskHandler.groovy +++ b/plugins/nf-nomad/src/main/nextflow/nomad/executor/NomadTaskHandler.groovy @@ -16,7 +16,7 @@ */ package nextflow.nomad.executor - +import groovy.transform.CompileDynamic import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import io.nomadproject.client.model.Resources @@ -131,7 +131,7 @@ class NomadTaskHandler extends TaskHandler implements FusionAwareTask { final taskLauncher = getSubmitCommand(task) final taskEnv = getEnv(task) - nomadService.submitTask(this.jobName, task, taskLauncher, taskEnv) + nomadService.submitTask(this.jobName, task, taskLauncher, taskEnv, debugPath()) // submit the task execution log.debug "[NOMAD] Submitted task ${task.name} with taskId=${this.jobName}" @@ -139,6 +139,11 @@ class NomadTaskHandler extends TaskHandler implements FusionAwareTask { this.status = TaskStatus.SUBMITTED } + protected Path debugPath() { + boolean debug = config.debug?.getJson() + return debug ? task.workDir.resolve('.job.json') : null + } + protected List<String> getSubmitCommand(TaskRun task) { return fusionEnabled() ? fusionSubmitCli() diff --git a/plugins/nf-nomad/src/test/nextflow/nomad/executor/NomadServiceSpec.groovy b/plugins/nf-nomad/src/test/nextflow/nomad/executor/NomadServiceSpec.groovy index 12b7896..445a67d 100644 --- a/plugins/nf-nomad/src/test/nextflow/nomad/executor/NomadServiceSpec.groovy +++ b/plugins/nf-nomad/src/test/nextflow/nomad/executor/NomadServiceSpec.groovy @@ -30,6 +30,7 @@ import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer import spock.lang.Specification +import java.nio.file.Files import java.nio.file.Path /** @@ -504,4 +505,60 @@ class NomadServiceSpec extends Specification{ body.Job.TaskGroups[0].Tasks[0].Constraints[0].LTarget == '${meta.my_custom_value}' } + void "save the job spec if requested"(){ + given: + def config = new NomadConfig( + client:[ + address : "http://${mockWebServer.hostName}:${mockWebServer.port}" + ], + debug:[ + json: true + ] + ) + def service = new NomadService(config) + + String id = "theId" + String name = "theName" + String image = "theImage" + List<String> args = ["theCommand", "theArgs"] + String workingDir = "/a/b/c" + Map<String, String>env = [test:"test"] + + def mockTask = Mock(TaskRun){ + getName() >> name + getContainer() >> image + getConfig() >> Mock(TaskConfig) + getWorkDirStr() >> workingDir + getContainer() >> "ubuntu" + getProcessor() >> Mock(TaskProcessor){ + getExecutor() >> Mock(Executor){ + isFusionEnabled() >> false + } + } + getWorkDir() >> Path.of(workingDir) + toTaskBean() >> Mock(TaskBean){ + getWorkDir() >> Path.of(workingDir) + getScript() >> "theScript" + getShell() >> ["bash"] + getInputFiles() >> [:] + } + } + + mockWebServer.enqueue(new MockResponse() + .setBody(JsonOutput.toJson(["EvalID":"test"]).toString()) + .addHeader("Content-Type", "application/json")); + + def outputJson = Files.createTempFile("nomad",".json") + when: + + def idJob = service.submitTask(id, mockTask, args, env, outputJson) + + then: + idJob + + and: + outputJson.text.indexOf(" Job {") != -1 + } + + }