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

More Local Dev Server Support #3252

Merged
merged 24 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2203efb
More Local Dev Server Support
CauhxMilloy Jul 8, 2023
a04cfd2
Merge remote-tracking branch 'remotes/original_origin/main' into more…
CauhxMilloy Jul 8, 2023
9d2be3b
* Update documentation clarification location... -ation.
CauhxMilloy Jul 8, 2023
df14a37
* Fixing formatting (had 1 too many spaces).
CauhxMilloy Jul 8, 2023
559901f
* Fixing ref relative paths.
CauhxMilloy Jul 9, 2023
d7020c1
* Removing quotes missed in previous cleanups.
CauhxMilloy Jul 9, 2023
1c83925
* As per review feedback, updating `GAMESERVER_NAME` and `POD_NAMESPA…
CauhxMilloy Jul 12, 2023
8ace56c
* As per review feedback, changing from negative to positive logic fo…
CauhxMilloy Jul 12, 2023
6c070c4
* Updating links to use `ref` instead of absolute site link.
CauhxMilloy Jul 12, 2023
125bfde
* Switching "out of cluster" command example to use binary instead of…
CauhxMilloy Jul 12, 2023
60bc8d9
Merge remote-tracking branch 'remotes/original_origin/main' into more…
CauhxMilloy Jul 12, 2023
060e3c8
* Adding controller test for RequestReady -> Ready state progression.
CauhxMilloy Jul 15, 2023
62cb705
* Adding `RequestReady` -> `Ready` state progressing in `TestDevelopm…
CauhxMilloy Jul 17, 2023
597ecf0
* Fixing call to `StartInformers()` in test.
CauhxMilloy Jul 22, 2023
7a6bfb5
* Moving discussion of running from source code down to the bottom.
CauhxMilloy Jul 22, 2023
b0bc52f
* Adding more cross-reference links for game server allocation.
CauhxMilloy Jul 22, 2023
de032f7
* Moving "out of cluster" documentation into its own file.
CauhxMilloy Jul 22, 2023
3a6036a
* Switching OS/file description back to bulleted form similar to how …
CauhxMilloy Jul 22, 2023
6347a99
Merge remote-tracking branch 'remotes/original_origin/main' into more…
CauhxMilloy Jul 22, 2023
d94cfe5
* Removing link to moved section (covered by "Next Steps" section).
CauhxMilloy Jul 23, 2023
608f415
* Cleaning up some wording.
CauhxMilloy Jul 23, 2023
af34674
* Adding comments about restarting locally run binaries.
CauhxMilloy Jul 23, 2023
fddea9d
* Proofread and update.
CauhxMilloy Jul 23, 2023
6dba945
Merge remote-tracking branch 'remotes/original_origin/main' into more…
CauhxMilloy Jul 25, 2023
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
96 changes: 59 additions & 37 deletions cmd/sdk-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,27 @@ import (
"google.golang.org/grpc/credentials/insecure"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)

const (
defaultGRPCPort = 9357
defaultHTTPPort = 9358

// specifically env vars
gameServerNameEnv = "GAMESERVER_NAME"
podNamespaceEnv = "POD_NAMESPACE"

// Flags (that can also be env vars)
localFlag = "local"
fileFlag = "file"
testFlag = "test"
testSdkNameFlag = "sdk-name"
addressFlag = "address"
delayFlag = "delay"
timeoutFlag = "timeout"
grpcPortFlag = "grpc-port"
httpPortFlag = "http-port"
gameServerNameFlag = "gameserver-name"
podNamespaceFlag = "pod-namespace"
localFlag = "local"
fileFlag = "file"
testFlag = "test"
testSdkNameFlag = "sdk-name"
kubeconfigFlag = "kubeconfig"
gracefulTerminationFlag = "graceful-termination"
addressFlag = "address"
delayFlag = "delay"
timeoutFlag = "timeout"
grpcPortFlag = "grpc-port"
httpPortFlag = "http-port"
)

