Skip to content

Commit

Permalink
finalize the tests for JobBuilder and NomadJobOpts classes
Browse files Browse the repository at this point in the history
  • Loading branch information
abhi18av committed Sep 18, 2024
1 parent 4a0b9a2 commit 6d4e386
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import io.nomadproject.client.model.Resources
import io.nomadproject.client.model.Template
import io.nomadproject.client.model.VolumeMount
import io.nomadproject.client.model.VolumeRequest
import nextflow.nomad.config.NomadConfig
import nextflow.nomad.config.NomadJobOpts
import nextflow.nomad.executor.TaskDirectives
import nextflow.nomad.models.ConstraintsBuilder
Expand All @@ -54,22 +53,22 @@ import groovy.util.logging.Slf4j
class JobBuilder {
private Job job = new Job()

JobBuilder id(String id) {
JobBuilder withId(String id) {
job.ID = id
return this
}

JobBuilder name(String name) {
JobBuilder withName(String name) {
job.name = name
return this
}

JobBuilder type(String type) {
JobBuilder withType(String type) {
job.type = type
return this
}

JobBuilder datacenters(List<String> datacenters) {
JobBuilder withDatacenters(List<String> datacenters) {
job.datacenters = datacenters
return this
}
Expand All @@ -92,12 +91,12 @@ class JobBuilder {
job
}

JobBuilder namespace(String namespace) {
JobBuilder withNamespace(String namespace) {
job.namespace = namespace
return this
}

JobBuilder taskGroups(List<TaskGroup> taskGroups) {
JobBuilder withTaskGroups(List<TaskGroup> taskGroups) {
job.taskGroups = taskGroups
return this
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,8 @@ import io.nomadproject.client.api.JobsApi
import io.nomadproject.client.api.VariablesApi
import io.nomadproject.client.model.*
import nextflow.nomad.builders.JobBuilder
import nextflow.nomad.models.ConstraintsBuilder
import nextflow.nomad.models.JobConstraints
import nextflow.nomad.config.NomadConfig
import nextflow.nomad.models.JobSpreads
import nextflow.nomad.models.JobVolume
import nextflow.nomad.models.SpreadsBuilder
import nextflow.processor.TaskRun
import nextflow.util.MemoryUnit
import nextflow.exception.ProcessSubmitException

import java.nio.file.Path
Expand Down Expand Up @@ -81,12 +75,12 @@ class NomadService implements Closeable{

String submitTask(String id, TaskRun task, List<String> args, Map<String, String> env, Path saveJsonPath = null) {
Job job = new JobBuilder()
.id(id)
.name(task.name)
.type("batch")
// .datacenters(task, this.config.jobOpts().datacenters)
.namespace(this.config.jobOpts().namespace)
.taskGroups([JobBuilder.createTaskGroup(task, args, env, this.config.jobOpts())])
.withId(id)
.withName(task.name)
.withType("batch")
// .withDatacenters(task, this.config.jobOpts().datacenters)
.withNamespace(this.config.jobOpts().namespace)
.withTaskGroups([JobBuilder.createTaskGroup(task, args, env, this.config.jobOpts())])
.build()

JobBuilder.assignDatacenters(task, job)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* 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.builders

import nextflow.nomad.config.NomadConfig
import nextflow.nomad.config.NomadJobOpts
import nextflow.processor.TaskRun
import okhttp3.mockwebserver.MockWebServer
import spock.lang.Specification

/**
* Unit test for Nomad JobBuilder
*
* @author : Abhinav Sharma <abhi18av@outlook.com>
*/


class JobBuilderSpec extends Specification {

def "test JobBuilder withId method"() {
given:
def jobBuilder = new JobBuilder()

when:
def jb = jobBuilder
.withId("test-id")
.build()

then:
jb.ID == "test-id"
}


def "test createTask method"() {
given:
def jobOpts = Mock(NomadJobOpts)
def taskRun = Mock(TaskRun)
def args = ["arg1", "arg2"]
def env = ["key": "value"]

taskRun.container >> "test-container"
taskRun.workDir >> new File("/test/workdir").toPath()
taskRun.getConfig() >> [cpus: 2, memory: "1GB"]

when:
def task = JobBuilder.createTask(taskRun, args, env, jobOpts)

then:
task.name == "nf-task"
task.driver == "docker"
task.config.image == "test-container"
task.config.command == "arg1"
task.config.args == ["arg2"]
task.env == env
task.resources.cores == 2
task.resources.memoryMB == 1024
}


def "test createTaskGroup method"() {
given:
def volumes = [{ type "csi" path "/container/path"}]

def jobOpts = Mock(NomadJobOpts)

def taskRun = Mock(TaskRun)
def args = ["arg1", "arg2"]
def env = ["key": "value"]

taskRun.container >> "test-container"
taskRun.workDir >> new File("/test/workdir").toPath()
taskRun.getConfig() >> [cpus: 2, memory: "1GB"]

when:
def taskGroup = JobBuilder.createTaskGroup(taskRun, args, env, jobOpts)

then:
taskGroup.name == "group"
taskGroup.tasks.size() == 1
taskGroup.tasks[0].name == "nf-task"
taskGroup.tasks[0].config.image == "test-container"
taskGroup.tasks[0].config.command == "arg1"
taskGroup.tasks[0].config.args == ["arg2"]
taskGroup.tasks[0].env == env
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* 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.config

import spock.lang.Specification

/**
* Unit test for NomadJobOpts
*
* @author : Abhinav Sharma <abhi18av@outlook.com>
*/
class NomadJobOptsSpec extends Specification {


def "test default values"() {
given:
def nomadJobOpts = new NomadJobOpts([:])

expect:
nomadJobOpts.rescheduleAttempts == 1
nomadJobOpts.restartAttempts == 1
nomadJobOpts.dockerVolume == null
}

def "test rescheduleAttempts and restartAttempts"() {
given:
def nomadJobOpts = new NomadJobOpts([rescheduleAttempts: 3, restartAttempts: 2])

expect:
nomadJobOpts.rescheduleAttempts == 3
nomadJobOpts.restartAttempts == 2
}

def "test dockerVolume"() {
given:
def nomadJobOpts = new NomadJobOpts([dockerVolume: "test-volume"])

expect:
nomadJobOpts.dockerVolume == "test-volume"
}

def "test parseVolumes with single volume"() {
given:
def volumeClosure = { type "csi" name "test" }
def nomadJobOpts = new NomadJobOpts([volume: volumeClosure])

when:
def volumes = nomadJobOpts.parseVolumes([volume: volumeClosure])

then:
volumes.size() == 1
volumes[0].workDir == true
}

def "test parseVolumes with multiple volumes"() {
given:
def volumeClosure1 = { type "host" name "test" path "/volume/csi" }
def volumeClosure2 = { type "csi" name "test" path "/volume/host"}
def nomadJobOpts = new NomadJobOpts([volumes: [volumeClosure1, volumeClosure2]])

when:
def volumes = nomadJobOpts.parseVolumes([volumes: [volumeClosure1, volumeClosure2]])

then:
volumes.size() == 2
volumes[0].path == "/volume/csi"
volumes[1].path == "/volume/host"
}

def "test parseAffinity"() {
given:
def affinityClosure = { -> }
def nomadJobOpts = new NomadJobOpts([affinity: affinityClosure])

when:
def affinity = nomadJobOpts.parseAffinity([affinity: affinityClosure])

then:
affinity != null
}

def "test parseConstraint"() {
given:
def constraintClosure = { -> }
def nomadJobOpts = new NomadJobOpts([constraint: constraintClosure])

when:
def constraint = nomadJobOpts.parseConstraint([constraint: constraintClosure])

then:
constraint != null
}

def "test parseConstraints"() {
given:
def constraintsClosure = { -> }
def nomadJobOpts = new NomadJobOpts([constraints: constraintsClosure])

when:
def constraints = nomadJobOpts.parseConstraints([constraints: constraintsClosure])

then:
constraints != null
}


def "test parseSpreads"() {
given:
def spreadsClosure = { -> }
def nomadJobOpts = new NomadJobOpts([spreads: spreadsClosure])

when:
def spreads = nomadJobOpts.parseSpreads([spreads: spreadsClosure])

then:
spreads != null
}
}

0 comments on commit 6d4e386

Please sign in to comment.