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  }