Skip to content

Commit

Permalink
handle static port policy (#3375)
Browse files Browse the repository at this point in the history
* handle static port policy
* test cases for static portpolicy with TCPUDP
* e2e test for TCPUDP protocol of static
  • Loading branch information
ashutosji authored Nov 15, 2023
1 parent 25a2c9e commit 9597660
Show file tree
Hide file tree
Showing 4 changed files with 248 additions and 1 deletion.
18 changes: 18 additions & 0 deletions pkg/gameservers/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,24 @@ func (c *Controller) syncGameServerCreatingState(ctx context.Context, gs *agones
// 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)
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
gs.Spec.Ports[i].Name = name + "-tcp"
gs.Spec.Ports[i].Protocol = corev1.ProtocolTCP

// Add separate UDP port configuration
gs.Spec.Ports = append(gs.Spec.Ports, agonesv1.GameServerPort{
PortPolicy: agonesv1.Static,
Name: name + "-udp",
ContainerPort: gs.Spec.Ports[i].ContainerPort,
HostPort: gs.Spec.Ports[i].HostPort,
Protocol: corev1.ProtocolUDP,
Container: gs.Spec.Ports[i].Container,
})
}
}
gs, err = c.createGameServerPod(ctx, gs)
if err != nil || gs.Status.State == agonesv1.GameServerStateError {
return gs, err
Expand Down
130 changes: 130 additions & 0 deletions pkg/gameservers/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,136 @@ func TestControllerSyncGameServerCreatingState(t *testing.T) {
return fixture
}

t.Run("Testing TCPUDP protocol of static portpolicy", func(t *testing.T) {
c, m := newFakeController()
fixture := &agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"},
Spec: newSingleContainerSpec(), Status: agonesv1.GameServerStatus{State: agonesv1.GameServerStateCreating}}
fixture.Spec.Ports[0].Name = "default"
fixture.Spec.Ports[0].HostPort = 7000
fixture.Spec.Ports[0].Protocol = agonesv1.ProtocolTCPUDP
fixture.ApplyDefaults()
podCreated := false
gsUpdated := false

var pod *corev1.Pod
m.KubeClient.AddReactor("create", "pods", func(action k8stesting.Action) (bool, runtime.Object, error) {
podCreated = true
ca := action.(k8stesting.CreateAction)
pod = ca.GetObject().(*corev1.Pod)
assert.True(t, metav1.IsControlledBy(pod, fixture))
return true, pod, nil
})
m.AgonesClient.AddReactor("update", "gameservers", func(action k8stesting.Action) (bool, runtime.Object, error) {
gsUpdated = true
ua := action.(k8stesting.UpdateAction)
gs := ua.GetObject().(*agonesv1.GameServer)
assert.Equal(t, agonesv1.GameServerStateStarting, gs.Status.State)
assert.Len(t, gs.Spec.Ports, 2)
assert.Equal(t, "default-tcp", gs.Spec.Ports[0].Name)
assert.Equal(t, corev1.ProtocolTCP, gs.Spec.Ports[0].Protocol)
assert.Equal(t, "default-udp", gs.Spec.Ports[1].Name)
assert.Equal(t, corev1.ProtocolUDP, gs.Spec.Ports[1].Protocol)
return true, gs, nil
})

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

gs, err := c.syncGameServerCreatingState(ctx, fixture)

assert.NoError(t, err)
assert.True(t, podCreated, "Pod should have been created")

assert.Equal(t, agonesv1.GameServerStateStarting, gs.Status.State)
assert.True(t, gsUpdated, "GameServer should have been updated")
agtesting.AssertEventContains(t, m.FakeRecorder.Events, "Pod")
})

