nomad: test evaluation of node plan
This commit is contained in:
parent
f733ba0efa
commit
d758d459f7
|
@ -48,6 +48,7 @@ func (s *Server) planApply() {
|
|||
// which may be partial or if there was an error
|
||||
func (s *Server) evaluatePlan(plan *structs.Plan) (*structs.PlanResult, error) {
|
||||
defer metrics.MeasureSince([]string{"nomad", "plan", "evaluate"}, time.Now())
|
||||
|
||||
// Snapshot the state so that we have a consistent view of the world
|
||||
snap, err := s.fsm.State().Snapshot()
|
||||
if err != nil {
|
||||
|
@ -61,30 +62,13 @@ func (s *Server) evaluatePlan(plan *structs.Plan) (*structs.PlanResult, error) {
|
|||
}
|
||||
|
||||
// Check each allocation to see if it should be allowed
|
||||
for nodeID, allocList := range plan.NodeAllocation {
|
||||
// Get the node itself
|
||||
node, err := snap.GetNodeByID(nodeID)
|
||||
for nodeID := range plan.NodeAllocation {
|
||||
// Evaluate the plan for this node
|
||||
fit, err := evaluateNodePlan(snap, plan, nodeID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get node '%s': %v", node, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the existing allocations
|
||||
existingAlloc, err := snap.AllocsByNode(nodeID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get existing allocations for '%s': %v", node, err)
|
||||
}
|
||||
|
||||
// Determine the proposed allocation by first removing allocations
|
||||
// that are planned evictions and adding the new allocations.
|
||||
proposed := existingAlloc
|
||||
evictions := plan.NodeEvict[nodeID]
|
||||
if len(evictions) > 0 {
|
||||
proposed = structs.RemoveAllocs(existingAlloc, evictions)
|
||||
}
|
||||
proposed = append(proposed, allocList...)
|
||||
|
||||
// Determine if everything fits
|
||||
if !AllocationsFit(node, proposed) {
|
||||
if !fit {
|
||||
// Scheduler must have stale data, RefreshIndex should force
|
||||
// the latest view of allocations and nodes
|
||||
allocIndex, err := snap.GetIndex("allocs")
|
||||
|
@ -108,12 +92,8 @@ func (s *Server) evaluatePlan(plan *structs.Plan) (*structs.PlanResult, error) {
|
|||
}
|
||||
|
||||
// Add this to the plan result
|
||||
if len(evictions) > 0 {
|
||||
result.NodeEvict[nodeID] = evictions
|
||||
}
|
||||
if len(allocList) > 0 {
|
||||
result.NodeAllocation[nodeID] = allocList
|
||||
}
|
||||
result.NodeEvict[nodeID] = plan.NodeEvict[nodeID]
|
||||
result.NodeAllocation[nodeID] = plan.NodeAllocation[nodeID]
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
@ -132,3 +112,35 @@ func (s *Server) applyPlan(result *structs.PlanResult) (uint64, error) {
|
|||
_, index, err := s.raftApply(structs.AllocUpdateRequestType, &req)
|
||||
return index, err
|
||||
}
|
||||
|
||||
// evaluateNodePlan is used to evalute the plan for a single node,
|
||||
// returning if the plan is valid or if an error is encountered
|
||||
func evaluateNodePlan(snap *StateSnapshot, plan *structs.Plan, nodeID string) (bool, error) {
|
||||
// Get the node itself
|
||||
node, err := snap.GetNodeByID(nodeID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get node '%s': %v", node, err)
|
||||
}
|
||||
|
||||
// If the node does not exist or is not ready for schduling it is not fit
|
||||
if node == nil || node.Status != structs.NodeStatusReady {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Get the existing allocations
|
||||
existingAlloc, err := snap.AllocsByNode(nodeID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get existing allocations for '%s': %v", node, err)
|
||||
}
|
||||
|
||||
// Determine the proposed allocation by first removing allocations
|
||||
// that are planned evictions and adding the new allocations.
|
||||
proposed := existingAlloc
|
||||
if evict := plan.NodeEvict[nodeID]; len(evict) > 0 {
|
||||
proposed = structs.RemoveAllocs(existingAlloc, evict)
|
||||
}
|
||||
proposed = append(proposed, plan.NodeAllocation[nodeID]...)
|
||||
|
||||
// Check if these allocations fit
|
||||
return structs.AllocsFit(node, proposed)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,131 @@
|
|||
package nomad
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
|
||||
func TestPlanApply(t *testing.T) {
|
||||
// TODO
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
)
|
||||
|
||||
func TestPlanApply_EvalNodePlan_Simple(t *testing.T) {
|
||||
state := testStateStore(t)
|
||||
node := mockNode()
|
||||
state.RegisterNode(1000, node)
|
||||
snap, _ := state.Snapshot()
|
||||
|
||||
alloc := mockAlloc()
|
||||
plan := &structs.Plan{
|
||||
NodeAllocation: map[string][]*structs.Allocation{
|
||||
node.ID: []*structs.Allocation{alloc},
|
||||
},
|
||||
}
|
||||
|
||||
fit, err := evaluateNodePlan(snap, plan, node.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if !fit {
|
||||
t.Fatalf("bad")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlanApply_EvalNodePlan_NodeNotReady(t *testing.T) {
|
||||
state := testStateStore(t)
|
||||
node := mockNode()
|
||||
node.Status = structs.NodeStatusInit
|
||||
state.RegisterNode(1000, node)
|
||||
snap, _ := state.Snapshot()
|
||||
|
||||
alloc := mockAlloc()
|
||||
plan := &structs.Plan{
|
||||
NodeAllocation: map[string][]*structs.Allocation{
|
||||
node.ID: []*structs.Allocation{alloc},
|
||||
},
|
||||
}
|
||||
|
||||
fit, err := evaluateNodePlan(snap, plan, node.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if fit {
|
||||
t.Fatalf("bad")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlanApply_EvalNodePlan_NodeNotExist(t *testing.T) {
|
||||
state := testStateStore(t)
|
||||
snap, _ := state.Snapshot()
|
||||
|
||||
nodeID := "foo"
|
||||
alloc := mockAlloc()
|
||||
plan := &structs.Plan{
|
||||
NodeAllocation: map[string][]*structs.Allocation{
|
||||
nodeID: []*structs.Allocation{alloc},
|
||||
},
|
||||
}
|
||||
|
||||
fit, err := evaluateNodePlan(snap, plan, nodeID)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if fit {
|
||||
t.Fatalf("bad")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlanApply_EvalNodePlan_NodeFull(t *testing.T) {
|
||||
alloc := mockAlloc()
|
||||
state := testStateStore(t)
|
||||
node := mockNode()
|
||||
alloc.NodeID = node.ID
|
||||
node.Resources = alloc.Resources
|
||||
node.Reserved = nil
|
||||
state.RegisterNode(1000, node)
|
||||
state.UpdateAllocations(1001, nil,
|
||||
[]*structs.Allocation{alloc})
|
||||
snap, _ := state.Snapshot()
|
||||
|
||||
plan := &structs.Plan{
|
||||
NodeAllocation: map[string][]*structs.Allocation{
|
||||
node.ID: []*structs.Allocation{alloc},
|
||||
},
|
||||
}
|
||||
|
||||
fit, err := evaluateNodePlan(snap, plan, node.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if fit {
|
||||
t.Fatalf("bad")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlanApply_EvalNodePlan_NodeFull_Evict(t *testing.T) {
|
||||
alloc := mockAlloc()
|
||||
state := testStateStore(t)
|
||||
node := mockNode()
|
||||
alloc.NodeID = node.ID
|
||||
node.Resources = alloc.Resources
|
||||
node.Reserved = nil
|
||||
state.RegisterNode(1000, node)
|
||||
state.UpdateAllocations(1001, nil,
|
||||
[]*structs.Allocation{alloc})
|
||||
snap, _ := state.Snapshot()
|
||||
|
||||
alloc2 := mockAlloc()
|
||||
plan := &structs.Plan{
|
||||
NodeEvict: map[string][]string{
|
||||
node.ID: []string{alloc.ID},
|
||||
},
|
||||
NodeAllocation: map[string][]*structs.Allocation{
|
||||
node.ID: []*structs.Allocation{alloc2},
|
||||
},
|
||||
}
|
||||
|
||||
fit, err := evaluateNodePlan(snap, plan, node.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if !fit {
|
||||
t.Fatalf("bad")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ func mockNode() *structs.Node {
|
|||
"pci-dss": "true",
|
||||
},
|
||||
NodeClass: "linux-medium-pci",
|
||||
Status: structs.NodeStatusInit,
|
||||
Status: structs.NodeStatusReady,
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
@ -125,6 +125,20 @@ func mockAlloc() *structs.Allocation {
|
|||
alloc := &structs.Allocation{
|
||||
ID: generateUUID(),
|
||||
NodeID: "foo",
|
||||
Resources: &structs.Resources{
|
||||
CPU: 1.0,
|
||||
MemoryMB: 1024,
|
||||
DiskMB: 1024,
|
||||
IOPS: 10,
|
||||
Networks: []*structs.NetworkResource{
|
||||
&structs.NetworkResource{
|
||||
Public: true,
|
||||
CIDR: "192.168.0.100/32",
|
||||
ReservedPorts: []int{12345},
|
||||
MBits: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return alloc
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue