Skip to content

fix: move GameServer to Shutdown when the game container exits cleanly and the Pod stays Running - #4737

Merged
markmandel merged 9 commits into
agones-dev:mainfrom
GrowlyX:fix/gameserver-container-exit-zero
Sep 19, 2026
Merged

markmandel merged 9 commits into
agones-dev:mainfrom
GrowlyX:fix/gameserver-container-exit-zero

Conversation

@GrowlyX

@GrowlyX GrowlyX commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

What type of PR is this?

/kind bug

What this PR does / Why we need it:

The SucceededController only moved a GameServer to Shutdown once the Pod reached the Succeeded phase.

A Pod only reaches Succeeded once every container in containers has terminated, so a long-lived non-sidecar container holds the Pod in Running after the game server container exits 0, and the GameServer runs forever.

The controller now also treats the Pod as completed when the game server container has terminated with exit code 0, on a Pod with more than one entry in containers and RestartPolicy: Never — the conditions under which something other than the game server can hold the Pod in Running, and a terminated container is final. #4663 covered the non-zero exit code path in the HealthController; this is the exit code 0 counterpart.

Docs

  • health-checking.md: rule 5 of the SidecarContainers list said the GameServer moves to Shutdown "once the Pod completes", which no longer holds. It now matches rule 4's wording and fires as soon as the game server container exits.
  • health-checking.md: the "additional workloads" section now says Agones watches the game server container directly, so a long-lived container no longer keeps a finished GameServer alive (gated behind publishVersion="1.61.0").
  • gameserver.md / health-checking.md: the existing "some CNIs may not support ports on init containers" warning is replaced with the actual cause. The kubelet only passes hostPort mappings from regular containers to the CNI when it creates the Pod sandbox (kuberuntime_sandbox.go), so a hostPort on a native sidecar never reaches the portmap plugin. This matters for Health Checks not running when having "Completed" containers in Pods #4728 because "make it a native sidecar" is the suggested workaround, and it is not available to a container that needs a GameServer port (as @nrwiersma pointed out). This is also why TestGameServerWithPortsMappedToInitSidecarContainers is skipped.

Which issue(s) this PR fixes:

Closes #4728

Did you use AI tools in preparing this PR?:

Y

Special notes for your reviewer:

Since the first push:

  • Dropped the SidecarContainers feature gate check from the new code path, per @nrwiersma. The gate only moves the SDK server into initContainers and says nothing about whether the user's own containers are sidecars. It was also redundant — RestartPolicy: Never is only defaulted inside that gate today — and it wrongly excluded anyone who sets restartPolicy: Never themselves.
  • Fixed the new e2e test, which failed the last build. It expected an ACK from CRASH 0, but handleCrash calls os.Exit before replying, so SendGameServerUDP always timed out. It now ignores the reply, as TestGameServerUnhealthyAfterReadyCrashWithGenericContainer already does.

The e2e test still skips without SidecarContainers, since DefaultGameServer only gets RestartPolicy: Never via that gate.

…y and the Pod stays Running

With SidecarContainers enabled, the SucceededController only moved a
GameServer to Shutdown once the Pod reached the Succeeded phase. A Pod
only reaches Succeeded once every container in `containers` has
terminated, so a long-lived non-sidecar container held the Pod in
Running after the game server container exited 0, and the GameServer
ran forever. agones-dev#4663 covered the non-zero exit code path in the
HealthController; this is the exit code 0 counterpart.

The SucceededController now also treats a Pod as completed when the
game server container has terminated with exit code 0 and the Pod is
RestartPolicy: Never, mirroring the HealthController check.

Also documents that hostPort on sidecar (init) containers does not work
on most clusters: the kubelet only passes port mappings declared on
regular containers to the CNI when creating the Pod sandbox, so the
recommended "use a native sidecar" workaround is not available to
containers that need a GameServer port.

Closes agones-dev#4728

Signed-off-by: Subham K. <me@growly.gg>
@github-actions github-actions Bot added size/M kind/bug These are bugs. labels Sep 13, 2026
@markmandel

Copy link
Copy Markdown
Member

/gcbrun

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

User-facing and controller documentation still contradict the newly implemented lifecycle behavior.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Updates GameServer lifecycle handling so a cleanly exited game container triggers shutdown even when another container keeps the Pod running.

Changes:

  • Detects clean game-container termination independently of Pod phase.
  • Adds unit and end-to-end coverage.
  • Updates sidecar lifecycle and port-mapping documentation.
File summaries
File Description
pkg/gameservers/succeeded.go Adds container-level completion detection.
pkg/gameservers/succeeded_test.go Tests completion and shutdown behavior.
test/e2e/gameserver_test.go Covers clean exit with a long-lived container.
site/content/en/docs/Guides/health-checking.md Documents updated lifecycle semantics.
site/content/en/docs/Reference/gameserver.md Clarifies native-sidecar port limitations.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pkg/gameservers/succeeded.go
Comment thread site/content/en/docs/Guides/health-checking.md
Comment thread pkg/gameservers/succeeded.go Outdated
@agones-bot

Copy link
Copy Markdown
Collaborator

Build Failed 😭

Build Id: a02934e2-0152-4a68-8f04-3fe480968ccc

Status: FAILURE

To get permission to view the Cloud Build view, join the agones-discuss Google Group.

…e gate

Review feedback on agones-dev#4737:

