diff --git a/nomad/plan_apply.go b/nomad/plan_apply.go index cec714545..8a2f9387b 100644 --- a/nomad/plan_apply.go +++ b/nomad/plan_apply.go @@ -132,33 +132,3 @@ func (s *Server) applyPlan(result *structs.PlanResult) (uint64, error) { _, index, err := s.raftApply(structs.AllocUpdateRequestType, &req) return index, err } - -// AllocationsFit checks if a given set of allocations will fit on a node -func AllocationsFit(node *structs.Node, allocs []*structs.Allocation) bool { - // Start with no resource utilization - resourcesUsed := new(structs.Resources) - - // Add the reserved resources of the node - if node.Reserved != nil { - addResources(resourcesUsed, node.Reserved) - } - - // For each allocaiton, add the resources - for _, alloc := range allocs { - addResources(resourcesUsed, alloc.Resources) - } - - // Check that the node resources are a super set of those - // that are being allocated - if !node.Resources.Superset(resourcesUsed) { - return false - } - - // Ensure ports are not over commited - if structs.PortsOvercommited(resourcesUsed) { - return false - } - - // Everything is in order! - return true -} diff --git a/nomad/structs/funcs.go b/nomad/structs/funcs.go index 90343b8ca..53e141d86 100644 --- a/nomad/structs/funcs.go +++ b/nomad/structs/funcs.go @@ -37,3 +37,43 @@ func PortsOvercommited(r *Resources) bool { } return false } + +// AllocsFit checks if a given set of allocations will fit on a node +func AllocsFit(node *Node, allocs []*Allocation) (bool, error) { + // Compute the utilization from zero + used := new(Resources) + for _, net := range node.Resources.Networks { + used.Networks = append(used.Networks, &NetworkResource{ + Public: net.Public, + CIDR: net.CIDR, + }) + } + + // Add the reserved resources of the node + if node.Reserved != nil { + if err := used.Add(node.Reserved); err != nil { + return false, err + } + } + + // For each alloc, add the resources + for _, alloc := range allocs { + if err := used.Add(alloc.Resources); err != nil { + return false, err + } + } + + // Check that the node resources are a super set of those + // that are being allocated + if !node.Resources.Superset(used) { + return false, nil + } + + // Ensure ports are not over commited + if PortsOvercommited(used) { + return false, nil + } + + // Allocations fit! + return true, nil +} diff --git a/nomad/structs/funcs_test.go b/nomad/structs/funcs_test.go index c79f4826b..f53d99777 100644 --- a/nomad/structs/funcs_test.go +++ b/nomad/structs/funcs_test.go @@ -40,3 +40,67 @@ func TestPortsOvercommitted(t *testing.T) { t.Fatalf("bad") } } + +func TestAllocsFit(t *testing.T) { + n := &Node{ + Resources: &Resources{ + CPU: 2.0, + MemoryMB: 2048, + DiskMB: 10000, + IOPS: 100, + Networks: []*NetworkResource{ + &NetworkResource{ + CIDR: "10.0.0.0/8", + MBits: 100, + }, + }, + }, + Reserved: &Resources{ + CPU: 1.0, + MemoryMB: 1024, + DiskMB: 5000, + IOPS: 50, + Networks: []*NetworkResource{ + &NetworkResource{ + CIDR: "10.0.0.0/8", + MBits: 50, + ReservedPorts: []int{80}, + }, + }, + }, + } + + a1 := &Allocation{ + Resources: &Resources{ + CPU: 1.0, + MemoryMB: 1024, + DiskMB: 5000, + IOPS: 50, + Networks: []*NetworkResource{ + &NetworkResource{ + CIDR: "10.0.0.0/8", + MBits: 50, + ReservedPorts: []int{8000}, + }, + }, + }, + } + + // Should fit one allocation + fit, err := AllocsFit(n, []*Allocation{a1}) + if err != nil { + t.Fatalf("err: %v", err) + } + if !fit { + t.Fatalf("Bad") + } + + // Should not fit second allocation + fit, err = AllocsFit(n, []*Allocation{a1, a1}) + if err != nil { + t.Fatalf("err: %v", err) + } + if fit { + t.Fatalf("Bad") + } +}