Skip to content

Commit

Permalink
Change the API for allocator to allocation.proto (#1123)
Browse files Browse the repository at this point in the history
  • Loading branch information
pooneh-m authored and roberthbailey committed Oct 17, 2019
1 parent 17912d1 commit b22140a
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 58 deletions.
18 changes: 12 additions & 6 deletions cmd/allocator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ import (
"strings"

"agones.dev/agones/pkg"
allocationv1 "agones.dev/agones/pkg/apis/allocation/v1"
"agones.dev/agones/pkg/allocation/converters"
pb "agones.dev/agones/pkg/allocation/go/v1alpha1"
"agones.dev/agones/pkg/client/clientset/versioned"
"agones.dev/agones/pkg/metrics"
"agones.dev/agones/pkg/util/runtime"
Expand Down Expand Up @@ -183,23 +184,28 @@ type httpHandler struct {
}

func (h *httpHandler) allocateHandler(w http.ResponseWriter, r *http.Request) {
gsa := allocationv1.GameServerAllocation{}
if err := json.NewDecoder(r.Body).Decode(&gsa); err != nil {
request := pb.AllocationRequest{}
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
http.Error(w, "invalid request", http.StatusBadRequest)
logger.WithError(err).Info("bad request")
return
}
logger.WithField("gsa", gsa).Infof("allocation request received")
logger.WithField("request", request).Infof("allocation request received")

gsa := converters.ConvertAllocationRequestV1Alpha1ToGSAV1(&request)
allocation := h.agonesClient.AllocationV1().GameServerAllocations(gsa.ObjectMeta.Namespace)
allocatedGsa, err := allocation.Create(&gsa)
allocatedGsa, err := allocation.Create(gsa)
if err != nil {
http.Error(w, err.Error(), httpCode(err))
logger.WithField("gsa", gsa).WithError(err).Info("calling allocation extension API failed")
return
}

response := converters.ConvertGSAV1ToAllocationResponseV1Alpha1(allocatedGsa)
logger.WithField("response", response).Infof("allocation response is being sent")

w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(allocatedGsa)
err = json.NewEncoder(w).Encode(response)
if err != nil {
http.Error(w, "internal server error", http.StatusInternalServerError)
logger.Error(err)
Expand Down
20 changes: 13 additions & 7 deletions cmd/allocator/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"os"
"testing"

pb "agones.dev/agones/pkg/allocation/go/v1alpha1"
allocationv1 "agones.dev/agones/pkg/apis/allocation/v1"
agonesfake "agones.dev/agones/pkg/client/clientset/versioned/fake"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -50,8 +51,13 @@ func TestAllocateHandler(t *testing.T) {
}, nil
})

gsa := &allocationv1.GameServerAllocation{}
body, _ := json.Marshal(gsa)
request := &pb.AllocationRequest{
Namespace: "ns",
MultiClusterSetting: &pb.MultiClusterSetting{
Enabled: true,
},
}
body, _ := json.Marshal(request)
buf := bytes.NewBuffer(body)
req, err := http.NewRequest(http.MethodPost, "/", buf)
if !assert.Nil(t, err) {
Expand All @@ -61,12 +67,12 @@ func TestAllocateHandler(t *testing.T) {
rec := httptest.NewRecorder()
h.allocateHandler(rec, req)

ret := &allocationv1.GameServerAllocation{}
response := &pb.AllocationResponse{}
assert.Equal(t, rec.Code, 200)
assert.Equal(t, "application/json", rec.Header()["Content-Type"][0])
err = json.Unmarshal(rec.Body.Bytes(), ret)
err = json.Unmarshal(rec.Body.Bytes(), response)
assert.NoError(t, err)
assert.Equal(t, allocationv1.GameServerAllocationContention, ret.Status.State)
assert.Equal(t, pb.AllocationResponse_Contention, response.State)
}

func TestAllocateHandlerReturnsError(t *testing.T) {
Expand All @@ -81,8 +87,8 @@ func TestAllocateHandlerReturnsError(t *testing.T) {
return true, nil, k8serror.NewBadRequest("error")
})

gsa := &allocationv1.GameServerAllocation{}
body, _ := json.Marshal(gsa)
request := &pb.AllocationRequest{}
body, _ := json.Marshal(request)
buf := bytes.NewBuffer(body)
req, err := http.NewRequest(http.MethodPost, "/", buf)
if !assert.Nil(t, err) {
Expand Down
23 changes: 13 additions & 10 deletions pkg/gameserverallocations/allocator.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
"strconv"
"time"

"agones.dev/agones/pkg/allocation/converters"
pb "agones.dev/agones/pkg/allocation/go/v1alpha1"
agonesv1 "agones.dev/agones/pkg/apis/agones/v1"
allocationv1 "agones.dev/agones/pkg/apis/allocation/v1"
multiclusterv1alpha1 "agones.dev/agones/pkg/apis/multicluster/v1alpha1"
Expand Down Expand Up @@ -283,7 +285,7 @@ func (c *Allocator) applyMultiClusterAllocation(gsa *allocationv1.GameServerAllo
c.loggerForGameServerAllocation(gsaCopy).WithError(err).Error("self-allocation failed")
}
} else {
result, err = c.allocateFromRemoteCluster(*gsa, connectionInfo, gsa.ObjectMeta.Namespace)
result, err = c.allocateFromRemoteCluster(gsa, connectionInfo, gsa.ObjectMeta.Namespace)
if err != nil {
c.loggerForGameServerAllocation(gsa).WithField("allocConnInfo", connectionInfo).WithError(err).Error("remote-allocation failed")
}
Expand All @@ -297,8 +299,8 @@ func (c *Allocator) applyMultiClusterAllocation(gsa *allocationv1.GameServerAllo

// allocateFromRemoteCluster allocates gameservers from a remote cluster by making
// an http call to allocation service in that cluster.
func (c *Allocator) allocateFromRemoteCluster(gsa allocationv1.GameServerAllocation, connectionInfo *multiclusterv1alpha1.ClusterConnectionInfo, namespace string) (*allocationv1.GameServerAllocation, error) {
var gsaResult allocationv1.GameServerAllocation
func (c *Allocator) allocateFromRemoteCluster(gsa *allocationv1.GameServerAllocation, connectionInfo *multiclusterv1alpha1.ClusterConnectionInfo, namespace string) (*allocationv1.GameServerAllocation, error) {
var allocationResponse pb.AllocationResponse

// TODO: handle converting error to apiserver error
// TODO: cache the client
Expand All @@ -310,16 +312,17 @@ func (c *Allocator) allocateFromRemoteCluster(gsa allocationv1.GameServerAllocat
// Forward the game server allocation request to another cluster,
// and disable multicluster settings to avoid the target cluster
// forward the allocation request again.
gsa.Spec.MultiClusterSetting.Enabled = false
gsa.Namespace = connectionInfo.Namespace
body, err := json.Marshal(gsa)
request := converters.ConvertGSAV1ToAllocationRequestV1Alpha1(gsa)
request.MultiClusterSetting.Enabled = false
request.Namespace = connectionInfo.Namespace
body, err := json.Marshal(request)
if err != nil {
return nil, err
}

// TODO: Retry on transient error --> response.StatusCode >= 500
for i, endpoint := range connectionInfo.AllocationEndpoints {
c.loggerForGameServerAllocation(&gsa).WithField("endpoint", endpoint).Info("forwarding allocation request")
c.loggerForGameServerAllocationKey("remote-allocation").WithField("request", request).WithField("endpoint", endpoint).Info("forwarding allocation request")
requestURL := fmt.Sprintf(allocatorRequestURLFmt, endpoint)
response, err := client.Post(requestURL, "application/json", bytes.NewBuffer(body))
if err != nil {
Expand All @@ -335,21 +338,21 @@ func (c *Allocator) allocateFromRemoteCluster(gsa allocationv1.GameServerAllocat
// failing with 5xx http status, try the next endpoint. Otherwise, return the error response.
if response.StatusCode >= 500 && (i+1) < len(connectionInfo.AllocationEndpoints) {
// If there is a server error try a different endpoint
c.loggerForGameServerAllocation(&gsa).WithError(err).WithField("endpoint", endpoint).Warn("The request failed. Trying next endpoint")
c.loggerForGameServerAllocationKey("remote-allocation").WithField("request", request).WithError(err).WithField("endpoint", endpoint).Warn("The request failed. Trying next endpoint")
continue
}
if response.StatusCode >= 400 {
// For error responses return the body without deserializing to an object.
return nil, errors.New(string(data))
}

err = json.Unmarshal(data, &gsaResult)
err = json.Unmarshal(data, &allocationResponse)
if err != nil {
return nil, err
}
break
}
return &gsaResult, nil
return converters.ConvertAllocationResponseV1Alpha1ToGSAV1(&allocationResponse), nil
}

// createRemoteClusterRestClient creates a rest client with proper certs to make a remote call.
Expand Down
21 changes: 9 additions & 12 deletions pkg/gameserverallocations/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"testing"
"time"

pb "agones.dev/agones/pkg/allocation/go/v1alpha1"
"agones.dev/agones/pkg/apis"
agonesv1 "agones.dev/agones/pkg/apis/agones/v1"
allocationv1 "agones.dev/agones/pkg/apis/allocation/v1"
Expand Down Expand Up @@ -909,12 +910,10 @@ func TestMultiClusterAllocationFromRemote(t *testing.T) {
fleetName := addReactorForGameServer(&m)

// Mock server
expectedGSAName := "mocked"
expectedGSName := "mocked"
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
serverResponse := allocationv1.GameServerAllocation{
ObjectMeta: metav1.ObjectMeta{
Name: expectedGSAName,
},
serverResponse := pb.AllocationResponse{
GameServerName: expectedGSName,
}
response, _ := json.Marshal(serverResponse)
_, _ = w.Write(response)
Expand Down Expand Up @@ -984,7 +983,7 @@ func TestMultiClusterAllocationFromRemote(t *testing.T) {

result, err := executeAllocation(gsa, c)
if assert.NoError(t, err) {
assert.Equal(t, expectedGSAName, result.ObjectMeta.Name)
assert.Equal(t, expectedGSName, result.Status.GameServerName)
}
})

Expand Down Expand Up @@ -1080,12 +1079,10 @@ func TestMultiClusterAllocationFromRemote(t *testing.T) {
unhealthyServer.TLS.ClientAuth = tls.RequireAndVerifyClientCert

// Mock healthyServer
expectedGSAName := "mocked"
expectedGSName := "mocked"
healthyServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
serverResponse := allocationv1.GameServerAllocation{
ObjectMeta: metav1.ObjectMeta{
Name: expectedGSAName,
},
serverResponse := pb.AllocationResponse{
GameServerName: expectedGSName,
}
response, _ := json.Marshal(serverResponse)
_, _ = w.Write(response)
Expand Down Expand Up @@ -1148,7 +1145,7 @@ func TestMultiClusterAllocationFromRemote(t *testing.T) {

result, err := executeAllocation(gsa, c)
if assert.NoError(t, err) {
assert.Equal(t, expectedGSAName, result.ObjectMeta.Name)
assert.Equal(t, expectedGSName, result.Status.GameServerName)
}
})
}
Expand Down
40 changes: 17 additions & 23 deletions test/e2e/allocator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ import (
"testing"
"time"

pb "agones.dev/agones/pkg/allocation/go/v1alpha1"
agonesv1 "agones.dev/agones/pkg/apis/agones/v1"
allocationv1 "agones.dev/agones/pkg/apis/allocation/v1"
multiclusterv1alpha1 "agones.dev/agones/pkg/apis/multicluster/v1alpha1"
e2e "agones.dev/agones/test/e2e/framework"
"github.com/pkg/errors"
Expand Down Expand Up @@ -75,15 +75,12 @@ func TestAllocator(t *testing.T) {
return
}
framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas))
gsa := &allocationv1.GameServerAllocation{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespace,
},
Spec: allocationv1.GameServerAllocationSpec{
Required: metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}},
}}
request := &pb.AllocationRequest{
Namespace: namespace,
RequiredGameServerSelector: &metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}},
}

body, err := json.Marshal(gsa)
body, err := json.Marshal(request)
if !assert.Nil(t, err) {
return
}
Expand Down Expand Up @@ -116,12 +113,12 @@ func TestAllocator(t *testing.T) {
assert.FailNow(t, "Http test failed")
}

result := allocationv1.GameServerAllocation{}
result := pb.AllocationResponse{}
err = json.Unmarshal(body, &result)
if !assert.Nil(t, err) {
t.Fatalf("failed to unmarshall response body: %s\nerror:%s", string(body), err)
}
assert.Equal(t, allocationv1.GameServerAllocationAllocated, result.Status.State)
assert.Equal(t, pb.AllocationResponse_Allocated, result.State)
}

// Tests multi-cluster allocation by reusing the same cluster but across namespace.
Expand Down Expand Up @@ -170,17 +167,14 @@ func TestAllocatorCrossNamespace(t *testing.T) {
return
}
framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas))
gsa := &allocationv1.GameServerAllocation{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespaceA,
},
Spec: allocationv1.GameServerAllocationSpec{
// Enable multi-cluster setting
MultiClusterSetting: allocationv1.MultiClusterSetting{Enabled: true},
Required: metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}},
}}
request := &pb.AllocationRequest{
Namespace: namespaceA,
// Enable multi-cluster setting
MultiClusterSetting: &pb.MultiClusterSetting{Enabled: true},
RequiredGameServerSelector: &metav1.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}},
}

body, err := json.Marshal(gsa)
body, err := json.Marshal(request)
if !assert.Nil(t, err) {
return
}
Expand Down Expand Up @@ -213,12 +207,12 @@ func TestAllocatorCrossNamespace(t *testing.T) {
assert.FailNow(t, "Http test failed")
}

result := allocationv1.GameServerAllocation{}
result := pb.AllocationResponse{}
err = json.Unmarshal(body, &result)
if !assert.Nil(t, err) {
t.Fatalf("failed to unmarshall response body: %s\nerror:%s", string(body), err)
}
assert.Equal(t, allocationv1.GameServerAllocationAllocated, result.Status.State)
assert.Equal(t, pb.AllocationResponse_Allocated, result.State)
}

func createAllocationPolicy(t *testing.T, p *multiclusterv1alpha1.GameServerAllocationPolicy) {
Expand Down

0 comments on commit b22140a

Please sign in to comment.