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  }