Skip to content

Commit

Permalink
Introduce the Source field in GameServerAllocationStatus to indicate …
Browse files Browse the repository at this point in the history
…the allocation source (#2860)

* Introduce the Source field in GameServerAllocationStatus to indicate the allocation source

* fix indent

* ignore space check for the new added line

* fix markdown

* apply gofmt formatting
  • Loading branch information
gongmax committed Dec 13, 2022
1 parent 60b48ec commit 05da749
Show file tree
Hide file tree
Showing 11 changed files with 2,655 additions and 2,492 deletions.
4 changes: 3 additions & 1 deletion pkg/allocation/converters/converter.go
Expand Up @@ -242,11 +242,12 @@ func ConvertGSAToAllocationResponse(in *allocationv1.GameServerAllocation) (*pb.
Address: in.Status.Address,
NodeName: in.Status.NodeName,
Ports: convertGSAAgonesPortsToAllocationPorts(in.Status.Ports),
Source: in.Status.Source,
}, nil
}

// ConvertAllocationResponseToGSA converts AllocationResponse to GameServerAllocation V1 (GSA)
func ConvertAllocationResponseToGSA(in *pb.AllocationResponse) *allocationv1.GameServerAllocation {
func ConvertAllocationResponseToGSA(in *pb.AllocationResponse, rs string) *allocationv1.GameServerAllocation {
if in == nil {
return nil
}
Expand All @@ -258,6 +259,7 @@ func ConvertAllocationResponseToGSA(in *pb.AllocationResponse) *allocationv1.Gam
Address: in.Address,
NodeName: in.NodeName,
Ports: convertAllocationPortsToGSAAgonesPorts(in.Ports),
Source: rs,
},
}
out.SetGroupVersionKind(allocationv1.SchemeGroupVersion.WithKind("GameServerAllocation"))
Expand Down
16 changes: 12 additions & 4 deletions pkg/allocation/converters/converter_test.go
Expand Up @@ -523,6 +523,7 @@ func TestConvertGSAToAllocationResponse(t *testing.T) {
},
Address: "address",
NodeName: "node-name",
Source: "local",
},
},
want: &pb.AllocationResponse{
Expand All @@ -537,6 +538,7 @@ func TestConvertGSAToAllocationResponse(t *testing.T) {
Name: "port-name",
},
},
Source: "local",
},
},
{
Expand Down Expand Up @@ -629,7 +631,11 @@ func TestConvertGSAToAllocationResponse(t *testing.T) {
}

if !tc.skipConvertToGSA {
gsa := ConvertAllocationResponseToGSA(tc.want)
source := ""
if tc.in != nil {
source = tc.in.Status.Source
}
gsa := ConvertAllocationResponseToGSA(tc.want, source)
if !assert.Equal(t, tc.in, gsa) {
t.Errorf("mismatch with input after double conversion \"%s\"", tc.name)
}
Expand All @@ -647,15 +653,17 @@ func TestConvertAllocationResponseToGSA(t *testing.T) {
{
name: "Empty fields",
in: &pb.AllocationResponse{
Ports: []*pb.AllocationResponse_GameServerStatusPort{},
Ports: []*pb.AllocationResponse_GameServerStatusPort{},
Source: "33.188.237.156:443",
},
want: &allocationv1.GameServerAllocation{
TypeMeta: metav1.TypeMeta{
Kind: "GameServerAllocation",
APIVersion: "allocation.agones.dev/v1",
},
Status: allocationv1.GameServerAllocationStatus{
State: allocationv1.GameServerAllocationAllocated,
State: allocationv1.GameServerAllocationAllocated,
Source: "33.188.237.156:443",
},
},
},
Expand All @@ -665,7 +673,7 @@ func TestConvertAllocationResponseToGSA(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

out := ConvertAllocationResponseToGSA(tc.in)
out := ConvertAllocationResponseToGSA(tc.in, tc.in.Source)
if !assert.Equal(t, tc.want, out) {
t.Errorf("mismatch with want after conversion: \"%s\"", tc.name)
}
Expand Down
180 changes: 95 additions & 85 deletions pkg/allocation/go/allocation.pb.go

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions pkg/allocation/go/allocation.swagger.json
Expand Up @@ -136,6 +136,9 @@
},
"nodeName": {
"type": "string"
},
"source": {
"type": "string"
}
}
},
Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/allocation/v1/gameserverallocation.go
Expand Up @@ -279,6 +279,9 @@ type GameServerAllocationStatus struct {
Ports []agonesv1.GameServerStatusPort `json:"ports,omitempty"`
Address string `json:"address,omitempty"`
NodeName string `json:"nodeName,omitempty"`
// If the allocation is from a remote cluster, Source is the endpoint of the remote agones-allocator.
// Otherwise, Source is "local"
Source string `json:"source"`
}

// ApplyDefaults applies the default values to this GameServerAllocation
Expand Down
7 changes: 5 additions & 2 deletions pkg/gameserverallocations/allocator.go
Expand Up @@ -78,6 +78,7 @@ const (
allocatorPort = "443"
maxBatchQueue = 100
maxBatchBeforeRefresh = 100
localAllocationSource = "local"
)

var allocationRetry = wait.Backoff{
Expand Down Expand Up @@ -281,6 +282,7 @@ func (c *Allocator) allocateFromLocalCluster(ctx context.Context, gsa *allocatio
gsa.Status.Ports = gs.Status.Ports
gsa.Status.Address = gs.Status.Address
gsa.Status.NodeName = gs.Status.NodeName
gsa.Status.Source = localAllocationSource
}

c.loggerForGameServerAllocation(gsa).Debug("Game server allocation")
Expand Down Expand Up @@ -356,14 +358,15 @@ func (c *Allocator) allocateFromRemoteCluster(gsa *allocationv1.GameServerAlloca
ctx, cancel := context.WithTimeout(context.Background(), c.totalRemoteAllocationTimeout)
defer cancel() // nolint: errcheck
// Retry on remote call failures.
var endpoint string
err = Retry(remoteAllocationRetry, func() error {
for i, ip := range connectionInfo.AllocationEndpoints {
select {
case <-ctx.Done():
return ErrTotalTimeoutExceeded
default:
}
endpoint := addPort(ip)
endpoint = addPort(ip)
c.loggerForGameServerAllocationKey("remote-allocation").WithField("request", request).WithField("endpoint", endpoint).Debug("forwarding allocation request")
allocationResponse, err = c.remoteAllocationCallback(ctx, endpoint, dialOpts, request)
if err != nil {
Expand All @@ -383,7 +386,7 @@ func (c *Allocator) allocateFromRemoteCluster(gsa *allocationv1.GameServerAlloca
return nil
})

return converters.ConvertAllocationResponseToGSA(allocationResponse), err
return converters.ConvertAllocationResponseToGSA(allocationResponse, endpoint), err
}

// createRemoteClusterDialOption creates a grpc client dial option with proper certs to make a remote call.
Expand Down
2 changes: 1 addition & 1 deletion pkg/gameserverallocations/metrics.go
Expand Up @@ -107,7 +107,7 @@ func (r *metrics) setResponse(o k8sruntime.Object) {
r.setStatus(string(out.Status.State))
var tags []tag.Mutator
// sets the fleet name tag if possible
if out.Status.State == allocationv1.GameServerAllocationAllocated {
if out.Status.State == allocationv1.GameServerAllocationAllocated && out.Status.Source == localAllocationSource {
gs, err := r.gameServerLister.GameServers(out.Namespace).Get(out.Status.GameServerName)
if err != nil {
r.logger.WithError(err).Warnf("failed to get gameserver:%s namespace:%s", out.Status.GameServerName, out.Namespace)
Expand Down
119 changes: 119 additions & 0 deletions pkg/gameserverallocations/metrics_test.go
@@ -0,0 +1,119 @@
// Copyright 2022 Google LLC All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gameserverallocations

import (
"context"
"testing"
"time"

agonesv1 "agones.dev/agones/pkg/apis/agones/v1"
allocationv1 "agones.dev/agones/pkg/apis/allocation/v1"
gameserverv1 "agones.dev/agones/pkg/client/listers/agones/v1"
"agones.dev/agones/pkg/util/runtime"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
)

type mockGameServerLister struct {
gameServerNamespaceLister mockGameServerNamespaceLister
gameServersCalled bool
}

type mockGameServerNamespaceLister struct {
gameServer *agonesv1.GameServer
}

func (s *mockGameServerLister) List(selector labels.Selector) (ret []*agonesv1.GameServer, err error) {
return ret, nil
}

func (s *mockGameServerLister) GameServers(namespace string) gameserverv1.GameServerNamespaceLister {
s.gameServersCalled = true
return s.gameServerNamespaceLister
}

func (s mockGameServerNamespaceLister) Get(name string) (*agonesv1.GameServer, error) {
return s.gameServer, nil
}

func (s mockGameServerNamespaceLister) List(selector labels.Selector) (ret []*agonesv1.GameServer, err error) {
return ret, nil
}

func TestSetResponse(t *testing.T) {
subtests := []struct {
name string
gameServer *agonesv1.GameServer
err error
allocation *allocationv1.GameServerAllocation
expectedState allocationv1.GameServerAllocationState
expectedCalled bool
}{
{
name: "Try to get gs from local cluster for local allocation",
gameServer: &agonesv1.GameServer{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{agonesv1.FleetNameLabel: "fleetName"},
},
},
allocation: &allocationv1.GameServerAllocation{
Status: allocationv1.GameServerAllocationStatus{
State: allocationv1.GameServerAllocationAllocated,
GameServerName: "gameServerName",
Source: "local",
},
},
expectedCalled: true,
},
{
name: "Do not try to get gs from local cluster for remote allocation",
gameServer: &agonesv1.GameServer{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{agonesv1.FleetNameLabel: "fleetName"},
},
},
allocation: &allocationv1.GameServerAllocation{
Status: allocationv1.GameServerAllocationStatus{
State: allocationv1.GameServerAllocationAllocated,
GameServerName: "gameServerName",
Source: "33.188.237.156:443",
},
},
expectedCalled: false,
},
}

for _, subtest := range subtests {
gsl := mockGameServerLister{
gameServerNamespaceLister: mockGameServerNamespaceLister{
gameServer: subtest.gameServer,
},
}

metrics := metrics{
ctx: context.Background(),
gameServerLister: &gsl,
logger: runtime.NewLoggerWithSource("metrics_test"),
start: time.Now(),
}

t.Run(subtest.name, func(t *testing.T) {
metrics.setResponse(subtest.allocation)
assert.Equal(t, subtest.expectedCalled, gsl.gameServersCalled)
})
}
}
1 change: 1 addition & 0 deletions proto/allocation/allocation.proto
Expand Up @@ -84,6 +84,7 @@ message AllocationResponse {
repeated GameServerStatusPort ports = 3;
string address = 4;
string nodeName = 5;
string source = 6;

// The gameserver port info that is allocated.
message GameServerStatusPort {
Expand Down
5 changes: 3 additions & 2 deletions site/content/en/docs/Advanced/multi-cluster-allocation.md
Expand Up @@ -71,7 +71,7 @@ EOF

The certificates are base 64 string of the certificate file e.g. `cat ${CERT_FILE} | base64 -w 0`

Agones recommends using [cert-manager.io](https://cert-manager.io/) solution for generating client certificates.
Agones recommends using [cert-manager.io](https://cert-manager.io/) solution for generating client certificates.

2.Add client CA to the list of authorized client certificates by agones-allocator in the targeted cluster.

Expand All @@ -93,9 +93,10 @@ EOF

## Allocate multi-cluster

To enable multi-cluster allocation, set `multiClusterSetting.enabled` to `true` in {{< ghlink href="proto/allocation/allocation.proto" >}}allocation.proto{{< /ghlink >}} and send allocation requests. For more information visit [agones-allocator]({{< relref "allocator-service.md">}}). In the following, using {{< ghlink href="examples/allocator-client/main.go" >}}allocator-client sample{{< /ghlink >}}, a multi-cluster allocation request is sent to the agones-allocator service.
To enable multi-cluster allocation, set `multiClusterSetting.enabled` to `true` in {{< ghlink href="proto/allocation/allocation.proto" >}}allocation.proto{{< /ghlink >}} and send allocation requests. For more information visit [agones-allocator]({{< relref "allocator-service.md">}}). In the following, using {{< ghlink href="examples/allocator-client/main.go" >}}allocator-client sample{{< /ghlink >}}, a multi-cluster allocation request is sent to the agones-allocator service. If the allocation succeeds, the AllocationResponse will contain a {{< ghlink href="proto/allocation/allocation.proto" >}}Source{{< /ghlink >}} field which indicates the endpoint of the remote agones-allocator.

Set the environment variables and store the client secrets before allocating using gRPC or REST APIs

```bash
#!/bin/bash

Expand Down

0 comments on commit 05da749

Please sign in to comment.