From 92de6a73272d853f715ad626003119e54dac8093 Mon Sep 17 00:00:00 2001 From: Mark Mandel Date: Wed, 1 Apr 2020 14:41:41 -0700 Subject: [PATCH] Local implementation of Set/GetPlayerCapacity Local SDK implementations of SetPlayerCapacity and GetPlayerCapacity, including conformance tests for the Go SDK. Work on #1033 --- build/includes/sdk.mk | 15 ++++++++--- pkg/sdkserver/localsdk.go | 43 +++++++++++++++++++++++++++--- pkg/sdkserver/localsdk_test.go | 48 +++++++++++++++++++++++++++++++++- test/sdk/go/sdk-client-test.go | 28 ++++++++++++++++++++ 4 files changed, 125 insertions(+), 9 deletions(-) diff --git a/build/includes/sdk.mk b/build/includes/sdk.mk index b4c1405355..ccacb30964 100644 --- a/build/includes/sdk.mk +++ b/build/includes/sdk.mk @@ -33,6 +33,9 @@ examples_folder = ../examples/ SDK_FOLDER ?= go COMMAND ?= gen SDK_IMAGE_TAG=$(build_sdk_prefix)$(SDK_FOLDER):$(build_sdk_version) +DEFAULT_CONFORMANCE_TESTS = ready,allocate,setlabel,setannotation,gameserver,health,shutdown,watch,reserve +ALPHA_CONFORMANCE_TESTS = getplayercapacity,setplayercapacity + .PHONY: test-sdks test-sdk build-sdks build-sdk gen-all-sdk-grpc gen-sdk-grpc run-all-sdk-command run-sdk-command build-example @@ -133,13 +136,14 @@ run-sdk-conformance-local: ensure-agones-sdk-image run-sdk-conformance-no-build: TIMEOUT ?= 30 run-sdk-conformance-no-build: RANDOM := $(shell bash -c 'echo $$RANDOM') run-sdk-conformance-no-build: DELAY ?= $(shell bash -c "echo $$[ ($(RANDOM) % 5 ) + 1 ]") -run-sdk-conformance-no-build: TESTS ?= ready,allocate,setlabel,setannotation,gameserver,health,shutdown,watch,reserve +run-sdk-conformance-no-build: TESTS ?= $(DEFAULT_CONFORMANCE_TESTS) run-sdk-conformance-no-build: GRPC_PORT ?= 9357 run-sdk-conformance-no-build: HTTP_PORT ?= 9358 +run-sdk-conformance-no-build: FEATURE_GATES ?= run-sdk-conformance-no-build: ensure-agones-sdk-image run-sdk-conformance-no-build: ensure-build-sdk-image - DOCKER_RUN_ARGS="--net host -e AGONES_SDK_GRPC_PORT=$(GRPC_PORT) -e AGONES_SDK_HTTP_PORT=$(HTTP_PORT) $(DOCKER_RUN_ARGS)" COMMAND=sdktest $(MAKE) run-sdk-command & \ - docker run -p $(GRPC_PORT):$(GRPC_PORT) -p $(HTTP_PORT):$(HTTP_PORT) -e "ADDRESS=" -e "TEST=$(TESTS)" -e "TIMEOUT=$(TIMEOUT)" -e "DELAY=$(DELAY)" \ + DOCKER_RUN_ARGS="--net host -e AGONES_SDK_GRPC_PORT=$(GRPC_PORT) -e AGONES_SDK_HTTP_PORT=$(HTTP_PORT) -e FEATURE_GATES=$(FEATURE_GATES) $(DOCKER_RUN_ARGS)" COMMAND=sdktest $(MAKE) run-sdk-command & \ + docker run -p $(GRPC_PORT):$(GRPC_PORT) -p $(HTTP_PORT):$(HTTP_PORT) -e "FEATURE_GATES=$(FEATURE_GATES)" -e "ADDRESS=" -e "TEST=$(TESTS)" -e "TIMEOUT=$(TIMEOUT)" -e "DELAY=$(DELAY)" \ --net=host $(sidecar_tag) --grpc-port $(GRPC_PORT) --http-port $(HTTP_PORT) # Run SDK conformance test for a specific SDK_FOLDER @@ -155,7 +159,10 @@ run-sdk-conformance-test-node: $(MAKE) run-sdk-conformance-test SDK_FOLDER=node GRPC_PORT=9002 HTTP_PORT=9102 run-sdk-conformance-test-go: - $(MAKE) run-sdk-conformance-test SDK_FOLDER=go GRPC_PORT=9001 HTTP_PORT=9101 + # run without feature flags + $(MAKE) run-sdk-conformance-test SDK_FOLDER=go GRPC_PORT=9001 HTTP_PORT=9101 + # run with feature flags enabled + $(MAKE) run-sdk-conformance-no-build SDK_FOLDER=go GRPC_PORT=9001 HTTP_PORT=9101 FEATURE_GATES=PlayerTracking=true TESTS=$(DEFAULT_CONFORMANCE_TESTS),$(ALPHA_CONFORMANCE_TESTS) run-sdk-conformance-test-rust: $(MAKE) run-sdk-conformance-test SDK_FOLDER=rust diff --git a/pkg/sdkserver/localsdk.go b/pkg/sdkserver/localsdk.go index 818ea53c0e..04c7d11bb5 100644 --- a/pkg/sdkserver/localsdk.go +++ b/pkg/sdkserver/localsdk.go @@ -27,6 +27,7 @@ import ( "agones.dev/agones/pkg/sdk" "agones.dev/agones/pkg/sdk/alpha" "agones.dev/agones/pkg/sdk/beta" + "agones.dev/agones/pkg/util/runtime" "github.com/fsnotify/fsnotify" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -177,6 +178,8 @@ func (l *LocalSDKServer) recordRequestWithValue(request string, value string, ob fieldVal = strconv.FormatInt(l.gs.ObjectMeta.CreationTimestamp, 10) case "UID": fieldVal = l.gs.ObjectMeta.Uid + case "PlayerCapacity": + fieldVal = strconv.FormatInt(l.gs.Status.Players.Capacity, 10) default: fmt.Printf("Error: Unexpected Field to compare") } @@ -375,15 +378,47 @@ func (l *LocalSDKServer) PlayerDisconnect(ctx context.Context, id *alpha.PlayerI // SetPlayerCapacity to change the game server's player capacity. // [Stage:Alpha] // [FeatureFlag:PlayerTesting] -func (l *LocalSDKServer) SetPlayerCapacity(ctx context.Context, count *alpha.Count) (*alpha.Empty, error) { - panic("implement me") +func (l *LocalSDKServer) SetPlayerCapacity(_ context.Context, count *alpha.Count) (*alpha.Empty, error) { + if !runtime.FeatureEnabled(runtime.FeaturePlayerTracking) { + return nil, errors.New(string(runtime.FeaturePlayerTracking) + " not enabled") + } + + logrus.WithField("capacity", count.Count).Info("Setting Player Capacity") + l.gsMutex.Lock() + defer l.gsMutex.Unlock() + + if l.gs.Status.Players == nil { + l.gs.Status.Players = &sdk.GameServer_Status_PlayerStatus{} + } + + l.gs.Status.Players.Capacity = count.Count + + l.update <- struct{}{} + l.recordRequestWithValue("setplayercapacity", strconv.FormatInt(count.Count, 10), "PlayerCapacity") + return &alpha.Empty{}, nil } // GetPlayerCapacity returns the current player capacity. // [Stage:Alpha] // [FeatureFlag:PlayerTesting] -func (l *LocalSDKServer) GetPlayerCapacity(ctx context.Context, _ *alpha.Empty) (*alpha.Count, error) { - panic("implement me") +func (l *LocalSDKServer) GetPlayerCapacity(_ context.Context, _ *alpha.Empty) (*alpha.Count, error) { + if !runtime.FeatureEnabled(runtime.FeaturePlayerTracking) { + return nil, errors.New(string(runtime.FeaturePlayerTracking) + " not enabled") + } + logrus.Info("Getting Player Capacity") + l.recordRequest("getplayercapacity") + l.gsMutex.RLock() + defer l.gsMutex.RUnlock() + + // SDK.GetPlayerCapacity() has a contract of always return a number, + // so if we're nil, then let's always return a value, and + // remove lots of special cases upstream. + result := &alpha.Count{} + if l.gs.Status.Players != nil { + result.Count = l.gs.Status.Players.Capacity + } + + return result, nil } // GetPlayerCount returns the current player count. diff --git a/pkg/sdkserver/localsdk_test.go b/pkg/sdkserver/localsdk_test.go index ec359a048b..4932b112e0 100644 --- a/pkg/sdkserver/localsdk_test.go +++ b/pkg/sdkserver/localsdk_test.go @@ -15,6 +15,7 @@ package sdkserver import ( + "context" "encoding/json" "io/ioutil" "os" @@ -24,8 +25,9 @@ import ( agonesv1 "agones.dev/agones/pkg/apis/agones/v1" "agones.dev/agones/pkg/sdk" + "agones.dev/agones/pkg/sdk/alpha" + "agones.dev/agones/pkg/util/runtime" "github.com/stretchr/testify/assert" - "golang.org/x/net/context" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" ) @@ -275,6 +277,50 @@ func TestLocalSDKServerWatchGameServer(t *testing.T) { } } +func TestLocalSDKServerPlayerCapacity(t *testing.T) { + t.Parallel() + + runtime.FeatureTestMutex.Lock() + defer runtime.FeatureTestMutex.Unlock() + assert.NoError(t, runtime.ParseFeatures(string(runtime.FeaturePlayerTracking)+"=true")) + + fixture := &agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "stuff"}} + + e := &alpha.Empty{} + path, err := gsToTmpFile(fixture) + assert.NoError(t, err) + l, err := NewLocalSDKServer(path) + assert.Nil(t, err) + + stream := newGameServerMockStream() + go func() { + err := l.WatchGameServer(&sdk.Empty{}, stream) + assert.Nil(t, err) + }() + + c, err := l.GetPlayerCapacity(context.Background(), e) + assert.NoError(t, err) + assert.Equal(t, int64(0), c.Count) + + _, err = l.SetPlayerCapacity(context.Background(), &alpha.Count{Count: 10}) + assert.NoError(t, err) + + select { + case msg := <-stream.msgs: + assert.Equal(t, int64(10), msg.Status.Players.Capacity) + case <-time.After(10 * time.Second): + assert.Fail(t, "timeout getting watch") + } + + c, err = l.GetPlayerCapacity(context.Background(), e) + assert.NoError(t, err) + assert.Equal(t, int64(10), c.Count) + + gs, err := l.GetGameServer(context.Background(), &sdk.Empty{}) + assert.NoError(t, err) + assert.Equal(t, int64(10), gs.Status.Players.Capacity) +} + func gsToTmpFile(gs *agonesv1.GameServer) (string, error) { file, err := ioutil.TempFile(os.TempDir(), "gameserver-") if err != nil { diff --git a/test/sdk/go/sdk-client-test.go b/test/sdk/go/sdk-client-test.go index 8faae3abbe..755a7ea051 100644 --- a/test/sdk/go/sdk-client-test.go +++ b/test/sdk/go/sdk-client-test.go @@ -17,15 +17,27 @@ package main import ( "log" "strconv" + "strings" "time" pkgSdk "agones.dev/agones/pkg/sdk" + "agones.dev/agones/pkg/util/runtime" goSdk "agones.dev/agones/sdks/go" + "github.com/spf13/pflag" + "github.com/spf13/viper" ) func main() { + viper.AllowEmptyEnv(true) + runtime.FeaturesBindFlags() + pflag.Parse() + viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) + runtime.Must(runtime.FeaturesBindEnv()) + runtime.Must(runtime.ParseFeaturesFromEnv()) + log.SetFlags(log.Lshortfile) log.Println("Client is starting") + log.Printf("Feature Flags: %s\n", runtime.EncodeFeatures()) time.Sleep(100 * time.Millisecond) sdk, err := goSdk.NewSDK() if err != nil { @@ -80,6 +92,22 @@ func main() { if err != nil { log.Fatalf("Could not set annotation: %s", err) } + + if runtime.FeatureEnabled(runtime.FeaturePlayerTracking) { + capacity := int64(10) + if err = sdk.Alpha().SetPlayerCapacity(capacity); err != nil { + log.Fatalf("Error setting player capacity: %s", err) + } + + c, err := sdk.Alpha().GetPlayerCapacity() + if err != nil { + log.Fatalf("Error getting player capacity: %s", err) + } + if c != capacity { + log.Fatalf("Player Capacity should be %d, but is %d", capacity, c) + } + } + err = sdk.Shutdown() if err != nil { log.Fatalf("Could not shutdown GameServer: %s", err)