From c38e020c9f15e974fd53108e53fa52e1029f0243 Mon Sep 17 00:00:00 2001 From: Daniel Oliveira Date: Wed, 20 Jan 2021 19:09:08 +0100 Subject: [PATCH 1/6] update ignore list --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index bfb5a44..63e08cc 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ vendor/ bin/ .idea +.envrc +.infrastructure \ No newline at end of file From d8229a88df8f971d2dbcdc9d8482a45cb03a01b4 Mon Sep 17 00:00:00 2001 From: Daniel Oliveira Date: Wed, 20 Jan 2021 19:09:21 +0100 Subject: [PATCH 2/6] Support Agones --- cmd/q3/app/server/server.go | 96 ++++++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 29 deletions(-) diff --git a/cmd/q3/app/server/server.go b/cmd/q3/app/server/server.go index 93437c9..4d711c1 100644 --- a/cmd/q3/app/server/server.go +++ b/cmd/q3/app/server/server.go @@ -1,8 +1,10 @@ package server import ( + sdk "agones.dev/agones/sdks/go" "context" "fmt" + quakenet "github.com/criticalstack/quake-kube/internal/quake/net" "log" "net/url" "time" @@ -15,8 +17,6 @@ import ( quakeserver "github.com/criticalstack/quake-kube/internal/quake/server" httputil "github.com/criticalstack/quake-kube/internal/util/net/http" "github.com/criticalstack/quake-kube/public" - - sdk "agones.dev/agones/sdks/go" ) var opts struct { @@ -57,34 +57,9 @@ func NewCommand() *cobra.Command { } if opts.WithAgones { - log.Println("starting Agones SDK client") - s, err := sdk.NewSDK() - if err != nil { - return errors.Wrap(err, "Agones SDK could not be initialized") + if err := startAgones(ctx, opts.ServerAddr); err != nil { + log.Printf("[Agones] Error: %v", err) } - - if err := s.Ready(); err != nil { - log.Println("failed to make the server Ready") - } - - go func() { - tick := time.Tick(2 * time.Second) - maxAttempts := 0 - for { - if err := s.Health(); err != nil { - if maxAttempts > 5 { - log.Fatalf("Could not send health ping: %v", err) - } - maxAttempts++ - } - select { - case <-ctx.Done(): - log.Print("Stopped health pings") - return - case <-tick: - } - } - }() } go func() { @@ -126,3 +101,66 @@ func NewCommand() *cobra.Command { cmd.Flags().BoolVar(&opts.WithAgones, "with-agones", false, "use Agones SDK integration") return cmd } + +func startAgones(ctx context.Context, addr string) error { + log.Println("[Agones] starting Agones SDK client") + s, err := sdk.NewSDK() + if err != nil { + return err + } + + if err := s.Ready(); err != nil { + log.Println("[Agones] failed to make the server Ready") + } + + go func() { + tick := time.Tick(2 * time.Second) + maxAttempts := 0 + for { + if err := s.Health(); err != nil { + if maxAttempts > 5 { + log.Fatalf("[Agones] could not send health ping: %v", err) + } + maxAttempts++ + } else { + maxAttempts = 0 + } + + select { + case <-ctx.Done(): + log.Print("[Agones] stopped health pings") + return + case <-tick: + } + } + }() + + go func() { + tick := time.Tick(5 * time.Second) + for { + if status, err := quakenet.GetStatus(addr); err == nil { + if err != nil { + log.Printf("[Agones] failed to get status from server: %s", err.Error()) + continue + } + for _, p := range status.Players { + log.Printf("[Agones] player: %s", p.Name) + + if _, err := s.Alpha().PlayerConnect(p.Name); err != nil { + log.Printf("[Agones] failed to register player: %s", err.Error()) + } + } + log.Println("[Agones] status checked") + } + + select { + case <-ctx.Done(): + log.Print("[Agones] stopped status checks") + return + case <-tick: + } + } + }() + + return nil +} From e8726c3971daaadbe71084ebc59e29bd62c84449 Mon Sep 17 00:00:00 2001 From: Daniel Oliveira Date: Wed, 20 Jan 2021 19:09:40 +0100 Subject: [PATCH 3/6] Add Fleet manifest example --- examples/fleet.yaml | 128 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 examples/fleet.yaml diff --git a/examples/fleet.yaml b/examples/fleet.yaml new file mode 100644 index 0000000..d82e187 --- /dev/null +++ b/examples/fleet.yaml @@ -0,0 +1,128 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: quake3-server-config +data: + config.yaml: | + fragLimit: 25 + timeLimit: 15m + bot: + minPlayers: 3 + game: + motd: "Welcome to Critical Stack" + type: FreeForAll + forceRespawn: false + inactivity: 10m + quadFactor: 3 + weaponRespawn: 3 + server: + hostname: "quakekube" + maxClients: 12 + password: "changeme" + commands: + - addbot sarge 2 + maps: + - name: q3dm7 + type: FreeForAll + timeLimit: 10m + - name: q3dm17 + type: FreeForAll + - name: q3wctf1 + type: CaptureTheFlag + captureLimit: 8 + - name: q3tourney2 + type: Tournament + - name: q3wctf3 + type: CaptureTheFlag + captureLimit: 8 + - name: ztn3tourney1 + type: Tournament +--- +apiVersion: "agones.dev/v1" +kind: Fleet +metadata: + name: octops + labels: + cluster: gke-1.17 + region: us-east-1 +spec: + replicas: 1 + template: + metadata: + labels: + cluster: gke-1.17 + region: us-east-1 + annotations: + octops.io/gameserver-ingress-domain: "mydomain.com" + octops.io/terminate-tls: "true" + octops.io/issuer-tls-name: "selfsigned-issuer" + spec: + players: + # Set initial player capacity if using PlayerTracking Alpha() + initialCapacity: 100 + container: gameserver + ports: + - name: default + containerPort: 8081 + protocol: TCP + health: + disabled: true + template: + spec: + containers: + - name: gameserver + imagePullPolicy: Always + image: octops/quake:latest + command: + - q3 + - server + - --config=/config/config.yaml + - --content-server=http://127.0.0.1:9090 + - --agree-eula + - --client-addr=0.0.0.0:8081 + - --with-agones + ports: + - containerPort: 8081 + readinessProbe: + tcpSocket: + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 5 + resources: + requests: + memory: "1Gi" + cpu: "0.5" + limits: + memory: "2Gi" + cpu: "1" + volumeMounts: + - name: quake3-server-config + mountPath: /config + - name: quake3-content + mountPath: /assets + - name: content-server + imagePullPolicy: Always + image: octops/quake:latest + command: + - q3 + - content + - --seed-content-url=http://content.quakejs.com + ports: + - containerPort: 9090 + resources: + requests: + memory: "1Gi" + cpu: "0.5" + limits: + memory: "2Gi" + cpu: "1" + volumeMounts: + - name: quake3-content + mountPath: /assets + volumes: + - name: quake3-server-config + configMap: + name: quake3-server-config + - name: quake3-content + emptyDir: {} \ No newline at end of file From 8d51e7901bab67fb479504628ea1e979718fb5ba Mon Sep 17 00:00:00 2001 From: Daniel Oliveira Date: Wed, 20 Jan 2021 20:06:29 +0100 Subject: [PATCH 4/6] Add Agones extension --- pkg/extensions/agones_extension.go | 92 ++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 pkg/extensions/agones_extension.go diff --git a/pkg/extensions/agones_extension.go b/pkg/extensions/agones_extension.go new file mode 100644 index 0000000..91efc06 --- /dev/null +++ b/pkg/extensions/agones_extension.go @@ -0,0 +1,92 @@ +package extensions + +import ( + sdk "agones.dev/agones/sdks/go" + "context" + quakenet "github.com/criticalstack/quake-kube/internal/quake/net" + quakeserver "github.com/criticalstack/quake-kube/internal/quake/server" + "github.com/pkg/errors" + "log" + "time" +) + +type Agones struct { + Server *quakeserver.Server + sdk *sdk.SDK +} + +func (a *Agones) Start(ctx context.Context, server *quakeserver.Server) error { + if err := a.InitSdk(); err != nil { + return err + } + a.Server = server + + go a.StartHeathCheck(ctx) + go a.TrackStatus(ctx) + + if err := server.Start(ctx); err != nil { + return errors.Wrap(err, "failed to start server") + } + + return nil +} + +func (a *Agones) InitSdk() error { + log.Println("[Agones] starting Agones SDK client") + s, err := sdk.NewSDK() + if err != nil { + return err + } + + a.sdk = s + + return nil +} + +func (a *Agones) StartHeathCheck(ctx context.Context) { + tick := time.Tick(2 * time.Second) + maxAttempts := 0 + for { + if err := a.sdk.Health(); err != nil { + if maxAttempts > 5 { + log.Fatalf("[Agones] could not send health ping: %v", err) + } + maxAttempts++ + } else { + maxAttempts = 0 + } + + select { + case <-ctx.Done(): + log.Print("[Agones] stopped health pings") + return + case <-tick: + } + } +} + +func (a *Agones) TrackStatus(ctx context.Context) { + tick := time.Tick(5 * time.Second) + for { + if status, err := quakenet.GetStatus(a.Server.Addr); err == nil { + if err != nil { + log.Printf("[Agones] failed to get status from server: %s", err.Error()) + continue + } + for _, p := range status.Players { + log.Printf("[Agones] player: %s", p.Name) + if _, err := a.sdk.Alpha().PlayerConnect(p.Name); err != nil { + log.Printf("[Agones] failed to register player: %s", err.Error()) + } + } + log.Println("[Agones] status checked") + } + + select { + case <-ctx.Done(): + log.Print("[Agones] stopped status checks") + return + case <-tick: + } + } +} From 53a609fb1dd0b1a1905225258b90eb29430b5f35 Mon Sep 17 00:00:00 2001 From: Daniel Oliveira Date: Wed, 20 Jan 2021 20:06:40 +0100 Subject: [PATCH 5/6] Clean up Agones from cmd --- cmd/q3/app/server/server.go | 85 +++++-------------------------------- 1 file changed, 11 insertions(+), 74 deletions(-) diff --git a/cmd/q3/app/server/server.go b/cmd/q3/app/server/server.go index 4d711c1..abd0bdb 100644 --- a/cmd/q3/app/server/server.go +++ b/cmd/q3/app/server/server.go @@ -1,11 +1,9 @@ package server import ( - sdk "agones.dev/agones/sdks/go" "context" "fmt" - quakenet "github.com/criticalstack/quake-kube/internal/quake/net" - "log" + "github.com/criticalstack/quake-kube/pkg/extensions" "net/url" "time" @@ -56,12 +54,6 @@ func NewCommand() *cobra.Command { return err } - if opts.WithAgones { - if err := startAgones(ctx, opts.ServerAddr); err != nil { - log.Printf("[Agones] Error: %v", err) - } - } - go func() { s := quakeserver.Server{ Dir: opts.AssetsDir, @@ -69,8 +61,16 @@ func NewCommand() *cobra.Command { ConfigFile: opts.ConfigFile, Addr: opts.ServerAddr, } - if err := s.Start(ctx); err != nil { - panic(err) + + if opts.WithAgones { + agones := &extensions.Agones{} + if err := agones.Start(ctx, &s); err != nil { + panic(err) + } + } else { + if err := s.Start(ctx); err != nil { + panic(err) + } } }() @@ -101,66 +101,3 @@ func NewCommand() *cobra.Command { cmd.Flags().BoolVar(&opts.WithAgones, "with-agones", false, "use Agones SDK integration") return cmd } - -func startAgones(ctx context.Context, addr string) error { - log.Println("[Agones] starting Agones SDK client") - s, err := sdk.NewSDK() - if err != nil { - return err - } - - if err := s.Ready(); err != nil { - log.Println("[Agones] failed to make the server Ready") - } - - go func() { - tick := time.Tick(2 * time.Second) - maxAttempts := 0 - for { - if err := s.Health(); err != nil { - if maxAttempts > 5 { - log.Fatalf("[Agones] could not send health ping: %v", err) - } - maxAttempts++ - } else { - maxAttempts = 0 - } - - select { - case <-ctx.Done(): - log.Print("[Agones] stopped health pings") - return - case <-tick: - } - } - }() - - go func() { - tick := time.Tick(5 * time.Second) - for { - if status, err := quakenet.GetStatus(addr); err == nil { - if err != nil { - log.Printf("[Agones] failed to get status from server: %s", err.Error()) - continue - } - for _, p := range status.Players { - log.Printf("[Agones] player: %s", p.Name) - - if _, err := s.Alpha().PlayerConnect(p.Name); err != nil { - log.Printf("[Agones] failed to register player: %s", err.Error()) - } - } - log.Println("[Agones] status checked") - } - - select { - case <-ctx.Done(): - log.Print("[Agones] stopped status checks") - return - case <-tick: - } - } - }() - - return nil -} From 62ee8de50af1113d90000d737a6e36a3f598206c Mon Sep 17 00:00:00 2001 From: Daniel Oliveira Date: Wed, 20 Jan 2021 20:06:53 +0100 Subject: [PATCH 6/6] Update Fleet manifest --- examples/fleet.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/fleet.yaml b/examples/fleet.yaml index d82e187..bc9f9b9 100644 --- a/examples/fleet.yaml +++ b/examples/fleet.yaml @@ -66,8 +66,9 @@ spec: - name: default containerPort: 8081 protocol: TCP - health: - disabled: true +# Disable should be false if the flag --with-agones=false +# health: +# disabled: true template: spec: containers: