agones.dev/agones@v1.53.0/pkg/gameserverallocations/find.go (about) 1 // Copyright 2018 Google LLC All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package gameserverallocations 16 17 import ( 18 "math/rand" 19 20 "agones.dev/agones/pkg/apis" 21 agonesv1 "agones.dev/agones/pkg/apis/agones/v1" 22 allocationv1 "agones.dev/agones/pkg/apis/allocation/v1" 23 "agones.dev/agones/pkg/util/runtime" 24 "github.com/pkg/errors" 25 ) 26 27 // findGameServerForAllocation finds an optimal gameserver, given the 28 // set of preferred and required selectors on the GameServerAllocation. This also returns the index 29 // that the gameserver was found at in `list`, in case you want to remove it from the list 30 // Packed: will search list from start to finish 31 // Distributed: will search in a random order through the list 32 // It is assumed that all gameservers passed in, are Ready and not being deleted, and are sorted in Packed priority order 33 func findGameServerForAllocation(gsa *allocationv1.GameServerAllocation, list []*agonesv1.GameServer) (*agonesv1.GameServer, int, error) { 34 type result struct { 35 gs *agonesv1.GameServer 36 index int 37 } 38 39 selectors := make([]*result, len(gsa.Spec.Selectors)) 40 41 var loop func(list []*agonesv1.GameServer, f func(i int, gs *agonesv1.GameServer)) 42 43 // packed is forward looping, distributed is random looping 44 switch gsa.Spec.Scheduling { 45 case apis.Packed: 46 loop = func(list []*agonesv1.GameServer, f func(i int, gs *agonesv1.GameServer)) { 47 for i, gs := range list { 48 f(i, gs) 49 } 50 } 51 case apis.Distributed: 52 // randomised looping - make a list of indices, and then randomise them 53 // as we don't want to change the order of the gameserver slice 54 if !runtime.FeatureEnabled(runtime.FeatureCountsAndLists) || len(gsa.Spec.Priorities) == 0 { 55 l := len(list) 56 indices := make([]int, l) 57 for i := 0; i < l; i++ { 58 indices[i] = i 59 } 60 rand.Shuffle(l, func(i, j int) { 61 indices[i], indices[j] = indices[j], indices[i] 62 }) 63 64 loop = func(list []*agonesv1.GameServer, f func(i int, gs *agonesv1.GameServer)) { 65 for _, i := range indices { 66 f(i, list[i]) 67 } 68 } 69 } else { 70 // For FeatureCountsAndLists we do not do randomized looping -- instead choose the game 71 // server based on the list of Priorities. (The order in which the game servers were sorted 72 // in ListSortedGameServersPriorities.) 73 loop = func(list []*agonesv1.GameServer, f func(i int, gs *agonesv1.GameServer)) { 74 for i, gs := range list { 75 f(i, gs) 76 } 77 } 78 } 79 default: 80 return nil, -1, errors.Errorf("scheduling strategy of '%s' is not supported", gsa.Spec.Scheduling) 81 } 82 83 loop(list, func(i int, gs *agonesv1.GameServer) { 84 // only search the same namespace 85 if gs.ObjectMeta.Namespace != gsa.ObjectMeta.Namespace { 86 return 87 } 88 89 for j, sel := range gsa.Spec.Selectors { 90 if selectors[j] == nil && sel.Matches(gs) { 91 selectors[j] = &result{gs: gs, index: i} 92 } 93 } 94 }) 95 96 for _, r := range selectors { 97 if r != nil { 98 return r.gs, r.index, nil 99 } 100 } 101 102 return nil, 0, ErrNoGameServer 103 }