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 6c4d1fd
Show file tree
Hide file tree
Showing 30 changed files with 2,474 additions and 381 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
83 changes: 62 additions & 21 deletions examples/simple-udp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@
package main

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

"agones.dev/agones/sdks/go"
"encoding/json"
coresdk "agones.dev/agones/pkg/sdk"
"agones.dev/agones/sdks/go"
"strconv"
)

// main starts a UDP server that received 1024 byte sized packets at at time
Expand Down Expand Up @@ -67,23 +68,12 @@ func main() {
func readWriteLoop(conn net.PacketConn, stop chan struct{}, s *sdk.SDK) {
b := make([]byte, 1024)
for {
n, sender, err := conn.ReadFrom(b)
if err != nil {
log.Fatalf("Could not read from udp stream: %v", err)
}

txt := strings.TrimSpace(string(b[:n]))
log.Printf("Received packet from %v: %v", sender.String(), txt)
sender, txt := readPacket(conn, b)
switch txt {

// shuts down the gameserver
case "EXIT":
log.Printf("Received EXIT command. Exiting.")
// This tells Agones to shutdown this Game Server
shutdownErr := s.Shutdown()
if shutdownErr != nil {
log.Printf("Could not shutdown")
}
os.Exit(0)
exit(s)

// turns off the health pings
case "UNHEALTHY":
Expand All @@ -94,14 +84,46 @@ func readWriteLoop(conn net.PacketConn, stop chan struct{}, s *sdk.SDK) {

case "WATCH":
watchGameServerEvents(s)
}

// echo it back
ack := "ACK: " + txt + "\n"
if _, err = conn.WriteTo([]byte(ack), sender); err != nil {
log.Fatalf("Could not write to udp stream: %v", err)
case "LABEL":
setLabel(s)

case "ANNOTATION":
setAnnotation(s)
}

ack(conn, sender, txt)
}
}

// readPacket reads a string from the connection
func readPacket(conn net.PacketConn, b []byte) (net.Addr, string) {
n, sender, err := conn.ReadFrom(b)
if err != nil {
log.Fatalf("Could not read from udp stream: %v", err)
}
txt := strings.TrimSpace(string(b[:n]))
log.Printf("Received packet from %v: %v", sender.String(), txt)
return sender, txt
}

// ack echoes it back, with an ACK
func ack(conn net.PacketConn, sender net.Addr, txt string) {
ack := "ACK: " + txt + "\n"
if _, err := conn.WriteTo([]byte(ack), sender); err != nil {
log.Fatalf("Could not write to udp stream: %v", err)
}
}

// exit shutdowns the server
func exit(s *sdk.SDK) {
log.Printf("Received EXIT command. Exiting.")
// This tells Agones to shutdown this Game Server
shutdownErr := s.Shutdown()
if shutdownErr != nil {
log.Printf("Could not shutdown")
}
os.Exit(0)
}

// writes the GameServer name to the connection UDP stream
Expand Down Expand Up @@ -138,6 +160,25 @@ func watchGameServerEvents(s *sdk.SDK) {
}
}

// setAnnotation sets a given annotation
func setAnnotation(s *sdk.SDK) {
log.Print("Setting annotation")
err := s.SetAnnotation("timestamp", time.Now().UTC().String())
if err != nil {
log.Fatalf("could not set annotation: %v", err)
}
}

// setLabel sets a given label
func setLabel(s *sdk.SDK) {
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)
}
}

// doHealth sends the regular Health Pings
func doHealth(sdk *sdk.SDK, stop <-chan struct{}) {
tick := time.Tick(2 * time.Second)
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
Loading

0 comments on commit 6c4d1fd

Please sign in to comment.