123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- package genericresource
- import (
- "fmt"
- "github.com/docker/swarmkit/api"
- )
- // Claim assigns GenericResources to a task by taking them from the
- // node's GenericResource list and storing them in the task's available list
- func Claim(nodeAvailableResources, taskAssigned *[]*api.GenericResource,
- taskReservations []*api.GenericResource) error {
- var resSelected []*api.GenericResource
- for _, res := range taskReservations {
- tr := res.GetDiscreteResourceSpec()
- if tr == nil {
- return fmt.Errorf("task should only hold Discrete type")
- }
- // Select the resources
- nrs, err := selectNodeResources(*nodeAvailableResources, tr)
- if err != nil {
- return err
- }
- resSelected = append(resSelected, nrs...)
- }
- ClaimResources(nodeAvailableResources, taskAssigned, resSelected)
- return nil
- }
- // ClaimResources adds the specified resources to the task's list
- // and removes them from the node's generic resource list
- func ClaimResources(nodeAvailableResources, taskAssigned *[]*api.GenericResource,
- resSelected []*api.GenericResource) {
- *taskAssigned = append(*taskAssigned, resSelected...)
- ConsumeNodeResources(nodeAvailableResources, resSelected)
- }
- func selectNodeResources(nodeRes []*api.GenericResource,
- tr *api.DiscreteGenericResource) ([]*api.GenericResource, error) {
- var nrs []*api.GenericResource
- for _, res := range nodeRes {
- if Kind(res) != tr.Kind {
- continue
- }
- switch nr := res.Resource.(type) {
- case *api.GenericResource_DiscreteResourceSpec:
- if nr.DiscreteResourceSpec.Value >= tr.Value && tr.Value != 0 {
- nrs = append(nrs, NewDiscrete(tr.Kind, tr.Value))
- }
- return nrs, nil
- case *api.GenericResource_NamedResourceSpec:
- nrs = append(nrs, res.Copy())
- if int64(len(nrs)) == tr.Value {
- return nrs, nil
- }
- }
- }
- if len(nrs) == 0 {
- return nil, fmt.Errorf("not enough resources available for task reservations: %+v", tr)
- }
- return nrs, nil
- }
- // Reclaim adds the resources taken by the task to the node's store
- func Reclaim(nodeAvailableResources *[]*api.GenericResource, taskAssigned, nodeRes []*api.GenericResource) error {
- err := reclaimResources(nodeAvailableResources, taskAssigned)
- if err != nil {
- return err
- }
- sanitize(nodeRes, nodeAvailableResources)
- return nil
- }
- func reclaimResources(nodeAvailableResources *[]*api.GenericResource, taskAssigned []*api.GenericResource) error {
- // The node could have been updated
- if nodeAvailableResources == nil {
- return fmt.Errorf("node no longer has any resources")
- }
- for _, res := range taskAssigned {
- switch tr := res.Resource.(type) {
- case *api.GenericResource_DiscreteResourceSpec:
- nrs := GetResource(tr.DiscreteResourceSpec.Kind, *nodeAvailableResources)
- // If the resource went down to 0 it's no longer in the
- // available list
- if len(nrs) == 0 {
- *nodeAvailableResources = append(*nodeAvailableResources, res.Copy())
- }
- if len(nrs) != 1 {
- continue // Type change
- }
- nr := nrs[0].GetDiscreteResourceSpec()
- if nr == nil {
- continue // Type change
- }
- nr.Value += tr.DiscreteResourceSpec.Value
- case *api.GenericResource_NamedResourceSpec:
- *nodeAvailableResources = append(*nodeAvailableResources, res.Copy())
- }
- }
- return nil
- }
- // sanitize checks that nodeAvailableResources does not add resources unknown
- // to the nodeSpec (nodeRes) or goes over the integer bound specified
- // by the spec.
- // Note this is because the user is able to update a node's resources
- func sanitize(nodeRes []*api.GenericResource, nodeAvailableResources *[]*api.GenericResource) {
- // - We add the sanitized resources at the end, after
- // having removed the elements from the list
- // - When a set changes to a Discrete we also need
- // to make sure that we don't add the Discrete multiple
- // time hence, the need of a map to remember that
- var sanitized []*api.GenericResource
- kindSanitized := make(map[string]struct{})
- w := 0
- for _, na := range *nodeAvailableResources {
- ok, nrs := sanitizeResource(nodeRes, na)
- if !ok {
- if _, ok = kindSanitized[Kind(na)]; ok {
- continue
- }
- kindSanitized[Kind(na)] = struct{}{}
- sanitized = append(sanitized, nrs...)
- continue
- }
- (*nodeAvailableResources)[w] = na
- w++
- }
- *nodeAvailableResources = (*nodeAvailableResources)[:w]
- *nodeAvailableResources = append(*nodeAvailableResources, sanitized...)
- }
- // Returns true if the element is in nodeRes and "sane"
- // Returns false if the element isn't in nodeRes and "sane" and the element(s) that should be replacing it
- func sanitizeResource(nodeRes []*api.GenericResource, res *api.GenericResource) (ok bool, nrs []*api.GenericResource) {
- switch na := res.Resource.(type) {
- case *api.GenericResource_DiscreteResourceSpec:
- nrs := GetResource(na.DiscreteResourceSpec.Kind, nodeRes)
- // Type change or removed: reset
- if len(nrs) != 1 {
- return false, nrs
- }
- // Type change: reset
- nr := nrs[0].GetDiscreteResourceSpec()
- if nr == nil {
- return false, nrs
- }
- // Amount change: reset
- if na.DiscreteResourceSpec.Value > nr.Value {
- return false, nrs
- }
- case *api.GenericResource_NamedResourceSpec:
- nrs := GetResource(na.NamedResourceSpec.Kind, nodeRes)
- // Type change
- if len(nrs) == 0 {
- return false, nrs
- }
- for _, nr := range nrs {
- // Type change: reset
- if nr.GetDiscreteResourceSpec() != nil {
- return false, nrs
- }
- if na.NamedResourceSpec.Value == nr.GetNamedResourceSpec().Value {
- return true, nil
- }
- }
- // Removed
- return false, nil
- }
- return true, nil
- }
|