Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
5944adc
fix: ensure we set ports addresses while waiting for pod IPs
lacroixthomas Jan 21, 2025
fc9b8d2
fix: update unit tests
lacroixthomas Jan 21, 2025
70cd30f
fix: ensure podIPs are also set on ready if called before controller
lacroixthomas Jan 22, 2025
3e1a602
fix: ensure podIPs are also set on ready if called before controller
lacroixthomas Jan 22, 2025
329bf81
fix: ensure we set the new PodIPs if they are present
lacroixthomas Jan 22, 2025
f55f2e1
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
lacroixthomas Feb 18, 2025
37dcef7
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
lacroixthomas Mar 4, 2025
359abf6
feat: update comment
lacroixthomas Mar 12, 2025
e8646ad
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
lacroixthomas Mar 12, 2025
2eeb51e
feat: WIP update following feedback
lacroixthomas Apr 1, 2025
095cd3c
feat: revert the applyGameServerAddressAndPort
lacroixthomas Apr 9, 2025
2667a7c
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
markmandel Apr 15, 2025
3c4dc9a
feat: ensure pod exists to apply addressandport
lacroixthomas Apr 17, 2025
13dd475
Merge branch 'googleforgames:main' into bugfixes/delays-awaiting-pod-ips
lacroixthomas Apr 23, 2025
9312bcd
feat: ensure to not stop the process if node not yet set
lacroixthomas Apr 23, 2025
0023936
feat: fix unit tests
lacroixthomas Apr 23, 2025
d83d0e2
feat: trigger pipeline
lacroixthomas Apr 23, 2025
8c66673
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
lacroixthomas Apr 28, 2025
8971843
feat: add comment
lacroixthomas May 2, 2025
7c44bbd
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
lacroixthomas May 2, 2025
6309f55
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
igooch May 5, 2025
20b89a4
feat: ensure pod ips are set on each gsSync func where pods are avail…
lacroixthomas May 10, 2025
ae72407
feat: use defer to update gs if needed
lacroixthomas May 13, 2025
9730c94
feat: remove unit tests for syncGameServerStartingState
lacroixthomas May 13, 2025
49c911f
feat: remove podIPs check from main tests and keep the one when avail…
lacroixthomas Jun 3, 2025
4b6d17d
feat: remove commented code
lacroixthomas Jun 4, 2025
23f928d
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
lacroixthomas Jun 4, 2025
8d8c27c
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
lacroixthomas Jun 10, 2025
fd467fd
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
Jun 13, 2025
a6af646
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
Aug 12, 2025
a491f30
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
lacroixthomas Aug 16, 2025
5c45723
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
lacroixthomas Sep 12, 2025
d6293c6
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
lacroixthomas Sep 17, 2025
aabd5fb
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
lacroixthomas Oct 19, 2025
0b66d2f
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
lacroixthomas Oct 20, 2025
010a278
Merge branch 'main' into bugfixes/delays-awaiting-pod-ips
Sivasankaran25 Oct 21, 2025
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
34 changes: 26 additions & 8 deletions pkg/gameservers/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -576,9 +576,8 @@ func (c *Controller) syncGameServerCreatingState(ctx context.Context, gs *agones
loggerForGameServer(gs, c.baseLogger).Debug("Syncing Create State")

// Maybe something went wrong, and the pod was created, but the state was never moved to Starting, so let's check
_, err := c.gameServerPod(gs)
pod, err := c.gameServerPod(gs)
if k8serrors.IsNotFound(err) {

for i := range gs.Spec.Ports {
if gs.Spec.Ports[i].PortPolicy == agonesv1.Static && gs.Spec.Ports[i].Protocol == agonesv1.ProtocolTCPUDP {
name := gs.Spec.Ports[i].Name
Expand Down Expand Up @@ -607,6 +606,9 @@ func (c *Controller) syncGameServerCreatingState(ctx context.Context, gs *agones
}

gsCopy := gs.DeepCopy()

gsCopy, _ = applyGameServerPodIP(gsCopy, pod)

gsCopy.Status.State = agonesv1.GameServerStateStarting
gs, err = c.gameServerGetter.GameServers(gs.ObjectMeta.Namespace).Update(ctx, gsCopy, metav1.UpdateOptions{})
if err != nil {
Expand Down Expand Up @@ -906,11 +908,6 @@ func (c *Controller) syncGameServerStartingState(ctx context.Context, gs *agones
return gs, workerqueue.NewTraceError(errors.Errorf("node not yet populated for Pod %s", pod.ObjectMeta.Name))
}

// Ensure the pod IPs are populated
if len(pod.Status.PodIPs) == 0 {
return gs, workerqueue.NewTraceError(errors.Errorf("pod IPs not yet populated for Pod %s", pod.ObjectMeta.Name))
}

node, err := c.nodeLister.Get(pod.Spec.NodeName)
if err != nil {
return gs, errors.Wrapf(err, "error retrieving node %s for Pod %s", pod.Spec.NodeName, pod.ObjectMeta.Name)
Expand All @@ -921,6 +918,8 @@ func (c *Controller) syncGameServerStartingState(ctx context.Context, gs *agones
return gs, err
}

gsCopy, _ = applyGameServerPodIP(gsCopy, pod)

gsCopy.Status.State = agonesv1.GameServerStateScheduled
gs, err = c.gameServerGetter.GameServers(gs.ObjectMeta.Namespace).Update(ctx, gsCopy, metav1.UpdateOptions{})
if err != nil {
Expand Down Expand Up @@ -954,12 +953,28 @@ func (c *Controller) syncGameServerRequestReadyState(ctx context.Context, gs *ag
return nil, err
}

var gsUpdated bool

gsCopy, podIPUpdated := applyGameServerPodIP(gsCopy, pod)
if podIPUpdated {
// defer the update of the GameServer until the end of this function
// in case there is no gs state update and the podIP is available and populated
defer func(gs *agonesv1.GameServer, alreadyUpdated *bool) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This freaks me out a bunch 😆 - but you still end up with a double update, which seems unnecessary.

The way I wanted to this work was that whenever the Pod IP is updated, then the GameServer gets updated - unless it's already captured in an existing status update.

We shouldn't need to have a double updated if it's already seen inside one of these status change sync functions.

// Only update if the GameServer is not already updated
if alreadyUpdated == nil || *alreadyUpdated {
return
}
_, _ = c.gameServerGetter.GameServers(gs.ObjectMeta.Namespace).Update(ctx, gs, metav1.UpdateOptions{})
}(gsCopy, &gsUpdated)
}

// if the address hasn't been populated, and the Ready request comes
// before the controller has had a chance to do it, then
// do it here instead
addressPopulated := false
if gs.Status.NodeName == "" {
if gsCopy.Status.NodeName == "" {
addressPopulated = true

if pod.Spec.NodeName == "" {
return gs, workerqueue.NewTraceError(errors.Errorf("node not yet populated for Pod %s", pod.ObjectMeta.Name))
}
Expand All @@ -978,6 +993,9 @@ func (c *Controller) syncGameServerRequestReadyState(ctx context.Context, gs *ag
return gs, err
}

// if the address is already populated, then we don't need to re-update for podIPs
gsUpdated = true

gsCopy.Status.State = agonesv1.GameServerStateReady
gs, err = c.gameServerGetter.GameServers(gs.ObjectMeta.Namespace).Update(ctx, gsCopy, metav1.UpdateOptions{})
if err != nil {
Expand Down
45 changes: 17 additions & 28 deletions pkg/gameservers/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1189,33 +1189,6 @@ func TestControllerSyncGameServerStartingState(t *testing.T) {
assert.NotEmpty(t, gs.Status.Ports)
})

t.Run("Error on podIPs not populated", func(t *testing.T) {
c, m := newFakeController()
gsFixture := newFixture()
gsFixture.ApplyDefaults()
pod, err := gsFixture.Pod(agtesting.FakeAPIHooks{})
require.NoError(t, err)
pod.Spec.NodeName = nodeFixtureName

m.KubeClient.AddReactor("list", "nodes", func(_ k8stesting.Action) (bool, runtime.Object, error) {
return true, &corev1.NodeList{Items: []corev1.Node{node}}, nil
})
m.KubeClient.AddReactor("list", "pods", func(_ k8stesting.Action) (bool, runtime.Object, error) {
return true, &corev1.PodList{Items: []corev1.Pod{*pod}}, nil
})
m.AgonesClient.AddReactor("update", "gameservers", func(action k8stesting.Action) (bool, runtime.Object, error) {
ua := action.(k8stesting.UpdateAction)
gs := ua.GetObject().(*agonesv1.GameServer)
assert.Equal(t, agonesv1.GameServerStateScheduled, gs.Status.State)
return true, gs, errors.New("update-err")
})
ctx, cancel := agtesting.StartInformers(m, c.gameServerSynced, c.podSynced, c.nodeSynced)
defer cancel()

_, err = c.syncGameServerStartingState(ctx, gsFixture)
assert.Error(t, err)
})

t.Run("Error on update", func(t *testing.T) {
c, m := newFakeController()
gsFixture := newFixture()
Expand Down Expand Up @@ -1455,6 +1428,9 @@ func TestControllerSyncGameServerRequestReadyState(t *testing.T) {
Spec: newSingleContainerSpec(), Status: agonesv1.GameServerStatus{State: agonesv1.GameServerStateRequestReady}}
gsFixture.ApplyDefaults()
gsFixture.Status.NodeName = nodeName
gsFixture.Status.Addresses = []corev1.NodeAddress{
{Type: agonesv1.NodePodIP, Address: "0.0.0.0"},
}
pod, err := gsFixture.Pod(agtesting.FakeAPIHooks{})
require.NoError(t, err)
pod.Status.ContainerStatuses = []corev1.ContainerStatus{
Expand Down Expand Up @@ -1515,6 +1491,9 @@ func TestControllerSyncGameServerRequestReadyState(t *testing.T) {
Spec: newSingleContainerSpec(), Status: agonesv1.GameServerStatus{State: agonesv1.GameServerStateRequestReady}}
gsFixture.ApplyDefaults()
gsFixture.Status.NodeName = nodeName
gsFixture.Status.Addresses = []corev1.NodeAddress{
{Type: agonesv1.NodePodIP, Address: "0.0.0.0"},
}
pod, err := gsFixture.Pod(agtesting.FakeAPIHooks{})
require.NoError(t, err)
pod.Status.ContainerStatuses = []corev1.ContainerStatus{
Expand Down Expand Up @@ -1559,6 +1538,9 @@ func TestControllerSyncGameServerRequestReadyState(t *testing.T) {
Spec: newSingleContainerSpec(), Status: agonesv1.GameServerStatus{State: agonesv1.GameServerStateRequestReady}}
gsFixture.ApplyDefaults()
gsFixture.Status.NodeName = nodeName
gsFixture.Status.Addresses = []corev1.NodeAddress{
{Type: agonesv1.NodePodIP, Address: "0.0.0.0"},
}
pod, err := gsFixture.Pod(agtesting.FakeAPIHooks{})
require.NoError(t, err)
pod.Status.ContainerStatuses = []corev1.ContainerStatus{
Expand Down Expand Up @@ -1602,6 +1584,9 @@ func TestControllerSyncGameServerRequestReadyState(t *testing.T) {
Spec: newSingleContainerSpec(), Status: agonesv1.GameServerStatus{State: agonesv1.GameServerStateRequestReady}}
gsFixture.ApplyDefaults()
gsFixture.Status.NodeName = nodeName
gsFixture.Status.Addresses = []corev1.NodeAddress{
{Type: agonesv1.NodePodIP, Address: "0.0.0.0"},
}
pod, err := gsFixture.Pod(agtesting.FakeAPIHooks{})
require.NoError(t, err)
pod.ObjectMeta.Annotations = map[string]string{agonesv1.GameServerReadyContainerIDAnnotation: containerID}
Expand Down Expand Up @@ -1659,6 +1644,7 @@ func TestControllerSyncGameServerRequestReadyState(t *testing.T) {
ContainerID: containerID,
},
}
pod.Status.PodIPs = []corev1.PodIP{{IP: ipv6Fixture}}
gsUpdated := false
podUpdated := false

Expand Down Expand Up @@ -1699,7 +1685,10 @@ func TestControllerSyncGameServerRequestReadyState(t *testing.T) {

assert.Equal(t, gs.Status.NodeName, nodeFixture.ObjectMeta.Name)
assert.Equal(t, gs.Status.Address, ipFixture)
assert.Equal(t, []corev1.NodeAddress{{Address: ipFixture, Type: "ExternalIP"}}, gs.Status.Addresses)
assert.Equal(t, []corev1.NodeAddress{
{Address: ipFixture, Type: "ExternalIP"},
{Address: ipv6Fixture, Type: "PodIP"},
}, gs.Status.Addresses)

agtesting.AssertEventContains(t, m.FakeRecorder.Events, "Address and port populated")
agtesting.AssertEventContains(t, m.FakeRecorder.Events, "SDK.Ready() complete")
Expand Down
35 changes: 29 additions & 6 deletions pkg/gameservers/gameservers.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,7 @@ func applyGameServerAddressAndPort(gs *agonesv1.GameServer, node *corev1.Node, p
gs.Status.Addresses = addrs
gs.Status.NodeName = pod.Spec.NodeName

for _, ip := range pod.Status.PodIPs {
gs.Status.Addresses = append(gs.Status.Addresses, corev1.NodeAddress{
Type: agonesv1.NodePodIP,
Address: ip.IP,
})
}
gs, _ = applyGameServerPodIP(gs, pod)

if err := syncPodPortsToGameServer(gs, pod); err != nil {
return gs, errors.Wrapf(err, "cloud product error syncing ports on GameServer %s", gs.ObjectMeta.Name)
Expand All @@ -114,6 +109,34 @@ func applyGameServerAddressAndPort(gs *agonesv1.GameServer, node *corev1.Node, p
return gs, nil
}

// applyGameServerPodIP checks the pod IPs and adds them to the GameServer status if they are not already there.
func applyGameServerPodIP(gs *agonesv1.GameServer, pod *corev1.Pod) (*agonesv1.GameServer, bool) {
var updated bool

if pod == nil {
return gs, false
}

gsPodIPs := make(map[string]interface{})
for _, addr := range gs.Status.Addresses {
if addr.Type == agonesv1.NodePodIP {
gsPodIPs[addr.Address] = nil
}
}

for _, ip := range pod.Status.PodIPs {
if _, ok := gsPodIPs[ip.IP]; !ok {
gs.Status.Addresses = append(gs.Status.Addresses, corev1.NodeAddress{
Type: agonesv1.NodePodIP,
Address: ip.IP,
})
updated = true
}
}

return gs, updated
}

// isBeforePodCreated checks to see if the GameServer is in a state in which the pod could not have been
// created yet. This includes "Starting" in which a pod MAY exist, but may not yet be available, depending on when the
// informer cache updates
Expand Down