Skip to content

Commit

Permalink
Ability to pass GameServer yaml/json to local sdk server
Browse files Browse the repository at this point in the history
To be able to work locally, you need to be able to specify your
local `GameServer` configuration, as it likely will have application specific
configuration in it -- or maybe you want to specify what state it's in.

This commit allow you to specify the local resource as either yaml/json
through a `-f` or `--file` flag.

Closes #296
  • Loading branch information
markmandel committed Aug 27, 2018
1 parent f8bfe38 commit a486366
Show file tree
Hide file tree
Showing 8 changed files with 343 additions and 171 deletions.
63 changes: 58 additions & 5 deletions cmd/sdk-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,24 @@ import (
"fmt"
"net"
"net/http"
"os"
"path/filepath"
"strings"

"agones.dev/agones/pkg"
"agones.dev/agones/pkg/apis/stable/v1alpha1"
"agones.dev/agones/pkg/client/clientset/versioned"
"agones.dev/agones/pkg/gameservers"
"agones.dev/agones/pkg/sdk"
"agones.dev/agones/pkg/util/runtime"
"agones.dev/agones/pkg/util/signals"
gwruntime "github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/pkg/errors"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"golang.org/x/net/context"
"google.golang.org/grpc"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
Expand All @@ -46,6 +51,7 @@ const (

// Flags (that can also be env vars)
localFlag = "local"
fileFlag = "file"
addressFlag = "address"
)

Expand Down Expand Up @@ -81,7 +87,10 @@ func main() {
defer cancel()

if ctlConf.IsLocal {
sdk.RegisterSDKServer(grpcServer, gameservers.NewLocalSDKServer())
err = registerLocal(grpcServer, ctlConf)
if err != nil {
logger.WithError(err).Fatal("Could not start local sdk server")
}
} else {
var config *rest.Config
config, err = rest.InClusterConfig()
Expand Down Expand Up @@ -124,6 +133,46 @@ func main() {
logger.Info("shutting down sdk server")
}

func registerLocal(grpcServer *grpc.Server, ctlConf config) error {
var local *gameservers.LocalSDKServer
if ctlConf.LocalFile != "" {
path, err := filepath.Abs(ctlConf.LocalFile)
if err != nil {
return err
}

if _, err = os.Stat(path); os.IsNotExist(err) {
return errors.Errorf("Could not find file: %s", path)
}

logger.WithField("path", path).Info("Reading GameServer configuration")
reader, err := os.Open(path) // nolint: gosec
if err != nil {
return err
}

var gs v1alpha1.GameServer
// 4096 is the number of bytes the YAMLOrJSONDecoder goes looking
// into the file to determine if it's JSON or YAML
// (JSON == has whitespace followed by an open brace).
// The Kubernetes uses 4096 bytes as its default, so that's what we'll
// use as well.
// https://github.com/kubernetes/kubernetes/blob/master/plugin/pkg/admission/podnodeselector/admission.go#L86
decoder := yaml.NewYAMLOrJSONDecoder(reader, 4096)
err = decoder.Decode(&gs)
if err != nil {
return err
}
local = gameservers.NewLocalSDKServer(&gs)
} else {
local = gameservers.NewLocalSDKServer(nil)
}

sdk.RegisterSDKServer(grpcServer, local)

return nil
}

// runGrpc runs the grpc service
func runGrpc(grpcServer *grpc.Server, lis net.Listener) {
logger.Info("Starting SDKServer grpc service...")
Expand Down Expand Up @@ -157,9 +206,11 @@ func runGateway(ctx context.Context, grpcEndpoint string, mux *gwruntime.ServeMu
// a configuration structure
func parseEnvFlags() config {
viper.SetDefault(localFlag, false)
viper.SetDefault(fileFlag, "")
viper.SetDefault(addressFlag, "localhost")
pflag.Bool(localFlag, viper.GetBool(localFlag),
"Set this, or LOCAL env, to 'true' to run this binary in local development mode. Defaults to 'false'")
pflag.StringP(fileFlag, "f", viper.GetString(fileFlag), "Set this, or FILE env var to the path of a local yaml or json file that contains your GameServer resoure configuration")
pflag.String(addressFlag, viper.GetString(addressFlag), "The Address to bind the server grpcPort to. Defaults to 'localhost")
pflag.Parse()

Expand All @@ -170,13 +221,15 @@ func parseEnvFlags() config {
runtime.Must(viper.BindPFlags(pflag.CommandLine))

return config{
IsLocal: viper.GetBool(localFlag),
Address: viper.GetString(addressFlag),
IsLocal: viper.GetBool(localFlag),
Address: viper.GetString(addressFlag),
LocalFile: viper.GetString(fileFlag),
}
}

// config is all the configuration for this program
type config struct {
Address string
IsLocal bool
Address string
IsLocal bool
LocalFile string
}
37 changes: 30 additions & 7 deletions pkg/gameservers/localsdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ package gameservers

import (
"io"
"time"
"sync"
"time"

"agones.dev/agones/pkg/apis/stable/v1alpha1"
"agones.dev/agones/pkg/sdk"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
Expand All @@ -28,7 +29,7 @@ import (
var (
_ sdk.SDKServer = &LocalSDKServer{}

fixture = &sdk.GameServer{
defaultGs = &sdk.GameServer{
ObjectMeta: &sdk.GameServer_ObjectMeta{
Name: "local",
Namespace: "default",
Expand All @@ -51,19 +52,25 @@ var (
// is being run for local development, and doesn't connect to the
// Kubernetes cluster
type LocalSDKServer struct {
gs *sdk.GameServer
watchPeriod time.Duration
update chan struct{}
updateObservers sync.Map
}

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

if gs != nil {
l.gs = convert(gs)
}

go func() {
for value := range l.update {
logrus.Info("gameserver update received")
Expand Down Expand Up @@ -107,23 +114,39 @@ 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

if l.gs.ObjectMeta == nil {
l.gs.ObjectMeta = &sdk.GameServer_ObjectMeta{}
}
if l.gs.ObjectMeta.Labels == nil {
l.gs.ObjectMeta.Labels = map[string]string{}
}

l.gs.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

if l.gs.ObjectMeta == nil {
l.gs.ObjectMeta = &sdk.GameServer_ObjectMeta{}
}
if l.gs.ObjectMeta.Annotations == nil {
l.gs.ObjectMeta.Annotations = map[string]string{}
}

l.gs.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")
return fixture, nil
return l.gs, nil
}

// WatchGameServer will return a dummy GameServer (with no changes), 3 times, every 5 seconds
Expand All @@ -150,7 +173,7 @@ func (l *LocalSDKServer) WatchGameServer(_ *sdk.Empty, stream sdk.SDK_WatchGameS
}()

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

0 comments on commit a486366

Please sign in to comment.