agones.dev/agones@v1.53.0/test/e2e/allocator_test.go (about) 1 // Copyright 2019 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 "bytes" 19 "context" 20 "encoding/json" 21 "fmt" 22 "io" 23 "net/http" 24 "testing" 25 "time" 26 27 pb "agones.dev/agones/pkg/allocation/go" 28 agonesv1 "agones.dev/agones/pkg/apis/agones/v1" 29 multiclusterv1 "agones.dev/agones/pkg/apis/multicluster/v1" 30 "agones.dev/agones/pkg/util/runtime" 31 helper "agones.dev/agones/test/e2e/allochelper" 32 e2e "agones.dev/agones/test/e2e/framework" 33 "github.com/sirupsen/logrus" 34 "github.com/stretchr/testify/assert" 35 "github.com/stretchr/testify/require" 36 "google.golang.org/grpc" 37 "google.golang.org/grpc/codes" 38 "google.golang.org/grpc/status" 39 "google.golang.org/protobuf/encoding/protojson" 40 "google.golang.org/protobuf/types/known/wrapperspb" 41 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 42 "k8s.io/apimachinery/pkg/util/uuid" 43 "k8s.io/apimachinery/pkg/util/wait" 44 ) 45 46 const ( 47 agonesSystemNamespace = "agones-system" 48 allocatorServiceName = "agones-allocator" 49 allocatorTLSName = "allocator-tls" 50 tlsCrtTag = "tls.crt" 51 tlsKeyTag = "tls.key" 52 allocatorReqURLFmt = "%s:%d" 53 allocatorClientSecretName = "allocator-client.default" 54 allocatorClientSecretNamespace = "default" 55 ) 56 57 func TestAllocatorWithDeprecatedRequired(t *testing.T) { 58 ctx := context.Background() 59 60 ip, port := helper.GetAllocatorEndpoint(ctx, t, framework) 61 requestURL := fmt.Sprintf(allocatorReqURLFmt, ip, port) 62 tlsCA := helper.RefreshAllocatorTLSCerts(ctx, t, ip, framework) 63 64 var flt *agonesv1.Fleet 65 var err error 66 if runtime.FeatureEnabled(runtime.FeaturePlayerAllocationFilter) { 67 flt, err = helper.CreateFleetWithOpts(ctx, framework.Namespace, framework, func(f *agonesv1.Fleet) { 68 f.Spec.Template.Spec.Players = &agonesv1.PlayersSpec{ 69 InitialCapacity: 10, 70 } 71 }) 72 } else { 73 flt, err = helper.CreateFleet(ctx, framework.Namespace, framework) 74 } 75 require.NoError(t, err) 76 defer framework.AgonesClient.AgonesV1().Fleets(framework.Namespace).Delete(ctx, flt.Name, metav1.DeleteOptions{}) // nolint: errcheck 77 78 framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas)) 79 request := &pb.AllocationRequest{ 80 Namespace: framework.Namespace, 81 RequiredGameServerSelector: &pb.GameServerSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}, 82 PreferredGameServerSelectors: []*pb.GameServerSelector{{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}}, 83 Scheduling: pb.AllocationRequest_Packed, 84 Metadata: &pb.MetaPatch{Labels: map[string]string{"gslabel": "allocatedbytest"}}, 85 } 86 87 var response *pb.AllocationResponse 88 // wait for the allocation system to come online 89 err = wait.PollUntilContextTimeout(context.Background(), 2*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) { 90 // create the grpc client each time, as we may end up looking at an old cert 91 dialOpts, err := helper.CreateRemoteClusterDialOptions(ctx, allocatorClientSecretNamespace, allocatorClientSecretName, tlsCA, framework) 92 if err != nil { 93 return false, err 94 } 95 96 conn, err := grpc.NewClient(requestURL, dialOpts...) 97 if err != nil { 98 logrus.WithError(err).Info("failing grpc.NewClient") 99 return false, nil 100 } 101 defer conn.Close() // nolint: errcheck 102 103 grpcClient := pb.NewAllocationServiceClient(conn) 104 response, err = grpcClient.Allocate(context.Background(), request) 105 if err != nil { 106 logrus.WithError(err).Info("failing Allocate request") 107 return false, nil 108 } 109 helper.ValidateAllocatorResponse(t, response) 110 111 // let's do a re-allocation 112 if runtime.FeatureEnabled(runtime.FeaturePlayerAllocationFilter) { 113 // nolint:staticcheck 114 request.PreferredGameServerSelectors[0].GameServerState = pb.GameServerSelector_ALLOCATED 115 allocatedResponse, err := grpcClient.Allocate(context.Background(), request) 116 require.NoError(t, err) 117 require.Equal(t, response.GameServerName, allocatedResponse.GameServerName) 118 helper.ValidateAllocatorResponse(t, allocatedResponse) 119 120 // do a capacity based allocation 121 logrus.Info("testing capacity allocation filter") 122 // nolint:staticcheck 123 request.PreferredGameServerSelectors[0].Players = &pb.PlayerSelector{ 124 MinAvailable: 5, 125 MaxAvailable: 10, 126 } 127 allocatedResponse, err = grpcClient.Allocate(context.Background(), request) 128 require.NoError(t, err) 129 require.Equal(t, response.GameServerName, allocatedResponse.GameServerName) 130 helper.ValidateAllocatorResponse(t, allocatedResponse) 131 132 // do a capacity based allocation that should fail 133 // nolint:staticcheck 134 request.PreferredGameServerSelectors = nil 135 // nolint:staticcheck 136 request.RequiredGameServerSelector.GameServerState = pb.GameServerSelector_ALLOCATED 137 // nolint:staticcheck 138 request.RequiredGameServerSelector.Players = &pb.PlayerSelector{MinAvailable: 99, MaxAvailable: 200} 139 140 allocatedResponse, err = grpcClient.Allocate(context.Background(), request) 141 assert.Nil(t, allocatedResponse) 142 status, ok := status.FromError(err) 143 require.True(t, ok) 144 assert.Equal(t, codes.ResourceExhausted, status.Code()) 145 } 146 147 return true, nil 148 }) 149 150 require.NoError(t, err) 151 } 152 153 func TestAllocatorWithSelectors(t *testing.T) { 154 ctx := context.Background() 155 156 ip, port := helper.GetAllocatorEndpoint(ctx, t, framework) 157 requestURL := fmt.Sprintf(allocatorReqURLFmt, ip, port) 158 tlsCA := helper.RefreshAllocatorTLSCerts(ctx, t, ip, framework) 159 160 var flt *agonesv1.Fleet 161 var err error 162 if runtime.FeatureEnabled(runtime.FeaturePlayerAllocationFilter) { 163 flt, err = helper.CreateFleetWithOpts(ctx, framework.Namespace, framework, func(f *agonesv1.Fleet) { 164 f.Spec.Template.Spec.Players = &agonesv1.PlayersSpec{ 165 InitialCapacity: 10, 166 } 167 }) 168 } else { 169 flt, err = helper.CreateFleet(ctx, framework.Namespace, framework) 170 } 171 assert.NoError(t, err) 172 defer framework.AgonesClient.AgonesV1().Fleets(framework.Namespace).Delete(ctx, flt.Name, metav1.DeleteOptions{}) // nolint: errcheck 173 174 framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas)) 175 request := &pb.AllocationRequest{ 176 Namespace: framework.Namespace, 177 GameServerSelectors: []*pb.GameServerSelector{{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}}, 178 Scheduling: pb.AllocationRequest_Packed, 179 Metadata: &pb.MetaPatch{Labels: map[string]string{"gslabel": "allocatedbytest", "blue-frog.fred_thing": "test.dog_fred-blue"}}, 180 } 181 182 var response *pb.AllocationResponse 183 // wait for the allocation system to come online 184 err = wait.PollUntilContextTimeout(context.Background(), 2*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) { 185 // create the grpc client each time, as we may end up looking at an old cert 186 dialOpts, err := helper.CreateRemoteClusterDialOptions(ctx, allocatorClientSecretNamespace, allocatorClientSecretName, tlsCA, framework) 187 if err != nil { 188 return false, err 189 } 190 191 conn, err := grpc.NewClient(requestURL, dialOpts...) 192 if err != nil { 193 logrus.WithError(err).Info("failing grpc.NewClient") 194 return false, nil 195 } 196 defer conn.Close() // nolint: errcheck 197 198 grpcClient := pb.NewAllocationServiceClient(conn) 199 response, err = grpcClient.Allocate(context.Background(), request) 200 if err != nil { 201 logrus.WithError(err).Info("failing Allocate request") 202 return false, nil 203 } 204 helper.ValidateAllocatorResponse(t, response) 205 206 // let's do a re-allocation 207 if runtime.FeatureEnabled(runtime.FeaturePlayerAllocationFilter) { 208 request.GameServerSelectors[0].GameServerState = pb.GameServerSelector_ALLOCATED 209 allocatedResponse, err := grpcClient.Allocate(context.Background(), request) 210 require.NoError(t, err) 211 require.Equal(t, response.GameServerName, allocatedResponse.GameServerName) 212 helper.ValidateAllocatorResponse(t, allocatedResponse) 213 assert.Equal(t, flt.ObjectMeta.Name, allocatedResponse.Metadata.Labels[agonesv1.FleetNameLabel]) 214 215 // do a capacity based allocation 216 logrus.Info("testing capacity allocation filter") 217 request.GameServerSelectors[0].Players = &pb.PlayerSelector{ 218 MinAvailable: 5, 219 MaxAvailable: 10, 220 } 221 allocatedResponse, err = grpcClient.Allocate(context.Background(), request) 222 require.NoError(t, err) 223 require.Equal(t, response.GameServerName, allocatedResponse.GameServerName) 224 helper.ValidateAllocatorResponse(t, allocatedResponse) 225 226 // do a capacity based allocation that should fail 227 request.GameServerSelectors[0].GameServerState = pb.GameServerSelector_ALLOCATED 228 request.GameServerSelectors[0].Players = &pb.PlayerSelector{MinAvailable: 99, MaxAvailable: 200} 229 230 allocatedResponse, err = grpcClient.Allocate(context.Background(), request) 231 assert.Nil(t, allocatedResponse) 232 status, ok := status.FromError(err) 233 require.True(t, ok) 234 assert.Equal(t, codes.ResourceExhausted, status.Code()) 235 } 236 237 return true, nil 238 }) 239 240 assert.NoError(t, err) 241 } 242 243 func TestRestAllocatorWithDeprecatedRequired(t *testing.T) { 244 ctx := context.Background() 245 246 ip, port := helper.GetAllocatorEndpoint(ctx, t, framework) 247 requestURL := fmt.Sprintf(allocatorReqURLFmt, ip, port) 248 tlsCA := helper.RefreshAllocatorTLSCerts(ctx, t, ip, framework) 249 250 flt, err := helper.CreateFleet(ctx, framework.Namespace, framework) 251 if !assert.Nil(t, err) { 252 return 253 } 254 framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas)) 255 defer framework.AgonesClient.AgonesV1().Fleets(framework.Namespace).Delete(ctx, flt.Name, metav1.DeleteOptions{}) // nolint: errcheck 256 257 request := &pb.AllocationRequest{ 258 Namespace: framework.Namespace, 259 RequiredGameServerSelector: &pb.GameServerSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}, 260 PreferredGameServerSelectors: []*pb.GameServerSelector{{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}}, 261 Scheduling: pb.AllocationRequest_Packed, 262 Metadata: &pb.MetaPatch{Labels: map[string]string{"gslabel": "allocatedbytest"}}, 263 } 264 tlsCfg, err := helper.GetTLSConfig(ctx, allocatorClientSecretNamespace, allocatorClientSecretName, tlsCA, framework) 265 if !assert.Nil(t, err) { 266 return 267 } 268 client := &http.Client{ 269 Transport: &http.Transport{ 270 TLSClientConfig: tlsCfg, 271 }, 272 } 273 jsonRes, err := json.Marshal(request) 274 if !assert.Nil(t, err) { 275 return 276 } 277 req, err := http.NewRequest("POST", "https://"+requestURL+"/gameserverallocation", bytes.NewBuffer(jsonRes)) 278 if !assert.Nil(t, err) { 279 logrus.WithError(err).Info("failed to create rest request") 280 return 281 } 282 283 // wait for the allocation system to come online 284 err = wait.PollUntilContextTimeout(context.Background(), 2*time.Second, 5*time.Minute, true, func(_ context.Context) (bool, error) { 285 resp, err := client.Do(req) 286 if err != nil { 287 logrus.WithError(err).Info("failed Allocate rest request") 288 return false, nil 289 } 290 body, err := io.ReadAll(resp.Body) 291 if err != nil { 292 logrus.WithError(err).Info("failed to read Allocate response body") 293 return false, nil 294 } 295 defer resp.Body.Close() // nolint: errcheck 296 var response pb.AllocationResponse 297 err = json.Unmarshal(body, &response) 298 if err != nil { 299 logrus.WithError(err).Info("failed to unmarshal Allocate response") 300 return false, nil 301 } 302 helper.ValidateAllocatorResponse(t, &response) 303 return true, nil 304 }) 305 306 assert.NoError(t, err) 307 } 308 309 func TestAllocatorWithCountersAndLists(t *testing.T) { 310 if !runtime.FeatureEnabled(runtime.FeatureCountsAndLists) { 311 t.Skip("FeatureCountsAndLists is not enabled") 312 return 313 } 314 ctx := context.Background() 315 316 ip, port := helper.GetAllocatorEndpoint(ctx, t, framework) 317 requestURL := fmt.Sprintf(allocatorReqURLFmt, ip, port) 318 tlsCA := helper.RefreshAllocatorTLSCerts(ctx, t, ip, framework) 319 320 var flt *agonesv1.Fleet 321 var err error 322 flt, err = helper.CreateFleetWithOpts(ctx, framework.Namespace, framework, func(f *agonesv1.Fleet) { 323 f.Spec.Template.Spec.Counters = map[string]agonesv1.CounterStatus{ 324 "players": { 325 Capacity: 10, 326 }, 327 } 328 f.Spec.Template.Spec.Lists = map[string]agonesv1.ListStatus{ 329 "rooms": { 330 Capacity: 10, 331 }, 332 } 333 }) 334 assert.NoError(t, err) 335 defer framework.AgonesClient.AgonesV1().Fleets(framework.Namespace).Delete(ctx, flt.Name, metav1.DeleteOptions{}) // nolint: errcheck 336 framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas)) 337 338 request := &pb.AllocationRequest{ 339 Namespace: framework.Namespace, 340 GameServerSelectors: []*pb.GameServerSelector{{ 341 MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}, 342 Counters: map[string]*pb.CounterSelector{ 343 "players": { 344 MinAvailable: 1, 345 }, 346 }, 347 Lists: map[string]*pb.ListSelector{ 348 "rooms": { 349 MinAvailable: 1, 350 }, 351 }, 352 }}, 353 Counters: map[string]*pb.CounterAction{ 354 "players": { 355 Action: wrapperspb.String(agonesv1.GameServerPriorityIncrement), 356 Amount: wrapperspb.Int64(1), 357 }, 358 }, 359 Lists: map[string]*pb.ListAction{ 360 "rooms": { 361 AddValues: []string{"1"}, 362 DeleteValues: []string{"2"}, // This action is ignored. (Value does not exist.) 363 }, 364 }, 365 } 366 err = wait.PollUntilContextTimeout(context.Background(), 2*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) { 367 dialOpts, err := helper.CreateRemoteClusterDialOptions(ctx, allocatorClientSecretNamespace, allocatorClientSecretName, tlsCA, framework) 368 if err != nil { 369 return false, err 370 } 371 conn, err := grpc.NewClient(requestURL, dialOpts...) 372 if err != nil { 373 logrus.WithError(err).Info("failing grpc.NewClient") 374 return false, nil 375 } 376 defer conn.Close() // nolint: errcheck 377 378 grpcClient := pb.NewAllocationServiceClient(conn) 379 response, err := grpcClient.Allocate(context.Background(), request) 380 if err != nil { 381 return false, nil 382 } 383 assert.Contains(t, response.GetCounters(), "players") 384 assert.Equal(t, int64(10), response.GetCounters()["players"].Capacity.GetValue()) 385 assert.Equal(t, int64(1), response.GetCounters()["players"].Count.GetValue()) 386 assert.Contains(t, response.GetLists(), "rooms") 387 assert.Equal(t, int64(10), response.GetLists()["rooms"].Capacity.GetValue()) 388 assert.EqualValues(t, request.Lists["rooms"].AddValues, response.GetLists()["rooms"].Values) 389 assert.NotEqualValues(t, request.Lists["rooms"].DeleteValues, response.GetLists()["rooms"].Values) 390 return true, nil 391 }) 392 require.NoError(t, err) 393 } 394 395 func TestRestAllocatorWithCountersAndLists(t *testing.T) { 396 if !runtime.FeatureEnabled(runtime.FeatureCountsAndLists) { 397 t.Skip("FeatureCountsAndLists is not enabled") 398 return 399 } 400 ctx := context.Background() 401 402 ip, port := helper.GetAllocatorEndpoint(ctx, t, framework) 403 requestURL := fmt.Sprintf(allocatorReqURLFmt, ip, port) 404 tlsCA := helper.RefreshAllocatorTLSCerts(ctx, t, ip, framework) 405 406 var flt *agonesv1.Fleet 407 var err error 408 flt, err = helper.CreateFleetWithOpts(ctx, framework.Namespace, framework, func(f *agonesv1.Fleet) { 409 f.Spec.Template.Spec.Counters = map[string]agonesv1.CounterStatus{ 410 "players": { 411 Capacity: 10, 412 }, 413 } 414 f.Spec.Template.Spec.Lists = map[string]agonesv1.ListStatus{ 415 "rooms": { 416 Values: []string{"one", "two", "three"}, 417 Capacity: 10, 418 }, 419 } 420 }) 421 assert.NoError(t, err) 422 defer framework.AgonesClient.AgonesV1().Fleets(framework.Namespace).Delete(ctx, flt.Name, metav1.DeleteOptions{}) // nolint: errcheck 423 framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas)) 424 425 request := &pb.AllocationRequest{ 426 Namespace: framework.Namespace, 427 GameServerSelectors: []*pb.GameServerSelector{{ 428 MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}, 429 Counters: map[string]*pb.CounterSelector{ 430 "players": { 431 MinAvailable: 1, 432 }, 433 }, 434 Lists: map[string]*pb.ListSelector{ 435 "rooms": { 436 MinAvailable: 1, 437 }, 438 }, 439 }}, 440 Counters: map[string]*pb.CounterAction{ 441 "players": { 442 Action: wrapperspb.String(agonesv1.GameServerPriorityIncrement), 443 Amount: wrapperspb.Int64(1), 444 }, 445 }, 446 Lists: map[string]*pb.ListAction{ 447 "rooms": { 448 AddValues: []string{"1"}, 449 DeleteValues: []string{"three", "one"}, 450 }, 451 }, 452 } 453 err = wait.PollUntilContextTimeout(context.Background(), 2*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) { 454 tlsCfg, err := helper.GetTLSConfig(ctx, allocatorClientSecretNamespace, allocatorClientSecretName, tlsCA, framework) 455 if !assert.Nil(t, err) { 456 return false, err 457 } 458 client := &http.Client{ 459 Transport: &http.Transport{ 460 TLSClientConfig: tlsCfg, 461 }, 462 } 463 jsonRes, err := protojson.Marshal(request) 464 if !assert.Nil(t, err) { 465 return false, nil 466 } 467 req, err := http.NewRequest("POST", "https://"+requestURL+"/gameserverallocation", bytes.NewBuffer(jsonRes)) 468 if !assert.Nil(t, err) { 469 return false, nil 470 } 471 resp, err := client.Do(req) 472 if err != nil { 473 return false, nil 474 } 475 body, err := io.ReadAll(resp.Body) 476 if err != nil { 477 return false, nil 478 } 479 defer resp.Body.Close() // nolint: errcheck 480 if resp.StatusCode != http.StatusOK { 481 return false, nil 482 } 483 var response pb.AllocationResponse 484 err = protojson.Unmarshal(body, &response) 485 if err != nil { 486 return false, nil 487 } 488 assert.Contains(t, response.GetCounters(), "players") 489 assert.Equal(t, int64(10), response.GetCounters()["players"].Capacity.GetValue()) 490 assert.Equal(t, int64(1), response.GetCounters()["players"].Count.GetValue()) 491 assert.Contains(t, response.GetLists(), "rooms") 492 assert.Equal(t, int64(10), response.GetLists()["rooms"].Capacity.GetValue()) 493 assert.Contains(t, response.GetLists()["rooms"].Values, request.Lists["rooms"].AddValues[0]) 494 assert.NotContains(t, response.GetLists()["rooms"].Values, request.Lists["rooms"].DeleteValues[0]) 495 assert.NotContains(t, response.GetLists()["rooms"].Values, request.Lists["rooms"].DeleteValues[1]) 496 return true, nil 497 }) 498 require.NoError(t, err) 499 } 500 501 func TestRestAllocatorWithSelectors(t *testing.T) { 502 ctx := context.Background() 503 504 ip, port := helper.GetAllocatorEndpoint(ctx, t, framework) 505 requestURL := fmt.Sprintf(allocatorReqURLFmt, ip, port) 506 tlsCA := helper.RefreshAllocatorTLSCerts(ctx, t, ip, framework) 507 508 flt, err := helper.CreateFleet(ctx, framework.Namespace, framework) 509 require.NoError(t, err) 510 defer framework.AgonesClient.AgonesV1().Fleets(framework.Namespace).Delete(ctx, flt.Name, metav1.DeleteOptions{}) // nolint: errcheck 511 framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas)) 512 513 request := &pb.AllocationRequest{ 514 Namespace: framework.Namespace, 515 GameServerSelectors: []*pb.GameServerSelector{{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}}, 516 Scheduling: pb.AllocationRequest_Packed, 517 Metadata: &pb.MetaPatch{Labels: map[string]string{"gslabel": "allocatedbytest", "blue-frog.fred_thing": "test.dog_fred-blue"}}, 518 } 519 tlsCfg, err := helper.GetTLSConfig(ctx, allocatorClientSecretNamespace, allocatorClientSecretName, tlsCA, framework) 520 if !assert.Nil(t, err) { 521 return 522 } 523 client := &http.Client{ 524 Transport: &http.Transport{ 525 TLSClientConfig: tlsCfg, 526 }, 527 } 528 jsonRes, err := json.Marshal(request) 529 if !assert.Nil(t, err) { 530 return 531 } 532 req, err := http.NewRequest("POST", "https://"+requestURL+"/gameserverallocation", bytes.NewBuffer(jsonRes)) 533 if !assert.Nil(t, err) { 534 logrus.WithError(err).Info("failed to create rest request") 535 return 536 } 537 538 // wait for the allocation system to come online 539 var response pb.AllocationResponse 540 err = wait.PollUntilContextTimeout(context.Background(), 2*time.Second, 5*time.Minute, true, func(_ context.Context) (bool, error) { 541 resp, err := client.Do(req) 542 if err != nil { 543 logrus.WithError(err).Info("failed Allocate rest request") 544 return false, nil 545 } 546 body, err := io.ReadAll(resp.Body) 547 if err != nil { 548 logrus.WithError(err).Info("failed to read Allocate response body") 549 return false, nil 550 } 551 defer resp.Body.Close() // nolint: errcheck 552 err = json.Unmarshal(body, &response) 553 if err != nil { 554 logrus.WithError(err).Info("failed to unmarshal Allocate response") 555 return false, nil 556 } 557 helper.ValidateAllocatorResponse(t, &response) 558 return true, nil 559 }) 560 require.NoError(t, err) 561 562 gs, err := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Get(ctx, response.GameServerName, metav1.GetOptions{}) 563 require.NoError(t, err) 564 assert.Equal(t, agonesv1.GameServerStateAllocated, gs.Status.State) 565 assert.Equal(t, "allocatedbytest", gs.ObjectMeta.Labels["gslabel"]) 566 assert.Equal(t, "test.dog_fred-blue", gs.ObjectMeta.Labels["blue-frog.fred_thing"]) 567 } 568 569 // Tests multi-cluster allocation by reusing the same cluster but across namespace. 570 // Multi-cluster is represented as two namespaces A and B in the same cluster. 571 // Namespace A received the allocation request, but because namespace B has the highest priority, A will forward the request to B. 572 func TestAllocatorCrossNamespace(t *testing.T) { 573 ctx := context.Background() 574 575 ip, port := helper.GetAllocatorEndpoint(ctx, t, framework) 576 requestURL := fmt.Sprintf(allocatorReqURLFmt, ip, port) 577 tlsCA := helper.RefreshAllocatorTLSCerts(ctx, t, ip, framework) 578 579 // Create namespaces A and B 580 namespaceA := framework.Namespace // let's reuse an existing one 581 helper.CopyDefaultAllocatorClientSecret(ctx, t, namespaceA, framework) 582 583 namespaceB := fmt.Sprintf("allocator-b-%s", uuid.NewUUID()) 584 err := framework.CreateNamespace(namespaceB) 585 if !assert.Nil(t, err) { 586 return 587 } 588 defer func() { 589 if derr := framework.DeleteNamespace(namespaceB); derr != nil { 590 t.Error(derr) 591 } 592 }() 593 594 policyName := fmt.Sprintf("a-to-b-%s", uuid.NewUUID()) 595 p := &multiclusterv1.GameServerAllocationPolicy{ 596 ObjectMeta: metav1.ObjectMeta{ 597 Name: policyName, 598 Namespace: namespaceA, 599 }, 600 Spec: multiclusterv1.GameServerAllocationPolicySpec{ 601 Priority: 1, 602 Weight: 1, 603 ConnectionInfo: multiclusterv1.ClusterConnectionInfo{ 604 SecretName: allocatorClientSecretName, 605 Namespace: namespaceB, 606 AllocationEndpoints: []string{ip}, 607 ServerCA: tlsCA, 608 }, 609 }, 610 } 611 helper.CreateAllocationPolicy(ctx, t, framework, p) 612 613 // Create a fleet in namespace B. Allocation should not happen in A according to policy 614 flt, err := helper.CreateFleet(ctx, namespaceB, framework) 615 if !assert.Nil(t, err) { 616 return 617 } 618 framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas)) 619 defer framework.AgonesClient.AgonesV1().Fleets(namespaceB).Delete(ctx, flt.Name, metav1.DeleteOptions{}) // nolint: errcheck 620 621 request := &pb.AllocationRequest{ 622 Namespace: namespaceA, 623 // Enable multi-cluster setting 624 MultiClusterSetting: &pb.MultiClusterSetting{Enabled: true}, 625 GameServerSelectors: []*pb.GameServerSelector{{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}}, 626 } 627 628 // wait for the allocation system to come online 629 err = wait.PollUntilContextTimeout(context.Background(), 2*time.Second, 5*time.Minute, true, func(ctx context.Context) (bool, error) { 630 // create the grpc client each time, as we may end up looking at an old cert 631 dialOpts, err := helper.CreateRemoteClusterDialOptions(ctx, namespaceA, allocatorClientSecretName, tlsCA, framework) 632 if err != nil { 633 return false, err 634 } 635 636 conn, err := grpc.NewClient(requestURL, dialOpts...) 637 if err != nil { 638 logrus.WithError(err).Info("failing grpc.NewClient") 639 return false, nil 640 } 641 defer conn.Close() // nolint: errcheck 642 643 grpcClient := pb.NewAllocationServiceClient(conn) 644 response, err := grpcClient.Allocate(context.Background(), request) 645 if err != nil { 646 logrus.WithError(err).Info("failing Allocate request") 647 return false, nil 648 } 649 helper.ValidateAllocatorResponse(t, response) 650 return true, nil 651 }) 652 653 assert.NoError(t, err) 654 }