open-nomad/scheduler/util_test.go

393 lines
8.5 KiB
Go
Raw Normal View History

2015-08-13 23:25:59 +00:00
package scheduler
import (
"fmt"
2015-08-14 00:19:09 +00:00
"os"
2015-09-07 18:23:38 +00:00
"reflect"
2015-08-13 23:25:59 +00:00
"testing"
"github.com/hashicorp/nomad/nomad/mock"
2015-08-14 00:19:09 +00:00
"github.com/hashicorp/nomad/nomad/state"
2015-08-13 23:25:59 +00:00
"github.com/hashicorp/nomad/nomad/structs"
)
func TestMaterializeTaskGroups(t *testing.T) {
job := mock.Job()
index := materializeTaskGroups(job)
if len(index) != 10 {
t.Fatalf("Bad: %#v", index)
}
for i := 0; i < 10; i++ {
name := fmt.Sprintf("my-job.web[%d]", i)
tg, ok := index[name]
if !ok {
t.Fatalf("bad")
}
if tg != job.TaskGroups[0] {
t.Fatalf("bad")
}
}
}
func TestDiffAllocs(t *testing.T) {
job := mock.Job()
required := materializeTaskGroups(job)
// The "old" job has a previous modify index
oldJob := new(structs.Job)
*oldJob = *job
oldJob.ModifyIndex -= 1
tainted := map[string]bool{
"dead": true,
"zip": false,
}
2015-08-13 23:25:59 +00:00
allocs := []*structs.Allocation{
// Update the 1st
&structs.Allocation{
ID: structs.GenerateUUID(),
2015-08-13 23:25:59 +00:00
NodeID: "zip",
Name: "my-job.web[0]",
Job: oldJob,
},
// Ignore the 2rd
&structs.Allocation{
ID: structs.GenerateUUID(),
2015-08-13 23:25:59 +00:00
NodeID: "zip",
Name: "my-job.web[1]",
Job: job,
},
// Evict 11th
&structs.Allocation{
ID: structs.GenerateUUID(),
2015-08-13 23:25:59 +00:00
NodeID: "zip",
Name: "my-job.web[10]",
},
// Migrate the 3rd
&structs.Allocation{
ID: structs.GenerateUUID(),
NodeID: "dead",
Name: "my-job.web[2]",
},
2015-08-13 23:25:59 +00:00
}
2015-08-14 01:28:09 +00:00
diff := diffAllocs(job, tainted, required, allocs)
place := diff.place
update := diff.update
migrate := diff.migrate
2015-08-26 00:06:06 +00:00
stop := diff.stop
2015-08-14 01:28:09 +00:00
ignore := diff.ignore
2015-08-13 23:25:59 +00:00
// We should update the first alloc
2015-08-14 01:16:32 +00:00
if len(update) != 1 || update[0].Alloc != allocs[0] {
2015-08-13 23:25:59 +00:00
t.Fatalf("bad: %#v", update)
}
// We should ignore the second alloc
2015-08-14 01:16:32 +00:00
if len(ignore) != 1 || ignore[0].Alloc != allocs[1] {
2015-08-13 23:25:59 +00:00
t.Fatalf("bad: %#v", ignore)
}
2015-08-26 00:06:06 +00:00
// We should stop the 3rd alloc
if len(stop) != 1 || stop[0].Alloc != allocs[2] {
t.Fatalf("bad: %#v", stop)
2015-08-13 23:25:59 +00:00
}
// We should migrate the 4rd alloc
2015-08-14 01:16:32 +00:00
if len(migrate) != 1 || migrate[0].Alloc != allocs[3] {
t.Fatalf("bad: %#v", migrate)
}
// We should place 7
if len(place) != 7 {
2015-08-13 23:25:59 +00:00
t.Fatalf("bad: %#v", place)
}
}
func TestDiffSystemAllocs(t *testing.T) {
job := mock.SystemJob()
// Create three alive nodes.
nodes := []*structs.Node{{ID: "foo"}, {ID: "bar"}, {ID: "baz"}}
// The "old" job has a previous modify index
oldJob := new(structs.Job)
*oldJob = *job
oldJob.ModifyIndex -= 1
tainted := map[string]bool{
"dead": true,
"baz": false,
}
allocs := []*structs.Allocation{
// Update allocation on baz
&structs.Allocation{
ID: structs.GenerateUUID(),
NodeID: "baz",
Name: "my-job.web[0]",
Job: oldJob,
},
// Ignore allocation on bar
&structs.Allocation{
ID: structs.GenerateUUID(),
NodeID: "bar",
Name: "my-job.web[0]",
Job: job,
},
// Stop allocation on dead.
&structs.Allocation{
ID: structs.GenerateUUID(),
NodeID: "dead",
Name: "my-job.web[0]",
},
}
diff := diffSystemAllocs(job, nodes, tainted, allocs)
place := diff.place
update := diff.update
migrate := diff.migrate
stop := diff.stop
ignore := diff.ignore
// We should update the first alloc
if len(update) != 1 || update[0].Alloc != allocs[0] {
t.Fatalf("bad: %#v", update)
}
// We should ignore the second alloc
if len(ignore) != 1 || ignore[0].Alloc != allocs[1] {
t.Fatalf("bad: %#v", ignore)
}
// We should stop the third alloc
if len(stop) != 1 || stop[0].Alloc != allocs[2] {
t.Fatalf("bad: %#v", stop)
}
// There should be no migrates.
if len(migrate) != 0 {
t.Fatalf("bad: %#v", migrate)
}
// We should place 1
if len(place) != 1 {
t.Fatalf("bad: %#v", place)
}
}
2015-08-14 00:19:09 +00:00
func TestReadyNodesInDCs(t *testing.T) {
state, err := state.NewStateStore(os.Stderr)
if err != nil {
t.Fatalf("err: %v", err)
}
node1 := mock.Node()
node2 := mock.Node()
node2.Datacenter = "dc2"
node3 := mock.Node()
node3.Datacenter = "dc2"
node3.Status = structs.NodeStatusDown
2015-09-07 02:47:02 +00:00
node4 := mock.Node()
node4.Drain = true
2015-08-14 00:19:09 +00:00
2015-09-07 03:47:42 +00:00
noErr(t, state.UpsertNode(1000, node1))
noErr(t, state.UpsertNode(1001, node2))
noErr(t, state.UpsertNode(1002, node3))
noErr(t, state.UpsertNode(1003, node4))
2015-08-14 00:19:09 +00:00
nodes, err := readyNodesInDCs(state, []string{"dc1", "dc2"})
if err != nil {
t.Fatalf("err: %v", err)
}
if len(nodes) != 2 {
2015-08-15 23:10:10 +00:00
t.Fatalf("bad: %v", nodes)
2015-08-14 00:19:09 +00:00
}
if nodes[0].ID == node3.ID || nodes[1].ID == node3.ID {
t.Fatalf("Bad: %#v", nodes)
}
}
2015-08-14 00:40:23 +00:00
func TestRetryMax(t *testing.T) {
calls := 0
bad := func() (bool, error) {
calls += 1
return false, nil
}
err := retryMax(3, bad)
if err == nil {
t.Fatalf("should fail")
}
if calls != 3 {
t.Fatalf("mis match")
}
calls = 0
good := func() (bool, error) {
calls += 1
return true, nil
}
err = retryMax(3, good)
if err != nil {
2015-08-15 23:10:10 +00:00
t.Fatalf("err: %v", err)
2015-08-14 00:40:23 +00:00
}
if calls != 1 {
t.Fatalf("mis match")
}
}
2015-08-14 00:51:31 +00:00
func TestTaintedNodes(t *testing.T) {
state, err := state.NewStateStore(os.Stderr)
if err != nil {
t.Fatalf("err: %v", err)
}
node1 := mock.Node()
node2 := mock.Node()
node2.Datacenter = "dc2"
node3 := mock.Node()
node3.Datacenter = "dc2"
node3.Status = structs.NodeStatusDown
2015-09-07 02:47:02 +00:00
node4 := mock.Node()
node4.Drain = true
2015-09-07 03:47:42 +00:00
noErr(t, state.UpsertNode(1000, node1))
noErr(t, state.UpsertNode(1001, node2))
noErr(t, state.UpsertNode(1002, node3))
noErr(t, state.UpsertNode(1003, node4))
2015-08-14 00:51:31 +00:00
allocs := []*structs.Allocation{
&structs.Allocation{NodeID: node1.ID},
&structs.Allocation{NodeID: node2.ID},
&structs.Allocation{NodeID: node3.ID},
2015-09-07 02:47:02 +00:00
&structs.Allocation{NodeID: node4.ID},
2015-08-14 01:05:31 +00:00
&structs.Allocation{NodeID: "blah"},
2015-08-14 00:51:31 +00:00
}
tainted, err := taintedNodes(state, allocs)
if err != nil {
t.Fatalf("err: %v", err)
}
2015-09-07 02:47:02 +00:00
if len(tainted) != 5 {
2015-08-15 23:10:10 +00:00
t.Fatalf("bad: %v", tainted)
2015-08-14 00:51:31 +00:00
}
if tainted[node1.ID] || tainted[node2.ID] {
t.Fatalf("Bad: %v", tainted)
}
2015-09-07 02:47:02 +00:00
if !tainted[node3.ID] || !tainted[node4.ID] || !tainted["blah"] {
2015-08-14 00:51:31 +00:00
t.Fatalf("Bad: %v", tainted)
}
}
2015-09-07 18:23:38 +00:00
func TestShuffleNodes(t *testing.T) {
nodes := []*structs.Node{
mock.Node(),
mock.Node(),
mock.Node(),
mock.Node(),
mock.Node(),
}
orig := make([]*structs.Node, len(nodes))
copy(orig, nodes)
shuffleNodes(nodes)
if reflect.DeepEqual(nodes, orig) {
t.Fatalf("shoudl not match")
}
}
func TestTasksUpdated(t *testing.T) {
j1 := mock.Job()
j2 := mock.Job()
if tasksUpdated(j1.TaskGroups[0], j2.TaskGroups[0]) {
t.Fatalf("bad")
}
j2.TaskGroups[0].Tasks[0].Config["command"] = "/bin/other"
if !tasksUpdated(j1.TaskGroups[0], j2.TaskGroups[0]) {
t.Fatalf("bad")
}
j3 := mock.Job()
j3.TaskGroups[0].Tasks[0].Name = "foo"
if !tasksUpdated(j1.TaskGroups[0], j3.TaskGroups[0]) {
t.Fatalf("bad")
}
j4 := mock.Job()
j4.TaskGroups[0].Tasks[0].Driver = "foo"
if !tasksUpdated(j1.TaskGroups[0], j4.TaskGroups[0]) {
t.Fatalf("bad")
}
j5 := mock.Job()
j5.TaskGroups[0].Tasks = append(j5.TaskGroups[0].Tasks,
j5.TaskGroups[0].Tasks[0])
if !tasksUpdated(j1.TaskGroups[0], j5.TaskGroups[0]) {
t.Fatalf("bad")
}
j6 := mock.Job()
2015-09-22 20:33:16 +00:00
j6.TaskGroups[0].Tasks[0].Resources.Networks[0].DynamicPorts = []string{"http", "https", "admin"}
if !tasksUpdated(j1.TaskGroups[0], j6.TaskGroups[0]) {
t.Fatalf("bad")
}
}
func TestTaskGroupConstraints(t *testing.T) {
constr := &structs.Constraint{Hard: true}
constr2 := &structs.Constraint{LTarget: "foo"}
constr3 := &structs.Constraint{Weight: 10}
tg := &structs.TaskGroup{
Name: "web",
Count: 10,
Constraints: []*structs.Constraint{constr},
Tasks: []*structs.Task{
&structs.Task{
Driver: "exec",
Resources: &structs.Resources{
CPU: 500,
MemoryMB: 256,
},
Constraints: []*structs.Constraint{constr2},
},
&structs.Task{
Driver: "docker",
Resources: &structs.Resources{
CPU: 500,
MemoryMB: 256,
},
Constraints: []*structs.Constraint{constr3},
},
},
}
// Build the expected values.
expConstr := []*structs.Constraint{constr, constr2, constr3}
expDrivers := map[string]struct{}{"exec": struct{}{}, "docker": struct{}{}}
expSize := &structs.Resources{
CPU: 1000,
MemoryMB: 512,
}
actConstrains := taskGroupConstraints(tg)
if !reflect.DeepEqual(actConstrains.constraints, expConstr) {
t.Fatalf("taskGroupConstraints(%v) returned %v; want %v", tg, actConstrains.constraints, expConstr)
}
if !reflect.DeepEqual(actConstrains.drivers, expDrivers) {
t.Fatalf("taskGroupConstraints(%v) returned %v; want %v", tg, actConstrains.drivers, expDrivers)
}
if !reflect.DeepEqual(actConstrains.size, expSize) {
t.Fatalf("taskGroupConstraints(%v) returned %v; want %v", tg, actConstrains.size, expSize)
}
}