t.Run("Testing TCP protocol of static portpolicy", func(t *testing.T) {
c, m := newFakeController()
fixture := &agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"},
Spec: newSingleContainerSpec(), Status: agonesv1.GameServerStatus{State: agonesv1.GameServerStateCreating}}
fixture.Spec.Ports[0].Name = "tcp-port"
fixture.Spec.Ports[0].HostPort = 7000
fixture.Spec.Ports[0].Protocol = corev1.ProtocolTCP
fixture.ApplyDefaults()
podCreated := false
gsUpdated := false

var pod *corev1.Pod
m.KubeClient.AddReactor("create", "pods", func(action k8stesting.Action) (bool, runtime.Object, error) {
podCreated = true
ca := action.(k8stesting.CreateAction)
pod = ca.GetObject().(*corev1.Pod)
assert.True(t, metav1.IsControlledBy(pod, fixture))
return true, pod, nil
})
m.AgonesClient.AddReactor("update", "gameservers", func(action k8stesting.Action) (bool, runtime.Object, error) {
gsUpdated = true
ua := action.(k8stesting.UpdateAction)
gs := ua.GetObject().(*agonesv1.GameServer)
assert.Equal(t, agonesv1.GameServerStateStarting, gs.Status.State)
assert.Len(t, gs.Spec.Ports, 1)
assert.Equal(t, "tcp-port", gs.Spec.Ports[0].Name)
assert.Equal(t, corev1.ProtocolTCP, gs.Spec.Ports[0].Protocol)
return true, gs, nil
})

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

gs, err := c.syncGameServerCreatingState(ctx, fixture)

assert.NoError(t, err)
assert.True(t, podCreated, "Pod should have been created")

assert.Equal(t, agonesv1.GameServerStateStarting, gs.Status.State)
assert.True(t, gsUpdated, "GameServer should have been updated")
agtesting.AssertEventContains(t, m.FakeRecorder.Events, "Pod")
})

t.Run("Testing default protocol of static portpolicy", func(t *testing.T) {
c, m := newFakeController()
fixture := &agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"},
Spec: newSingleContainerSpec(), Status: agonesv1.GameServerStatus{State: agonesv1.GameServerStateCreating}}
fixture.Spec.Ports[0].Name = "udp-port"
fixture.Spec.Ports[0].HostPort = 7000
fixture.ApplyDefaults()
podCreated := false
gsUpdated := false

var pod *corev1.Pod
m.KubeClient.AddReactor("create", "pods", func(action k8stesting.Action) (bool, runtime.Object, error) {
podCreated = true
ca := action.(k8stesting.CreateAction)
pod = ca.GetObject().(*corev1.Pod)
assert.True(t, metav1.IsControlledBy(pod, fixture))
return true, pod, nil
})
m.AgonesClient.AddReactor("update", "gameservers", func(action k8stesting.Action) (bool, runtime.Object, error) {
gsUpdated = true
ua := action.(k8stesting.UpdateAction)
gs := ua.GetObject().(*agonesv1.GameServer)
assert.Equal(t, agonesv1.GameServerStateStarting, gs.Status.State)
assert.Len(t, gs.Spec.Ports, 1)
assert.Equal(t, "udp-port", gs.Spec.Ports[0].Name)
assert.Equal(t, corev1.ProtocolUDP, gs.Spec.Ports[0].Protocol)
return true, gs, nil
})

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

gs, err := c.syncGameServerCreatingState(ctx, fixture)

assert.NoError(t, err)
assert.True(t, podCreated, "Pod should have been created")

assert.Equal(t, agonesv1.GameServerStateStarting, gs.Status.State)
assert.True(t, gsUpdated, "GameServer should have been updated")
agtesting.AssertEventContains(t, m.FakeRecorder.Events, "Pod")
})

t.Run("Syncing from Created State, with no issues", func(t *testing.T) {
c, m := newFakeController()
fixture := newFixture()
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/framework/framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ func SendGameServerTCP(gs *agonesv1.GameServer, msg string) (string, error) {
return SendGameServerTCPToPort(gs, p.Name, msg)
}
}
return "", errors.New("No UDP ports")
return "", errors.New("No TCP ports")
}

// SendGameServerTCPToPort sends a message to a gameserver at the named port and returns its reply
Expand Down
99 changes: 99 additions & 0 deletions test/e2e/gameserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,105 @@ func TestGameServerTcpUdpProtocol(t *testing.T) {
assert.Equal(t, "ACK TCP: Hello World !\n", replyTCP)
}

