agones.dev/agones@v1.53.0/pkg/gameservers/pernodecounter_test.go (about)

     1  // Copyright 2019 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  	"time"
    20  
    21  	agonesv1 "agones.dev/agones/pkg/apis/agones/v1"
    22  	agtesting "agones.dev/agones/pkg/testing"
    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  	"k8s.io/apimachinery/pkg/runtime"
    28  	"k8s.io/apimachinery/pkg/watch"
    29  	k8stesting "k8s.io/client-go/testing"
    30  )
    31  
    32  const (
    33  	defaultNs = "default"
    34  	name1     = "node1"
    35  	name2     = "node2"
    36  )
    37  
    38  func TestPerNodeCounterGameServerEvents(t *testing.T) {
    39  	t.Parallel()
    40  
    41  	pnc, m := newFakePerNodeCounter()
    42  
    43  	fakeWatch := watch.NewFake()
    44  	m.AgonesClient.AddWatchReactor("gameservers", k8stesting.DefaultWatchReactor(fakeWatch, nil))
    45  
    46  	_, cancel := agtesting.StartInformers(m)
    47  	defer cancel()
    48  
    49  	assert.Empty(t, pnc.Counts())
    50  
    51  	gs := &agonesv1.GameServer{
    52  		ObjectMeta: metav1.ObjectMeta{Name: "gs1", Namespace: defaultNs},
    53  		Status: agonesv1.GameServerStatus{
    54  			State: agonesv1.GameServerStateStarting, NodeName: name1,
    55  		},
    56  	}
    57  
    58  	fakeWatch.Add(gs.DeepCopy())
    59  	require.Eventuallyf(t, func() bool {
    60  		return len(pnc.Counts()) == 0
    61  	}, 5*time.Second, time.Second, "Should be empty, instead has %v elements", len(pnc.Counts()))
    62  
    63  	gs.Status.State = agonesv1.GameServerStateReady
    64  	fakeWatch.Add(gs.DeepCopy())
    65  
    66  	var counts map[string]NodeCount
    67  	require.Eventuallyf(t, func() bool {
    68  		counts = pnc.Counts()
    69  		return len(counts) == 1
    70  	}, 5*time.Second, time.Second, "len should be 1, instead: %v", len(counts))
    71  	assert.Equal(t, int64(1), counts[name1].Ready)
    72  	assert.Equal(t, int64(0), counts[name1].Allocated)
    73  
    74  	gs.Status.State = agonesv1.GameServerStateAllocated
    75  	fakeWatch.Add(gs.DeepCopy())
    76  
    77  	require.Eventuallyf(t, func() bool {
    78  		counts = pnc.Counts()
    79  		return len(counts) == 1 && int64(0) == counts[name1].Ready
    80  	}, 5*time.Second, time.Second, "Ready should be 0, but is instead", counts[name1].Ready)
    81  	assert.Equal(t, int64(1), counts[name1].Allocated)
    82  
    83  	gs.Status.State = agonesv1.GameServerStateShutdown
    84  	fakeWatch.Add(gs.DeepCopy())
    85  	require.Eventuallyf(t, func() bool {
    86  		counts = pnc.Counts()
    87  		return len(counts) == 1 && int64(0) == counts[name1].Allocated
    88  	}, 5*time.Second, time.Second, "Allocated should be 0, but is instead", counts[name1].Allocated)
    89  	assert.Equal(t, int64(0), counts[name1].Ready)
    90  
    91  	gs.ObjectMeta.Name = "gs2"
    92  	gs.Status.State = agonesv1.GameServerStateReady
    93  	gs.Status.NodeName = name2
    94  
    95  	fakeWatch.Add(gs.DeepCopy())
    96  	require.Eventuallyf(t, func() bool {
    97  		counts = pnc.Counts()
    98  		return len(counts) == 2
    99  	}, 5*time.Second, time.Second, "len should be 2, instead: %v", len(counts))
   100  	assert.Equal(t, int64(0), counts[name1].Ready)
   101  	assert.Equal(t, int64(0), counts[name1].Allocated)
   102  	assert.Equal(t, int64(1), counts[name2].Ready)
   103  	assert.Equal(t, int64(0), counts[name2].Allocated)
   104  
   105  	gs.ObjectMeta.Name = "gs3"
   106  	// not likely, but to test the flow
   107  	gs.Status.State = agonesv1.GameServerStateAllocated
   108  	gs.Status.NodeName = name2
   109  
   110  	fakeWatch.Add(gs.DeepCopy())
   111  	require.Eventuallyf(t, func() bool {
   112  		counts = pnc.Counts()
   113  		return len(counts) == 2 && int64(1) == counts[name2].Allocated
   114  	}, 5*time.Second, time.Second, "Allocated should be 1, but is instead", counts[name2].Allocated)
   115  	assert.Equal(t, int64(0), counts[name1].Ready)
   116  	assert.Equal(t, int64(0), counts[name1].Allocated)
   117  	assert.Equal(t, int64(1), counts[name2].Ready)
   118  }
   119  
   120  func TestPerNodeCounterNodeEvents(t *testing.T) {
   121  	t.Parallel()
   122  
   123  	pnc, m := newFakePerNodeCounter()
   124  
   125  	gsWatch := watch.NewFake()
   126  	nodeWatch := watch.NewFake()
   127  	m.AgonesClient.AddWatchReactor("gameservers", k8stesting.DefaultWatchReactor(gsWatch, nil))
   128  	m.KubeClient.AddWatchReactor("nodes", k8stesting.DefaultWatchReactor(nodeWatch, nil))
   129  
   130  	_, cancel := agtesting.StartInformers(m)
   131  	defer cancel()
   132  
   133  	require.Empty(t, pnc.Counts())
   134  
   135  	gs := &agonesv1.GameServer{
   136  		ObjectMeta: metav1.ObjectMeta{Name: "gs1", Namespace: defaultNs},
   137  		Status: agonesv1.GameServerStatus{
   138  			State: agonesv1.GameServerStateReady, NodeName: name1}}
   139  	node := &corev1.Node{ObjectMeta: metav1.ObjectMeta{Namespace: defaultNs, Name: name1}}
   140  
   141  	gsWatch.Add(gs.DeepCopy())
   142  	nodeWatch.Add(node.DeepCopy())
   143  	require.Eventuallyf(t, func() bool {
   144  		return len(pnc.Counts()) == 1
   145  	}, 5*time.Second, time.Second, "Should be 1 element, not %v", len(pnc.Counts()))
   146  
   147  	nodeWatch.Delete(node.DeepCopy())
   148  	require.Eventually(t, func() bool {
   149  		return len(pnc.Counts()) == 0
   150  	}, 5*time.Second, time.Second, "pnc.Counts() should be empty, but is instead has %v element", len(pnc.Counts()))
   151  }
   152  
   153  func TestPerNodeCounterRun(t *testing.T) {
   154  	t.Parallel()
   155  	pnc, m := newFakePerNodeCounter()
   156  
   157  	gs1 := &agonesv1.GameServer{
   158  		ObjectMeta: metav1.ObjectMeta{Name: "gs1", Namespace: defaultNs},
   159  		Status: agonesv1.GameServerStatus{
   160  			State: agonesv1.GameServerStateReady, NodeName: name1}}
   161  
   162  	gs2 := gs1.DeepCopy()
   163  	gs2.ObjectMeta.Name = "gs2"
   164  	gs2.Status.State = agonesv1.GameServerStateAllocated
   165  
   166  	gs3 := gs1.DeepCopy()
   167  	gs3.ObjectMeta.Name = "gs3"
   168  	gs3.Status.State = agonesv1.GameServerStateStarting
   169  	gs3.Status.NodeName = name2
   170  
   171  	gs4 := gs1.DeepCopy()
   172  	gs4.ObjectMeta.Name = "gs4"
   173  	gs4.Status.State = agonesv1.GameServerStateAllocated
   174  
   175  	m.AgonesClient.AddReactor("list", "gameservers", func(_ k8stesting.Action) (bool, runtime.Object, error) {
   176  		return true, &agonesv1.GameServerList{Items: []agonesv1.GameServer{*gs1, *gs2, *gs3, *gs4}}, nil
   177  	})
   178  
   179  	ctx, cancel := agtesting.StartInformers(m, pnc.gameServerSynced)
   180  	defer cancel()
   181  
   182  	err := pnc.Run(ctx, 0)
   183  	assert.Nil(t, err)
   184  
   185  	counts := pnc.Counts()
   186  
   187  	require.Len(t, counts, 2)
   188  	assert.Equal(t, int64(1), counts[name1].Ready)
   189  	assert.Equal(t, int64(2), counts[name1].Allocated)
   190  	assert.Equal(t, int64(0), counts[name2].Ready)
   191  	assert.Equal(t, int64(0), counts[name2].Allocated)
   192  }
   193  
   194  // newFakeController returns a controller, backed by the fake Clientset
   195  func newFakePerNodeCounter() (*PerNodeCounter, agtesting.Mocks) {
   196  	m := agtesting.NewMocks()
   197  	c := NewPerNodeCounter(m.KubeInformerFactory, m.AgonesInformerFactory)
   198  	return c, m
   199  }