agones.dev/agones@v1.54.0/pkg/gameserverallocations/allocation_cache_test.go (about)

     1  // Copyright 2021 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  	"context"
    19  	"fmt"
    20  	"testing"
    21  	"time"
    22  
    23  	agonesv1 "agones.dev/agones/pkg/apis/agones/v1"
    24  	allocationv1 "agones.dev/agones/pkg/apis/allocation/v1"
    25  	"agones.dev/agones/pkg/gameservers"
    26  	agtesting "agones.dev/agones/pkg/testing"
    27  	"agones.dev/agones/pkg/util/runtime"
    28  	"github.com/heptiolabs/healthcheck"
    29  	"github.com/sirupsen/logrus"
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	k8sruntime "k8s.io/apimachinery/pkg/runtime"
    34  	"k8s.io/apimachinery/pkg/util/wait"
    35  	"k8s.io/apimachinery/pkg/watch"
    36  	k8stesting "k8s.io/client-go/testing"
    37  )
    38  
    39  func TestAllocationCacheListSortedGameServers(t *testing.T) {
    40  	t.Parallel()
    41  	runtime.FeatureTestMutex.Lock()
    42  	defer runtime.FeatureTestMutex.Unlock()
    43  
    44  	gs1 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs1", Namespace: defaultNs, UID: "1"},
    45  		Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady}}
    46  	gs2 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs2", Namespace: defaultNs, UID: "2"},
    47  		Status: agonesv1.GameServerStatus{NodeName: "node2", State: agonesv1.GameServerStateReady}}
    48  	gs3 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs3", Namespace: defaultNs, UID: "3"},
    49  		Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateAllocated}}
    50  	gs4 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs4", Namespace: defaultNs, UID: "4"},
    51  		Status: agonesv1.GameServerStatus{NodeName: "node2", State: agonesv1.GameServerStateReady}}
    52  	gs5 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs5", Namespace: defaultNs, UID: "5"},
    53  		Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady,
    54  			Players: &agonesv1.PlayerStatus{
    55  				Count:    0,
    56  				Capacity: 10,
    57  			},
    58  			Counters: map[string]agonesv1.CounterStatus{
    59  				"players": {
    60  					Count:    4,
    61  					Capacity: 40, // Available Capacity == 36
    62  				},
    63  			}},
    64  	}
    65  	gs6 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs6", Namespace: defaultNs, UID: "6"},
    66  		Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady, Players: &agonesv1.PlayerStatus{
    67  			Count:    2,
    68  			Capacity: 10,
    69  		},
    70  			Counters: map[string]agonesv1.CounterStatus{
    71  				"players": {
    72  					Count:    14,
    73  					Capacity: 40, // Available Capacity == 26
    74  				},
    75  			}},
    76  	}
    77  
    78  	fixtures := map[string]struct {
    79  		list     []agonesv1.GameServer
    80  		test     func(*testing.T, []*agonesv1.GameServer)
    81  		features string
    82  		gsa      *allocationv1.GameServerAllocation
    83  	}{
    84  		"allocated first (StateAllocationFilter)": {
    85  			list: []agonesv1.GameServer{gs1, gs2, gs3},
    86  			test: func(t *testing.T, list []*agonesv1.GameServer) {
    87  				assert.Equal(t, []*agonesv1.GameServer{&gs3, &gs1, &gs2}, list)
    88  			},
    89  		},
    90  		"nil player status (PlayerAllocationFilter)": {
    91  			list:     []agonesv1.GameServer{gs1, gs2, gs4},
    92  			features: fmt.Sprintf("%s=true", runtime.FeaturePlayerAllocationFilter),
    93  			test: func(t *testing.T, list []*agonesv1.GameServer) {
    94  				require.Len(t, list, 3)
    95  				// first two items can come in any order
    96  				assert.ElementsMatchf(t, []*agonesv1.GameServer{&gs2, &gs4}, list[:2], "GameServer Names")
    97  				assert.Equal(t, &gs1, list[2])
    98  			},
    99  		},
   100  		"least player capacity (PlayerAllocationFilter)": {
   101  			list:     []agonesv1.GameServer{gs5, gs6},
   102  			features: fmt.Sprintf("%s=true", runtime.FeaturePlayerAllocationFilter),
   103  			test: func(t *testing.T, list []*agonesv1.GameServer) {
   104  				assert.Equal(t, []*agonesv1.GameServer{&gs6, &gs5}, list)
   105  			},
   106  		},
   107  		"list ready": {
   108  			// node1: 1 ready, node2: 2 ready
   109  			list: []agonesv1.GameServer{gs1, gs2, gs4},
   110  			test: func(t *testing.T, list []*agonesv1.GameServer) {
   111  				assert.Len(t, list, 3)
   112  				// first two items can come in any order
   113  				assert.ElementsMatchf(t, []*agonesv1.GameServer{&gs2, &gs4}, list[:2], "GameServer Names")
   114  				assert.Equal(t, &gs1, list[2])
   115  			},
   116  		},
   117  		"lexicographical (node name)": {
   118  			list: []agonesv1.GameServer{gs2, gs1},
   119  			test: func(t *testing.T, list []*agonesv1.GameServer) {
   120  				assert.Len(t, list, 2)
   121  				if !assert.Equal(t, []*agonesv1.GameServer{&gs1, &gs2}, list) {
   122  					for _, gs := range list {
   123  						logrus.WithField("name", gs.Name).Info("game server")
   124  					}
   125  				}
   126  			},
   127  		},
   128  		"counters Descending": {
   129  			list:     []agonesv1.GameServer{gs1, gs2, gs3, gs4, gs5, gs6},
   130  			features: fmt.Sprintf("%s=true", runtime.FeatureCountsAndLists),
   131  			gsa: &allocationv1.GameServerAllocation{
   132  				Spec: allocationv1.GameServerAllocationSpec{
   133  					Priorities: []agonesv1.Priority{
   134  						{
   135  							Type:  "Counter",
   136  							Key:   "players",
   137  							Order: "Descending",
   138  						},
   139  					},
   140  				},
   141  			},
   142  			test: func(t *testing.T, list []*agonesv1.GameServer) {
   143  				assert.Len(t, list, 6)
   144  				if !assert.Equal(t, []*agonesv1.GameServer{&gs3, &gs5, &gs6, &gs1, &gs2, &gs4}, list) {
   145  					for _, gs := range list {
   146  						logrus.WithField("game", gs.Name).Info("game server")
   147  					}
   148  				}
   149  			},
   150  		},
   151  		"counters Ascending": {
   152  			list:     []agonesv1.GameServer{gs1, gs2, gs3, gs4, gs5, gs6},
   153  			features: fmt.Sprintf("%s=true", runtime.FeatureCountsAndLists),
   154  			gsa: &allocationv1.GameServerAllocation{
   155  				Spec: allocationv1.GameServerAllocationSpec{
   156  					Priorities: []agonesv1.Priority{
   157  						{
   158  							Type:  "Counter",
   159  							Key:   "players",
   160  							Order: "Ascending",
   161  						},
   162  					},
   163  				},
   164  			},
   165  			test: func(t *testing.T, list []*agonesv1.GameServer) {
   166  				assert.Len(t, list, 6)
   167  				if !assert.Equal(t, []*agonesv1.GameServer{&gs3, &gs6, &gs5, &gs1, &gs2, &gs4}, list) {
   168  					for _, gs := range list {
   169  						logrus.WithField("game", gs.Name).Info("game server")
   170  					}
   171  				}
   172  			},
   173  		},
   174  	}
   175  
   176  	for k, v := range fixtures {
   177  		t.Run(k, func(t *testing.T) {
   178  			// deliberately not resetting the Feature state, to catch any possible unknown regressions with the
   179  			// new feature flags
   180  			if v.features != "" {
   181  				require.NoError(t, runtime.ParseFeatures(v.features))
   182  			}
   183  
   184  			cache, m := newFakeAllocationCache()
   185  
   186  			gsList := v.list
   187  
   188  			m.AgonesClient.AddReactor("list", "gameservers", func(_ k8stesting.Action) (bool, k8sruntime.Object, error) {
   189  				return true, &agonesv1.GameServerList{Items: gsList}, nil
   190  			})
   191  
   192  			ctx, cancel := agtesting.StartInformers(m, cache.gameServerSynced)
   193  			defer cancel()
   194  
   195  			// This call initializes the cache
   196  			err := cache.syncCache()
   197  			assert.Nil(t, err)
   198  
   199  			err = cache.counter.Run(ctx, 0)
   200  			assert.Nil(t, err)
   201  
   202  			list := cache.ListSortedGameServers(v.gsa)
   203  
   204  			v.test(t, list)
   205  		})
   206  	}
   207  }
   208  
   209  func TestListSortedGameServersPriorities(t *testing.T) {
   210  	t.Parallel()
   211  	runtime.FeatureTestMutex.Lock()
   212  	defer runtime.FeatureTestMutex.Unlock()
   213  	assert.NoError(t, runtime.ParseFeatures(string(runtime.FeatureCountsAndLists)+"=true"))
   214  
   215  	gs1 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs1", Namespace: defaultNs, UID: "1"},
   216  		Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady,
   217  			Lists: map[string]agonesv1.ListStatus{
   218  				"players": {
   219  					Values:   []string{"player1"},
   220  					Capacity: 100, // Available Capacity == 99
   221  				},
   222  				"layers": {
   223  					Values:   []string{"layer1", "layer2", "layer3"},
   224  					Capacity: 100, // Available Capacity == 97
   225  				}}}}
   226  	gs2 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs2", Namespace: defaultNs, UID: "2"},
   227  		Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady,
   228  			Lists: map[string]agonesv1.ListStatus{
   229  				"players": {
   230  					Values:   []string{},
   231  					Capacity: 100, // Available Capacity == 100
   232  				},
   233  			},
   234  			Counters: map[string]agonesv1.CounterStatus{
   235  				"assets": {
   236  					Count:    101,
   237  					Capacity: 1000, // Available Capacity = 899
   238  				},
   239  			}}}
   240  	gs3 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs3", Namespace: defaultNs, UID: "3"},
   241  		Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady,
   242  			Lists: map[string]agonesv1.ListStatus{
   243  				"players": {
   244  					Values:   []string{"player2", "player3"},
   245  					Capacity: 100, // Available Capacity == 98
   246  				}},
   247  			Counters: map[string]agonesv1.CounterStatus{
   248  				"sessions": {
   249  					Count:    9,
   250  					Capacity: 1000, // Available Capacity == 991
   251  				},
   252  				"assets": {
   253  					Count:    100,
   254  					Capacity: 1000, // Available Capacity == 900
   255  				},
   256  			}}}
   257  	gs4 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs4", Namespace: defaultNs, UID: "4"},
   258  		Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady,
   259  			Counters: map[string]agonesv1.CounterStatus{
   260  				"sessions": {
   261  					Count:    99,
   262  					Capacity: 1000, // Available Capacity == 901
   263  				},
   264  			},
   265  			Lists: map[string]agonesv1.ListStatus{
   266  				"players": {
   267  					Values:   []string{"player4"},
   268  					Capacity: 100, // Available Capacity == 99
   269  				},
   270  				"layers": {
   271  					Values:   []string{"layer4, layer5"},
   272  					Capacity: 100, // Available Capacity == 98
   273  				}}}}
   274  	gs5 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs5", Namespace: defaultNs, UID: "5"},
   275  		Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady,
   276  			Counters: map[string]agonesv1.CounterStatus{
   277  				"sessions": {
   278  					Count:    9,
   279  					Capacity: 1000, // Available Capacity == 991
   280  				},
   281  				"assets": {
   282  					Count:    99,
   283  					Capacity: 1000, // Available Capacity == 901
   284  				},
   285  			},
   286  			Lists: map[string]agonesv1.ListStatus{
   287  				"layers": {
   288  					Values:   []string{},
   289  					Capacity: 100, // Available Capacity == 100
   290  				}}}}
   291  	gs6 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs6", Namespace: defaultNs, UID: "6"},
   292  		Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady,
   293  			Counters: map[string]agonesv1.CounterStatus{
   294  				"sessions": {
   295  					Count:    999,
   296  					Capacity: 1000, // Available Capacity == 1
   297  				},
   298  			}}}
   299  
   300  	testScenarios := map[string]struct {
   301  		list []agonesv1.GameServer
   302  		gsa  *allocationv1.GameServerAllocation
   303  		want []*agonesv1.GameServer
   304  	}{
   305  		"Sort by one Priority Counter Ascending": {
   306  			list: []agonesv1.GameServer{gs4, gs5, gs6},
   307  			gsa: &allocationv1.GameServerAllocation{
   308  				Spec: allocationv1.GameServerAllocationSpec{
   309  					Priorities: []agonesv1.Priority{
   310  						{
   311  							Type:  "Counter",
   312  							Key:   "sessions",
   313  							Order: "Ascending",
   314  						},
   315  					},
   316  				},
   317  			},
   318  			want: []*agonesv1.GameServer{&gs6, &gs4, &gs5},
   319  		},
   320  		"Sort by one Priority Counter Descending": {
   321  			list: []agonesv1.GameServer{gs4, gs5, gs6},
   322  			gsa: &allocationv1.GameServerAllocation{
   323  				Spec: allocationv1.GameServerAllocationSpec{
   324  					Priorities: []agonesv1.Priority{
   325  						{
   326  							Type:  "Counter",
   327  							Key:   "sessions",
   328  							Order: "Descending",
   329  						},
   330  					},
   331  				},
   332  			},
   333  			want: []*agonesv1.GameServer{&gs5, &gs4, &gs6},
   334  		},
   335  		"Sort by two Priority Counter Ascending and Ascending": {
   336  			list: []agonesv1.GameServer{gs3, gs5, gs6, gs4, gs1, gs2},
   337  			gsa: &allocationv1.GameServerAllocation{
   338  				Spec: allocationv1.GameServerAllocationSpec{
   339  					Priorities: []agonesv1.Priority{
   340  						{
   341  							Type:  "Counter",
   342  							Key:   "sessions",
   343  							Order: "Ascending",
   344  						},
   345  						{
   346  							Type:  "Counter",
   347  							Key:   "assets",
   348  							Order: "Ascending",
   349  						},
   350  					},
   351  				},
   352  			},
   353  			want: []*agonesv1.GameServer{&gs6, &gs4, &gs3, &gs5, &gs2, &gs1},
   354  		},
   355  		"Sort by two Priority Counter Ascending and Descending": {
   356  			list: []agonesv1.GameServer{gs3, gs5, gs6, gs4, gs1, gs2},
   357  			gsa: &allocationv1.GameServerAllocation{
   358  				Spec: allocationv1.GameServerAllocationSpec{
   359  					Priorities: []agonesv1.Priority{
   360  						{
   361  							Type:  "Counter",
   362  							Key:   "sessions",
   363  							Order: "Ascending",
   364  						},
   365  						{
   366  							Type:  "Counter",
   367  							Key:   "assets",
   368  							Order: "Descending",
   369  						},
   370  					},
   371  				},
   372  			},
   373  			want: []*agonesv1.GameServer{&gs6, &gs4, &gs5, &gs3, &gs2, &gs1},
   374  		},
   375  		"Sort by one Priority Counter game server without Counter": {
   376  			list: []agonesv1.GameServer{gs1, gs5, gs6, gs4},
   377  			gsa: &allocationv1.GameServerAllocation{
   378  				Spec: allocationv1.GameServerAllocationSpec{
   379  					Priorities: []agonesv1.Priority{
   380  						{
   381  							Type:  "Counter",
   382  							Key:   "sessions",
   383  							Order: "Ascending",
   384  						},
   385  					},
   386  				},
   387  			},
   388  			want: []*agonesv1.GameServer{&gs6, &gs4, &gs5, &gs1},
   389  		},
   390  		"Sort by one Priority List Ascending": {
   391  			list: []agonesv1.GameServer{gs3, gs2, gs1},
   392  			gsa: &allocationv1.GameServerAllocation{
   393  				Spec: allocationv1.GameServerAllocationSpec{
   394  					Priorities: []agonesv1.Priority{
   395  						{
   396  							Type:  "List",
   397  							Key:   "players",
   398  							Order: "Ascending",
   399  						},
   400  					},
   401  				},
   402  			},
   403  			want: []*agonesv1.GameServer{&gs3, &gs1, &gs2},
   404  		},
   405  		"Sort by one Priority List Descending": {
   406  			list: []agonesv1.GameServer{gs3, gs2, gs1},
   407  			gsa: &allocationv1.GameServerAllocation{
   408  				Spec: allocationv1.GameServerAllocationSpec{
   409  					Priorities: []agonesv1.Priority{
   410  						{
   411  							Type:  "List",
   412  							Key:   "players",
   413  							Order: "Descending",
   414  						},
   415  					},
   416  				},
   417  			},
   418  			want: []*agonesv1.GameServer{&gs2, &gs1, &gs3},
   419  		},
   420  		"Sort by two Priority List Descending and Ascending": {
   421  			list: []agonesv1.GameServer{gs1, gs2, gs3, gs4},
   422  			gsa: &allocationv1.GameServerAllocation{
   423  				Spec: allocationv1.GameServerAllocationSpec{
   424  					Priorities: []agonesv1.Priority{
   425  						{
   426  							Type:  "List",
   427  							Key:   "players",
   428  							Order: "Descending",
   429  						},
   430  						{
   431  							Type:  "List",
   432  							Key:   "layers",
   433  							Order: "Ascending",
   434  						},
   435  					},
   436  				},
   437  			},
   438  			want: []*agonesv1.GameServer{&gs2, &gs1, &gs4, &gs3},
   439  		},
   440  		"Sort by two Priority List Descending and Descending": {
   441  			list: []agonesv1.GameServer{gs6, gs5, gs4, gs3, gs2, gs1},
   442  			gsa: &allocationv1.GameServerAllocation{
   443  				Spec: allocationv1.GameServerAllocationSpec{
   444  					Priorities: []agonesv1.Priority{
   445  						{
   446  							Type:  "List",
   447  							Key:   "players",
   448  							Order: "Descending",
   449  						},
   450  						{
   451  							Type:  "List",
   452  							Key:   "layers",
   453  							Order: "Descending",
   454  						},
   455  					},
   456  				},
   457  			},
   458  			want: []*agonesv1.GameServer{&gs2, &gs4, &gs1, &gs3, &gs5, &gs6},
   459  		},
   460  		"Sort lexigraphically as no game server has the priority": {
   461  			list: []agonesv1.GameServer{gs6, gs5, gs4, gs3, gs2, gs1},
   462  			gsa: &allocationv1.GameServerAllocation{
   463  				Spec: allocationv1.GameServerAllocationSpec{
   464  					Priorities: []agonesv1.Priority{
   465  						{
   466  							Type:  "Counter",
   467  							Key:   "sayers",
   468  							Order: "Ascending",
   469  						},
   470  					},
   471  				},
   472  			},
   473  			want: []*agonesv1.GameServer{&gs1, &gs2, &gs3, &gs4, &gs5, &gs6},
   474  		},
   475  	}
   476  
   477  	for testName, testScenario := range testScenarios {
   478  		t.Run(testName, func(t *testing.T) {
   479  
   480  			cache, m := newFakeAllocationCache()
   481  
   482  			m.AgonesClient.AddReactor("list", "gameservers", func(_ k8stesting.Action) (bool, k8sruntime.Object, error) {
   483  				return true, &agonesv1.GameServerList{Items: testScenario.list}, nil
   484  			})
   485  
   486  			ctx, cancel := agtesting.StartInformers(m, cache.gameServerSynced)
   487  			defer cancel()
   488  
   489  			// This call initializes the cache
   490  			err := cache.syncCache()
   491  			assert.Nil(t, err)
   492  
   493  			err = cache.counter.Run(ctx, 0)
   494  			assert.Nil(t, err)
   495  
   496  			got := cache.ListSortedGameServersPriorities(testScenario.gsa)
   497  
   498  			assert.Equal(t, testScenario.want, got)
   499  		})
   500  	}
   501  }
   502  
   503  func TestAllocatorRunCacheSync(t *testing.T) {
   504  	t.Parallel()
   505  	cache, m := newFakeAllocationCache()
   506  	gsWatch := watch.NewFake()
   507  
   508  	m.AgonesClient.AddWatchReactor("gameservers", k8stesting.DefaultWatchReactor(gsWatch, nil))
   509  
   510  	ctx, cancel := agtesting.StartInformers(m, cache.gameServerSynced)
   511  	defer cancel()
   512  
   513  	assertCacheEntries := func(expected int) {
   514  		count := 0
   515  		err := wait.PollUntilContextTimeout(context.Background(), time.Second, 5*time.Second, true, func(_ context.Context) (done bool, err error) {
   516  			count = 0
   517  			cache.cache.Range(func(_ string, _ *agonesv1.GameServer) bool {
   518  				count++
   519  				return true
   520  			})
   521  
   522  			return count == expected, nil
   523  		})
   524  
   525  		assert.NoError(t, err, fmt.Sprintf("Should be %d values", expected))
   526  	}
   527  
   528  	go func() {
   529  		err := cache.Run(ctx)
   530  		assert.Nil(t, err)
   531  	}()
   532  
   533  	gs := agonesv1.GameServer{
   534  		ObjectMeta: metav1.ObjectMeta{Name: "gs1", Namespace: "default", ResourceVersion: "1"},
   535  		Status:     agonesv1.GameServerStatus{State: agonesv1.GameServerStateStarting},
   536  	}
   537  
   538  	logrus.Info("adding ready game server")
   539  	gsWatch.Add(gs.DeepCopy())
   540  
   541  	assertCacheEntries(0)
   542  
   543  	gs.Status.State = agonesv1.GameServerStateReady
   544  	gs.ObjectMeta.ResourceVersion = "2"
   545  	gsWatch.Modify(gs.DeepCopy())
   546  
   547  	assertCacheEntries(1)
   548  
   549  	// try again, should be no change
   550  	gs.Status.State = agonesv1.GameServerStateReady
   551  	gs.ObjectMeta.ResourceVersion = "3"
   552  	gsWatch.Modify(gs.DeepCopy())
   553  
   554  	assertCacheEntries(1)
   555  
   556  	// now move it to Shutdown
   557  	gs.Status.State = agonesv1.GameServerStateShutdown
   558  	gs.ObjectMeta.ResourceVersion = "4"
   559  	gsWatch.Modify(gs.DeepCopy())
   560  	assertCacheEntries(0)
   561  
   562  	// add it back in as Allocated
   563  	gs.Status.State = agonesv1.GameServerStateAllocated
   564  	gs.ObjectMeta.ResourceVersion = "5"
   565  	gsWatch.Modify(gs.DeepCopy())
   566  	assertCacheEntries(1)
   567  
   568  	// now move it to Shutdown
   569  	gs.Status.State = agonesv1.GameServerStateShutdown
   570  	gs.ObjectMeta.ResourceVersion = "6"
   571  	gsWatch.Modify(gs.DeepCopy())
   572  	assertCacheEntries(0)
   573  
   574  	// do not add back in with stale resource version
   575  	gs.Status.State = agonesv1.GameServerStateAllocated
   576  	gs.ObjectMeta.ResourceVersion = "6"
   577  	gsWatch.Modify(gs.DeepCopy())
   578  	assertCacheEntries(0)
   579  
   580  	// add back in ready gameserver
   581  	gs.Status.State = agonesv1.GameServerStateReady
   582  	gs.ObjectMeta.ResourceVersion = "7"
   583  	gsWatch.Modify(gs.DeepCopy())
   584  	assertCacheEntries(1)
   585  
   586  	// update with deletion timestamp
   587  	n := metav1.Now()
   588  	deletedCopy := gs.DeepCopy()
   589  	deletedCopy.ObjectMeta.DeletionTimestamp = &n
   590  	deletedCopy.ObjectMeta.ResourceVersion = "8"
   591  	gsWatch.Modify(deletedCopy.DeepCopy())
   592  	assertCacheEntries(0)
   593  
   594  	// add back in ready gameserver
   595  	gs.Status.State = agonesv1.GameServerStateReady
   596  	deletedCopy.ObjectMeta.ResourceVersion = "9"
   597  	gsWatch.Modify(gs.DeepCopy())
   598  	assertCacheEntries(1)
   599  
   600  	// now actually delete it
   601  	gsWatch.Delete(gs.DeepCopy())
   602  	assertCacheEntries(0)
   603  }
   604  
   605  func newFakeAllocationCache() (*AllocationCache, agtesting.Mocks) {
   606  	m := agtesting.NewMocks()
   607  	cache := NewAllocationCache(m.AgonesInformerFactory.Agones().V1().GameServers(), gameservers.NewPerNodeCounter(m.KubeInformerFactory, m.AgonesInformerFactory), healthcheck.NewHandler())
   608  	return cache, m
   609  }