// TestGameServerStaticTcpUdpProtocol checks UDP and TCP connection for TCPUDP Protocol of Static Portpolicy
func TestGameServerStaticTcpUdpProtocol(t *testing.T) {
framework.SkipOnCloudProduct(t, "gke-autopilot", "does not support Static PortPolicy")
t.Parallel()
gs := framework.DefaultGameServer(framework.Namespace)
gs.Spec.Ports[0].PortPolicy = agonesv1.Static
gs.Spec.Ports[0].Protocol = agonesv1.ProtocolTCPUDP
gs.Spec.Ports[0].HostPort = 7000
gs.Spec.Ports[0].Name = "gameserver"
gs.Spec.Template.Spec.Containers[0].Env = []corev1.EnvVar{{Name: "TCP", Value: "TRUE"}}

errs := gs.Validate(agtesting.FakeAPIHooks{})
require.Len(t, errs, 0)

readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
require.NoError(t, err)

tcpPort := readyGs.Spec.Ports[0]
assert.Equal(t, corev1.ProtocolTCP, tcpPort.Protocol)
assert.NotEmpty(t, tcpPort.HostPort)
assert.Equal(t, "gameserver-tcp", tcpPort.Name)

udpPort := readyGs.Spec.Ports[1]
assert.Equal(t, corev1.ProtocolUDP, udpPort.Protocol)
assert.NotEmpty(t, udpPort.HostPort)
assert.Equal(t, "gameserver-udp", udpPort.Name)

assert.Equal(t, tcpPort.HostPort, udpPort.HostPort)

logrus.WithField("name", readyGs.ObjectMeta.Name).Info("GameServer created, sending UDP ping")

replyUDP, err := framework.SendGameServerUDPToPort(t, readyGs, udpPort.Name, "Hello World !")
require.NoError(t, err)
if err != nil {
t.Fatalf("Could not ping UDP GameServer: %v", err)
}

assert.Equal(t, "ACK: Hello World !\n", replyUDP)

logrus.WithField("name", readyGs.ObjectMeta.Name).Info("UDP ping passed, sending TCP ping")

replyTCP, err := e2eframework.SendGameServerTCPToPort(readyGs, tcpPort.Name, "Hello World !")
if err != nil {
t.Fatalf("Could not ping TCP GameServer: %v", err)
}

assert.Equal(t, "ACK TCP: Hello World !\n", replyTCP)
}

// TestGameServerStaticTcpProtocol checks TCP connection for TCP Protocol of Static Portpolicy
func TestGameServerStaticTcpProtocol(t *testing.T) {
framework.SkipOnCloudProduct(t, "gke-autopilot", "does not support Static PortPolicy")
t.Parallel()
gs := framework.DefaultGameServer(framework.Namespace)

gs.Spec.Ports[0].PortPolicy = agonesv1.Static
gs.Spec.Ports[0].Protocol = corev1.ProtocolTCP
gs.Spec.Ports[0].HostPort = 7000
gs.Spec.Template.Spec.Containers[0].Env = []corev1.EnvVar{{Name: "TCP", Value: "TRUE"}}

errs := gs.Validate(agtesting.FakeAPIHooks{})
require.Len(t, errs, 0)

readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
require.NoError(t, err)

logrus.WithField("name", readyGs.ObjectMeta.Name).Info("sending TCP ping")

replyTCP, err := e2eframework.SendGameServerTCP(readyGs, "Hello World !")
require.NoError(t, err)
assert.Equal(t, "ACK TCP: Hello World !\n", replyTCP)

logrus.WithField("name", readyGs.ObjectMeta.Name).Info("TCP ping Passed")
}

// TestGameServerStaticUdpProtocol checks default(UDP) connection of Static Portpolicy
func TestGameServerStaticUdpProtocol(t *testing.T) {
framework.SkipOnCloudProduct(t, "gke-autopilot", "does not support Static PortPolicy")
t.Parallel()
gs := framework.DefaultGameServer(framework.Namespace)

gs.Spec.Ports[0].PortPolicy = agonesv1.Static
gs.Spec.Ports[0].HostPort = 7000

errs := gs.Validate(agtesting.FakeAPIHooks{})
require.Len(t, errs, 0)

readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
require.NoError(t, err)

logrus.WithField("name", readyGs.ObjectMeta.Name).Info("sending UDP ping")

replyTCP, err := framework.SendGameServerUDP(t, readyGs, "Default UDP connection check")
require.NoError(t, err)
assert.Equal(t, "ACK: Default UDP connection check\n", replyTCP)

logrus.WithField("name", readyGs.ObjectMeta.Name).Info("UDP ping Passed")
}

func TestGameServerWithoutPort(t *testing.T) {
t.Parallel()
gs := framework.DefaultGameServer(framework.Namespace)
Expand Down

0 comments on commit 9597660

Please sign in to comment.