2015-08-13 23:25:59 +00:00
|
|
|
package scheduler
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2015-08-14 00:19:09 +00:00
|
|
|
"os"
|
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 TestIndexAllocs(t *testing.T) {
|
|
|
|
allocs := []*structs.Allocation{
|
|
|
|
&structs.Allocation{Name: "foo"},
|
|
|
|
&structs.Allocation{Name: "foo"},
|
|
|
|
&structs.Allocation{Name: "bar"},
|
|
|
|
}
|
|
|
|
index := indexAllocs(allocs)
|
|
|
|
if len(index) != 2 {
|
|
|
|
t.Fatalf("bad: %#v", index)
|
|
|
|
}
|
|
|
|
if len(index["foo"]) != 2 {
|
|
|
|
t.Fatalf("bad: %#v", index)
|
|
|
|
}
|
|
|
|
if len(index["bar"]) != 1 {
|
|
|
|
t.Fatalf("bad: %#v", index)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2015-08-13 23:47:39 +00:00
|
|
|
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: mock.GenerateUUID(),
|
|
|
|
NodeID: "zip",
|
|
|
|
Name: "my-job.web[0]",
|
|
|
|
Job: oldJob,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Ignore the 2rd
|
|
|
|
&structs.Allocation{
|
|
|
|
ID: mock.GenerateUUID(),
|
|
|
|
NodeID: "zip",
|
|
|
|
Name: "my-job.web[1]",
|
|
|
|
Job: job,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Evict 11th
|
|
|
|
&structs.Allocation{
|
|
|
|
ID: mock.GenerateUUID(),
|
|
|
|
NodeID: "zip",
|
|
|
|
Name: "my-job.web[10]",
|
|
|
|
},
|
2015-08-13 23:47:39 +00:00
|
|
|
|
|
|
|
// Migrate the 3rd
|
|
|
|
&structs.Allocation{
|
|
|
|
ID: mock.GenerateUUID(),
|
|
|
|
NodeID: "dead",
|
|
|
|
Name: "my-job.web[2]",
|
|
|
|
},
|
2015-08-13 23:25:59 +00:00
|
|
|
}
|
|
|
|
existing := indexAllocs(allocs)
|
|
|
|
|
2015-08-13 23:47:39 +00:00
|
|
|
place, update, migrate, evict, ignore := diffAllocs(job, tainted, required, existing)
|
2015-08-13 23:25:59 +00:00
|
|
|
|
|
|
|
// We should update the first alloc
|
|
|
|
if len(update) != 1 || update[0].ID != allocs[0].ID {
|
|
|
|
t.Fatalf("bad: %#v", update)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We should ignore the second alloc
|
|
|
|
if len(ignore) != 1 || ignore[0].ID != allocs[1].ID {
|
|
|
|
t.Fatalf("bad: %#v", ignore)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We should evict the 3rd alloc
|
|
|
|
if len(evict) != 1 || evict[0].ID != allocs[2].ID {
|
|
|
|
t.Fatalf("bad: %#v", evict)
|
|
|
|
}
|
|
|
|
|
2015-08-13 23:47:39 +00:00
|
|
|
// We should migrate the 4rd alloc
|
|
|
|
if len(migrate) != 1 || migrate[0].ID != allocs[3].ID {
|
|
|
|
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 TestAddEvictsToPlan(t *testing.T) {
|
|
|
|
allocs := []*structs.Allocation{
|
|
|
|
&structs.Allocation{
|
|
|
|
ID: mock.GenerateUUID(),
|
|
|
|
NodeID: "zip",
|
|
|
|
Name: "foo",
|
|
|
|
},
|
|
|
|
&structs.Allocation{
|
|
|
|
ID: mock.GenerateUUID(),
|
|
|
|
NodeID: "zip",
|
|
|
|
Name: "foo",
|
|
|
|
},
|
|
|
|
&structs.Allocation{
|
|
|
|
ID: mock.GenerateUUID(),
|
|
|
|
NodeID: "zip",
|
|
|
|
Name: "bar",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
plan := &structs.Plan{
|
|
|
|
NodeEvict: make(map[string][]string),
|
|
|
|
}
|
|
|
|
index := indexAllocs(allocs)
|
|
|
|
|
|
|
|
evict := []allocNameID{
|
|
|
|
allocNameID{Name: "foo", ID: allocs[0].ID},
|
|
|
|
allocNameID{Name: "bar", ID: allocs[2].ID},
|
|
|
|
}
|
|
|
|
addEvictsToPlan(plan, evict, index)
|
|
|
|
|
|
|
|
nodeEvict := plan.NodeEvict["zip"]
|
|
|
|
if len(nodeEvict) != 2 {
|
|
|
|
t.Fatalf("bad: %#v %v", plan, nodeEvict)
|
|
|
|
}
|
|
|
|
if nodeEvict[0] != allocs[0].ID || nodeEvict[1] != allocs[2].ID {
|
|
|
|
t.Fatalf("bad: %v", nodeEvict)
|
|
|
|
}
|
|
|
|
}
|
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
|
|
|
|
|
|
|
|
noErr(t, state.RegisterNode(1000, node1))
|
|
|
|
noErr(t, state.RegisterNode(1001, node2))
|
|
|
|
noErr(t, state.RegisterNode(1002, node3))
|
|
|
|
|
|
|
|
nodes, err := readyNodesInDCs(state, []string{"dc1", "dc2"})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(nodes) != 2 {
|
|
|
|
t.Fatalf("err: %v")
|
|
|
|
}
|
|
|
|
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 {
|
|
|
|
t.Fatalf("err: %v")
|
|
|
|
}
|
|
|
|
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
|
|
|
|
noErr(t, state.RegisterNode(1000, node1))
|
|
|
|
noErr(t, state.RegisterNode(1001, node2))
|
|
|
|
noErr(t, state.RegisterNode(1002, node3))
|
|
|
|
|
|
|
|
allocs := []*structs.Allocation{
|
|
|
|
&structs.Allocation{NodeID: node1.ID},
|
|
|
|
&structs.Allocation{NodeID: node2.ID},
|
|
|
|
&structs.Allocation{NodeID: node3.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-08-14 01:05:31 +00:00
|
|
|
if len(tainted) != 4 {
|
2015-08-14 00:51:31 +00:00
|
|
|
t.Fatalf("err: %v")
|
|
|
|
}
|
|
|
|
if tainted[node1.ID] || tainted[node2.ID] {
|
|
|
|
t.Fatalf("Bad: %v", tainted)
|
|
|
|
}
|
2015-08-14 01:05:31 +00:00
|
|
|
if !tainted[node3.ID] || !tainted["blah"] {
|
2015-08-14 00:51:31 +00:00
|
|
|
t.Fatalf("Bad: %v", tainted)
|
|
|
|
}
|
|
|
|
}
|