scheduler: testing bin pack scoring

This commit is contained in:
Armon Dadgar 2015-08-13 13:08:15 -07:00
parent 022e583a2a
commit f5e37c926c
3 changed files with 217 additions and 3 deletions

View file

@ -14,7 +14,10 @@ func testContext(t *testing.T) (*state.StateStore, *EvalContext) {
if err != nil { if err != nil {
t.Fatalf("err: %v", err) t.Fatalf("err: %v", err)
} }
plan := new(structs.Plan) plan := &structs.Plan{
NodeEvict: make(map[string][]string),
NodeAllocation: make(map[string][]*structs.Allocation),
}
logger := log.New(os.Stderr, "", log.LstdFlags) logger := log.New(os.Stderr, "", log.LstdFlags)

View file

@ -1,6 +1,10 @@
package scheduler package scheduler
import "github.com/hashicorp/nomad/nomad/structs" import (
"fmt"
"github.com/hashicorp/nomad/nomad/structs"
)
// Rank is used to provide a score and various ranking metadata // Rank is used to provide a score and various ranking metadata
// along with a node when iterating. This state can be modified as // along with a node when iterating. This state can be modified as
@ -10,6 +14,10 @@ type RankedNode struct {
Score float64 Score float64
} }
func (r *RankedNode) GoString() string {
return fmt.Sprintf("<Node: %s Score: %0.3f>", r.Node.ID, r.Score)
}
// RankFeasibleIterator is used to iteratively yield nodes along // RankFeasibleIterator is used to iteratively yield nodes along
// with ranking metadata. The iterators may manage some state for // with ranking metadata. The iterators may manage some state for
// performance optimizations. // performance optimizations.

View file

@ -23,7 +23,7 @@ func TestFeasibleRankIterator(t *testing.T) {
} }
} }
func TestBinPackIterator_Simple_NoExistingAlloc(t *testing.T) { func TestBinPackIterator_NoExistingAlloc(t *testing.T) {
_, ctx := testContext(t) _, ctx := testContext(t)
nodes := []*RankedNode{ nodes := []*RankedNode{
&RankedNode{ &RankedNode{
@ -90,6 +90,209 @@ func TestBinPackIterator_Simple_NoExistingAlloc(t *testing.T) {
} }
} }
func TestBinPackIterator_PlannedAlloc(t *testing.T) {
_, ctx := testContext(t)
nodes := []*RankedNode{
&RankedNode{
Node: &structs.Node{
// Perfect fit
ID: mock.GenerateUUID(),
Resources: &structs.Resources{
CPU: 2048,
MemoryMB: 2048,
},
},
},
&RankedNode{
Node: &structs.Node{
// Perfect fit
ID: mock.GenerateUUID(),
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,
},
},
}
resources := &structs.Resources{
CPU: 1024,
MemoryMB: 1024,
}
binp := NewBinPackIterator(ctx, static, resources, false, 0)
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
ID: mock.GenerateUUID(),
Resources: &structs.Resources{
CPU: 2048,
MemoryMB: 2048,
},
},
},
&RankedNode{
Node: &structs.Node{
// Perfect fit
ID: mock.GenerateUUID(),
Resources: &structs.Resources{
CPU: 2048,
MemoryMB: 2048,
},
},
},
}
static := NewStaticRankIterator(ctx, nodes)
// Add existing allocations
alloc1 := &structs.Allocation{
ID: mock.GenerateUUID(),
NodeID: nodes[0].Node.ID,
JobID: mock.GenerateUUID(),
Resources: &structs.Resources{
CPU: 2048,
MemoryMB: 2048,
},
}
alloc2 := &structs.Allocation{
ID: mock.GenerateUUID(),
NodeID: nodes[1].Node.ID,
JobID: mock.GenerateUUID(),
Resources: &structs.Resources{
CPU: 1024,
MemoryMB: 1024,
},
}
noErr(t, state.UpdateAllocations(1000, nil, []*structs.Allocation{alloc1, alloc2}))
resources := &structs.Resources{
CPU: 1024,
MemoryMB: 1024,
}
binp := NewBinPackIterator(ctx, static, resources, false, 0)
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
ID: mock.GenerateUUID(),
Resources: &structs.Resources{
CPU: 2048,
MemoryMB: 2048,
},
},
},
&RankedNode{
Node: &structs.Node{
// Perfect fit
ID: mock.GenerateUUID(),
Resources: &structs.Resources{
CPU: 2048,
MemoryMB: 2048,
},
},
},
}
static := NewStaticRankIterator(ctx, nodes)
// Add existing allocations
alloc1 := &structs.Allocation{
ID: mock.GenerateUUID(),
NodeID: nodes[0].Node.ID,
JobID: mock.GenerateUUID(),
Resources: &structs.Resources{
CPU: 2048,
MemoryMB: 2048,
},
}
alloc2 := &structs.Allocation{
ID: mock.GenerateUUID(),
NodeID: nodes[1].Node.ID,
JobID: mock.GenerateUUID(),
Resources: &structs.Resources{
CPU: 1024,
MemoryMB: 1024,
},
}
noErr(t, state.UpdateAllocations(1000, nil, []*structs.Allocation{alloc1, alloc2}))
// Add a planned eviction to alloc1
plan := ctx.Plan()
plan.NodeEvict[nodes[0].Node.ID] = []string{alloc1.ID}
resources := &structs.Resources{
CPU: 1024,
MemoryMB: 1024,
}
binp := NewBinPackIterator(ctx, static, resources, false, 0)
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 {
t.Fatalf("Bad: %v", out[0])
}
}
func collectRanked(iter RankIterator) (out []*RankedNode) { func collectRanked(iter RankIterator) (out []*RankedNode) {
for { for {
next := iter.Next() next := iter.Next()