2015-08-13 17:13:11 +00:00
|
|
|
package scheduler
|
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/hashicorp/nomad/nomad/mock"
|
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestFeasibleRankIterator(t *testing.T) {
|
2015-08-13 18:33:58 +00:00
|
|
|
_, ctx := testContext(t)
|
2015-08-13 17:13:11 +00:00
|
|
|
var nodes []*structs.Node
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
nodes = append(nodes, mock.Node())
|
|
|
|
}
|
|
|
|
static := NewStaticIterator(ctx, nodes)
|
|
|
|
|
|
|
|
feasible := NewFeasibleRankIterator(ctx, static)
|
|
|
|
|
2015-08-13 19:02:42 +00:00
|
|
|
out := collectRanked(feasible)
|
2015-08-13 17:13:11 +00:00
|
|
|
if len(out) != len(nodes) {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
}
|
2015-08-13 18:28:02 +00:00
|
|
|
|
2015-08-13 20:08:15 +00:00
|
|
|
func TestBinPackIterator_NoExistingAlloc(t *testing.T) {
|
2015-08-13 19:02:42 +00:00
|
|
|
_, ctx := testContext(t)
|
|
|
|
nodes := []*RankedNode{
|
|
|
|
&RankedNode{
|
2015-08-13 19:08:53 +00:00
|
|
|
Node: &structs.Node{
|
|
|
|
// Perfect fit
|
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 2048,
|
|
|
|
MemoryMB: 2048,
|
|
|
|
},
|
|
|
|
Reserved: &structs.Resources{
|
|
|
|
CPU: 1024,
|
|
|
|
MemoryMB: 1024,
|
|
|
|
},
|
|
|
|
},
|
2015-08-13 19:02:42 +00:00
|
|
|
},
|
|
|
|
&RankedNode{
|
2015-08-13 19:08:53 +00:00
|
|
|
Node: &structs.Node{
|
|
|
|
// Overloaded
|
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 1024,
|
|
|
|
MemoryMB: 1024,
|
|
|
|
},
|
|
|
|
Reserved: &structs.Resources{
|
|
|
|
CPU: 512,
|
|
|
|
MemoryMB: 512,
|
|
|
|
},
|
|
|
|
},
|
2015-08-13 19:02:42 +00:00
|
|
|
},
|
|
|
|
&RankedNode{
|
2015-08-13 19:08:53 +00:00
|
|
|
Node: &structs.Node{
|
|
|
|
// 50% fit
|
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 4096,
|
|
|
|
MemoryMB: 4096,
|
|
|
|
},
|
|
|
|
Reserved: &structs.Resources{
|
|
|
|
CPU: 1024,
|
|
|
|
MemoryMB: 1024,
|
|
|
|
},
|
|
|
|
},
|
2015-08-13 19:02:42 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
static := NewStaticRankIterator(ctx, nodes)
|
|
|
|
|
2016-08-25 17:27:19 +00:00
|
|
|
taskGroup := &structs.TaskGroup{
|
|
|
|
LocalDisk: &structs.LocalDisk{},
|
|
|
|
Tasks: []*structs.Task{
|
|
|
|
{
|
|
|
|
Name: "web",
|
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 1024,
|
|
|
|
MemoryMB: 1024,
|
|
|
|
},
|
|
|
|
},
|
2015-09-13 21:31:32 +00:00
|
|
|
},
|
2015-08-13 19:08:53 +00:00
|
|
|
}
|
2015-09-13 21:31:32 +00:00
|
|
|
binp := NewBinPackIterator(ctx, static, false, 0)
|
2016-08-25 17:27:19 +00:00
|
|
|
binp.SetTaskGroup(taskGroup)
|
2015-08-13 19:02:42 +00:00
|
|
|
|
|
|
|
out := collectRanked(binp)
|
2015-08-13 19:08:53 +00:00
|
|
|
if len(out) != 2 {
|
|
|
|
t.Fatalf("Bad: %v", out)
|
|
|
|
}
|
|
|
|
if out[0] != nodes[0] || out[1] != nodes[2] {
|
2015-08-13 19:02:42 +00:00
|
|
|
t.Fatalf("Bad: %v", out)
|
|
|
|
}
|
2015-08-13 19:08:53 +00:00
|
|
|
|
|
|
|
if out[0].Score != 18 {
|
|
|
|
t.Fatalf("Bad: %v", out[0])
|
|
|
|
}
|
|
|
|
if out[1].Score < 10 || out[1].Score > 16 {
|
|
|
|
t.Fatalf("Bad: %v", out[1])
|
|
|
|
}
|
2015-08-13 19:02:42 +00:00
|
|
|
}
|
|
|
|
|
2015-08-13 20:08:15 +00:00
|
|
|
func TestBinPackIterator_PlannedAlloc(t *testing.T) {
|
|
|
|
_, ctx := testContext(t)
|
|
|
|
nodes := []*RankedNode{
|
|
|
|
&RankedNode{
|
|
|
|
Node: &structs.Node{
|
|
|
|
// Perfect fit
|
2015-09-07 22:23:03 +00:00
|
|
|
ID: structs.GenerateUUID(),
|
2015-08-13 20:08:15 +00:00
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 2048,
|
|
|
|
MemoryMB: 2048,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&RankedNode{
|
|
|
|
Node: &structs.Node{
|
|
|
|
// Perfect fit
|
2015-09-07 22:23:03 +00:00
|
|
|
ID: structs.GenerateUUID(),
|
2015-08-13 20:08:15 +00:00
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 2048,
|
|
|
|
MemoryMB: 2048,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
static := NewStaticRankIterator(ctx, nodes)
|
|
|
|
|
|
|
|
// Add a planned alloc to node1 that fills it
|
|
|
|
plan := ctx.Plan()
|
|
|
|
plan.NodeAllocation[nodes[0].Node.ID] = []*structs.Allocation{
|
|
|
|
&structs.Allocation{
|
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 2048,
|
|
|
|
MemoryMB: 2048,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add a planned alloc to node2 that half fills it
|
|
|
|
plan.NodeAllocation[nodes[1].Node.ID] = []*structs.Allocation{
|
|
|
|
&structs.Allocation{
|
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 1024,
|
|
|
|
MemoryMB: 1024,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2016-08-25 17:27:19 +00:00
|
|
|
taskGroup := &structs.TaskGroup{
|
|
|
|
LocalDisk: &structs.LocalDisk{},
|
|
|
|
Tasks: []*structs.Task{
|
|
|
|
{
|
|
|
|
Name: "web",
|
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 1024,
|
|
|
|
MemoryMB: 1024,
|
|
|
|
},
|
|
|
|
},
|
2015-09-13 21:31:32 +00:00
|
|
|
},
|
2015-08-13 20:08:15 +00:00
|
|
|
}
|
2015-09-13 21:31:32 +00:00
|
|
|
|
|
|
|
binp := NewBinPackIterator(ctx, static, false, 0)
|
2016-08-25 17:27:19 +00:00
|
|
|
binp.SetTaskGroup(taskGroup)
|
2015-08-13 20:08:15 +00:00
|
|
|
|
|
|
|
out := collectRanked(binp)
|
|
|
|
if len(out) != 1 {
|
|
|
|
t.Fatalf("Bad: %#v", out)
|
|
|
|
}
|
|
|
|
if out[0] != nodes[1] {
|
|
|
|
t.Fatalf("Bad: %v", out)
|
|
|
|
}
|
|
|
|
|
|
|
|
if out[0].Score != 18 {
|
|
|
|
t.Fatalf("Bad: %v", out[0])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBinPackIterator_ExistingAlloc(t *testing.T) {
|
|
|
|
state, ctx := testContext(t)
|
|
|
|
nodes := []*RankedNode{
|
|
|
|
&RankedNode{
|
|
|
|
Node: &structs.Node{
|
|
|
|
// Perfect fit
|
2015-09-07 22:23:03 +00:00
|
|
|
ID: structs.GenerateUUID(),
|
2015-08-13 20:08:15 +00:00
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 2048,
|
|
|
|
MemoryMB: 2048,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&RankedNode{
|
|
|
|
Node: &structs.Node{
|
|
|
|
// Perfect fit
|
2015-09-07 22:23:03 +00:00
|
|
|
ID: structs.GenerateUUID(),
|
2015-08-13 20:08:15 +00:00
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 2048,
|
|
|
|
MemoryMB: 2048,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
static := NewStaticRankIterator(ctx, nodes)
|
|
|
|
|
|
|
|
// Add existing allocations
|
|
|
|
alloc1 := &structs.Allocation{
|
2015-09-07 22:23:03 +00:00
|
|
|
ID: structs.GenerateUUID(),
|
|
|
|
EvalID: structs.GenerateUUID(),
|
2015-08-13 20:08:15 +00:00
|
|
|
NodeID: nodes[0].Node.ID,
|
2015-09-07 22:23:03 +00:00
|
|
|
JobID: structs.GenerateUUID(),
|
2015-08-13 20:08:15 +00:00
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 2048,
|
|
|
|
MemoryMB: 2048,
|
|
|
|
},
|
2015-08-26 00:06:06 +00:00
|
|
|
DesiredStatus: structs.AllocDesiredStatusRun,
|
2015-12-16 23:01:15 +00:00
|
|
|
ClientStatus: structs.AllocClientStatusPending,
|
2016-07-22 21:53:49 +00:00
|
|
|
TaskGroup: "web",
|
2015-08-13 20:08:15 +00:00
|
|
|
}
|
|
|
|
alloc2 := &structs.Allocation{
|
2015-09-07 22:23:03 +00:00
|
|
|
ID: structs.GenerateUUID(),
|
|
|
|
EvalID: structs.GenerateUUID(),
|
2015-08-13 20:08:15 +00:00
|
|
|
NodeID: nodes[1].Node.ID,
|
2015-09-07 22:23:03 +00:00
|
|
|
JobID: structs.GenerateUUID(),
|
2015-08-13 20:08:15 +00:00
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 1024,
|
|
|
|
MemoryMB: 1024,
|
|
|
|
},
|
2015-08-26 00:06:06 +00:00
|
|
|
DesiredStatus: structs.AllocDesiredStatusRun,
|
2015-12-16 23:01:15 +00:00
|
|
|
ClientStatus: structs.AllocClientStatusPending,
|
2016-07-22 21:53:49 +00:00
|
|
|
TaskGroup: "web",
|
2015-08-13 20:08:15 +00:00
|
|
|
}
|
2016-07-22 21:53:49 +00:00
|
|
|
noErr(t, state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID)))
|
|
|
|
noErr(t, state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID)))
|
2015-09-07 03:47:42 +00:00
|
|
|
noErr(t, state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2}))
|
2015-08-13 20:08:15 +00:00
|
|
|
|
2016-08-25 17:27:19 +00:00
|
|
|
taskGroup := &structs.TaskGroup{
|
|
|
|
LocalDisk: &structs.LocalDisk{},
|
|
|
|
Tasks: []*structs.Task{
|
|
|
|
{
|
|
|
|
Name: "web",
|
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 1024,
|
|
|
|
MemoryMB: 1024,
|
|
|
|
},
|
|
|
|
},
|
2015-09-13 21:31:32 +00:00
|
|
|
},
|
2015-08-13 20:08:15 +00:00
|
|
|
}
|
2015-09-13 21:31:32 +00:00
|
|
|
binp := NewBinPackIterator(ctx, static, false, 0)
|
2016-08-25 17:27:19 +00:00
|
|
|
binp.SetTaskGroup(taskGroup)
|
2015-08-13 20:08:15 +00:00
|
|
|
|
|
|
|
out := collectRanked(binp)
|
|
|
|
if len(out) != 1 {
|
|
|
|
t.Fatalf("Bad: %#v", out)
|
|
|
|
}
|
|
|
|
if out[0] != nodes[1] {
|
|
|
|
t.Fatalf("Bad: %v", out)
|
|
|
|
}
|
|
|
|
if out[0].Score != 18 {
|
|
|
|
t.Fatalf("Bad: %v", out[0])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBinPackIterator_ExistingAlloc_PlannedEvict(t *testing.T) {
|
|
|
|
state, ctx := testContext(t)
|
|
|
|
nodes := []*RankedNode{
|
|
|
|
&RankedNode{
|
|
|
|
Node: &structs.Node{
|
|
|
|
// Perfect fit
|
2015-09-07 22:23:03 +00:00
|
|
|
ID: structs.GenerateUUID(),
|
2015-08-13 20:08:15 +00:00
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 2048,
|
|
|
|
MemoryMB: 2048,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
&RankedNode{
|
|
|
|
Node: &structs.Node{
|
|
|
|
// Perfect fit
|
2015-09-07 22:23:03 +00:00
|
|
|
ID: structs.GenerateUUID(),
|
2015-08-13 20:08:15 +00:00
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 2048,
|
|
|
|
MemoryMB: 2048,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
static := NewStaticRankIterator(ctx, nodes)
|
|
|
|
|
|
|
|
// Add existing allocations
|
|
|
|
alloc1 := &structs.Allocation{
|
2015-09-07 22:23:03 +00:00
|
|
|
ID: structs.GenerateUUID(),
|
|
|
|
EvalID: structs.GenerateUUID(),
|
2015-08-13 20:08:15 +00:00
|
|
|
NodeID: nodes[0].Node.ID,
|
2015-09-07 22:23:03 +00:00
|
|
|
JobID: structs.GenerateUUID(),
|
2015-08-13 20:08:15 +00:00
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 2048,
|
|
|
|
MemoryMB: 2048,
|
|
|
|
},
|
2015-08-26 00:06:06 +00:00
|
|
|
DesiredStatus: structs.AllocDesiredStatusRun,
|
2015-12-16 23:01:15 +00:00
|
|
|
ClientStatus: structs.AllocClientStatusPending,
|
2016-07-22 21:53:49 +00:00
|
|
|
TaskGroup: "web",
|
2015-08-13 20:08:15 +00:00
|
|
|
}
|
|
|
|
alloc2 := &structs.Allocation{
|
2015-09-07 22:23:03 +00:00
|
|
|
ID: structs.GenerateUUID(),
|
|
|
|
EvalID: structs.GenerateUUID(),
|
2015-08-13 20:08:15 +00:00
|
|
|
NodeID: nodes[1].Node.ID,
|
2015-09-07 22:23:03 +00:00
|
|
|
JobID: structs.GenerateUUID(),
|
2015-08-13 20:08:15 +00:00
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 1024,
|
|
|
|
MemoryMB: 1024,
|
|
|
|
},
|
2015-08-26 00:06:06 +00:00
|
|
|
DesiredStatus: structs.AllocDesiredStatusRun,
|
2015-12-16 23:01:15 +00:00
|
|
|
ClientStatus: structs.AllocClientStatusPending,
|
2016-07-22 21:53:49 +00:00
|
|
|
TaskGroup: "web",
|
2015-08-13 20:08:15 +00:00
|
|
|
}
|
2016-07-22 21:53:49 +00:00
|
|
|
noErr(t, state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID)))
|
|
|
|
noErr(t, state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID)))
|
2015-09-07 03:47:42 +00:00
|
|
|
noErr(t, state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2}))
|
2015-08-13 20:08:15 +00:00
|
|
|
|
|
|
|
// Add a planned eviction to alloc1
|
|
|
|
plan := ctx.Plan()
|
2015-08-26 00:06:06 +00:00
|
|
|
plan.NodeUpdate[nodes[0].Node.ID] = []*structs.Allocation{alloc1}
|
2015-08-13 20:08:15 +00:00
|
|
|
|
2016-08-25 17:27:19 +00:00
|
|
|
taskGroup := &structs.TaskGroup{
|
|
|
|
LocalDisk: &structs.LocalDisk{},
|
|
|
|
Tasks: []*structs.Task{
|
|
|
|
{
|
|
|
|
Name: "web",
|
|
|
|
Resources: &structs.Resources{
|
|
|
|
CPU: 1024,
|
|
|
|
MemoryMB: 1024,
|
|
|
|
},
|
|
|
|
},
|
2015-09-13 21:31:32 +00:00
|
|
|
},
|
2015-08-13 20:08:15 +00:00
|
|
|
}
|
2015-09-13 21:31:32 +00:00
|
|
|
|
|
|
|
binp := NewBinPackIterator(ctx, static, false, 0)
|
2016-08-25 17:27:19 +00:00
|
|
|
binp.SetTaskGroup(taskGroup)
|
2015-08-13 20:08:15 +00:00
|
|
|
|
|
|
|
out := collectRanked(binp)
|
|
|
|
if len(out) != 2 {
|
|
|
|
t.Fatalf("Bad: %#v", out)
|
|
|
|
}
|
|
|
|
if out[0] != nodes[0] || out[1] != nodes[1] {
|
|
|
|
t.Fatalf("Bad: %v", out)
|
|
|
|
}
|
|
|
|
if out[0].Score < 10 || out[0].Score > 16 {
|
|
|
|
t.Fatalf("Bad: %v", out[0])
|
|
|
|
}
|
|
|
|
if out[1].Score != 18 {
|
2015-12-15 03:20:57 +00:00
|
|
|
t.Fatalf("Bad: %v", out[1])
|
2015-08-13 20:08:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-16 17:32:25 +00:00
|
|
|
func TestJobAntiAffinity_PlannedAlloc(t *testing.T) {
|
|
|
|
_, ctx := testContext(t)
|
|
|
|
nodes := []*RankedNode{
|
|
|
|
&RankedNode{
|
|
|
|
Node: &structs.Node{
|
2015-09-07 22:23:03 +00:00
|
|
|
ID: structs.GenerateUUID(),
|
2015-08-16 17:32:25 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
&RankedNode{
|
|
|
|
Node: &structs.Node{
|
2015-09-07 22:23:03 +00:00
|
|
|
ID: structs.GenerateUUID(),
|
2015-08-16 17:32:25 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
static := NewStaticRankIterator(ctx, nodes)
|
|
|
|
|
|
|
|
// Add a planned alloc to node1 that fills it
|
|
|
|
plan := ctx.Plan()
|
|
|
|
plan.NodeAllocation[nodes[0].Node.ID] = []*structs.Allocation{
|
|
|
|
&structs.Allocation{
|
2016-03-22 00:23:04 +00:00
|
|
|
ID: structs.GenerateUUID(),
|
2015-08-16 17:32:25 +00:00
|
|
|
JobID: "foo",
|
|
|
|
},
|
|
|
|
&structs.Allocation{
|
2016-03-22 00:23:04 +00:00
|
|
|
ID: structs.GenerateUUID(),
|
2015-08-16 17:32:25 +00:00
|
|
|
JobID: "foo",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add a planned alloc to node2 that half fills it
|
|
|
|
plan.NodeAllocation[nodes[1].Node.ID] = []*structs.Allocation{
|
|
|
|
&structs.Allocation{
|
|
|
|
JobID: "bar",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
binp := NewJobAntiAffinityIterator(ctx, static, 5.0, "foo")
|
|
|
|
|
|
|
|
out := collectRanked(binp)
|
|
|
|
if len(out) != 2 {
|
|
|
|
t.Fatalf("Bad: %#v", out)
|
|
|
|
}
|
|
|
|
if out[0] != nodes[0] {
|
|
|
|
t.Fatalf("Bad: %v", out)
|
|
|
|
}
|
|
|
|
if out[0].Score != -10.0 {
|
2016-03-22 00:23:04 +00:00
|
|
|
t.Fatalf("Bad: %#v", out[0])
|
2015-08-16 17:32:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if out[1] != nodes[1] {
|
|
|
|
t.Fatalf("Bad: %v", out)
|
|
|
|
}
|
|
|
|
if out[1].Score != 0.0 {
|
|
|
|
t.Fatalf("Bad: %v", out[1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-13 19:02:42 +00:00
|
|
|
func collectRanked(iter RankIterator) (out []*RankedNode) {
|
|
|
|
for {
|
|
|
|
next := iter.Next()
|
|
|
|
if next == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
out = append(out, next)
|
|
|
|
}
|
|
|
|
return
|
2015-08-13 18:33:58 +00:00
|
|
|
}
|