agones.dev/agones@v1.54.0/test/e2e/gameserver_test.go (about)

     1  // Copyright 2018 Google LLC All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package e2e
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"fmt"
    21  	"net"
    22  	"os"
    23  	"os/exec"
    24  	"sort"
    25  	"strings"
    26  	"sync"
    27  	"sync/atomic"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/sirupsen/logrus"
    32  	"github.com/stretchr/testify/assert"
    33  	"github.com/stretchr/testify/require"
    34  	corev1 "k8s.io/api/core/v1"
    35  	policyv1 "k8s.io/api/policy/v1"
    36  	k8serrors "k8s.io/apimachinery/pkg/api/errors"
    37  	"k8s.io/apimachinery/pkg/api/resource"
    38  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    39  	"k8s.io/apimachinery/pkg/types"
    40  	"k8s.io/apimachinery/pkg/util/rand"
    41  	"k8s.io/apimachinery/pkg/util/wait"
    42  
    43  	agonesv1 "agones.dev/agones/pkg/apis/agones/v1"
    44  	allocationv1 "agones.dev/agones/pkg/apis/allocation/v1"
    45  	agtesting "agones.dev/agones/pkg/testing"
    46  	"agones.dev/agones/pkg/util/runtime"
    47  	e2eframework "agones.dev/agones/test/e2e/framework"
    48  )
    49  
    50  const (
    51  	fakeIPAddress = "192.1.1.2"
    52  )
    53  
    54  func TestCreateConnect(t *testing.T) {
    55  	t.Parallel()
    56  	gs := framework.DefaultGameServer(framework.Namespace)
    57  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
    58  	if err != nil {
    59  		t.Fatalf("Could not get a GameServer ready: %v", err)
    60  	}
    61  	assert.Equal(t, len(readyGs.Status.Ports), 1)
    62  	assert.NotEmpty(t, readyGs.Status.Ports[0].Port)
    63  	assert.NotEmpty(t, readyGs.Status.Address)
    64  	assert.NotEmpty(t, readyGs.Status.Addresses)
    65  
    66  	var hasPodIPAddress bool
    67  	for i, addr := range readyGs.Status.Addresses {
    68  		if addr.Type == agonesv1.NodePodIP {
    69  			assert.NotEmpty(t, readyGs.Status.Addresses[i].Address)
    70  			hasPodIPAddress = true
    71  		}
    72  	}
    73  	assert.True(t, hasPodIPAddress)
    74  
    75  	assert.NotEmpty(t, readyGs.Status.NodeName)
    76  	assert.Equal(t, readyGs.Status.State, agonesv1.GameServerStateReady)
    77  
    78  	reply, err := framework.SendGameServerUDP(t, readyGs, "Hello World !")
    79  	if err != nil {
    80  		t.Fatalf("Could ping GameServer: %v", err)
    81  	}
    82  
    83  	assert.Equal(t, "ACK: Hello World !\n", reply)
    84  }
    85  
    86  func TestHostName(t *testing.T) {
    87  	t.Parallel()
    88  
    89  	pods := framework.KubeClient.CoreV1().Pods(framework.Namespace)
    90  
    91  	fixtures := map[string]struct {
    92  		setup func(gs *agonesv1.GameServer)
    93  		test  func(gs *agonesv1.GameServer, pod *corev1.Pod)
    94  	}{
    95  		"standard hostname": {
    96  			setup: func(_ *agonesv1.GameServer) {},
    97  			test: func(gs *agonesv1.GameServer, pod *corev1.Pod) {
    98  				assert.Equal(t, gs.ObjectMeta.Name, pod.Spec.Hostname)
    99  			},
   100  		},
   101  		"a . in the name": {
   102  			setup: func(gs *agonesv1.GameServer) {
   103  				gs.ObjectMeta.GenerateName = "game-server-1.0-"
   104  			},
   105  			test: func(_ *agonesv1.GameServer, pod *corev1.Pod) {
   106  				expected := "game-server-1-0-"
   107  				// since it's a generated name, we just check the beginning.
   108  				assert.Equal(t, expected, pod.Spec.Hostname[:len(expected)])
   109  			},
   110  		},
   111  		// generated name will automatically truncate to 63 chars.
   112  		"generated with > 63 chars": {
   113  			setup: func(gs *agonesv1.GameServer) {
   114  				gs.ObjectMeta.GenerateName = "game-server-" + strings.Repeat("n", 100)
   115  			},
   116  			test: func(gs *agonesv1.GameServer, pod *corev1.Pod) {
   117  				assert.Equal(t, gs.ObjectMeta.Name, pod.Spec.Hostname)
   118  			},
   119  		},
   120  		// Note: no need to test for a gs.ObjectMeta.Name > 63 chars, as it will be rejected as invalid
   121  	}
   122  
   123  	for k, v := range fixtures {
   124  		t.Run(k, func(t *testing.T) {
   125  			gs := framework.DefaultGameServer(framework.Namespace)
   126  			gs.Spec.Template.Spec.Subdomain = "default"
   127  			v.setup(gs)
   128  			readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   129  			require.NoError(t, err)
   130  			pod, err := pods.Get(context.Background(), readyGs.ObjectMeta.Name, metav1.GetOptions{})
   131  			require.NoError(t, err)
   132  			v.test(readyGs, pod)
   133  		})
   134  	}
   135  }
   136  
   137  // nolint:dupl
   138  func TestSDKSetLabel(t *testing.T) {
   139  	t.Parallel()
   140  	ctx := context.Background()
   141  	gs := framework.DefaultGameServer(framework.Namespace)
   142  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   143  	if err != nil {
   144  		t.Fatalf("Could not get a GameServer ready: %v", err)
   145  	}
   146  
   147  	assert.Equal(t, readyGs.Status.State, agonesv1.GameServerStateReady)
   148  	reply, err := framework.SendGameServerUDP(t, readyGs, "LABEL")
   149  	if err != nil {
   150  		t.Fatalf("Could ping GameServer: %v", err)
   151  	}
   152  
   153  	assert.Equal(t, "ACK: LABEL\n", reply)
   154  
   155  	// the label is set in a queue, so it may take a moment
   156  	err = wait.PollUntilContextTimeout(ctx, time.Second, 10*time.Second, true, func(ctx context.Context) (bool, error) {
   157  		gs, err = framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Get(ctx, readyGs.ObjectMeta.Name, metav1.GetOptions{})
   158  		if err != nil {
   159  			return true, err
   160  		}
   161  		return gs.ObjectMeta.Labels != nil, nil
   162  	})
   163  
   164  	if assert.NoError(t, err) {
   165  		assert.NotEmpty(t, gs.ObjectMeta.Labels["agones.dev/sdk-timestamp"])
   166  	}
   167  }
   168  
   169  func TestHealthCheckDisable(t *testing.T) {
   170  	t.Parallel()
   171  	ctx := context.Background()
   172  	gs := framework.DefaultGameServer(framework.Namespace)
   173  	gs.Spec.Health = agonesv1.Health{
   174  		Disabled:            true,
   175  		FailureThreshold:    1,
   176  		InitialDelaySeconds: 1,
   177  		PeriodSeconds:       1,
   178  	}
   179  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   180  	if err != nil {
   181  		t.Fatalf("Could not get a GameServer ready: %v", err)
   182  	}
   183  	defer framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Delete(ctx, readyGs.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint: errcheck
   184  
   185  	_, err = framework.SendGameServerUDP(t, readyGs, "UNHEALTHY")
   186  	if err != nil {
   187  		t.Fatalf("Could not ping GameServer: %v", err)
   188  	}
   189  
   190  	time.Sleep(10 * time.Second)
   191  
   192  	gs, err = framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Get(ctx, readyGs.ObjectMeta.Name, metav1.GetOptions{})
   193  	if err != nil {
   194  		assert.FailNow(t, "gameserver get failed", err.Error())
   195  	}
   196  
   197  	assert.Equal(t, agonesv1.GameServerStateReady, gs.Status.State)
   198  }
   199  
   200  // nolint:dupl
   201  func TestSDKSetAnnotation(t *testing.T) {
   202  	t.Parallel()
   203  	ctx := context.Background()
   204  	gs := framework.DefaultGameServer(framework.Namespace)
   205  	annotation := "agones.dev/sdk-timestamp"
   206  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   207  	if err != nil {
   208  		t.Fatalf("Could not get a GameServer ready: %v", err)
   209  	}
   210  	defer framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Delete(ctx, readyGs.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint: errcheck
   211  
   212  	assert.Equal(t, readyGs.Status.State, agonesv1.GameServerStateReady)
   213  	reply, err := framework.SendGameServerUDP(t, readyGs, "ANNOTATION")
   214  	if err != nil {
   215  		t.Fatalf("Could ping GameServer: %v", err)
   216  	}
   217  
   218  	assert.Equal(t, "ACK: ANNOTATION\n", reply)
   219  
   220  	// the label is set in a queue, so it may take a moment
   221  	err = wait.PollUntilContextTimeout(context.Background(), time.Second, time.Minute, true, func(ctx context.Context) (bool, error) {
   222  		gs, err = framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Get(ctx, readyGs.ObjectMeta.Name, metav1.GetOptions{})
   223  		if err != nil {
   224  			return true, err
   225  		}
   226  
   227  		_, ok := gs.ObjectMeta.Annotations[annotation]
   228  		return ok, nil
   229  	})
   230  
   231  	logrus.WithField("annotations", gs.ObjectMeta.Annotations).Info("annotation information")
   232  
   233  	if !assert.Nil(t, err) {
   234  		assert.FailNow(t, "error waiting on annotation to be set")
   235  	}
   236  	assert.NotEmpty(t, gs.ObjectMeta.Annotations[annotation])
   237  	assert.NotEmpty(t, gs.ObjectMeta.Annotations[agonesv1.VersionAnnotation])
   238  }
   239  
   240  func TestUnhealthyGameServerAfterHealthCheckFail(t *testing.T) {
   241  	t.Parallel()
   242  	gs := framework.DefaultGameServer(framework.Namespace)
   243  	gs.Spec.Health.FailureThreshold = 1
   244  
   245  	gs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   246  	if err != nil {
   247  		assert.FailNow(t, "Failed to create a gameserver", err.Error())
   248  	}
   249  
   250  	reply, err := framework.SendGameServerUDP(t, gs, "UNHEALTHY")
   251  	if err != nil {
   252  		assert.FailNow(t, "Failed to send a message to a gameserver", err.Error())
   253  	}
   254  	assert.Equal(t, "ACK: UNHEALTHY\n", reply)
   255  
   256  	_, err = framework.WaitForGameServerState(t, gs, agonesv1.GameServerStateUnhealthy, time.Minute)
   257  	assert.NoError(t, err)
   258  }
   259  
   260  func TestUnhealthyGameServersWithoutFreePorts(t *testing.T) {
   261  	framework.SkipOnCloudProduct(t, "gke-autopilot", "does not support Static PortPolicy")
   262  	if runtime.FeatureEnabled(runtime.FeatureSidecarContainers) {
   263  		t.SkipNow()
   264  	}
   265  
   266  	t.Parallel()
   267  	ctx := context.Background()
   268  	nodes, err := framework.KubeClient.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
   269  	if err != nil {
   270  		assert.FailNow(t, "Failed to list nodes", err.Error())
   271  	}
   272  	assert.True(t, len(nodes.Items) > 0)
   273  
   274  	template := framework.DefaultGameServer(framework.Namespace)
   275  	// choose port out of the minport/maxport range
   276  	// also rand it, just in case there are still previous static GameServers floating around.
   277  	template.Spec.Ports[0].HostPort = int32(rand.IntnRange(4000, 5000))
   278  	template.Spec.Ports[0].PortPolicy = agonesv1.Static
   279  
   280  	gameServers := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace)
   281  	// one successful static port GameServer
   282  	gs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, template.DeepCopy())
   283  	require.NoError(t, err)
   284  
   285  	// now let's create the same one, but this time, require it be on the same node.
   286  	newGs := template.DeepCopy()
   287  
   288  	if newGs.Spec.Template.Spec.NodeSelector == nil {
   289  		newGs.Spec.Template.Spec.NodeSelector = map[string]string{}
   290  	}
   291  	newGs.Spec.Template.Spec.NodeSelector["kubernetes.io/hostname"] = gs.Status.NodeName
   292  	newGs, err = gameServers.Create(ctx, newGs, metav1.CreateOptions{})
   293  	require.NoError(t, err)
   294  
   295  	_, err = framework.WaitForGameServerState(t, newGs, agonesv1.GameServerStateUnhealthy, 5*time.Minute)
   296  	assert.NoError(t, err)
   297  }
   298  
   299  func TestGameServerUnhealthyAfterDeletingPod(t *testing.T) {
   300  	t.Parallel()
   301  	ctx := context.Background()
   302  	gs := framework.DefaultGameServer(framework.Namespace)
   303  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   304  	if err != nil {
   305  		t.Fatalf("Could not get a GameServer ready: %v", err)
   306  	}
   307  
   308  	logrus.WithField("gsKey", readyGs.ObjectMeta.Name).Info("GameServer Ready")
   309  
   310  	gsClient := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace)
   311  	podClient := framework.KubeClient.CoreV1().Pods(framework.Namespace)
   312  
   313  	defer gsClient.Delete(ctx, readyGs.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint: errcheck
   314  
   315  	pod, err := podClient.Get(ctx, readyGs.ObjectMeta.Name, metav1.GetOptions{})
   316  	require.NoError(t, err)
   317  	require.True(t, metav1.IsControlledBy(pod, readyGs))
   318  
   319  	err = podClient.Delete(ctx, pod.ObjectMeta.Name, metav1.DeleteOptions{})
   320  	require.NoError(t, err)
   321  
   322  	_, err = framework.WaitForGameServerState(t, readyGs, agonesv1.GameServerStateUnhealthy, 3*time.Minute)
   323  	require.NoError(t, err)
   324  }
   325  
   326  func TestGameServerRestartBeforeReadyCrash(t *testing.T) {
   327  	if runtime.FeatureEnabled(runtime.FeatureSidecarContainers) {
   328  		t.SkipNow()
   329  	}
   330  
   331  	t.Parallel()
   332  	ctx := context.Background()
   333  	logger := e2eframework.TestLogger(t)
   334  
   335  	gs := framework.DefaultGameServer(framework.Namespace)
   336  	// give some buffer with gameservers crashing and coming back
   337  	gs.Spec.Health.PeriodSeconds = 60 * 60
   338  	gs.Spec.Template.Spec.Containers[0].Env = append(gs.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{Name: "READY", Value: "FALSE"})
   339  	gsClient := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace)
   340  	newGs, err := gsClient.Create(ctx, gs, metav1.CreateOptions{})
   341  	if err != nil {
   342  		assert.Fail(t, "could not create the gameserver", err.Error())
   343  	}
   344  	defer gsClient.Delete(ctx, newGs.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint: errcheck
   345  
   346  	logger.Info("Waiting for us to have an address to send things to")
   347  	newGs, err = framework.WaitForGameServerState(t, newGs, agonesv1.GameServerStateScheduled, framework.WaitForState)
   348  	if err != nil {
   349  		assert.FailNow(t, "Failed schedule a pod", err.Error())
   350  	}
   351  
   352  	logger.WithField("gs", newGs.ObjectMeta.Name).Info("GameServer created")
   353  
   354  	address := fmt.Sprintf("%s:%d", newGs.Status.Address, newGs.Status.Ports[0].Port)
   355  	logger.WithField("address", address).Info("Dialing UDP message to address")
   356  
   357  	messageAndWait := func(gs *agonesv1.GameServer, msg string, check func(gs *agonesv1.GameServer, pod *corev1.Pod) bool) error {
   358  		return wait.PollUntilContextTimeout(context.Background(), 200*time.Millisecond, 3*time.Minute, true, func(ctx context.Context) (bool, error) {
   359  			gs, err := gsClient.Get(ctx, gs.ObjectMeta.Name, metav1.GetOptions{})
   360  			if err != nil {
   361  				logger.WithError(err).Warn("could not get gameserver")
   362  				return true, err
   363  			}
   364  			pod, err := framework.KubeClient.CoreV1().Pods(framework.Namespace).Get(ctx, newGs.ObjectMeta.Name, metav1.GetOptions{})
   365  			if err != nil {
   366  				logger.WithError(err).Warn("could not get pod for gameserver")
   367  				return true, err
   368  			}
   369  
   370  			if check(gs, pod) {
   371  				return true, nil
   372  			}
   373  
   374  			// create a connection each time, as weird stuff happens if the receiver isn't up and running.
   375  			conn, err := net.Dial("udp", address)
   376  			if err != nil {
   377  				logger.WithError(err).Warn("could not create connection")
   378  				return true, err
   379  			}
   380  			defer conn.Close() // nolint: errcheck
   381  			// doing this last, so that there is a short delay between the msg being sent, and the check.
   382  			logger.WithField("gs", gs.ObjectMeta.Name).WithField("msg", msg).
   383  				WithField("state", gs.Status.State).Info("sending message")
   384  			if _, err = conn.Write([]byte(msg)); err != nil {
   385  				logger.WithError(err).WithField("gs", gs.ObjectMeta.Name).
   386  					WithField("state", gs.Status.State).Info("error sending packet")
   387  			}
   388  			return false, nil
   389  		})
   390  	}
   391  
   392  	logger.Info("crashing, and waiting to see restart")
   393  	err = messageAndWait(newGs, "CRASH", func(_ *agonesv1.GameServer, pod *corev1.Pod) bool {
   394  		for _, c := range pod.Status.ContainerStatuses {
   395  			if c.Name == newGs.Spec.Container && c.RestartCount > 0 {
   396  				logger.Info("successfully crashed. Moving on!")
   397  				return true
   398  			}
   399  		}
   400  		return false
   401  	})
   402  	assert.NoError(t, err)
   403  
   404  	// check that the GameServer is not in an unhealthy state. If it does happen, it should happen pretty quick.
   405  	// We wait an extra 5s to close the kubelet race in #2445.
   406  	newGs, err = framework.WaitForGameServerState(t, newGs, agonesv1.GameServerStateUnhealthy, 10*time.Second)
   407  	// should be an error, as the state should not occur
   408  	if !assert.Error(t, err) {
   409  		assert.FailNow(t, "GameServer should not be Unhealthy")
   410  	}
   411  	assert.Contains(t, err.Error(), "waiting for GameServer")
   412  
   413  	// ping READY until it doesn't fail anymore - since it may take a while
   414  	// for this to come back up -- or we could get a delayed CRASH, so we have to
   415  	// wait for the process to restart again to fire the SDK.Ready()
   416  	logger.Info("marking GameServer as ready")
   417  	err = messageAndWait(newGs, "READY", func(gs *agonesv1.GameServer, _ *corev1.Pod) bool {
   418  		if gs.Status.State == agonesv1.GameServerStateReady {
   419  			logger.Info("ready! Moving On!")
   420  			return true
   421  		}
   422  		return false
   423  	})
   424  	assert.NoError(t, err)
   425  
   426  	// now crash, should be unhealthy, since it's after being Ready
   427  	logger.Info("crashing again, should be unhealthy")
   428  	// retry on crash, as with the restarts, sometimes Go takes a moment to send this through.
   429  	err = messageAndWait(newGs, "CRASH", func(gs *agonesv1.GameServer, _ *corev1.Pod) bool {
   430  		logger.WithField("gs", gs.ObjectMeta.Name).WithField("state", gs.Status.State).
   431  			Info("checking final crash state")
   432  		if gs.Status.State == agonesv1.GameServerStateUnhealthy {
   433  			logger.Info("Unhealthy! We are done!")
   434  			return true
   435  		}
   436  		return false
   437  	})
   438  	assert.NoError(t, err)
   439  }
   440  
   441  func TestGameServerUnhealthyAfterReadyCrash(t *testing.T) {
   442  	t.Parallel()
   443  	ctx := context.Background()
   444  	log := e2eframework.TestLogger(t)
   445  
   446  	gs := framework.DefaultGameServer(framework.Namespace)
   447  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   448  	if err != nil {
   449  		t.Fatalf("Could not get a GameServer ready: %v", err)
   450  	}
   451  
   452  	log.WithField("gs", readyGs.ObjectMeta.Name).Info("GameServer created")
   453  
   454  	gsClient := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace)
   455  	defer gsClient.Delete(ctx, readyGs.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint: errcheck
   456  
   457  	address := fmt.Sprintf("%s:%d", readyGs.Status.Address, readyGs.Status.Ports[0].Port)
   458  
   459  	// keep crashing, until we move to Unhealthy. Solves potential issues with controller Informer cache
   460  	// race conditions in which it has yet to see a GameServer is Ready before the crash.
   461  	var stop int32
   462  	defer func() {
   463  		atomic.StoreInt32(&stop, 1)
   464  	}()
   465  	go func() {
   466  		for {
   467  			if atomic.LoadInt32(&stop) > 0 {
   468  				log.Info("UDP Crash stop signal received. Stopping.")
   469  				return
   470  			}
   471  			var writeErr error
   472  			func() {
   473  				conn, err := net.Dial("udp", address)
   474  				assert.NoError(t, err)
   475  				defer conn.Close() // nolint: errcheck
   476  				_, writeErr = conn.Write([]byte("CRASH"))
   477  			}()
   478  			if writeErr != nil {
   479  				log.WithError(err).Warn("error sending udp packet. Stopping.")
   480  				return
   481  			}
   482  			log.WithField("address", address).Info("sent UDP packet")
   483  			time.Sleep(5 * time.Second)
   484  		}
   485  	}()
   486  	_, err = framework.WaitForGameServerState(t, readyGs, agonesv1.GameServerStateUnhealthy, 3*time.Minute)
   487  	assert.NoError(t, err)
   488  }
   489  
   490  func TestGameServerPodCompletedAfterCleanExit(t *testing.T) {
   491  	if !runtime.FeatureEnabled(runtime.FeatureSidecarContainers) {
   492  		t.SkipNow()
   493  	}
   494  
   495  	t.Parallel()
   496  	ctx, cancel := context.WithCancel(context.Background())
   497  	defer cancel()
   498  	log := e2eframework.TestLogger(t)
   499  
   500  	gs := framework.DefaultGameServer(framework.Namespace)
   501  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   502  	if err != nil {
   503  		t.Fatalf("Could not get a GameServer ready: %v", err)
   504  	}
   505  
   506  	address := fmt.Sprintf("%s:%d", readyGs.Status.Address, readyGs.Status.Ports[0].Port)
   507  	conn, err := net.Dial("udp", address)
   508  	mtx := &sync.Mutex{}
   509  	require.NoError(t, err)
   510  	defer func() {
   511  		mtx.Lock()
   512  		defer mtx.Unlock()
   513  		if conn != nil {
   514  			err = conn.Close()
   515  			if err != nil {
   516  				log.WithError(err).Warn("error closing udp connection")
   517  			}
   518  		}
   519  	}()
   520  
   521  	go func() {
   522  		for {
   523  			select {
   524  			case <-ctx.Done():
   525  				return
   526  			default:
   527  				mtx.Lock()
   528  				log.Info("Sending CRASH 0")
   529  				_, err := conn.Write([]byte("CRASH 0"))
   530  				mtx.Unlock()
   531  				if err != nil {
   532  					log.WithError(err).Warn("error sending udp packet. Stopping.")
   533  					return
   534  				}
   535  			}
   536  			time.Sleep(5 * time.Second)
   537  		}
   538  	}()
   539  
   540  	err = wait.PollUntilContextTimeout(ctx, 3*time.Second, 3*time.Minute, true, func(ctx context.Context) (bool, error) {
   541  		gs, err = framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Get(ctx, readyGs.ObjectMeta.Name, metav1.GetOptions{})
   542  		log.WithField("gs", readyGs.ObjectMeta.Name).WithField("state", gs.Status.State).WithError(err).Info("checking if GameServer exists")
   543  		if k8serrors.IsNotFound(err) {
   544  			return true, nil
   545  		}
   546  		return false, err
   547  	})
   548  	require.NoError(t, err)
   549  }
   550  
   551  func TestDevelopmentGameServerLifecycle(t *testing.T) {
   552  	t.Parallel()
   553  	ctx := context.Background()
   554  
   555  	labels := map[string]string{"development": "true"}
   556  	gs := &agonesv1.GameServer{
   557  		ObjectMeta: metav1.ObjectMeta{
   558  			GenerateName: "udp-server",
   559  			Namespace:    framework.Namespace,
   560  			Annotations:  map[string]string{agonesv1.DevAddressAnnotation: fakeIPAddress},
   561  			Labels:       labels,
   562  		},
   563  		Spec: agonesv1.GameServerSpec{
   564  			Container: "udp-server",
   565  			Ports: []agonesv1.GameServerPort{{
   566  				ContainerPort: 7654,
   567  				HostPort:      7654,
   568  				Name:          "gameport",
   569  				PortPolicy:    agonesv1.Static,
   570  				Protocol:      corev1.ProtocolUDP,
   571  			}},
   572  			Template: corev1.PodTemplateSpec{
   573  				Spec: corev1.PodSpec{
   574  					Containers: []corev1.Container{{
   575  						Name:  "placebo",
   576  						Image: "this is ignored",
   577  					}},
   578  				},
   579  			},
   580  		},
   581  	}
   582  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs.DeepCopy())
   583  	if err != nil {
   584  		t.Fatalf("Could not get a GameServer ready: %v", err)
   585  	}
   586  	require.Equal(t, agonesv1.GameServerStateReady, readyGs.Status.State)
   587  
   588  	// Set dev GS into RequestReady and confirm it goes back to Ready.
   589  	gsCopy := readyGs.DeepCopy()
   590  	gsCopy.Status.State = agonesv1.GameServerStateRequestReady
   591  	reqReadyGs, err := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Update(ctx, gsCopy, metav1.UpdateOptions{})
   592  	require.NoError(t, err)
   593  	require.Equal(t, agonesv1.GameServerStateRequestReady, reqReadyGs.Status.State)
   594  
   595  	readyGs, err = framework.WaitForGameServerState(t, reqReadyGs, agonesv1.GameServerStateReady, framework.WaitForState)
   596  	if err != nil {
   597  		t.Fatalf("Could not get a GameServer ready from request ready: %v", err)
   598  	}
   599  	require.Equal(t, agonesv1.GameServerStateReady, readyGs.Status.State)
   600  
   601  	// confirm delete works, because if the finalisers don't get removed, this won't work.
   602  	err = framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Delete(ctx, readyGs.ObjectMeta.Name, metav1.DeleteOptions{})
   603  	require.NoError(t, err)
   604  
   605  	err = wait.PollUntilContextTimeout(context.Background(), time.Second, time.Minute, true, func(ctx context.Context) (bool, error) {
   606  		_, err = framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Get(ctx, readyGs.ObjectMeta.Name, metav1.GetOptions{})
   607  		if k8serrors.IsNotFound(err) {
   608  			return true, nil
   609  		}
   610  
   611  		return false, err
   612  	})
   613  	require.NoError(t, err)
   614  
   615  	// let's make sure we can allocate a dev gameserver
   616  	readyGs, err = framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs.DeepCopy())
   617  	require.NoError(t, err)
   618  
   619  	gsa := &allocationv1.GameServerAllocation{
   620  		Spec: allocationv1.GameServerAllocationSpec{
   621  			Selectors: []allocationv1.GameServerSelector{{
   622  				LabelSelector: metav1.LabelSelector{MatchLabels: labels},
   623  			}},
   624  		},
   625  	}
   626  	gsa, err = framework.AgonesClient.AllocationV1().GameServerAllocations(framework.Namespace).Create(ctx, gsa, metav1.CreateOptions{})
   627  	require.NoError(t, err)
   628  
   629  	require.Equal(t, readyGs.ObjectMeta.Name, gsa.Status.GameServerName)
   630  
   631  	_, err = framework.WaitForGameServerState(t, readyGs, agonesv1.GameServerStateAllocated, time.Minute)
   632  	require.NoError(t, err)
   633  
   634  	// Also confirm that delete works for Allocated state, it should be fine but let's be sure.
   635  	err = framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Delete(ctx, readyGs.ObjectMeta.Name, metav1.DeleteOptions{})
   636  	require.NoError(t, err)
   637  
   638  	err = wait.PollUntilContextTimeout(context.Background(), time.Second, time.Minute, true, func(ctx context.Context) (bool, error) {
   639  		_, err = framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Get(ctx, readyGs.ObjectMeta.Name, metav1.GetOptions{})
   640  		if k8serrors.IsNotFound(err) {
   641  			return true, nil
   642  		}
   643  
   644  		return false, err
   645  	})
   646  	require.NoError(t, err)
   647  }
   648  
   649  func TestGameServerSelfAllocate(t *testing.T) {
   650  	t.Parallel()
   651  	ctx := context.Background()
   652  	gs := framework.DefaultGameServer(framework.Namespace)
   653  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   654  	if err != nil {
   655  		t.Fatalf("Could not get a GameServer ready: %v", err)
   656  	}
   657  	defer framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Delete(ctx, readyGs.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint: errcheck
   658  
   659  	assert.Equal(t, readyGs.Status.State, agonesv1.GameServerStateReady)
   660  	reply, err := framework.SendGameServerUDP(t, readyGs, "ALLOCATE")
   661  	if err != nil {
   662  		t.Fatalf("Could not message GameServer: %v", err)
   663  	}
   664  
   665  	assert.Equal(t, "ACK: ALLOCATE\n", reply)
   666  	gs, err = framework.WaitForGameServerState(t, readyGs, agonesv1.GameServerStateAllocated, time.Minute)
   667  	if assert.NoError(t, err) {
   668  		assert.Equal(t, agonesv1.GameServerStateAllocated, gs.Status.State)
   669  	}
   670  }
   671  
   672  func TestGameServerReadyAllocateReady(t *testing.T) {
   673  	t.Parallel()
   674  	ctx := context.Background()
   675  	logger := e2eframework.TestLogger(t)
   676  
   677  	gs := framework.DefaultGameServer(framework.Namespace)
   678  
   679  	logger.Info("Moving to Ready")
   680  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   681  	require.NoError(t, err, "Could not get a GameServer ready")
   682  	logger = logger.WithField("gs", readyGs.ObjectMeta.Name)
   683  
   684  	defer framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Delete(ctx, readyGs.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint: errcheck
   685  
   686  	require.Equal(t, readyGs.Status.State, agonesv1.GameServerStateReady)
   687  
   688  	logger.Info("Moving to Allocated")
   689  	reply, err := framework.SendGameServerUDP(t, readyGs, "ALLOCATE")
   690  	require.NoError(t, err, "Could not message GameServer")
   691  
   692  	require.Equal(t, "ACK: ALLOCATE\n", reply)
   693  	gs, err = framework.WaitForGameServerState(t, readyGs, agonesv1.GameServerStateAllocated, time.Minute)
   694  	require.NoError(t, err)
   695  	require.Equal(t, agonesv1.GameServerStateAllocated, gs.Status.State)
   696  
   697  	logger.Info("Moving to Ready again")
   698  	reply, err = framework.SendGameServerUDP(t, readyGs, "READY")
   699  	require.NoError(t, err, "Could not message GameServer")
   700  	require.Equal(t, "ACK: READY\n", reply)
   701  	gs, err = framework.WaitForGameServerState(t, gs, agonesv1.GameServerStateReady, time.Minute)
   702  	require.NoError(t, err)
   703  	require.Equal(t, agonesv1.GameServerStateReady, gs.Status.State)
   704  }
   705  
   706  func TestGameServerWithPortsMappedToMultipleContainers(t *testing.T) {
   707  	t.Parallel()
   708  	ctx := context.Background()
   709  
   710  	firstContainerName := "udp-server"
   711  	secondContainerName := "second-udp-server"
   712  	firstPort := "gameport"
   713  	secondPort := "second-gameport"
   714  	gs := &agonesv1.GameServer{
   715  		ObjectMeta: metav1.ObjectMeta{GenerateName: "udp-server", Namespace: framework.Namespace},
   716  		Spec: agonesv1.GameServerSpec{
   717  			Container: firstContainerName,
   718  			Ports: []agonesv1.GameServerPort{{
   719  				ContainerPort: 7654,
   720  				Name:          firstPort,
   721  				PortPolicy:    agonesv1.Dynamic,
   722  				Protocol:      corev1.ProtocolUDP,
   723  			}, {
   724  				ContainerPort: 5000,
   725  				Name:          secondPort,
   726  				PortPolicy:    agonesv1.Dynamic,
   727  				Protocol:      corev1.ProtocolUDP,
   728  				Container:     &secondContainerName,
   729  			}},
   730  			Template: corev1.PodTemplateSpec{
   731  				Spec: corev1.PodSpec{
   732  					Containers: []corev1.Container{
   733  						{
   734  							Name:            firstContainerName,
   735  							Image:           framework.GameServerImage,
   736  							ImagePullPolicy: corev1.PullIfNotPresent,
   737  						},
   738  						{
   739  							Name:            secondContainerName,
   740  							Image:           framework.GameServerImage,
   741  							ImagePullPolicy: corev1.PullIfNotPresent,
   742  							Args:            []string{"-port", "5000"},
   743  						},
   744  					},
   745  				},
   746  			},
   747  		},
   748  	}
   749  
   750  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   751  	if err != nil {
   752  		t.Fatalf("Could not get a GameServer ready: %v", err)
   753  	}
   754  	defer framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Delete(ctx, readyGs.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint: errcheck
   755  	assert.Equal(t, readyGs.Status.State, agonesv1.GameServerStateReady)
   756  
   757  	interval := 2 * time.Second
   758  	timeOut := 60 * time.Second
   759  
   760  	expectedMsg1 := "Ping 1"
   761  	errPoll := wait.PollUntilContextTimeout(context.Background(), interval, timeOut, true, func(_ context.Context) (done bool, err error) {
   762  		res, err := framework.SendGameServerUDPToPort(t, readyGs, firstPort, expectedMsg1)
   763  		if err != nil {
   764  			t.Logf("Could not message GameServer on %s: %v. Will try again...", firstPort, err)
   765  		}
   766  		return err == nil && strings.Contains(res, expectedMsg1), nil
   767  	})
   768  	if errPoll != nil {
   769  		assert.FailNow(t, errPoll.Error(), "expected no errors after polling a port: %s", firstPort)
   770  	}
   771  
   772  	expectedMsg2 := "Ping 2"
   773  	errPoll = wait.PollUntilContextTimeout(context.Background(), interval, timeOut, true, func(_ context.Context) (done bool, err error) {
   774  		res, err := framework.SendGameServerUDPToPort(t, readyGs, secondPort, expectedMsg2)
   775  		if err != nil {
   776  			t.Logf("Could not message GameServer on %s: %v. Will try again...", secondPort, err)
   777  		}
   778  		return err == nil && strings.Contains(res, expectedMsg2), nil
   779  	})
   780  
   781  	assert.NoError(t, errPoll, "expected no errors after polling a port: %s", secondPort)
   782  }
   783  
   784  func TestGameServerReserve(t *testing.T) {
   785  	t.Parallel()
   786  	ctx := context.Background()
   787  
   788  	// We are deliberately not trying to test the transition between Reserved -> Ready.
   789  	//
   790  	// We have found that trying to catch the GameServer in the Reserved state can be flaky,
   791  	// as we can't control the speed in which the Kubernetes API is going to reply to request,
   792  	// and we could sometimes miss when the GameServer is in the Reserved State before it goes to Ready.
   793  	//
   794  	// Therefore we are going to test for concrete states that we don't need to catch while
   795  	// in a transitive state.
   796  
   797  	gs := framework.DefaultGameServer(framework.Namespace)
   798  	gs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   799  	if err != nil {
   800  		assert.FailNow(t, "Could not get a GameServer ready", err.Error())
   801  	}
   802  	defer framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Delete(ctx, gs.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint: errcheck
   803  	assert.Equal(t, gs.Status.State, agonesv1.GameServerStateReady)
   804  
   805  	reply, err := framework.SendGameServerUDP(t, gs, "RESERVE 0")
   806  	if err != nil {
   807  		assert.FailNow(t, "Could not message GameServer", err.Error())
   808  	}
   809  	assert.Equal(t, "ACK: RESERVE 0\n", reply)
   810  
   811  	gs, err = framework.WaitForGameServerState(t, gs, agonesv1.GameServerStateReserved, 3*time.Minute)
   812  	if err != nil {
   813  		assert.FailNow(t, "Time out on waiting for gs in a Reserved state", err.Error())
   814  	}
   815  
   816  	reply, err = framework.SendGameServerUDP(t, gs, "ALLOCATE")
   817  	if err != nil {
   818  		assert.FailNow(t, "Could not message GameServer", err.Error())
   819  	}
   820  	assert.Equal(t, "ACK: ALLOCATE\n", reply)
   821  
   822  	// put it in a totally different state, just to reset things.
   823  	gs, err = framework.WaitForGameServerState(t, gs, agonesv1.GameServerStateAllocated, 3*time.Minute)
   824  	if err != nil {
   825  		assert.FailNow(t, "Time out on waiting for gs in an Allocated state", err.Error())
   826  	}
   827  
   828  	reply, err = framework.SendGameServerUDP(t, gs, "RESERVE 5s")
   829  	if err != nil {
   830  		assert.FailNow(t, "Could not message GameServer", err.Error())
   831  	}
   832  	assert.Equal(t, "ACK: RESERVE 5s\n", reply)
   833  
   834  	// sleep, since we're going to wait for the Ready response.
   835  	time.Sleep(5 * time.Second)
   836  	_, err = framework.WaitForGameServerState(t, gs, agonesv1.GameServerStateReady, 3*time.Minute)
   837  	assert.NoError(t, err)
   838  }
   839  
   840  func TestGameServerShutdown(t *testing.T) {
   841  	t.Parallel()
   842  	ctx := context.Background()
   843  	gs := framework.DefaultGameServer(framework.Namespace)
   844  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   845  	if err != nil {
   846  		t.Fatalf("Could not get a GameServer ready: %v", err)
   847  	}
   848  	assert.Equal(t, readyGs.Status.State, agonesv1.GameServerStateReady)
   849  
   850  	reply, err := framework.SendGameServerUDP(t, readyGs, "EXIT")
   851  	if err != nil {
   852  		t.Fatalf("Could not message GameServer: %v", err)
   853  	}
   854  
   855  	assert.Equal(t, "ACK: EXIT\n", reply)
   856  
   857  	err = wait.PollUntilContextTimeout(ctx, time.Second, 3*time.Minute, true, func(ctx context.Context) (bool, error) {
   858  		gs, err = framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Get(ctx, readyGs.ObjectMeta.Name, metav1.GetOptions{})
   859  
   860  		if k8serrors.IsNotFound(err) {
   861  			return true, nil
   862  		}
   863  
   864  		return false, err
   865  	})
   866  
   867  	assert.NoError(t, err)
   868  }
   869  
   870  // TestGameServerEvicted test that if Gameserver would be evicted than it becomes Unhealthy
   871  // Ephemeral Storage limit set to 0Mi
   872  func TestGameServerEvicted(t *testing.T) {
   873  	t.Parallel()
   874  	log := e2eframework.TestLogger(t)
   875  
   876  	ctx := context.Background()
   877  	gs := framework.DefaultGameServer(framework.Namespace)
   878  	newGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   879  	require.NoError(t, err)
   880  	log.WithField("name", newGs.ObjectMeta.Name).Info("GameServer created, waiting for being Evicted and Unhealthy")
   881  	defer framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Delete(ctx, newGs.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint: errcheck
   882  
   883  	pods := framework.KubeClient.CoreV1().Pods(framework.Namespace)
   884  	pod, err := pods.Get(ctx, newGs.ObjectMeta.Name, metav1.GetOptions{})
   885  	require.NoError(t, err)
   886  
   887  	eviction := &policyv1.Eviction{
   888  		ObjectMeta: metav1.ObjectMeta{
   889  			Name:      pod.Name,
   890  			Namespace: pod.Namespace,
   891  		},
   892  	}
   893  	go func() {
   894  		time.Sleep(3 * time.Second) // just make sure it comes in later
   895  		log.WithField("name", eviction.ObjectMeta.Name).Info("Evicting pod!")
   896  		err := pods.EvictV1(context.Background(), eviction)
   897  		require.NoError(t, err)
   898  	}()
   899  
   900  	_, err = framework.WaitForGameServerState(t, newGs, agonesv1.GameServerStateUnhealthy, 10*time.Minute)
   901  	require.NoError(t, err, fmt.Sprintf("waiting for [%v] GameServer Unhealthy state timed out (%v)", gs.Status.State, gs.Name))
   902  }
   903  
   904  func TestGameServerPassthroughPort(t *testing.T) {
   905  	framework.SkipOnCloudProduct(t, "gke-autopilot", "does not support Passthrough PortPolicy")
   906  	t.Parallel()
   907  	gs := framework.DefaultGameServer(framework.Namespace)
   908  	gs.Spec.Ports[0] = agonesv1.GameServerPort{PortPolicy: agonesv1.Passthrough}
   909  	gs.Spec.Template.Spec.Containers[0].Env = []corev1.EnvVar{{Name: "PASSTHROUGH", Value: "TRUE"}}
   910  	// gate
   911  	errs := gs.Validate(agtesting.FakeAPIHooks{})
   912  	assert.Len(t, errs, 0)
   913  
   914  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   915  	if err != nil {
   916  		assert.FailNow(t, "Could not get a GameServer ready", err.Error())
   917  	}
   918  
   919  	port := readyGs.Spec.Ports[0]
   920  	assert.Equal(t, agonesv1.Passthrough, port.PortPolicy)
   921  	assert.NotEmpty(t, port.HostPort)
   922  	assert.Equal(t, port.HostPort, port.ContainerPort)
   923  
   924  	reply, err := framework.SendGameServerUDP(t, readyGs, "Hello World !")
   925  	if err != nil {
   926  		t.Fatalf("Could ping GameServer: %v", err)
   927  	}
   928  
   929  	assert.Equal(t, "ACK: Hello World !\n", reply)
   930  }
   931  
   932  func TestGameServerPortPolicyNone(t *testing.T) {
   933  	if !runtime.FeatureEnabled(runtime.FeaturePortPolicyNone) {
   934  		t.SkipNow()
   935  	}
   936  
   937  	t.Parallel()
   938  
   939  	gs := framework.DefaultGameServer(framework.Namespace)
   940  	gs.Spec.Ports[0] = agonesv1.GameServerPort{PortPolicy: agonesv1.None, ContainerPort: 7777}
   941  	// gate
   942  	errs := gs.Validate(agtesting.FakeAPIHooks{})
   943  	assert.Len(t, errs, 0)
   944  
   945  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   946  	if err != nil {
   947  		assert.FailNow(t, "Could not get a GameServer ready", err.Error())
   948  	}
   949  
   950  	// To test sending UDP traffic directly to a pod when no hostPort is set, a product like Calico which uses BGP is needed
   951  	// so this only tests that the port is set correctly.
   952  	port := readyGs.Spec.Ports[0]
   953  	assert.Equal(t, agonesv1.None, port.PortPolicy)
   954  	assert.Empty(t, port.HostPort)
   955  	assert.EqualValues(t, 7777, port.ContainerPort)
   956  }
   957  
   958  func TestGameServerTcpProtocol(t *testing.T) {
   959  	t.Parallel()
   960  	gs := framework.DefaultGameServer(framework.Namespace)
   961  
   962  	gs.Spec.Ports[0].Protocol = corev1.ProtocolTCP
   963  	gs.Spec.Template.Spec.Containers[0].Env = []corev1.EnvVar{{Name: "TCP", Value: "TRUE"}}
   964  
   965  	errs := gs.Validate(agtesting.FakeAPIHooks{})
   966  	require.Len(t, errs, 0)
   967  
   968  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   969  	require.NoError(t, err)
   970  
   971  	replyTCP, err := e2eframework.SendGameServerTCP(readyGs, "Hello World !")
   972  	require.NoError(t, err)
   973  	assert.Equal(t, "ACK TCP: Hello World !\n", replyTCP)
   974  }
   975  
   976  func TestGameServerTcpUdpProtocol(t *testing.T) {
   977  	t.Parallel()
   978  	gs := framework.DefaultGameServer(framework.Namespace)
   979  
   980  	gs.Spec.Ports[0].Protocol = agonesv1.ProtocolTCPUDP
   981  	gs.Spec.Ports[0].Name = "gameserver"
   982  	gs.Spec.Template.Spec.Containers[0].Env = []corev1.EnvVar{{Name: "TCP", Value: "TRUE"}}
   983  
   984  	errs := gs.Validate(agtesting.FakeAPIHooks{})
   985  	require.Len(t, errs, 0)
   986  
   987  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
   988  	if err != nil {
   989  		assert.FailNow(t, "Could not get a GameServer ready", err.Error())
   990  	}
   991  
   992  	tcpPort := readyGs.Spec.Ports[0]
   993  	assert.Equal(t, corev1.ProtocolTCP, tcpPort.Protocol)
   994  	assert.NotEmpty(t, tcpPort.HostPort)
   995  	assert.Equal(t, "gameserver-tcp", tcpPort.Name)
   996  
   997  	udpPort := readyGs.Spec.Ports[1]
   998  	assert.Equal(t, corev1.ProtocolUDP, udpPort.Protocol)
   999  	assert.NotEmpty(t, udpPort.HostPort)
  1000  	assert.Equal(t, "gameserver-udp", udpPort.Name)
  1001  
  1002  	assert.Equal(t, tcpPort.HostPort, udpPort.HostPort)
  1003  
  1004  	logrus.WithField("name", readyGs.ObjectMeta.Name).Info("GameServer created, sending UDP ping")
  1005  
  1006  	replyUDP, err := framework.SendGameServerUDPToPort(t, readyGs, udpPort.Name, "Hello World !")
  1007  	require.NoError(t, err)
  1008  	if err != nil {
  1009  		t.Fatalf("Could not ping UDP GameServer: %v", err)
  1010  	}
  1011  
  1012  	assert.Equal(t, "ACK: Hello World !\n", replyUDP)
  1013  
  1014  	logrus.WithField("name", readyGs.ObjectMeta.Name).Info("UDP ping passed, sending TCP ping")
  1015  
  1016  	replyTCP, err := e2eframework.SendGameServerTCPToPort(readyGs, tcpPort.Name, "Hello World !")
  1017  	if err != nil {
  1018  		t.Fatalf("Could not ping TCP GameServer: %v", err)
  1019  	}
  1020  
  1021  	assert.Equal(t, "ACK TCP: Hello World !\n", replyTCP)
  1022  }
  1023  
  1024  // TestGameServerStaticTcpUdpProtocol checks UDP and TCP connection for TCPUDP Protocol of Static Portpolicy
  1025  func TestGameServerStaticTcpUdpProtocol(t *testing.T) {
  1026  	framework.SkipOnCloudProduct(t, "gke-autopilot", "does not support Static PortPolicy")
  1027  	t.Parallel()
  1028  	gs := framework.DefaultGameServer(framework.Namespace)
  1029  	gs.Spec.Ports[0].PortPolicy = agonesv1.Static
  1030  	gs.Spec.Ports[0].Protocol = agonesv1.ProtocolTCPUDP
  1031  	gs.Spec.Ports[0].HostPort = 7000
  1032  	gs.Spec.Ports[0].Name = "gameserver"
  1033  	gs.Spec.Template.Spec.Containers[0].Env = []corev1.EnvVar{{Name: "TCP", Value: "TRUE"}}
  1034  
  1035  	errs := gs.Validate(agtesting.FakeAPIHooks{})
  1036  	require.Len(t, errs, 0)
  1037  
  1038  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
  1039  	require.NoError(t, err)
  1040  
  1041  	tcpPort := readyGs.Spec.Ports[0]
  1042  	assert.Equal(t, corev1.ProtocolTCP, tcpPort.Protocol)
  1043  	assert.NotEmpty(t, tcpPort.HostPort)
  1044  	assert.Equal(t, "gameserver-tcp", tcpPort.Name)
  1045  
  1046  	udpPort := readyGs.Spec.Ports[1]
  1047  	assert.Equal(t, corev1.ProtocolUDP, udpPort.Protocol)
  1048  	assert.NotEmpty(t, udpPort.HostPort)
  1049  	assert.Equal(t, "gameserver-udp", udpPort.Name)
  1050  
  1051  	assert.Equal(t, tcpPort.HostPort, udpPort.HostPort)
  1052  
  1053  	logrus.WithField("name", readyGs.ObjectMeta.Name).Info("GameServer created, sending UDP ping")
  1054  
  1055  	replyUDP, err := framework.SendGameServerUDPToPort(t, readyGs, udpPort.Name, "Hello World !")
  1056  	require.NoError(t, err)
  1057  	if err != nil {
  1058  		t.Fatalf("Could not ping UDP GameServer: %v", err)
  1059  	}
  1060  
  1061  	assert.Equal(t, "ACK: Hello World !\n", replyUDP)
  1062  
  1063  	logrus.WithField("name", readyGs.ObjectMeta.Name).Info("UDP ping passed, sending TCP ping")
  1064  
  1065  	replyTCP, err := e2eframework.SendGameServerTCPToPort(readyGs, tcpPort.Name, "Hello World !")
  1066  	if err != nil {
  1067  		t.Fatalf("Could not ping TCP GameServer: %v", err)
  1068  	}
  1069  
  1070  	assert.Equal(t, "ACK TCP: Hello World !\n", replyTCP)
  1071  }
  1072  
  1073  // TestGameServerStaticTcpProtocol checks TCP connection for TCP Protocol of Static Portpolicy
  1074  func TestGameServerStaticTcpProtocol(t *testing.T) {
  1075  	framework.SkipOnCloudProduct(t, "gke-autopilot", "does not support Static PortPolicy")
  1076  	t.Parallel()
  1077  	gs := framework.DefaultGameServer(framework.Namespace)
  1078  
  1079  	gs.Spec.Ports[0].PortPolicy = agonesv1.Static
  1080  	gs.Spec.Ports[0].Protocol = corev1.ProtocolTCP
  1081  	gs.Spec.Ports[0].HostPort = 7000
  1082  	gs.Spec.Template.Spec.Containers[0].Env = []corev1.EnvVar{{Name: "TCP", Value: "TRUE"}}
  1083  
  1084  	errs := gs.Validate(agtesting.FakeAPIHooks{})
  1085  	require.Len(t, errs, 0)
  1086  
  1087  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
  1088  	require.NoError(t, err)
  1089  
  1090  	logrus.WithField("name", readyGs.ObjectMeta.Name).Info("sending TCP ping")
  1091  
  1092  	replyTCP, err := e2eframework.SendGameServerTCP(readyGs, "Hello World !")
  1093  	require.NoError(t, err)
  1094  	assert.Equal(t, "ACK TCP: Hello World !\n", replyTCP)
  1095  
  1096  	logrus.WithField("name", readyGs.ObjectMeta.Name).Info("TCP ping Passed")
  1097  }
  1098  
  1099  // TestGameServerStaticUdpProtocol checks default(UDP) connection of Static Portpolicy
  1100  func TestGameServerStaticUdpProtocol(t *testing.T) {
  1101  	framework.SkipOnCloudProduct(t, "gke-autopilot", "does not support Static PortPolicy")
  1102  	t.Parallel()
  1103  	gs := framework.DefaultGameServer(framework.Namespace)
  1104  
  1105  	gs.Spec.Ports[0].PortPolicy = agonesv1.Static
  1106  	gs.Spec.Ports[0].HostPort = 7000
  1107  
  1108  	errs := gs.Validate(agtesting.FakeAPIHooks{})
  1109  	require.Len(t, errs, 0)
  1110  
  1111  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
  1112  	require.NoError(t, err)
  1113  
  1114  	logrus.WithField("name", readyGs.ObjectMeta.Name).Info("sending UDP ping")
  1115  
  1116  	replyTCP, err := framework.SendGameServerUDP(t, readyGs, "Default UDP connection check")
  1117  	require.NoError(t, err)
  1118  	assert.Equal(t, "ACK: Default UDP connection check\n", replyTCP)
  1119  
  1120  	logrus.WithField("name", readyGs.ObjectMeta.Name).Info("UDP ping Passed")
  1121  }
  1122  
  1123  func TestGameServerWithoutPort(t *testing.T) {
  1124  	t.Parallel()
  1125  	gs := framework.DefaultGameServer(framework.Namespace)
  1126  	gs.Spec.Ports = nil
  1127  
  1128  	errs := gs.Validate(agtesting.FakeAPIHooks{})
  1129  	assert.Len(t, errs, 0)
  1130  
  1131  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
  1132  
  1133  	require.NoError(t, err, "Could not get a GameServer ready")
  1134  	assert.Empty(t, readyGs.Spec.Ports)
  1135  }
  1136  
  1137  // TestGameServerResourceValidation - check that we are not able to use
  1138  // invalid PodTemplate for GameServer Spec with wrong Resource Requests and Limits
  1139  func TestGameServerResourceValidation(t *testing.T) {
  1140  	t.Parallel()
  1141  	ctx := context.Background()
  1142  	gs := framework.DefaultGameServer(framework.Namespace)
  1143  	mi64 := resource.MustParse("64Mi")
  1144  	gs.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceMemory] = mi64
  1145  	gs.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceMemory] = resource.MustParse("128Mi")
  1146  
  1147  	errs := gs.Validate(agtesting.FakeAPIHooks{})
  1148  	assert.False(t, len(errs) == 0)
  1149  
  1150  	gsClient := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace)
  1151  
  1152  	_, err := gsClient.Create(ctx, gs.DeepCopy(), metav1.CreateOptions{})
  1153  	assert.NotNil(t, err)
  1154  	statusErr, ok := err.(*k8serrors.StatusError)
  1155  	assert.True(t, ok)
  1156  	assert.Len(t, statusErr.Status().Details.Causes, 1)
  1157  	assert.Equal(t, metav1.CauseTypeFieldValueInvalid, statusErr.Status().Details.Causes[0].Type)
  1158  	assert.Equal(t, "spec.template.spec.containers[0].resources.requests", statusErr.Status().Details.Causes[0].Field)
  1159  
  1160  	gs.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceCPU] = resource.MustParse("-50m")
  1161  	_, err = gsClient.Create(ctx, gs.DeepCopy(), metav1.CreateOptions{})
  1162  	assert.NotNil(t, err)
  1163  	statusErr, ok = err.(*k8serrors.StatusError)
  1164  	assert.True(t, ok)
  1165  	assert.Len(t, statusErr.Status().Details.Causes, 2)
  1166  	sort.Slice(statusErr.Status().Details.Causes, func(i, j int) bool {
  1167  		return statusErr.Status().Details.Causes[i].Field > statusErr.Status().Details.Causes[j].Field
  1168  	})
  1169  	assert.Equal(t, metav1.CauseTypeFieldValueInvalid, statusErr.Status().Details.Causes[0].Type)
  1170  	assert.Equal(t, "spec.template.spec.containers[0].resources.requests[cpu]", statusErr.Status().Details.Causes[0].Field)
  1171  
  1172  	// test that values are still being set correctly
  1173  	m50 := resource.MustParse("50m")
  1174  	gs.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceMemory] = mi64
  1175  	gs.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceMemory] = mi64
  1176  	gs.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceCPU] = m50
  1177  	gs.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceCPU] = m50
  1178  
  1179  	// confirm we have a valid GameServer before running the test
  1180  	errs = gs.Validate(agtesting.FakeAPIHooks{})
  1181  	require.Len(t, errs, 0)
  1182  
  1183  	gsCopy, err := gsClient.Create(ctx, gs.DeepCopy(), metav1.CreateOptions{})
  1184  	require.NoError(t, err)
  1185  	assert.Equal(t, mi64, gsCopy.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceMemory])
  1186  	assert.Equal(t, mi64, gsCopy.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceMemory])
  1187  	assert.Equal(t, m50, gsCopy.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceCPU])
  1188  	assert.Equal(t, m50, gsCopy.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceCPU])
  1189  }
  1190  
  1191  func TestGameServerPodTemplateSpecFailSchemaValidation(t *testing.T) {
  1192  	t.Parallel()
  1193  
  1194  	// The Kubernetes dynamic client skips schema validation (for reasons I'm not sure of), so the
  1195  	// best way I could determine to test schema validation is via calling kubectl directly.
  1196  	// The schema's from Kubernetes don't include anything like `pattern:` or `enum:` which would
  1197  	// potentially make this easier to test.
  1198  
  1199  	gsYaml := `
  1200  apiVersion: "agones.dev/v1"
  1201  kind: GameServer
  1202  metadata:
  1203    name: "invalid-gameserver"
  1204  spec:
  1205    ports:
  1206      - name: default
  1207        portPolicy: Dynamic
  1208        containerPort: 7654
  1209    template:
  1210      spec:
  1211        affinity:
  1212          nodeAffinity:
  1213            preferredDuringSchedulingIgnoredDuringExecution: ERROR
  1214        containers:
  1215          - name: simple-game-server
  1216            image: us-docker.pkg.dev/agones-images/examples/simple-game-server:0.39
  1217  `
  1218  	err := os.WriteFile("/tmp/invalid.yaml", []byte(gsYaml), 0o644)
  1219  	require.NoError(t, err)
  1220  
  1221  	cmd := exec.Command("kubectl", "apply", "-f", "/tmp/invalid.yaml")
  1222  	var stdout bytes.Buffer
  1223  	var stderr bytes.Buffer
  1224  	cmd.Stdout = &stdout
  1225  	cmd.Stderr = &stderr
  1226  	err = cmd.Run()
  1227  	logrus.WithField("stdout", stdout.String()).WithField("stderr", stderr.String()).WithError(err).Info("Ran command!")
  1228  	require.Error(t, err)
  1229  	assert.Contains(t, stderr.String(), "spec.template.spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution")
  1230  }
  1231  
  1232  func TestGameServerSetPlayerCapacity(t *testing.T) {
  1233  	if !runtime.FeatureEnabled(runtime.FeaturePlayerTracking) {
  1234  		t.SkipNow()
  1235  	}
  1236  	t.Parallel()
  1237  	ctx := context.Background()
  1238  
  1239  	t.Run("no initial capacity set", func(t *testing.T) {
  1240  		gs := framework.DefaultGameServer(framework.Namespace)
  1241  		gs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
  1242  		if err != nil {
  1243  			t.Fatalf("Could not get a GameServer ready: %v", err)
  1244  		}
  1245  		assert.Equal(t, gs.Status.State, agonesv1.GameServerStateReady)
  1246  		assert.Equal(t, int64(0), gs.Status.Players.Capacity)
  1247  
  1248  		reply, err := framework.SendGameServerUDP(t, gs, "PLAYER_CAPACITY")
  1249  		if err != nil {
  1250  			t.Fatalf("Could not message GameServer: %v", err)
  1251  		}
  1252  		assert.Equal(t, "0\n", reply)
  1253  
  1254  		reply, err = framework.SendGameServerUDP(t, gs, "PLAYER_CAPACITY 20")
  1255  		if err != nil {
  1256  			t.Fatalf("Could not message GameServer: %v", err)
  1257  		}
  1258  		assert.Equal(t, "ACK: PLAYER_CAPACITY 20\n", reply)
  1259  
  1260  		reply, err = framework.SendGameServerUDP(t, gs, "PLAYER_CAPACITY")
  1261  		if err != nil {
  1262  			t.Fatalf("Could not message GameServer: %v", err)
  1263  		}
  1264  		assert.Equal(t, "20\n", reply)
  1265  
  1266  		err = wait.PollUntilContextTimeout(ctx, time.Second, time.Minute, true, func(ctx context.Context) (bool, error) {
  1267  			gs, err := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Get(ctx, gs.ObjectMeta.Name, metav1.GetOptions{})
  1268  			if err != nil {
  1269  				return false, err
  1270  			}
  1271  			return gs.Status.Players.Capacity == 20, nil
  1272  		})
  1273  		assert.NoError(t, err)
  1274  	})
  1275  
  1276  	t.Run("initial capacity set", func(t *testing.T) {
  1277  		gs := framework.DefaultGameServer(framework.Namespace)
  1278  		gs.Spec.Players = &agonesv1.PlayersSpec{InitialCapacity: 10}
  1279  		gs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
  1280  		if err != nil {
  1281  			t.Fatalf("Could not get a GameServer ready: %v", err)
  1282  		}
  1283  		assert.Equal(t, gs.Status.State, agonesv1.GameServerStateReady)
  1284  		assert.Equal(t, int64(10), gs.Status.Players.Capacity)
  1285  
  1286  		reply, err := framework.SendGameServerUDP(t, gs, "PLAYER_CAPACITY")
  1287  		if err != nil {
  1288  			t.Fatalf("Could not message GameServer: %v", err)
  1289  		}
  1290  		assert.Equal(t, "10\n", reply)
  1291  
  1292  		reply, err = framework.SendGameServerUDP(t, gs, "PLAYER_CAPACITY 20")
  1293  		if err != nil {
  1294  			t.Fatalf("Could not message GameServer: %v", err)
  1295  		}
  1296  		assert.Equal(t, "ACK: PLAYER_CAPACITY 20\n", reply)
  1297  
  1298  		reply, err = framework.SendGameServerUDP(t, gs, "PLAYER_CAPACITY")
  1299  		if err != nil {
  1300  			t.Fatalf("Could not message GameServer: %v", err)
  1301  		}
  1302  		assert.Equal(t, "20\n", reply)
  1303  
  1304  		err = wait.PollUntilContextTimeout(context.Background(), time.Second, time.Minute, true, func(ctx context.Context) (bool, error) {
  1305  			gs, err := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Get(ctx, gs.ObjectMeta.Name, metav1.GetOptions{})
  1306  			if err != nil {
  1307  				return false, err
  1308  			}
  1309  			return gs.Status.Players.Capacity == 20, nil
  1310  		})
  1311  		assert.NoError(t, err)
  1312  
  1313  		time.Sleep(30 * time.Second)
  1314  	})
  1315  }
  1316  
  1317  func TestPlayerConnectWithCapacityZero(t *testing.T) {
  1318  	if !runtime.FeatureEnabled(runtime.FeaturePlayerTracking) {
  1319  		t.SkipNow()
  1320  	}
  1321  	t.Parallel()
  1322  
  1323  	gs := framework.DefaultGameServer(framework.Namespace)
  1324  	playerCount := int64(0)
  1325  	gs.Spec.Players = &agonesv1.PlayersSpec{InitialCapacity: playerCount}
  1326  	gs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
  1327  	require.NoError(t, err)
  1328  	assert.Equal(t, gs.Status.State, agonesv1.GameServerStateReady)
  1329  	assert.Equal(t, playerCount, gs.Status.Players.Capacity)
  1330  
  1331  	// add a player
  1332  	msg := "PLAYER_CONNECT 1"
  1333  	logrus.WithField("msg", msg).Info("Sending Player Connect")
  1334  	_, err = framework.SendGameServerUDP(t, gs, msg)
  1335  	// expected error from the log.Fatalf("could not connect player: %v", err)
  1336  	if assert.Error(t, err) {
  1337  		_, err := framework.WaitForGameServerState(t, gs, agonesv1.GameServerStateUnhealthy, time.Minute)
  1338  		assert.NoError(t, err)
  1339  	}
  1340  }
  1341  
  1342  func TestPlayerConnectAndDisconnect(t *testing.T) {
  1343  	if !runtime.FeatureEnabled(runtime.FeaturePlayerTracking) {
  1344  		t.SkipNow()
  1345  	}
  1346  	t.Parallel()
  1347  	ctx := context.Background()
  1348  
  1349  	gs := framework.DefaultGameServer(framework.Namespace)
  1350  	playerCount := int64(3)
  1351  	gs.Spec.Players = &agonesv1.PlayersSpec{InitialCapacity: playerCount}
  1352  	gs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
  1353  	if err != nil {
  1354  		t.Fatalf("Could not get a GameServer ready: %v", err)
  1355  	}
  1356  	assert.Equal(t, gs.Status.State, agonesv1.GameServerStateReady)
  1357  	assert.Equal(t, playerCount, gs.Status.Players.Capacity)
  1358  
  1359  	// add three players in quick succession
  1360  	for i := int64(1); i <= playerCount; i++ {
  1361  		msg := "PLAYER_CONNECT " + fmt.Sprintf("%d", i)
  1362  		logrus.WithField("msg", msg).Info("Sending Player Connect")
  1363  		reply, err := framework.SendGameServerUDP(t, gs, msg)
  1364  		if err != nil {
  1365  			t.Fatalf("Could not message GameServer: %v", err)
  1366  		}
  1367  		assert.Equal(t, fmt.Sprintf("ACK: %s\n", msg), reply)
  1368  	}
  1369  
  1370  	// deliberately do this before polling, to test the SDK returning the correct
  1371  	// results before it is committed to the GameServer resource.
  1372  	reply, err := framework.SendGameServerUDP(t, gs, "PLAYER_CONNECTED 1")
  1373  	if err != nil {
  1374  		t.Fatalf("Could not message GameServer: %v", err)
  1375  	}
  1376  	assert.Equal(t, "true\n", reply)
  1377  
  1378  	reply, err = framework.SendGameServerUDP(t, gs, "GET_PLAYERS")
  1379  	if err != nil {
  1380  		t.Fatalf("Could not message GameServer: %v", err)
  1381  	}
  1382  	assert.ElementsMatch(t, []string{"1", "2", "3"}, strings.Split(strings.TrimSpace(reply), ","))
  1383  
  1384  	reply, err = framework.SendGameServerUDP(t, gs, "PLAYER_COUNT")
  1385  	if err != nil {
  1386  		t.Fatalf("Could not message GameServer: %v", err)
  1387  	}
  1388  	assert.Equal(t, "3\n", reply)
  1389  
  1390  	err = wait.PollUntilContextTimeout(ctx, time.Second, time.Minute, true, func(ctx context.Context) (bool, error) {
  1391  		gs, err = framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Get(ctx, gs.ObjectMeta.Name, metav1.GetOptions{})
  1392  		if err != nil {
  1393  			return false, err
  1394  		}
  1395  		return gs.Status.Players.Count == playerCount, nil
  1396  	})
  1397  	assert.NoError(t, err)
  1398  	assert.ElementsMatch(t, []string{"1", "2", "3"}, gs.Status.Players.IDs)
  1399  
  1400  	// let's disconnect player 2
  1401  	logrus.Info("Disconnect Player 2")
  1402  	reply, err = framework.SendGameServerUDP(t, gs, "PLAYER_DISCONNECT 2")
  1403  	if err != nil {
  1404  		t.Fatalf("Could not message GameServer: %v", err)
  1405  	}
  1406  	assert.Equal(t, "ACK: PLAYER_DISCONNECT 2\n", reply)
  1407  
  1408  	reply, err = framework.SendGameServerUDP(t, gs, "PLAYER_CONNECTED 2")
  1409  	if err != nil {
  1410  		t.Fatalf("Could not message GameServer: %v", err)
  1411  	}
  1412  	assert.Equal(t, "false\n", reply)
  1413  
  1414  	reply, err = framework.SendGameServerUDP(t, gs, "GET_PLAYERS")
  1415  	if err != nil {
  1416  		t.Fatalf("Could not message GameServer: %v", err)
  1417  	}
  1418  	assert.ElementsMatch(t, []string{"1", "3"}, strings.Split(strings.TrimSpace(reply), ","))
  1419  
  1420  	reply, err = framework.SendGameServerUDP(t, gs, "PLAYER_COUNT")
  1421  	if err != nil {
  1422  		t.Fatalf("Could not message GameServer: %v", err)
  1423  	}
  1424  	assert.Equal(t, "2\n", reply)
  1425  
  1426  	err = wait.PollUntilContextTimeout(ctx, time.Second, time.Minute, true, func(ctx context.Context) (bool, error) {
  1427  		gs, err = framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Get(ctx, gs.ObjectMeta.Name, metav1.GetOptions{})
  1428  		if err != nil {
  1429  			return false, err
  1430  		}
  1431  		return gs.Status.Players.Count == 2, nil
  1432  	})
  1433  	assert.NoError(t, err)
  1434  	assert.ElementsMatch(t, []string{"1", "3"}, gs.Status.Players.IDs)
  1435  }
  1436  
  1437  func TestCounters(t *testing.T) {
  1438  	if !runtime.FeatureEnabled(runtime.FeatureCountsAndLists) {
  1439  		t.SkipNow()
  1440  	}
  1441  	t.Parallel()
  1442  	ctx := context.Background()
  1443  	gs := framework.DefaultGameServer(framework.Namespace)
  1444  
  1445  	gs.Spec.Counters = make(map[string]agonesv1.CounterStatus)
  1446  	gs.Spec.Counters["games"] = agonesv1.CounterStatus{
  1447  		Count:    1,
  1448  		Capacity: 50,
  1449  	}
  1450  	gs.Spec.Counters["foo"] = agonesv1.CounterStatus{
  1451  		Count:    10,
  1452  		Capacity: 100,
  1453  	}
  1454  	gs.Spec.Counters["bar"] = agonesv1.CounterStatus{
  1455  		Count:    10,
  1456  		Capacity: 10,
  1457  	}
  1458  	gs.Spec.Counters["baz"] = agonesv1.CounterStatus{
  1459  		Count:    1000,
  1460  		Capacity: 1000,
  1461  	}
  1462  	gs.Spec.Counters["qux"] = agonesv1.CounterStatus{
  1463  		Count:    42,
  1464  		Capacity: 50,
  1465  	}
  1466  
  1467  	gs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
  1468  	require.NoError(t, err)
  1469  	defer framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Delete(ctx, gs.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint: errcheck
  1470  	assert.Equal(t, agonesv1.GameServerStateReady, gs.Status.State)
  1471  
  1472  	testCases := map[string]struct {
  1473  		msg          string
  1474  		want         string
  1475  		counterName  string
  1476  		wantCount    string
  1477  		wantCapacity string
  1478  	}{
  1479  		"GetCounterCount": {
  1480  			msg:  "GET_COUNTER_COUNT games",
  1481  			want: "COUNTER: 1\n",
  1482  		},
  1483  		"GetCounterCount Counter Does Not Exist": {
  1484  			msg:  "GET_COUNTER_COUNT fame",
  1485  			want: "ERROR: -1\n",
  1486  		},
  1487  		"IncrementCounter": {
  1488  			msg:         "INCREMENT_COUNTER foo 10",
  1489  			want:        "SUCCESS\n",
  1490  			counterName: "foo",
  1491  			wantCount:   "COUNTER: 20\n",
  1492  		},
  1493  		"IncrementCounter Past Capacity": {
  1494  			msg:         "INCREMENT_COUNTER games 50",
  1495  			want:        "ERROR: could not increment Counter games by amount 50: rpc error: code = Unknown desc = out of range. Count must be within range [0,Capacity]. Found Count: 51, Capacity: 50\n",
  1496  			counterName: "games",
  1497  			wantCount:   "COUNTER: 1\n",
  1498  		},
  1499  		"IncrementCounter Negative": {
  1500  			msg:         "INCREMENT_COUNTER games -1",
  1501  			want:        "ERROR: amount must be a positive int64, found -1\n",
  1502  			counterName: "games",
  1503  			wantCount:   "COUNTER: 1\n",
  1504  		},
  1505  		"IncrementCounter Counter Does Not Exist": {
  1506  			msg:  "INCREMENT_COUNTER same 1",
  1507  			want: "ERROR: could not increment Counter same by amount 1: rpc error: code = Unknown desc = counter not found: same\n",
  1508  		},
  1509  		"DecrementCounter": {
  1510  			msg:         "DECREMENT_COUNTER bar 10",
  1511  			want:        "SUCCESS\n",
  1512  			counterName: "bar",
  1513  			wantCount:   "COUNTER: 0\n",
  1514  		},
  1515  		"DecrementCounter Past Capacity": {
  1516  			msg:         "DECREMENT_COUNTER games 2",
  1517  			want:        "ERROR: could not decrement Counter games by amount 2: rpc error: code = Unknown desc = out of range. Count must be within range [0,Capacity]. Found Count: -1, Capacity: 50\n",
  1518  			counterName: "games",
  1519  			wantCount:   "COUNTER: 1\n",
  1520  		},
  1521  		"DecrementCounter Negative": {
  1522  			msg:         "DECREMENT_COUNTER games -1",
  1523  			want:        "ERROR: amount must be a positive int64, found -1\n",
  1524  			counterName: "games",
  1525  			wantCount:   "COUNTER: 1\n",
  1526  		},
  1527  		"DecrementCounter Counter Does Not Exist": {
  1528  			msg:  "DECREMENT_COUNTER lame 1",
  1529  			want: "ERROR: could not decrement Counter lame by amount 1: rpc error: code = Unknown desc = counter not found: lame\n",
  1530  		},
  1531  		"SetCounterCount": {
  1532  			msg:         "SET_COUNTER_COUNT baz 0",
  1533  			want:        "SUCCESS\n",
  1534  			counterName: "baz",
  1535  			wantCount:   "COUNTER: 0\n",
  1536  		},
  1537  		"SetCounterCount Past Capacity": {
  1538  			msg:         "SET_COUNTER_COUNT games 51",
  1539  			want:        "ERROR: could not set Counter games count to amount 51: rpc error: code = Unknown desc = out of range. Count must be within range [0,Capacity]. Found Count: 51, Capacity: 50\n",
  1540  			counterName: "games",
  1541  			wantCount:   "COUNTER: 1\n",
  1542  		},
  1543  		"SetCounterCount Past Zero": {
  1544  			msg:         "SET_COUNTER_COUNT games -1",
  1545  			want:        "ERROR: could not set Counter games count to amount -1: rpc error: code = Unknown desc = out of range. Count must be within range [0,Capacity]. Found Count: -1, Capacity: 50\n",
  1546  			counterName: "games",
  1547  			wantCount:   "COUNTER: 1\n",
  1548  		},
  1549  		"GetCounterCapacity": {
  1550  			msg:  "GET_COUNTER_CAPACITY games",
  1551  			want: "CAPACITY: 50\n",
  1552  		},
  1553  		"GetCounterCapacity Counter Does Not Exist": {
  1554  			msg:  "GET_COUNTER_CAPACITY dame",
  1555  			want: "ERROR: -1\n",
  1556  		},
  1557  		"SetCounterCapacity": {
  1558  			msg:          "SET_COUNTER_CAPACITY qux 0",
  1559  			want:         "SUCCESS\n",
  1560  			counterName:  "qux",
  1561  			wantCapacity: "CAPACITY: 0\n",
  1562  		},
  1563  		"SetCounterCapacity Past Zero": {
  1564  			msg:         "SET_COUNTER_CAPACITY games -42",
  1565  			want:        "ERROR: could not set Counter games capacity to amount -42: rpc error: code = Unknown desc = out of range. Capacity must be greater than or equal to 0. Found Capacity: -42\n",
  1566  			counterName: "games",
  1567  			wantCount:   "COUNTER: 1\n",
  1568  		},
  1569  	}
  1570  	// nolint:dupl  // Linter errors on lines are duplicate of TestLists
  1571  	for name, testCase := range testCases {
  1572  		t.Run(name, func(t *testing.T) {
  1573  			logrus.WithField("msg", testCase.msg).Info(name)
  1574  			reply, err := framework.SendGameServerUDP(t, gs, testCase.msg)
  1575  			require.NoError(t, err)
  1576  			assert.Equal(t, testCase.want, reply)
  1577  
  1578  			if testCase.wantCount != "" {
  1579  				msg := "GET_COUNTER_COUNT " + testCase.counterName
  1580  				logrus.WithField("msg", msg).Info("Sending GetCounterCount")
  1581  				reply, err = framework.SendGameServerUDP(t, gs, msg)
  1582  				require.NoError(t, err)
  1583  				assert.Equal(t, testCase.wantCount, reply)
  1584  			}
  1585  
  1586  			if testCase.wantCapacity != "" {
  1587  				msg := "GET_COUNTER_CAPACITY " + testCase.counterName
  1588  				logrus.WithField("msg", msg).Info("Sending GetCounterCapacity")
  1589  				reply, err = framework.SendGameServerUDP(t, gs, msg)
  1590  				require.NoError(t, err)
  1591  				assert.Equal(t, testCase.wantCapacity, reply)
  1592  			}
  1593  		})
  1594  	}
  1595  }
  1596  
  1597  func TestLists(t *testing.T) {
  1598  	if !runtime.FeatureEnabled(runtime.FeatureCountsAndLists) {
  1599  		t.SkipNow()
  1600  	}
  1601  	t.Parallel()
  1602  	ctx := context.Background()
  1603  	gs := framework.DefaultGameServer(framework.Namespace)
  1604  
  1605  	gs.Spec.Lists = make(map[string]agonesv1.ListStatus)
  1606  	gs.Spec.Lists["games"] = agonesv1.ListStatus{
  1607  		Values:   []string{"game1", "game2"},
  1608  		Capacity: 50,
  1609  	}
  1610  	gs.Spec.Lists["foo"] = agonesv1.ListStatus{
  1611  		Values:   []string{},
  1612  		Capacity: 1,
  1613  	}
  1614  	gs.Spec.Lists["bar"] = agonesv1.ListStatus{
  1615  		Values:   []string{"bar1", "bar2"},
  1616  		Capacity: 10,
  1617  	}
  1618  	gs.Spec.Lists["baz"] = agonesv1.ListStatus{
  1619  		Values:   []string{"baz1"},
  1620  		Capacity: 1,
  1621  	}
  1622  	gs.Spec.Lists["qux"] = agonesv1.ListStatus{
  1623  		Values:   []string{"qux1", "qux2", "qux3", "qux4"},
  1624  		Capacity: 5,
  1625  	}
  1626  
  1627  	gs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
  1628  	require.NoError(t, err)
  1629  	defer framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Delete(ctx, gs.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint: errcheck
  1630  	assert.Equal(t, agonesv1.GameServerStateReady, gs.Status.State)
  1631  
  1632  	testCases := map[string]struct {
  1633  		msg          string
  1634  		want         string
  1635  		listName     string
  1636  		wantLength   string
  1637  		wantCapacity string
  1638  	}{
  1639  		"GetListCapacity": {
  1640  			msg:  "GET_LIST_CAPACITY games",
  1641  			want: "CAPACITY: 50\n",
  1642  		},
  1643  		"SetListCapacity": {
  1644  			msg:          "SET_LIST_CAPACITY foo 1000",
  1645  			want:         "SUCCESS\n",
  1646  			listName:     "foo",
  1647  			wantCapacity: "CAPACITY: 1000\n",
  1648  		},
  1649  		"SetListCapacity past 1000": {
  1650  			msg:          "SET_LIST_CAPACITY games 1001",
  1651  			want:         "ERROR: could not set List games capacity to amount 1001: rpc error: code = Unknown desc = out of range. Capacity must be within range [0,1000]. Found Capacity: 1001\n",
  1652  			listName:     "games",
  1653  			wantCapacity: "CAPACITY: 50\n",
  1654  		},
  1655  		"SetListCapacity negative": {
  1656  			msg:          "SET_LIST_CAPACITY games -1",
  1657  			want:         "ERROR: could not set List games capacity to amount -1: rpc error: code = Unknown desc = out of range. Capacity must be within range [0,1000]. Found Capacity: -1\n",
  1658  			listName:     "games",
  1659  			wantCapacity: "CAPACITY: 50\n",
  1660  		},
  1661  		"ListContains": {
  1662  			msg:  "LIST_CONTAINS games game2",
  1663  			want: "FOUND: true\n",
  1664  		},
  1665  		"ListContains false": {
  1666  			msg:  "LIST_CONTAINS games game0",
  1667  			want: "FOUND: false\n",
  1668  		},
  1669  		"GetListLength": {
  1670  			msg:  "GET_LIST_LENGTH games",
  1671  			want: "LENGTH: 2\n",
  1672  		},
  1673  		"GetListValues": {
  1674  			msg:  "GET_LIST_VALUES games",
  1675  			want: "VALUES: game1,game2\n",
  1676  		},
  1677  		"GetListValues empty": {
  1678  			msg:  "GET_LIST_VALUES foo",
  1679  			want: "VALUES: <none>\n",
  1680  		},
  1681  		"AppendListValue": {
  1682  			msg:        "APPEND_LIST_VALUE bar bar3",
  1683  			want:       "SUCCESS\n",
  1684  			listName:   "bar",
  1685  			wantLength: "LENGTH: 3\n",
  1686  		},
  1687  		"AppendListValue past capacity": {
  1688  			msg:        "APPEND_LIST_VALUE baz baz2",
  1689  			want:       "ERROR: could not get List baz: rpc error: code = Unknown desc = out of range. No available capacity. Current Capacity: 1, List Size: 1\n",
  1690  			listName:   "baz",
  1691  			wantLength: "LENGTH: 1\n",
  1692  		},
  1693  		"DeleteListValue": {
  1694  			msg:        "DELETE_LIST_VALUE qux qux3",
  1695  			want:       "SUCCESS\n",
  1696  			listName:   "qux",
  1697  			wantLength: "LENGTH: 3\n",
  1698  		},
  1699  		"DeleteListValue value does not exist": {
  1700  			msg:        "DELETE_LIST_VALUE games game4",
  1701  			want:       "ERROR: could not get List games: rpc error: code = Unknown desc = not found: value game4 not in list games\n",
  1702  			listName:   "games",
  1703  			wantLength: "LENGTH: 2\n",
  1704  		},
  1705  	}
  1706  	// nolint:dupl  // Linter errors on lines are duplicate of TestCounters
  1707  	for name, testCase := range testCases {
  1708  		t.Run(name, func(t *testing.T) {
  1709  			logrus.WithField("msg", testCase.msg).Info(name)
  1710  			reply, err := framework.SendGameServerUDP(t, gs, testCase.msg)
  1711  			require.NoError(t, err)
  1712  			assert.Equal(t, testCase.want, reply)
  1713  
  1714  			if testCase.wantLength != "" {
  1715  				msg := "GET_LIST_LENGTH " + testCase.listName
  1716  				logrus.WithField("msg", msg).Info("Sending GetListLength")
  1717  				reply, err = framework.SendGameServerUDP(t, gs, msg)
  1718  				require.NoError(t, err)
  1719  				assert.Equal(t, testCase.wantLength, reply)
  1720  			}
  1721  
  1722  			if testCase.wantCapacity != "" {
  1723  				msg := "GET_LIST_CAPACITY " + testCase.listName
  1724  				logrus.WithField("msg", msg).Info("Sending GetListCapacity")
  1725  				reply, err = framework.SendGameServerUDP(t, gs, msg)
  1726  				require.NoError(t, err)
  1727  				assert.Equal(t, testCase.wantCapacity, reply)
  1728  			}
  1729  		})
  1730  	}
  1731  }
  1732  
  1733  func TestSideCarCommunicatesWhileTerminating(t *testing.T) {
  1734  	t.Parallel()
  1735  	ctx := context.Background()
  1736  	gs := framework.DefaultGameServer(framework.Namespace)
  1737  
  1738  	minute := int64(60)
  1739  	gs.Spec.Template.Spec.Containers[0].Args = append(gs.Spec.Template.Spec.Containers[0].Args, "--gracefulTerminationDelaySec", "60")
  1740  	gs.Spec.Template.Spec.TerminationGracePeriodSeconds = &minute
  1741  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
  1742  	require.NoError(t, err)
  1743  	require.Equal(t, readyGs.Status.State, agonesv1.GameServerStateReady)
  1744  
  1745  	// delete the GameServer
  1746  	gameServers := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace)
  1747  	err = gameServers.Delete(context.Background(), readyGs.ObjectMeta.Name, metav1.DeleteOptions{})
  1748  	require.NoError(t, err, "Could not delete GameServer")
  1749  
  1750  	// wait for the deletion timestamp to be set
  1751  	err = wait.PollUntilContextTimeout(ctx, time.Second, time.Minute, true, func(ctx context.Context) (bool, error) {
  1752  		gs, err := gameServers.Get(ctx, readyGs.ObjectMeta.Name, metav1.GetOptions{})
  1753  		if err != nil {
  1754  			return false, err
  1755  		}
  1756  		return gs.DeletionTimestamp != nil, nil
  1757  	})
  1758  	require.NoError(t, err, "Could not get a GameServer with deletion timestamp")
  1759  
  1760  	// send a "GAMESERVER" message, and confirm it works
  1761  	reply, err := framework.SendGameServerUDP(t, readyGs, "GAMESERVER")
  1762  	require.NoError(t, err)
  1763  	require.Equal(t, fmt.Sprintf("NAME: %s\n", readyGs.ObjectMeta.Name), reply)
  1764  }
  1765  
  1766  func TestGracefulShutdown(t *testing.T) {
  1767  	// with the new sidecar pattern, there's no need for waiting on the Shutdown state.
  1768  	if runtime.FeatureEnabled(runtime.FeatureSidecarContainers) {
  1769  		t.SkipNow()
  1770  	}
  1771  
  1772  	t.Parallel()
  1773  
  1774  	log := e2eframework.TestLogger(t)
  1775  	ctx := context.Background()
  1776  	gs := framework.DefaultGameServer(framework.Namespace)
  1777  	var minute int64 = 60
  1778  	gs.Spec.Template.Spec.TerminationGracePeriodSeconds = &minute
  1779  	readyGs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
  1780  	if err != nil {
  1781  		t.Fatalf("Could not get a GameServer ready: %v", err)
  1782  	}
  1783  	assert.Equal(t, readyGs.Status.State, agonesv1.GameServerStateReady)
  1784  	gameservers := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace)
  1785  	err = gameservers.Delete(ctx, readyGs.ObjectMeta.Name, metav1.DeleteOptions{})
  1786  	require.NoError(t, err)
  1787  	log.Info("Deleted GameServer, waiting 20 seconds...")
  1788  	time.Sleep(20 * time.Second)
  1789  	log.WithField("gs", gs).Info("Checking GameServer")
  1790  	gs, err = gameservers.Get(ctx, readyGs.ObjectMeta.Name, metav1.GetOptions{})
  1791  	require.NoError(t, err)
  1792  	assert.Equal(t, readyGs.ObjectMeta.Name, gs.ObjectMeta.Name)
  1793  
  1794  	// move it to shutdown
  1795  	gsCopy := gs.DeepCopy()
  1796  	gsCopy.Status.State = agonesv1.GameServerStateShutdown
  1797  	_, err = gameservers.Update(ctx, gsCopy, metav1.UpdateOptions{})
  1798  	require.NoError(t, err)
  1799  
  1800  	start := time.Now()
  1801  	require.Eventually(t, func() bool {
  1802  		_, err := gameservers.Get(ctx, readyGs.ObjectMeta.Name, metav1.GetOptions{})
  1803  		log.WithError(err).Info("checking GameServer")
  1804  		if err == nil {
  1805  			return false
  1806  		}
  1807  		return k8serrors.IsNotFound(err)
  1808  	}, 40*time.Second, time.Second)
  1809  
  1810  	diff := int(time.Since(start).Seconds())
  1811  	log.WithField("diff", diff).Info("Time difference")
  1812  	require.Less(t, diff, 40)
  1813  }
  1814  
  1815  func TestGameServerSlowStart(t *testing.T) {
  1816  	t.Parallel()
  1817  
  1818  	// Inject an additional game server sidecar that forces a delayed start
  1819  	// to the main game server container following the pattern at
  1820  	// https://medium.com/@marko.luksa/delaying-application-start-until-sidecar-is-ready-2ec2d21a7b74
  1821  	gs := framework.DefaultGameServer(framework.Namespace)
  1822  	gs.Spec.Template.Spec.Containers = append(
  1823  		[]corev1.Container{{
  1824  			Name:            "delay-game-server-start",
  1825  			Image:           "alpine:latest",
  1826  			ImagePullPolicy: corev1.PullIfNotPresent,
  1827  			Command:         []string{"sleep", "3600"},
  1828  			Lifecycle: &corev1.Lifecycle{
  1829  				PostStart: &corev1.LifecycleHandler{
  1830  					Exec: &corev1.ExecAction{
  1831  						Command: []string{"sleep", "60"},
  1832  					},
  1833  				},
  1834  			},
  1835  			Resources: corev1.ResourceRequirements{
  1836  				Requests: corev1.ResourceList{
  1837  					corev1.ResourceCPU:    resource.MustParse("30m"),
  1838  					corev1.ResourceMemory: resource.MustParse("64Mi"),
  1839  				},
  1840  				Limits: corev1.ResourceList{
  1841  					corev1.ResourceCPU:    resource.MustParse("30m"),
  1842  					corev1.ResourceMemory: resource.MustParse("64Mi"),
  1843  				},
  1844  			},
  1845  		}},
  1846  		gs.Spec.Template.Spec.Containers...)
  1847  
  1848  	// Validate that a game server whose primary container starts slowly (a full minute
  1849  	// after the SDK starts) is capable of reaching Ready. Here we force the condition
  1850  	// with a lifecycle hook, but it imitates a slow image pull, or other container
  1851  	// start delays.
  1852  	_, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
  1853  	assert.NoError(t, err)
  1854  }
  1855  
  1856  func TestGameServerPatch(t *testing.T) {
  1857  	if !runtime.FeatureEnabled(runtime.FeatureCountsAndLists) {
  1858  		t.SkipNow()
  1859  	}
  1860  	t.Parallel()
  1861  	ctx := context.Background()
  1862  
  1863  	gs := framework.DefaultGameServer(framework.Namespace)
  1864  	gs, err := framework.CreateGameServerAndWaitUntilReady(t, framework.Namespace, gs)
  1865  	require.NoError(t, err)
  1866  	defer framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Delete(ctx, gs.ObjectMeta.Name, metav1.DeleteOptions{}) // nolint: errcheck
  1867  	assert.Equal(t, agonesv1.GameServerStateReady, gs.Status.State)
  1868  
  1869  	// Create a gameserver to patch against
  1870  	gsCopy := gs.DeepCopy()
  1871  	gsCopy.ObjectMeta.Labels = map[string]string{"foo": "foo-value"}
  1872  
  1873  	patch, err := gs.Patch(gsCopy)
  1874  	require.NoError(t, err)
  1875  	patchString := string(patch)
  1876  	require.Contains(t, patchString, fmt.Sprintf("{\"op\":\"test\",\"path\":\"/metadata/resourceVersion\",\"value\":%q}", gs.ObjectMeta.ResourceVersion))
  1877  	require.Contains(t, patchString, "{\"op\":\"add\",\"path\":\"/metadata/labels\",\"value\":{\"foo\":\"foo-value\"}}")
  1878  
  1879  	// Confirm patch is applied correctly
  1880  	patchedGs, err := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Patch(ctx, gs.GetObjectMeta().GetName(), types.JSONPatchType, patch, metav1.PatchOptions{})
  1881  	require.NoError(t, err)
  1882  	require.Equal(t, patchedGs.ObjectMeta.Labels, map[string]string{"foo": "foo-value"})
  1883  	require.NotEqual(t, patchedGs.ObjectMeta.ResourceVersion, gs.ObjectMeta.ResourceVersion)
  1884  
  1885  	// Confirm a patch applied to an old version of a game server is not applied
  1886  	gsCopy.ObjectMeta.Labels = map[string]string{"bar": "bar-value"}
  1887  	patch, err = gs.Patch(gsCopy)
  1888  	require.NoError(t, err)
  1889  
  1890  	_, err = framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Patch(ctx, gs.GetObjectMeta().GetName(), types.JSONPatchType, patch, metav1.PatchOptions{})
  1891  	require.Error(t, err)
  1892  
  1893  	getGs, err := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Get(ctx, gs.ObjectMeta.Name, metav1.GetOptions{})
  1894  	require.NoError(t, err)
  1895  	require.Equal(t, getGs.ObjectMeta.Labels, map[string]string{"foo": "foo-value"})
  1896  	require.Equal(t, getGs.ObjectMeta.ResourceVersion, patchedGs.ObjectMeta.ResourceVersion)
  1897  
  1898  	// Confirm patch goes through with the most up-to-date game server
  1899  	gsCopy = patchedGs.DeepCopy()
  1900  	gsCopy.ObjectMeta.Labels = map[string]string{"bar": "bar-value"}
  1901  	patch, err = patchedGs.Patch(gsCopy)
  1902  	require.NoError(t, err)
  1903  
  1904  	rePatchedGs, err := framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Patch(ctx, gs.GetObjectMeta().GetName(), types.JSONPatchType, patch, metav1.PatchOptions{})
  1905  	require.NoError(t, err)
  1906  	require.Equal(t, rePatchedGs.ObjectMeta.Labels, map[string]string{"bar": "bar-value"})
  1907  
  1908  	getGs, err = framework.AgonesClient.AgonesV1().GameServers(framework.Namespace).Get(ctx, gs.ObjectMeta.Name, metav1.GetOptions{})
  1909  	require.NoError(t, err)
  1910  	require.Equal(t, getGs.ObjectMeta.Labels, map[string]string{"bar": "bar-value"})
  1911  	require.Equal(t, getGs.ObjectMeta.ResourceVersion, rePatchedGs.ObjectMeta.ResourceVersion)
  1912  }