Skip to content

Commit

Permalink
Implement SDK SetLabel and SetAnnotation functionality
Browse files Browse the repository at this point in the history
This implements new functions in the SDK:

- SetLabel(key, value) - that lets you set a label on the backing `GameServer`
- SetAnnotation(key, value) - that lets you set an annotation on the backing
`GameServer`

All keys are prefixed with "stable.agones.dev/sdk-" to maintain isolation.

Closes #279
  • Loading branch information
markmandel committed Aug 22, 2018
1 parent 32388aa commit 3dc0654
Show file tree
Hide file tree
Showing 29 changed files with 2,428 additions and 362 deletions.
38 changes: 38 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Copyright 2018 Google Inc. 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.

# project specific
.*
.idea
*.zip
/release
bin
/docs
*.md
*.amd64

# Created by .ignore support plugin (hsz.mobi)
### Go template
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, build with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
!.gitignore
!.helmignore
!.gitattributes
!.dockerignore
*.iml
bin
*.o
Expand Down
2 changes: 1 addition & 1 deletion build/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ GCP_CLUSTER_ZONE ?= us-west1-c
MINIKUBE_PROFILE ?= agones

# Game Server image to use while doing end-to-end tests
GS_TEST_IMAGE ?= gcr.io/agones-images/udp-server:0.1
GS_TEST_IMAGE ?= gcr.io/agones-images/udp-server:0.3

