-
Notifications
You must be signed in to change notification settings - Fork 817
Commit
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
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,41 @@ 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 what Kubernetes uses | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
markmandel
Author
Member
|
||
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 +201,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 +216,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 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// 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 | ||
|
||
import ( | ||
"agones.dev/agones/pkg/apis/stable/v1alpha1" | ||
"agones.dev/agones/pkg/sdk" | ||
) | ||
|
||
// convert converts a K8s GameServer object, into a gRPC SDK GameServer object | ||
func convert(gs *v1alpha1.GameServer) *sdk.GameServer { | ||
meta := gs.ObjectMeta | ||
status := gs.Status | ||
health := gs.Spec.Health | ||
result := &sdk.GameServer{ | ||
ObjectMeta: &sdk.GameServer_ObjectMeta{ | ||
Name: meta.Name, | ||
Namespace: meta.Namespace, | ||
Uid: string(meta.UID), | ||
ResourceVersion: meta.ResourceVersion, | ||
Generation: meta.Generation, | ||
CreationTimestamp: meta.CreationTimestamp.Unix(), | ||
Annotations: meta.Annotations, | ||
Labels: meta.Labels, | ||
}, | ||
Spec: &sdk.GameServer_Spec{ | ||
Health: &sdk.GameServer_Spec_Health{ | ||
Disabled: health.Disabled, | ||
PeriodSeconds: health.PeriodSeconds, | ||
FailureThreshold: health.FailureThreshold, | ||
InitialDelaySeconds: health.InitialDelaySeconds, | ||
}, | ||
}, | ||
Status: &sdk.GameServer_Status{ | ||
State: string(status.State), | ||
Address: status.Address, | ||
}, | ||
} | ||
if meta.DeletionTimestamp != nil { | ||
result.ObjectMeta.DeletionTimestamp = meta.DeletionTimestamp.Unix() | ||
} | ||
|
||
// loop around and add all the ports | ||
for _, p := range status.Ports { | ||
grpcPort := &sdk.GameServer_Status_Port{ | ||
Name: p.Name, | ||
Port: p.Port, | ||
} | ||
result.Status.Ports = append(result.Status.Ports, grpcPort) | ||
} | ||
|
||
return result | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// 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 | ||
|
||
import ( | ||
"testing" | ||
|
||
"agones.dev/agones/pkg/apis/stable/v1alpha1" | ||
"agones.dev/agones/pkg/sdk" | ||
"github.com/stretchr/testify/assert" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
func TestConvert(t *testing.T) { | ||
t.Parallel() | ||
|
||
fixture := &v1alpha1.GameServer{ | ||
ObjectMeta: v1.ObjectMeta{ | ||
CreationTimestamp: v1.Now(), | ||
Namespace: "default", | ||
Name: "test", | ||
Labels: map[string]string{"foo": "bar"}, | ||
Annotations: map[string]string{"stuff": "things"}, | ||
UID: "1234", | ||
}, | ||
Spec: v1alpha1.GameServerSpec{ | ||
Health: v1alpha1.Health{ | ||
Disabled: false, | ||
InitialDelaySeconds: 10, | ||
FailureThreshold: 15, | ||
PeriodSeconds: 20, | ||
}, | ||
}, | ||
Status: v1alpha1.GameServerStatus{ | ||
NodeName: "george", | ||
Address: "127.0.0.1", | ||
State: "Ready", | ||
Ports: []v1alpha1.GameServerStatusPort{ | ||
{Name: "default", Port: 12345}, | ||
{Name: "beacon", Port: 123123}, | ||
}, | ||
}, | ||
} | ||
|
||
eq := func(t *testing.T, fixture *v1alpha1.GameServer, sdkGs *sdk.GameServer) { | ||
assert.Equal(t, fixture.ObjectMeta.Name, sdkGs.ObjectMeta.Name) | ||
assert.Equal(t, fixture.ObjectMeta.Namespace, sdkGs.ObjectMeta.Namespace) | ||
assert.Equal(t, fixture.ObjectMeta.CreationTimestamp.Unix(), sdkGs.ObjectMeta.CreationTimestamp) | ||
assert.Equal(t, string(fixture.ObjectMeta.UID), sdkGs.ObjectMeta.Uid) | ||
assert.Equal(t, fixture.ObjectMeta.Labels, sdkGs.ObjectMeta.Labels) | ||
assert.Equal(t, fixture.ObjectMeta.Annotations, sdkGs.ObjectMeta.Annotations) | ||
assert.Equal(t, fixture.Spec.Health.Disabled, sdkGs.Spec.Health.Disabled) | ||
assert.Equal(t, fixture.Spec.Health.InitialDelaySeconds, sdkGs.Spec.Health.InitialDelaySeconds) | ||
assert.Equal(t, fixture.Spec.Health.FailureThreshold, sdkGs.Spec.Health.FailureThreshold) | ||
assert.Equal(t, fixture.Spec.Health.PeriodSeconds, sdkGs.Spec.Health.PeriodSeconds) | ||
assert.Equal(t, fixture.Status.Address, sdkGs.Status.Address) | ||
assert.Equal(t, string(fixture.Status.State), sdkGs.Status.State) | ||
assert.Len(t, sdkGs.Status.Ports, len(fixture.Status.Ports)) | ||
for i, fp := range fixture.Status.Ports { | ||
p := sdkGs.Status.Ports[i] | ||
assert.Equal(t, fp.Name, p.Name) | ||
assert.Equal(t, fp.Port, p.Port) | ||
} | ||
} | ||
|
||
sdkGs := convert(fixture) | ||
eq(t, fixture, sdkGs) | ||
assert.Zero(t, sdkGs.ObjectMeta.DeletionTimestamp) | ||
|
||
now := v1.Now() | ||
fixture.DeletionTimestamp = &now | ||
sdkGs = convert(fixture) | ||
eq(t, fixture, sdkGs) | ||
assert.Equal(t, fixture.ObjectMeta.DeletionTimestamp.Unix(), sdkGs.ObjectMeta.DeletionTimestamp) | ||
} |
4 comments
on commit e784314
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am also not sure about something - are changes to the input file watched and applied? 🤔
Or is there some way to emulate an allocation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This version doesn't have the watch and apply (yet) I just ran out of time. We still have that tracked in #314
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now you could pass in a gameserver.yaml file that already is set to state Allocated
-- not quite ideal, but a step in the right direction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly worth noting that #323 has a good chunk of the machinery that will make #314 possible, since that is setup so that changes to labels and annotations will fire WatchGameServer
events when in local mode - just like it would when running on a k8s cluster. So need to wait until that gets merged before we can do #314
I dont know how go works, but for c++ I would ask to put this into a constant somewhere, or even better import an existing constant from Kubernetes (If it exists)