agones.dev/agones@v1.53.0/pkg/gameserverallocations/allocator_test.go (about) 1 // Copyright 2021 Google LLC All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package gameserverallocations 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 "testing" 22 "time" 23 24 "agones.dev/agones/pkg/apis" 25 agonesv1 "agones.dev/agones/pkg/apis/agones/v1" 26 allocationv1 "agones.dev/agones/pkg/apis/allocation/v1" 27 multiclusterv1 "agones.dev/agones/pkg/apis/multicluster/v1" 28 "agones.dev/agones/pkg/gameservers" 29 agtesting "agones.dev/agones/pkg/testing" 30 "agones.dev/agones/pkg/util/runtime" 31 "agones.dev/agones/test/e2e/framework" 32 "github.com/heptiolabs/healthcheck" 33 "github.com/sirupsen/logrus" 34 "github.com/stretchr/testify/assert" 35 "github.com/stretchr/testify/require" 36 corev1 "k8s.io/api/core/v1" 37 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 38 k8sruntime "k8s.io/apimachinery/pkg/runtime" 39 "k8s.io/apimachinery/pkg/util/wait" 40 "k8s.io/apimachinery/pkg/watch" 41 k8stesting "k8s.io/client-go/testing" 42 "k8s.io/client-go/tools/cache" 43 ) 44 45 func TestAllocatorAllocate(t *testing.T) { 46 t.Parallel() 47 48 // TODO: remove when `CountsAndLists` feature flag is moved to stable. 49 runtime.FeatureTestMutex.Lock() 50 defer runtime.FeatureTestMutex.Unlock() 51 52 f, gsList := defaultFixtures(4) 53 a, m := newFakeAllocator() 54 n := metav1.Now() 55 labels := map[string]string{"mode": "deathmatch"} 56 annotations := map[string]string{"map": "searide"} 57 fam := allocationv1.MetaPatch{Labels: labels, Annotations: annotations} 58 59 gsList[3].ObjectMeta.DeletionTimestamp = &n 60 61 m.AgonesClient.AddReactor("list", "gameservers", func(_ k8stesting.Action) (bool, k8sruntime.Object, error) { 62 return true, &agonesv1.GameServerList{Items: gsList}, nil 63 }) 64 65 updated := false 66 gsWatch := watch.NewFake() 67 m.AgonesClient.AddWatchReactor("gameservers", k8stesting.DefaultWatchReactor(gsWatch, nil)) 68 m.AgonesClient.AddReactor("update", "gameservers", func(action k8stesting.Action) (bool, k8sruntime.Object, error) { 69 ua := action.(k8stesting.UpdateAction) 70 gs := ua.GetObject().(*agonesv1.GameServer) 71 72 updated = true 73 assert.Equal(t, agonesv1.GameServerStateAllocated, gs.Status.State) 74 gsWatch.Modify(gs) 75 76 return true, gs, nil 77 }) 78 79 ctx, cancel := agtesting.StartInformers(m, a.allocationCache.gameServerSynced) 80 defer cancel() 81 82 require.NoError(t, a.Run(ctx)) 83 // wait for it to be up and running 84 err := wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (done bool, err error) { 85 return a.allocationCache.workerqueue.RunCount() == 1, nil 86 }) 87 require.NoError(t, err) 88 89 gsa := allocationv1.GameServerAllocation{ObjectMeta: metav1.ObjectMeta{Name: "gsa-1", Namespace: defaultNs}, 90 Spec: allocationv1.GameServerAllocationSpec{ 91 Selectors: []allocationv1.GameServerSelector{{LabelSelector: metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: f.ObjectMeta.Name}}}}, 92 MetaPatch: fam, 93 }} 94 gsa.ApplyDefaults() 95 errs := gsa.Validate() 96 require.Len(t, errs, 0) 97 98 gs, err := a.allocate(ctx, &gsa) 99 require.NoError(t, err) 100 assert.Equal(t, agonesv1.GameServerStateAllocated, gs.Status.State) 101 assert.True(t, updated) 102 for key, value := range fam.Labels { 103 v, ok := gs.ObjectMeta.Labels[key] 104 assert.True(t, ok) 105 assert.Equal(t, v, value) 106 } 107 for key, value := range fam.Annotations { 108 v, ok := gs.ObjectMeta.Annotations[key] 109 assert.True(t, ok) 110 assert.Equal(t, v, value) 111 } 112 113 updated = false 114 gs, err = a.allocate(ctx, &gsa) 115 require.NoError(t, err) 116 assert.Equal(t, agonesv1.GameServerStateAllocated, gs.Status.State) 117 assert.True(t, updated) 118 119 updated = false 120 gs, err = a.allocate(ctx, &gsa) 121 require.NoError(t, err) 122 assert.Equal(t, agonesv1.GameServerStateAllocated, gs.Status.State) 123 assert.True(t, updated) 124 125 updated = false 126 _, err = a.allocate(ctx, &gsa) 127 require.Error(t, err) 128 assert.Equal(t, ErrNoGameServer, err) 129 assert.False(t, updated) 130 } 131 132 func TestAllocatorAllocatePriority(t *testing.T) { 133 t.Parallel() 134 135 // TODO: remove when `CountsAndLists` feature flag is moved to stable. 136 runtime.FeatureTestMutex.Lock() 137 defer runtime.FeatureTestMutex.Unlock() 138 139 run := func(t *testing.T, name string, test func(t *testing.T, a *Allocator, gas *allocationv1.GameServerAllocation)) { 140 f, gsList := defaultFixtures(4) 141 a, m := newFakeAllocator() 142 143 gsList[0].Status.NodeName = n1 144 gsList[1].Status.NodeName = n2 145 gsList[2].Status.NodeName = n1 146 gsList[3].Status.NodeName = n1 147 148 m.AgonesClient.AddReactor("list", "gameservers", func(_ k8stesting.Action) (bool, k8sruntime.Object, error) { 149 return true, &agonesv1.GameServerList{Items: gsList}, nil 150 }) 151 152 gsWatch := watch.NewFake() 153 m.AgonesClient.AddWatchReactor("gameservers", k8stesting.DefaultWatchReactor(gsWatch, nil)) 154 m.AgonesClient.AddReactor("update", "gameservers", func(action k8stesting.Action) (bool, k8sruntime.Object, error) { 155 ua := action.(k8stesting.UpdateAction) 156 gs := ua.GetObject().(*agonesv1.GameServer) 157 gsWatch.Modify(gs) 158 159 return true, gs, nil 160 }) 161 162 ctx, cancel := agtesting.StartInformers(m, a.allocationCache.gameServerSynced) 163 defer cancel() 164 165 require.NoError(t, a.Run(ctx)) 166 // wait for it to be up and running 167 err := wait.PollUntilContextTimeout(context.Background(), time.Second, 10*time.Second, true, func(_ context.Context) (done bool, err error) { 168 return a.allocationCache.workerqueue.RunCount() == 1, nil 169 }) 170 require.NoError(t, err) 171 172 gsa := &allocationv1.GameServerAllocation{ObjectMeta: metav1.ObjectMeta{Name: "fa-1", Namespace: defaultNs}, 173 Spec: allocationv1.GameServerAllocationSpec{ 174 Selectors: []allocationv1.GameServerSelector{{LabelSelector: metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: f.ObjectMeta.Name}}}}, 175 }} 176 gsa.ApplyDefaults() 177 errs := gsa.Validate() 178 require.Len(t, errs, 0) 179 180 t.Run(name, func(t *testing.T) { 181 test(t, a, gsa.DeepCopy()) 182 }) 183 } 184 185 run(t, "packed", func(t *testing.T, a *Allocator, gas *allocationv1.GameServerAllocation) { 186 ctx := context.Background() 187 // priority should be node1, then node2 188 gs1, err := a.allocate(ctx, gas) 189 assert.NoError(t, err) 190 assert.Equal(t, n1, gs1.Status.NodeName) 191 192 gs2, err := a.allocate(ctx, gas) 193 assert.NoError(t, err) 194 assert.Equal(t, n1, gs2.Status.NodeName) 195 assert.NotEqual(t, gs1.ObjectMeta.Name, gs2.ObjectMeta.Name) 196 197 gs3, err := a.allocate(ctx, gas) 198 assert.NoError(t, err) 199 assert.Equal(t, n1, gs3.Status.NodeName) 200 assert.NotContains(t, []string{gs1.ObjectMeta.Name, gs2.ObjectMeta.Name}, gs3.ObjectMeta.Name) 201 202 gs4, err := a.allocate(ctx, gas) 203 assert.NoError(t, err) 204 assert.Equal(t, n2, gs4.Status.NodeName) 205 assert.NotContains(t, []string{gs1.ObjectMeta.Name, gs2.ObjectMeta.Name, gs3.ObjectMeta.Name}, gs4.ObjectMeta.Name) 206 207 // should have none left 208 _, err = a.allocate(ctx, gas) 209 assert.Equal(t, err, ErrNoGameServer) 210 }) 211 212 run(t, "distributed", func(t *testing.T, a *Allocator, gas *allocationv1.GameServerAllocation) { 213 // make a copy, to avoid the race check 214 gas = gas.DeepCopy() 215 gas.Spec.Scheduling = apis.Distributed 216 217 // distributed is randomised, so no set pattern 218 ctx := context.Background() 219 220 gs1, err := a.allocate(ctx, gas) 221 assert.NoError(t, err) 222 223 gs2, err := a.allocate(ctx, gas) 224 assert.NoError(t, err) 225 assert.NotEqual(t, gs1.ObjectMeta.Name, gs2.ObjectMeta.Name) 226 227 gs3, err := a.allocate(ctx, gas) 228 assert.NoError(t, err) 229 assert.NotContains(t, []string{gs1.ObjectMeta.Name, gs2.ObjectMeta.Name}, gs3.ObjectMeta.Name) 230 231 gs4, err := a.allocate(ctx, gas) 232 assert.NoError(t, err) 233 assert.NotContains(t, []string{gs1.ObjectMeta.Name, gs2.ObjectMeta.Name, gs3.ObjectMeta.Name}, gs4.ObjectMeta.Name) 234 235 // should have none left 236 _, err = a.allocate(ctx, gas) 237 assert.Equal(t, err, ErrNoGameServer) 238 }) 239 } 240 241 func TestAllocatorApplyAllocationToGameServer(t *testing.T) { 242 t.Parallel() 243 m := agtesting.NewMocks() 244 ctx := context.Background() 245 246 m.AgonesClient.AddReactor("update", "gameservers", func(action k8stesting.Action) (bool, k8sruntime.Object, error) { 247 ua := action.(k8stesting.UpdateAction) 248 gs := ua.GetObject().(*agonesv1.GameServer) 249 return true, gs, nil 250 }) 251 252 allocator := NewAllocator(m.AgonesInformerFactory.Multicluster().V1().GameServerAllocationPolicies(), 253 m.KubeInformerFactory.Core().V1().Secrets(), 254 m.AgonesClient.AgonesV1(), m.KubeClient, 255 NewAllocationCache(m.AgonesInformerFactory.Agones().V1().GameServers(), gameservers.NewPerNodeCounter(m.KubeInformerFactory, m.AgonesInformerFactory), healthcheck.NewHandler()), 256 time.Second, 5*time.Second, 500*time.Millisecond, 257 ) 258 259 gs, err := allocator.applyAllocationToGameServer(ctx, allocationv1.MetaPatch{}, &agonesv1.GameServer{}, &allocationv1.GameServerAllocation{}) 260 assert.NoError(t, err) 261 assert.Equal(t, agonesv1.GameServerStateAllocated, gs.Status.State) 262 assert.NotNil(t, gs.ObjectMeta.Annotations["agones.dev/last-allocated"]) 263 var ts time.Time 264 assert.NoError(t, ts.UnmarshalText([]byte(gs.ObjectMeta.Annotations[LastAllocatedAnnotationKey]))) 265 266 gs, err = allocator.applyAllocationToGameServer(ctx, allocationv1.MetaPatch{Labels: map[string]string{"foo": "bar"}}, &agonesv1.GameServer{}, &allocationv1.GameServerAllocation{}) 267 assert.NoError(t, err) 268 assert.Equal(t, agonesv1.GameServerStateAllocated, gs.Status.State) 269 assert.Equal(t, "bar", gs.ObjectMeta.Labels["foo"]) 270 assert.NotNil(t, gs.ObjectMeta.Annotations["agones.dev/last-allocated"]) 271 272 gs, err = allocator.applyAllocationToGameServer(ctx, 273 allocationv1.MetaPatch{Labels: map[string]string{"foo": "bar"}, Annotations: map[string]string{"bar": "foo"}}, 274 &agonesv1.GameServer{}, &allocationv1.GameServerAllocation{}) 275 assert.NoError(t, err) 276 assert.Equal(t, agonesv1.GameServerStateAllocated, gs.Status.State) 277 assert.Equal(t, "bar", gs.ObjectMeta.Labels["foo"]) 278 assert.Equal(t, "foo", gs.ObjectMeta.Annotations["bar"]) 279 assert.NotNil(t, gs.ObjectMeta.Annotations[LastAllocatedAnnotationKey]) 280 } 281 282 func TestAllocatorApplyAllocationToGameServerCountsListsActions(t *testing.T) { 283 t.Parallel() 284 285 m := agtesting.NewMocks() 286 ctx := context.Background() 287 mp := allocationv1.MetaPatch{} 288 289 m.AgonesClient.AddReactor("update", "gameservers", func(action k8stesting.Action) (bool, k8sruntime.Object, error) { 290 ua := action.(k8stesting.UpdateAction) 291 gs := ua.GetObject().(*agonesv1.GameServer) 292 return true, gs, nil 293 }) 294 295 allocator := NewAllocator(m.AgonesInformerFactory.Multicluster().V1().GameServerAllocationPolicies(), 296 m.KubeInformerFactory.Core().V1().Secrets(), 297 m.AgonesClient.AgonesV1(), m.KubeClient, 298 NewAllocationCache(m.AgonesInformerFactory.Agones().V1().GameServers(), gameservers.NewPerNodeCounter(m.KubeInformerFactory, m.AgonesInformerFactory), healthcheck.NewHandler()), 299 time.Second, 5*time.Second, 500*time.Millisecond, 300 ) 301 302 ONE := int64(1) 303 FORTY := int64(40) 304 THOUSAND := int64(1000) 305 INCREMENT := "Increment" 306 READY := agonesv1.GameServerStateReady 307 308 gs1 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs1", Namespace: defaultNs, UID: "1"}, 309 Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady, 310 Lists: map[string]agonesv1.ListStatus{ 311 "players": { 312 Values: []string{"alice", "bob", "cat"}, 313 Capacity: 100, 314 }, 315 }, 316 Counters: map[string]agonesv1.CounterStatus{ 317 "rooms": { 318 Count: 101, 319 Capacity: 1000, 320 }}}} 321 gs2 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs2", Namespace: defaultNs, UID: "2"}, 322 Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady, 323 Lists: map[string]agonesv1.ListStatus{ 324 "players": { 325 Values: []string{}, 326 Capacity: 100, 327 }, 328 }, 329 Counters: map[string]agonesv1.CounterStatus{ 330 "rooms": { 331 Count: 101, 332 Capacity: 1000, 333 }}}} 334 335 testScenarios := map[string]struct { 336 features string 337 gs *agonesv1.GameServer 338 gsa *allocationv1.GameServerAllocation 339 wantCounters map[string]agonesv1.CounterStatus 340 wantLists map[string]agonesv1.ListStatus 341 }{ 342 "CounterActions increment and ListActions add, delete, and update capacity": { 343 features: fmt.Sprintf("%s=true", runtime.FeatureCountsAndLists), 344 gs: &gs1, 345 gsa: &allocationv1.GameServerAllocation{ 346 ObjectMeta: metav1.ObjectMeta{Namespace: defaultNs}, 347 Spec: allocationv1.GameServerAllocationSpec{ 348 Selectors: []allocationv1.GameServerSelector{{ 349 GameServerState: &READY, 350 }}, 351 Scheduling: apis.Packed, 352 Counters: map[string]allocationv1.CounterAction{ 353 "rooms": { 354 Action: &INCREMENT, 355 Amount: &ONE, 356 }}, 357 Lists: map[string]allocationv1.ListAction{ 358 "players": { 359 AddValues: []string{"x7un", "8inz"}, 360 Capacity: &FORTY, 361 DeleteValues: []string{"bob"}, 362 }}}}, 363 wantCounters: map[string]agonesv1.CounterStatus{ 364 "rooms": { 365 Count: 102, 366 Capacity: 1000, 367 }}, 368 wantLists: map[string]agonesv1.ListStatus{ 369 "players": { 370 Values: []string{"alice", "cat", "x7un", "8inz"}, 371 Capacity: 40, 372 }}, 373 }, 374 "CounterActions and ListActions truncate counter Count and update list capacity": { 375 features: fmt.Sprintf("%s=true", runtime.FeatureCountsAndLists), 376 gs: &gs2, 377 gsa: &allocationv1.GameServerAllocation{ 378 ObjectMeta: metav1.ObjectMeta{Namespace: defaultNs}, 379 Spec: allocationv1.GameServerAllocationSpec{ 380 Selectors: []allocationv1.GameServerSelector{{ 381 GameServerState: &READY, 382 }}, 383 Scheduling: apis.Packed, 384 Counters: map[string]allocationv1.CounterAction{ 385 "rooms": { 386 Action: &INCREMENT, 387 Amount: &THOUSAND, 388 }}, 389 Lists: map[string]allocationv1.ListAction{ 390 "players": { 391 AddValues: []string{"x7un", "8inz"}, 392 Capacity: &ONE, 393 }}}}, 394 wantCounters: map[string]agonesv1.CounterStatus{ 395 "rooms": { 396 Count: 1000, 397 Capacity: 1000, 398 }}, 399 wantLists: map[string]agonesv1.ListStatus{ 400 "players": { 401 Values: []string{"x7un"}, 402 Capacity: 1, 403 }}, 404 }, 405 } 406 407 for test, testScenario := range testScenarios { 408 t.Run(test, func(t *testing.T) { 409 runtime.FeatureTestMutex.Lock() 410 defer runtime.FeatureTestMutex.Unlock() 411 // we always set the feature flag in all these tests, so always process it. 412 require.NoError(t, runtime.ParseFeatures(testScenario.features)) 413 414 foundGs, err := allocator.applyAllocationToGameServer(ctx, mp, testScenario.gs, testScenario.gsa) 415 assert.NoError(t, err) 416 for counter, counterStatus := range testScenario.wantCounters { 417 if gsCounter, ok := foundGs.Status.Counters[counter]; ok { 418 assert.Equal(t, counterStatus, gsCounter) 419 } 420 } 421 for list, listStatus := range testScenario.wantLists { 422 if gsList, ok := foundGs.Status.Lists[list]; ok { 423 assert.Equal(t, listStatus, gsList) 424 } 425 } 426 }) 427 } 428 } 429 430 func TestAllocationApplyAllocationError(t *testing.T) { 431 t.Parallel() 432 m := agtesting.NewMocks() 433 ctx := context.Background() 434 435 m.AgonesClient.AddReactor("update", "gameservers", func(_ k8stesting.Action) (bool, k8sruntime.Object, error) { 436 return true, nil, errors.New("failed to update") 437 }) 438 439 allocator := NewAllocator(m.AgonesInformerFactory.Multicluster().V1().GameServerAllocationPolicies(), 440 m.KubeInformerFactory.Core().V1().Secrets(), 441 m.AgonesClient.AgonesV1(), m.KubeClient, 442 NewAllocationCache(m.AgonesInformerFactory.Agones().V1().GameServers(), gameservers.NewPerNodeCounter(m.KubeInformerFactory, m.AgonesInformerFactory), healthcheck.NewHandler()), 443 time.Second, 5*time.Second, 500*time.Millisecond, 444 ) 445 446 gsa, err := allocator.applyAllocationToGameServer(ctx, allocationv1.MetaPatch{}, &agonesv1.GameServer{}, &allocationv1.GameServerAllocation{}) 447 logrus.WithError(err).WithField("gsa", gsa).WithField("test", t.Name()).Info("Allocation should fail") 448 assert.Error(t, err) 449 } 450 451 func TestAllocatorAllocateOnGameServerUpdateError(t *testing.T) { 452 t.Parallel() 453 454 // TODO: remove when `CountsAndLists` feature flag is moved to stable. 455 runtime.FeatureTestMutex.Lock() 456 defer runtime.FeatureTestMutex.Unlock() 457 require.NoError(t, runtime.ParseFeatures(fmt.Sprintf("%s=false&%s=false", runtime.FeaturePlayerAllocationFilter, runtime.FeatureCountsAndLists))) 458 459 a, m := newFakeAllocator() 460 log := framework.TestLogger(t) 461 462 // make sure there is more than can be retried, so there is always at least some. 463 gsLen := allocationRetry.Steps * 2 464 _, gsList := defaultFixtures(gsLen) 465 m.AgonesClient.AddReactor("list", "gameservers", func(_ k8stesting.Action) (bool, k8sruntime.Object, error) { 466 return true, &agonesv1.GameServerList{Items: gsList}, nil 467 }) 468 m.AgonesClient.AddReactor("update", "gameservers", func(action k8stesting.Action) (bool, k8sruntime.Object, error) { 469 ua := action.(k8stesting.UpdateAction) 470 gs := ua.GetObject().(*agonesv1.GameServer) 471 472 return true, gs, errors.New("failed to update") 473 }) 474 475 ctx, cancel := agtesting.StartInformers(m, a.allocationCache.gameServerSynced) 476 defer cancel() 477 478 require.NoError(t, a.Run(ctx)) 479 // wait for all the gameservers to be in the cache 480 require.Eventuallyf(t, func() bool { 481 return a.allocationCache.cache.Len() == gsLen 482 }, 10*time.Second, time.Second, fmt.Sprintf("should be %d items in the cache", gsLen)) 483 484 gsa := allocationv1.GameServerAllocation{ObjectMeta: metav1.ObjectMeta{Name: "gsa-1", Namespace: defaultNs}, 485 Spec: allocationv1.GameServerAllocationSpec{}, 486 } 487 488 gsa.ApplyDefaults() 489 // without converter, we don't end up with at least one selector 490 gsa.Converter() 491 errs := gsa.Validate() 492 require.Len(t, errs, 0) 493 require.Len(t, gsa.Spec.Selectors, 1) 494 495 // try the private method 496 _, err := a.allocate(ctx, gsa.DeepCopy()) 497 log.WithError(err).Info("allocate (private): failed allocation") 498 require.NotEqual(t, ErrNoGameServer, err) 499 require.EqualError(t, err, "error updating allocated gameserver: failed to update") 500 501 // make sure we aren't in the same batch! 502 time.Sleep(2 * a.batchWaitTime) 503 504 // wait for all the gameservers to be in the cache 505 require.Eventuallyf(t, func() bool { 506 return a.allocationCache.cache.Len() == gsLen 507 }, 10*time.Second, time.Second, fmt.Sprintf("should be %d items in the cache", gsLen)) 508 509 // try the public method 510 result, err := a.Allocate(ctx, gsa.DeepCopy()) 511 log.WithField("result", result).WithError(err).Info("Allocate (public): failed allocation") 512 require.Nil(t, result) 513 require.NotEqual(t, ErrNoGameServer, err) 514 require.EqualError(t, err, "error updating allocated gameserver: failed to update") 515 } 516 517 func TestAllocatorRunLocalAllocations(t *testing.T) { 518 t.Parallel() 519 520 // TODO: remove when `CountsAndLists` feature flag is moved to stable. 521 runtime.FeatureTestMutex.Lock() 522 defer runtime.FeatureTestMutex.Unlock() 523 524 t.Run("no problems", func(t *testing.T) { 525 f, gsList := defaultFixtures(5) 526 gsList[0].Status.NodeName = "special" 527 528 a, m := newFakeAllocator() 529 m.AgonesClient.AddReactor("list", "gameservers", func(_ k8stesting.Action) (bool, k8sruntime.Object, error) { 530 return true, &agonesv1.GameServerList{Items: gsList}, nil 531 }) 532 updateCount := 0 533 m.AgonesClient.AddReactor("update", "gameservers", func(action k8stesting.Action) (bool, k8sruntime.Object, error) { 534 updateCount++ 535 536 uo := action.(k8stesting.UpdateAction) 537 gs := uo.GetObject().(*agonesv1.GameServer) 538 539 return true, gs, nil 540 }) 541 542 ctx, cancel := agtesting.StartInformers(m, a.allocationCache.gameServerSynced) 543 defer cancel() 544 545 // This call initializes the cache 546 err := a.allocationCache.syncCache() 547 assert.Nil(t, err) 548 549 err = a.allocationCache.counter.Run(ctx, 0) 550 assert.Nil(t, err) 551 552 gsa := &allocationv1.GameServerAllocation{ 553 ObjectMeta: metav1.ObjectMeta{ 554 Namespace: defaultNs, 555 }, 556 Spec: allocationv1.GameServerAllocationSpec{ 557 Selectors: []allocationv1.GameServerSelector{{LabelSelector: metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: f.ObjectMeta.Name}}}}, 558 }} 559 gsa.ApplyDefaults() 560 errs := gsa.Validate() 561 require.Len(t, errs, 0) 562 563 // line up 3 in a batch 564 j1 := request{gsa: gsa.DeepCopy(), response: make(chan response)} 565 a.pendingRequests <- j1 566 j2 := request{gsa: gsa.DeepCopy(), response: make(chan response)} 567 a.pendingRequests <- j2 568 j3 := request{gsa: gsa.DeepCopy(), response: make(chan response)} 569 a.pendingRequests <- j3 570 571 go a.ListenAndAllocate(ctx, 3) 572 573 res1 := <-j1.response 574 assert.NoError(t, res1.err) 575 assert.NotNil(t, res1.gs) 576 577 // since we gave gsList[0] a different nodename, it should always come first 578 assert.Contains(t, []string{"gs2", "gs3", "gs4", "gs5"}, res1.gs.ObjectMeta.Name) 579 assert.Equal(t, agonesv1.GameServerStateAllocated, res1.gs.Status.State) 580 581 res2 := <-j2.response 582 assert.NoError(t, res2.err) 583 assert.NotNil(t, res2.gs) 584 assert.NotEqual(t, res1.gs.ObjectMeta.Name, res2.gs.ObjectMeta.Name) 585 assert.Equal(t, agonesv1.GameServerStateAllocated, res2.gs.Status.State) 586 587 res3 := <-j3.response 588 assert.NoError(t, res3.err) 589 assert.NotNil(t, res3.gs) 590 assert.Equal(t, agonesv1.GameServerStateAllocated, res3.gs.Status.State) 591 assert.NotEqual(t, res1.gs.ObjectMeta.Name, res3.gs.ObjectMeta.Name) 592 assert.NotEqual(t, res2.gs.ObjectMeta.Name, res3.gs.ObjectMeta.Name) 593 594 assert.Equal(t, 3, updateCount) 595 }) 596 597 t.Run("no gameservers", func(t *testing.T) { 598 a, m := newFakeAllocator() 599 ctx, cancel := agtesting.StartInformers(m, a.allocationCache.gameServerSynced) 600 defer cancel() 601 602 // This call initializes the cache 603 err := a.allocationCache.syncCache() 604 assert.Nil(t, err) 605 606 err = a.allocationCache.counter.Run(ctx, 0) 607 assert.Nil(t, err) 608 609 gsa := &allocationv1.GameServerAllocation{ 610 ObjectMeta: metav1.ObjectMeta{ 611 Namespace: defaultNs, 612 }, 613 Spec: allocationv1.GameServerAllocationSpec{ 614 Selectors: []allocationv1.GameServerSelector{{LabelSelector: metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: "thereisnofleet"}}}}, 615 }} 616 gsa.ApplyDefaults() 617 errs := gsa.Validate() 618 require.Len(t, errs, 0) 619 620 j1 := request{gsa: gsa.DeepCopy(), response: make(chan response)} 621 a.pendingRequests <- j1 622 623 go a.ListenAndAllocate(ctx, 3) 624 625 res1 := <-j1.response 626 assert.Nil(t, res1.gs) 627 assert.Error(t, res1.err) 628 assert.Equal(t, ErrNoGameServer, res1.err) 629 }) 630 } 631 632 func TestAllocatorRunLocalAllocationsCountsAndLists(t *testing.T) { 633 t.Parallel() 634 635 runtime.FeatureTestMutex.Lock() 636 defer runtime.FeatureTestMutex.Unlock() 637 assert.NoError(t, runtime.ParseFeatures(string(runtime.FeatureCountsAndLists)+"=true")) 638 639 a, m := newFakeAllocator() 640 641 gs1 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs1", Namespace: defaultNs, UID: "1"}, 642 Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady, 643 Counters: map[string]agonesv1.CounterStatus{ 644 "foo": { // Available Capacity == 1000 645 Count: 0, 646 Capacity: 1000, 647 }}}} 648 gs2 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs2", Namespace: defaultNs, UID: "2"}, 649 Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady, 650 Counters: map[string]agonesv1.CounterStatus{ 651 "foo": { // Available Capacity == 900 652 Count: 100, 653 Capacity: 1000, 654 }}}} 655 gs3 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs3", Namespace: defaultNs, UID: "3"}, 656 Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady, 657 Counters: map[string]agonesv1.CounterStatus{ 658 "foo": { // Available Capacity == 1 659 Count: 999, 660 Capacity: 1000, 661 }}}} 662 gs4 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs4", Namespace: defaultNs, UID: "4"}, 663 Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady, 664 Lists: map[string]agonesv1.ListStatus{ 665 "foo": { // Available Capacity == 10 666 Values: []string{}, 667 Capacity: 10, 668 }}}} 669 gs5 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs5", Namespace: defaultNs, UID: "5"}, 670 Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady, 671 Lists: map[string]agonesv1.ListStatus{ 672 "foo": { // Available Capacity == 9 673 Values: []string{"1"}, 674 Capacity: 10, 675 }}}} 676 gs6 := agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gs6", Namespace: defaultNs, UID: "6"}, 677 Status: agonesv1.GameServerStatus{NodeName: "node1", State: agonesv1.GameServerStateReady, 678 Lists: map[string]agonesv1.ListStatus{ 679 "foo": { // Available Capacity == 1 680 Values: []string{"1", "2", "3", "4", "5", "6", "7", "8", "9"}, 681 Capacity: 10, 682 }}}} 683 684 gsList := []agonesv1.GameServer{gs1, gs2, gs3, gs4, gs5, gs6} 685 686 m.AgonesClient.AddReactor("list", "gameservers", func(_ k8stesting.Action) (bool, k8sruntime.Object, error) { 687 return true, &agonesv1.GameServerList{Items: gsList}, nil 688 }) 689 690 m.AgonesClient.AddReactor("update", "gameservers", func(action k8stesting.Action) (bool, k8sruntime.Object, error) { 691 uo := action.(k8stesting.UpdateAction) 692 gs := uo.GetObject().(*agonesv1.GameServer) 693 return true, gs, nil 694 }) 695 696 ctx, cancel := agtesting.StartInformers(m, a.allocationCache.gameServerSynced) 697 defer cancel() 698 699 // This call initializes the cache 700 err := a.allocationCache.syncCache() 701 assert.Nil(t, err) 702 703 err = a.allocationCache.counter.Run(ctx, 0) 704 assert.Nil(t, err) 705 706 READY := agonesv1.GameServerStateReady 707 708 gsaAscending := &allocationv1.GameServerAllocation{ 709 ObjectMeta: metav1.ObjectMeta{Namespace: defaultNs}, 710 Spec: allocationv1.GameServerAllocationSpec{ 711 Scheduling: apis.Packed, 712 Selectors: []allocationv1.GameServerSelector{{ 713 GameServerState: &READY, 714 }}, 715 Priorities: []agonesv1.Priority{ 716 {Type: agonesv1.GameServerPriorityCounter, 717 Key: "foo", 718 Order: agonesv1.GameServerPriorityAscending}, 719 }, 720 }} 721 gsaDescending := &allocationv1.GameServerAllocation{ 722 ObjectMeta: metav1.ObjectMeta{Namespace: defaultNs}, 723 Spec: allocationv1.GameServerAllocationSpec{ 724 Scheduling: apis.Packed, 725 Selectors: []allocationv1.GameServerSelector{{ 726 GameServerState: &READY, 727 }}, 728 Priorities: []agonesv1.Priority{ 729 {Type: agonesv1.GameServerPriorityCounter, 730 Key: "foo", 731 Order: agonesv1.GameServerPriorityDescending}, 732 }, 733 }} 734 gsaDistributed := &allocationv1.GameServerAllocation{ 735 ObjectMeta: metav1.ObjectMeta{Namespace: defaultNs}, 736 Spec: allocationv1.GameServerAllocationSpec{ 737 Scheduling: apis.Distributed, 738 Selectors: []allocationv1.GameServerSelector{{ 739 GameServerState: &READY, 740 }}, 741 Priorities: []agonesv1.Priority{ 742 {Type: agonesv1.GameServerPriorityCounter, 743 Key: "foo", 744 Order: agonesv1.GameServerPriorityDescending}, 745 }, 746 }} 747 gsaListAscending := &allocationv1.GameServerAllocation{ 748 ObjectMeta: metav1.ObjectMeta{Namespace: defaultNs}, 749 Spec: allocationv1.GameServerAllocationSpec{ 750 Scheduling: apis.Packed, 751 Selectors: []allocationv1.GameServerSelector{{ 752 GameServerState: &READY, 753 }}, 754 Priorities: []agonesv1.Priority{ 755 {Type: agonesv1.GameServerPriorityList, 756 Key: "foo", 757 Order: agonesv1.GameServerPriorityAscending}, 758 }, 759 }} 760 gsaListDescending := &allocationv1.GameServerAllocation{ 761 ObjectMeta: metav1.ObjectMeta{Namespace: defaultNs}, 762 Spec: allocationv1.GameServerAllocationSpec{ 763 Scheduling: apis.Packed, 764 Selectors: []allocationv1.GameServerSelector{{ 765 GameServerState: &READY, 766 }}, 767 Priorities: []agonesv1.Priority{ 768 {Type: agonesv1.GameServerPriorityList, 769 Key: "foo", 770 Order: agonesv1.GameServerPriorityDescending}, 771 }, 772 }} 773 gsaListDistributed := &allocationv1.GameServerAllocation{ 774 ObjectMeta: metav1.ObjectMeta{Namespace: defaultNs}, 775 Spec: allocationv1.GameServerAllocationSpec{ 776 Scheduling: apis.Distributed, 777 Selectors: []allocationv1.GameServerSelector{{ 778 GameServerState: &READY, 779 }}, 780 Priorities: []agonesv1.Priority{ 781 {Type: agonesv1.GameServerPriorityList, 782 Key: "foo", 783 Order: agonesv1.GameServerPriorityDescending}, 784 }, 785 }} 786 787 j1 := request{gsa: gsaDescending.DeepCopy(), response: make(chan response)} 788 a.pendingRequests <- j1 789 j2 := request{gsa: gsaAscending.DeepCopy(), response: make(chan response)} 790 a.pendingRequests <- j2 791 j3 := request{gsa: gsaDistributed.DeepCopy(), response: make(chan response)} 792 a.pendingRequests <- j3 793 j4 := request{gsa: gsaListDescending.DeepCopy(), response: make(chan response)} 794 a.pendingRequests <- j4 795 j5 := request{gsa: gsaListAscending.DeepCopy(), response: make(chan response)} 796 a.pendingRequests <- j5 797 j6 := request{gsa: gsaListDistributed.DeepCopy(), response: make(chan response)} 798 a.pendingRequests <- j6 799 800 go a.ListenAndAllocate(ctx, 5) 801 802 res1 := <-j1.response 803 assert.NoError(t, res1.err) 804 assert.NotNil(t, res1.gs) 805 assert.Equal(t, agonesv1.GameServerStateAllocated, res1.gs.Status.State) 806 assert.Equal(t, gs1.ObjectMeta.Name, res1.gs.ObjectMeta.Name) 807 assert.Equal(t, gs1.Status.Counters["foo"].Count, res1.gs.Status.Counters["foo"].Count) 808 assert.Equal(t, gs1.Status.Counters["foo"].Capacity, res1.gs.Status.Counters["foo"].Capacity) 809 810 res2 := <-j2.response 811 assert.NoError(t, res2.err) 812 assert.NotNil(t, res2.gs) 813 assert.Equal(t, agonesv1.GameServerStateAllocated, res2.gs.Status.State) 814 assert.Equal(t, gs3.ObjectMeta.Name, res2.gs.ObjectMeta.Name) 815 assert.Equal(t, gs3.Status.Counters["foo"].Count, res2.gs.Status.Counters["foo"].Count) 816 assert.Equal(t, gs3.Status.Counters["foo"].Capacity, res2.gs.Status.Counters["foo"].Capacity) 817 818 res3 := <-j3.response 819 assert.NoError(t, res3.err) 820 assert.NotNil(t, res3.gs) 821 assert.Equal(t, agonesv1.GameServerStateAllocated, res3.gs.Status.State) 822 assert.Equal(t, gs2.ObjectMeta.Name, res3.gs.ObjectMeta.Name) 823 assert.Equal(t, gs2.Status.Counters["foo"].Count, res3.gs.Status.Counters["foo"].Count) 824 assert.Equal(t, gs2.Status.Counters["foo"].Capacity, res3.gs.Status.Counters["foo"].Capacity) 825 826 res4 := <-j4.response 827 assert.NoError(t, res4.err) 828 assert.NotNil(t, res4.gs) 829 assert.Equal(t, agonesv1.GameServerStateAllocated, res4.gs.Status.State) 830 assert.Equal(t, gs4.ObjectMeta.Name, res4.gs.ObjectMeta.Name) 831 assert.Equal(t, gs4.Status.Lists["foo"].Values, res4.gs.Status.Lists["foo"].Values) 832 assert.Equal(t, gs4.Status.Lists["foo"].Capacity, res4.gs.Status.Lists["foo"].Capacity) 833 834 res5 := <-j5.response 835 assert.NoError(t, res5.err) 836 assert.NotNil(t, res5.gs) 837 assert.Equal(t, agonesv1.GameServerStateAllocated, res5.gs.Status.State) 838 assert.Equal(t, gs6.ObjectMeta.Name, res5.gs.ObjectMeta.Name) 839 assert.Equal(t, gs6.Status.Lists["foo"].Values, res5.gs.Status.Lists["foo"].Values) 840 assert.Equal(t, gs6.Status.Lists["foo"].Capacity, res5.gs.Status.Lists["foo"].Capacity) 841 842 res6 := <-j6.response 843 assert.NoError(t, res6.err) 844 assert.NotNil(t, res6.gs) 845 assert.Equal(t, agonesv1.GameServerStateAllocated, res6.gs.Status.State) 846 assert.Equal(t, gs5.ObjectMeta.Name, res6.gs.ObjectMeta.Name) 847 assert.Equal(t, gs5.Status.Lists["foo"].Values, res6.gs.Status.Lists["foo"].Values) 848 assert.Equal(t, gs5.Status.Lists["foo"].Capacity, res6.gs.Status.Lists["foo"].Capacity) 849 850 } 851 852 func TestControllerAllocationUpdateWorkers(t *testing.T) { 853 t.Run("no error", func(t *testing.T) { 854 a, m := newFakeAllocator() 855 856 updated := false 857 gs1 := &agonesv1.GameServer{ 858 ObjectMeta: metav1.ObjectMeta{Name: "gs1"}, 859 } 860 r := response{ 861 request: request{ 862 gsa: &allocationv1.GameServerAllocation{}, 863 response: make(chan response), 864 }, 865 gs: gs1, 866 } 867 868 m.AgonesClient.AddReactor("update", "gameservers", func(action k8stesting.Action) (bool, k8sruntime.Object, error) { 869 updated = true 870 871 uo := action.(k8stesting.UpdateAction) 872 gs := uo.GetObject().(*agonesv1.GameServer) 873 874 assert.Equal(t, gs1.ObjectMeta.Name, gs.ObjectMeta.Name) 875 assert.Equal(t, agonesv1.GameServerStateAllocated, gs.Status.State) 876 877 return true, gs, nil 878 }) 879 880 updateQueue := a.allocationUpdateWorkers(context.Background(), 1) 881 882 go func() { 883 updateQueue <- r 884 }() 885 886 r = <-r.request.response 887 888 assert.True(t, updated) 889 assert.NoError(t, r.err) 890 assert.Equal(t, gs1.ObjectMeta.Name, r.gs.ObjectMeta.Name) 891 assert.Equal(t, agonesv1.GameServerStateAllocated, r.gs.Status.State) 892 893 // Verify that the GameServer was added back to the cache after successful allocation 894 key, err := cache.MetaNamespaceKeyFunc(gs1) 895 require.NoError(t, err) 896 cached, ok := a.allocationCache.cache.Load(key) 897 require.True(t, ok, "GameServer should be in the cache after successful allocation") 898 require.Equal(t, gs1.ObjectMeta.Name, cached.ObjectMeta.Name) 899 900 agtesting.AssertEventContains(t, m.FakeRecorder.Events, "Allocated") 901 902 // make sure we can do more allocations than number of workers 903 gs2 := &agonesv1.GameServer{ 904 ObjectMeta: metav1.ObjectMeta{Name: "gs1"}, 905 } 906 r = response{ 907 request: request{ 908 gsa: &allocationv1.GameServerAllocation{}, 909 response: make(chan response), 910 }, 911 gs: gs2, 912 } 913 914 go func() { 915 updateQueue <- r 916 }() 917 918 r = <-r.request.response 919 920 assert.True(t, updated) 921 assert.NoError(t, r.err) 922 assert.Equal(t, gs2.ObjectMeta.Name, r.gs.ObjectMeta.Name) 923 assert.Equal(t, agonesv1.GameServerStateAllocated, r.gs.Status.State) 924 925 agtesting.AssertEventContains(t, m.FakeRecorder.Events, "Allocated") 926 }) 927 928 t.Run("error on update", func(t *testing.T) { 929 a, m := newFakeAllocator() 930 931 updated := false 932 gs1 := &agonesv1.GameServer{ 933 ObjectMeta: metav1.ObjectMeta{Name: "gs1"}, 934 } 935 key, err := cache.MetaNamespaceKeyFunc(gs1) 936 assert.NoError(t, err) 937 938 _, ok := a.allocationCache.cache.Load(key) 939 assert.False(t, ok) 940 941 r := response{ 942 request: request{ 943 gsa: &allocationv1.GameServerAllocation{}, 944 response: make(chan response), 945 }, 946 gs: gs1, 947 } 948 949 m.AgonesClient.AddReactor("update", "gameservers", func(action k8stesting.Action) (bool, k8sruntime.Object, error) { 950 updated = true 951 952 uo := action.(k8stesting.UpdateAction) 953 gs := uo.GetObject().(*agonesv1.GameServer) 954 assert.Equal(t, gs1.ObjectMeta.Name, gs.ObjectMeta.Name) 955 assert.Equal(t, agonesv1.GameServerStateAllocated, gs.Status.State) 956 957 return true, gs, errors.New("something went wrong") 958 }) 959 960 updateQueue := a.allocationUpdateWorkers(context.Background(), 1) 961 962 go func() { 963 updateQueue <- r 964 }() 965 966 r = <-r.request.response 967 968 assert.True(t, updated) 969 assert.EqualError(t, r.err, "error updating allocated gameserver: something went wrong") 970 assert.Equal(t, gs1, r.gs) 971 agtesting.AssertNoEvent(t, m.FakeRecorder.Events) 972 973 var cached *agonesv1.GameServer 974 cached, ok = a.allocationCache.cache.Load(key) 975 assert.True(t, ok) 976 assert.Equal(t, gs1.ObjectMeta.Name, cached.ObjectMeta.Name) 977 }) 978 } 979 980 func TestAllocatorCreateRestClientError(t *testing.T) { 981 t.Parallel() 982 t.Run("Missing secret", func(t *testing.T) { 983 a, _ := newFakeAllocator() 984 985 connectionInfo := &multiclusterv1.ClusterConnectionInfo{ 986 SecretName: "secret-name", 987 } 988 _, err := a.createRemoteClusterDialOption(defaultNs, connectionInfo) 989 assert.Error(t, err) 990 assert.Contains(t, err.Error(), "secret-name") 991 }) 992 t.Run("Missing cert", func(t *testing.T) { 993 a, m := newFakeAllocator() 994 995 m.KubeClient.AddReactor("list", "secrets", 996 func(_ k8stesting.Action) (bool, k8sruntime.Object, error) { 997 return true, &corev1.SecretList{ 998 Items: []corev1.Secret{{ 999 Data: map[string][]byte{ 1000 "tls.crt": clientCert, 1001 }, 1002 ObjectMeta: metav1.ObjectMeta{ 1003 Name: "secret-name", 1004 Namespace: defaultNs, 1005 }, 1006 }}}, nil 1007 }) 1008 1009 _, cancel := agtesting.StartInformers(m, a.secretSynced) 1010 defer cancel() 1011 1012 connectionInfo := &multiclusterv1.ClusterConnectionInfo{ 1013 SecretName: "secret-name", 1014 } 1015 _, err := a.createRemoteClusterDialOption(defaultNs, connectionInfo) 1016 assert.Error(t, err) 1017 assert.Contains(t, err.Error(), "missing client certificate key pair in secret secret-name") 1018 }) 1019 t.Run("Bad client cert", func(t *testing.T) { 1020 a, m := newFakeAllocator() 1021 1022 m.KubeClient.AddReactor("list", "secrets", 1023 func(_ k8stesting.Action) (bool, k8sruntime.Object, error) { 1024 return true, &corev1.SecretList{ 1025 Items: []corev1.Secret{{ 1026 Data: map[string][]byte{ 1027 "tls.crt": []byte("XXX"), 1028 "tls.key": []byte("XXX"), 1029 }, 1030 ObjectMeta: metav1.ObjectMeta{ 1031 Name: "secret-name", 1032 Namespace: defaultNs, 1033 }, 1034 }}}, nil 1035 }) 1036 1037 _, cancel := agtesting.StartInformers(m, a.secretSynced) 1038 defer cancel() 1039 1040 connectionInfo := &multiclusterv1.ClusterConnectionInfo{ 1041 SecretName: "secret-name", 1042 } 1043 _, err := a.createRemoteClusterDialOption(defaultNs, connectionInfo) 1044 assert.Error(t, err) 1045 assert.Contains(t, err.Error(), "failed to find any PEM data in certificate input") 1046 }) 1047 t.Run("Bad CA cert", func(t *testing.T) { 1048 a, m := newFakeAllocator() 1049 1050 m.KubeClient.AddReactor("list", "secrets", 1051 func(_ k8stesting.Action) (bool, k8sruntime.Object, error) { 1052 return true, getTestSecret("secret-name", clientCert), nil 1053 }) 1054 1055 _, cancel := agtesting.StartInformers(m, a.secretSynced) 1056 defer cancel() 1057 1058 connectionInfo := &multiclusterv1.ClusterConnectionInfo{ 1059 SecretName: "secret-name", 1060 ServerCA: []byte("XXX"), 1061 } 1062 _, err := a.createRemoteClusterDialOption(defaultNs, connectionInfo) 1063 assert.Error(t, err) 1064 assert.Contains(t, err.Error(), "PEM format") 1065 }) 1066 t.Run("Bad client CA cert", func(t *testing.T) { 1067 a, m := newFakeAllocator() 1068 1069 m.KubeClient.AddReactor("list", "secrets", 1070 func(_ k8stesting.Action) (bool, k8sruntime.Object, error) { 1071 return true, getTestSecret("secret-name", []byte("XXX")), nil 1072 }) 1073 1074 _, cancel := agtesting.StartInformers(m, a.secretSynced) 1075 defer cancel() 1076 1077 connectionInfo := &multiclusterv1.ClusterConnectionInfo{ 1078 SecretName: "secret-name", 1079 } 1080 _, err := a.createRemoteClusterDialOption(defaultNs, connectionInfo) 1081 assert.Nil(t, err) 1082 }) 1083 } 1084 1085 // newFakeAllocator returns a fake allocator. 1086 func newFakeAllocator() (*Allocator, agtesting.Mocks) { 1087 m := agtesting.NewMocks() 1088 1089 counter := gameservers.NewPerNodeCounter(m.KubeInformerFactory, m.AgonesInformerFactory) 1090 a := NewAllocator( 1091 m.AgonesInformerFactory.Multicluster().V1().GameServerAllocationPolicies(), 1092 m.KubeInformerFactory.Core().V1().Secrets(), 1093 m.AgonesClient.AgonesV1(), 1094 m.KubeClient, 1095 NewAllocationCache(m.AgonesInformerFactory.Agones().V1().GameServers(), counter, healthcheck.NewHandler()), 1096 time.Second, 1097 5*time.Second, 1098 500*time.Millisecond) 1099 a.recorder = m.FakeRecorder 1100 1101 return a, m 1102 }