# Directory that this Makefile is in.
mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
Expand Down
22 changes: 22 additions & 0 deletions docs/sdk_rest_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,28 @@ Call when the GameServer session is over and it's time to shut down
$ curl -d "{}" -H "Content-Type: application/json" -X POST http://localhost:59358/shutdown
```

### Set Label

Apply a Label with the prefix "stable.agones.dev/sdk-" to the backing `GameServer` metadata.

See the SDK [SetLabel](../sdks/README.md#setlabelkey-value) documentation for restrictions.

#### Example

```bash
$ curl -d '{"key": "foo", "value": "bar"}' -H "Content-Type: application/json" -X PUT http://localhost:59358/metadata/label
```

### Set Annotation

Apply a Annotation with the prefix "stable.agones.dev/sdk-" to the backing `GameServer` metadata

#### Example

```bash
$ curl -d '{"key": "foo", "value": "bar"}' -H "Content-Type: application/json" -X PUT http://localhost:59358/metadata/annotation
```

### GameServer

Call when you want to retrieve the backing `GameServer` configuration details
Expand Down
16 changes: 15 additions & 1 deletion examples/cpp-simple/server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,22 @@ int main() {
std::thread health (doHealth, sdk);
std::thread watch (watchUpdates, sdk);

std::cout << "Setting a label" << std::endl;
grpc::Status status = sdk->SetLabel("test-label", "test-value");
if (!status.ok()) {
std::cout << "Could not run SetLabel(): "+ status.error_message() + ". Exiting!" << std::endl;
return -1;
}

std::cout << "Setting an annotation" << std::endl;
status = sdk->SetAnnotation("test-annotation", "test value");
if (!status.ok()) {
std::cout << "Could not run SetAnnotation(): "+ status.error_message() + ". Exiting!" << std::endl;
return -1;
}

std::cout << "Marking server as ready..." << std::endl;
grpc::Status status = sdk->Ready();
status = sdk->Ready();
if (!status.ok()) {
std::cout << "Could not run Ready(): "+ status.error_message() + ". Exiting!" << std::endl;
return -1;
Expand Down
21 changes: 19 additions & 2 deletions examples/simple-udp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,18 @@
package main

import (
"encoding/json"
"flag"
"log"
"net"
"os"
"strings"
"time"

"agones.dev/agones/sdks/go"
"encoding/json"
"strconv"

coresdk "agones.dev/agones/pkg/sdk"
"agones.dev/agones/sdks/go"
)

// main starts a UDP server that received 1024 byte sized packets at at time
Expand Down Expand Up @@ -94,6 +96,21 @@ func readWriteLoop(conn net.PacketConn, stop chan struct{}, s *sdk.SDK) {

case "WATCH":
watchGameServerEvents(s)

case "LABEL":
log.Print("Setting label")
// label values can only be alpha, - and .
err := s.SetLabel("timestamp", strconv.FormatInt(time.Now().Unix(), 10))
if err != nil {
log.Fatalf("could not set label: %v", err)
}

case "ANNOTATION":
log.Print("Setting annotation")
err := s.SetAnnotation("timestamp", time.Now().UTC().String())
if err != nil {
log.Fatalf("could not set annotation: %v", err)
}
}

// echo it back
Expand Down
64 changes: 56 additions & 8 deletions pkg/gameservers/localsdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package gameservers
import (
"io"
"time"
"sync"

"agones.dev/agones/pkg/sdk"
"github.com/pkg/errors"
Expand Down Expand Up @@ -50,14 +51,30 @@ var (
// is being run for local development, and doesn't connect to the
// Kubernetes cluster
type LocalSDKServer struct {
watchPeriod time.Duration
watchPeriod time.Duration
update chan struct{}
updateObservers sync.Map
}

// NewLocalSDKServer returns the default LocalSDKServer
func NewLocalSDKServer() *LocalSDKServer {
return &LocalSDKServer{
watchPeriod: 5 * time.Second,
l := &LocalSDKServer{
watchPeriod: 5 * time.Second,
update: make(chan struct{}),
updateObservers: sync.Map{},
}

go func() {
for value := range l.update {
logrus.Info("gameserver update received")
l.updateObservers.Range(func(observer, _ interface{}) bool {
observer.(chan struct{}) <- value
return true
})
}
}()

return l
}

// Ready logs that the Ready request has been received
Expand Down Expand Up @@ -87,6 +104,22 @@ func (l *LocalSDKServer) Health(stream sdk.SDK_HealthServer) error {
}
}

// SetLabel applies a Label to the backing GameServer metadata
func (l *LocalSDKServer) SetLabel(_ context.Context, kv *sdk.KeyValue) (*sdk.Empty, error) {
logrus.WithField("values", kv).Info("Setting label")
fixture.ObjectMeta.Labels[metadataPrefix+kv.Key] = kv.Value
l.update <- struct{}{}
return &sdk.Empty{}, nil
}

// SetAnnotation applies a Annotation to the backing GameServer metadata
func (l *LocalSDKServer) SetAnnotation(_ context.Context, kv *sdk.KeyValue) (*sdk.Empty, error) {
logrus.WithField("values", kv).Info("Setting annotation")
fixture.ObjectMeta.Annotations[metadataPrefix+kv.Key] = kv.Value
l.update <- struct{}{}
return &sdk.Empty{}, nil
}

// GetGameServer returns a dummy game server.
func (l *LocalSDKServer) GetGameServer(context.Context, *sdk.Empty) (*sdk.GameServer, error) {
logrus.Info("getting GameServer details")
Expand All @@ -96,17 +129,32 @@ func (l *LocalSDKServer) GetGameServer(context.Context, *sdk.Empty) (*sdk.GameSe
// WatchGameServer will return a dummy GameServer (with no changes), 3 times, every 5 seconds
func (l *LocalSDKServer) WatchGameServer(_ *sdk.Empty, stream sdk.SDK_WatchGameServerServer) error {
logrus.Info("connected to watch GameServer...")
times := 3
observer := make(chan struct{})

defer func() {
l.updateObservers.Delete(observer)
close(observer)
}()

l.updateObservers.Store(observer, true)

// on connect, send 3 events, as advertised
go func() {
times := 3

for i := 0; i < times; i++ {
logrus.Info("Sending watched GameServer!")
for i := 0; i < times; i++ {
logrus.Info("Sending watched GameServer!")
l.update <- struct{}{}
time.Sleep(l.watchPeriod)
}
}()

for range observer {
err := stream.Send(fixture)
if err != nil {
logrus.WithError(err).Error("error sending gameserver")
return err
}

time.Sleep(l.watchPeriod)
}

return nil
Expand Down
62 changes: 59 additions & 3 deletions pkg/gameservers/localsdk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,60 @@ func TestLocal(t *testing.T) {
assert.Equal(t, fixture, gs)
}

func TestLocalSDKServerSetLabel(t *testing.T) {
ctx := context.Background()
e := &sdk.Empty{}
l := NewLocalSDKServer()
kv := &sdk.KeyValue{Key: "foo", Value: "bar"}

stream := newGameServerMockStream()
go func() {
err := l.WatchGameServer(e, stream)
assert.Nil(t, err)
}()

_, err := l.SetLabel(ctx, kv)
assert.Nil(t, err)

gs, err := l.GetGameServer(ctx, e)
assert.Nil(t, err)
assert.Equal(t, gs.ObjectMeta.Labels[metadataPrefix+"foo"], "bar")

select {
case msg := <-stream.msgs:
assert.Equal(t, msg.ObjectMeta.Labels[metadataPrefix+"foo"], "bar")
case <-time.After(2 * l.watchPeriod):
assert.FailNow(t, "timeout on receiving messages")
}
}

func TestLocalSDKServerSetAnnotation(t *testing.T) {
ctx := context.Background()
e := &sdk.Empty{}
l := NewLocalSDKServer()
kv := &sdk.KeyValue{Key: "bar", Value: "foo"}

stream := newGameServerMockStream()
go func() {
err := l.WatchGameServer(e, stream)
assert.Nil(t, err)
}()

_, err := l.SetAnnotation(ctx, kv)
assert.Nil(t, err)

gs, err := l.GetGameServer(ctx, e)
assert.Nil(t, err)
assert.Equal(t, gs.ObjectMeta.Annotations[metadataPrefix+"bar"], "foo")

select {
case msg := <-stream.msgs:
assert.Equal(t, msg.ObjectMeta.Annotations[metadataPrefix+"bar"], "foo")
case <-time.After(2 * l.watchPeriod):
assert.FailNow(t, "timeout on receiving messages")
}
}

func TestLocalSDKServerWatchGameServer(t *testing.T) {
t.Parallel()

Expand All @@ -64,15 +118,17 @@ func TestLocalSDKServerWatchGameServer(t *testing.T) {
l.watchPeriod = time.Second

stream := newGameServerMockStream()
err := l.WatchGameServer(e, stream)
assert.Nil(t, err)
go func() {
err := l.WatchGameServer(e, stream)
assert.Nil(t, err)
}()

for i := 0; i < 3; i++ {
select {
case msg := <-stream.msgs:
assert.Equal(t, fixture, msg)
case <-time.After(2 * l.watchPeriod):
assert.FailNow(t, "timeout on receiving messagess")
assert.FailNow(t, "timeout on receiving messages")
}
}
}
20 changes: 20 additions & 0 deletions pkg/gameservers/sdk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2018 Google Inc. 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 gameservers

const (
// metadataPrefix prefix for labels and annotations
metadataPrefix = "stable.agones.dev/sdk-"
)
Loading

0 comments on commit 3dc0654

Please sign in to comment.