...

Source file src/k8s.io/kubernetes/pkg/kubelet/cm/helpers_linux_test.go

Documentation: k8s.io/kubernetes/pkg/kubelet/cm

     1  //go:build linux
     2  // +build linux
     3  
     4  /*
     5  Copyright 2015 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package cm
    21  
    22  import (
    23  	"reflect"
    24  	"strconv"
    25  	"testing"
    26  	"time"
    27  
    28  	v1 "k8s.io/api/core/v1"
    29  	"k8s.io/apimachinery/pkg/api/resource"
    30  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    31  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    32  	pkgfeatures "k8s.io/kubernetes/pkg/features"
    33  )
    34  
    35  // getResourceList returns a ResourceList with the
    36  // specified cpu and memory resource values
    37  func getResourceList(cpu, memory string) v1.ResourceList {
    38  	res := v1.ResourceList{}
    39  	if cpu != "" {
    40  		res[v1.ResourceCPU] = resource.MustParse(cpu)
    41  	}
    42  	if memory != "" {
    43  		res[v1.ResourceMemory] = resource.MustParse(memory)
    44  	}
    45  	return res
    46  }
    47  
    48  // getResourceRequirements returns a ResourceRequirements object
    49  func getResourceRequirements(requests, limits v1.ResourceList) v1.ResourceRequirements {
    50  	res := v1.ResourceRequirements{}
    51  	res.Requests = requests
    52  	res.Limits = limits
    53  	return res
    54  }
    55  
    56  func TestResourceConfigForPod(t *testing.T) {
    57  	defaultQuotaPeriod := uint64(100 * time.Millisecond / time.Microsecond) // in microseconds
    58  	tunedQuotaPeriod := uint64(5 * time.Millisecond / time.Microsecond)     // in microseconds
    59  
    60  	minShares := uint64(MinShares)
    61  	burstableShares := MilliCPUToShares(100)
    62  	memoryQuantity := resource.MustParse("200Mi")
    63  	burstableMemory := memoryQuantity.Value()
    64  	burstablePartialShares := MilliCPUToShares(200)
    65  	burstableQuota := MilliCPUToQuota(200, int64(defaultQuotaPeriod))
    66  	guaranteedShares := MilliCPUToShares(100)
    67  	guaranteedQuota := MilliCPUToQuota(100, int64(defaultQuotaPeriod))
    68  	guaranteedTunedQuota := MilliCPUToQuota(100, int64(tunedQuotaPeriod))
    69  	memoryQuantity = resource.MustParse("100Mi")
    70  	cpuNoLimit := int64(-1)
    71  	guaranteedMemory := memoryQuantity.Value()
    72  	testCases := map[string]struct {
    73  		pod              *v1.Pod
    74  		expected         *ResourceConfig
    75  		enforceCPULimits bool
    76  		quotaPeriod      uint64 // in microseconds
    77  	}{
    78  		"besteffort": {
    79  			pod: &v1.Pod{
    80  				Spec: v1.PodSpec{
    81  					Containers: []v1.Container{
    82  						{
    83  							Resources: getResourceRequirements(getResourceList("", ""), getResourceList("", "")),
    84  						},
    85  					},
    86  				},
    87  			},
    88  			enforceCPULimits: true,
    89  			quotaPeriod:      defaultQuotaPeriod,
    90  			expected:         &ResourceConfig{CPUShares: &minShares},
    91  		},
    92  		"burstable-no-limits": {
    93  			pod: &v1.Pod{
    94  				Spec: v1.PodSpec{
    95  					Containers: []v1.Container{
    96  						{
    97  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")),
    98  						},
    99  					},
   100  				},
   101  			},
   102  			enforceCPULimits: true,
   103  			quotaPeriod:      defaultQuotaPeriod,
   104  			expected:         &ResourceConfig{CPUShares: &burstableShares},
   105  		},
   106  		"burstable-with-limits": {
   107  			pod: &v1.Pod{
   108  				Spec: v1.PodSpec{
   109  					Containers: []v1.Container{
   110  						{
   111  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   112  						},
   113  					},
   114  				},
   115  			},
   116  			enforceCPULimits: true,
   117  			quotaPeriod:      defaultQuotaPeriod,
   118  			expected:         &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &burstableQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory},
   119  		},
   120  		"burstable-with-limits-no-cpu-enforcement": {
   121  			pod: &v1.Pod{
   122  				Spec: v1.PodSpec{
   123  					Containers: []v1.Container{
   124  						{
   125  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   126  						},
   127  					},
   128  				},
   129  			},
   130  			enforceCPULimits: false,
   131  			quotaPeriod:      defaultQuotaPeriod,
   132  			expected:         &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &cpuNoLimit, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory},
   133  		},
   134  		"burstable-partial-limits": {
   135  			pod: &v1.Pod{
   136  				Spec: v1.PodSpec{
   137  					Containers: []v1.Container{
   138  						{
   139  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   140  						},
   141  						{
   142  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")),
   143  						},
   144  					},
   145  				},
   146  			},
   147  			enforceCPULimits: true,
   148  			quotaPeriod:      defaultQuotaPeriod,
   149  			expected:         &ResourceConfig{CPUShares: &burstablePartialShares},
   150  		},
   151  		"burstable-with-limits-with-tuned-quota": {
   152  			pod: &v1.Pod{
   153  				Spec: v1.PodSpec{
   154  					Containers: []v1.Container{
   155  						{
   156  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   157  						},
   158  					},
   159  				},
   160  			},
   161  			enforceCPULimits: true,
   162  			quotaPeriod:      tunedQuotaPeriod,
   163  			expected:         &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &burstableQuota, CPUPeriod: &tunedQuotaPeriod, Memory: &burstableMemory},
   164  		},
   165  		"burstable-with-limits-no-cpu-enforcement-with-tuned-quota": {
   166  			pod: &v1.Pod{
   167  				Spec: v1.PodSpec{
   168  					Containers: []v1.Container{
   169  						{
   170  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   171  						},
   172  					},
   173  				},
   174  			},
   175  			enforceCPULimits: false,
   176  			quotaPeriod:      tunedQuotaPeriod,
   177  			expected:         &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &cpuNoLimit, CPUPeriod: &tunedQuotaPeriod, Memory: &burstableMemory},
   178  		},
   179  		"burstable-partial-limits-with-tuned-quota": {
   180  			pod: &v1.Pod{
   181  				Spec: v1.PodSpec{
   182  					Containers: []v1.Container{
   183  						{
   184  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   185  						},
   186  						{
   187  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")),
   188  						},
   189  					},
   190  				},
   191  			},
   192  			enforceCPULimits: true,
   193  			quotaPeriod:      tunedQuotaPeriod,
   194  			expected:         &ResourceConfig{CPUShares: &burstablePartialShares},
   195  		},
   196  		"guaranteed": {
   197  			pod: &v1.Pod{
   198  				Spec: v1.PodSpec{
   199  					Containers: []v1.Container{
   200  						{
   201  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
   202  						},
   203  					},
   204  				},
   205  			},
   206  			enforceCPULimits: true,
   207  			quotaPeriod:      defaultQuotaPeriod,
   208  			expected:         &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &guaranteedQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &guaranteedMemory},
   209  		},
   210  		"guaranteed-no-cpu-enforcement": {
   211  			pod: &v1.Pod{
   212  				Spec: v1.PodSpec{
   213  					Containers: []v1.Container{
   214  						{
   215  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
   216  						},
   217  					},
   218  				},
   219  			},
   220  			enforceCPULimits: false,
   221  			quotaPeriod:      defaultQuotaPeriod,
   222  			expected:         &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &cpuNoLimit, CPUPeriod: &defaultQuotaPeriod, Memory: &guaranteedMemory},
   223  		},
   224  		"guaranteed-with-tuned-quota": {
   225  			pod: &v1.Pod{
   226  				Spec: v1.PodSpec{
   227  					Containers: []v1.Container{
   228  						{
   229  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
   230  						},
   231  					},
   232  				},
   233  			},
   234  			enforceCPULimits: true,
   235  			quotaPeriod:      tunedQuotaPeriod,
   236  			expected:         &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &guaranteedTunedQuota, CPUPeriod: &tunedQuotaPeriod, Memory: &guaranteedMemory},
   237  		},
   238  		"guaranteed-no-cpu-enforcement-with-tuned-quota": {
   239  			pod: &v1.Pod{
   240  				Spec: v1.PodSpec{
   241  					Containers: []v1.Container{
   242  						{
   243  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
   244  						},
   245  					},
   246  				},
   247  			},
   248  			enforceCPULimits: false,
   249  			quotaPeriod:      tunedQuotaPeriod,
   250  			expected:         &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &cpuNoLimit, CPUPeriod: &tunedQuotaPeriod, Memory: &guaranteedMemory},
   251  		},
   252  		"burstable-partial-limits-with-init-containers": {
   253  			pod: &v1.Pod{
   254  				Spec: v1.PodSpec{
   255  					Containers: []v1.Container{
   256  						{
   257  							Resources: getResourceRequirements(getResourceList("100m", "100m"), getResourceList("100m", "100Mi")),
   258  						},
   259  						{
   260  							Resources: getResourceRequirements(getResourceList("100m", "100m"), getResourceList("", "")),
   261  						},
   262  					},
   263  					InitContainers: []v1.Container{
   264  						{
   265  							Resources: getResourceRequirements(getResourceList("100m", "100m"), getResourceList("100m", "100Mi")),
   266  						},
   267  						{
   268  							Resources: getResourceRequirements(getResourceList("100m", "100m"), getResourceList("", "")),
   269  						},
   270  					},
   271  				},
   272  			},
   273  			enforceCPULimits: true,
   274  			quotaPeriod:      tunedQuotaPeriod,
   275  			expected:         &ResourceConfig{CPUShares: &burstablePartialShares},
   276  		},
   277  	}
   278  
   279  	for testName, testCase := range testCases {
   280  
   281  		actual := ResourceConfigForPod(testCase.pod, testCase.enforceCPULimits, testCase.quotaPeriod, false)
   282  
   283  		if !reflect.DeepEqual(actual.CPUPeriod, testCase.expected.CPUPeriod) {
   284  			t.Errorf("unexpected result, test: %v, cpu period not as expected. Expected: %v, Actual:%v", testName, *testCase.expected.CPUPeriod, *actual.CPUPeriod)
   285  		}
   286  		if !reflect.DeepEqual(actual.CPUQuota, testCase.expected.CPUQuota) {
   287  			t.Errorf("unexpected result, test: %v, cpu quota not as expected. Expected: %v, Actual:%v", testName, *testCase.expected.CPUQuota, *actual.CPUQuota)
   288  		}
   289  		if !reflect.DeepEqual(actual.CPUShares, testCase.expected.CPUShares) {
   290  			t.Errorf("unexpected result, test: %v, cpu shares not as expected. Expected: %v, Actual:%v", testName, *testCase.expected.CPUShares, &actual.CPUShares)
   291  		}
   292  		if !reflect.DeepEqual(actual.Memory, testCase.expected.Memory) {
   293  			t.Errorf("unexpected result, test: %v, memory not as expected. Expected: %v, Actual:%v", testName, *testCase.expected.Memory, *actual.Memory)
   294  		}
   295  	}
   296  }
   297  
   298  func TestResourceConfigForPodWithCustomCPUCFSQuotaPeriod(t *testing.T) {
   299  	defaultQuotaPeriod := uint64(100 * time.Millisecond / time.Microsecond) // in microseconds
   300  	tunedQuotaPeriod := uint64(5 * time.Millisecond / time.Microsecond)     // in microseconds
   301  	tunedQuota := int64(1 * time.Millisecond / time.Microsecond)
   302  
   303  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.CPUCFSQuotaPeriod, true)()
   304  
   305  	minShares := uint64(MinShares)
   306  	burstableShares := MilliCPUToShares(100)
   307  	memoryQuantity := resource.MustParse("200Mi")
   308  	burstableMemory := memoryQuantity.Value()
   309  	burstablePartialShares := MilliCPUToShares(200)
   310  	burstableQuota := MilliCPUToQuota(200, int64(defaultQuotaPeriod))
   311  	guaranteedShares := MilliCPUToShares(100)
   312  	guaranteedQuota := MilliCPUToQuota(100, int64(defaultQuotaPeriod))
   313  	guaranteedTunedQuota := MilliCPUToQuota(100, int64(tunedQuotaPeriod))
   314  	memoryQuantity = resource.MustParse("100Mi")
   315  	cpuNoLimit := int64(-1)
   316  	guaranteedMemory := memoryQuantity.Value()
   317  	testCases := map[string]struct {
   318  		pod              *v1.Pod
   319  		expected         *ResourceConfig
   320  		enforceCPULimits bool
   321  		quotaPeriod      uint64 // in microseconds
   322  	}{
   323  		"besteffort": {
   324  			pod: &v1.Pod{
   325  				Spec: v1.PodSpec{
   326  					Containers: []v1.Container{
   327  						{
   328  							Resources: getResourceRequirements(getResourceList("", ""), getResourceList("", "")),
   329  						},
   330  					},
   331  				},
   332  			},
   333  			enforceCPULimits: true,
   334  			quotaPeriod:      defaultQuotaPeriod,
   335  			expected:         &ResourceConfig{CPUShares: &minShares},
   336  		},
   337  		"burstable-no-limits": {
   338  			pod: &v1.Pod{
   339  				Spec: v1.PodSpec{
   340  					Containers: []v1.Container{
   341  						{
   342  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")),
   343  						},
   344  					},
   345  				},
   346  			},
   347  			enforceCPULimits: true,
   348  			quotaPeriod:      defaultQuotaPeriod,
   349  			expected:         &ResourceConfig{CPUShares: &burstableShares},
   350  		},
   351  		"burstable-with-limits": {
   352  			pod: &v1.Pod{
   353  				Spec: v1.PodSpec{
   354  					Containers: []v1.Container{
   355  						{
   356  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   357  						},
   358  					},
   359  				},
   360  			},
   361  			enforceCPULimits: true,
   362  			quotaPeriod:      defaultQuotaPeriod,
   363  			expected:         &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &burstableQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory},
   364  		},
   365  		"burstable-with-limits-no-cpu-enforcement": {
   366  			pod: &v1.Pod{
   367  				Spec: v1.PodSpec{
   368  					Containers: []v1.Container{
   369  						{
   370  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   371  						},
   372  					},
   373  				},
   374  			},
   375  			enforceCPULimits: false,
   376  			quotaPeriod:      defaultQuotaPeriod,
   377  			expected:         &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &cpuNoLimit, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory},
   378  		},
   379  		"burstable-partial-limits": {
   380  			pod: &v1.Pod{
   381  				Spec: v1.PodSpec{
   382  					Containers: []v1.Container{
   383  						{
   384  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   385  						},
   386  						{
   387  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")),
   388  						},
   389  					},
   390  				},
   391  			},
   392  			enforceCPULimits: true,
   393  			quotaPeriod:      defaultQuotaPeriod,
   394  			expected:         &ResourceConfig{CPUShares: &burstablePartialShares},
   395  		},
   396  		"burstable-with-limits-with-tuned-quota": {
   397  			pod: &v1.Pod{
   398  				Spec: v1.PodSpec{
   399  					Containers: []v1.Container{
   400  						{
   401  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   402  						},
   403  					},
   404  				},
   405  			},
   406  			enforceCPULimits: true,
   407  			quotaPeriod:      tunedQuotaPeriod,
   408  			expected:         &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &tunedQuota, CPUPeriod: &tunedQuotaPeriod, Memory: &burstableMemory},
   409  		},
   410  		"burstable-with-limits-no-cpu-enforcement-with-tuned-quota": {
   411  			pod: &v1.Pod{
   412  				Spec: v1.PodSpec{
   413  					Containers: []v1.Container{
   414  						{
   415  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   416  						},
   417  					},
   418  				},
   419  			},
   420  			enforceCPULimits: false,
   421  			quotaPeriod:      tunedQuotaPeriod,
   422  			expected:         &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &cpuNoLimit, CPUPeriod: &tunedQuotaPeriod, Memory: &burstableMemory},
   423  		},
   424  		"burstable-partial-limits-with-tuned-quota": {
   425  			pod: &v1.Pod{
   426  				Spec: v1.PodSpec{
   427  					Containers: []v1.Container{
   428  						{
   429  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   430  						},
   431  						{
   432  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")),
   433  						},
   434  					},
   435  				},
   436  			},
   437  			enforceCPULimits: true,
   438  			quotaPeriod:      tunedQuotaPeriod,
   439  			expected:         &ResourceConfig{CPUShares: &burstablePartialShares},
   440  		},
   441  		"guaranteed": {
   442  			pod: &v1.Pod{
   443  				Spec: v1.PodSpec{
   444  					Containers: []v1.Container{
   445  						{
   446  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
   447  						},
   448  					},
   449  				},
   450  			},
   451  			enforceCPULimits: true,
   452  			quotaPeriod:      defaultQuotaPeriod,
   453  			expected:         &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &guaranteedQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &guaranteedMemory},
   454  		},
   455  		"guaranteed-no-cpu-enforcement": {
   456  			pod: &v1.Pod{
   457  				Spec: v1.PodSpec{
   458  					Containers: []v1.Container{
   459  						{
   460  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
   461  						},
   462  					},
   463  				},
   464  			},
   465  			enforceCPULimits: false,
   466  			quotaPeriod:      defaultQuotaPeriod,
   467  			expected:         &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &cpuNoLimit, CPUPeriod: &defaultQuotaPeriod, Memory: &guaranteedMemory},
   468  		},
   469  		"guaranteed-with-tuned-quota": {
   470  			pod: &v1.Pod{
   471  				Spec: v1.PodSpec{
   472  					Containers: []v1.Container{
   473  						{
   474  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
   475  						},
   476  					},
   477  				},
   478  			},
   479  			enforceCPULimits: true,
   480  			quotaPeriod:      tunedQuotaPeriod,
   481  			expected:         &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &guaranteedTunedQuota, CPUPeriod: &tunedQuotaPeriod, Memory: &guaranteedMemory},
   482  		},
   483  		"guaranteed-no-cpu-enforcement-with-tuned-quota": {
   484  			pod: &v1.Pod{
   485  				Spec: v1.PodSpec{
   486  					Containers: []v1.Container{
   487  						{
   488  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
   489  						},
   490  					},
   491  				},
   492  			},
   493  			enforceCPULimits: false,
   494  			quotaPeriod:      tunedQuotaPeriod,
   495  			expected:         &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &cpuNoLimit, CPUPeriod: &tunedQuotaPeriod, Memory: &guaranteedMemory},
   496  		},
   497  	}
   498  
   499  	for testName, testCase := range testCases {
   500  
   501  		actual := ResourceConfigForPod(testCase.pod, testCase.enforceCPULimits, testCase.quotaPeriod, false)
   502  
   503  		if !reflect.DeepEqual(actual.CPUPeriod, testCase.expected.CPUPeriod) {
   504  			t.Errorf("unexpected result, test: %v, cpu period not as expected", testName)
   505  		}
   506  		if !reflect.DeepEqual(actual.CPUQuota, testCase.expected.CPUQuota) {
   507  			t.Errorf("unexpected result, test: %v, cpu quota not as expected", testName)
   508  		}
   509  		if !reflect.DeepEqual(actual.CPUShares, testCase.expected.CPUShares) {
   510  			t.Errorf("unexpected result, test: %v, cpu shares not as expected", testName)
   511  		}
   512  		if !reflect.DeepEqual(actual.Memory, testCase.expected.Memory) {
   513  			t.Errorf("unexpected result, test: %v, memory not as expected", testName)
   514  		}
   515  	}
   516  }
   517  
   518  func TestMilliCPUToQuota(t *testing.T) {
   519  	testCases := []struct {
   520  		input  int64
   521  		quota  int64
   522  		period uint64
   523  	}{
   524  		{
   525  			input:  int64(0),
   526  			quota:  int64(0),
   527  			period: uint64(0),
   528  		},
   529  		{
   530  			input:  int64(5),
   531  			quota:  int64(1000),
   532  			period: uint64(100000),
   533  		},
   534  		{
   535  			input:  int64(9),
   536  			quota:  int64(1000),
   537  			period: uint64(100000),
   538  		},
   539  		{
   540  			input:  int64(10),
   541  			quota:  int64(1000),
   542  			period: uint64(100000),
   543  		},
   544  		{
   545  			input:  int64(200),
   546  			quota:  int64(20000),
   547  			period: uint64(100000),
   548  		},
   549  		{
   550  			input:  int64(500),
   551  			quota:  int64(50000),
   552  			period: uint64(100000),
   553  		},
   554  		{
   555  			input:  int64(1000),
   556  			quota:  int64(100000),
   557  			period: uint64(100000),
   558  		},
   559  		{
   560  			input:  int64(1500),
   561  			quota:  int64(150000),
   562  			period: uint64(100000),
   563  		},
   564  	}
   565  	for _, testCase := range testCases {
   566  		quota := MilliCPUToQuota(testCase.input, int64(testCase.period))
   567  		if quota != testCase.quota {
   568  			t.Errorf("Input %v and %v, expected quota %v, but got quota %v", testCase.input, testCase.period, testCase.quota, quota)
   569  		}
   570  	}
   571  }
   572  
   573  func TestHugePageLimits(t *testing.T) {
   574  	Mi := int64(1024 * 1024)
   575  	type inputStruct struct {
   576  		key   string
   577  		input string
   578  	}
   579  
   580  	testCases := []struct {
   581  		name     string
   582  		inputs   []inputStruct
   583  		expected map[int64]int64
   584  	}{
   585  		{
   586  			name: "no valid hugepages",
   587  			inputs: []inputStruct{
   588  				{
   589  					key:   "2Mi",
   590  					input: "128",
   591  				},
   592  			},
   593  			expected: map[int64]int64{},
   594  		},
   595  		{
   596  			name: "2Mi only",
   597  			inputs: []inputStruct{
   598  				{
   599  					key:   v1.ResourceHugePagesPrefix + "2Mi",
   600  					input: "128",
   601  				},
   602  			},
   603  			expected: map[int64]int64{2 * Mi: 128},
   604  		},
   605  		{
   606  			name: "2Mi and 4Mi",
   607  			inputs: []inputStruct{
   608  				{
   609  					key:   v1.ResourceHugePagesPrefix + "2Mi",
   610  					input: "128",
   611  				},
   612  				{
   613  					key:   v1.ResourceHugePagesPrefix + strconv.FormatInt(2*Mi, 10),
   614  					input: "256",
   615  				},
   616  				{
   617  					key:   v1.ResourceHugePagesPrefix + "4Mi",
   618  					input: "512",
   619  				},
   620  				{
   621  					key:   "4Mi",
   622  					input: "1024",
   623  				},
   624  			},
   625  			expected: map[int64]int64{2 * Mi: 384, 4 * Mi: 512},
   626  		},
   627  	}
   628  
   629  	for _, testcase := range testCases {
   630  		t.Run(testcase.name, func(t *testing.T) {
   631  			resourceList := v1.ResourceList{}
   632  
   633  			for _, input := range testcase.inputs {
   634  				value, err := resource.ParseQuantity(input.input)
   635  				if err != nil {
   636  					t.Fatalf("error in parsing hugepages, value: %s", input.input)
   637  				} else {
   638  					resourceList[v1.ResourceName(input.key)] = value
   639  				}
   640  			}
   641  
   642  			resultValue := HugePageLimits(resourceList)
   643  
   644  			if !reflect.DeepEqual(testcase.expected, resultValue) {
   645  				t.Errorf("unexpected result for HugePageLimits(), expected: %v, actual: %v", testcase.expected, resultValue)
   646  			}
   647  
   648  			// ensure ResourceConfigForPod uses HugePageLimits correctly internally
   649  			p := v1.Pod{
   650  				Spec: v1.PodSpec{
   651  					Containers: []v1.Container{
   652  						{
   653  							Resources: v1.ResourceRequirements{
   654  								Requests: resourceList,
   655  							},
   656  						},
   657  					},
   658  				},
   659  			}
   660  			resultValuePod := ResourceConfigForPod(&p, false, 0, false)
   661  			if !reflect.DeepEqual(testcase.expected, resultValuePod.HugePageLimit) {
   662  				t.Errorf("unexpected result for ResourceConfigForPod(), expected: %v, actual: %v", testcase.expected, resultValuePod)
   663  			}
   664  		})
   665  	}
   666  }
   667  
   668  func TestResourceConfigForPodWithEnforceMemoryQoS(t *testing.T) {
   669  	defaultQuotaPeriod := uint64(100 * time.Millisecond / time.Microsecond) // in microseconds
   670  	tunedQuotaPeriod := uint64(5 * time.Millisecond / time.Microsecond)     // in microseconds
   671  
   672  	minShares := uint64(MinShares)
   673  	burstableShares := MilliCPUToShares(100)
   674  	memoryQuantity := resource.MustParse("200Mi")
   675  	burstableMemory := memoryQuantity.Value()
   676  	burstablePartialShares := MilliCPUToShares(200)
   677  	burstableQuota := MilliCPUToQuota(200, int64(defaultQuotaPeriod))
   678  	guaranteedShares := MilliCPUToShares(100)
   679  	guaranteedQuota := MilliCPUToQuota(100, int64(defaultQuotaPeriod))
   680  	guaranteedTunedQuota := MilliCPUToQuota(100, int64(tunedQuotaPeriod))
   681  	memoryQuantity = resource.MustParse("100Mi")
   682  	cpuNoLimit := int64(-1)
   683  	guaranteedMemory := memoryQuantity.Value()
   684  	testCases := map[string]struct {
   685  		pod              *v1.Pod
   686  		expected         *ResourceConfig
   687  		enforceCPULimits bool
   688  		quotaPeriod      uint64 // in microseconds
   689  	}{
   690  		"besteffort": {
   691  			pod: &v1.Pod{
   692  				Spec: v1.PodSpec{
   693  					Containers: []v1.Container{
   694  						{
   695  							Resources: getResourceRequirements(getResourceList("", ""), getResourceList("", "")),
   696  						},
   697  					},
   698  				},
   699  			},
   700  			enforceCPULimits: true,
   701  			quotaPeriod:      defaultQuotaPeriod,
   702  			expected:         &ResourceConfig{CPUShares: &minShares},
   703  		},
   704  		"burstable-no-limits": {
   705  			pod: &v1.Pod{
   706  				Spec: v1.PodSpec{
   707  					Containers: []v1.Container{
   708  						{
   709  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")),
   710  						},
   711  					},
   712  				},
   713  			},
   714  			enforceCPULimits: true,
   715  			quotaPeriod:      defaultQuotaPeriod,
   716  			expected:         &ResourceConfig{CPUShares: &burstableShares, Unified: map[string]string{"memory.min": "104857600"}},
   717  		},
   718  		"burstable-with-limits": {
   719  			pod: &v1.Pod{
   720  				Spec: v1.PodSpec{
   721  					Containers: []v1.Container{
   722  						{
   723  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   724  						},
   725  					},
   726  				},
   727  			},
   728  			enforceCPULimits: true,
   729  			quotaPeriod:      defaultQuotaPeriod,
   730  			expected:         &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &burstableQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory, Unified: map[string]string{"memory.min": "104857600"}},
   731  		},
   732  		"burstable-with-limits-no-cpu-enforcement": {
   733  			pod: &v1.Pod{
   734  				Spec: v1.PodSpec{
   735  					Containers: []v1.Container{
   736  						{
   737  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   738  						},
   739  					},
   740  				},
   741  			},
   742  			enforceCPULimits: false,
   743  			quotaPeriod:      defaultQuotaPeriod,
   744  			expected:         &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &cpuNoLimit, CPUPeriod: &defaultQuotaPeriod, Memory: &burstableMemory, Unified: map[string]string{"memory.min": "104857600"}},
   745  		},
   746  		"burstable-partial-limits": {
   747  			pod: &v1.Pod{
   748  				Spec: v1.PodSpec{
   749  					Containers: []v1.Container{
   750  						{
   751  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   752  						},
   753  						{
   754  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")),
   755  						},
   756  					},
   757  				},
   758  			},
   759  			enforceCPULimits: true,
   760  			quotaPeriod:      defaultQuotaPeriod,
   761  			expected:         &ResourceConfig{CPUShares: &burstablePartialShares, Unified: map[string]string{"memory.min": "209715200"}},
   762  		},
   763  		"burstable-with-limits-with-tuned-quota": {
   764  			pod: &v1.Pod{
   765  				Spec: v1.PodSpec{
   766  					Containers: []v1.Container{
   767  						{
   768  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   769  						},
   770  					},
   771  				},
   772  			},
   773  			enforceCPULimits: true,
   774  			quotaPeriod:      tunedQuotaPeriod,
   775  			expected:         &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &burstableQuota, CPUPeriod: &tunedQuotaPeriod, Memory: &burstableMemory, Unified: map[string]string{"memory.min": "104857600"}},
   776  		},
   777  		"burstable-with-limits-no-cpu-enforcement-with-tuned-quota": {
   778  			pod: &v1.Pod{
   779  				Spec: v1.PodSpec{
   780  					Containers: []v1.Container{
   781  						{
   782  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   783  						},
   784  					},
   785  				},
   786  			},
   787  			enforceCPULimits: false,
   788  			quotaPeriod:      tunedQuotaPeriod,
   789  			expected:         &ResourceConfig{CPUShares: &burstableShares, CPUQuota: &cpuNoLimit, CPUPeriod: &tunedQuotaPeriod, Memory: &burstableMemory, Unified: map[string]string{"memory.min": "104857600"}},
   790  		},
   791  		"burstable-partial-limits-with-tuned-quota": {
   792  			pod: &v1.Pod{
   793  				Spec: v1.PodSpec{
   794  					Containers: []v1.Container{
   795  						{
   796  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("200m", "200Mi")),
   797  						},
   798  						{
   799  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("", "")),
   800  						},
   801  					},
   802  				},
   803  			},
   804  			enforceCPULimits: true,
   805  			quotaPeriod:      tunedQuotaPeriod,
   806  			expected:         &ResourceConfig{CPUShares: &burstablePartialShares, Unified: map[string]string{"memory.min": "209715200"}},
   807  		},
   808  		"guaranteed": {
   809  			pod: &v1.Pod{
   810  				Spec: v1.PodSpec{
   811  					Containers: []v1.Container{
   812  						{
   813  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
   814  						},
   815  					},
   816  				},
   817  			},
   818  			enforceCPULimits: true,
   819  			quotaPeriod:      defaultQuotaPeriod,
   820  			expected:         &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &guaranteedQuota, CPUPeriod: &defaultQuotaPeriod, Memory: &guaranteedMemory, Unified: map[string]string{"memory.min": "104857600"}},
   821  		},
   822  		"guaranteed-no-cpu-enforcement": {
   823  			pod: &v1.Pod{
   824  				Spec: v1.PodSpec{
   825  					Containers: []v1.Container{
   826  						{
   827  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
   828  						},
   829  					},
   830  				},
   831  			},
   832  			enforceCPULimits: false,
   833  			quotaPeriod:      defaultQuotaPeriod,
   834  			expected:         &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &cpuNoLimit, CPUPeriod: &defaultQuotaPeriod, Memory: &guaranteedMemory, Unified: map[string]string{"memory.min": "104857600"}},
   835  		},
   836  		"guaranteed-with-tuned-quota": {
   837  			pod: &v1.Pod{
   838  				Spec: v1.PodSpec{
   839  					Containers: []v1.Container{
   840  						{
   841  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
   842  						},
   843  					},
   844  				},
   845  			},
   846  			enforceCPULimits: true,
   847  			quotaPeriod:      tunedQuotaPeriod,
   848  			expected:         &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &guaranteedTunedQuota, CPUPeriod: &tunedQuotaPeriod, Memory: &guaranteedMemory, Unified: map[string]string{"memory.min": "104857600"}},
   849  		},
   850  		"guaranteed-no-cpu-enforcement-with-tuned-quota": {
   851  			pod: &v1.Pod{
   852  				Spec: v1.PodSpec{
   853  					Containers: []v1.Container{
   854  						{
   855  							Resources: getResourceRequirements(getResourceList("100m", "100Mi"), getResourceList("100m", "100Mi")),
   856  						},
   857  					},
   858  				},
   859  			},
   860  			enforceCPULimits: false,
   861  			quotaPeriod:      tunedQuotaPeriod,
   862  			expected:         &ResourceConfig{CPUShares: &guaranteedShares, CPUQuota: &cpuNoLimit, CPUPeriod: &tunedQuotaPeriod, Memory: &guaranteedMemory, Unified: map[string]string{"memory.min": "104857600"}},
   863  		},
   864  	}
   865  
   866  	for testName, testCase := range testCases {
   867  
   868  		actual := ResourceConfigForPod(testCase.pod, testCase.enforceCPULimits, testCase.quotaPeriod, true)
   869  
   870  		if !reflect.DeepEqual(actual.Unified, testCase.expected.Unified) {
   871  			t.Errorf("unexpected result, test: %v, unified not as expected", testName)
   872  		}
   873  	}
   874  }
   875  

View as plain text