agones.dev/agones@v1.53.0/pkg/gameserversets/gameserversets.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 gameserversets 16 17 import ( 18 "sort" 19 20 "agones.dev/agones/pkg/apis" 21 agonesv1 "agones.dev/agones/pkg/apis/agones/v1" 22 listerv1 "agones.dev/agones/pkg/client/listers/agones/v1" 23 "agones.dev/agones/pkg/gameservers" 24 "agones.dev/agones/pkg/util/logfields" 25 "agones.dev/agones/pkg/util/runtime" 26 "github.com/pkg/errors" 27 "github.com/sirupsen/logrus" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/labels" 30 ) 31 32 func loggerForGameServerSetKey(log *logrus.Entry, key string) *logrus.Entry { 33 return logfields.AugmentLogEntry(log, logfields.GameServerSetKey, key) 34 } 35 36 func loggerForGameServerSet(log *logrus.Entry, gsSet *agonesv1.GameServerSet) *logrus.Entry { 37 gsSetName := "NilGameServerSet" 38 if gsSet != nil { 39 gsSetName = gsSet.Namespace + "/" + gsSet.Name 40 } 41 return loggerForGameServerSetKey(log, gsSetName).WithField("gss", gsSet) 42 } 43 44 // SortGameServersByStrategy will sort by least full nodes when Packed, and newest first when Distributed 45 func SortGameServersByStrategy(strategy apis.SchedulingStrategy, list []*agonesv1.GameServer, counts map[string]gameservers.NodeCount, priorities []agonesv1.Priority) []*agonesv1.GameServer { 46 if strategy == apis.Packed { 47 return sortGameServersByPackedStrategy(list, counts, priorities) 48 } 49 return sortGameServersByDistributedStrategy(list, priorities) 50 } 51 52 // sortGameServersByPackedStrategy sorts the list of gameservers by which gameservers reside on the least full nodes 53 // Performs a tie-breaking sort if nodes are equally full on CountsAndLists Priorities. 54 func sortGameServersByPackedStrategy(list []*agonesv1.GameServer, count map[string]gameservers.NodeCount, priorities []agonesv1.Priority) []*agonesv1.GameServer { 55 sort.Slice(list, func(i, j int) bool { 56 a := list[i] 57 b := list[j] 58 59 // not scheduled yet/node deleted, put them first 60 ac, ok := count[a.Status.NodeName] 61 if !ok { 62 return true 63 } 64 65 bc, ok := count[b.Status.NodeName] 66 if !ok { 67 return false 68 } 69 70 if a.Status.NodeName == b.Status.NodeName { 71 if a.IsBeforeReady() && b.Status.State == agonesv1.GameServerStateReady { 72 return true 73 } 74 75 if b.IsBeforeReady() && a.Status.State == agonesv1.GameServerStateReady { 76 return false 77 } 78 } 79 80 // Check Node total count 81 acTotal, bcTotal := ac.Allocated+ac.Ready, bc.Allocated+bc.Ready 82 if acTotal < bcTotal { 83 return true 84 } 85 if acTotal > bcTotal { 86 return false 87 } 88 89 if a.Status.NodeName == b.Status.NodeName { 90 // See if Count and List priorities can be used as a tie-breaker within the node 91 if runtime.FeatureEnabled(runtime.FeatureCountsAndLists) { 92 if res := a.CompareCountAndListPriorities(priorities, b); res != nil { 93 return *res 94 } 95 } 96 97 // Sort lexicographically for a stable sort within the node 98 return a.GetObjectMeta().GetName() < b.GetObjectMeta().GetName() 99 } 100 // if both Nodes have the same count, one node is emptied first (packed scheduling behavior) 101 return a.Status.NodeName < b.Status.NodeName 102 }) 103 104 return list 105 } 106 107 // sortGameServersByDistributedStrategy sorts by newest gameservers first. 108 // If FeatureCountsAndLists is enabled, sort by Priority first, then tie-break with newest gameservers. 109 func sortGameServersByDistributedStrategy(list []*agonesv1.GameServer, priorities []agonesv1.Priority) []*agonesv1.GameServer { 110 sort.Slice(list, func(i, j int) bool { 111 a := list[i] 112 b := list[j] 113 114 if runtime.FeatureEnabled(runtime.FeatureCountsAndLists) { 115 if res := a.CompareCountAndListPriorities(priorities, b); res != nil { 116 return *res 117 } 118 } 119 120 return a.ObjectMeta.CreationTimestamp.Before(&b.ObjectMeta.CreationTimestamp) 121 }) 122 123 return list 124 } 125 126 // ListGameServersByGameServerSetOwner lists the GameServers for a given GameServerSet 127 func ListGameServersByGameServerSetOwner(gameServerLister listerv1.GameServerLister, 128 gsSet *agonesv1.GameServerSet) ([]*agonesv1.GameServer, error) { 129 list, err := gameServerLister.List(labels.SelectorFromSet(labels.Set{agonesv1.GameServerSetGameServerLabel: gsSet.ObjectMeta.Name})) 130 if err != nil { 131 return list, errors.Wrapf(err, "error listing gameservers for gameserverset %s", gsSet.ObjectMeta.Name) 132 } 133 134 var result []*agonesv1.GameServer 135 for _, gs := range list { 136 if metav1.IsControlledBy(gs, gsSet) { 137 result = append(result, gs) 138 } 139 } 140 141 return result, nil 142 }