From eea6c1fdd11f34e894ce58f47b0978167e6b0bdd Mon Sep 17 00:00:00 2001 From: Sivasankaran R Date: Mon, 11 May 2026 08:58:52 +0000 Subject: [PATCH 1/3] Implmemnted Feature Windows Gameserver Move Alpha to beta --- build/terraform/e2e/gke-standard/module.tf | 18 +++ ci/e2e-test-cloudbuild.yaml | 27 ++++ .../gameserver-windows.yaml | 7 +- .../en/docs/Guides/windows-gameservers.md | 6 +- site/content/en/docs/Installation/_index.md | 2 +- test/e2e/windows_test.go | 146 ++++++++++++++++++ 6 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 test/e2e/windows_test.go diff --git a/build/terraform/e2e/gke-standard/module.tf b/build/terraform/e2e/gke-standard/module.tf index 67ce4ce630..31991caf30 100644 --- a/build/terraform/e2e/gke-standard/module.tf +++ b/build/terraform/e2e/gke-standard/module.tf @@ -65,3 +65,21 @@ module "gke_cluster" { udpFirewall = false // firewall is created at the project module level } + +resource "google_container_node_pool" "windows_ltsc2022" { + name = "win-ltsc2022" + cluster = module.agones.cluster_name + location = var.location + node_count = 2 + + node_config { + machine_type = "n2-standard-4" + image_type = "WINDOWS_LTSC_CONTAINERD" + tags = ["game-server"] + } + + management { + auto_repair = true + auto_upgrade = false + } +} \ No newline at end of file diff --git a/ci/e2e-test-cloudbuild.yaml b/ci/e2e-test-cloudbuild.yaml index dc578fc547..1c6f797077 100644 --- a/ci/e2e-test-cloudbuild.yaml +++ b/ci/e2e-test-cloudbuild.yaml @@ -41,6 +41,33 @@ steps: - build-e2e # + # Run the Windows GameServer smoke test + # + - id: e2e-windows-smoke + name: "us-docker.pkg.dev/agones-images/ci/e2e-runner:latest" + entrypoint: bash + args: + - "-c" + - | + go test ./test/e2e/... \ + -run TestWindowsCreateConnect \ + -tags e2e \ + -v \ + -timeout 15m \ + -args \ + -kubeconfig=/workspace/kubeconfig \ + -gameServerImage=$(REGISTRY)/examples/simple-game-server:$(VERSION)-windows_amd64-ltsc2022 \ + 2>&1 | tee /workspace/e2e-windows.log + + grep -q "FAIL" /workspace/e2e-windows.log && exit 1 || true + env: + - "REGISTRY=${_REGISTRY}" + - "VERSION=${_VERSION}" + waitFor: + - setup-e2e-cluster + - push-windows-images + timeout: 900s + # # Run the e2e tests without FeatureGates # - name: e2e-runner diff --git a/examples/simple-game-server/gameserver-windows.yaml b/examples/simple-game-server/gameserver-windows.yaml index 29b6e4d2d9..b90519ccff 100644 --- a/examples/simple-game-server/gameserver-windows.yaml +++ b/examples/simple-game-server/gameserver-windows.yaml @@ -25,7 +25,7 @@ spec: spec: containers: - name: simple-game-server - image: us-docker.pkg.dev/agones-images/examples/simple-game-server:0.42 + image: us-docker.pkg.dev/agones-images/examples/simple-game-server:0.42-windows_amd64-ltsc2022 resources: requests: memory: 64Mi @@ -36,3 +36,8 @@ spec: # Limit this pod to Windows nodes. nodeSelector: kubernetes.io/os: windows + tolerations: + - key: node.kubernetes.io/os + operator: Equal + value: windows + effect: NoSchedule diff --git a/site/content/en/docs/Guides/windows-gameservers.md b/site/content/en/docs/Guides/windows-gameservers.md index f302d4554a..e9b65aafea 100644 --- a/site/content/en/docs/Guides/windows-gameservers.md +++ b/site/content/en/docs/Guides/windows-gameservers.md @@ -7,9 +7,9 @@ description: > Run `GameServers` on Kubernetes nodes with the Windows operating system. --- -{{% alert title="Warning" color="warning" %}} -Running `GameServers` on Windows nodes is currently Alpha, and any feedback -would be appreciated. +{{% alert title="Note" color="info" %}} +Windows GameServer support is in Beta. Only the SDK sidecar (`sdk-server`) +runs on Windows nodes. All Agones control-plane components remain Linux-only. {{% /alert %}} ## Prerequisites diff --git a/site/content/en/docs/Installation/_index.md b/site/content/en/docs/Installation/_index.md index 3fcd2e40cd..1ceb9ca440 100644 --- a/site/content/en/docs/Installation/_index.md +++ b/site/content/en/docs/Installation/_index.md @@ -33,7 +33,7 @@ The following container operating systems and architectures can be utilised with | --------- | ------------ | ---------- | | linux | `amd64` | **Stable** | | linux | `arm64` | Alpha | -| [windows] | `amd64` | Alpha | +| [windows] | `amd64` | Beta | For all the platforms in Alpha, we would appreciate testing and bug reports on any issue found. diff --git a/test/e2e/windows_test.go b/test/e2e/windows_test.go new file mode 100644 index 0000000000..fa38defe51 --- /dev/null +++ b/test/e2e/windows_test.go @@ -0,0 +1,146 @@ +// Copyright Contributors to Agones a Series of LF Projects, LLC. +// +// 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. + +//go:build e2e +// +build e2e + +package e2e + +import ( + "context" + "testing" + + agonesv1 "agones.dev/agones/pkg/apis/agones/v1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// windowsGameServerImage is the windows/amd64 build of simple-game-server. +// Built from examples/simple-game-server/Dockerfile.windows with ltsc2022 base. +const windowsGameServerImage = "us-docker.pkg.dev/agones-images/examples/simple-game-server:0.42-windows_amd64-ltsc2022" + +// windowsGameServerFixture returns a GameServer spec targeting the Windows node pool. +// +// Both nodeSelector AND toleration are required on GKE: +// - nodeSelector: schedules only to Windows nodes +// - toleration: GKE auto-applies node.kubernetes.io/os=windows:NoSchedule to +// all Windows nodes; without this toleration the pod stays Pending forever. +func windowsGameServerFixture() *agonesv1.GameServer { + return &agonesv1.GameServer{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "simple-game-server-windows-", + Namespace: framework.Namespace, // package-level var from main_test.go + }, + Spec: agonesv1.GameServerSpec{ + Ports: []agonesv1.GameServerPort{ + { + Name: "default", + PortPolicy: agonesv1.Dynamic, + ContainerPort: 7654, + Protocol: corev1.ProtocolUDP, + }, + }, + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "simple-game-server", + Image: windowsGameServerImage, + Resources: corev1.ResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("64Mi"), + corev1.ResourceCPU: resource.MustParse("20m"), + }, + Limits: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("64Mi"), + corev1.ResourceCPU: resource.MustParse("20m"), + }, + }, + }, + }, + NodeSelector: map[string]string{ + "kubernetes.io/os": "windows", + }, + Tolerations: []corev1.Toleration{ + { + Key: "node.kubernetes.io/os", + Operator: corev1.TolerationOpEqual, + Value: "windows", + Effect: corev1.TaintEffectNoSchedule, + }, + }, + }, + }, + }, + } +} + +// TestWindowsCreateConnect is the Beta smoke test for Windows GameServer support. +// Validates: +// 1. GameServer with Windows image + nodeSelector + toleration reaches Ready. +// 2. Pod is scheduled on a Windows node (not Linux). +// 3. sdk-server sidecar (windows/amd64) initialises and signals Ready. +// 4. UDP connectivity to the host port works from outside the cluster. +func TestWindowsCreateConnect(t *testing.T) { + t.Parallel() + + gs := windowsGameServerFixture() + + // framework.WaitForState is the cluster-default timeout (set in main_test.go). + // Windows image pulls on a cold node can take 3-8 min, so we use 10 minutes. + // The framework variable is the *framework.Framework from main_test.go — no import needed. + readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs) + require.NoError(t, err, "GameServer must reach Ready state — check Windows node pool exists and image is pushed") + + defer framework.AgonesClient.AgonesV1(). + GameServers(readyGs.Namespace). + Delete(context.Background(), readyGs.Name, metav1.DeleteOptions{}) // nolint:errcheck + + t.Logf("GameServer %s is Ready: address=%s port=%d", + readyGs.Name, readyGs.Status.Address, readyGs.Status.Ports[0].Port) + + // Assert the pod landed on a Windows node. + pod, err := framework.KubeClient.CoreV1(). + Pods(readyGs.Namespace). + Get(context.Background(), readyGs.Name, metav1.GetOptions{}) + require.NoError(t, err) + + node, err := framework.KubeClient.CoreV1(). + Nodes(). + Get(context.Background(), pod.Spec.NodeName, metav1.GetOptions{}) + require.NoError(t, err) + + nodeOS := node.Labels["kubernetes.io/os"] + assert.Equal(t, "windows", nodeOS, + "Pod must run on a Windows node, got os=%s on node %s", nodeOS, pod.Spec.NodeName) + t.Logf("Pod %s is running on node %s (os=%s)", pod.Name, pod.Spec.NodeName, nodeOS) + + // Assert address and port are populated. + require.NotEmpty(t, readyGs.Status.Address, "GameServer must have an external address") + require.NotEmpty(t, readyGs.Status.Ports, "GameServer must have at least one port") + + // Send a UDP message using the framework helper. + // SendGameServerUDP retries 5 times and times out after 10 seconds. + // simple-game-server echoes back "ACK: ". + reply, err := framework.SendGameServerUDP(t, readyGs, "PING") + require.NoError(t, err, "UDP send/receive must succeed against Windows GameServer at %s:%d", + readyGs.Status.Address, readyGs.Status.Ports[0].Port) + + assert.Contains(t, reply, "ACK", + "simple-game-server must echo back ACK, got: %q", reply) + t.Logf("UDP response from Windows GameServer: %q", reply) +} From 7fef0afa114a20985130b7b635990c2d6002f49b Mon Sep 17 00:00:00 2001 From: Sivasankaran R Date: Mon, 11 May 2026 09:37:23 +0000 Subject: [PATCH 2/3] Fix e2e test cloudbuild.yam --- ci/e2e-test-cloudbuild.yaml | 47 +++++++++++++++---------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/ci/e2e-test-cloudbuild.yaml b/ci/e2e-test-cloudbuild.yaml index 1c6f797077..e82599b4e7 100644 --- a/ci/e2e-test-cloudbuild.yaml +++ b/ci/e2e-test-cloudbuild.yaml @@ -39,34 +39,6 @@ steps: id: e2e-feature-gates waitFor: - build-e2e - - # - # Run the Windows GameServer smoke test - # - - id: e2e-windows-smoke - name: "us-docker.pkg.dev/agones-images/ci/e2e-runner:latest" - entrypoint: bash - args: - - "-c" - - | - go test ./test/e2e/... \ - -run TestWindowsCreateConnect \ - -tags e2e \ - -v \ - -timeout 15m \ - -args \ - -kubeconfig=/workspace/kubeconfig \ - -gameServerImage=$(REGISTRY)/examples/simple-game-server:$(VERSION)-windows_amd64-ltsc2022 \ - 2>&1 | tee /workspace/e2e-windows.log - - grep -q "FAIL" /workspace/e2e-windows.log && exit 1 || true - env: - - "REGISTRY=${_REGISTRY}" - - "VERSION=${_VERSION}" - waitFor: - - setup-e2e-cluster - - push-windows-images - timeout: 900s # # Run the e2e tests without FeatureGates # @@ -80,6 +52,25 @@ steps: - ${_GS_TEST_IMAGE} id: e2e-stable waitFor: [e2e-feature-gates] + # + # Run Windows GameServer smoke test + # Runs parallel with e2e-feature-gates so it does not add to wall-clock time. + # + - name: e2e-runner + args: + - ${_FEATURE_WITHOUT_GATE} + - ${_CLOUD_PRODUCT} + - ${_TEST_CLUSTER_NAME} + - ${_TEST_CLUSTER_LOCATION} + - ${_REGISTRY} + - ${_GS_TEST_IMAGE} + env: + - 'WINDOWS_GS_IMAGE=${_GS_TEST_IMAGE}-windows_amd64-ltsc2019' + - 'E2E_TEST_RUN=TestWindowsCreateConnect' + id: e2e-windows-smoke + waitFor: + - build-e2e + timeout: 900s tags: - e2e-test - cluster-${_TEST_CLUSTER_NAME} From 0470921ac904ffd3a7a096fb00493da65fe083a2 Mon Sep 17 00:00:00 2001 From: Sivasankaran R Date: Tue, 12 May 2026 08:32:50 +0000 Subject: [PATCH 3/3] Change Windows base lts --- ci/e2e-test-cloudbuild.yaml | 3 +-- examples/simple-game-server/gameserver-windows.yaml | 2 +- test/e2e/windows_test.go | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ci/e2e-test-cloudbuild.yaml b/ci/e2e-test-cloudbuild.yaml index e82599b4e7..07b940bd75 100644 --- a/ci/e2e-test-cloudbuild.yaml +++ b/ci/e2e-test-cloudbuild.yaml @@ -24,7 +24,6 @@ steps: dir: build/e2e-image id: build-e2e waitFor: ['-'] - # # Run the e2e tests with FeatureGates inverted compared to Stable # @@ -54,7 +53,7 @@ steps: waitFor: [e2e-feature-gates] # # Run Windows GameServer smoke test - # Runs parallel with e2e-feature-gates so it does not add to wall-clock time. + # Runs parallel with e2e-feature-gates, does not add to wall-clock time. # - name: e2e-runner args: diff --git a/examples/simple-game-server/gameserver-windows.yaml b/examples/simple-game-server/gameserver-windows.yaml index b90519ccff..df65351d61 100644 --- a/examples/simple-game-server/gameserver-windows.yaml +++ b/examples/simple-game-server/gameserver-windows.yaml @@ -25,7 +25,7 @@ spec: spec: containers: - name: simple-game-server - image: us-docker.pkg.dev/agones-images/examples/simple-game-server:0.42-windows_amd64-ltsc2022 + image: us-docker.pkg.dev/agones-images/examples/simple-game-server:0.42-windows_amd64-ltsc2019 resources: requests: memory: 64Mi diff --git a/test/e2e/windows_test.go b/test/e2e/windows_test.go index fa38defe51..fdad85bd86 100644 --- a/test/e2e/windows_test.go +++ b/test/e2e/windows_test.go @@ -30,8 +30,8 @@ import ( ) // windowsGameServerImage is the windows/amd64 build of simple-game-server. -// Built from examples/simple-game-server/Dockerfile.windows with ltsc2022 base. -const windowsGameServerImage = "us-docker.pkg.dev/agones-images/examples/simple-game-server:0.42-windows_amd64-ltsc2022" +// Built from examples/simple-game-server/Dockerfile.windows with ltsc2019 base. +const windowsGameServerImage = "us-docker.pkg.dev/agones-images/examples/simple-game-server:0.42-windows_amd64-ltsc2019" // windowsGameServerFixture returns a GameServer spec targeting the Windows node pool. //