nomad: testing plan evaluation
This commit is contained in:
parent
2382993b78
commit
3aba600230
|
@ -19,8 +19,16 @@ func (s *Server) planApply() {
|
|||
return
|
||||
}
|
||||
|
||||
// Snapshot the state so that we have a consistent view of the world
|
||||
snap, err := s.fsm.State().Snapshot()
|
||||
if err != nil {
|
||||
s.logger.Printf("[ERR] nomad: failed to snapshot state: %v", err)
|
||||
pending.respond(nil, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Evaluate the plan
|
||||
result, err := s.evaluatePlan(pending.plan)
|
||||
result, err := evaluatePlan(snap, pending.plan)
|
||||
if err != nil {
|
||||
s.logger.Printf("[ERR] nomad: failed to evaluate plan: %v", err)
|
||||
pending.respond(nil, err)
|
||||
|
@ -43,18 +51,27 @@ func (s *Server) planApply() {
|
|||
}
|
||||
}
|
||||
|
||||
// applyPlan is used to apply the plan result and to return the alloc index
|
||||
func (s *Server) applyPlan(result *structs.PlanResult) (uint64, error) {
|
||||
defer metrics.MeasureSince([]string{"nomad", "plan", "apply"}, time.Now())
|
||||
req := structs.AllocUpdateRequest{}
|
||||
for _, evictList := range result.NodeEvict {
|
||||
req.Evict = append(req.Evict, evictList...)
|
||||
}
|
||||
for _, allocList := range result.NodeAllocation {
|
||||
req.Alloc = append(req.Alloc, allocList...)
|
||||
}
|
||||
|
||||
_, index, err := s.raftApply(structs.AllocUpdateRequestType, &req)
|
||||
return index, err
|
||||
}
|
||||
|
||||
// evaluatePlan is used to determine what portions of a plan
|
||||
// can be applied if any. Returns if there should be a plan application
|
||||
// which may be partial or if there was an error
|
||||
func (s *Server) evaluatePlan(plan *structs.Plan) (*structs.PlanResult, error) {
|
||||
func evaluatePlan(snap *StateSnapshot, 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 {
|
||||
return nil, fmt.Errorf("failed to snapshot state: %v", err)
|
||||
}
|
||||
|
||||
// Create a result holder for the plan
|
||||
result := &structs.PlanResult{
|
||||
NodeEvict: make(map[string][]string),
|
||||
|
@ -84,6 +101,8 @@ func (s *Server) evaluatePlan(plan *structs.Plan) (*structs.PlanResult, error) {
|
|||
// If we require all-at-once scheduling, there is no point
|
||||
// to continue the evaluation, as we've already failed.
|
||||
if plan.AllAtOnce {
|
||||
result.NodeEvict = nil
|
||||
result.NodeAllocation = nil
|
||||
return result, nil
|
||||
}
|
||||
|
||||
|
@ -98,21 +117,6 @@ func (s *Server) evaluatePlan(plan *structs.Plan) (*structs.PlanResult, error) {
|
|||
return result, nil
|
||||
}
|
||||
|
||||
// applyPlan is used to apply the plan result and to return the alloc index
|
||||
func (s *Server) applyPlan(result *structs.PlanResult) (uint64, error) {
|
||||
defer metrics.MeasureSince([]string{"nomad", "plan", "apply"}, time.Now())
|
||||
req := structs.AllocUpdateRequest{}
|
||||
for _, evictList := range result.NodeEvict {
|
||||
req.Evict = append(req.Evict, evictList...)
|
||||
}
|
||||
for _, allocList := range result.NodeAllocation {
|
||||
req.Alloc = append(req.Alloc, allocList...)
|
||||
}
|
||||
|
||||
_, 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) {
|
||||
|
|
|
@ -101,6 +101,94 @@ func TestPlanApply_applyPlan(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPlanApply_EvalPlan_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},
|
||||
},
|
||||
}
|
||||
|
||||
result, err := evaluatePlan(snap, plan)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatalf("missing result")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlanApply_EvalPlan_Partial(t *testing.T) {
|
||||
state := testStateStore(t)
|
||||
node := mockNode()
|
||||
state.RegisterNode(1000, node)
|
||||
node2 := mockNode()
|
||||
state.RegisterNode(1001, node2)
|
||||
snap, _ := state.Snapshot()
|
||||
|
||||
alloc := mockAlloc()
|
||||
alloc2 := mockAlloc() // Ensure alloc2 does not fit
|
||||
alloc2.Resources = node2.Resources
|
||||
plan := &structs.Plan{
|
||||
NodeAllocation: map[string][]*structs.Allocation{
|
||||
node.ID: []*structs.Allocation{alloc},
|
||||
node2.ID: []*structs.Allocation{alloc2},
|
||||
},
|
||||
}
|
||||
|
||||
result, err := evaluatePlan(snap, plan)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatalf("missing result")
|
||||
}
|
||||
|
||||
if _, ok := result.NodeAllocation[node.ID]; !ok {
|
||||
t.Fatalf("should allow alloc")
|
||||
}
|
||||
if _, ok := result.NodeAllocation[node2.ID]; ok {
|
||||
t.Fatalf("should not allow alloc2")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlanApply_EvalPlan_Partial_AllAtOnce(t *testing.T) {
|
||||
state := testStateStore(t)
|
||||
node := mockNode()
|
||||
state.RegisterNode(1000, node)
|
||||
node2 := mockNode()
|
||||
state.RegisterNode(1001, node2)
|
||||
snap, _ := state.Snapshot()
|
||||
|
||||
alloc := mockAlloc()
|
||||
alloc2 := mockAlloc() // Ensure alloc2 does not fit
|
||||
alloc2.Resources = node2.Resources
|
||||
plan := &structs.Plan{
|
||||
AllAtOnce: true, // Require all to make progress
|
||||
NodeAllocation: map[string][]*structs.Allocation{
|
||||
node.ID: []*structs.Allocation{alloc},
|
||||
node2.ID: []*structs.Allocation{alloc2},
|
||||
},
|
||||
}
|
||||
|
||||
result, err := evaluatePlan(snap, plan)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if result == nil {
|
||||
t.Fatalf("missing result")
|
||||
}
|
||||
|
||||
if len(result.NodeAllocation) != 0 {
|
||||
t.Fatalf("should not alloc: %v", result.NodeAllocation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlanApply_EvalNodePlan_Simple(t *testing.T) {
|
||||
state := testStateStore(t)
|
||||
node := mockNode()
|
||||
|
|
Loading…
Reference in a new issue