7bdd43f3e0
Given that the values will rarely change, specially considering that any changes would be backward incompatible change. As such, it's simpler to keep syncing manually in the rare occasion and avoid the syncing code overhead.
809 lines
20 KiB
Go
809 lines
20 KiB
Go
package api
|
|
|
|
import (
|
|
"path/filepath"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestTaskGroup_NewTaskGroup(t *testing.T) {
|
|
t.Parallel()
|
|
grp := NewTaskGroup("grp1", 2)
|
|
expect := &TaskGroup{
|
|
Name: stringToPtr("grp1"),
|
|
Count: intToPtr(2),
|
|
}
|
|
if !reflect.DeepEqual(grp, expect) {
|
|
t.Fatalf("expect: %#v, got: %#v", expect, grp)
|
|
}
|
|
}
|
|
|
|
func TestTaskGroup_Constrain(t *testing.T) {
|
|
t.Parallel()
|
|
grp := NewTaskGroup("grp1", 1)
|
|
|
|
// Add a constraint to the group
|
|
out := grp.Constrain(NewConstraint("kernel.name", "=", "darwin"))
|
|
if n := len(grp.Constraints); n != 1 {
|
|
t.Fatalf("expected 1 constraint, got: %d", n)
|
|
}
|
|
|
|
// Check that the group was returned
|
|
if out != grp {
|
|
t.Fatalf("expected: %#v, got: %#v", grp, out)
|
|
}
|
|
|
|
// Add a second constraint
|
|
grp.Constrain(NewConstraint("memory.totalbytes", ">=", "128000000"))
|
|
expect := []*Constraint{
|
|
{
|
|
LTarget: "kernel.name",
|
|
RTarget: "darwin",
|
|
Operand: "=",
|
|
},
|
|
{
|
|
LTarget: "memory.totalbytes",
|
|
RTarget: "128000000",
|
|
Operand: ">=",
|
|
},
|
|
}
|
|
if !reflect.DeepEqual(grp.Constraints, expect) {
|
|
t.Fatalf("expect: %#v, got: %#v", expect, grp.Constraints)
|
|
}
|
|
}
|
|
|
|
func TestTaskGroup_AddAffinity(t *testing.T) {
|
|
t.Parallel()
|
|
grp := NewTaskGroup("grp1", 1)
|
|
|
|
// Add an affinity to the group
|
|
out := grp.AddAffinity(NewAffinity("kernel.version", "=", "4.6", 100))
|
|
if n := len(grp.Affinities); n != 1 {
|
|
t.Fatalf("expected 1 affinity, got: %d", n)
|
|
}
|
|
|
|
// Check that the group was returned
|
|
if out != grp {
|
|
t.Fatalf("expected: %#v, got: %#v", grp, out)
|
|
}
|
|
|
|
// Add a second affinity
|
|
grp.AddAffinity(NewAffinity("${node.affinity}", "=", "dc2", 50))
|
|
expect := []*Affinity{
|
|
{
|
|
LTarget: "kernel.version",
|
|
RTarget: "4.6",
|
|
Operand: "=",
|
|
Weight: 100,
|
|
},
|
|
{
|
|
LTarget: "${node.affinity}",
|
|
RTarget: "dc2",
|
|
Operand: "=",
|
|
Weight: 50,
|
|
},
|
|
}
|
|
if !reflect.DeepEqual(grp.Affinities, expect) {
|
|
t.Fatalf("expect: %#v, got: %#v", expect, grp.Constraints)
|
|
}
|
|
}
|
|
|
|
func TestTaskGroup_SetMeta(t *testing.T) {
|
|
t.Parallel()
|
|
grp := NewTaskGroup("grp1", 1)
|
|
|
|
// Initializes an empty map
|
|
out := grp.SetMeta("foo", "bar")
|
|
if grp.Meta == nil {
|
|
t.Fatalf("should be initialized")
|
|
}
|
|
|
|
// Check that we returned the group
|
|
if out != grp {
|
|
t.Fatalf("expect: %#v, got: %#v", grp, out)
|
|
}
|
|
|
|
// Add a second meta k/v
|
|
grp.SetMeta("baz", "zip")
|
|
expect := map[string]string{"foo": "bar", "baz": "zip"}
|
|
if !reflect.DeepEqual(grp.Meta, expect) {
|
|
t.Fatalf("expect: %#v, got: %#v", expect, grp.Meta)
|
|
}
|
|
}
|
|
|
|
func TestTaskGroup_AddSpread(t *testing.T) {
|
|
t.Parallel()
|
|
grp := NewTaskGroup("grp1", 1)
|
|
|
|
// Create and add spread
|
|
spreadTarget := NewSpreadTarget("r1", 50)
|
|
spread := NewSpread("${meta.rack}", 100, []*SpreadTarget{spreadTarget})
|
|
|
|
out := grp.AddSpread(spread)
|
|
if n := len(grp.Spreads); n != 1 {
|
|
t.Fatalf("expected 1 spread, got: %d", n)
|
|
}
|
|
|
|
// Check that the group was returned
|
|
if out != grp {
|
|
t.Fatalf("expected: %#v, got: %#v", grp, out)
|
|
}
|
|
|
|
// Add a second spread
|
|
spreadTarget2 := NewSpreadTarget("dc1", 100)
|
|
spread2 := NewSpread("${node.datacenter}", 100, []*SpreadTarget{spreadTarget2})
|
|
|
|
grp.AddSpread(spread2)
|
|
|
|
expect := []*Spread{
|
|
{
|
|
Attribute: "${meta.rack}",
|
|
Weight: intToPtr(100),
|
|
SpreadTarget: []*SpreadTarget{
|
|
{
|
|
Value: "r1",
|
|
Percent: 50,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Attribute: "${node.datacenter}",
|
|
Weight: intToPtr(100),
|
|
SpreadTarget: []*SpreadTarget{
|
|
{
|
|
Value: "dc1",
|
|
Percent: 100,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
if !reflect.DeepEqual(grp.Spreads, expect) {
|
|
t.Fatalf("expect: %#v, got: %#v", expect, grp.Spreads)
|
|
}
|
|
}
|
|
|
|
func TestTaskGroup_AddTask(t *testing.T) {
|
|
t.Parallel()
|
|
grp := NewTaskGroup("grp1", 1)
|
|
|
|
// Add the task to the task group
|
|
out := grp.AddTask(NewTask("task1", "java"))
|
|
if n := len(grp.Tasks); n != 1 {
|
|
t.Fatalf("expected 1 task, got: %d", n)
|
|
}
|
|
|
|
// Check that we returned the group
|
|
if out != grp {
|
|
t.Fatalf("expect: %#v, got: %#v", grp, out)
|
|
}
|
|
|
|
// Add a second task
|
|
grp.AddTask(NewTask("task2", "exec"))
|
|
expect := []*Task{
|
|
{
|
|
Name: "task1",
|
|
Driver: "java",
|
|
},
|
|
{
|
|
Name: "task2",
|
|
Driver: "exec",
|
|
},
|
|
}
|
|
if !reflect.DeepEqual(grp.Tasks, expect) {
|
|
t.Fatalf("expect: %#v, got: %#v", expect, grp.Tasks)
|
|
}
|
|
}
|
|
|
|
func TestTask_NewTask(t *testing.T) {
|
|
t.Parallel()
|
|
task := NewTask("task1", "exec")
|
|
expect := &Task{
|
|
Name: "task1",
|
|
Driver: "exec",
|
|
}
|
|
if !reflect.DeepEqual(task, expect) {
|
|
t.Fatalf("expect: %#v, got: %#v", expect, task)
|
|
}
|
|
}
|
|
|
|
func TestTask_SetConfig(t *testing.T) {
|
|
t.Parallel()
|
|
task := NewTask("task1", "exec")
|
|
|
|
// Initializes an empty map
|
|
out := task.SetConfig("foo", "bar")
|
|
if task.Config == nil {
|
|
t.Fatalf("should be initialized")
|
|
}
|
|
|
|
// Check that we returned the task
|
|
if out != task {
|
|
t.Fatalf("expect: %#v, got: %#v", task, out)
|
|
}
|
|
|
|
// Set another config value
|
|
task.SetConfig("baz", "zip")
|
|
expect := map[string]interface{}{"foo": "bar", "baz": "zip"}
|
|
if !reflect.DeepEqual(task.Config, expect) {
|
|
t.Fatalf("expect: %#v, got: %#v", expect, task.Config)
|
|
}
|
|
}
|
|
|
|
func TestTask_SetMeta(t *testing.T) {
|
|
t.Parallel()
|
|
task := NewTask("task1", "exec")
|
|
|
|
// Initializes an empty map
|
|
out := task.SetMeta("foo", "bar")
|
|
if task.Meta == nil {
|
|
t.Fatalf("should be initialized")
|
|
}
|
|
|
|
// Check that we returned the task
|
|
if out != task {
|
|
t.Fatalf("expect: %#v, got: %#v", task, out)
|
|
}
|
|
|
|
// Set another meta k/v
|
|
task.SetMeta("baz", "zip")
|
|
expect := map[string]string{"foo": "bar", "baz": "zip"}
|
|
if !reflect.DeepEqual(task.Meta, expect) {
|
|
t.Fatalf("expect: %#v, got: %#v", expect, task.Meta)
|
|
}
|
|
}
|
|
|
|
func TestTask_Require(t *testing.T) {
|
|
t.Parallel()
|
|
task := NewTask("task1", "exec")
|
|
|
|
// Create some require resources
|
|
resources := &Resources{
|
|
CPU: intToPtr(1250),
|
|
MemoryMB: intToPtr(128),
|
|
DiskMB: intToPtr(2048),
|
|
Networks: []*NetworkResource{
|
|
{
|
|
CIDR: "0.0.0.0/0",
|
|
MBits: intToPtr(100),
|
|
ReservedPorts: []Port{{"", 80}, {"", 443}},
|
|
},
|
|
},
|
|
}
|
|
out := task.Require(resources)
|
|
if !reflect.DeepEqual(task.Resources, resources) {
|
|
t.Fatalf("expect: %#v, got: %#v", resources, task.Resources)
|
|
}
|
|
|
|
// Check that we returned the task
|
|
if out != task {
|
|
t.Fatalf("expect: %#v, got: %#v", task, out)
|
|
}
|
|
}
|
|
|
|
func TestTask_Constrain(t *testing.T) {
|
|
t.Parallel()
|
|
task := NewTask("task1", "exec")
|
|
|
|
// Add a constraint to the task
|
|
out := task.Constrain(NewConstraint("kernel.name", "=", "darwin"))
|
|
if n := len(task.Constraints); n != 1 {
|
|
t.Fatalf("expected 1 constraint, got: %d", n)
|
|
}
|
|
|
|
// Check that the task was returned
|
|
if out != task {
|
|
t.Fatalf("expected: %#v, got: %#v", task, out)
|
|
}
|
|
|
|
// Add a second constraint
|
|
task.Constrain(NewConstraint("memory.totalbytes", ">=", "128000000"))
|
|
expect := []*Constraint{
|
|
{
|
|
LTarget: "kernel.name",
|
|
RTarget: "darwin",
|
|
Operand: "=",
|
|
},
|
|
{
|
|
LTarget: "memory.totalbytes",
|
|
RTarget: "128000000",
|
|
Operand: ">=",
|
|
},
|
|
}
|
|
if !reflect.DeepEqual(task.Constraints, expect) {
|
|
t.Fatalf("expect: %#v, got: %#v", expect, task.Constraints)
|
|
}
|
|
}
|
|
|
|
func TestTask_AddAffinity(t *testing.T) {
|
|
t.Parallel()
|
|
task := NewTask("task1", "exec")
|
|
|
|
// Add an affinity to the task
|
|
out := task.AddAffinity(NewAffinity("kernel.version", "=", "4.6", 100))
|
|
require := require.New(t)
|
|
require.Len(out.Affinities, 1)
|
|
|
|
// Check that the task was returned
|
|
if out != task {
|
|
t.Fatalf("expected: %#v, got: %#v", task, out)
|
|
}
|
|
|
|
// Add a second affinity
|
|
task.AddAffinity(NewAffinity("${node.datacenter}", "=", "dc2", 50))
|
|
expect := []*Affinity{
|
|
{
|
|
LTarget: "kernel.version",
|
|
RTarget: "4.6",
|
|
Operand: "=",
|
|
Weight: 100,
|
|
},
|
|
{
|
|
LTarget: "${node.datacenter}",
|
|
RTarget: "dc2",
|
|
Operand: "=",
|
|
Weight: 50,
|
|
},
|
|
}
|
|
if !reflect.DeepEqual(task.Affinities, expect) {
|
|
t.Fatalf("expect: %#v, got: %#v", expect, task.Affinities)
|
|
}
|
|
}
|
|
|
|
func TestTask_Artifact(t *testing.T) {
|
|
t.Parallel()
|
|
a := TaskArtifact{
|
|
GetterSource: stringToPtr("http://localhost/foo.txt"),
|
|
GetterMode: stringToPtr("file"),
|
|
}
|
|
a.Canonicalize()
|
|
if *a.GetterMode != "file" {
|
|
t.Errorf("expected file but found %q", *a.GetterMode)
|
|
}
|
|
if filepath.ToSlash(*a.RelativeDest) != "local/foo.txt" {
|
|
t.Errorf("expected local/foo.txt but found %q", *a.RelativeDest)
|
|
}
|
|
}
|
|
|
|
// Ensures no regression on https://github.com/hashicorp/nomad/issues/3132
|
|
func TestTaskGroup_Canonicalize_Update(t *testing.T) {
|
|
job := &Job{
|
|
ID: stringToPtr("test"),
|
|
Update: &UpdateStrategy{
|
|
AutoRevert: boolToPtr(false),
|
|
Canary: intToPtr(0),
|
|
HealthCheck: stringToPtr(""),
|
|
HealthyDeadline: timeToPtr(0),
|
|
ProgressDeadline: timeToPtr(0),
|
|
MaxParallel: intToPtr(0),
|
|
MinHealthyTime: timeToPtr(0),
|
|
Stagger: timeToPtr(0),
|
|
},
|
|
}
|
|
job.Canonicalize()
|
|
tg := &TaskGroup{
|
|
Name: stringToPtr("foo"),
|
|
}
|
|
tg.Canonicalize(job)
|
|
assert.Nil(t, tg.Update)
|
|
}
|
|
|
|
// Verifies that reschedule policy is merged correctly
|
|
func TestTaskGroup_Canonicalize_ReschedulePolicy(t *testing.T) {
|
|
type testCase struct {
|
|
desc string
|
|
jobReschedulePolicy *ReschedulePolicy
|
|
taskReschedulePolicy *ReschedulePolicy
|
|
expected *ReschedulePolicy
|
|
}
|
|
|
|
testCases := []testCase{
|
|
{
|
|
desc: "Default",
|
|
jobReschedulePolicy: nil,
|
|
taskReschedulePolicy: nil,
|
|
expected: &ReschedulePolicy{
|
|
Attempts: intToPtr(structs.DefaultBatchJobReschedulePolicy.Attempts),
|
|
Interval: timeToPtr(structs.DefaultBatchJobReschedulePolicy.Interval),
|
|
Delay: timeToPtr(structs.DefaultBatchJobReschedulePolicy.Delay),
|
|
DelayFunction: stringToPtr(structs.DefaultBatchJobReschedulePolicy.DelayFunction),
|
|
MaxDelay: timeToPtr(structs.DefaultBatchJobReschedulePolicy.MaxDelay),
|
|
Unlimited: boolToPtr(structs.DefaultBatchJobReschedulePolicy.Unlimited),
|
|
},
|
|
},
|
|
{
|
|
desc: "Empty job reschedule policy",
|
|
jobReschedulePolicy: &ReschedulePolicy{
|
|
Attempts: intToPtr(0),
|
|
Interval: timeToPtr(0),
|
|
Delay: timeToPtr(0),
|
|
MaxDelay: timeToPtr(0),
|
|
DelayFunction: stringToPtr(""),
|
|
Unlimited: boolToPtr(false),
|
|
},
|
|
taskReschedulePolicy: nil,
|
|
expected: &ReschedulePolicy{
|
|
Attempts: intToPtr(0),
|
|
Interval: timeToPtr(0),
|
|
Delay: timeToPtr(0),
|
|
MaxDelay: timeToPtr(0),
|
|
DelayFunction: stringToPtr(""),
|
|
Unlimited: boolToPtr(false),
|
|
},
|
|
},
|
|
{
|
|
desc: "Inherit from job",
|
|
jobReschedulePolicy: &ReschedulePolicy{
|
|
Attempts: intToPtr(1),
|
|
Interval: timeToPtr(20 * time.Second),
|
|
Delay: timeToPtr(20 * time.Second),
|
|
MaxDelay: timeToPtr(10 * time.Minute),
|
|
DelayFunction: stringToPtr("constant"),
|
|
Unlimited: boolToPtr(false),
|
|
},
|
|
taskReschedulePolicy: nil,
|
|
expected: &ReschedulePolicy{
|
|
Attempts: intToPtr(1),
|
|
Interval: timeToPtr(20 * time.Second),
|
|
Delay: timeToPtr(20 * time.Second),
|
|
MaxDelay: timeToPtr(10 * time.Minute),
|
|
DelayFunction: stringToPtr("constant"),
|
|
Unlimited: boolToPtr(false),
|
|
},
|
|
},
|
|
{
|
|
desc: "Set in task",
|
|
jobReschedulePolicy: nil,
|
|
taskReschedulePolicy: &ReschedulePolicy{
|
|
Attempts: intToPtr(5),
|
|
Interval: timeToPtr(2 * time.Minute),
|
|
Delay: timeToPtr(20 * time.Second),
|
|
MaxDelay: timeToPtr(10 * time.Minute),
|
|
DelayFunction: stringToPtr("constant"),
|
|
Unlimited: boolToPtr(false),
|
|
},
|
|
expected: &ReschedulePolicy{
|
|
Attempts: intToPtr(5),
|
|
Interval: timeToPtr(2 * time.Minute),
|
|
Delay: timeToPtr(20 * time.Second),
|
|
MaxDelay: timeToPtr(10 * time.Minute),
|
|
DelayFunction: stringToPtr("constant"),
|
|
Unlimited: boolToPtr(false),
|
|
},
|
|
},
|
|
{
|
|
desc: "Merge from job",
|
|
jobReschedulePolicy: &ReschedulePolicy{
|
|
Attempts: intToPtr(1),
|
|
Delay: timeToPtr(20 * time.Second),
|
|
MaxDelay: timeToPtr(10 * time.Minute),
|
|
},
|
|
taskReschedulePolicy: &ReschedulePolicy{
|
|
Interval: timeToPtr(5 * time.Minute),
|
|
DelayFunction: stringToPtr("constant"),
|
|
Unlimited: boolToPtr(false),
|
|
},
|
|
expected: &ReschedulePolicy{
|
|
Attempts: intToPtr(1),
|
|
Interval: timeToPtr(5 * time.Minute),
|
|
Delay: timeToPtr(20 * time.Second),
|
|
MaxDelay: timeToPtr(10 * time.Minute),
|
|
DelayFunction: stringToPtr("constant"),
|
|
Unlimited: boolToPtr(false),
|
|
},
|
|
},
|
|
{
|
|
desc: "Override from group",
|
|
jobReschedulePolicy: &ReschedulePolicy{
|
|
Attempts: intToPtr(1),
|
|
MaxDelay: timeToPtr(10 * time.Second),
|
|
},
|
|
taskReschedulePolicy: &ReschedulePolicy{
|
|
Attempts: intToPtr(5),
|
|
Delay: timeToPtr(20 * time.Second),
|
|
MaxDelay: timeToPtr(20 * time.Minute),
|
|
DelayFunction: stringToPtr("constant"),
|
|
Unlimited: boolToPtr(false),
|
|
},
|
|
expected: &ReschedulePolicy{
|
|
Attempts: intToPtr(5),
|
|
Interval: timeToPtr(structs.DefaultBatchJobReschedulePolicy.Interval),
|
|
Delay: timeToPtr(20 * time.Second),
|
|
MaxDelay: timeToPtr(20 * time.Minute),
|
|
DelayFunction: stringToPtr("constant"),
|
|
Unlimited: boolToPtr(false),
|
|
},
|
|
},
|
|
{
|
|
desc: "Attempts from job, default interval",
|
|
jobReschedulePolicy: &ReschedulePolicy{
|
|
Attempts: intToPtr(1),
|
|
},
|
|
taskReschedulePolicy: nil,
|
|
expected: &ReschedulePolicy{
|
|
Attempts: intToPtr(1),
|
|
Interval: timeToPtr(structs.DefaultBatchJobReschedulePolicy.Interval),
|
|
Delay: timeToPtr(structs.DefaultBatchJobReschedulePolicy.Delay),
|
|
DelayFunction: stringToPtr(structs.DefaultBatchJobReschedulePolicy.DelayFunction),
|
|
MaxDelay: timeToPtr(structs.DefaultBatchJobReschedulePolicy.MaxDelay),
|
|
Unlimited: boolToPtr(structs.DefaultBatchJobReschedulePolicy.Unlimited),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
job := &Job{
|
|
ID: stringToPtr("test"),
|
|
Reschedule: tc.jobReschedulePolicy,
|
|
Type: stringToPtr(JobTypeBatch),
|
|
}
|
|
job.Canonicalize()
|
|
tg := &TaskGroup{
|
|
Name: stringToPtr("foo"),
|
|
ReschedulePolicy: tc.taskReschedulePolicy,
|
|
}
|
|
tg.Canonicalize(job)
|
|
assert.Equal(t, tc.expected, tg.ReschedulePolicy)
|
|
})
|
|
}
|
|
}
|
|
|
|
// Verifies that migrate strategy is merged correctly
|
|
func TestTaskGroup_Canonicalize_MigrateStrategy(t *testing.T) {
|
|
type testCase struct {
|
|
desc string
|
|
jobType string
|
|
jobMigrate *MigrateStrategy
|
|
taskMigrate *MigrateStrategy
|
|
expected *MigrateStrategy
|
|
}
|
|
|
|
testCases := []testCase{
|
|
{
|
|
desc: "Default batch",
|
|
jobType: "batch",
|
|
jobMigrate: nil,
|
|
taskMigrate: nil,
|
|
expected: nil,
|
|
},
|
|
{
|
|
desc: "Default service",
|
|
jobType: "service",
|
|
jobMigrate: nil,
|
|
taskMigrate: nil,
|
|
expected: &MigrateStrategy{
|
|
MaxParallel: intToPtr(1),
|
|
HealthCheck: stringToPtr("checks"),
|
|
MinHealthyTime: timeToPtr(10 * time.Second),
|
|
HealthyDeadline: timeToPtr(5 * time.Minute),
|
|
},
|
|
},
|
|
{
|
|
desc: "Empty job migrate strategy",
|
|
jobType: "service",
|
|
jobMigrate: &MigrateStrategy{
|
|
MaxParallel: intToPtr(0),
|
|
HealthCheck: stringToPtr(""),
|
|
MinHealthyTime: timeToPtr(0),
|
|
HealthyDeadline: timeToPtr(0),
|
|
},
|
|
taskMigrate: nil,
|
|
expected: &MigrateStrategy{
|
|
MaxParallel: intToPtr(0),
|
|
HealthCheck: stringToPtr(""),
|
|
MinHealthyTime: timeToPtr(0),
|
|
HealthyDeadline: timeToPtr(0),
|
|
},
|
|
},
|
|
{
|
|
desc: "Inherit from job",
|
|
jobType: "service",
|
|
jobMigrate: &MigrateStrategy{
|
|
MaxParallel: intToPtr(3),
|
|
HealthCheck: stringToPtr("checks"),
|
|
MinHealthyTime: timeToPtr(2),
|
|
HealthyDeadline: timeToPtr(2),
|
|
},
|
|
taskMigrate: nil,
|
|
expected: &MigrateStrategy{
|
|
MaxParallel: intToPtr(3),
|
|
HealthCheck: stringToPtr("checks"),
|
|
MinHealthyTime: timeToPtr(2),
|
|
HealthyDeadline: timeToPtr(2),
|
|
},
|
|
},
|
|
{
|
|
desc: "Set in task",
|
|
jobType: "service",
|
|
jobMigrate: nil,
|
|
taskMigrate: &MigrateStrategy{
|
|
MaxParallel: intToPtr(3),
|
|
HealthCheck: stringToPtr("checks"),
|
|
MinHealthyTime: timeToPtr(2),
|
|
HealthyDeadline: timeToPtr(2),
|
|
},
|
|
expected: &MigrateStrategy{
|
|
MaxParallel: intToPtr(3),
|
|
HealthCheck: stringToPtr("checks"),
|
|
MinHealthyTime: timeToPtr(2),
|
|
HealthyDeadline: timeToPtr(2),
|
|
},
|
|
},
|
|
{
|
|
desc: "Merge from job",
|
|
jobType: "service",
|
|
jobMigrate: &MigrateStrategy{
|
|
MaxParallel: intToPtr(11),
|
|
},
|
|
taskMigrate: &MigrateStrategy{
|
|
HealthCheck: stringToPtr("checks"),
|
|
MinHealthyTime: timeToPtr(2),
|
|
HealthyDeadline: timeToPtr(2),
|
|
},
|
|
expected: &MigrateStrategy{
|
|
MaxParallel: intToPtr(11),
|
|
HealthCheck: stringToPtr("checks"),
|
|
MinHealthyTime: timeToPtr(2),
|
|
HealthyDeadline: timeToPtr(2),
|
|
},
|
|
},
|
|
{
|
|
desc: "Override from group",
|
|
jobType: "service",
|
|
jobMigrate: &MigrateStrategy{
|
|
MaxParallel: intToPtr(11),
|
|
},
|
|
taskMigrate: &MigrateStrategy{
|
|
MaxParallel: intToPtr(5),
|
|
HealthCheck: stringToPtr("checks"),
|
|
MinHealthyTime: timeToPtr(2),
|
|
HealthyDeadline: timeToPtr(2),
|
|
},
|
|
expected: &MigrateStrategy{
|
|
MaxParallel: intToPtr(5),
|
|
HealthCheck: stringToPtr("checks"),
|
|
MinHealthyTime: timeToPtr(2),
|
|
HealthyDeadline: timeToPtr(2),
|
|
},
|
|
},
|
|
{
|
|
desc: "Parallel from job, defaulting",
|
|
jobType: "service",
|
|
jobMigrate: &MigrateStrategy{
|
|
MaxParallel: intToPtr(5),
|
|
},
|
|
taskMigrate: nil,
|
|
expected: &MigrateStrategy{
|
|
MaxParallel: intToPtr(5),
|
|
HealthCheck: stringToPtr("checks"),
|
|
MinHealthyTime: timeToPtr(10 * time.Second),
|
|
HealthyDeadline: timeToPtr(5 * time.Minute),
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
job := &Job{
|
|
ID: stringToPtr("test"),
|
|
Migrate: tc.jobMigrate,
|
|
Type: stringToPtr(tc.jobType),
|
|
}
|
|
job.Canonicalize()
|
|
tg := &TaskGroup{
|
|
Name: stringToPtr("foo"),
|
|
Migrate: tc.taskMigrate,
|
|
}
|
|
tg.Canonicalize(job)
|
|
assert.Equal(t, tc.expected, tg.Migrate)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestService_CheckRestart asserts Service.CheckRestart settings are properly
|
|
// inherited by Checks.
|
|
func TestService_CheckRestart(t *testing.T) {
|
|
job := &Job{Name: stringToPtr("job")}
|
|
tg := &TaskGroup{Name: stringToPtr("group")}
|
|
task := &Task{Name: "task"}
|
|
service := &Service{
|
|
CheckRestart: &CheckRestart{
|
|
Limit: 11,
|
|
Grace: timeToPtr(11 * time.Second),
|
|
IgnoreWarnings: true,
|
|
},
|
|
Checks: []ServiceCheck{
|
|
{
|
|
Name: "all-set",
|
|
CheckRestart: &CheckRestart{
|
|
Limit: 22,
|
|
Grace: timeToPtr(22 * time.Second),
|
|
IgnoreWarnings: true,
|
|
},
|
|
},
|
|
{
|
|
Name: "some-set",
|
|
CheckRestart: &CheckRestart{
|
|
Limit: 33,
|
|
Grace: timeToPtr(33 * time.Second),
|
|
},
|
|
},
|
|
{
|
|
Name: "unset",
|
|
},
|
|
},
|
|
}
|
|
|
|
service.Canonicalize(task, tg, job)
|
|
assert.Equal(t, service.Checks[0].CheckRestart.Limit, 22)
|
|
assert.Equal(t, *service.Checks[0].CheckRestart.Grace, 22*time.Second)
|
|
assert.True(t, service.Checks[0].CheckRestart.IgnoreWarnings)
|
|
|
|
assert.Equal(t, service.Checks[1].CheckRestart.Limit, 33)
|
|
assert.Equal(t, *service.Checks[1].CheckRestart.Grace, 33*time.Second)
|
|
assert.True(t, service.Checks[1].CheckRestart.IgnoreWarnings)
|
|
|
|
assert.Equal(t, service.Checks[2].CheckRestart.Limit, 11)
|
|
assert.Equal(t, *service.Checks[2].CheckRestart.Grace, 11*time.Second)
|
|
assert.True(t, service.Checks[2].CheckRestart.IgnoreWarnings)
|
|
}
|
|
|
|
// TestSpread_Canonicalize asserts that the spread stanza is canonicalized correctly
|
|
func TestSpread_Canonicalize(t *testing.T) {
|
|
job := &Job{
|
|
ID: stringToPtr("test"),
|
|
Type: stringToPtr("batch"),
|
|
}
|
|
job.Canonicalize()
|
|
tg := &TaskGroup{
|
|
Name: stringToPtr("foo"),
|
|
}
|
|
type testCase struct {
|
|
desc string
|
|
spread *Spread
|
|
expectedWeight int
|
|
}
|
|
cases := []testCase{
|
|
{
|
|
"Nil spread",
|
|
&Spread{
|
|
Attribute: "test",
|
|
Weight: nil,
|
|
},
|
|
50,
|
|
},
|
|
{
|
|
"Zero spread",
|
|
&Spread{
|
|
Attribute: "test",
|
|
Weight: intToPtr(0),
|
|
},
|
|
0,
|
|
},
|
|
{
|
|
"Non Zero spread",
|
|
&Spread{
|
|
Attribute: "test",
|
|
Weight: intToPtr(100),
|
|
},
|
|
100,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
require := require.New(t)
|
|
tg.Spreads = []*Spread{tc.spread}
|
|
tg.Canonicalize(job)
|
|
for _, spr := range tg.Spreads {
|
|
require.Equal(tc.expectedWeight, *spr.Weight)
|
|
}
|
|
})
|
|
}
|
|
}
|