open-nomad/scheduler/rank_test.go

467 lines
9.5 KiB
Go
Raw Normal View History

2015-08-13 17:13:11 +00:00
package scheduler
import (
"testing"
"github.com/hashicorp/nomad/helper/uuid"
2015-08-13 17:13:11 +00:00
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
require "github.com/stretchr/testify/require"
2015-08-13 17:13:11 +00:00
)
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{
2017-09-26 22:26:33 +00:00
{
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
},
2017-09-26 22:26:33 +00:00
{
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
},
2017-09-26 22:26:33 +00:00
{
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)
taskGroup := &structs.TaskGroup{
EphemeralDisk: &structs.EphemeralDisk{},
Tasks: []*structs.Task{
{
Name: "web",
Resources: &structs.Resources{
CPU: 1024,
MemoryMB: 1024,
},
},
},
2015-08-13 19:08:53 +00:00
}
binp := NewBinPackIterator(ctx, static, false, 0)
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{
2017-09-26 22:26:33 +00:00
{
2015-08-13 20:08:15 +00:00
Node: &structs.Node{
// Perfect fit
ID: uuid.Generate(),
2015-08-13 20:08:15 +00:00
Resources: &structs.Resources{
CPU: 2048,
MemoryMB: 2048,
},
},
},
2017-09-26 22:26:33 +00:00
{
2015-08-13 20:08:15 +00:00
Node: &structs.Node{
// Perfect fit
ID: uuid.Generate(),
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{
2017-09-26 22:26:33 +00:00
{
2015-08-13 20:08:15 +00:00
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{
2017-09-26 22:26:33 +00:00
{
2015-08-13 20:08:15 +00:00
Resources: &structs.Resources{
CPU: 1024,
MemoryMB: 1024,
},
},
}
taskGroup := &structs.TaskGroup{
EphemeralDisk: &structs.EphemeralDisk{},
Tasks: []*structs.Task{
{
Name: "web",
Resources: &structs.Resources{
CPU: 1024,
MemoryMB: 1024,
},
},
},
2015-08-13 20:08:15 +00:00
}
binp := NewBinPackIterator(ctx, static, false, 0)
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{
2017-09-26 22:26:33 +00:00
{
2015-08-13 20:08:15 +00:00
Node: &structs.Node{
// Perfect fit
ID: uuid.Generate(),
2015-08-13 20:08:15 +00:00
Resources: &structs.Resources{
CPU: 2048,
MemoryMB: 2048,
},
},
},
2017-09-26 22:26:33 +00:00
{
2015-08-13 20:08:15 +00:00
Node: &structs.Node{
// Perfect fit
ID: uuid.Generate(),
2015-08-13 20:08:15 +00:00
Resources: &structs.Resources{
CPU: 2048,
MemoryMB: 2048,
},
},
},
}
static := NewStaticRankIterator(ctx, nodes)
// Add existing allocations
2017-05-01 20:54:26 +00:00
j1, j2 := mock.Job(), mock.Job()
2015-08-13 20:08:15 +00:00
alloc1 := &structs.Allocation{
2017-09-07 23:56:15 +00:00
Namespace: structs.DefaultNamespace,
ID: uuid.Generate(),
EvalID: uuid.Generate(),
2017-09-07 23:56:15 +00:00
NodeID: nodes[0].Node.ID,
JobID: j1.ID,
Job: j1,
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{
2017-09-07 23:56:15 +00:00
Namespace: structs.DefaultNamespace,
ID: uuid.Generate(),
EvalID: uuid.Generate(),
2017-09-07 23:56:15 +00:00
NodeID: nodes[1].Node.ID,
JobID: j2.ID,
Job: j2,
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
taskGroup := &structs.TaskGroup{
EphemeralDisk: &structs.EphemeralDisk{},
Tasks: []*structs.Task{
{
Name: "web",
Resources: &structs.Resources{
CPU: 1024,
MemoryMB: 1024,
},
},
},
2015-08-13 20:08:15 +00:00
}
binp := NewBinPackIterator(ctx, static, false, 0)
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{
2017-09-26 22:26:33 +00:00
{
2015-08-13 20:08:15 +00:00
Node: &structs.Node{
// Perfect fit
ID: uuid.Generate(),
2015-08-13 20:08:15 +00:00
Resources: &structs.Resources{
CPU: 2048,
MemoryMB: 2048,
},
},
},
2017-09-26 22:26:33 +00:00
{
2015-08-13 20:08:15 +00:00
Node: &structs.Node{
// Perfect fit
ID: uuid.Generate(),
2015-08-13 20:08:15 +00:00
Resources: &structs.Resources{
CPU: 2048,
MemoryMB: 2048,
},
},
},
}
static := NewStaticRankIterator(ctx, nodes)
// Add existing allocations
2017-05-01 20:54:26 +00:00
j1, j2 := mock.Job(), mock.Job()
2015-08-13 20:08:15 +00:00
alloc1 := &structs.Allocation{
2017-09-07 23:56:15 +00:00
Namespace: structs.DefaultNamespace,
ID: uuid.Generate(),
EvalID: uuid.Generate(),
2017-09-07 23:56:15 +00:00
NodeID: nodes[0].Node.ID,
JobID: j1.ID,
Job: j1,
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{
2017-09-07 23:56:15 +00:00
Namespace: structs.DefaultNamespace,
ID: uuid.Generate(),
EvalID: uuid.Generate(),
2017-09-07 23:56:15 +00:00
NodeID: nodes[1].Node.ID,
JobID: j2.ID,
Job: j2,
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
taskGroup := &structs.TaskGroup{
EphemeralDisk: &structs.EphemeralDisk{},
Tasks: []*structs.Task{
{
Name: "web",
Resources: &structs.Resources{
CPU: 1024,
MemoryMB: 1024,
},
},
},
2015-08-13 20:08:15 +00:00
}
binp := NewBinPackIterator(ctx, static, false, 0)
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
}
}
func TestJobAntiAffinity_PlannedAlloc(t *testing.T) {
_, ctx := testContext(t)
nodes := []*RankedNode{
2017-09-26 22:26:33 +00:00
{
Node: &structs.Node{
ID: uuid.Generate(),
},
},
2017-09-26 22:26:33 +00:00
{
Node: &structs.Node{
ID: uuid.Generate(),
},
},
}
static := NewStaticRankIterator(ctx, nodes)
// Add a planned alloc to node1 that fills it
plan := ctx.Plan()
plan.NodeAllocation[nodes[0].Node.ID] = []*structs.Allocation{
2017-09-26 22:26:33 +00:00
{
ID: uuid.Generate(),
JobID: "foo",
},
2017-09-26 22:26:33 +00:00
{
ID: uuid.Generate(),
JobID: "foo",
},
}
// Add a planned alloc to node2 that half fills it
plan.NodeAllocation[nodes[1].Node.ID] = []*structs.Allocation{
2017-09-26 22:26:33 +00:00
{
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 {
t.Fatalf("Bad: %#v", out[0])
}
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
}
func TestNodeAntiAffinity_PenaltyNodes(t *testing.T) {
_, ctx := testContext(t)
node1 := &structs.Node{
ID: uuid.Generate(),
}
node2 := &structs.Node{
ID: uuid.Generate(),
}
nodes := []*RankedNode{
{
Node: node1,
},
{
Node: node2,
},
}
static := NewStaticRankIterator(ctx, nodes)
nodeAntiAffIter := NewNodeAntiAffinityIterator(ctx, static, 50.0)
nodeAntiAffIter.SetPenaltyNodes(map[string]struct{}{node1.ID: {}})
out := collectRanked(nodeAntiAffIter)
require := require.New(t)
require.Equal(2, len(out))
require.Equal(node1.ID, out[0].Node.ID)
require.Equal(-50.0, out[0].Score)
require.Equal(node2.ID, out[1].Node.ID)
require.Equal(0.0, out[1].Score)
}