The SidecarContainers feature gate only moves the Agones SDK server into
initContainers; it says nothing about whether the user's own containers are
sidecars, so it is the wrong signal for "something other than the game server
can hold this Pod in Running". Check the Pod shape instead: more than one entry
in `containers`, and RestartPolicy: Never so a terminated container is final.

The e2e test expected an ACK from `CRASH 0`, but handleCrash calls os.Exit
before replying, so SendGameServerUDP always timed out and the test failed.
Ignore the reply, as TestGameServerUnhealthyAfterReadyCrashWithGenericContainer
already does.

Also reword the controller docs and health checking rule 5, which still said
this only happens once the Pod reaches Succeeded.

Signed-off-by: Subham K. <me@growly.gg>
Comment thread pkg/gameservers/succeeded_test.go Outdated
Comment thread site/content/en/docs/Guides/health-checking.md Outdated
Comment thread site/content/en/docs/Reference/gameserver.md Outdated
- Split the gameServerContainerCompleted assertions into named subtests.
- The Pod does not outlive the GameServer, so say the containers keep
  running until the GameServer is shutdown, not deleted.
- Drop "on most clusters" for the hostPort-on-sidecar warning, in both
  docs that carry it: Kubernetes is what does not map the port, and
  whether a CNI picks it up anyway is already covered below.

Signed-off-by: Subham K. <me@growly.gg>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new completion logic omits OnFailure, races with failed-Pod handling, and introduces an avoidable one-minute test delay.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread pkg/gameservers/succeeded.go Outdated
Comment thread pkg/gameservers/succeeded.go
Comment thread test/e2e/gameserver_test.go Outdated
- A Failed Pod already makes the GameServer Unhealthy via the
  HealthController's failedPod(), which watches the same updates. Bail
  out on that phase so the two controllers can't race to write the
  GameServer's state.
- OnFailure never restarts a container that exited 0, and nothing
  validates the Pod's restartPolicy, so a long-lived container could
  still strand a GameServer on one. Only reject Always.
- The e2e test's SendGameServerUDP waited a minute for an ACK that
  CRASH 0 never sends, and dumped failure diagnostics on the way out.
  Write to the socket inside the poll loop instead, which also re-sends
  if the packet is dropped.

Signed-off-by: Subham K. <me@growly.gg>
@markmandel

Copy link
Copy Markdown
Member

/gcbrun

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The health-checking documentation overstates behavior when users override the default Pod restart policy.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

site/content/en/docs/Guides/health-checking.md:82

  • This paragraph is unconditional, but the direct non-zero-exit path only applies to restartPolicy: Never, and the clean-exit path rejects Always. A user may override the default Pod restart policy, so the documented outcomes are not generally true. Scope this statement to the default policy (the behavior described by rules 4 and 5).
after the game server container has exited. Agones does not depend on the Pod phase for this: it watches the game
server container directly, and will move the `GameServer` to `Unhealthy` on a non-zero exit code (rule 4) or to
`Shutdown` on a clean exit (rule 5) as soon as the game server container terminates. The Pod's own phase, however,
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread site/content/en/docs/Guides/health-checking.md Outdated
@agones-bot

Copy link
Copy Markdown
Collaborator

Build Failed 😭

Build Id: 4f369f4b-0593-49ef-9b3f-11aaa3dceb19

Status: FAILURE

To get permission to view the Cloud Build view, join the agones-discuss Google Group.

@markmandel

Copy link
Copy Markdown
Member

/gcbrun

@agones-bot

Copy link
Copy Markdown
Collaborator

Build Succeeded 🥳

Build Id: 67df54e9-564b-4350-bc79-0b4e60533dae

The following development artifacts have been built, and will exist for the next 30 days:

A preview of the website (the last 30 builds are retained):

To install this version:

git fetch https://github.com/googleforgames/agones.git pull/4737/head:pr_4737 && git checkout pr_4737
helm install agones ./install/helm/agones --namespace agones-system --set agones.image.registry=us-docker.pkg.dev/agones-images/ci --set agones.image.tag=1.61.0-dev-95c567f

@markmandel markmandel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good - just some docs things I wanted to poke at 👍🏻

Comment thread pkg/gameservers/succeeded.go Outdated
Comment thread site/content/en/docs/Guides/health-checking.md Outdated
Comment thread site/content/en/docs/Reference/gameserver.md Outdated
Simplify the restart policy check, version-gate the clean-exit health
checking rule, and clarify the sidecar port CNI warning.

Signed-off-by: Subham K. <me@growly.gg>
@markmandel

Copy link
Copy Markdown
Member

/gcbrun

@agones-bot

Copy link
Copy Markdown
Collaborator

Build Succeeded 🥳

Build Id: d7249135-7182-4e27-a32a-c26c12cd0de4

The following development artifacts have been built, and will exist for the next 30 days:

A preview of the website (the last 30 builds are retained):

To install this version:

git fetch https://github.com/googleforgames/agones.git pull/4737/head:pr_4737 && git checkout pr_4737
helm install agones ./install/helm/agones --namespace agones-system --set agones.image.registry=us-docker.pkg.dev/agones-images/ci --set agones.image.tag=1.61.0-dev-98f6ee2

@markmandel
markmandel merged commit 12cf52d into agones-dev:main Sep 19, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/bug These are bugs. size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Health Checks not running when having "Completed" containers in Pods

5 participants