agones.dev/agones@v1.53.0/pkg/apis/agones/v1/fleet_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 v1
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  	"testing"
    21  
    22  	"github.com/stretchr/testify/assert"
    23  	"github.com/stretchr/testify/require"
    24  	appsv1 "k8s.io/api/apps/v1"
    25  	corev1 "k8s.io/api/core/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/util/intstr"
    28  	"k8s.io/apimachinery/pkg/util/validation"
    29  	"k8s.io/apimachinery/pkg/util/validation/field"
    30  
    31  	"agones.dev/agones/pkg"
    32  	"agones.dev/agones/pkg/apis"
    33  	"agones.dev/agones/pkg/util/runtime"
    34  )
    35  
    36  func TestFleetGameServerSetGameServer(t *testing.T) {
    37  	t.Parallel()
    38  
    39  	f := Fleet{
    40  		ObjectMeta: metav1.ObjectMeta{
    41  			Name:      "test",
    42  			Namespace: "namespace",
    43  			UID:       "1234",
    44  		},
    45  		Spec: FleetSpec{
    46  			Replicas:   10,
    47  			Scheduling: apis.Packed,
    48  			Template: GameServerTemplateSpec{
    49  				Spec: GameServerSpec{
    50  					Ports: []GameServerPort{{ContainerPort: 1234}},
    51  					Template: corev1.PodTemplateSpec{
    52  						Spec: corev1.PodSpec{
    53  							Containers: []corev1.Container{{Name: "container", Image: "myimage"}},
    54  						},
    55  					},
    56  				},
    57  			},
    58  		},
    59  	}
    60  
    61  	gsSet := f.GameServerSet()
    62  	assert.Equal(t, "", gsSet.ObjectMeta.Name)
    63  	assert.Equal(t, f.ObjectMeta.Namespace, gsSet.ObjectMeta.Namespace)
    64  	assert.Equal(t, f.ObjectMeta.Name+"-", gsSet.ObjectMeta.GenerateName)
    65  	assert.Equal(t, f.ObjectMeta.Name, gsSet.ObjectMeta.Labels[FleetNameLabel])
    66  	assert.Equal(t, int32(0), gsSet.Spec.Replicas)
    67  	assert.Equal(t, f.Spec.Scheduling, gsSet.Spec.Scheduling)
    68  	assert.Equal(t, f.Spec.Template, gsSet.Spec.Template)
    69  	assert.True(t, metav1.IsControlledBy(gsSet, &f))
    70  
    71  	runtime.FeatureTestMutex.Lock()
    72  	defer runtime.FeatureTestMutex.Unlock()
    73  
    74  	runtime.Must(runtime.ParseFeatures(fmt.Sprintf("%s=true", runtime.FeatureCountsAndLists)))
    75  	gsSet = f.GameServerSet()
    76  	assert.Nil(t, gsSet.Spec.AllocationOverflow)
    77  
    78  	f.Spec.AllocationOverflow = &AllocationOverflow{
    79  		Labels:      map[string]string{"stuff": "things"},
    80  		Annotations: nil,
    81  	}
    82  
    83  	assert.Nil(t, f.Spec.Priorities)
    84  	f.Spec.Priorities = []Priority{
    85  		{Type: "Counter",
    86  			Key:   "Foo",
    87  			Order: "Ascending"}}
    88  	assert.NotNil(t, f.Spec.Priorities)
    89  	assert.Equal(t, f.Spec.Priorities[0], Priority{Type: "Counter", Key: "Foo", Order: "Ascending"})
    90  
    91  	gsSet = f.GameServerSet()
    92  	assert.NotNil(t, gsSet.Spec.AllocationOverflow)
    93  	assert.Equal(t, "things", gsSet.Spec.AllocationOverflow.Labels["stuff"])
    94  
    95  	assert.Equal(t, gsSet.Spec.Priorities[0], Priority{Type: "Counter", Key: "Foo", Order: "Ascending"})
    96  }
    97  
    98  func TestFleetApplyDefaults(t *testing.T) {
    99  	f := &Fleet{}
   100  
   101  	// gate
   102  	assert.EqualValues(t, "", f.Spec.Strategy.Type)
   103  	assert.EqualValues(t, "", f.Spec.Scheduling)
   104  	assert.EqualValues(t, 0, f.Spec.Replicas)
   105  
   106  	f.ApplyDefaults()
   107  	assert.Equal(t, appsv1.RollingUpdateDeploymentStrategyType, f.Spec.Strategy.Type)
   108  	assert.Equal(t, "25%", f.Spec.Strategy.RollingUpdate.MaxUnavailable.String())
   109  	assert.Equal(t, "25%", f.Spec.Strategy.RollingUpdate.MaxSurge.String())
   110  	assert.Equal(t, apis.Packed, f.Spec.Scheduling)
   111  	assert.Equal(t, int32(0), f.Spec.Replicas)
   112  	assert.Equal(t, pkg.Version, f.ObjectMeta.Annotations[VersionAnnotation])
   113  
   114  	// Test apply defaults is idempotent -- calling ApplyDefaults more than one time does not change the original result.
   115  	f.ApplyDefaults()
   116  	assert.Equal(t, appsv1.RollingUpdateDeploymentStrategyType, f.Spec.Strategy.Type)
   117  	assert.Equal(t, "25%", f.Spec.Strategy.RollingUpdate.MaxUnavailable.String())
   118  	assert.Equal(t, "25%", f.Spec.Strategy.RollingUpdate.MaxSurge.String())
   119  	assert.Equal(t, apis.Packed, f.Spec.Scheduling)
   120  	assert.Equal(t, int32(0), f.Spec.Replicas)
   121  	assert.Equal(t, pkg.Version, f.ObjectMeta.Annotations[VersionAnnotation])
   122  }
   123  
   124  func TestFleetUpperBoundReplicas(t *testing.T) {
   125  	f := &Fleet{Spec: FleetSpec{Replicas: 10}}
   126  
   127  	assert.Equal(t, int32(10), f.UpperBoundReplicas(12))
   128  	assert.Equal(t, int32(10), f.UpperBoundReplicas(10))
   129  	assert.Equal(t, int32(5), f.UpperBoundReplicas(5))
   130  }
   131  
   132  func TestFleetLowerBoundReplicas(t *testing.T) {
   133  	f := &Fleet{Spec: FleetSpec{Replicas: 10}}
   134  
   135  	assert.Equal(t, int32(5), f.LowerBoundReplicas(5))
   136  	assert.Equal(t, int32(0), f.LowerBoundReplicas(0))
   137  	assert.Equal(t, int32(0), f.LowerBoundReplicas(-5))
   138  }
   139  
   140  func TestSumStatusAllocatedReplicas(t *testing.T) {
   141  	f := Fleet{}
   142  	gsSet1 := f.GameServerSet()
   143  	gsSet1.Status.AllocatedReplicas = 2
   144  
   145  	gsSet2 := f.GameServerSet()
   146  	gsSet2.Status.AllocatedReplicas = 3
   147  
   148  	assert.Equal(t, int32(5), SumStatusAllocatedReplicas([]*GameServerSet{gsSet1, gsSet2}))
   149  }
   150  
   151  func TestFleetGameserverSpec(t *testing.T) {
   152  	f := defaultFleet()
   153  	f.ApplyDefaults()
   154  	errs := f.Validate(fakeAPIHooks{})
   155  	assert.Len(t, errs, 0)
   156  
   157  	f.Spec.Template.Spec.Template =
   158  		corev1.PodTemplateSpec{
   159  			Spec: corev1.PodSpec{
   160  				Containers: []corev1.Container{{Name: "container", Image: "myimage"}, {Name: "container2", Image: "myimage"}},
   161  			},
   162  		}
   163  
   164  	errs = f.Validate(fakeAPIHooks{})
   165  	assert.Len(t, errs, 1)
   166  	assert.Equal(t, "spec.template.spec.container", errs[0].Field)
   167  
   168  	f.Spec.Template.Spec.Container = "testing"
   169  	errs = f.Validate(fakeAPIHooks{})
   170  	assert.Len(t, errs, 1)
   171  	assert.Equal(t, "Could not find a container named testing", errs[0].Detail)
   172  
   173  	f.Spec.Template.Spec.Container = "container"
   174  	errs = f.Validate(fakeAPIHooks{})
   175  	assert.Len(t, errs, 0)
   176  
   177  	// Verify RollingUpdate parameters validation
   178  	percent := intstr.FromString("0%")
   179  	f.Spec.Strategy.RollingUpdate.MaxUnavailable = &percent
   180  	f.Spec.Strategy.RollingUpdate.MaxSurge = &percent
   181  	errs = f.Validate(fakeAPIHooks{})
   182  	assert.Len(t, errs, 2)
   183  
   184  	intParam := intstr.FromInt(0)
   185  	f.Spec.Strategy.RollingUpdate.MaxUnavailable = &intParam
   186  	f.Spec.Strategy.RollingUpdate.MaxSurge = &intParam
   187  	errs = f.Validate(fakeAPIHooks{})
   188  	assert.Len(t, errs, 2)
   189  
   190  	percent = intstr.FromString("2a")
   191  	f.Spec.Strategy.RollingUpdate.MaxUnavailable = &percent
   192  	f.Spec.Strategy.RollingUpdate.MaxSurge = &percent
   193  	errs = f.Validate(fakeAPIHooks{})
   194  	assert.Len(t, errs, 2)
   195  
   196  	longName := strings.Repeat("f", validation.LabelValueMaxLength+1)
   197  	f = defaultFleet()
   198  	f.ApplyDefaults()
   199  	f.Spec.Template.ObjectMeta.Labels = make(map[string]string)
   200  	f.Spec.Template.ObjectMeta.Labels["label"] = longName
   201  	errs = f.Validate(fakeAPIHooks{})
   202  	assert.Len(t, errs, 1)
   203  
   204  	f = defaultFleet()
   205  	f.ApplyDefaults()
   206  	f.Spec.Template.Spec.Template.ObjectMeta.Labels = make(map[string]string)
   207  	f.Spec.Template.Spec.Template.ObjectMeta.Labels["label"] = longName
   208  	errs = f.Validate(fakeAPIHooks{})
   209  	assert.Len(t, errs, 1)
   210  
   211  	// Annotations test
   212  	f = defaultFleet()
   213  	f.ApplyDefaults()
   214  	f.Spec.Template.Spec.Template.ObjectMeta.Annotations = make(map[string]string)
   215  	f.Spec.Template.Spec.Template.ObjectMeta.Annotations[longName] = ""
   216  	errs = f.Validate(fakeAPIHooks{})
   217  	assert.Len(t, errs, 1)
   218  
   219  	// Strategy Type validation test
   220  	f = defaultFleet()
   221  	f.ApplyDefaults()
   222  	f.Spec.Strategy.Type = appsv1.DeploymentStrategyType("")
   223  	errs = f.Validate(fakeAPIHooks{})
   224  	assert.Len(t, errs, 1)
   225  }
   226  
   227  func TestFleetAllocationOverflow(t *testing.T) {
   228  	t.Parallel()
   229  
   230  	f := defaultFleet()
   231  	f.ApplyDefaults()
   232  
   233  	errs := f.Validate(fakeAPIHooks{})
   234  	require.Empty(t, errs)
   235  
   236  	f.Spec.AllocationOverflow = &AllocationOverflow{
   237  		Labels:      map[string]string{"$$$nope": "value"},
   238  		Annotations: nil,
   239  	}
   240  
   241  	errs = f.Validate(fakeAPIHooks{})
   242  	require.Len(t, errs, 1)
   243  	require.Equal(t, field.ErrorTypeInvalid, errs[0].Type)
   244  
   245  }
   246  
   247  func TestFleetName(t *testing.T) {
   248  	f := defaultFleet()
   249  	f.ApplyDefaults()
   250  
   251  	longName := strings.Repeat("f", validation.LabelValueMaxLength+1)
   252  	f.Name = longName
   253  	errs := f.Validate(fakeAPIHooks{})
   254  	assert.Len(t, errs, 1)
   255  	assert.Equal(t, "metadata.name", errs[0].Field)
   256  
   257  	f.Name = ""
   258  	f.GenerateName = longName
   259  	errs = f.Validate(fakeAPIHooks{})
   260  	assert.Len(t, errs, 0)
   261  }
   262  
   263  func TestSumStatusReplicas(t *testing.T) {
   264  	fixture := []*GameServerSet{
   265  		{Status: GameServerSetStatus{Replicas: 10}},
   266  		{Status: GameServerSetStatus{Replicas: 15}},
   267  		{Status: GameServerSetStatus{Replicas: 5}},
   268  	}
   269  
   270  	assert.Equal(t, int32(30), SumStatusReplicas(fixture))
   271  }
   272  
   273  func TestSumSpecReplicas(t *testing.T) {
   274  	fixture := []*GameServerSet{
   275  		{Spec: GameServerSetSpec{Replicas: 11}},
   276  		{Spec: GameServerSetSpec{Replicas: 14}},
   277  		{Spec: GameServerSetSpec{Replicas: 100}},
   278  		nil,
   279  	}
   280  
   281  	assert.Equal(t, int32(125), SumSpecReplicas(fixture))
   282  }
   283  
   284  func TestGetReadyReplicaCountForGameServerSets(t *testing.T) {
   285  	fixture := []*GameServerSet{
   286  		{Status: GameServerSetStatus{ReadyReplicas: 1000}},
   287  		{Status: GameServerSetStatus{ReadyReplicas: 15}},
   288  		{Status: GameServerSetStatus{ReadyReplicas: 5}},
   289  		nil,
   290  	}
   291  
   292  	assert.Equal(t, int32(1020), GetReadyReplicaCountForGameServerSets(fixture))
   293  }
   294  
   295  func TestSumGameServerSets(t *testing.T) {
   296  	fixture := []*GameServerSet{
   297  		{Status: GameServerSetStatus{ReadyReplicas: 1000}},
   298  		{Status: GameServerSetStatus{ReadyReplicas: 15}},
   299  		{Status: GameServerSetStatus{ReadyReplicas: 5}},
   300  		nil,
   301  	}
   302  
   303  	assert.Equal(t, int32(1020), SumGameServerSets(fixture, func(gsSet *GameServerSet) int32 {
   304  		return gsSet.Status.ReadyReplicas
   305  	}))
   306  
   307  	assert.Equal(t, int32(0), SumGameServerSets(fixture, func(gsSet *GameServerSet) int32 {
   308  		return gsSet.Status.Replicas
   309  	}))
   310  }
   311  
   312  func defaultFleet() *Fleet {
   313  	gs := GameServer{
   314  		Spec: GameServerSpec{
   315  			Template: corev1.PodTemplateSpec{
   316  				Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "testing", Image: "testing/image"}}}}},
   317  	}
   318  	return &Fleet{
   319  		ObjectMeta: metav1.ObjectMeta{GenerateName: "simple-fleet-", Namespace: "defaultNs"},
   320  		Spec: FleetSpec{
   321  			Replicas: 2,
   322  			Template: GameServerTemplateSpec{
   323  				Spec: gs.Spec,
   324  			},
   325  		},
   326  	}
   327  }