Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement SDK SetLabel and SetAnnotation functionality #323

Merged
merged 1 commit into from
Aug 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
4 changes: 2 additions & 2 deletions 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 Expand Up @@ -119,7 +119,7 @@ test-go:
# Runs end-to-end tests on the current configured cluster
# For minikube user the minikube-test-e2e targets
test-e2e: $(ensure-build-image)
$(DOCKER_RUN) go test -v $(agones_package)/test/e2e/... \
$(DOCKER_RUN) go test -v -race $(agones_package)/test/e2e/... \
--kubeconfig /root/.kube/config \
--gameserver-image=$(GS_TEST_IMAGE)

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