From 62d30b3662390a665213f29af6dca2515bfce03e Mon Sep 17 00:00:00 2001 From: Kalaiselvim <117940852+Kalaiselvi84@users.noreply.github.com> Date: Fri, 11 Aug 2023 10:44:08 -0700 Subject: [PATCH] Graduate StateAllocationFilter to Stable (#3308) * Graduate StateAllocationFilter to Stable * resolved lint * revised necessary modification * GameServerState update * modified expected values in find_test.go * one label test deleted * make gen-api-docs target made change in aones_crd_api_reference.html * Alter shortcode content * Elimination of FeatureStateAllocationFilter * removed log for state allocation filter --- cloudbuild.yaml | 4 +- examples/gameserverallocation-deprecated.yaml | 4 +- examples/gameserverallocation.yaml | 2 - install/helm/agones/defaultfeaturegates.yaml | 1 - pkg/allocation/converters/converter.go | 18 ++- pkg/allocation/converters/converter_test.go | 36 ++++-- .../allocation/v1/gameserverallocation.go | 23 ++-- .../v1/gameserverallocation_test.go | 13 +- pkg/gameserverallocations/allocation_cache.go | 20 +-- .../allocation_cache_test.go | 104 +--------------- pkg/gameserverallocations/allocator_test.go | 2 +- pkg/gameserverallocations/find_test.go | 57 +-------- pkg/util/runtime/features.go | 4 - site/content/en/docs/Guides/feature-stages.md | 1 - .../content/en/docs/Guides/troubleshooting.md | 2 +- .../high-density-gameservers.md | 2 + .../Integration Patterns/player-capacity.md | 7 ++ .../Reference/agones_crd_api_reference.html | 13 +- .../en/docs/Reference/gameserverallocation.md | 115 ++++++++++++++++++ test/e2e/allocator_test.go | 6 +- test/e2e/gameserverallocation_test.go | 8 +- 21 files changed, 187 insertions(+), 255 deletions(-) diff --git a/cloudbuild.yaml b/cloudbuild.yaml index e29ba0d92d..6f32d696a3 100644 --- a/cloudbuild.yaml +++ b/cloudbuild.yaml @@ -325,10 +325,10 @@ steps: region=${versionsAndRegions[$version]} if [ $cloudProduct = generic ] then - featureWithGate="StateAllocationFilter=false&PlayerAllocationFilter=true&PlayerTracking=true&ResetMetricsOnDelete=false&PodHostname=false&SplitControllerAndExtensions=false&FleetAllocationOverflow=true&Example=true" + featureWithGate="PlayerAllocationFilter=true&PlayerTracking=true&ResetMetricsOnDelete=false&PodHostname=false&SplitControllerAndExtensions=false&FleetAllocationOverflow=true&Example=true" testCluster="standard-e2e-test-cluster-${version//./-}" else - featureWithGate="StateAllocationFilter=false&PlayerAllocationFilter=true&PlayerTracking=true&ResetMetricsOnDelete=false&PodHostname=false&SplitControllerAndExtensions=true&FleetAllocationOverflow=true&Example=true" + featureWithGate="PlayerAllocationFilter=true&PlayerTracking=true&ResetMetricsOnDelete=false&PodHostname=false&SplitControllerAndExtensions=true&FleetAllocationOverflow=true&Example=true" testCluster="gke-autopilot-e2e-test-cluster-${version//./-}" fi featureWithoutGate="" diff --git a/examples/gameserverallocation-deprecated.yaml b/examples/gameserverallocation-deprecated.yaml index d50e464694..ee5b64ff56 100644 --- a/examples/gameserverallocation-deprecated.yaml +++ b/examples/gameserverallocation-deprecated.yaml @@ -37,11 +37,9 @@ spec: game: my-game matchExpressions: - {key: tier, operator: In, values: [cache]} - # [Stage:Beta] - # [FeatureFlag:StateAllocationFilter] # Specifies which State is the filter to be used when attempting to retrieve a GameServer # via Allocation. Defaults to "Ready". The only other option is "Allocated", which can be used in conjunction with - # label/annotation/player selectors to retrieve an already Allocated GameServer. + # label/annotation/player selectors to retrieve an already Allocated GameServer. gameServerState: Ready # [Stage:Alpha] # [FeatureFlag:PlayerAllocationFilter] diff --git a/examples/gameserverallocation.yaml b/examples/gameserverallocation.yaml index 8e36e3702e..8e365aa3d2 100644 --- a/examples/gameserverallocation.yaml +++ b/examples/gameserverallocation.yaml @@ -46,8 +46,6 @@ spec: game: my-game matchExpressions: - {key: tier, operator: In, values: [cache]} - # [Stage:Beta] - # [FeatureFlag:StateAllocationFilter] # Specifies which State is the filter to be used when attempting to retrieve a GameServer # via Allocation. Defaults to "Ready". The only other option is "Allocated", which can be used in conjunction with # label/annotation/player selectors to retrieve an already Allocated GameServer. diff --git a/install/helm/agones/defaultfeaturegates.yaml b/install/helm/agones/defaultfeaturegates.yaml index dc8a8b4e1d..38587fa906 100644 --- a/install/helm/agones/defaultfeaturegates.yaml +++ b/install/helm/agones/defaultfeaturegates.yaml @@ -18,7 +18,6 @@ PodHostname: true ResetMetricsOnDelete: true SplitControllerAndExtensions: true -StateAllocationFilter: true # Alpha features PlayerAllocationFilter: false diff --git a/pkg/allocation/converters/converter.go b/pkg/allocation/converters/converter.go index 3da938875e..69f97e4dea 100644 --- a/pkg/allocation/converters/converter.go +++ b/pkg/allocation/converters/converter.go @@ -164,15 +164,13 @@ func convertGameServerSelectorToInternalGameServerSelector(in *pb.GameServerSele LabelSelector: metav1.LabelSelector{MatchLabels: in.GetMatchLabels()}, } - if runtime.FeatureEnabled(runtime.FeatureStateAllocationFilter) { - switch in.GameServerState { - case pb.GameServerSelector_ALLOCATED: - allocated := agonesv1.GameServerStateAllocated - result.GameServerState = &allocated - case pb.GameServerSelector_READY: - ready := agonesv1.GameServerStateReady - result.GameServerState = &ready - } + switch in.GameServerState { + case pb.GameServerSelector_ALLOCATED: + allocated := agonesv1.GameServerStateAllocated + result.GameServerState = &allocated + case pb.GameServerSelector_READY: + ready := agonesv1.GameServerStateReady + result.GameServerState = &ready } if runtime.FeatureEnabled(runtime.FeaturePlayerAllocationFilter) && in.Players != nil { @@ -217,7 +215,7 @@ func convertInternalGameServerSelectorToGameServer(in *allocationv1.GameServerSe MatchLabels: in.MatchLabels, } - if runtime.FeatureEnabled(runtime.FeatureStateAllocationFilter) && in.GameServerState != nil { + if in.GameServerState != nil { switch *in.GameServerState { case agonesv1.GameServerStateReady: result.GameServerState = pb.GameServerSelector_READY diff --git a/pkg/allocation/converters/converter_test.go b/pkg/allocation/converters/converter_test.go index 52c7b573ba..b32db446d3 100644 --- a/pkg/allocation/converters/converter_test.go +++ b/pkg/allocation/converters/converter_test.go @@ -42,8 +42,8 @@ func TestConvertAllocationRequestToGameServerAllocation(t *testing.T) { want *allocationv1.GameServerAllocation }{ { - name: "all fields are set (StateAllocationFilter, PlayerAllocationFilter, CountsAndListsFilter)", - features: fmt.Sprintf("%s=true&%s=true&%s=true", runtime.FeaturePlayerAllocationFilter, runtime.FeatureStateAllocationFilter, runtime.FeatureCountsAndLists), + name: "all fields are set (PlayerAllocationFilter, CountsAndListsFilter)", + features: fmt.Sprintf("%s=true&%s=true", runtime.FeaturePlayerAllocationFilter, runtime.FeatureCountsAndLists), in: &pb.AllocationRequest{ Namespace: "ns", MultiClusterSetting: &pb.MultiClusterSetting{ @@ -217,7 +217,7 @@ func TestConvertAllocationRequestToGameServerAllocation(t *testing.T) { }, { name: "all fields are set", - features: fmt.Sprintf("%s=false&%s=false&%s=false", runtime.FeaturePlayerAllocationFilter, runtime.FeatureStateAllocationFilter, runtime.FeatureCountsAndLists), + features: fmt.Sprintf("%s=false&%s=false", runtime.FeaturePlayerAllocationFilter, runtime.FeatureCountsAndLists), in: &pb.AllocationRequest{ Namespace: "ns", MultiClusterSetting: &pb.MultiClusterSetting{ @@ -281,7 +281,9 @@ func TestConvertAllocationRequestToGameServerAllocation(t *testing.T) { MatchLabels: map[string]string{ "c": "d", }, - }}, + }, + GameServerState: &ready, + }, Preferred: []allocationv1.GameServerSelector{ { LabelSelector: metav1.LabelSelector{ @@ -289,6 +291,7 @@ func TestConvertAllocationRequestToGameServerAllocation(t *testing.T) { "e": "f", }, }, + GameServerState: &ready, }, { LabelSelector: metav1.LabelSelector{ @@ -296,6 +299,7 @@ func TestConvertAllocationRequestToGameServerAllocation(t *testing.T) { "g": "h", }, }, + GameServerState: &ready, }, }, Selectors: []allocationv1.GameServerSelector{ @@ -305,6 +309,7 @@ func TestConvertAllocationRequestToGameServerAllocation(t *testing.T) { "m": "n", }, }, + GameServerState: &ready, }, }, Scheduling: apis.Packed, @@ -318,7 +323,7 @@ func TestConvertAllocationRequestToGameServerAllocation(t *testing.T) { }, { name: "empty fields to GSA", - features: fmt.Sprintf("%s=false&%s=false&%s=false", runtime.FeaturePlayerAllocationFilter, runtime.FeatureStateAllocationFilter, runtime.FeatureCountsAndLists), + features: fmt.Sprintf("%s=false&%s=false", runtime.FeaturePlayerAllocationFilter, runtime.FeatureCountsAndLists), in: &pb.AllocationRequest{ Namespace: "", MultiClusterSetting: &pb.MultiClusterSetting{}, @@ -336,12 +341,15 @@ func TestConvertAllocationRequestToGameServerAllocation(t *testing.T) { Enabled: false, }, Scheduling: apis.Distributed, + Required: allocationv1.GameServerSelector{ + GameServerState: &ready, + }, }, }, }, { - name: "empty fields to GSA (StateAllocationFilter, PlayerAllocationFilter, CountsAndListsFilter)", - features: fmt.Sprintf("%s=true&%s=true&%s=true", runtime.FeaturePlayerAllocationFilter, runtime.FeatureStateAllocationFilter, runtime.FeatureCountsAndLists), + name: "empty fields to GSA (PlayerAllocationFilter, CountsAndListsFilter)", + features: fmt.Sprintf("%s=true&%s=true", runtime.FeaturePlayerAllocationFilter, runtime.FeatureCountsAndLists), in: &pb.AllocationRequest{ Namespace: "", MultiClusterSetting: &pb.MultiClusterSetting{}, @@ -367,7 +375,7 @@ func TestConvertAllocationRequestToGameServerAllocation(t *testing.T) { }, { name: "empty fields to GSA with selectors fields", - features: fmt.Sprintf("%s=false&%s=false&%s=false", runtime.FeaturePlayerAllocationFilter, runtime.FeatureStateAllocationFilter, runtime.FeatureCountsAndLists), + features: fmt.Sprintf("%s=false&%s=false", runtime.FeaturePlayerAllocationFilter, runtime.FeatureCountsAndLists), in: &pb.AllocationRequest{ Namespace: "", MultiClusterSetting: &pb.MultiClusterSetting{}, @@ -383,14 +391,16 @@ func TestConvertAllocationRequestToGameServerAllocation(t *testing.T) { MultiClusterSetting: allocationv1.MultiClusterSetting{ Enabled: false, }, - Selectors: []allocationv1.GameServerSelector{{}}, + Selectors: []allocationv1.GameServerSelector{{ + GameServerState: &ready, + }}, Scheduling: apis.Distributed, }, }, }, { - name: "empty fields to GSA (StateAllocationFilter, PlayerAllocationFilter, CountsAndListsFilter) with selectors fields", - features: fmt.Sprintf("%s=true&%s=true&%s=true", runtime.FeaturePlayerAllocationFilter, runtime.FeatureStateAllocationFilter, runtime.FeatureCountsAndLists), + name: "empty fields to GSA (PlayerAllocationFilter, CountsAndListsFilter) with selectors fields", + features: fmt.Sprintf("%s=true&%s=true", runtime.FeaturePlayerAllocationFilter, runtime.FeatureCountsAndLists), in: &pb.AllocationRequest{ Namespace: "", MultiClusterSetting: &pb.MultiClusterSetting{}, @@ -501,8 +511,8 @@ func TestConvertAllocationRequestToGameServerAllocation(t *testing.T) { }, }, { - name: "partially empty Counters and Lists fields to GSA (StateAllocationFilter, PlayerAllocationFilter, CountsAndListsFilter)", - features: fmt.Sprintf("%s=true&%s=true&%s=true", runtime.FeaturePlayerAllocationFilter, runtime.FeatureStateAllocationFilter, runtime.FeatureCountsAndLists), + name: "partially empty Counters and Lists fields to GSA (PlayerAllocationFilter, CountsAndListsFilter)", + features: fmt.Sprintf("%s=true&%s=true", runtime.FeaturePlayerAllocationFilter, runtime.FeatureCountsAndLists), in: &pb.AllocationRequest{ Namespace: "", MultiClusterSetting: &pb.MultiClusterSetting{}, diff --git a/pkg/apis/allocation/v1/gameserverallocation.go b/pkg/apis/allocation/v1/gameserverallocation.go index 0acee39c88..3982f20c8c 100644 --- a/pkg/apis/allocation/v1/gameserverallocation.go +++ b/pkg/apis/allocation/v1/gameserverallocation.go @@ -117,9 +117,6 @@ type GameServerAllocationSpec struct { type GameServerSelector struct { // See: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ metav1.LabelSelector `json:",inline"` - // [Stage:Beta] - // [FeatureFlag:StateAllocationFilter] - // +optional // GameServerState specifies which State is the filter to be used when attempting to retrieve a GameServer // via Allocation. Defaults to "Ready". The only other option is "Allocated", which can be used in conjunction with // label/annotation/player selectors to retrieve an already Allocated GameServer. @@ -187,11 +184,9 @@ type ListAction struct { // ApplyDefaults applies default values func (s *GameServerSelector) ApplyDefaults() { - if runtime.FeatureEnabled(runtime.FeatureStateAllocationFilter) { - if s.GameServerState == nil { - state := agonesv1.GameServerStateReady - s.GameServerState = &state - } + if s.GameServerState == nil { + state := agonesv1.GameServerStateReady + s.GameServerState = &state } if runtime.FeatureEnabled(runtime.FeaturePlayerAllocationFilter) { @@ -229,10 +224,8 @@ func (s *GameServerSelector) Matches(gs *agonesv1.GameServer) bool { } // then if state is being checked, check state - if runtime.FeatureEnabled(runtime.FeatureStateAllocationFilter) { - if s.GameServerState != nil && gs.Status.State != *s.GameServerState { - return false - } + if s.GameServerState != nil && gs.Status.State != *s.GameServerState { + return false } // then if player count is being checked, check that @@ -371,10 +364,8 @@ func (s *GameServerSelector) Validate(fldPath *field.Path) field.ErrorList { allErrs = append(allErrs, field.Invalid(fldPath.Child("labelSelector"), s.LabelSelector, fmt.Sprintf("Error converting label selector: %s", err))) } - if runtime.FeatureEnabled(runtime.FeatureStateAllocationFilter) { - if s.GameServerState != nil && !(*s.GameServerState == agonesv1.GameServerStateAllocated || *s.GameServerState == agonesv1.GameServerStateReady) { - allErrs = append(allErrs, field.Invalid(fldPath.Child("gameServerState"), *s.GameServerState, "GameServerState must be either Allocated or Ready")) - } + if s.GameServerState != nil && !(*s.GameServerState == agonesv1.GameServerStateAllocated || *s.GameServerState == agonesv1.GameServerStateReady) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("gameServerState"), *s.GameServerState, "GameServerState must be either Allocated or Ready")) } if runtime.FeatureEnabled(runtime.FeaturePlayerAllocationFilter) && s.Players != nil { diff --git a/pkg/apis/allocation/v1/gameserverallocation_test.go b/pkg/apis/allocation/v1/gameserverallocation_test.go index e8899b34e3..4814aea089 100644 --- a/pkg/apis/allocation/v1/gameserverallocation_test.go +++ b/pkg/apis/allocation/v1/gameserverallocation_test.go @@ -41,7 +41,7 @@ func TestGameServerAllocationApplyDefaults(t *testing.T) { runtime.FeatureTestMutex.Lock() defer runtime.FeatureTestMutex.Unlock() - assert.NoError(t, runtime.ParseFeatures(fmt.Sprintf("%s=true&%s=true&%s=true", runtime.FeaturePlayerAllocationFilter, runtime.FeatureStateAllocationFilter, runtime.FeatureCountsAndLists))) + assert.NoError(t, runtime.ParseFeatures(fmt.Sprintf("%s=true&%s=true", runtime.FeaturePlayerAllocationFilter, runtime.FeatureCountsAndLists))) gsa = &GameServerAllocation{} gsa.ApplyDefaults() @@ -114,9 +114,8 @@ func TestGameServerSelectorApplyDefaults(t *testing.T) { runtime.FeatureTestMutex.Lock() defer runtime.FeatureTestMutex.Unlock() - assert.NoError(t, runtime.ParseFeatures(fmt.Sprintf("%s=true&%s=true&%s=true", + assert.NoError(t, runtime.ParseFeatures(fmt.Sprintf("%s=true&%s=true", runtime.FeaturePlayerAllocationFilter, - runtime.FeatureStateAllocationFilter, runtime.FeatureCountsAndLists))) s := &GameServerSelector{} @@ -156,7 +155,7 @@ func TestGameServerSelectorValidate(t *testing.T) { runtime.FeatureTestMutex.Lock() defer runtime.FeatureTestMutex.Unlock() - assert.NoError(t, runtime.ParseFeatures(fmt.Sprintf("%s=true&%s=true&%s=true", runtime.FeaturePlayerAllocationFilter, runtime.FeatureStateAllocationFilter, runtime.FeatureCountsAndLists))) + assert.NoError(t, runtime.ParseFeatures(fmt.Sprintf("%s=true&%s=true", runtime.FeaturePlayerAllocationFilter, runtime.FeatureCountsAndLists))) allocated := agonesv1.GameServerStateAllocated starting := agonesv1.GameServerStateStarting @@ -441,7 +440,6 @@ func TestGameServerSelectorMatches(t *testing.T) { matches: false, }, "state filter, pass": { - features: string(runtime.FeatureStateAllocationFilter) + "=true", selector: &GameServerSelector{ GameServerState: &allocatedState, }, @@ -449,7 +447,6 @@ func TestGameServerSelectorMatches(t *testing.T) { matches: true, }, "state filter, fail": { - features: string(runtime.FeatureStateAllocationFilter) + "=true", selector: &GameServerSelector{ GameServerState: &allocatedState, }, @@ -511,7 +508,7 @@ func TestGameServerSelectorMatches(t *testing.T) { matches: true, }, "combo": { - features: string(runtime.FeaturePlayerAllocationFilter) + "=true&" + string(runtime.FeatureStateAllocationFilter) + "=true", + features: string(runtime.FeaturePlayerAllocationFilter) + "=true&", selector: &GameServerSelector{ LabelSelector: metav1.LabelSelector{ MatchLabels: map[string]string{"colour": "blue"}, @@ -1060,7 +1057,7 @@ func TestGameServerAllocationValidate(t *testing.T) { runtime.FeatureTestMutex.Lock() defer runtime.FeatureTestMutex.Unlock() - assert.NoError(t, runtime.ParseFeatures(fmt.Sprintf("%s=true&%s=true", runtime.FeaturePlayerAllocationFilter, runtime.FeatureStateAllocationFilter))) + assert.NoError(t, runtime.ParseFeatures(fmt.Sprintf("%s=true", runtime.FeaturePlayerAllocationFilter))) // invalid player selection gsa = &GameServerAllocation{ diff --git a/pkg/gameserverallocations/allocation_cache.go b/pkg/gameserverallocations/allocation_cache.go index 73bb692c84..1f21f84a18 100644 --- a/pkg/gameserverallocations/allocation_cache.go +++ b/pkg/gameserverallocations/allocation_cache.go @@ -36,11 +36,6 @@ import ( type matcher func(*agonesv1.GameServer) bool -// readyGameServerMatcher return true when a GameServer is in a Ready state. -func readyGameServerMatcher(gs *agonesv1.GameServer) bool { - return gs.Status.State == agonesv1.GameServerStateReady -} - // readyOrAllocatedGameServerMatcher returns true when a GameServer is in a Ready or Allocated state. func readyOrAllocatedGameServerMatcher(gs *agonesv1.GameServer) bool { return gs.Status.State == agonesv1.GameServerStateReady || gs.Status.State == agonesv1.GameServerStateAllocated @@ -63,12 +58,7 @@ func NewAllocationCache(informer informerv1.GameServerInformer, counter *gameser gameServerSynced: informer.Informer().HasSynced, gameServerLister: informer.Lister(), counter: counter, - matcher: readyGameServerMatcher, - } - - // if we can do state filtering, then cache both Ready and Allocated GameServers - if runtime.FeatureEnabled(runtime.FeatureStateAllocationFilter) { - c.matcher = readyOrAllocatedGameServerMatcher + matcher: readyOrAllocatedGameServerMatcher, } _, _ = informer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ @@ -184,11 +174,9 @@ func (c *AllocationCache) ListSortedGameServers(gsa *allocationv1.GameServerAllo gs1 := list[i] gs2 := list[j] - if runtime.FeatureEnabled(runtime.FeatureStateAllocationFilter) { - // Search Allocated GameServers first. - if gs1.Status.State != gs2.Status.State { - return gs1.Status.State == agonesv1.GameServerStateAllocated - } + // Search Allocated GameServers first. + if gs1.Status.State != gs2.Status.State { + return gs1.Status.State == agonesv1.GameServerStateAllocated } c1, ok := counts[gs1.Status.NodeName] diff --git a/pkg/gameserverallocations/allocation_cache_test.go b/pkg/gameserverallocations/allocation_cache_test.go index a6071f57a4..6c0fdf0455 100644 --- a/pkg/gameserverallocations/allocation_cache_test.go +++ b/pkg/gameserverallocations/allocation_cache_test.go @@ -80,22 +80,8 @@ func TestAllocationCacheListSortedGameServers(t *testing.T) { features string gsa *allocationv1.GameServerAllocation }{ - "most allocated": { - // node1: 1 ready, 1 allocated, node2: 1 ready - list: []agonesv1.GameServer{gs1, gs2, gs3}, - test: func(t *testing.T, list []*agonesv1.GameServer) { - assert.Len(t, list, 2) - if !assert.Equal(t, []*agonesv1.GameServer{&gs1, &gs2}, list) { - for _, gs := range list { - logrus.WithField("name", gs.Name).Info("game server") - } - } - }, - features: fmt.Sprintf("%s=false", runtime.FeatureStateAllocationFilter), - }, "allocated first (StateAllocationFilter)": { - list: []agonesv1.GameServer{gs1, gs2, gs3}, - features: fmt.Sprintf("%s=true", runtime.FeatureStateAllocationFilter), + list: []agonesv1.GameServer{gs1, gs2, gs3}, test: func(t *testing.T, list []*agonesv1.GameServer) { assert.Equal(t, []*agonesv1.GameServer{&gs3, &gs1, &gs2}, list) }, @@ -500,14 +486,8 @@ func TestAllocationCacheCompareGameServers(t *testing.T) { } } -func TestAllocatorRunCacheSyncFeatureStateAllocationFilter(t *testing.T) { +func TestAllocatorRunCacheSync(t *testing.T) { t.Parallel() - - // TODO(markmandel): When this feature gets promoted to stable, replace test TestAllocatorRunCacheSync below with this test. - runtime.FeatureTestMutex.Lock() - defer runtime.FeatureTestMutex.Unlock() - require.NoError(t, runtime.ParseFeatures(string(runtime.FeatureStateAllocationFilter)+"=true")) - cache, m := newFakeAllocationCache() gsWatch := watch.NewFake() @@ -594,86 +574,6 @@ func TestAllocatorRunCacheSyncFeatureStateAllocationFilter(t *testing.T) { assertCacheEntries(0) } -func TestAllocatorRunCacheSync(t *testing.T) { - t.Parallel() - - cache, m := newFakeAllocationCache() - gsWatch := watch.NewFake() - - m.AgonesClient.AddWatchReactor("gameservers", k8stesting.DefaultWatchReactor(gsWatch, nil)) - - ctx, cancel := agtesting.StartInformers(m, cache.gameServerSynced) - defer cancel() - - assertCacheEntries := func(expected int) { - count := 0 - err := wait.PollImmediate(time.Second, 5*time.Second, func() (done bool, err error) { - count = 0 - cache.cache.Range(func(key string, gs *agonesv1.GameServer) bool { - count++ - return true - }) - - return count == expected, nil - }) - - assert.NoError(t, err, fmt.Sprintf("Should be %d values", expected)) - } - - go func() { - err := cache.Run(ctx) - assert.Nil(t, err) - }() - - gs := agonesv1.GameServer{ - ObjectMeta: metav1.ObjectMeta{Name: "gs1", Namespace: "default"}, - Status: agonesv1.GameServerStatus{State: agonesv1.GameServerStateStarting}, - } - - logrus.Info("adding ready game server") - gsWatch.Add(gs.DeepCopy()) - - assertCacheEntries(0) - - gs.Status.State = agonesv1.GameServerStateReady - gsWatch.Modify(gs.DeepCopy()) - - assertCacheEntries(1) - - // try again, should be no change - gs.Status.State = agonesv1.GameServerStateReady - gsWatch.Modify(gs.DeepCopy()) - - assertCacheEntries(1) - - // now move it to Shutdown - gs.Status.State = agonesv1.GameServerStateShutdown - gsWatch.Modify(gs.DeepCopy()) - - assertCacheEntries(0) - - // add back in ready gameserver - gs.Status.State = agonesv1.GameServerStateReady - gsWatch.Modify(gs.DeepCopy()) - assertCacheEntries(1) - - // update with deletion timestamp - n := metav1.Now() - deletedCopy := gs.DeepCopy() - deletedCopy.ObjectMeta.DeletionTimestamp = &n - gsWatch.Modify(deletedCopy) - assertCacheEntries(0) - - // add back in ready gameserver - gs.Status.State = agonesv1.GameServerStateReady - gsWatch.Modify(gs.DeepCopy()) - assertCacheEntries(1) - - // now actually delete it - gsWatch.Delete(gs.DeepCopy()) - assertCacheEntries(0) -} - func newFakeAllocationCache() (*AllocationCache, agtesting.Mocks) { m := agtesting.NewMocks() cache := NewAllocationCache(m.AgonesInformerFactory.Agones().V1().GameServers(), gameservers.NewPerNodeCounter(m.KubeInformerFactory, m.AgonesInformerFactory), healthcheck.NewHandler()) diff --git a/pkg/gameserverallocations/allocator_test.go b/pkg/gameserverallocations/allocator_test.go index d572b8716c..f4588e47ca 100644 --- a/pkg/gameserverallocations/allocator_test.go +++ b/pkg/gameserverallocations/allocator_test.go @@ -453,7 +453,7 @@ func TestAllocatorAllocateOnGameServerUpdateError(t *testing.T) { // TODO: remove when `CountsAndLists` feature flag is moved to stable. runtime.FeatureTestMutex.Lock() defer runtime.FeatureTestMutex.Unlock() - require.NoError(t, runtime.ParseFeatures(fmt.Sprintf("%s=false&%s=false&%s=false", runtime.FeaturePlayerAllocationFilter, runtime.FeatureStateAllocationFilter, runtime.FeatureCountsAndLists))) + require.NoError(t, runtime.ParseFeatures(fmt.Sprintf("%s=false&%s=false", runtime.FeaturePlayerAllocationFilter, runtime.FeatureCountsAndLists))) a, m := newFakeAllocator() log := framework.TestLogger(t) diff --git a/pkg/gameserverallocations/find_test.go b/pkg/gameserverallocations/find_test.go index b98b8c6de8..f626c054c9 100644 --- a/pkg/gameserverallocations/find_test.go +++ b/pkg/gameserverallocations/find_test.go @@ -82,53 +82,6 @@ func TestFindGameServerForAllocationPacked(t *testing.T) { assert.NoError(t, err) }, }, - "one label": { - // nolint: dupl - list: []agonesv1.GameServer{ - {ObjectMeta: metav1.ObjectMeta{Name: "gs6", Namespace: defaultNs, Labels: oneLabel, DeletionTimestamp: &n}, Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady}}, - {ObjectMeta: metav1.ObjectMeta{Name: "gs1", Namespace: defaultNs, Labels: oneLabel}, Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady}}, - {ObjectMeta: metav1.ObjectMeta{Name: "gs2", Namespace: defaultNs, Labels: oneLabel}, Status: agonesv1.GameServerStatus{NodeName: "node2", State: agonesv1.GameServerStateReady}}, - {ObjectMeta: metav1.ObjectMeta{Name: "gs3", Namespace: defaultNs, Labels: oneLabel}, Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateAllocated}}, - {ObjectMeta: metav1.ObjectMeta{Name: "gs4", Namespace: defaultNs, Labels: oneLabel}, Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateAllocated}}, - {ObjectMeta: metav1.ObjectMeta{Name: "gs5", Namespace: defaultNs, Labels: oneLabel}, Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateError}}, - {ObjectMeta: metav1.ObjectMeta{Name: "gs6", Namespace: "does-not-apply", Labels: oneLabel}, Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady}}, - }, - test: func(t *testing.T, list []*agonesv1.GameServer) { - assert.Len(t, list, 3) - - gs, index, err := findGameServerForAllocation(gsa, list) - assert.NoError(t, err) - if !assert.NotNil(t, gs) { - assert.FailNow(t, "gameserver should not be nil") - } - assert.Equal(t, "node1", gs.Status.NodeName) - assert.Equal(t, "gs1", gs.ObjectMeta.Name) - assert.Equal(t, gs, list[index]) - assert.Equal(t, agonesv1.GameServerStateReady, gs.Status.State) - - // mock that the first found game server is allocated - list = append(list[:index], list[index+1:]...) - assert.Equal(t, agonesv1.GameServerStateReady, list[0].Status.State) - assert.Len(t, list, 2) - - gs, index, err = findGameServerForAllocation(gsa, list) - assert.NoError(t, err) - if !assert.NotNil(t, gs) { - assert.FailNow(t, "gameserver should not be nil") - } - assert.Equal(t, "node2", gs.Status.NodeName) - assert.Equal(t, "gs2", gs.ObjectMeta.Name) - assert.Equal(t, gs, list[index]) - assert.Equal(t, agonesv1.GameServerStateReady, gs.Status.State) - - list = nil - gs, _, err = findGameServerForAllocation(gsa, list) - assert.Error(t, err) - assert.Equal(t, ErrNoGameServer, err) - assert.Nil(t, gs) - }, - features: fmt.Sprintf("%s=false&%s=false", runtime.FeaturePlayerAllocationFilter, runtime.FeatureStateAllocationFilter), - }, "one label with player state (StateAllocationFilter)": { // nolint: dupl list: []agonesv1.GameServer{ @@ -186,7 +139,7 @@ func TestFindGameServerForAllocationPacked(t *testing.T) { assert.Equal(t, ErrNoGameServer, err) assert.Nil(t, gs) }, - features: fmt.Sprintf("%s=true&%s=true", runtime.FeaturePlayerAllocationFilter, runtime.FeatureStateAllocationFilter), + features: fmt.Sprintf("%s=true", runtime.FeaturePlayerAllocationFilter), }, "one label with player counts and state (PlayerAllocationFilter)": { list: []agonesv1.GameServer{ @@ -219,7 +172,7 @@ func TestFindGameServerForAllocationPacked(t *testing.T) { assert.Equal(t, gs, list[index]) assert.Equal(t, agonesv1.GameServerStateAllocated, gs.Status.State) }, - features: fmt.Sprintf("%s=true&%s=true", runtime.FeaturePlayerAllocationFilter, runtime.FeatureStateAllocationFilter), + features: fmt.Sprintf("%s=true", runtime.FeaturePlayerAllocationFilter), }, "preferred": { list: []agonesv1.GameServer{ @@ -256,7 +209,7 @@ func TestFindGameServerForAllocationPacked(t *testing.T) { assert.Equal(t, gs, list[index]) assert.Equal(t, agonesv1.GameServerStateReady, gs.Status.State) }, - features: fmt.Sprintf("%s=false&%s=false", runtime.FeaturePlayerAllocationFilter, runtime.FeatureStateAllocationFilter), + features: fmt.Sprintf("%s=false", runtime.FeaturePlayerAllocationFilter), }, "allocation trap": { list: []agonesv1.GameServer{ @@ -270,7 +223,7 @@ func TestFindGameServerForAllocationPacked(t *testing.T) { {ObjectMeta: metav1.ObjectMeta{Name: "gs8", Labels: oneLabel, Namespace: defaultNs}, Status: agonesv1.GameServerStatus{NodeName: "node2", State: agonesv1.GameServerStateReady}}, }, test: func(t *testing.T, list []*agonesv1.GameServer) { - assert.Len(t, list, 4) + assert.Len(t, list, 8) gs, index, err := findGameServerForAllocation(gsa, list) assert.Nil(t, err) @@ -278,7 +231,7 @@ func TestFindGameServerForAllocationPacked(t *testing.T) { assert.Equal(t, gs, list[index]) assert.Equal(t, agonesv1.GameServerStateReady, gs.Status.State) }, - features: fmt.Sprintf("%s=false&%s=false", runtime.FeaturePlayerAllocationFilter, runtime.FeatureStateAllocationFilter), + features: fmt.Sprintf("%s=false", runtime.FeaturePlayerAllocationFilter), }, } diff --git a/pkg/util/runtime/features.go b/pkg/util/runtime/features.go index 96a2149b72..b13e47b0de 100644 --- a/pkg/util/runtime/features.go +++ b/pkg/util/runtime/features.go @@ -34,9 +34,6 @@ const ( // FeatureSplitControllerAndExtensions is a feature flag that will split agones-controller into two deployments FeatureSplitControllerAndExtensions Feature = "SplitControllerAndExtensions" - // FeatureStateAllocationFilter is a feature flag that enables state filtering on Allocation. - FeatureStateAllocationFilter Feature = "StateAllocationFilter" - //////////////// // Alpha features @@ -104,7 +101,6 @@ var ( FeaturePodHostname: true, FeatureResetMetricsOnDelete: true, FeatureSplitControllerAndExtensions: true, - FeatureStateAllocationFilter: true, // Alpha features FeaturePlayerAllocationFilter: false, diff --git a/site/content/en/docs/Guides/feature-stages.md b/site/content/en/docs/Guides/feature-stages.md index befb4f07dc..6e751a34ea 100644 --- a/site/content/en/docs/Guides/feature-stages.md +++ b/site/content/en/docs/Guides/feature-stages.md @@ -45,7 +45,6 @@ The current set of `alpha` and `beta` feature gates: | [GameServer Stable Network ID]({{% ref "/docs/Reference/gameserver.md#stable-network-id" %}}) | `PodHostname` | Enabled | `Beta` | 1.32.0 | | [Reset Metric Export on Fleet / Autoscaler deletion]({{% relref "./metrics.md#dropping-metric-labels" %}}) | `ResetMetricsOnDelete` | Enabled | `Beta` | 1.32.0 | | [Split `agones-controller` ](https://github.com/googleforgames/agones/issues/2797) | `SplitControllerAndExtensions` | Enabled | `Beta` | 1.32.0 | -| [GameServer state filtering on GameServerAllocations](https://github.com/googleforgames/agones/issues/1239) | `StateAllocationFilter` | Enabled | `Beta` | 1.26.0 | | [GameServer player capacity filtering on GameServerAllocations](https://github.com/googleforgames/agones/issues/1239) | `PlayerAllocationFilter` | Disabled | `Alpha` | 1.14.0 | | [Player Tracking]({{< ref "/docs/Guides/player-tracking.md" >}}) | `PlayerTracking` | Disabled | `Alpha` | 1.6.0 | | [Allocated GameServers are notified on relevant Fleet Updates][fleet-updates] | `FleetAllocationOverflow` | Disabled | `Alpha` | 1.32.0 | diff --git a/site/content/en/docs/Guides/troubleshooting.md b/site/content/en/docs/Guides/troubleshooting.md index d50af0ee2b..35ceb31ff0 100644 --- a/site/content/en/docs/Guides/troubleshooting.md +++ b/site/content/en/docs/Guides/troubleshooting.md @@ -316,7 +316,7 @@ For example: $ kubectl logs -n agones-system agones-controller-7575dc59-7p2rg | head {"filename":"/home/agones/logs/agones-controller-20220615_211540.log","message":"logging to file","numbackups":99,"severity":"info","source":"main","time":"2022-06-15T21:15:40.309349789Z"} {"logLevel":"info","message":"Setting LogLevel configuration","severity":"info","source":"main","time":"2022-06-15T21:15:40.309403296Z"} -{"ctlConf":{"MinPort":7000,"MaxPort":8000,"SidecarImage":"gcr.io/agones-images/agones-sdk:1.23.0","SidecarCPURequest":"30m","SidecarCPULimit":"0","SidecarMemoryRequest":"0","SidecarMemoryLimit":"0","SdkServiceAccount":"agones-sdk","AlwaysPullSidecar":false,"PrometheusMetrics":true,"Stackdriver":false,"StackdriverLabels":"","KeyFile":"/home/agones/certs/server.key","CertFile":"/home/agones/certs/server.crt","KubeConfig":"","GCPProjectID":"","NumWorkers":100,"APIServerSustainedQPS":400,"APIServerBurstQPS":500,"LogDir":"/home/agones/logs","LogLevel":"info","LogSizeLimitMB":10000},"featureGates":"Example=true\u0026NodeExternalDNS=true\u0026PlayerAllocationFilter=false\u0026PlayerTracking=false\u0026StateAllocationFilter=false","message":"starting gameServer operator...","severity":"info","source":"main","time":"2022-06-15T21:15:40.309528802Z","version":"1.23.0"} +{"ctlConf":{"MinPort":7000,"MaxPort":8000,"SidecarImage":"gcr.io/agones-images/agones-sdk:1.23.0","SidecarCPURequest":"30m","SidecarCPULimit":"0","SidecarMemoryRequest":"0","SidecarMemoryLimit":"0","SdkServiceAccount":"agones-sdk","AlwaysPullSidecar":false,"PrometheusMetrics":true,"Stackdriver":false,"StackdriverLabels":"","KeyFile":"/home/agones/certs/server.key","CertFile":"/home/agones/certs/server.crt","KubeConfig":"","GCPProjectID":"","NumWorkers":100,"APIServerSustainedQPS":400,"APIServerBurstQPS":500,"LogDir":"/home/agones/logs","LogLevel":"info","LogSizeLimitMB":10000},"featureGates":"Example=true\u0026NodeExternalDNS=true\u0026PlayerAllocationFilter=false\u0026PlayerTracking=false","message":"starting gameServer operator...","severity":"info","source":"main","time":"2022-06-15T21:15:40.309528802Z","version":"1.23.0"} ... ``` diff --git a/site/content/en/docs/Integration Patterns/high-density-gameservers.md b/site/content/en/docs/Integration Patterns/high-density-gameservers.md index 770309e01c..722d062447 100644 --- a/site/content/en/docs/Integration Patterns/high-density-gameservers.md +++ b/site/content/en/docs/Integration Patterns/high-density-gameservers.md @@ -6,7 +6,9 @@ weight: 70 description: > How to run multiple concurrent game sessions in a single GameServer process. --- +{{% feature expiryVersion="1.34.0" %}} {{< beta title="Allocation State Filter" gate="StateAllocationFilter" >}} +{{% /feature %}} Depending on the setup and resource requirements of your game server process, sometimes it can be a more economical use of resources to run multiple concurrent game sessions from within a single `GameServer` instance. diff --git a/site/content/en/docs/Integration Patterns/player-capacity.md b/site/content/en/docs/Integration Patterns/player-capacity.md index 666bd25dfc..ae9a147174 100644 --- a/site/content/en/docs/Integration Patterns/player-capacity.md +++ b/site/content/en/docs/Integration Patterns/player-capacity.md @@ -7,10 +7,17 @@ description: > Find a `GameServer` that has room for a specific number of players. --- +{{% feature expiryVersion="1.34.0" %}} {{< alpha title="Player Tracking and Allocation Player Filter" gate="PlayerTracking,PlayerAllocationFilter" >}} {{< beta title="Allocation State Filter" gate="StateAllocationFilter" >}} +{{% /feature %}} +{{% feature publishVersion="1.34.0" %}} +{{< alpha +title="Player Tracking and Allocation Player Filter" +gate="PlayerTracking,PlayerAllocationFilter" >}} +{{% /feature %}} Using this approach, we are able to be able to make a request that is akin to: "Find me a `GameServer` that is already allocated, with room for _n_ number of players, and if one is not available, allocate me a `Ready` `GameServer`". diff --git a/site/content/en/docs/Reference/agones_crd_api_reference.html b/site/content/en/docs/Reference/agones_crd_api_reference.html index 6cd1a344fb..982482612b 100644 --- a/site/content/en/docs/Reference/agones_crd_api_reference.html +++ b/site/content/en/docs/Reference/agones_crd_api_reference.html @@ -2669,14 +2669,6 @@
[Stage:Beta] -[FeatureFlag:StateAllocationFilter] -GameServerState specifies which State is the filter to be used when attempting to retrieve a GameServer -via Allocation. Defaults to “Ready”. The only other option is “Allocated”, which can be used in conjunction with -label/annotation/player selectors to retrieve an already Allocated GameServer.
-[Stage:Beta] -[FeatureFlag:StateAllocationFilter] -GameServerState specifies which State is the filter to be used when attempting to retrieve a GameServer +
GameServerState specifies which State is the filter to be used when attempting to retrieve a GameServer via Allocation. Defaults to “Ready”. The only other option is “Allocated”, which can be used in conjunction with label/annotation/player selectors to retrieve an already Allocated GameServer.