agones.dev/agones@v1.54.0/test/e2e/gameserverallocation_test.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 e2e
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"sync"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/google/go-cmp/cmp"
    25  	"github.com/sirupsen/logrus"
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  	"k8s.io/apimachinery/pkg/api/errors"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/util/uuid"
    31  	"k8s.io/apimachinery/pkg/util/wait"
    32  
    33  	"agones.dev/agones/pkg/apis"
    34  	agonesv1 "agones.dev/agones/pkg/apis/agones/v1"
    35  	allocationv1 "agones.dev/agones/pkg/apis/allocation/v1"
    36  	multiclusterv1 "agones.dev/agones/pkg/apis/multicluster/v1"
    37  	"agones.dev/agones/pkg/util/runtime"
    38  	e2e "agones.dev/agones/test/e2e/framework"
    39  )
    40  
    41  func TestCreateFleetAndGameServerAllocate(t *testing.T) {
    42  	t.Parallel()
    43  
    44  	fixtures := []apis.SchedulingStrategy{apis.Packed, apis.Distributed}
    45  
    46  	for _, strategy := range fixtures {
    47  		t.Run(string(strategy), func(t *testing.T) {
    48  			t.Parallel()
    49  			ctx := context.Background()
    50  
    51  			fleets := framework.AgonesClient.AgonesV1().Fleets(framework.Namespace)
    52  			fleet := defaultFleet(framework.Namespace)
    53  			fleet.Spec.Scheduling = strategy
    54  			flt, err := fleets.Create(ctx, fleet, metav1.CreateOptions{})
    55  			if strategy != apis.Packed && framework.CloudProduct == "gke-autopilot" {
    56  				// test that Autopilot rejects anything but Packed and skip the rest of the test
    57  				assert.ErrorContains(t, err, "Invalid value")
    58  				return
    59  			}
    60  			if assert.NoError(t, err) {
    61  				defer fleets.Delete(ctx, flt.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint:errcheck
    62  			}
    63  
    64  			framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas))
    65  
    66  			gsaList := []*allocationv1.GameServerAllocation{
    67  				{Spec: allocationv1.GameServerAllocationSpec{
    68  					Scheduling: strategy,
    69  					Selectors:  []allocationv1.GameServerSelector{{LabelSelector: metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}}}},
    70  				},
    71  				{Spec: allocationv1.GameServerAllocationSpec{
    72  					Scheduling: strategy,
    73  					Required:   allocationv1.GameServerSelector{LabelSelector: metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}}},
    74  				},
    75  			}
    76  
    77  			for _, gsa := range gsaList {
    78  				gsa, err = framework.AgonesClient.AllocationV1().GameServerAllocations(fleet.ObjectMeta.Namespace).Create(ctx, gsa, metav1.CreateOptions{})
    79  				if assert.NoError(t, err) {
    80  					assert.Equal(t, string(allocationv1.GameServerAllocationAllocated), string(gsa.Status.State))
    81  				}
    82  			}
    83  		})
    84  	}
    85  }
    86  
    87  func TestCreateFleetAndGameServerStateFilterAllocation(t *testing.T) {
    88  	t.Parallel()
    89  
    90  	fleets := framework.AgonesClient.AgonesV1().Fleets(framework.Namespace)
    91  	fleet := defaultFleet(framework.Namespace)
    92  	ctx := context.Background()
    93  
    94  	flt, err := fleets.Create(ctx, fleet, metav1.CreateOptions{})
    95  	require.NoError(t, err)
    96  	defer fleets.Delete(ctx, flt.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint:errcheck
    97  
    98  	framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas))
    99  
   100  	fleetSelector := metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}
   101  	gsa := &allocationv1.GameServerAllocation{
   102  		Spec: allocationv1.GameServerAllocationSpec{
   103  			Selectors: []allocationv1.GameServerSelector{{LabelSelector: fleetSelector}},
   104  		}}
   105  
   106  	// standard allocation
   107  	gsa, err = framework.AgonesClient.AllocationV1().GameServerAllocations(fleet.ObjectMeta.Namespace).Create(ctx, gsa, metav1.CreateOptions{})
   108  	require.NoError(t, err)
   109  	assert.Equal(t, string(allocationv1.GameServerAllocationAllocated), string(gsa.Status.State))
   110  
   111  	gs1, err := framework.AgonesClient.AgonesV1().GameServers(fleet.ObjectMeta.Namespace).Get(ctx, gsa.Status.GameServerName, metav1.GetOptions{})
   112  	require.NoError(t, err)
   113  	assert.Equal(t, agonesv1.GameServerStateAllocated, gs1.Status.State)
   114  	assert.NotNil(t, gs1.ObjectMeta.Annotations["agones.dev/last-allocated"])
   115  
   116  	// now let's get it back again
   117  	gsa = gsa.DeepCopy()
   118  	allocated := agonesv1.GameServerStateAllocated
   119  	gsa.Spec.Selectors[0].GameServerState = &allocated
   120  
   121  	gsa, err = framework.AgonesClient.AllocationV1().GameServerAllocations(fleet.ObjectMeta.Namespace).Create(ctx, gsa, metav1.CreateOptions{})
   122  	require.NoError(t, err)
   123  	assert.Equal(t, string(allocationv1.GameServerAllocationAllocated), string(gsa.Status.State))
   124  	assert.Equal(t, gs1.ObjectMeta.Name, gsa.Status.GameServerName)
   125  
   126  	gs2, err := framework.AgonesClient.AgonesV1().GameServers(fleet.ObjectMeta.Namespace).Get(ctx, gsa.Status.GameServerName, metav1.GetOptions{})
   127  	require.NoError(t, err)
   128  	assert.Equal(t, agonesv1.GameServerStateAllocated, gs2.Status.State)
   129  
   130  	require.Equal(t, gs1.ObjectMeta.Name, gs2.ObjectMeta.Name)
   131  	require.NotEqual(t, gs1.ObjectMeta.ResourceVersion, gs2.ObjectMeta.ResourceVersion)
   132  	require.NotEqual(t, gs1.ObjectMeta.Annotations["agones.dev/last-allocated"], gs2.ObjectMeta.Annotations["agones.dev/last-allocated"])
   133  }
   134  
   135  func TestHighDensityGameServerFlow(t *testing.T) {
   136  	t.Parallel()
   137  	log := e2e.TestLogger(t)
   138  	ctx := context.Background()
   139  
   140  	fleets := framework.AgonesClient.AgonesV1().Fleets(framework.Namespace)
   141  	fleet := defaultFleet(framework.Namespace)
   142  	lockLabel := "agones.dev/sdk-available"
   143  	// to start they are all available
   144  	fleet.Spec.Template.ObjectMeta.Labels = map[string]string{lockLabel: "true"}
   145  
   146  	flt, err := fleets.Create(ctx, fleet, metav1.CreateOptions{})
   147  	require.NoError(t, err)
   148  	defer fleets.Delete(ctx, flt.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint:errcheck
   149  
   150  	framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas))
   151  
   152  	fleetSelector := metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}
   153  	allocatedSelector := fleetSelector.DeepCopy()
   154  
   155  	allocated := agonesv1.GameServerStateAllocated
   156  	allocatedSelector.MatchLabels[lockLabel] = "true"
   157  	gsa := &allocationv1.GameServerAllocation{
   158  		Spec: allocationv1.GameServerAllocationSpec{
   159  			MetaPatch: allocationv1.MetaPatch{Labels: map[string]string{lockLabel: "false"}},
   160  			Selectors: []allocationv1.GameServerSelector{
   161  				{LabelSelector: *allocatedSelector, GameServerState: &allocated},
   162  				{LabelSelector: fleetSelector},
   163  			},
   164  		}}
   165  
   166  	// standard allocation
   167  	result, err := framework.AgonesClient.AllocationV1().GameServerAllocations(fleet.ObjectMeta.Namespace).Create(ctx, gsa, metav1.CreateOptions{})
   168  	require.NoError(t, err)
   169  	require.Equal(t, string(allocationv1.GameServerAllocationAllocated), string(result.Status.State))
   170  
   171  	gs, err := framework.AgonesClient.AgonesV1().GameServers(fleet.ObjectMeta.Namespace).Get(ctx, result.Status.GameServerName, metav1.GetOptions{})
   172  	require.NoError(t, err)
   173  	require.Equal(t, allocated, gs.Status.State)
   174  
   175  	// set the label to being available again
   176  	_, err = framework.SendGameServerUDP(t, gs, "LABEL available true")
   177  	require.NoError(t, err)
   178  
   179  	// wait for the label to be applied!
   180  	require.Eventuallyf(t, func() bool {
   181  		gs, err := framework.AgonesClient.AgonesV1().GameServers(fleet.ObjectMeta.Namespace).Get(ctx, result.Status.GameServerName, metav1.GetOptions{})
   182  		require.NoError(t, err)
   183  		log.WithField("labels", gs.ObjectMeta.Labels).Info("checking labels")
   184  		return gs.ObjectMeta.Labels[lockLabel] == "true"
   185  	}, time.Minute, time.Second, "GameServer did not unlock")
   186  
   187  	// Run the same allocation again, we should get back the preferred item.
   188  	expected := result.Status.GameServerName
   189  
   190  	// we will run this as an Eventually, as caches are eventually consistent
   191  	require.Eventuallyf(t, func() bool {
   192  		result, err = framework.AgonesClient.AllocationV1().GameServerAllocations(fleet.ObjectMeta.Namespace).Create(ctx, gsa, metav1.CreateOptions{})
   193  		require.NoError(t, err)
   194  		require.Equal(t, string(allocationv1.GameServerAllocationAllocated), string(result.Status.State))
   195  
   196  		if expected != result.Status.GameServerName {
   197  			log.WithField("expected", expected).WithField("gsa", result).Info("Re-allocation attempt failed. Retrying.")
   198  			return false
   199  		}
   200  
   201  		return true
   202  	}, time.Minute, time.Second, "Could not re-allocation")
   203  }
   204  
   205  func TestCreateFleetAndGameServerPlayerCapacityAllocation(t *testing.T) {
   206  	if !(runtime.FeatureEnabled(runtime.FeaturePlayerAllocationFilter)) {
   207  		t.SkipNow()
   208  	}
   209  	t.Parallel()
   210  
   211  	fleets := framework.AgonesClient.AgonesV1().Fleets(framework.Namespace)
   212  	fleet := defaultFleet(framework.Namespace)
   213  	fleet.Spec.Template.Spec.Players = &agonesv1.PlayersSpec{InitialCapacity: 10}
   214  	ctx := context.Background()
   215  
   216  	flt, err := fleets.Create(ctx, fleet, metav1.CreateOptions{})
   217  	require.NoError(t, err)
   218  	defer fleets.Delete(ctx, flt.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint:errcheck
   219  
   220  	framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas))
   221  
   222  	fleetSelector := metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}
   223  	allocated := agonesv1.GameServerStateAllocated
   224  	gsa := &allocationv1.GameServerAllocation{
   225  		Spec: allocationv1.GameServerAllocationSpec{
   226  			Selectors: []allocationv1.GameServerSelector{
   227  				{
   228  					LabelSelector:   fleetSelector,
   229  					GameServerState: &allocated,
   230  					Players: &allocationv1.PlayerSelector{
   231  						MinAvailable: 1,
   232  						MaxAvailable: 99,
   233  					},
   234  				},
   235  				{LabelSelector: fleetSelector, Players: &allocationv1.PlayerSelector{MinAvailable: 5, MaxAvailable: 10}},
   236  			},
   237  		}}
   238  
   239  	// first try should give me a Ready->Allocated server
   240  	gsa, err = framework.AgonesClient.AllocationV1().GameServerAllocations(fleet.ObjectMeta.Namespace).Create(ctx, gsa.DeepCopy(), metav1.CreateOptions{})
   241  	require.NoError(t, err)
   242  	assert.Equal(t, string(allocationv1.GameServerAllocationAllocated), string(gsa.Status.State))
   243  
   244  	gs1, err := framework.AgonesClient.AgonesV1().GameServers(fleet.ObjectMeta.Namespace).Get(ctx, gsa.Status.GameServerName, metav1.GetOptions{})
   245  	require.NoError(t, err)
   246  	assert.Equal(t, agonesv1.GameServerStateAllocated, gs1.Status.State)
   247  	assert.NotNil(t, gs1.ObjectMeta.Annotations["agones.dev/last-allocated"])
   248  
   249  	// second try should give me the same allocated server
   250  	gsa, err = framework.AgonesClient.AllocationV1().GameServerAllocations(fleet.ObjectMeta.Namespace).Create(ctx, gsa.DeepCopy(), metav1.CreateOptions{})
   251  	require.NoError(t, err)
   252  	assert.Equal(t, string(allocationv1.GameServerAllocationAllocated), string(gsa.Status.State))
   253  	assert.Equal(t, gs1.ObjectMeta.Name, gsa.Status.GameServerName)
   254  
   255  	gs2, err := framework.AgonesClient.AgonesV1().GameServers(fleet.ObjectMeta.Namespace).Get(ctx, gsa.Status.GameServerName, metav1.GetOptions{})
   256  	require.NoError(t, err)
   257  	assert.Equal(t, agonesv1.GameServerStateAllocated, gs2.Status.State)
   258  
   259  	require.Equal(t, gs1.ObjectMeta.Name, gs2.ObjectMeta.Name)
   260  	require.NotEqual(t, gs1.ObjectMeta.ResourceVersion, gs2.ObjectMeta.ResourceVersion)
   261  	require.NotEqual(t, gs1.ObjectMeta.Annotations["agones.dev/last-allocated"], gs2.ObjectMeta.Annotations["agones.dev/last-allocated"])
   262  }
   263  
   264  func TestCounterAndListGameServerAllocation(t *testing.T) {
   265  	if !runtime.FeatureEnabled(runtime.FeatureCountsAndLists) {
   266  		t.SkipNow()
   267  	}
   268  	t.Parallel()
   269  	ctx := context.Background()
   270  	client := framework.AgonesClient.AgonesV1()
   271  
   272  	flt := defaultFleet(framework.Namespace)
   273  	initialCounters := map[string]agonesv1.CounterStatus{
   274  		"games": {
   275  			Count:    2,
   276  			Capacity: 10,
   277  		},
   278  	}
   279  	initialLists := map[string]agonesv1.ListStatus{
   280  		"players": {
   281  			Values:   []string{"player0"},
   282  			Capacity: 10,
   283  		},
   284  	}
   285  	flt.Spec.Template.Spec.Counters = initialCounters
   286  	flt.Spec.Template.Spec.Lists = initialLists
   287  
   288  	flt, err := client.Fleets(framework.Namespace).Create(ctx, flt.DeepCopy(), metav1.CreateOptions{})
   289  	require.NoError(t, err)
   290  	defer client.Fleets(framework.Namespace).Delete(ctx, flt.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint:errcheck
   291  	framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas))
   292  
   293  	// Need fleetSelector to get the correct fleet, otherwise GSA will return game servers from any fleet in the namespace.
   294  	fleetSelector := metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}
   295  	stateAllocated := agonesv1.GameServerStateAllocated
   296  	ready := agonesv1.GameServerStateReady
   297  	allocated := allocationv1.GameServerAllocationAllocated
   298  	unallocated := allocationv1.GameServerAllocationUnAllocated
   299  
   300  	testCases := map[string]struct {
   301  		gsa           allocationv1.GameServerAllocation
   302  		wantGsaErr    bool                                   // For invalid GSA
   303  		wantAllocated allocationv1.GameServerAllocationState // For a valid GSA: "allocated" if you expect the GSA to succed in allocating a GameServer, "unallocated" if not
   304  		wantState     agonesv1.GameServerState
   305  	}{
   306  		"Counter Allocate to same GameServer MinAvailable (available capacity)": {
   307  			gsa: allocationv1.GameServerAllocation{
   308  				Spec: allocationv1.GameServerAllocationSpec{
   309  					Selectors: []allocationv1.GameServerSelector{
   310  						{
   311  							LabelSelector:   fleetSelector,
   312  							GameServerState: &stateAllocated,
   313  							Counters: map[string]allocationv1.CounterSelector{
   314  								"games": {
   315  									MinAvailable: 5,
   316  								}}}, {
   317  							LabelSelector:   fleetSelector,
   318  							GameServerState: &ready,
   319  							Counters: map[string]allocationv1.CounterSelector{
   320  								"games": {
   321  									MinAvailable: 5,
   322  								}}}}}},
   323  			wantGsaErr:    false,
   324  			wantAllocated: allocated,
   325  			wantState:     stateAllocated,
   326  		},
   327  		"List Allocate to same GameServer MinAvailable (available capacity)": {
   328  			gsa: allocationv1.GameServerAllocation{
   329  				Spec: allocationv1.GameServerAllocationSpec{
   330  					Selectors: []allocationv1.GameServerSelector{
   331  						{
   332  							LabelSelector:   fleetSelector,
   333  							GameServerState: &stateAllocated,
   334  							Lists: map[string]allocationv1.ListSelector{
   335  								"players": {
   336  									MinAvailable: 9,
   337  								}}}, {
   338  							LabelSelector:   fleetSelector,
   339  							GameServerState: &ready,
   340  							Lists: map[string]allocationv1.ListSelector{
   341  								"players": {
   342  									MinAvailable: 9,
   343  								}}}}}},
   344  			wantGsaErr:    false,
   345  			wantAllocated: allocated,
   346  			wantState:     stateAllocated,
   347  		},
   348  		"Counter Allocate to same GameServer MaxAvailable": {
   349  			gsa: allocationv1.GameServerAllocation{
   350  				Spec: allocationv1.GameServerAllocationSpec{
   351  					Selectors: []allocationv1.GameServerSelector{
   352  						{
   353  							LabelSelector:   fleetSelector,
   354  							GameServerState: &stateAllocated,
   355  							Counters: map[string]allocationv1.CounterSelector{
   356  								"games": {
   357  									MaxAvailable: 10,
   358  								}}}, {
   359  							LabelSelector:   fleetSelector,
   360  							GameServerState: &ready,
   361  							Counters: map[string]allocationv1.CounterSelector{
   362  								"games": {
   363  									MaxAvailable: 10,
   364  								}}}}}},
   365  			wantGsaErr:    false,
   366  			wantAllocated: allocated,
   367  			wantState:     stateAllocated,
   368  		},
   369  		"List Allocate to same GameServer MaxAvailable": {
   370  			gsa: allocationv1.GameServerAllocation{
   371  				Spec: allocationv1.GameServerAllocationSpec{
   372  					Selectors: []allocationv1.GameServerSelector{
   373  						{
   374  							LabelSelector:   fleetSelector,
   375  							GameServerState: &stateAllocated,
   376  							Lists: map[string]allocationv1.ListSelector{
   377  								"players": {
   378  									MaxAvailable: 9,
   379  								}}}, {
   380  							LabelSelector:   fleetSelector,
   381  							GameServerState: &ready,
   382  							Lists: map[string]allocationv1.ListSelector{
   383  								"players": {
   384  									MaxAvailable: 9,
   385  								}}}}}},
   386  			wantGsaErr:    false,
   387  			wantAllocated: allocated,
   388  			wantState:     stateAllocated,
   389  		},
   390  		"Allocate to same GameServer MinCount (count value)": {
   391  			gsa: allocationv1.GameServerAllocation{
   392  				Spec: allocationv1.GameServerAllocationSpec{
   393  					Selectors: []allocationv1.GameServerSelector{
   394  						{
   395  							LabelSelector:   fleetSelector,
   396  							GameServerState: &stateAllocated,
   397  							Counters: map[string]allocationv1.CounterSelector{
   398  								"games": {
   399  									MinCount: 2,
   400  								}}}, {
   401  							LabelSelector:   fleetSelector,
   402  							GameServerState: &ready,
   403  							Counters: map[string]allocationv1.CounterSelector{
   404  								"games": {
   405  									MinCount: 1,
   406  								}}}}}},
   407  			wantGsaErr:    false,
   408  			wantAllocated: allocated,
   409  			wantState:     stateAllocated,
   410  		},
   411  		"Allocate to same GameServer MaxCount (count value)": {
   412  			gsa: allocationv1.GameServerAllocation{
   413  				Spec: allocationv1.GameServerAllocationSpec{
   414  					Selectors: []allocationv1.GameServerSelector{
   415  						{
   416  							LabelSelector:   fleetSelector,
   417  							GameServerState: &stateAllocated,
   418  							Counters: map[string]allocationv1.CounterSelector{
   419  								"games": {
   420  									MaxCount: 3,
   421  								}}}, {
   422  							LabelSelector:   fleetSelector,
   423  							GameServerState: &ready,
   424  							Counters: map[string]allocationv1.CounterSelector{
   425  								"games": {
   426  									MaxCount: 2,
   427  								}}}}}},
   428  			wantGsaErr:    false,
   429  			wantAllocated: allocated,
   430  			wantState:     stateAllocated,
   431  		},
   432  		"List Allocate to same GameServer Contains Value": {
   433  			gsa: allocationv1.GameServerAllocation{
   434  				Spec: allocationv1.GameServerAllocationSpec{
   435  					Selectors: []allocationv1.GameServerSelector{
   436  						{
   437  							LabelSelector:   fleetSelector,
   438  							GameServerState: &stateAllocated,
   439  							Lists: map[string]allocationv1.ListSelector{
   440  								"players": {
   441  									ContainsValue: "player0",
   442  								}}}, {
   443  							LabelSelector:   fleetSelector,
   444  							GameServerState: &ready,
   445  							Lists: map[string]allocationv1.ListSelector{
   446  								"players": {
   447  									ContainsValue: "player0",
   448  								}}}}}},
   449  			wantGsaErr:    false,
   450  			wantAllocated: allocated,
   451  			wantState:     stateAllocated,
   452  		},
   453  		// 0 for MaxCount or MaxAvailable means unlimited maximum. Default for all fields: 0
   454  		"Allocate to same GameServer no Counter values": {
   455  			gsa: allocationv1.GameServerAllocation{
   456  				Spec: allocationv1.GameServerAllocationSpec{
   457  					Selectors: []allocationv1.GameServerSelector{
   458  						{
   459  							LabelSelector:   fleetSelector,
   460  							GameServerState: &stateAllocated,
   461  							Counters: map[string]allocationv1.CounterSelector{
   462  								"games": {}}}, {
   463  							LabelSelector:   fleetSelector,
   464  							GameServerState: &ready,
   465  							Counters: map[string]allocationv1.CounterSelector{
   466  								"games": {}}}}}},
   467  			wantGsaErr:    false,
   468  			wantAllocated: allocated,
   469  			wantState:     stateAllocated,
   470  		},
   471  		"Allocate to same GameServer no List values": {
   472  			gsa: allocationv1.GameServerAllocation{
   473  				Spec: allocationv1.GameServerAllocationSpec{
   474  					Selectors: []allocationv1.GameServerSelector{
   475  						{
   476  							LabelSelector:   fleetSelector,
   477  							GameServerState: &stateAllocated,
   478  							Lists: map[string]allocationv1.ListSelector{
   479  								"players": {}}}, {
   480  							LabelSelector:   fleetSelector,
   481  							GameServerState: &ready,
   482  							Lists: map[string]allocationv1.ListSelector{
   483  								"players": {}}}}}},
   484  			wantGsaErr:    false,
   485  			wantAllocated: allocated,
   486  			wantState:     stateAllocated,
   487  		},
   488  		"Counter does not exist": {
   489  			gsa: allocationv1.GameServerAllocation{
   490  				Spec: allocationv1.GameServerAllocationSpec{
   491  					Selectors: []allocationv1.GameServerSelector{{
   492  						LabelSelector:   fleetSelector,
   493  						GameServerState: &ready,
   494  						Counters: map[string]allocationv1.CounterSelector{
   495  							"players": {
   496  								MinAvailable: 1,
   497  							}}}}}},
   498  			wantGsaErr:    false,
   499  			wantAllocated: unallocated,
   500  		},
   501  		"List does not exist": {
   502  			gsa: allocationv1.GameServerAllocation{
   503  				Spec: allocationv1.GameServerAllocationSpec{
   504  					Selectors: []allocationv1.GameServerSelector{{
   505  						LabelSelector:   fleetSelector,
   506  						GameServerState: &ready,
   507  						Lists: map[string]allocationv1.ListSelector{
   508  							"games": {
   509  								MinAvailable: 1,
   510  							}}}}}},
   511  			wantGsaErr:    false,
   512  			wantAllocated: unallocated,
   513  		},
   514  		"Counter MaxAvailable < MinAvailable": {
   515  			gsa: allocationv1.GameServerAllocation{
   516  				Spec: allocationv1.GameServerAllocationSpec{
   517  					Selectors: []allocationv1.GameServerSelector{{
   518  						LabelSelector:   fleetSelector,
   519  						GameServerState: &ready,
   520  						Counters: map[string]allocationv1.CounterSelector{
   521  							"games": {
   522  								MaxAvailable: 1,
   523  								MinAvailable: 2,
   524  							}}}}}},
   525  			wantGsaErr: true,
   526  		},
   527  		"List MaxAvailable < MinAvailable": {
   528  			gsa: allocationv1.GameServerAllocation{
   529  				Spec: allocationv1.GameServerAllocationSpec{
   530  					Selectors: []allocationv1.GameServerSelector{{
   531  						LabelSelector:   fleetSelector,
   532  						GameServerState: &ready,
   533  						Lists: map[string]allocationv1.ListSelector{
   534  							"players": {
   535  								MaxAvailable: 8,
   536  								MinAvailable: 9,
   537  							}}}}}},
   538  			wantGsaErr: true,
   539  		},
   540  		"Counter Maxcount < MinCount": {
   541  			gsa: allocationv1.GameServerAllocation{
   542  				Spec: allocationv1.GameServerAllocationSpec{
   543  					Selectors: []allocationv1.GameServerSelector{{
   544  						LabelSelector:   fleetSelector,
   545  						GameServerState: &ready,
   546  						Counters: map[string]allocationv1.CounterSelector{
   547  							"games": {
   548  								MaxCount: 1,
   549  								MinCount: 2,
   550  							}}}}}},
   551  			wantGsaErr: true,
   552  		},
   553  		"List Value does not exist": {
   554  			gsa: allocationv1.GameServerAllocation{
   555  				Spec: allocationv1.GameServerAllocationSpec{
   556  					Selectors: []allocationv1.GameServerSelector{{
   557  						LabelSelector:   fleetSelector,
   558  						GameServerState: &ready,
   559  						Lists: map[string]allocationv1.ListSelector{
   560  							"players": {
   561  								ContainsValue: "player1",
   562  							}}}}}},
   563  			wantGsaErr:    false,
   564  			wantAllocated: unallocated,
   565  		},
   566  		"Negative values for MinCount, MaxCount, MaxAvailable, MinAvailable": {
   567  			gsa: allocationv1.GameServerAllocation{
   568  				Spec: allocationv1.GameServerAllocationSpec{
   569  					Selectors: []allocationv1.GameServerSelector{{
   570  						LabelSelector:   fleetSelector,
   571  						GameServerState: &ready,
   572  						Counters: map[string]allocationv1.CounterSelector{
   573  							"games": {
   574  								MaxCount:     -1,
   575  								MinCount:     -2,
   576  								MaxAvailable: -10,
   577  								MinAvailable: -1,
   578  							}}}}}},
   579  			wantGsaErr: true,
   580  		},
   581  	}
   582  
   583  	for name, testCase := range testCases {
   584  		t.Run(name, func(t *testing.T) {
   585  
   586  			// First allocation
   587  			gsa, err := framework.AgonesClient.AllocationV1().GameServerAllocations(flt.ObjectMeta.Namespace).Create(ctx, testCase.gsa.DeepCopy(), metav1.CreateOptions{})
   588  			if testCase.wantGsaErr {
   589  				require.Error(t, err)
   590  				return
   591  			}
   592  			require.NoError(t, err)
   593  			assert.Equal(t, string(testCase.wantAllocated), string(gsa.Status.State))
   594  			if gsa.Status.State == allocated {
   595  				assert.Equal(t, initialCounters, gsa.Status.Counters)
   596  				assert.Equal(t, initialLists, gsa.Status.Lists)
   597  			}
   598  
   599  			gs1, err := framework.AgonesClient.AgonesV1().GameServers(flt.ObjectMeta.Namespace).Get(ctx, gsa.Status.GameServerName, metav1.GetOptions{})
   600  			if testCase.wantAllocated == unallocated {
   601  				require.Error(t, err)
   602  				return
   603  			}
   604  			require.NoError(t, err)
   605  			assert.Equal(t, testCase.wantState, gs1.Status.State)
   606  			assert.NotNil(t, gs1.ObjectMeta.Annotations["agones.dev/last-allocated"])
   607  
   608  			// Second allocation
   609  			gsa, err = framework.AgonesClient.AllocationV1().GameServerAllocations(flt.ObjectMeta.Namespace).Create(ctx, gsa.DeepCopy(), metav1.CreateOptions{})
   610  			require.NoError(t, err)
   611  			assert.Equal(t, string(testCase.wantAllocated), string(gsa.Status.State))
   612  			assert.Equal(t, gs1.ObjectMeta.Name, gsa.Status.GameServerName)
   613  			if gsa.Status.State == allocated {
   614  				assert.Equal(t, initialCounters, gsa.Status.Counters)
   615  				assert.Equal(t, initialLists, gsa.Status.Lists)
   616  			}
   617  
   618  			gs2, err := framework.AgonesClient.AgonesV1().GameServers(flt.ObjectMeta.Namespace).Get(ctx, gsa.Status.GameServerName, metav1.GetOptions{})
   619  			require.NoError(t, err)
   620  			assert.Equal(t, testCase.wantState, gs2.Status.State)
   621  
   622  			// Confirm allocated to the same GameServer (gs2 == gs1)
   623  			require.Equal(t, gs1.ObjectMeta.Name, gs2.ObjectMeta.Name)
   624  			require.NotEqual(t, gs1.ObjectMeta.ResourceVersion, gs2.ObjectMeta.ResourceVersion)
   625  			require.NotEqual(t, gs1.ObjectMeta.Annotations["agones.dev/last-allocated"], gs2.ObjectMeta.Annotations["agones.dev/last-allocated"])
   626  
   627  			// Reset any GameServers in state Allocated -> Ready. Note: This does not reset any changes to Counters or Lists.
   628  			list, err := framework.ListGameServersFromFleet(flt)
   629  			require.NoError(t, err)
   630  			for _, gs := range list {
   631  				if gs.Status.State == ready {
   632  					continue
   633  				}
   634  				gsCopy := gs.DeepCopy()
   635  				gsCopy.Status.State = ready
   636  				reqReadyGs, err := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Update(ctx, gsCopy, metav1.UpdateOptions{})
   637  				require.NoError(t, err)
   638  				require.Equal(t, ready, reqReadyGs.Status.State)
   639  			}
   640  		})
   641  	}
   642  }
   643  
   644  func TestCounterGameServerAllocationActions(t *testing.T) {
   645  	if !runtime.FeatureEnabled(runtime.FeatureCountsAndLists) {
   646  		t.SkipNow()
   647  	}
   648  	t.Parallel()
   649  	ctx := context.Background()
   650  	client := framework.AgonesClient.AgonesV1()
   651  
   652  	counters := map[string]agonesv1.CounterStatus{}
   653  	counters["games"] = agonesv1.CounterStatus{
   654  		Count:    5,
   655  		Capacity: 10,
   656  	}
   657  
   658  	flt := defaultFleet(framework.Namespace)
   659  	flt.Spec.Template.Spec.Counters = counters
   660  
   661  	flt, err := client.Fleets(framework.Namespace).Create(ctx, flt.DeepCopy(), metav1.CreateOptions{})
   662  	require.NoError(t, err)
   663  	defer client.Fleets(framework.Namespace).Delete(ctx, flt.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint:errcheck
   664  	framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas))
   665  
   666  	fleetSelector := metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}
   667  	allocated := agonesv1.GameServerStateAllocated
   668  	ready := agonesv1.GameServerStateReady
   669  	increment := agonesv1.GameServerPriorityIncrement
   670  	decrement := agonesv1.GameServerPriorityDecrement
   671  	zero := int64(0)
   672  	one := int64(1)
   673  	five := int64(5)
   674  	six := int64(6)
   675  	ten := int64(10)
   676  	negativeOne := int64(-1)
   677  
   678  	testCases := map[string]struct {
   679  		gsa          allocationv1.GameServerAllocation
   680  		wantGsaErr   bool
   681  		wantCount    *int64
   682  		wantCapacity *int64
   683  	}{
   684  		"increment": {
   685  			gsa: allocationv1.GameServerAllocation{
   686  				Spec: allocationv1.GameServerAllocationSpec{
   687  					Selectors: []allocationv1.GameServerSelector{
   688  						{LabelSelector: fleetSelector},
   689  					},
   690  					Counters: map[string]allocationv1.CounterAction{
   691  						"games": {
   692  							Action: &increment,
   693  							Amount: &one,
   694  						}}}},
   695  			wantGsaErr:   false,
   696  			wantCount:    &six,
   697  			wantCapacity: &ten,
   698  		},
   699  		"decrement": {
   700  			gsa: allocationv1.GameServerAllocation{
   701  				Spec: allocationv1.GameServerAllocationSpec{
   702  					Selectors: []allocationv1.GameServerSelector{
   703  						{LabelSelector: fleetSelector},
   704  					},
   705  					Counters: map[string]allocationv1.CounterAction{
   706  						"games": {
   707  							Action: &decrement,
   708  							Amount: &five,
   709  						}}}},
   710  			wantGsaErr:   false,
   711  			wantCount:    &zero,
   712  			wantCapacity: &ten,
   713  		},
   714  		"change capacity to less than count also updates count": {
   715  			gsa: allocationv1.GameServerAllocation{
   716  				Spec: allocationv1.GameServerAllocationSpec{
   717  					Selectors: []allocationv1.GameServerSelector{
   718  						{LabelSelector: fleetSelector},
   719  					},
   720  					Counters: map[string]allocationv1.CounterAction{
   721  						"games": {
   722  							Capacity: &zero,
   723  						}}}},
   724  			wantGsaErr:   false,
   725  			wantCount:    &zero,
   726  			wantCapacity: &zero,
   727  		},
   728  		"decrement past zero truncated": {
   729  			gsa: allocationv1.GameServerAllocation{
   730  				Spec: allocationv1.GameServerAllocationSpec{
   731  					Selectors: []allocationv1.GameServerSelector{
   732  						{LabelSelector: fleetSelector},
   733  					},
   734  					Counters: map[string]allocationv1.CounterAction{
   735  						"games": {
   736  							Action: &decrement,
   737  							Amount: &six,
   738  						}}}},
   739  			wantGsaErr:   false,
   740  			wantCount:    &zero,
   741  			wantCapacity: &ten,
   742  		},
   743  		"decrement negative": {
   744  			gsa: allocationv1.GameServerAllocation{
   745  				Spec: allocationv1.GameServerAllocationSpec{
   746  					Selectors: []allocationv1.GameServerSelector{
   747  						{LabelSelector: fleetSelector},
   748  					},
   749  					Counters: map[string]allocationv1.CounterAction{
   750  						"games": {
   751  							Action: &decrement,
   752  							Amount: &negativeOne,
   753  						}}}},
   754  			wantGsaErr: true,
   755  		},
   756  		"increment past capacity truncated": {
   757  			gsa: allocationv1.GameServerAllocation{
   758  				Spec: allocationv1.GameServerAllocationSpec{
   759  					Selectors: []allocationv1.GameServerSelector{
   760  						{LabelSelector: fleetSelector},
   761  					},
   762  					Counters: map[string]allocationv1.CounterAction{
   763  						"games": {
   764  							Action: &increment,
   765  							Amount: &six,
   766  						}}}},
   767  			wantGsaErr:   false,
   768  			wantCount:    &ten,
   769  			wantCapacity: &ten,
   770  		},
   771  		"increment negative": {
   772  			gsa: allocationv1.GameServerAllocation{
   773  				Spec: allocationv1.GameServerAllocationSpec{
   774  					Selectors: []allocationv1.GameServerSelector{
   775  						{LabelSelector: fleetSelector},
   776  					},
   777  					Counters: map[string]allocationv1.CounterAction{
   778  						"games": {
   779  							Action: &increment,
   780  							Amount: &negativeOne,
   781  						}}}},
   782  			wantGsaErr: true,
   783  		},
   784  		"change capacity negative": {
   785  			gsa: allocationv1.GameServerAllocation{
   786  				Spec: allocationv1.GameServerAllocationSpec{
   787  					Selectors: []allocationv1.GameServerSelector{
   788  						{LabelSelector: fleetSelector},
   789  					},
   790  					Counters: map[string]allocationv1.CounterAction{
   791  						"games": {
   792  							Capacity: &negativeOne,
   793  						}}}},
   794  			wantGsaErr: true,
   795  		},
   796  		// Note: a gameserver is still allocated even though the counter does not exist (and thus the
   797  		// action cannot be performed). gsa.Validate() is not able to see the state of Counters in the
   798  		// fleet, so the GSA is not able to validate the existence of a Counter. Use the
   799  		// GameServerSelector to filter the Counters.
   800  		"Counter does not exist": {
   801  			gsa: allocationv1.GameServerAllocation{
   802  				Spec: allocationv1.GameServerAllocationSpec{
   803  					Selectors: []allocationv1.GameServerSelector{
   804  						{LabelSelector: fleetSelector},
   805  					},
   806  					Counters: map[string]allocationv1.CounterAction{
   807  						"lames": {
   808  							Action: &increment,
   809  							Amount: &one,
   810  						}}}},
   811  			wantGsaErr: false,
   812  		},
   813  	}
   814  
   815  	for name, testCase := range testCases {
   816  		t.Run(name, func(t *testing.T) {
   817  
   818  			gsa, err := framework.AgonesClient.AllocationV1().GameServerAllocations(flt.ObjectMeta.Namespace).Create(ctx, testCase.gsa.DeepCopy(), metav1.CreateOptions{})
   819  			if testCase.wantGsaErr {
   820  				require.Error(t, err)
   821  				return
   822  			}
   823  			require.NoError(t, err)
   824  			assert.Equal(t, string(allocated), string(gsa.Status.State))
   825  			counterStatus, ok := gsa.Status.Counters["games"]
   826  			assert.True(t, ok)
   827  			if testCase.wantCount != nil {
   828  				assert.Equal(t, *testCase.wantCount, counterStatus.Count)
   829  			}
   830  			if testCase.wantCapacity != nil {
   831  				assert.Equal(t, *testCase.wantCapacity, counterStatus.Capacity)
   832  			}
   833  
   834  			gs1, err := framework.AgonesClient.AgonesV1().GameServers(flt.ObjectMeta.Namespace).Get(ctx, gsa.Status.GameServerName, metav1.GetOptions{})
   835  			require.NoError(t, err)
   836  			assert.Equal(t, allocated, gs1.Status.State)
   837  			assert.NotNil(t, gs1.ObjectMeta.Annotations["agones.dev/last-allocated"])
   838  
   839  			counter, ok := gs1.Status.Counters["games"]
   840  			assert.True(t, ok)
   841  			if testCase.wantCount != nil {
   842  				assert.Equal(t, *testCase.wantCount, counter.Count)
   843  			}
   844  			if testCase.wantCapacity != nil {
   845  				assert.Equal(t, *testCase.wantCapacity, counter.Capacity)
   846  			}
   847  
   848  			// Reset any GameServers in state Allocated -> Ready, and reset any changes to Counters.
   849  			gsList, err := framework.ListGameServersFromFleet(flt)
   850  			require.NoError(t, err)
   851  			for _, gs := range gsList {
   852  				if gs.Status.State == ready && cmp.Equal(gs.Status.Counters, counters) {
   853  					continue
   854  				}
   855  				gsCopy := gs.DeepCopy()
   856  				gsCopy.Status.State = ready
   857  				gsCopy.Status.Counters = counters
   858  				reqReadyGs, err := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Update(ctx, gsCopy, metav1.UpdateOptions{})
   859  				require.NoError(t, err)
   860  				require.Equal(t, ready, reqReadyGs.Status.State)
   861  			}
   862  		})
   863  	}
   864  }
   865  
   866  func TestListGameServerAllocationActions(t *testing.T) {
   867  	if !runtime.FeatureEnabled(runtime.FeatureCountsAndLists) {
   868  		t.SkipNow()
   869  	}
   870  	t.Parallel()
   871  	ctx := context.Background()
   872  	client := framework.AgonesClient.AgonesV1()
   873  
   874  	lists := map[string]agonesv1.ListStatus{}
   875  	lists["players"] = agonesv1.ListStatus{
   876  		Values:   []string{"player0", "player1", "player2"},
   877  		Capacity: 8,
   878  	}
   879  	lists["rooms"] = agonesv1.ListStatus{
   880  		Values:   []string{"room0", "room1", "room2"},
   881  		Capacity: 20,
   882  	}
   883  
   884  	flt := defaultFleet(framework.Namespace)
   885  	flt.Spec.Template.Spec.Lists = lists
   886  
   887  	flt, err := client.Fleets(framework.Namespace).Create(ctx, flt.DeepCopy(), metav1.CreateOptions{})
   888  	require.NoError(t, err)
   889  	defer client.Fleets(framework.Namespace).Delete(ctx, flt.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint:errcheck
   890  	framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas))
   891  
   892  	fleetSelector := metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}
   893  	allocated := agonesv1.GameServerStateAllocated
   894  	ready := agonesv1.GameServerStateReady
   895  	one := int64(1)
   896  
   897  	testCases := map[string]struct {
   898  		gsa          allocationv1.GameServerAllocation
   899  		listName     string
   900  		wantGsaErr   bool
   901  		wantCapacity *int64
   902  		wantValues   []string
   903  	}{
   904  		"add List values": {
   905  			gsa: allocationv1.GameServerAllocation{
   906  				Spec: allocationv1.GameServerAllocationSpec{
   907  					Selectors: []allocationv1.GameServerSelector{
   908  						{LabelSelector: fleetSelector},
   909  					},
   910  					Lists: map[string]allocationv1.ListAction{
   911  						"players": {
   912  							AddValues: []string{"player3", "player4", "player5"},
   913  						}}}},
   914  			listName:   "players",
   915  			wantGsaErr: false,
   916  			wantValues: []string{"player0", "player1", "player2", "player3", "player4", "player5"},
   917  		},
   918  		"change List capacity truncates": {
   919  			gsa: allocationv1.GameServerAllocation{
   920  				Spec: allocationv1.GameServerAllocationSpec{
   921  					Selectors: []allocationv1.GameServerSelector{
   922  						{LabelSelector: fleetSelector},
   923  					},
   924  					Lists: map[string]allocationv1.ListAction{
   925  						"players": {
   926  							Capacity: &one,
   927  						}}}},
   928  			listName:     "players",
   929  			wantGsaErr:   false,
   930  			wantValues:   []string{"player0"},
   931  			wantCapacity: &one,
   932  		},
   933  		"delete List values": {
   934  			gsa: allocationv1.GameServerAllocation{
   935  				Spec: allocationv1.GameServerAllocationSpec{
   936  					Selectors: []allocationv1.GameServerSelector{
   937  						{LabelSelector: fleetSelector},
   938  					},
   939  					Lists: map[string]allocationv1.ListAction{
   940  						"rooms": {
   941  							DeleteValues: []string{"room1", "room0"},
   942  						}}}},
   943  			listName:   "rooms",
   944  			wantGsaErr: false,
   945  			wantValues: []string{"room2"},
   946  		},
   947  	}
   948  
   949  	for name, testCase := range testCases {
   950  		t.Run(name, func(t *testing.T) {
   951  
   952  			gsa, err := framework.AgonesClient.AllocationV1().GameServerAllocations(flt.ObjectMeta.Namespace).Create(ctx, testCase.gsa.DeepCopy(), metav1.CreateOptions{})
   953  			if testCase.wantGsaErr {
   954  				require.Error(t, err)
   955  				return
   956  			}
   957  			require.NoError(t, err)
   958  			assert.Equal(t, string(allocated), string(gsa.Status.State))
   959  			listStatus, ok := gsa.Status.Lists[testCase.listName]
   960  			assert.True(t, ok)
   961  			if testCase.wantCapacity != nil {
   962  				assert.Equal(t, *testCase.wantCapacity, listStatus.Capacity)
   963  			}
   964  			assert.Equal(t, testCase.wantValues, listStatus.Values)
   965  
   966  			gs1, err := framework.AgonesClient.AgonesV1().GameServers(flt.ObjectMeta.Namespace).Get(ctx, gsa.Status.GameServerName, metav1.GetOptions{})
   967  			require.NoError(t, err)
   968  			assert.Equal(t, allocated, gs1.Status.State)
   969  			assert.NotNil(t, gs1.ObjectMeta.Annotations["agones.dev/last-allocated"])
   970  
   971  			list, ok := gs1.Status.Lists[testCase.listName]
   972  			assert.True(t, ok)
   973  			if testCase.wantCapacity != nil {
   974  				assert.Equal(t, *testCase.wantCapacity, list.Capacity)
   975  			}
   976  			assert.Equal(t, testCase.wantValues, list.Values)
   977  
   978  			// Reset any GameServers in state Allocated -> Ready, and reset any changes to Lists.
   979  			gsList, err := framework.ListGameServersFromFleet(flt)
   980  			require.NoError(t, err)
   981  			for _, gs := range gsList {
   982  				if gs.Status.State == ready && cmp.Equal(gs.Status.Lists, lists) {
   983  					continue
   984  				}
   985  				gsCopy := gs.DeepCopy()
   986  				gsCopy.Status.State = ready
   987  				gsCopy.Status.Lists = lists
   988  				reqReadyGs, err := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Update(ctx, gsCopy, metav1.UpdateOptions{})
   989  				require.NoError(t, err)
   990  				require.Equal(t, ready, reqReadyGs.Status.State)
   991  			}
   992  		})
   993  	}
   994  }
   995  
   996  func TestMultiClusterAllocationOnLocalCluster(t *testing.T) {
   997  	t.Parallel()
   998  
   999  	fixtures := []apis.SchedulingStrategy{apis.Packed, apis.Distributed}
  1000  	for _, strategy := range fixtures {
  1001  		t.Run(string(strategy), func(t *testing.T) {
  1002  			if strategy == apis.Distributed {
  1003  				framework.SkipOnCloudProduct(t, "gke-autopilot", "Autopilot does not support Distributed scheduling")
  1004  			}
  1005  			t.Parallel()
  1006  			ctx := context.Background()
  1007  
  1008  			namespace := fmt.Sprintf("gsa-multicluster-local-%s", uuid.NewUUID())
  1009  			err := framework.CreateNamespace(namespace)
  1010  			if !assert.Nil(t, err) {
  1011  				return
  1012  			}
  1013  			defer func() {
  1014  				if derr := framework.DeleteNamespace(namespace); derr != nil {
  1015  					t.Error(derr)
  1016  				}
  1017  			}()
  1018  
  1019  			fleets := framework.AgonesClient.AgonesV1().Fleets(namespace)
  1020  			fleet := defaultFleet(namespace)
  1021  			fleet.Spec.Scheduling = strategy
  1022  			flt, err := fleets.Create(ctx, fleet, metav1.CreateOptions{})
  1023  			if assert.Nil(t, err) {
  1024  				defer fleets.Delete(ctx, flt.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint:errcheck
  1025  			}
  1026  
  1027  			framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas))
  1028  
  1029  			// Allocation Policy #1: local cluster with desired label.
  1030  			// This policy allocates locally on the cluster due to matching namespace with gsa and not setting AllocationEndpoints.
  1031  			mca := &multiclusterv1.GameServerAllocationPolicy{
  1032  				Spec: multiclusterv1.GameServerAllocationPolicySpec{
  1033  					Priority: 1,
  1034  					Weight:   100,
  1035  					ConnectionInfo: multiclusterv1.ClusterConnectionInfo{
  1036  						ClusterName: "multicluster1",
  1037  						SecretName:  "sec1",
  1038  						Namespace:   namespace,
  1039  					},
  1040  				},
  1041  				ObjectMeta: metav1.ObjectMeta{
  1042  					Labels:       map[string]string{"cluster": "onprem"},
  1043  					GenerateName: "allocationpolicy-",
  1044  				},
  1045  			}
  1046  			resp, err := framework.AgonesClient.MulticlusterV1().GameServerAllocationPolicies(fleet.ObjectMeta.Namespace).Create(ctx, mca, metav1.CreateOptions{})
  1047  			if !assert.Nil(t, err) {
  1048  				assert.FailNowf(t, "GameServerAllocationPolicies(%v).Create(ctx, %v, metav1.CreateOptions{})", fleet.ObjectMeta.Namespace, mca)
  1049  			}
  1050  			assert.Equal(t, mca.Spec, resp.Spec)
  1051  
  1052  			// Allocation Policy #2: another cluster with desired label, but lower priority.
  1053  			// If the policy is selected due to a bug the request fails as it cannot find the secret.
  1054  			mca = &multiclusterv1.GameServerAllocationPolicy{
  1055  				Spec: multiclusterv1.GameServerAllocationPolicySpec{
  1056  					Priority: 2,
  1057  					Weight:   100,
  1058  					ConnectionInfo: multiclusterv1.ClusterConnectionInfo{
  1059  						AllocationEndpoints: []string{"another-endpoint"},
  1060  						ClusterName:         "multicluster2",
  1061  						SecretName:          "sec2",
  1062  						Namespace:           namespace,
  1063  					},
  1064  				},
  1065  				ObjectMeta: metav1.ObjectMeta{
  1066  					Labels:       map[string]string{"cluster": "onprem"},
  1067  					GenerateName: "allocationpolicy-",
  1068  				},
  1069  			}
  1070  			resp, err = framework.AgonesClient.MulticlusterV1().GameServerAllocationPolicies(fleet.ObjectMeta.Namespace).Create(ctx, mca, metav1.CreateOptions{})
  1071  			if assert.Nil(t, err) {
  1072  				assert.Equal(t, mca.Spec, resp.Spec)
  1073  			}
  1074  
  1075  			// Allocation Policy #3: another cluster with highest priority, but missing desired label (will not be selected)
  1076  			mca = &multiclusterv1.GameServerAllocationPolicy{
  1077  				Spec: multiclusterv1.GameServerAllocationPolicySpec{
  1078  					Priority: 1,
  1079  					Weight:   10,
  1080  					ConnectionInfo: multiclusterv1.ClusterConnectionInfo{
  1081  						AllocationEndpoints: []string{"another-endpoint"},
  1082  						ClusterName:         "multicluster3",
  1083  						SecretName:          "sec3",
  1084  						Namespace:           namespace,
  1085  					},
  1086  				},
  1087  				ObjectMeta: metav1.ObjectMeta{
  1088  					GenerateName: "allocationpolicy-",
  1089  				},
  1090  			}
  1091  			resp, err = framework.AgonesClient.MulticlusterV1().GameServerAllocationPolicies(fleet.ObjectMeta.Namespace).Create(ctx, mca, metav1.CreateOptions{})
  1092  			if assert.Nil(t, err) {
  1093  				assert.Equal(t, mca.Spec, resp.Spec)
  1094  			}
  1095  
  1096  			gsa := &allocationv1.GameServerAllocation{
  1097  				Spec: allocationv1.GameServerAllocationSpec{
  1098  					Scheduling: strategy,
  1099  					Selectors:  []allocationv1.GameServerSelector{{LabelSelector: metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}}},
  1100  					MultiClusterSetting: allocationv1.MultiClusterSetting{
  1101  						Enabled: true,
  1102  						PolicySelector: metav1.LabelSelector{
  1103  							MatchLabels: map[string]string{
  1104  								"cluster": "onprem",
  1105  							},
  1106  						},
  1107  					},
  1108  				},
  1109  				ObjectMeta: metav1.ObjectMeta{
  1110  					GenerateName: "allocation-",
  1111  					Namespace:    namespace,
  1112  				},
  1113  			}
  1114  
  1115  			// wait for the allocation policies to be added.
  1116  			err = wait.PollUntilContextTimeout(context.Background(), 2*time.Second, 2*time.Minute, true, func(ctx context.Context) (bool, error) {
  1117  				gsa, err = framework.AgonesClient.AllocationV1().GameServerAllocations(fleet.ObjectMeta.Namespace).Create(ctx, gsa, metav1.CreateOptions{})
  1118  				if err != nil {
  1119  					t.Logf("GameServerAllocations(%v).Create(ctx, %v, metav1.CreateOptions{}) failed: %s", fleet.ObjectMeta.Namespace, gsa, err)
  1120  					return false, nil
  1121  				}
  1122  
  1123  				assert.Equal(t, string(allocationv1.GameServerAllocationAllocated), string(gsa.Status.State))
  1124  				return true, nil
  1125  			})
  1126  
  1127  			assert.NoError(t, err)
  1128  		})
  1129  	}
  1130  }
  1131  
  1132  // Can't allocate more GameServers if a fleet is fully used.
  1133  func TestCreateFullFleetAndCantGameServerAllocate(t *testing.T) {
  1134  	t.Parallel()
  1135  
  1136  	fixtures := []apis.SchedulingStrategy{apis.Packed, apis.Distributed}
  1137  
  1138  	for _, strategy := range fixtures {
  1139  		t.Run(string(strategy), func(t *testing.T) {
  1140  			if strategy == apis.Distributed {
  1141  				framework.SkipOnCloudProduct(t, "gke-autopilot", "Autopilot does not support Distributed scheduling")
  1142  			}
  1143  			t.Parallel()
  1144  			ctx := context.Background()
  1145  
  1146  			fleets := framework.AgonesClient.AgonesV1().Fleets(framework.Namespace)
  1147  			fleet := defaultFleet(framework.Namespace)
  1148  			fleet.Spec.Scheduling = strategy
  1149  			flt, err := fleets.Create(ctx, fleet, metav1.CreateOptions{})
  1150  			if assert.Nil(t, err) {
  1151  				defer fleets.Delete(ctx, flt.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint:errcheck
  1152  			}
  1153  
  1154  			framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas))
  1155  
  1156  			gsa := &allocationv1.GameServerAllocation{
  1157  				Spec: allocationv1.GameServerAllocationSpec{
  1158  					Scheduling: strategy,
  1159  					Selectors:  []allocationv1.GameServerSelector{{LabelSelector: metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}}},
  1160  				}}
  1161  
  1162  			for i := 0; i < replicasCount; i++ {
  1163  				var gsa2 *allocationv1.GameServerAllocation
  1164  				gsa2, err = framework.AgonesClient.AllocationV1().GameServerAllocations(framework.Namespace).Create(ctx, gsa.DeepCopy(), metav1.CreateOptions{})
  1165  				if assert.Nil(t, err) {
  1166  					assert.Equal(t, allocationv1.GameServerAllocationAllocated, gsa2.Status.State)
  1167  				}
  1168  			}
  1169  
  1170  			framework.AssertFleetCondition(t, flt, func(_ *logrus.Entry, fleet *agonesv1.Fleet) bool {
  1171  				return fleet.Status.AllocatedReplicas == replicasCount
  1172  			})
  1173  
  1174  			gsa, err = framework.AgonesClient.AllocationV1().GameServerAllocations(framework.Namespace).Create(ctx, gsa.DeepCopy(), metav1.CreateOptions{})
  1175  			if assert.Nil(t, err) {
  1176  				assert.Equal(t, string(allocationv1.GameServerAllocationUnAllocated), string(gsa.Status.State))
  1177  			}
  1178  		})
  1179  	}
  1180  }
  1181  
  1182  func TestGameServerAllocationMetaDataPatch(t *testing.T) {
  1183  	t.Parallel()
  1184  	ctx := context.Background()
  1185  
  1186  	log := e2e.TestLogger(t)
  1187  	createAndAllocate := func(input *allocationv1.GameServerAllocation) *allocationv1.GameServerAllocation {
  1188  		gs := framework.DefaultGameServer(framework.Namespace)
  1189  		gs.ObjectMeta.Labels = map[string]string{"test": t.Name()}
  1190  		gs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
  1191  		require.NoError(t, err)
  1192  
  1193  		log.WithField("gs", gs.ObjectMeta.Name).Info("👍 created and ready")
  1194  
  1195  		// poll, as it may take a moment for the allocation cache to be populated
  1196  		err = wait.PollUntilContextTimeout(context.Background(), time.Second, 30*time.Second, true, func(ctx context.Context) (bool, error) {
  1197  			input, err = framework.AgonesClient.AllocationV1().GameServerAllocations(framework.Namespace).Create(ctx, input, metav1.CreateOptions{})
  1198  			if err != nil {
  1199  				log.WithError(err).Info("Failed, trying again...")
  1200  				return false, err
  1201  			}
  1202  
  1203  			return allocationv1.GameServerAllocationAllocated == input.Status.State, nil
  1204  		})
  1205  		require.NoError(t, err)
  1206  		return input
  1207  	}
  1208  
  1209  	// two standard labels
  1210  	gsa := &allocationv1.GameServerAllocation{ObjectMeta: metav1.ObjectMeta{GenerateName: "allocation-"},
  1211  		Spec: allocationv1.GameServerAllocationSpec{
  1212  			Selectors: []allocationv1.GameServerSelector{{LabelSelector: metav1.LabelSelector{MatchLabels: map[string]string{"test": t.Name()}}}},
  1213  			MetaPatch: allocationv1.MetaPatch{
  1214  				Labels:      map[string]string{"red": "blue"},
  1215  				Annotations: map[string]string{"dog": "good"},
  1216  			},
  1217  		}}
  1218  	result := createAndAllocate(gsa)
  1219  	defer framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Delete(ctx, result.Status.GameServerName, metav1.DeleteOptions{}) // nolint: errcheck
  1220  
  1221  	gs, err := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Get(ctx, result.Status.GameServerName, metav1.GetOptions{})
  1222  	require.NoError(t, err)
  1223  	assert.Equal(t, "blue", gs.ObjectMeta.Labels["red"])
  1224  	assert.Equal(t, "good", gs.ObjectMeta.Annotations["dog"])
  1225  
  1226  	// use special characters that are valid
  1227  	gsa.Spec.MetaPatch = allocationv1.MetaPatch{Labels: map[string]string{"blue-frog.fred_thing": "test"}}
  1228  	result = createAndAllocate(gsa)
  1229  	defer framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Delete(ctx, result.Status.GameServerName, metav1.DeleteOptions{}) // nolint: errcheck
  1230  
  1231  	gs, err = framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Get(ctx, result.Status.GameServerName, metav1.GetOptions{})
  1232  	require.NoError(t, err)
  1233  	assert.Equal(t, "test", gs.ObjectMeta.Labels["blue-frog.fred_thing"])
  1234  
  1235  	// throw something invalid at it.
  1236  	gsa.Spec.MetaPatch = allocationv1.MetaPatch{Labels: map[string]string{"$$$$$$$": "test"}}
  1237  	result, err = framework.AgonesClient.AllocationV1().GameServerAllocations(framework.Namespace).Create(ctx, gsa.DeepCopy(), metav1.CreateOptions{})
  1238  	log.WithField("result", result).WithError(err).Info("Failed allocation")
  1239  	require.Error(t, err)
  1240  	require.Contains(t, err.Error(), `GameServerAllocation.allocation.agones.dev "" is invalid`)
  1241  }
  1242  
  1243  func TestGameServerAllocationPreferredSelection(t *testing.T) {
  1244  	t.Parallel()
  1245  	ctx := context.Background()
  1246  
  1247  	fleets := framework.AgonesClient.AgonesV1().Fleets(framework.Namespace)
  1248  	gameServers := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace)
  1249  	label := map[string]string{"role": t.Name()}
  1250  
  1251  	preferred := defaultFleet(framework.Namespace)
  1252  	preferred.ObjectMeta.GenerateName = "preferred-"
  1253  	preferred.Spec.Replicas = 1
  1254  	preferred.Spec.Template.ObjectMeta.Labels = label
  1255  	preferred, err := fleets.Create(ctx, preferred, metav1.CreateOptions{})
  1256  	if assert.Nil(t, err) {
  1257  		defer fleets.Delete(ctx, preferred.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint:errcheck
  1258  	} else {
  1259  		assert.FailNow(t, "could not create first fleet")
  1260  	}
  1261  
  1262  	required := defaultFleet(framework.Namespace)
  1263  	required.ObjectMeta.GenerateName = "required-"
  1264  	required.Spec.Replicas = 2
  1265  	required.Spec.Template.ObjectMeta.Labels = label
  1266  	required, err = fleets.Create(ctx, required, metav1.CreateOptions{})
  1267  	if assert.Nil(t, err) {
  1268  		defer fleets.Delete(ctx, required.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint:errcheck
  1269  	} else {
  1270  		assert.FailNow(t, "could not create second fleet")
  1271  	}
  1272  
  1273  	framework.AssertFleetCondition(t, preferred, e2e.FleetReadyCount(preferred.Spec.Replicas))
  1274  	framework.AssertFleetCondition(t, required, e2e.FleetReadyCount(required.Spec.Replicas))
  1275  
  1276  	gsa := &allocationv1.GameServerAllocation{ObjectMeta: metav1.ObjectMeta{GenerateName: "allocation-"},
  1277  		Spec: allocationv1.GameServerAllocationSpec{
  1278  			Selectors: []allocationv1.GameServerSelector{
  1279  				{LabelSelector: metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: preferred.ObjectMeta.Name}}},
  1280  				{LabelSelector: metav1.LabelSelector{MatchLabels: label}},
  1281  			},
  1282  		}}
  1283  
  1284  	gsa1, err := framework.AgonesClient.AllocationV1().GameServerAllocations(framework.Namespace).Create(ctx, gsa.DeepCopy(), metav1.CreateOptions{})
  1285  	if assert.Nil(t, err) {
  1286  		assert.Equal(t, allocationv1.GameServerAllocationAllocated, gsa1.Status.State)
  1287  		gs, err := gameServers.Get(ctx, gsa1.Status.GameServerName, metav1.GetOptions{})
  1288  		assert.Nil(t, err)
  1289  		assert.Equal(t, preferred.ObjectMeta.Name, gs.ObjectMeta.Labels[agonesv1.FleetNameLabel])
  1290  	} else {
  1291  		assert.FailNow(t, "could not completed gsa1 allocation")
  1292  	}
  1293  
  1294  	gs2, err := framework.AgonesClient.AllocationV1().GameServerAllocations(framework.Namespace).Create(ctx, gsa.DeepCopy(), metav1.CreateOptions{})
  1295  	if assert.Nil(t, err) {
  1296  		assert.Equal(t, allocationv1.GameServerAllocationAllocated, gs2.Status.State)
  1297  		gs, err := gameServers.Get(ctx, gs2.Status.GameServerName, metav1.GetOptions{})
  1298  		assert.Nil(t, err)
  1299  		assert.Equal(t, required.ObjectMeta.Name, gs.ObjectMeta.Labels[agonesv1.FleetNameLabel])
  1300  	} else {
  1301  		assert.FailNow(t, "could not completed gs2 allocation")
  1302  	}
  1303  
  1304  	// delete the preferred gameserver, and then let's try allocating again, make sure it goes back to the
  1305  	// preferred one
  1306  	err = gameServers.Delete(ctx, gsa1.Status.GameServerName, metav1.DeleteOptions{})
  1307  	if !assert.Nil(t, err) {
  1308  		assert.FailNow(t, "could not delete gameserver")
  1309  	}
  1310  
  1311  	// wait until the game server is deleted
  1312  	err = wait.PollUntilContextTimeout(context.Background(), time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) {
  1313  		_, err = gameServers.Get(ctx, gsa1.Status.GameServerName, metav1.GetOptions{})
  1314  
  1315  		if err != nil && errors.IsNotFound(err) {
  1316  			return true, nil
  1317  		}
  1318  
  1319  		return false, err
  1320  	})
  1321  	assert.Nil(t, err)
  1322  
  1323  	// now wait for another one to come along
  1324  	framework.AssertFleetCondition(t, preferred, e2e.FleetReadyCount(preferred.Spec.Replicas))
  1325  
  1326  	gsa3, err := framework.AgonesClient.AllocationV1().GameServerAllocations(framework.Namespace).Create(ctx, gsa.DeepCopy(), metav1.CreateOptions{})
  1327  	if assert.Nil(t, err) {
  1328  		assert.Equal(t, allocationv1.GameServerAllocationAllocated, gsa3.Status.State)
  1329  		gs, err := gameServers.Get(ctx, gsa3.Status.GameServerName, metav1.GetOptions{})
  1330  		assert.Nil(t, err)
  1331  		assert.Equal(t, preferred.ObjectMeta.Name, gs.ObjectMeta.Labels[agonesv1.FleetNameLabel])
  1332  	}
  1333  }
  1334  
  1335  func TestGameServerAllocationReturnLabels(t *testing.T) {
  1336  	t.Parallel()
  1337  	ctx := context.Background()
  1338  
  1339  	fleets := framework.AgonesClient.AgonesV1().Fleets(framework.Namespace)
  1340  	gameServers := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace)
  1341  	role := "role"
  1342  	label := map[string]string{role: t.Name()}
  1343  	annotationKey := "someAnnotation"
  1344  	annotationValue := "someValue"
  1345  	annotations := map[string]string{annotationKey: annotationValue}
  1346  
  1347  	flt := defaultFleet(framework.Namespace)
  1348  	flt.Spec.Replicas = 1
  1349  	flt.Spec.Template.ObjectMeta.Labels = label
  1350  	flt.Spec.Template.ObjectMeta.Annotations = annotations
  1351  	flt, err := fleets.Create(ctx, flt, metav1.CreateOptions{})
  1352  	defer fleets.Delete(ctx, flt.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint:errcheck
  1353  	require.NoError(t, err)
  1354  
  1355  	framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas))
  1356  
  1357  	gsa := &allocationv1.GameServerAllocation{ObjectMeta: metav1.ObjectMeta{GenerateName: "allocation-"},
  1358  		Spec: allocationv1.GameServerAllocationSpec{
  1359  			Selectors: []allocationv1.GameServerSelector{
  1360  				{LabelSelector: metav1.LabelSelector{MatchLabels: label}},
  1361  			},
  1362  		}}
  1363  
  1364  	gsa, err = framework.AgonesClient.AllocationV1().GameServerAllocations(framework.Namespace).Create(ctx, gsa.DeepCopy(), metav1.CreateOptions{})
  1365  	require.NoError(t, err)
  1366  
  1367  	assert.Equal(t, allocationv1.GameServerAllocationAllocated, gsa.Status.State)
  1368  	assert.Equal(t, t.Name(), gsa.Status.Metadata.Labels[role])
  1369  	assert.Equal(t, flt.ObjectMeta.Name, gsa.Status.Metadata.Labels[agonesv1.FleetNameLabel])
  1370  	assert.Equal(t, annotationValue, gsa.Status.Metadata.Annotations[annotationKey])
  1371  	gs, err := gameServers.Get(ctx, gsa.Status.GameServerName, metav1.GetOptions{})
  1372  	require.NoError(t, err)
  1373  	assert.Equal(t, flt.ObjectMeta.Name, gs.ObjectMeta.Labels[agonesv1.FleetNameLabel])
  1374  }
  1375  
  1376  func TestGameServerAllocationDeletionOnUnAllocate(t *testing.T) {
  1377  	t.Parallel()
  1378  	ctx := context.Background()
  1379  
  1380  	allocations := framework.AgonesClient.AllocationV1().GameServerAllocations(framework.Namespace)
  1381  
  1382  	gsa := &allocationv1.GameServerAllocation{ObjectMeta: metav1.ObjectMeta{GenerateName: "allocation-"},
  1383  		Spec: allocationv1.GameServerAllocationSpec{
  1384  			Selectors: []allocationv1.GameServerSelector{{LabelSelector: metav1.LabelSelector{MatchLabels: map[string]string{"never": "goingtohappen"}}}},
  1385  		}}
  1386  
  1387  	gsa, err := allocations.Create(ctx, gsa.DeepCopy(), metav1.CreateOptions{})
  1388  	if assert.Nil(t, err) {
  1389  		assert.Equal(t, allocationv1.GameServerAllocationUnAllocated, gsa.Status.State)
  1390  	}
  1391  }
  1392  
  1393  func TestGameServerAllocationDuringMultipleAllocationClients(t *testing.T) {
  1394  	t.Parallel()
  1395  	log := e2e.TestLogger(t)
  1396  	ctx := context.Background()
  1397  
  1398  	fleets := framework.AgonesClient.AgonesV1().Fleets(framework.Namespace)
  1399  	label := map[string]string{"role": t.Name()}
  1400  
  1401  	preferred := defaultFleet(framework.Namespace)
  1402  	preferred.ObjectMeta.GenerateName = "preferred-"
  1403  	preferred.Spec.Replicas = 150
  1404  	preferred.Spec.Template.ObjectMeta.Labels = label
  1405  	preferred, err := fleets.Create(ctx, preferred, metav1.CreateOptions{})
  1406  	if assert.Nil(t, err) {
  1407  		defer fleets.Delete(ctx, preferred.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint:errcheck
  1408  	} else {
  1409  		assert.FailNow(t, "could not create first fleet")
  1410  	}
  1411  
  1412  	framework.AssertFleetCondition(t, preferred, e2e.FleetReadyCount(preferred.Spec.Replicas))
  1413  
  1414  	// scale down before starting allocation
  1415  	preferred = scaleFleetPatch(ctx, t, preferred, preferred.Spec.Replicas-20)
  1416  
  1417  	gsa := &allocationv1.GameServerAllocation{ObjectMeta: metav1.ObjectMeta{GenerateName: "allocation-"},
  1418  		Spec: allocationv1.GameServerAllocationSpec{
  1419  			Selectors: []allocationv1.GameServerSelector{
  1420  				{LabelSelector: metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: "preferred"}}},
  1421  				{LabelSelector: metav1.LabelSelector{MatchLabels: label}},
  1422  			},
  1423  		}}
  1424  
  1425  	allocatedGS := sync.Map{}
  1426  
  1427  	log.Info("Starting 100 allocation attempts")
  1428  	var wg sync.WaitGroup
  1429  
  1430  	// Allocate GS by 10 clients in parallel while the fleet is scaling down
  1431  	for i := 0; i < 10; i++ {
  1432  		wg.Add(1)
  1433  
  1434  		go func() {
  1435  			defer wg.Done()
  1436  			for j := 0; j < 10; j++ {
  1437  				gsa1, err := framework.AgonesClient.AllocationV1().GameServerAllocations(framework.Namespace).Create(ctx, gsa.DeepCopy(), metav1.CreateOptions{})
  1438  				if err == nil {
  1439  					allocatedGS.LoadOrStore(gsa1.Status.GameServerName, true)
  1440  				} else {
  1441  					log.Infof("Allocation error: %v", err)
  1442  				}
  1443  			}
  1444  		}()
  1445  	}
  1446  
  1447  	// scale down further while allocating
  1448  	time.Sleep(1 * time.Second)
  1449  	log.Infof("Scaling Fleet down by 10 replicas")
  1450  	scaleFleetPatch(ctx, t, preferred, preferred.Spec.Replicas-10)
  1451  
  1452  	wg.Wait()
  1453  	log.Infof("Finished allocation attempts")
  1454  
  1455  	// count the number of unique game servers allocated
  1456  	// there should not be any duplicate
  1457  	uniqueAllocatedGSs := 0
  1458  	allocatedGS.Range(func(_, _ interface{}) bool {
  1459  		uniqueAllocatedGSs++
  1460  		return true
  1461  	})
  1462  
  1463  	// TODO: Compromising on the expected allocation count to be between 98 to 100 due to a known allocation issue. Please check: [https://github.com/googleforgames/agones/issues/3553]
  1464  	switch {
  1465  	case uniqueAllocatedGSs < 98:
  1466  		t.Errorf("Test failed: Less than 98 GameServers were allocated. Allocated: %d", uniqueAllocatedGSs)
  1467  	case uniqueAllocatedGSs < 100:
  1468  		t.Logf("Number of GameServers Allocated: %d. This might be due to a known allocation issue. Please check: [https://github.com/googleforgames/agones/issues/3553]", uniqueAllocatedGSs)
  1469  	default:
  1470  		t.Logf("Number of GameServers allocated: %d. This matches the expected outcome.", uniqueAllocatedGSs)
  1471  	}
  1472  }