diff --git a/nomad/job_test.go b/nomad/job_test.go new file mode 100644 index 0000000..0dd3cab --- /dev/null +++ b/nomad/job_test.go @@ -0,0 +1,252 @@ +package nomad + +import ( + "fmt" + nomadApi "github.com/hashicorp/nomad/api" + "github.com/sirupsen/logrus" + "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "testing" +) + +func TestParseJob(t *testing.T) { + exited := false + logger, hook := test.NewNullLogger() + logger.ExitFunc = func(i int) { + exited = true + } + + log = logger.WithField("pkg", "nomad") + + t.Run("parses the given default job", func(t *testing.T) { + job := parseJob(defaultJobHCL) + assert.False(t, exited) + assert.NotNil(t, job) + }) + + t.Run("fatals when given wrong job", func(t *testing.T) { + job := parseJob("") + assert.True(t, exited) + assert.Nil(t, job) + assert.Equal(t, logrus.FatalLevel, hook.LastEntry().Level) + }) +} + +func createTestTaskGroup() *nomadApi.TaskGroup { + return nomadApi.NewTaskGroup("taskGroup", 42) +} + +func createTestTask() *nomadApi.Task { + return nomadApi.NewTask("task", "docker") +} + +func createTestResources() *nomadApi.Resources { + expectedCPULimit := 1337 + expectedMemoryLimit := 42 + return &nomadApi.Resources{CPU: &expectedCPULimit, MemoryMB: &expectedMemoryLimit} +} + +func createTestJob() (*nomadApi.Job, *nomadApi.Job) { + base := nomadApi.NewBatchJob("python-job", "python-job", "region-name", 100) + job := nomadApi.NewBatchJob("python-job", "python-job", "region-name", 100) + task := createTestTask() + task.Name = fmt.Sprintf(TaskNameFormat, *job.ID) + image := "python:latest" + task.Config = map[string]interface{}{"image": image} + task.Config["network_mode"] = "none" + task.Resources = createTestResources() + taskGroup := createTestTaskGroup() + taskGroupName := fmt.Sprintf(TaskGroupNameFormat, *job.ID) + taskGroup.Name = &taskGroupName + taskGroup.Tasks = []*nomadApi.Task{task} + job.TaskGroups = []*nomadApi.TaskGroup{taskGroup} + return job, base +} + +func TestCreateTaskGroupCreatesNewTaskGroupWhenJobHasNoTaskGroup(t *testing.T) { + job := nomadApi.NewBatchJob("test", "test", "test", 1) + + if assert.Equal(t, 0, len(job.TaskGroups)) { + expectedTaskGroup := createTestTaskGroup() + taskGroup := createTaskGroup(job, *expectedTaskGroup.Name, uint(*expectedTaskGroup.Count)) + + assert.Equal(t, *expectedTaskGroup, *taskGroup) + assert.Equal(t, []*nomadApi.TaskGroup{taskGroup}, job.TaskGroups, "it should add the task group to the job") + } +} + +func TestCreateTaskGroupOverwritesOptionsWhenJobHasTaskGroup(t *testing.T) { + job := nomadApi.NewBatchJob("test", "test", "test", 1) + existingTaskGroup := createTestTaskGroup() + existingTaskGroup.Meta = map[string]string{"field": "should still exist"} + newTaskGroupList := []*nomadApi.TaskGroup{existingTaskGroup} + job.TaskGroups = newTaskGroupList + + newName := *existingTaskGroup.Name + "longerName" + newCount := *existingTaskGroup.Count + 42 + + taskGroup := createTaskGroup(job, newName, uint(newCount)) + + // create a new copy to avoid changing the original one as it is a pointer + expectedTaskGroup := *existingTaskGroup + expectedTaskGroup.Name = &newName + expectedTaskGroup.Count = &newCount + + assert.Equal(t, expectedTaskGroup, *taskGroup) + assert.Equal(t, newTaskGroupList, job.TaskGroups, "it should not modify the jobs task group list") +} + +func TestConfigureNetworkFatalsWhenNoTaskExists(t *testing.T) { + logger, hook := test.NewNullLogger() + logger.ExitFunc = func(i int) { + panic(i) + } + log = logger.WithField("pkg", "job_test") + taskGroup := createTestTaskGroup() + if assert.Equal(t, 0, len(taskGroup.Tasks)) { + assert.Panics(t, func() { + configureNetwork(taskGroup, false, nil) + }) + assert.Equal(t, logrus.FatalLevel, hook.LastEntry().Level) + } +} + +func TestConfigureNetworkCreatesNewNetworkWhenNoNetworkExists(t *testing.T) { + taskGroup := createTestTaskGroup() + task := createTestTask() + taskGroup.Tasks = []*nomadApi.Task{task} + + if assert.Equal(t, 0, len(taskGroup.Networks)) { + configureNetwork(taskGroup, true, []uint16{}) + + assert.Equal(t, 1, len(taskGroup.Networks)) + } +} + +func TestConfigureNetworkDoesNotCreateNewNetworkWhenNetworkExists(t *testing.T) { + taskGroup := createTestTaskGroup() + task := createTestTask() + taskGroup.Tasks = []*nomadApi.Task{task} + networkResource := &nomadApi.NetworkResource{Mode: "bridge"} + taskGroup.Networks = []*nomadApi.NetworkResource{networkResource} + + if assert.Equal(t, 1, len(taskGroup.Networks)) { + configureNetwork(taskGroup, true, []uint16{}) + + assert.Equal(t, 1, len(taskGroup.Networks)) + assert.Equal(t, networkResource, taskGroup.Networks[0]) + } +} + +func TestConfigureNetworkSetsCorrectValues(t *testing.T) { + taskGroup := createTestTaskGroup() + task := createTestTask() + _, ok := task.Config["network_mode"] + + require.False(t, ok, "Test tasks network_mode should not be set") + + taskGroup.Tasks = []*nomadApi.Task{task} + exposedPortsTests := [][]uint16{{}, {1337}, {42, 1337}} + + t.Run("with no network access", func(t *testing.T) { + for _, ports := range exposedPortsTests { + testTaskGroup := *taskGroup + testTask := *task + testTaskGroup.Tasks = []*nomadApi.Task{&testTask} + + configureNetwork(&testTaskGroup, false, ports) + mode, ok := testTask.Config["network_mode"] + assert.True(t, ok) + assert.Equal(t, "none", mode) + assert.Equal(t, 0, len(testTaskGroup.Networks)) + } + }) + + t.Run("with network access", func(t *testing.T) { + for _, ports := range exposedPortsTests { + testTaskGroup := *taskGroup + testTask := *task + testTaskGroup.Tasks = []*nomadApi.Task{&testTask} + + configureNetwork(&testTaskGroup, true, ports) + require.Equal(t, 1, len(testTaskGroup.Networks)) + + networkResource := testTaskGroup.Networks[0] + assert.Equal(t, "bridge", networkResource.Mode) + require.Equal(t, len(ports), len(networkResource.DynamicPorts)) + + for _, expectedPort := range ports { + found := false + for _, actualPort := range networkResource.DynamicPorts { + if actualPort.To == int(expectedPort) { + found = true + break + } + } + assert.True(t, found, fmt.Sprintf("port list should contain %v", expectedPort)) + } + + mode, ok := testTask.Config["network_mode"] + assert.False(t, ok && mode == "none") + } + }) +} + +func TestConfigureTaskWhenNoTaskExists(t *testing.T) { + taskGroup := createTestTaskGroup() + require.Equal(t, 0, len(taskGroup.Tasks)) + + expectedResources := createTestResources() + expectedTaskGroup := *taskGroup + expectedTask := nomadApi.NewTask("task", DefaultTaskDriver) + expectedTask.Resources = expectedResources + expectedImage := "python:latest" + expectedTask.Config = map[string]interface{}{"image": expectedImage, "network_mode": "none"} + expectedTaskGroup.Tasks = []*nomadApi.Task{expectedTask} + + configureTask(taskGroup, expectedTask.Name, + uint(*expectedResources.CPU), uint(*expectedResources.MemoryMB), + expectedImage, false, []uint16{}) + + assert.Equal(t, expectedTaskGroup, *taskGroup) +} + +func TestConfigureTaskWhenTaskExists(t *testing.T) { + taskGroup := createTestTaskGroup() + task := createTestTask() + task.Config = map[string]interface{}{"my_custom_config": "should not be overwritten"} + taskGroup.Tasks = []*nomadApi.Task{task} + require.Equal(t, 1, len(taskGroup.Tasks)) + + expectedResources := createTestResources() + expectedTaskGroup := *taskGroup + expectedTask := *task + expectedTask.Resources = expectedResources + expectedImage := "python:latest" + expectedTask.Config["image"] = expectedImage + expectedTask.Config["network_mode"] = "none" + expectedTaskGroup.Tasks = []*nomadApi.Task{&expectedTask} + + configureTask(taskGroup, expectedTask.Name, + uint(*expectedResources.CPU), uint(*expectedResources.MemoryMB), + expectedImage, false, []uint16{}) + + assert.Equal(t, expectedTaskGroup, *taskGroup) + assert.Equal(t, task, taskGroup.Tasks[0], "it should not create a new task") +} + +func TestCreateJobSetsAllGivenArguments(t *testing.T) { + testJob, base := createTestJob() + apiClient := ApiClient{&nomadApi.Client{}, *base} + job := apiClient.createJob( + *testJob.ID, + uint(*testJob.TaskGroups[0].Count), + uint(*testJob.TaskGroups[0].Tasks[0].Resources.CPU), + uint(*testJob.TaskGroups[0].Tasks[0].Resources.MemoryMB), + testJob.TaskGroups[0].Tasks[0].Config["image"].(string), + false, + nil, + ) + assert.Equal(t, *testJob, *job) +}