agones.dev/agones@v1.54.0/pkg/gameservers/gameservers_test.go (about)

     1  // Copyright 2020 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 gameservers
    16  
    17  import (
    18  	"testing"
    19  
    20  	agonesv1 "agones.dev/agones/pkg/apis/agones/v1"
    21  	agtesting "agones.dev/agones/pkg/testing"
    22  	"agones.dev/agones/pkg/util/runtime"
    23  	"github.com/stretchr/testify/assert"
    24  	"github.com/stretchr/testify/require"
    25  	corev1 "k8s.io/api/core/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  )
    28  
    29  func TestIsGameServerPod(t *testing.T) {
    30  	t.Parallel()
    31  
    32  	t.Run("it is a game server pod", func(t *testing.T) {
    33  		gs := &agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gameserver", UID: "1234"}, Spec: newSingleContainerSpec()}
    34  		gs.ApplyDefaults()
    35  		pod, err := gs.Pod(agtesting.FakeAPIHooks{})
    36  		require.NoError(t, err)
    37  		assert.True(t, isGameServerPod(pod))
    38  	})
    39  
    40  	t.Run("it is not a game server pod", func(t *testing.T) {
    41  		pod := &corev1.Pod{}
    42  		assert.False(t, isGameServerPod(pod))
    43  	})
    44  }
    45  
    46  func TestAddress(t *testing.T) {
    47  	t.Parallel()
    48  
    49  	fixture := map[string]struct {
    50  		node            *corev1.Node
    51  		expectedAddress string
    52  		featureFlags    string
    53  	}{
    54  		"node with external ip": {
    55  			node:            &corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: nodeFixtureName}, Status: corev1.NodeStatus{Addresses: []corev1.NodeAddress{{Address: "12.12.12.12", Type: corev1.NodeExternalIP}}}},
    56  			expectedAddress: "12.12.12.12",
    57  		},
    58  		"node with an internal ip": {
    59  			node:            &corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: nodeFixtureName}, Status: corev1.NodeStatus{Addresses: []corev1.NodeAddress{{Address: "11.11.11.11", Type: corev1.NodeInternalIP}}}},
    60  			expectedAddress: "11.11.11.11",
    61  		},
    62  		"node with internal and external ip": {
    63  			node: &corev1.Node{
    64  				ObjectMeta: metav1.ObjectMeta{Name: nodeFixtureName},
    65  				Status: corev1.NodeStatus{Addresses: []corev1.NodeAddress{
    66  					{Address: "9.9.9.8", Type: corev1.NodeExternalIP},
    67  					{Address: "12.12.12.12", Type: corev1.NodeInternalIP},
    68  				}},
    69  			},
    70  			expectedAddress: "9.9.9.8",
    71  		},
    72  		"node with external and internal dns": {
    73  			node: &corev1.Node{
    74  				ObjectMeta: metav1.ObjectMeta{Name: nodeFixtureName},
    75  				Status: corev1.NodeStatus{Addresses: []corev1.NodeAddress{
    76  					{Address: "external.example.com", Type: corev1.NodeExternalDNS},
    77  					{Address: "internal.example.com", Type: corev1.NodeInternalDNS},
    78  				}},
    79  			},
    80  			expectedAddress: "external.example.com",
    81  		},
    82  		"node with external and internal dns without feature flag": {
    83  			node: &corev1.Node{
    84  				ObjectMeta: metav1.ObjectMeta{Name: nodeFixtureName},
    85  				Status: corev1.NodeStatus{Addresses: []corev1.NodeAddress{
    86  					{Address: "external.example.com", Type: corev1.NodeExternalDNS},
    87  					{Address: "internal.example.com", Type: corev1.NodeInternalDNS},
    88  					{Address: "9.9.9.8", Type: corev1.NodeExternalIP},
    89  					{Address: "12.12.12.12", Type: corev1.NodeInternalIP},
    90  				}},
    91  			},
    92  			expectedAddress: "external.example.com",
    93  		},
    94  	}
    95  
    96  	dummyGS := &agonesv1.GameServer{}
    97  	dummyGS.Name = "some-gs"
    98  
    99  	runtime.FeatureTestMutex.Lock()
   100  	defer runtime.FeatureTestMutex.Unlock()
   101  
   102  	for name, fixture := range fixture {
   103  		t.Run(name, func(t *testing.T) {
   104  			err := runtime.ParseFeatures(fixture.featureFlags)
   105  			assert.NoError(t, err)
   106  
   107  			addr, addrs, err := address(fixture.node)
   108  			require.NoError(t, err)
   109  			assert.Equal(t, fixture.expectedAddress, addr)
   110  			assert.Equal(t, fixture.node.Status.Addresses, addrs)
   111  		})
   112  	}
   113  }
   114  
   115  func TestApplyGameServerAddressAndPort(t *testing.T) {
   116  	t.Parallel()
   117  
   118  	noopMod := func(*corev1.Pod) {}
   119  	noopSyncer := func(*agonesv1.GameServer, *corev1.Pod) error { return nil }
   120  	for name, tc := range map[string]struct {
   121  		podMod    func(*corev1.Pod)
   122  		podSyncer func(*agonesv1.GameServer, *corev1.Pod) error
   123  		wantPort  int32
   124  	}{
   125  		"normal": {noopMod, noopSyncer, 9999},
   126  		"host ports changed after create": {
   127  			podMod: func(pod *corev1.Pod) {
   128  				pod.Spec.Containers[0].Ports[0].HostPort = 9876
   129  			},
   130  			podSyncer: func(gs *agonesv1.GameServer, pod *corev1.Pod) error {
   131  				gs.Spec.Ports[0].HostPort = pod.Spec.Containers[0].Ports[0].HostPort
   132  				return nil
   133  			},
   134  			wantPort: 9876,
   135  		},
   136  		"container port with PortPolicy None changed after create": {
   137  			podMod: func(pod *corev1.Pod) {
   138  				pod.Spec.Containers[0].Ports[0].ContainerPort = 9876
   139  			},
   140  			podSyncer: func(gs *agonesv1.GameServer, pod *corev1.Pod) error {
   141  				gs.Spec.Ports[0].PortPolicy = agonesv1.None
   142  				gs.Spec.Ports[0].ContainerPort = pod.Spec.Containers[0].Ports[0].ContainerPort
   143  				return nil
   144  			},
   145  			wantPort: 9876,
   146  		},
   147  		"Pod IP changed after create": {
   148  			podMod: func(pod *corev1.Pod) {
   149  				pod.Status.PodIPs = []corev1.PodIP{
   150  					{IP: ipFixture},
   151  				}
   152  			},
   153  			podSyncer: noopSyncer,
   154  			wantPort:  9999,
   155  		},
   156  	} {
   157  		t.Run(name, func(t *testing.T) {
   158  			gsFixture := &agonesv1.GameServer{
   159  				ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"},
   160  				Spec:       newSingleContainerSpec(), Status: agonesv1.GameServerStatus{State: agonesv1.GameServerStateRequestReady},
   161  			}
   162  			gsFixture.ApplyDefaults()
   163  			node := &corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: nodeFixtureName}, Status: corev1.NodeStatus{Addresses: []corev1.NodeAddress{{Address: ipFixture, Type: corev1.NodeExternalIP}}}}
   164  			pod, err := gsFixture.Pod(agtesting.FakeAPIHooks{})
   165  			require.NoError(t, err)
   166  			pod.Spec.NodeName = node.ObjectMeta.Name
   167  			pod.Status.PodIPs = []corev1.PodIP{{IP: ipFixture}}
   168  			tc.podMod(pod)
   169  
   170  			// PortPolicy None is behind a feature flag
   171  			runtime.FeatureTestMutex.Lock()
   172  			defer runtime.FeatureTestMutex.Unlock()
   173  			require.NoError(t, runtime.ParseFeatures(string(runtime.FeaturePortPolicyNone)+"=true"))
   174  
   175  			gs, err := applyGameServerAddressAndPort(gsFixture, node, pod, tc.podSyncer)
   176  			require.NoError(t, err)
   177  			if assert.NotEmpty(t, gs.Spec.Ports) {
   178  				assert.Equal(t, tc.wantPort, gs.Status.Ports[0].Port)
   179  			}
   180  			assert.Equal(t, ipFixture, gs.Status.Address)
   181  			assert.Equal(t, node.ObjectMeta.Name, gs.Status.NodeName)
   182  			assert.Equal(t, []corev1.NodeAddress{
   183  				{Address: ipFixture, Type: "ExternalIP"},
   184  				{Address: ipFixture, Type: "PodIP"},
   185  			}, gs.Status.Addresses)
   186  		})
   187  	}
   188  
   189  	t.Run("No IP specified, err expected", func(t *testing.T) {
   190  		gsFixture := &agonesv1.GameServer{
   191  			ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"},
   192  			Spec:       newSingleContainerSpec(), Status: agonesv1.GameServerStatus{State: agonesv1.GameServerStateRequestReady},
   193  		}
   194  		gsFixture.ApplyDefaults()
   195  		node := &corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: nodeFixtureName}, Status: corev1.NodeStatus{Addresses: []corev1.NodeAddress{}}}
   196  		pod, err := gsFixture.Pod(agtesting.FakeAPIHooks{})
   197  		require.NoError(t, err)
   198  		pod.Spec.NodeName = node.ObjectMeta.Name
   199  
   200  		_, err = applyGameServerAddressAndPort(gsFixture, node, pod, noopSyncer)
   201  		if assert.Error(t, err) {
   202  			assert.Equal(t, "error getting external address for GameServer test: Could not find an address for Node: node1", err.Error())
   203  		}
   204  	})
   205  }
   206  
   207  func TestIsBeforePodCreated(t *testing.T) {
   208  	fixture := map[string]struct {
   209  		state    agonesv1.GameServerState
   210  		expected bool
   211  	}{
   212  		"port":      {state: agonesv1.GameServerStatePortAllocation, expected: true},
   213  		"creating":  {state: agonesv1.GameServerStateCreating, expected: true},
   214  		"starting":  {state: agonesv1.GameServerStateStarting, expected: true},
   215  		"allocated": {state: agonesv1.GameServerStateAllocated, expected: false},
   216  		"ready":     {state: agonesv1.GameServerStateReady, expected: false},
   217  	}
   218  
   219  	for k, v := range fixture {
   220  		t.Run(k, func(t *testing.T) {
   221  			gs := &agonesv1.GameServer{Status: agonesv1.GameServerStatus{State: v.state}}
   222  
   223  			assert.Equal(t, v.expected, isBeforePodCreated(gs))
   224  		})
   225  	}
   226  }