Permalink
Browse files

Ability to pass `GameServer` yaml/json to local sdk server

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 25, 2018
1 parent f8bfe38 commit 383bd224065f6d1e6ce3699b47b7e9822beed555
View
@@ -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"
)
@@ -46,6 +51,7 @@ const (
// Flags (that can also be env vars)
localFlag = "local"
fileFlag = "file"
addressFlag = "address"
)
@@ -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()
@@ -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...")
@@ -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()
@@ -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
}
@@ -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"
@@ -28,7 +29,7 @@ import (
var (
_ sdk.SDKServer = &LocalSDKServer{}
fixture = &sdk.GameServer{
defaultGs = &sdk.GameServer{
ObjectMeta: &sdk.GameServer_ObjectMeta{
Name: "local",
Namespace: "default",
@@ -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")
@@ -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
@@ -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
Oops, something went wrong.

0 comments on commit 383bd22

Please sign in to comment.