var (
Expand Down Expand Up @@ -118,7 +119,8 @@ func main() {
}
default:
var config *rest.Config
config, err := rest.InClusterConfig()
// if the kubeconfig fails BuildConfigFromFlags will try in cluster config
config, err := clientcmd.BuildConfigFromFlags("", ctlConf.KubeConfig)
if err != nil {
logger.WithError(err).Fatal("Could not create in cluster config")
}
Expand All @@ -136,8 +138,8 @@ func main() {
}

var s *sdkserver.SDKServer
s, err = sdkserver.NewSDKServer(viper.GetString(gameServerNameEnv),
viper.GetString(podNamespaceEnv), kubeClient, agonesClient)
s, err = sdkserver.NewSDKServer(ctlConf.GameServerName, ctlConf.PodNamespace,
kubeClient, agonesClient)
if err != nil {
logger.WithError(err).Fatalf("Could not start sidecar")
}
Expand All @@ -146,7 +148,9 @@ func main() {
if err := s.WaitForConnection(ctx); err != nil {
logger.WithError(err).Fatalf("Sidecar networking failure")
}
ctx = s.NewSDKServerContext(ctx)
if ctlConf.GracefulTermination {
ctx = s.NewSDKServerContext(ctx)
}
go func() {
err := s.Run(ctx)
if err != nil {
Expand Down Expand Up @@ -265,8 +269,13 @@ func parseEnvFlags() config {
viper.SetDefault(addressFlag, "localhost")
viper.SetDefault(delayFlag, 0)
viper.SetDefault(timeoutFlag, 0)
viper.SetDefault(gracefulTerminationFlag, true)
viper.SetDefault(grpcPortFlag, defaultGRPCPort)
viper.SetDefault(httpPortFlag, defaultHTTPPort)
pflag.String(gameServerNameFlag, viper.GetString(gameServerNameFlag),
"Optional flag to set GameServer name. Overrides value given from `GAMESERVER_NAME` environment variable.")
pflag.String(podNamespaceFlag, viper.GetString(gameServerNameFlag),
"Optional flag to set Kubernetes namespace which the GameServer/pod is in. Overrides value given from `POD_NAMESPACE` environment variable.")
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")
Expand All @@ -277,17 +286,22 @@ func parseEnvFlags() config {
pflag.Int(timeoutFlag, viper.GetInt(timeoutFlag), "Time of execution (in seconds) before close. Useful for tests")
pflag.String(testFlag, viper.GetString(testFlag), "List functions which should be called during the SDK Conformance test run.")
pflag.String(testSdkNameFlag, viper.GetString(testSdkNameFlag), "SDK name which is tested by this SDK Conformance test.")
pflag.String(kubeconfigFlag, viper.GetString(kubeconfigFlag),
"Optional. kubeconfig to run the SDK server out of the cluster.")
pflag.Bool(gracefulTerminationFlag, viper.GetBool(gracefulTerminationFlag),
"Immediately quits when receiving interrupt instead of waiting for GameServer state to progress to \"Shutdown\".")
runtime.FeaturesBindFlags()
pflag.Parse()

viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
runtime.Must(viper.BindEnv(gameServerNameFlag))
runtime.Must(viper.BindEnv(podNamespaceFlag))
runtime.Must(viper.BindEnv(localFlag))
runtime.Must(viper.BindEnv(fileFlag))
runtime.Must(viper.BindEnv(addressFlag))
runtime.Must(viper.BindEnv(testFlag))
runtime.Must(viper.BindEnv(testSdkNameFlag))
runtime.Must(viper.BindEnv(gameServerNameEnv))
runtime.Must(viper.BindEnv(podNamespaceEnv))
runtime.Must(viper.BindEnv(kubeconfigFlag))
runtime.Must(viper.BindEnv(delayFlag))
runtime.Must(viper.BindEnv(timeoutFlag))
runtime.Must(viper.BindEnv(grpcPortFlag))
Expand All @@ -298,29 +312,37 @@ func parseEnvFlags() config {
runtime.Must(runtime.ParseFeaturesFromEnv())

return config{
IsLocal: viper.GetBool(localFlag),
Address: viper.GetString(addressFlag),
LocalFile: viper.GetString(fileFlag),
Delay: viper.GetInt(delayFlag),
Timeout: viper.GetInt(timeoutFlag),
Test: viper.GetString(testFlag),
TestSdkName: viper.GetString(testSdkNameFlag),
GRPCPort: viper.GetInt(grpcPortFlag),
HTTPPort: viper.GetInt(httpPortFlag),
GameServerName: viper.GetString(gameServerNameFlag),
PodNamespace: viper.GetString(podNamespaceFlag),
IsLocal: viper.GetBool(localFlag),
Address: viper.GetString(addressFlag),
LocalFile: viper.GetString(fileFlag),
Delay: viper.GetInt(delayFlag),
Timeout: viper.GetInt(timeoutFlag),
Test: viper.GetString(testFlag),
TestSdkName: viper.GetString(testSdkNameFlag),
KubeConfig: viper.GetString(kubeconfigFlag),
GracefulTermination: viper.GetBool(gracefulTerminationFlag),
GRPCPort: viper.GetInt(grpcPortFlag),
HTTPPort: viper.GetInt(httpPortFlag),
}
}

// config is all the configuration for this program
type config struct {
Address string
IsLocal bool
LocalFile string
Delay int
Timeout int
Test string
TestSdkName string
GRPCPort int
HTTPPort int
GameServerName string
PodNamespace string
Address string
IsLocal bool
LocalFile string
Delay int
Timeout int
Test string
TestSdkName string
KubeConfig string
GracefulTermination bool
GRPCPort int
HTTPPort int
}

// healthCheckWrapper ensures that an http 400 response is returned
Expand Down
26 changes: 16 additions & 10 deletions pkg/gameservers/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -534,23 +534,29 @@ func (c *Controller) syncDevelopmentGameServer(ctx context.Context, gs *agonesv1
return gs, nil
}

// Only move from Creating -> Ready. Other manual state changes are up to the end user.
// We also don't want to move from Allocated -> Ready every time someone allocates a GameServer.
if gs.Status.State != agonesv1.GameServerStateCreating {
// Only move from Creating -> Ready or RequestReady -> Ready.
// Shutdown -> Delete will still be handled normally by syncGameServerShutdownState.
// Other manual state changes are up to the end user.
if gs.Status.State != agonesv1.GameServerStateCreating && gs.Status.State != agonesv1.GameServerStateRequestReady {
return gs, nil
}

loggerForGameServer(gs, c.baseLogger).Debug("GS is a development game server and will not be managed by Agones.")
gsCopy := gs.DeepCopy()
var ports []agonesv1.GameServerStatusPort
for _, p := range gs.Spec.Ports {
ports = append(ports, p.Status())
}

gsCopy.Status.State = agonesv1.GameServerStateReady
gsCopy.Status.Ports = ports
gsCopy.Status.Address = devIPAddress
gsCopy.Status.NodeName = devIPAddress

if gs.Status.State == agonesv1.GameServerStateCreating {
var ports []agonesv1.GameServerStatusPort
for _, p := range gs.Spec.Ports {
ports = append(ports, p.Status())
}

gsCopy.Status.Ports = ports
gsCopy.Status.Address = devIPAddress
gsCopy.Status.NodeName = devIPAddress
}

gs, err := c.gameServerGetter.GameServers(gs.ObjectMeta.Namespace).Update(ctx, gsCopy, metav1.UpdateOptions{})
if err != nil {
return gs, errors.Wrapf(err, "error updating GameServer %s to %v status", gs.Name, gs.Status)
Expand Down
35 changes: 35 additions & 0 deletions pkg/gameservers/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,41 @@ func TestControllerSyncGameServerWithDevIP(t *testing.T) {
assert.Equal(t, 1, updateCount, "update reactor should fire once")
})

t.Run("GameServer with ReadyRequest State", func(t *testing.T) {
c, mocks := newFakeController()

updateCount := 0

gsFixture := templateDevGs.DeepCopy()
gsFixture.ApplyDefaults()
gsFixture.Status.State = agonesv1.GameServerStateRequestReady


mocks.AgonesClient.AddReactor("list", "gameservers", func(action k8stesting.Action) (bool, runtime.Object, error) {
gameServers := &agonesv1.GameServerList{Items: []agonesv1.GameServer{*gsFixture}}
return true, gameServers, nil
})
mocks.AgonesClient.AddReactor("update", "gameservers", func(action k8stesting.Action) (bool, runtime.Object, error) {
ua := action.(k8stesting.UpdateAction)
gs := ua.GetObject().(*agonesv1.GameServer)
updateCount++

assert.Equal(t, agonesv1.GameServerStateReady, gs.Status.State)

return true, gs, nil
})

ctx, cancel := agtesting.StartInformers(mocks, c.gameServerSynced)
defer cancel()

err := c.portAllocator.Run(ctx)
assert.NoError(t, err, "should not error")

err = c.syncGameServer(ctx, "default/test")
assert.NoError(t, err, "should not error")
assert.Equal(t, 1, updateCount, "update reactor should fire once")
})

t.Run("Allocated GameServer", func(t *testing.T) {
c, mocks := newFakeController()

Expand Down
Loading