k8s.io/kubernetes@v1.29.3/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  /*
     5  Copyright 2018 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 kuberuntime
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  	"math"
    26  	"os"
    27  	"reflect"
    28  	"strconv"
    29  	"testing"
    30  
    31  	"k8s.io/kubernetes/pkg/kubelet/cm"
    32  	"k8s.io/kubernetes/pkg/kubelet/types"
    33  
    34  	"github.com/google/go-cmp/cmp"
    35  	libcontainercgroups "github.com/opencontainers/runc/libcontainer/cgroups"
    36  	"github.com/stretchr/testify/assert"
    37  	v1 "k8s.io/api/core/v1"
    38  	"k8s.io/apimachinery/pkg/api/resource"
    39  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    40  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    41  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    42  	runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
    43  	"k8s.io/kubernetes/pkg/features"
    44  	kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
    45  )
    46  
    47  func makeExpectedConfig(m *kubeGenericRuntimeManager, pod *v1.Pod, containerIndex int, enforceMemoryQoS bool) *runtimeapi.ContainerConfig {
    48  	ctx := context.Background()
    49  	container := &pod.Spec.Containers[containerIndex]
    50  	podIP := ""
    51  	restartCount := 0
    52  	opts, _, _ := m.runtimeHelper.GenerateRunContainerOptions(ctx, pod, container, podIP, []string{podIP})
    53  	containerLogsPath := buildContainerLogsPath(container.Name, restartCount)
    54  	restartCountUint32 := uint32(restartCount)
    55  	envs := make([]*runtimeapi.KeyValue, len(opts.Envs))
    56  
    57  	l, _ := m.generateLinuxContainerConfig(container, pod, new(int64), "", nil, enforceMemoryQoS)
    58  
    59  	expectedConfig := &runtimeapi.ContainerConfig{
    60  		Metadata: &runtimeapi.ContainerMetadata{
    61  			Name:    container.Name,
    62  			Attempt: restartCountUint32,
    63  		},
    64  		Image:       &runtimeapi.ImageSpec{Image: container.Image, UserSpecifiedImage: container.Image},
    65  		Command:     container.Command,
    66  		Args:        []string(nil),
    67  		WorkingDir:  container.WorkingDir,
    68  		Labels:      newContainerLabels(container, pod),
    69  		Annotations: newContainerAnnotations(container, pod, restartCount, opts),
    70  		Devices:     makeDevices(opts),
    71  		Mounts:      m.makeMounts(opts, container),
    72  		LogPath:     containerLogsPath,
    73  		Stdin:       container.Stdin,
    74  		StdinOnce:   container.StdinOnce,
    75  		Tty:         container.TTY,
    76  		Linux:       l,
    77  		Envs:        envs,
    78  		CDIDevices:  makeCDIDevices(opts),
    79  	}
    80  	return expectedConfig
    81  }
    82  
    83  func TestGenerateContainerConfig(t *testing.T) {
    84  	ctx := context.Background()
    85  	_, imageService, m, err := createTestRuntimeManager()
    86  	assert.NoError(t, err)
    87  
    88  	runAsUser := int64(1000)
    89  	runAsGroup := int64(2000)
    90  	pod := &v1.Pod{
    91  		ObjectMeta: metav1.ObjectMeta{
    92  			UID:       "12345678",
    93  			Name:      "bar",
    94  			Namespace: "new",
    95  		},
    96  		Spec: v1.PodSpec{
    97  			Containers: []v1.Container{
    98  				{
    99  					Name:            "foo",
   100  					Image:           "busybox",
   101  					ImagePullPolicy: v1.PullIfNotPresent,
   102  					Command:         []string{"testCommand"},
   103  					WorkingDir:      "testWorkingDir",
   104  					SecurityContext: &v1.SecurityContext{
   105  						RunAsUser:  &runAsUser,
   106  						RunAsGroup: &runAsGroup,
   107  					},
   108  				},
   109  			},
   110  		},
   111  	}
   112  
   113  	expectedConfig := makeExpectedConfig(m, pod, 0, false)
   114  	containerConfig, _, err := m.generateContainerConfig(ctx, &pod.Spec.Containers[0], pod, 0, "", pod.Spec.Containers[0].Image, []string{}, nil)
   115  	assert.NoError(t, err)
   116  	assert.Equal(t, expectedConfig, containerConfig, "generate container config for kubelet runtime v1.")
   117  	assert.Equal(t, runAsUser, containerConfig.GetLinux().GetSecurityContext().GetRunAsUser().GetValue(), "RunAsUser should be set")
   118  	assert.Equal(t, runAsGroup, containerConfig.GetLinux().GetSecurityContext().GetRunAsGroup().GetValue(), "RunAsGroup should be set")
   119  
   120  	runAsRoot := int64(0)
   121  	runAsNonRootTrue := true
   122  	podWithContainerSecurityContext := &v1.Pod{
   123  		ObjectMeta: metav1.ObjectMeta{
   124  			UID:       "12345678",
   125  			Name:      "bar",
   126  			Namespace: "new",
   127  		},
   128  		Spec: v1.PodSpec{
   129  			Containers: []v1.Container{
   130  				{
   131  					Name:            "foo",
   132  					Image:           "busybox",
   133  					ImagePullPolicy: v1.PullIfNotPresent,
   134  					Command:         []string{"testCommand"},
   135  					WorkingDir:      "testWorkingDir",
   136  					SecurityContext: &v1.SecurityContext{
   137  						RunAsNonRoot: &runAsNonRootTrue,
   138  						RunAsUser:    &runAsRoot,
   139  					},
   140  				},
   141  			},
   142  		},
   143  	}
   144  
   145  	_, _, err = m.generateContainerConfig(ctx, &podWithContainerSecurityContext.Spec.Containers[0], podWithContainerSecurityContext, 0, "", podWithContainerSecurityContext.Spec.Containers[0].Image, []string{}, nil)
   146  	assert.Error(t, err)
   147  
   148  	imageID, _ := imageService.PullImage(ctx, &runtimeapi.ImageSpec{Image: "busybox"}, nil, nil)
   149  	resp, _ := imageService.ImageStatus(ctx, &runtimeapi.ImageSpec{Image: imageID}, false)
   150  
   151  	resp.Image.Uid = nil
   152  	resp.Image.Username = "test"
   153  
   154  	podWithContainerSecurityContext.Spec.Containers[0].SecurityContext.RunAsUser = nil
   155  	podWithContainerSecurityContext.Spec.Containers[0].SecurityContext.RunAsNonRoot = &runAsNonRootTrue
   156  
   157  	_, _, err = m.generateContainerConfig(ctx, &podWithContainerSecurityContext.Spec.Containers[0], podWithContainerSecurityContext, 0, "", podWithContainerSecurityContext.Spec.Containers[0].Image, []string{}, nil)
   158  	assert.Error(t, err, "RunAsNonRoot should fail for non-numeric username")
   159  }
   160  
   161  func TestGenerateLinuxContainerConfigResources(t *testing.T) {
   162  	_, _, m, err := createTestRuntimeManager()
   163  	m.cpuCFSQuota = true
   164  
   165  	assert.NoError(t, err)
   166  
   167  	tests := []struct {
   168  		name         string
   169  		podResources v1.ResourceRequirements
   170  		expected     *runtimeapi.LinuxContainerResources
   171  	}{
   172  		{
   173  			name: "Request 128M/1C, Limit 256M/3C",
   174  			podResources: v1.ResourceRequirements{
   175  				Requests: v1.ResourceList{
   176  					v1.ResourceMemory: resource.MustParse("128Mi"),
   177  					v1.ResourceCPU:    resource.MustParse("1"),
   178  				},
   179  				Limits: v1.ResourceList{
   180  					v1.ResourceMemory: resource.MustParse("256Mi"),
   181  					v1.ResourceCPU:    resource.MustParse("3"),
   182  				},
   183  			},
   184  			expected: &runtimeapi.LinuxContainerResources{
   185  				CpuPeriod:          100000,
   186  				CpuQuota:           300000,
   187  				CpuShares:          1024,
   188  				MemoryLimitInBytes: 256 * 1024 * 1024,
   189  			},
   190  		},
   191  		{
   192  			name: "Request 128M/2C, No Limit",
   193  			podResources: v1.ResourceRequirements{
   194  				Requests: v1.ResourceList{
   195  					v1.ResourceMemory: resource.MustParse("128Mi"),
   196  					v1.ResourceCPU:    resource.MustParse("2"),
   197  				},
   198  			},
   199  			expected: &runtimeapi.LinuxContainerResources{
   200  				CpuPeriod:          100000,
   201  				CpuQuota:           0,
   202  				CpuShares:          2048,
   203  				MemoryLimitInBytes: 0,
   204  			},
   205  		},
   206  	}
   207  
   208  	for _, test := range tests {
   209  		pod := &v1.Pod{
   210  			ObjectMeta: metav1.ObjectMeta{
   211  				UID:       "12345678",
   212  				Name:      "bar",
   213  				Namespace: "new",
   214  			},
   215  			Spec: v1.PodSpec{
   216  				Containers: []v1.Container{
   217  					{
   218  						Name:            "foo",
   219  						Image:           "busybox",
   220  						ImagePullPolicy: v1.PullIfNotPresent,
   221  						Command:         []string{"testCommand"},
   222  						WorkingDir:      "testWorkingDir",
   223  						Resources:       test.podResources,
   224  					},
   225  				},
   226  			},
   227  		}
   228  
   229  		linuxConfig, err := m.generateLinuxContainerConfig(&pod.Spec.Containers[0], pod, new(int64), "", nil, false)
   230  		assert.NoError(t, err)
   231  		assert.Equal(t, test.expected.CpuPeriod, linuxConfig.GetResources().CpuPeriod, test.name)
   232  		assert.Equal(t, test.expected.CpuQuota, linuxConfig.GetResources().CpuQuota, test.name)
   233  		assert.Equal(t, test.expected.CpuShares, linuxConfig.GetResources().CpuShares, test.name)
   234  		assert.Equal(t, test.expected.MemoryLimitInBytes, linuxConfig.GetResources().MemoryLimitInBytes, test.name)
   235  	}
   236  }
   237  
   238  func TestCalculateLinuxResources(t *testing.T) {
   239  	_, _, m, err := createTestRuntimeManager()
   240  	m.cpuCFSQuota = true
   241  
   242  	assert.NoError(t, err)
   243  
   244  	generateResourceQuantity := func(str string) *resource.Quantity {
   245  		quantity := resource.MustParse(str)
   246  		return &quantity
   247  	}
   248  
   249  	tests := []struct {
   250  		name          string
   251  		cpuReq        *resource.Quantity
   252  		cpuLim        *resource.Quantity
   253  		memLim        *resource.Quantity
   254  		expected      *runtimeapi.LinuxContainerResources
   255  		cgroupVersion CgroupVersion
   256  	}{
   257  		{
   258  			name:   "Request128MBLimit256MB",
   259  			cpuReq: generateResourceQuantity("1"),
   260  			cpuLim: generateResourceQuantity("2"),
   261  			memLim: generateResourceQuantity("128Mi"),
   262  			expected: &runtimeapi.LinuxContainerResources{
   263  				CpuPeriod:          100000,
   264  				CpuQuota:           200000,
   265  				CpuShares:          1024,
   266  				MemoryLimitInBytes: 134217728,
   267  			},
   268  			cgroupVersion: cgroupV1,
   269  		},
   270  		{
   271  			name:   "RequestNoMemory",
   272  			cpuReq: generateResourceQuantity("2"),
   273  			cpuLim: generateResourceQuantity("8"),
   274  			memLim: generateResourceQuantity("0"),
   275  			expected: &runtimeapi.LinuxContainerResources{
   276  				CpuPeriod:          100000,
   277  				CpuQuota:           800000,
   278  				CpuShares:          2048,
   279  				MemoryLimitInBytes: 0,
   280  			},
   281  			cgroupVersion: cgroupV1,
   282  		},
   283  		{
   284  			name:   "RequestNilCPU",
   285  			cpuLim: generateResourceQuantity("2"),
   286  			memLim: generateResourceQuantity("0"),
   287  			expected: &runtimeapi.LinuxContainerResources{
   288  				CpuPeriod:          100000,
   289  				CpuQuota:           200000,
   290  				CpuShares:          2048,
   291  				MemoryLimitInBytes: 0,
   292  			},
   293  			cgroupVersion: cgroupV1,
   294  		},
   295  		{
   296  			name:   "RequestZeroCPU",
   297  			cpuReq: generateResourceQuantity("0"),
   298  			cpuLim: generateResourceQuantity("2"),
   299  			memLim: generateResourceQuantity("0"),
   300  			expected: &runtimeapi.LinuxContainerResources{
   301  				CpuPeriod:          100000,
   302  				CpuQuota:           200000,
   303  				CpuShares:          2,
   304  				MemoryLimitInBytes: 0,
   305  			},
   306  			cgroupVersion: cgroupV1,
   307  		},
   308  		{
   309  			name:   "Request128MBLimit256MB",
   310  			cpuReq: generateResourceQuantity("1"),
   311  			cpuLim: generateResourceQuantity("2"),
   312  			memLim: generateResourceQuantity("128Mi"),
   313  			expected: &runtimeapi.LinuxContainerResources{
   314  				CpuPeriod:          100000,
   315  				CpuQuota:           200000,
   316  				CpuShares:          1024,
   317  				MemoryLimitInBytes: 134217728,
   318  				Unified:            map[string]string{"memory.oom.group": "1"},
   319  			},
   320  			cgroupVersion: cgroupV2,
   321  		},
   322  		{
   323  			name:   "RequestNoMemory",
   324  			cpuReq: generateResourceQuantity("2"),
   325  			cpuLim: generateResourceQuantity("8"),
   326  			memLim: generateResourceQuantity("0"),
   327  			expected: &runtimeapi.LinuxContainerResources{
   328  				CpuPeriod:          100000,
   329  				CpuQuota:           800000,
   330  				CpuShares:          2048,
   331  				MemoryLimitInBytes: 0,
   332  				Unified:            map[string]string{"memory.oom.group": "1"},
   333  			},
   334  			cgroupVersion: cgroupV2,
   335  		},
   336  		{
   337  			name:   "RequestNilCPU",
   338  			cpuLim: generateResourceQuantity("2"),
   339  			memLim: generateResourceQuantity("0"),
   340  			expected: &runtimeapi.LinuxContainerResources{
   341  				CpuPeriod:          100000,
   342  				CpuQuota:           200000,
   343  				CpuShares:          2048,
   344  				MemoryLimitInBytes: 0,
   345  				Unified:            map[string]string{"memory.oom.group": "1"},
   346  			},
   347  			cgroupVersion: cgroupV2,
   348  		},
   349  		{
   350  			name:   "RequestZeroCPU",
   351  			cpuReq: generateResourceQuantity("0"),
   352  			cpuLim: generateResourceQuantity("2"),
   353  			memLim: generateResourceQuantity("0"),
   354  			expected: &runtimeapi.LinuxContainerResources{
   355  				CpuPeriod:          100000,
   356  				CpuQuota:           200000,
   357  				CpuShares:          2,
   358  				MemoryLimitInBytes: 0,
   359  				Unified:            map[string]string{"memory.oom.group": "1"},
   360  			},
   361  			cgroupVersion: cgroupV2,
   362  		},
   363  	}
   364  	for _, test := range tests {
   365  		setCgroupVersionDuringTest(test.cgroupVersion)
   366  		linuxContainerResources := m.calculateLinuxResources(test.cpuReq, test.cpuLim, test.memLim)
   367  		assert.Equal(t, test.expected, linuxContainerResources)
   368  	}
   369  }
   370  
   371  func TestGenerateContainerConfigWithMemoryQoSEnforced(t *testing.T) {
   372  	_, _, m, err := createTestRuntimeManager()
   373  	assert.NoError(t, err)
   374  
   375  	podRequestMemory := resource.MustParse("128Mi")
   376  	pod1LimitMemory := resource.MustParse("256Mi")
   377  	pod1 := &v1.Pod{
   378  		ObjectMeta: metav1.ObjectMeta{
   379  			UID:       "12345678",
   380  			Name:      "bar",
   381  			Namespace: "new",
   382  		},
   383  		Spec: v1.PodSpec{
   384  			Containers: []v1.Container{
   385  				{
   386  					Name:            "foo",
   387  					Image:           "busybox",
   388  					ImagePullPolicy: v1.PullIfNotPresent,
   389  					Command:         []string{"testCommand"},
   390  					WorkingDir:      "testWorkingDir",
   391  					Resources: v1.ResourceRequirements{
   392  						Requests: v1.ResourceList{
   393  							v1.ResourceMemory: podRequestMemory,
   394  						},
   395  						Limits: v1.ResourceList{
   396  							v1.ResourceMemory: pod1LimitMemory,
   397  						},
   398  					},
   399  				},
   400  			},
   401  		},
   402  	}
   403  
   404  	pod2 := &v1.Pod{
   405  		ObjectMeta: metav1.ObjectMeta{
   406  			UID:       "12345678",
   407  			Name:      "bar",
   408  			Namespace: "new",
   409  		},
   410  		Spec: v1.PodSpec{
   411  			Containers: []v1.Container{
   412  				{
   413  					Name:            "foo",
   414  					Image:           "busybox",
   415  					ImagePullPolicy: v1.PullIfNotPresent,
   416  					Command:         []string{"testCommand"},
   417  					WorkingDir:      "testWorkingDir",
   418  					Resources: v1.ResourceRequirements{
   419  						Requests: v1.ResourceList{
   420  							v1.ResourceMemory: podRequestMemory,
   421  						},
   422  					},
   423  				},
   424  			},
   425  		},
   426  	}
   427  	pageSize := int64(os.Getpagesize())
   428  	memoryNodeAllocatable := resource.MustParse(fakeNodeAllocatableMemory)
   429  	pod1MemoryHigh := int64(math.Floor(
   430  		float64(podRequestMemory.Value())+
   431  			(float64(pod1LimitMemory.Value())-float64(podRequestMemory.Value()))*float64(m.memoryThrottlingFactor))/float64(pageSize)) * pageSize
   432  	pod2MemoryHigh := int64(math.Floor(
   433  		float64(podRequestMemory.Value())+
   434  			(float64(memoryNodeAllocatable.Value())-float64(podRequestMemory.Value()))*float64(m.memoryThrottlingFactor))/float64(pageSize)) * pageSize
   435  
   436  	type expectedResult struct {
   437  		containerConfig *runtimeapi.LinuxContainerConfig
   438  		memoryLow       int64
   439  		memoryHigh      int64
   440  	}
   441  	l1, _ := m.generateLinuxContainerConfig(&pod1.Spec.Containers[0], pod1, new(int64), "", nil, true)
   442  	l2, _ := m.generateLinuxContainerConfig(&pod2.Spec.Containers[0], pod2, new(int64), "", nil, true)
   443  	tests := []struct {
   444  		name     string
   445  		pod      *v1.Pod
   446  		expected *expectedResult
   447  	}{
   448  		{
   449  			name: "Request128MBLimit256MB",
   450  			pod:  pod1,
   451  			expected: &expectedResult{
   452  				l1,
   453  				128 * 1024 * 1024,
   454  				int64(pod1MemoryHigh),
   455  			},
   456  		},
   457  		{
   458  			name: "Request128MBWithoutLimit",
   459  			pod:  pod2,
   460  			expected: &expectedResult{
   461  				l2,
   462  				128 * 1024 * 1024,
   463  				int64(pod2MemoryHigh),
   464  			},
   465  		},
   466  	}
   467  
   468  	for _, test := range tests {
   469  		linuxConfig, err := m.generateLinuxContainerConfig(&test.pod.Spec.Containers[0], test.pod, new(int64), "", nil, true)
   470  		assert.NoError(t, err)
   471  		assert.Equal(t, test.expected.containerConfig, linuxConfig, test.name)
   472  		assert.Equal(t, linuxConfig.GetResources().GetUnified()["memory.min"], strconv.FormatInt(test.expected.memoryLow, 10), test.name)
   473  		assert.Equal(t, linuxConfig.GetResources().GetUnified()["memory.high"], strconv.FormatInt(test.expected.memoryHigh, 10), test.name)
   474  	}
   475  }
   476  
   477  func TestGetHugepageLimitsFromResources(t *testing.T) {
   478  	var baseHugepage []*runtimeapi.HugepageLimit
   479  
   480  	// For each page size, limit to 0.
   481  	for _, pageSize := range libcontainercgroups.HugePageSizes() {
   482  		baseHugepage = append(baseHugepage, &runtimeapi.HugepageLimit{
   483  			PageSize: pageSize,
   484  			Limit:    uint64(0),
   485  		})
   486  	}
   487  
   488  	tests := []struct {
   489  		name      string
   490  		resources v1.ResourceRequirements
   491  		expected  []*runtimeapi.HugepageLimit
   492  	}{
   493  		{
   494  			name: "Success2MB",
   495  			resources: v1.ResourceRequirements{
   496  				Limits: v1.ResourceList{
   497  					"hugepages-2Mi": resource.MustParse("2Mi"),
   498  				},
   499  			},
   500  			expected: []*runtimeapi.HugepageLimit{
   501  				{
   502  					PageSize: "2MB",
   503  					Limit:    2097152,
   504  				},
   505  			},
   506  		},
   507  		{
   508  			name: "Success1GB",
   509  			resources: v1.ResourceRequirements{
   510  				Limits: v1.ResourceList{
   511  					"hugepages-1Gi": resource.MustParse("2Gi"),
   512  				},
   513  			},
   514  			expected: []*runtimeapi.HugepageLimit{
   515  				{
   516  					PageSize: "1GB",
   517  					Limit:    2147483648,
   518  				},
   519  			},
   520  		},
   521  		{
   522  			name: "Skip2MB",
   523  			resources: v1.ResourceRequirements{
   524  				Limits: v1.ResourceList{
   525  					"hugepages-2MB": resource.MustParse("2Mi"),
   526  				},
   527  			},
   528  			expected: []*runtimeapi.HugepageLimit{
   529  				{
   530  					PageSize: "2MB",
   531  					Limit:    0,
   532  				},
   533  			},
   534  		},
   535  		{
   536  			name: "Skip1GB",
   537  			resources: v1.ResourceRequirements{
   538  				Limits: v1.ResourceList{
   539  					"hugepages-1GB": resource.MustParse("2Gi"),
   540  				},
   541  			},
   542  			expected: []*runtimeapi.HugepageLimit{
   543  				{
   544  					PageSize: "1GB",
   545  					Limit:    0,
   546  				},
   547  			},
   548  		},
   549  		{
   550  			name: "Success2MBand1GB",
   551  			resources: v1.ResourceRequirements{
   552  				Limits: v1.ResourceList{
   553  					v1.ResourceName(v1.ResourceCPU): resource.MustParse("0"),
   554  					"hugepages-2Mi":                 resource.MustParse("2Mi"),
   555  					"hugepages-1Gi":                 resource.MustParse("2Gi"),
   556  				},
   557  			},
   558  			expected: []*runtimeapi.HugepageLimit{
   559  				{
   560  					PageSize: "2MB",
   561  					Limit:    2097152,
   562  				},
   563  				{
   564  					PageSize: "1GB",
   565  					Limit:    2147483648,
   566  				},
   567  			},
   568  		},
   569  		{
   570  			name: "Skip2MBand1GB",
   571  			resources: v1.ResourceRequirements{
   572  				Limits: v1.ResourceList{
   573  					v1.ResourceName(v1.ResourceCPU): resource.MustParse("0"),
   574  					"hugepages-2MB":                 resource.MustParse("2Mi"),
   575  					"hugepages-1GB":                 resource.MustParse("2Gi"),
   576  				},
   577  			},
   578  			expected: []*runtimeapi.HugepageLimit{
   579  				{
   580  					PageSize: "2MB",
   581  					Limit:    0,
   582  				},
   583  				{
   584  					PageSize: "1GB",
   585  					Limit:    0,
   586  				},
   587  			},
   588  		},
   589  	}
   590  
   591  	for _, test := range tests {
   592  		// Validate if machine supports hugepage size that used in test case.
   593  		machineHugepageSupport := true
   594  		for _, hugepageLimit := range test.expected {
   595  			hugepageSupport := false
   596  			for _, pageSize := range libcontainercgroups.HugePageSizes() {
   597  				if pageSize == hugepageLimit.PageSize {
   598  					hugepageSupport = true
   599  					break
   600  				}
   601  			}
   602  
   603  			if !hugepageSupport {
   604  				machineHugepageSupport = false
   605  				break
   606  			}
   607  		}
   608  
   609  		// Case of machine can't support hugepage size
   610  		if !machineHugepageSupport {
   611  			continue
   612  		}
   613  
   614  		expectedHugepages := baseHugepage
   615  		for _, hugepage := range test.expected {
   616  			for _, expectedHugepage := range expectedHugepages {
   617  				if expectedHugepage.PageSize == hugepage.PageSize {
   618  					expectedHugepage.Limit = hugepage.Limit
   619  				}
   620  			}
   621  		}
   622  
   623  		results := GetHugepageLimitsFromResources(test.resources)
   624  		if !reflect.DeepEqual(expectedHugepages, results) {
   625  			t.Errorf("%s test failed. Expected %v but got %v", test.name, expectedHugepages, results)
   626  		}
   627  
   628  		for _, hugepage := range baseHugepage {
   629  			hugepage.Limit = uint64(0)
   630  		}
   631  	}
   632  }
   633  
   634  func TestGenerateLinuxContainerConfigNamespaces(t *testing.T) {
   635  	_, _, m, err := createTestRuntimeManager()
   636  	if err != nil {
   637  		t.Fatalf("error creating test RuntimeManager: %v", err)
   638  	}
   639  
   640  	for _, tc := range []struct {
   641  		name   string
   642  		pod    *v1.Pod
   643  		target *kubecontainer.ContainerID
   644  		want   *runtimeapi.NamespaceOption
   645  	}{
   646  		{
   647  			"Default namespaces",
   648  			&v1.Pod{
   649  				Spec: v1.PodSpec{
   650  					Containers: []v1.Container{
   651  						{Name: "test"},
   652  					},
   653  				},
   654  			},
   655  			nil,
   656  			&runtimeapi.NamespaceOption{
   657  				Pid: runtimeapi.NamespaceMode_CONTAINER,
   658  			},
   659  		},
   660  		{
   661  			"PID Namespace POD",
   662  			&v1.Pod{
   663  				Spec: v1.PodSpec{
   664  					Containers: []v1.Container{
   665  						{Name: "test"},
   666  					},
   667  					ShareProcessNamespace: &[]bool{true}[0],
   668  				},
   669  			},
   670  			nil,
   671  			&runtimeapi.NamespaceOption{
   672  				Pid: runtimeapi.NamespaceMode_POD,
   673  			},
   674  		},
   675  		{
   676  			"PID Namespace TARGET",
   677  			&v1.Pod{
   678  				Spec: v1.PodSpec{
   679  					Containers: []v1.Container{
   680  						{Name: "test"},
   681  					},
   682  				},
   683  			},
   684  			&kubecontainer.ContainerID{Type: "docker", ID: "really-long-id-string"},
   685  			&runtimeapi.NamespaceOption{
   686  				Pid:      runtimeapi.NamespaceMode_TARGET,
   687  				TargetId: "really-long-id-string",
   688  			},
   689  		},
   690  	} {
   691  		t.Run(tc.name, func(t *testing.T) {
   692  			got, err := m.generateLinuxContainerConfig(&tc.pod.Spec.Containers[0], tc.pod, nil, "", tc.target, false)
   693  			assert.NoError(t, err)
   694  			if diff := cmp.Diff(tc.want, got.SecurityContext.NamespaceOptions); diff != "" {
   695  				t.Errorf("%v: diff (-want +got):\n%v", t.Name(), diff)
   696  			}
   697  		})
   698  	}
   699  }
   700  
   701  func TestGenerateLinuxContainerResources(t *testing.T) {
   702  	_, _, m, err := createTestRuntimeManager()
   703  	assert.NoError(t, err)
   704  	m.machineInfo.MemoryCapacity = 17179860387 // 16GB
   705  
   706  	pod := &v1.Pod{
   707  		ObjectMeta: metav1.ObjectMeta{
   708  			UID:       "12345678",
   709  			Name:      "foo",
   710  			Namespace: "bar",
   711  		},
   712  		Spec: v1.PodSpec{
   713  			Containers: []v1.Container{
   714  				{
   715  					Name:  "c1",
   716  					Image: "busybox",
   717  				},
   718  			},
   719  		},
   720  		Status: v1.PodStatus{},
   721  	}
   722  
   723  	for _, tc := range []struct {
   724  		name      string
   725  		scalingFg bool
   726  		limits    v1.ResourceList
   727  		requests  v1.ResourceList
   728  		cStatus   []v1.ContainerStatus
   729  		expected  *runtimeapi.LinuxContainerResources
   730  	}{
   731  		{
   732  			"requests & limits, cpu & memory, guaranteed qos - no container status",
   733  			true,
   734  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("250m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   735  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("250m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   736  			[]v1.ContainerStatus{},
   737  			&runtimeapi.LinuxContainerResources{CpuShares: 256, MemoryLimitInBytes: 524288000, OomScoreAdj: -997},
   738  		},
   739  		{
   740  			"requests & limits, cpu & memory, burstable qos - no container status",
   741  			true,
   742  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("500m"), v1.ResourceMemory: resource.MustParse("750Mi")},
   743  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("250m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   744  			[]v1.ContainerStatus{},
   745  			&runtimeapi.LinuxContainerResources{CpuShares: 256, MemoryLimitInBytes: 786432000, OomScoreAdj: 970},
   746  		},
   747  		{
   748  			"best-effort qos - no container status",
   749  			true,
   750  			nil,
   751  			nil,
   752  			[]v1.ContainerStatus{},
   753  			&runtimeapi.LinuxContainerResources{CpuShares: 2, OomScoreAdj: 1000},
   754  		},
   755  		{
   756  			"requests & limits, cpu & memory, guaranteed qos - empty resources container status",
   757  			true,
   758  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("250m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   759  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("250m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   760  			[]v1.ContainerStatus{{Name: "c1"}},
   761  			&runtimeapi.LinuxContainerResources{CpuShares: 256, MemoryLimitInBytes: 524288000, OomScoreAdj: -997},
   762  		},
   763  		{
   764  			"requests & limits, cpu & memory, burstable qos - empty resources container status",
   765  			true,
   766  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("500m"), v1.ResourceMemory: resource.MustParse("750Mi")},
   767  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("250m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   768  			[]v1.ContainerStatus{{Name: "c1"}},
   769  			&runtimeapi.LinuxContainerResources{CpuShares: 256, MemoryLimitInBytes: 786432000, OomScoreAdj: 999},
   770  		},
   771  		{
   772  			"best-effort qos - empty resources container status",
   773  			true,
   774  			nil,
   775  			nil,
   776  			[]v1.ContainerStatus{{Name: "c1"}},
   777  			&runtimeapi.LinuxContainerResources{CpuShares: 2, OomScoreAdj: 1000},
   778  		},
   779  		{
   780  			"requests & limits, cpu & memory, guaranteed qos - container status with allocatedResources",
   781  			true,
   782  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("200m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   783  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("200m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   784  			[]v1.ContainerStatus{
   785  				{
   786  					Name:               "c1",
   787  					AllocatedResources: v1.ResourceList{v1.ResourceCPU: resource.MustParse("200m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   788  				},
   789  			},
   790  			&runtimeapi.LinuxContainerResources{CpuShares: 204, MemoryLimitInBytes: 524288000, OomScoreAdj: -997},
   791  		},
   792  		{
   793  			"requests & limits, cpu & memory, burstable qos - container status with allocatedResources",
   794  			true,
   795  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("500m"), v1.ResourceMemory: resource.MustParse("750Mi")},
   796  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("250m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   797  			[]v1.ContainerStatus{
   798  				{
   799  					Name:               "c1",
   800  					AllocatedResources: v1.ResourceList{v1.ResourceCPU: resource.MustParse("250m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   801  				},
   802  			},
   803  			&runtimeapi.LinuxContainerResources{CpuShares: 256, MemoryLimitInBytes: 786432000, OomScoreAdj: 970},
   804  		},
   805  		{
   806  			"requests & limits, cpu & memory, guaranteed qos - no container status",
   807  			false,
   808  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("250m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   809  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("250m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   810  			[]v1.ContainerStatus{},
   811  			&runtimeapi.LinuxContainerResources{CpuShares: 256, MemoryLimitInBytes: 524288000, OomScoreAdj: -997},
   812  		},
   813  		{
   814  			"requests & limits, cpu & memory, burstable qos - container status with allocatedResources",
   815  			false,
   816  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("500m"), v1.ResourceMemory: resource.MustParse("750Mi")},
   817  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("250m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   818  			[]v1.ContainerStatus{
   819  				{
   820  					Name:               "c1",
   821  					AllocatedResources: v1.ResourceList{v1.ResourceCPU: resource.MustParse("250m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   822  				},
   823  			},
   824  			&runtimeapi.LinuxContainerResources{CpuShares: 256, MemoryLimitInBytes: 786432000, OomScoreAdj: 970},
   825  		},
   826  		{
   827  			"requests & limits, cpu & memory, guaranteed qos - container status with allocatedResources",
   828  			false,
   829  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("200m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   830  			v1.ResourceList{v1.ResourceCPU: resource.MustParse("200m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   831  			[]v1.ContainerStatus{
   832  				{
   833  					Name:               "c1",
   834  					AllocatedResources: v1.ResourceList{v1.ResourceCPU: resource.MustParse("200m"), v1.ResourceMemory: resource.MustParse("500Mi")},
   835  				},
   836  			},
   837  			&runtimeapi.LinuxContainerResources{CpuShares: 204, MemoryLimitInBytes: 524288000, OomScoreAdj: -997},
   838  		},
   839  		{
   840  			"best-effort qos - no container status",
   841  			false,
   842  			nil,
   843  			nil,
   844  			[]v1.ContainerStatus{},
   845  			&runtimeapi.LinuxContainerResources{CpuShares: 2, OomScoreAdj: 1000},
   846  		},
   847  	} {
   848  		t.Run(tc.name, func(t *testing.T) {
   849  			defer setSwapControllerAvailableDuringTest(false)()
   850  			if tc.scalingFg {
   851  				defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, true)()
   852  			}
   853  
   854  			setCgroupVersionDuringTest(cgroupV1)
   855  
   856  			pod.Spec.Containers[0].Resources = v1.ResourceRequirements{Limits: tc.limits, Requests: tc.requests}
   857  			if len(tc.cStatus) > 0 {
   858  				pod.Status.ContainerStatuses = tc.cStatus
   859  			}
   860  			resources := m.generateLinuxContainerResources(pod, &pod.Spec.Containers[0], false)
   861  			tc.expected.HugepageLimits = resources.HugepageLimits
   862  			if !cmp.Equal(resources, tc.expected) {
   863  				t.Errorf("Test %s: expected resources %+v, but got %+v", tc.name, tc.expected, resources)
   864  			}
   865  		})
   866  	}
   867  	//TODO(vinaykul,InPlacePodVerticalScaling): Add unit tests for cgroup v1 & v2
   868  }
   869  
   870  func TestGenerateLinuxContainerResourcesWithSwap(t *testing.T) {
   871  	_, _, m, err := createTestRuntimeManager()
   872  	assert.NoError(t, err)
   873  	m.machineInfo.MemoryCapacity = 42949672960 // 40Gb == 40 * 1024^3
   874  	m.machineInfo.SwapCapacity = 5368709120    // 5Gb == 5 * 1024^3
   875  
   876  	pod := &v1.Pod{
   877  		ObjectMeta: metav1.ObjectMeta{
   878  			UID:       "12345678",
   879  			Name:      "foo",
   880  			Namespace: "bar",
   881  		},
   882  		Spec: v1.PodSpec{
   883  			Containers: []v1.Container{
   884  				{
   885  					Name: "c1",
   886  				},
   887  				{
   888  					Name: "c2",
   889  				},
   890  			},
   891  		},
   892  		Status: v1.PodStatus{},
   893  	}
   894  
   895  	expectSwapDisabled := func(cgroupVersion CgroupVersion, resources ...*runtimeapi.LinuxContainerResources) {
   896  		const msg = "container is expected to not have swap configured"
   897  
   898  		for _, r := range resources {
   899  			switch cgroupVersion {
   900  			case cgroupV1:
   901  				assert.Equal(t, int64(0), r.MemorySwapLimitInBytes, msg)
   902  			case cgroupV2:
   903  				assert.NotContains(t, r.Unified, cm.Cgroup2MaxSwapFilename, msg)
   904  			}
   905  		}
   906  	}
   907  
   908  	expectNoSwap := func(cgroupVersion CgroupVersion, resources ...*runtimeapi.LinuxContainerResources) {
   909  		const msg = "container is expected to not have swap access"
   910  
   911  		for _, r := range resources {
   912  			switch cgroupVersion {
   913  			case cgroupV1:
   914  				assert.Equal(t, r.MemoryLimitInBytes, r.MemorySwapLimitInBytes, msg)
   915  			case cgroupV2:
   916  				assert.Equal(t, "0", r.Unified[cm.Cgroup2MaxSwapFilename], msg)
   917  			}
   918  		}
   919  	}
   920  
   921  	expectUnlimitedSwap := func(cgroupVersion CgroupVersion, resources ...*runtimeapi.LinuxContainerResources) {
   922  		const msg = "container is expected to have unlimited swap access"
   923  
   924  		for _, r := range resources {
   925  			switch cgroupVersion {
   926  			case cgroupV1:
   927  				assert.Equal(t, int64(-1), r.MemorySwapLimitInBytes, msg)
   928  			case cgroupV2:
   929  				assert.Equal(t, "max", r.Unified[cm.Cgroup2MaxSwapFilename], msg)
   930  			}
   931  		}
   932  	}
   933  
   934  	expectSwap := func(cgroupVersion CgroupVersion, swapBytesExpected int64, resources *runtimeapi.LinuxContainerResources) {
   935  		msg := fmt.Sprintf("container swap is expected to be limited by %d bytes", swapBytesExpected)
   936  
   937  		switch cgroupVersion {
   938  		case cgroupV1:
   939  			assert.Equal(t, resources.MemoryLimitInBytes+swapBytesExpected, resources.MemorySwapLimitInBytes, msg)
   940  		case cgroupV2:
   941  			assert.Equal(t, fmt.Sprintf("%d", swapBytesExpected), resources.Unified[cm.Cgroup2MaxSwapFilename], msg)
   942  		}
   943  	}
   944  
   945  	calcSwapForBurstablePods := func(containerMemoryRequest int64) int64 {
   946  		swapSize, err := calcSwapForBurstablePods(containerMemoryRequest, int64(m.machineInfo.MemoryCapacity), int64(m.machineInfo.SwapCapacity))
   947  		assert.NoError(t, err)
   948  
   949  		return swapSize
   950  	}
   951  
   952  	for _, tc := range []struct {
   953  		name                        string
   954  		cgroupVersion               CgroupVersion
   955  		qosClass                    v1.PodQOSClass
   956  		swapDisabledOnNode          bool
   957  		nodeSwapFeatureGateEnabled  bool
   958  		swapBehavior                string
   959  		addContainerWithoutRequests bool
   960  		addGuaranteedContainer      bool
   961  	}{
   962  		// With cgroup v1
   963  		{
   964  			name:                       "cgroups v1, LimitedSwap, Burstable QoS",
   965  			cgroupVersion:              cgroupV1,
   966  			qosClass:                   v1.PodQOSBurstable,
   967  			nodeSwapFeatureGateEnabled: true,
   968  			swapBehavior:               types.LimitedSwap,
   969  		},
   970  		{
   971  			name:                       "cgroups v1, UnlimitedSwap, Burstable QoS",
   972  			cgroupVersion:              cgroupV1,
   973  			qosClass:                   v1.PodQOSBurstable,
   974  			nodeSwapFeatureGateEnabled: true,
   975  			swapBehavior:               types.UnlimitedSwap,
   976  		},
   977  		{
   978  			name:                       "cgroups v1, LimitedSwap, Best-effort QoS",
   979  			cgroupVersion:              cgroupV1,
   980  			qosClass:                   v1.PodQOSBestEffort,
   981  			nodeSwapFeatureGateEnabled: true,
   982  			swapBehavior:               types.LimitedSwap,
   983  		},
   984  
   985  		// With feature gate turned off
   986  		{
   987  			name:                       "NodeSwap feature gate turned off, cgroups v2, LimitedSwap",
   988  			cgroupVersion:              cgroupV2,
   989  			qosClass:                   v1.PodQOSBurstable,
   990  			nodeSwapFeatureGateEnabled: false,
   991  			swapBehavior:               types.LimitedSwap,
   992  		},
   993  		{
   994  			name:                       "NodeSwap feature gate turned off, cgroups v2, UnlimitedSwap",
   995  			cgroupVersion:              cgroupV2,
   996  			qosClass:                   v1.PodQOSBurstable,
   997  			nodeSwapFeatureGateEnabled: false,
   998  			swapBehavior:               types.UnlimitedSwap,
   999  		},
  1000  
  1001  		// With no swapBehavior, UnlimitedSwap should be the default
  1002  		{
  1003  			name:                       "With no swapBehavior - UnlimitedSwap should be the default",
  1004  			cgroupVersion:              cgroupV2,
  1005  			qosClass:                   v1.PodQOSBestEffort,
  1006  			nodeSwapFeatureGateEnabled: true,
  1007  			swapBehavior:               "",
  1008  		},
  1009  
  1010  		// With Guaranteed and Best-effort QoS
  1011  		{
  1012  			name:                       "Best-effort QoS, cgroups v2, LimitedSwap",
  1013  			cgroupVersion:              cgroupV2,
  1014  			qosClass:                   v1.PodQOSBurstable,
  1015  			nodeSwapFeatureGateEnabled: true,
  1016  			swapBehavior:               types.LimitedSwap,
  1017  		},
  1018  		{
  1019  			name:                       "Best-effort QoS, cgroups v2, UnlimitedSwap",
  1020  			cgroupVersion:              cgroupV2,
  1021  			qosClass:                   v1.PodQOSBurstable,
  1022  			nodeSwapFeatureGateEnabled: true,
  1023  			swapBehavior:               types.UnlimitedSwap,
  1024  		},
  1025  		{
  1026  			name:                       "Guaranteed QoS, cgroups v2, LimitedSwap",
  1027  			cgroupVersion:              cgroupV2,
  1028  			qosClass:                   v1.PodQOSGuaranteed,
  1029  			nodeSwapFeatureGateEnabled: true,
  1030  			swapBehavior:               types.LimitedSwap,
  1031  		},
  1032  		{
  1033  			name:                       "Guaranteed QoS, cgroups v2, UnlimitedSwap",
  1034  			cgroupVersion:              cgroupV2,
  1035  			qosClass:                   v1.PodQOSGuaranteed,
  1036  			nodeSwapFeatureGateEnabled: true,
  1037  			swapBehavior:               types.UnlimitedSwap,
  1038  		},
  1039  
  1040  		// With a "guaranteed" container (when memory requests equal to limits)
  1041  		{
  1042  			name:                        "Burstable QoS, cgroups v2, LimitedSwap, with a guaranteed container",
  1043  			cgroupVersion:               cgroupV2,
  1044  			qosClass:                    v1.PodQOSBurstable,
  1045  			nodeSwapFeatureGateEnabled:  true,
  1046  			swapBehavior:                types.LimitedSwap,
  1047  			addContainerWithoutRequests: false,
  1048  			addGuaranteedContainer:      true,
  1049  		},
  1050  		{
  1051  			name:                        "Burstable QoS, cgroups v2, UnlimitedSwap, with a guaranteed container",
  1052  			cgroupVersion:               cgroupV2,
  1053  			qosClass:                    v1.PodQOSBurstable,
  1054  			nodeSwapFeatureGateEnabled:  true,
  1055  			swapBehavior:                types.UnlimitedSwap,
  1056  			addContainerWithoutRequests: false,
  1057  			addGuaranteedContainer:      true,
  1058  		},
  1059  
  1060  		// Swap is expected to be allocated
  1061  		{
  1062  			name:                        "Burstable QoS, cgroups v2, LimitedSwap",
  1063  			cgroupVersion:               cgroupV2,
  1064  			qosClass:                    v1.PodQOSBurstable,
  1065  			nodeSwapFeatureGateEnabled:  true,
  1066  			swapBehavior:                types.LimitedSwap,
  1067  			addContainerWithoutRequests: false,
  1068  			addGuaranteedContainer:      false,
  1069  		},
  1070  		{
  1071  			name:                        "Burstable QoS, cgroups v2, UnlimitedSwap",
  1072  			cgroupVersion:               cgroupV2,
  1073  			qosClass:                    v1.PodQOSBurstable,
  1074  			nodeSwapFeatureGateEnabled:  true,
  1075  			swapBehavior:                types.UnlimitedSwap,
  1076  			addContainerWithoutRequests: false,
  1077  			addGuaranteedContainer:      false,
  1078  		},
  1079  		{
  1080  			name:                        "Burstable QoS, cgroups v2, LimitedSwap, with a container with no requests",
  1081  			cgroupVersion:               cgroupV2,
  1082  			qosClass:                    v1.PodQOSBurstable,
  1083  			nodeSwapFeatureGateEnabled:  true,
  1084  			swapBehavior:                types.LimitedSwap,
  1085  			addContainerWithoutRequests: true,
  1086  			addGuaranteedContainer:      false,
  1087  		},
  1088  		{
  1089  			name:                        "Burstable QoS, cgroups v2, UnlimitedSwap, with a container with no requests",
  1090  			cgroupVersion:               cgroupV2,
  1091  			qosClass:                    v1.PodQOSBurstable,
  1092  			nodeSwapFeatureGateEnabled:  true,
  1093  			swapBehavior:                types.UnlimitedSwap,
  1094  			addContainerWithoutRequests: true,
  1095  			addGuaranteedContainer:      false,
  1096  		},
  1097  		// All the above examples with Swap disabled on node
  1098  		{
  1099  			name:                       "Swap disabled on node, cgroups v1, LimitedSwap, Burstable QoS",
  1100  			swapDisabledOnNode:         true,
  1101  			cgroupVersion:              cgroupV1,
  1102  			qosClass:                   v1.PodQOSBurstable,
  1103  			nodeSwapFeatureGateEnabled: true,
  1104  			swapBehavior:               types.LimitedSwap,
  1105  		},
  1106  		{
  1107  			name:                       "Swap disabled on node, cgroups v1, UnlimitedSwap, Burstable QoS",
  1108  			swapDisabledOnNode:         true,
  1109  			cgroupVersion:              cgroupV1,
  1110  			qosClass:                   v1.PodQOSBurstable,
  1111  			nodeSwapFeatureGateEnabled: true,
  1112  			swapBehavior:               types.UnlimitedSwap,
  1113  		},
  1114  		{
  1115  			name:                       "Swap disabled on node, cgroups v1, LimitedSwap, Best-effort QoS",
  1116  			swapDisabledOnNode:         true,
  1117  			cgroupVersion:              cgroupV1,
  1118  			qosClass:                   v1.PodQOSBestEffort,
  1119  			nodeSwapFeatureGateEnabled: true,
  1120  			swapBehavior:               types.LimitedSwap,
  1121  		},
  1122  
  1123  		// With feature gate turned off
  1124  		{
  1125  			name:                       "Swap disabled on node, NodeSwap feature gate turned off, cgroups v2, LimitedSwap",
  1126  			swapDisabledOnNode:         true,
  1127  			cgroupVersion:              cgroupV2,
  1128  			qosClass:                   v1.PodQOSBurstable,
  1129  			nodeSwapFeatureGateEnabled: false,
  1130  			swapBehavior:               types.LimitedSwap,
  1131  		},
  1132  		{
  1133  			name:                       "Swap disabled on node, NodeSwap feature gate turned off, cgroups v2, UnlimitedSwap",
  1134  			swapDisabledOnNode:         true,
  1135  			cgroupVersion:              cgroupV2,
  1136  			qosClass:                   v1.PodQOSBurstable,
  1137  			nodeSwapFeatureGateEnabled: false,
  1138  			swapBehavior:               types.UnlimitedSwap,
  1139  		},
  1140  
  1141  		// With no swapBehavior, UnlimitedSwap should be the default
  1142  		{
  1143  			name:                       "Swap disabled on node, With no swapBehavior - UnlimitedSwap should be the default",
  1144  			swapDisabledOnNode:         true,
  1145  			cgroupVersion:              cgroupV2,
  1146  			qosClass:                   v1.PodQOSBestEffort,
  1147  			nodeSwapFeatureGateEnabled: true,
  1148  			swapBehavior:               "",
  1149  		},
  1150  
  1151  		// With Guaranteed and Best-effort QoS
  1152  		{
  1153  			name:                       "Swap disabled on node, Best-effort QoS, cgroups v2, LimitedSwap",
  1154  			swapDisabledOnNode:         true,
  1155  			cgroupVersion:              cgroupV2,
  1156  			qosClass:                   v1.PodQOSBurstable,
  1157  			nodeSwapFeatureGateEnabled: true,
  1158  			swapBehavior:               types.LimitedSwap,
  1159  		},
  1160  		{
  1161  			name:                       "Swap disabled on node, Best-effort QoS, cgroups v2, UnlimitedSwap",
  1162  			swapDisabledOnNode:         true,
  1163  			cgroupVersion:              cgroupV2,
  1164  			qosClass:                   v1.PodQOSBurstable,
  1165  			nodeSwapFeatureGateEnabled: true,
  1166  			swapBehavior:               types.UnlimitedSwap,
  1167  		},
  1168  		{
  1169  			name:                       "Swap disabled on node, Guaranteed QoS, cgroups v2, LimitedSwap",
  1170  			swapDisabledOnNode:         true,
  1171  			cgroupVersion:              cgroupV2,
  1172  			qosClass:                   v1.PodQOSGuaranteed,
  1173  			nodeSwapFeatureGateEnabled: true,
  1174  			swapBehavior:               types.LimitedSwap,
  1175  		},
  1176  		{
  1177  			name:                       "Swap disabled on node, Guaranteed QoS, cgroups v2, UnlimitedSwap",
  1178  			swapDisabledOnNode:         true,
  1179  			cgroupVersion:              cgroupV2,
  1180  			qosClass:                   v1.PodQOSGuaranteed,
  1181  			nodeSwapFeatureGateEnabled: true,
  1182  			swapBehavior:               types.UnlimitedSwap,
  1183  		},
  1184  
  1185  		// With a "guaranteed" container (when memory requests equal to limits)
  1186  		{
  1187  			name:                        "Swap disabled on node, Burstable QoS, cgroups v2, LimitedSwap, with a guaranteed container",
  1188  			swapDisabledOnNode:          true,
  1189  			cgroupVersion:               cgroupV2,
  1190  			qosClass:                    v1.PodQOSBurstable,
  1191  			nodeSwapFeatureGateEnabled:  true,
  1192  			swapBehavior:                types.LimitedSwap,
  1193  			addContainerWithoutRequests: false,
  1194  			addGuaranteedContainer:      true,
  1195  		},
  1196  		{
  1197  			name:                        "Swap disabled on node, Burstable QoS, cgroups v2, UnlimitedSwap, with a guaranteed container",
  1198  			swapDisabledOnNode:          true,
  1199  			cgroupVersion:               cgroupV2,
  1200  			qosClass:                    v1.PodQOSBurstable,
  1201  			nodeSwapFeatureGateEnabled:  true,
  1202  			swapBehavior:                types.UnlimitedSwap,
  1203  			addContainerWithoutRequests: false,
  1204  			addGuaranteedContainer:      true,
  1205  		},
  1206  
  1207  		// Swap is expected to be allocated
  1208  		{
  1209  			name:                        "Swap disabled on node, Burstable QoS, cgroups v2, LimitedSwap",
  1210  			swapDisabledOnNode:          true,
  1211  			cgroupVersion:               cgroupV2,
  1212  			qosClass:                    v1.PodQOSBurstable,
  1213  			nodeSwapFeatureGateEnabled:  true,
  1214  			swapBehavior:                types.LimitedSwap,
  1215  			addContainerWithoutRequests: false,
  1216  			addGuaranteedContainer:      false,
  1217  		},
  1218  		{
  1219  			name:                        "Swap disabled on node, Burstable QoS, cgroups v2, UnlimitedSwap",
  1220  			swapDisabledOnNode:          true,
  1221  			cgroupVersion:               cgroupV2,
  1222  			qosClass:                    v1.PodQOSBurstable,
  1223  			nodeSwapFeatureGateEnabled:  true,
  1224  			swapBehavior:                types.UnlimitedSwap,
  1225  			addContainerWithoutRequests: false,
  1226  			addGuaranteedContainer:      false,
  1227  		},
  1228  		{
  1229  			name:                        "Swap disabled on node, Burstable QoS, cgroups v2, LimitedSwap, with a container with no requests",
  1230  			swapDisabledOnNode:          true,
  1231  			cgroupVersion:               cgroupV2,
  1232  			qosClass:                    v1.PodQOSBurstable,
  1233  			nodeSwapFeatureGateEnabled:  true,
  1234  			swapBehavior:                types.LimitedSwap,
  1235  			addContainerWithoutRequests: true,
  1236  			addGuaranteedContainer:      false,
  1237  		},
  1238  		{
  1239  			name:                        "Swap disabled on node, Burstable QoS, cgroups v2, UnlimitedSwap, with a container with no requests",
  1240  			swapDisabledOnNode:          true,
  1241  			cgroupVersion:               cgroupV2,
  1242  			qosClass:                    v1.PodQOSBurstable,
  1243  			nodeSwapFeatureGateEnabled:  true,
  1244  			swapBehavior:                types.UnlimitedSwap,
  1245  			addContainerWithoutRequests: true,
  1246  			addGuaranteedContainer:      false,
  1247  		},
  1248  	} {
  1249  		t.Run(tc.name, func(t *testing.T) {
  1250  			setCgroupVersionDuringTest(tc.cgroupVersion)
  1251  			defer setSwapControllerAvailableDuringTest(!tc.swapDisabledOnNode)()
  1252  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeSwap, tc.nodeSwapFeatureGateEnabled)()
  1253  			m.memorySwapBehavior = tc.swapBehavior
  1254  
  1255  			var resourceReqsC1, resourceReqsC2 v1.ResourceRequirements
  1256  			switch tc.qosClass {
  1257  			case v1.PodQOSBurstable:
  1258  				resourceReqsC1 = v1.ResourceRequirements{
  1259  					Requests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("1Gi")},
  1260  				}
  1261  
  1262  				if !tc.addContainerWithoutRequests {
  1263  					resourceReqsC2 = v1.ResourceRequirements{
  1264  						Requests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("2Gi")},
  1265  					}
  1266  
  1267  					if tc.addGuaranteedContainer {
  1268  						resourceReqsC2.Limits = v1.ResourceList{v1.ResourceMemory: resource.MustParse("2Gi")}
  1269  					}
  1270  				}
  1271  			case v1.PodQOSGuaranteed:
  1272  				resourceReqsC1 = v1.ResourceRequirements{
  1273  					Requests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("1Gi"), v1.ResourceCPU: resource.MustParse("1")},
  1274  					Limits:   v1.ResourceList{v1.ResourceMemory: resource.MustParse("1Gi"), v1.ResourceCPU: resource.MustParse("1")},
  1275  				}
  1276  				resourceReqsC2 = v1.ResourceRequirements{
  1277  					Requests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("2Gi"), v1.ResourceCPU: resource.MustParse("1")},
  1278  					Limits:   v1.ResourceList{v1.ResourceMemory: resource.MustParse("2Gi"), v1.ResourceCPU: resource.MustParse("1")},
  1279  				}
  1280  			}
  1281  			pod.Spec.Containers[0].Resources = resourceReqsC1
  1282  			pod.Spec.Containers[1].Resources = resourceReqsC2
  1283  
  1284  			resourcesC1 := m.generateLinuxContainerResources(pod, &pod.Spec.Containers[0], false)
  1285  			resourcesC2 := m.generateLinuxContainerResources(pod, &pod.Spec.Containers[1], false)
  1286  
  1287  			if tc.swapDisabledOnNode {
  1288  				expectSwapDisabled(tc.cgroupVersion, resourcesC1, resourcesC2)
  1289  				return
  1290  			}
  1291  
  1292  			if !tc.nodeSwapFeatureGateEnabled || tc.cgroupVersion == cgroupV1 || (tc.swapBehavior == types.LimitedSwap && tc.qosClass != v1.PodQOSBurstable) {
  1293  				expectNoSwap(tc.cgroupVersion, resourcesC1, resourcesC2)
  1294  				return
  1295  			}
  1296  
  1297  			if tc.swapBehavior == types.UnlimitedSwap || tc.swapBehavior == "" {
  1298  				expectUnlimitedSwap(tc.cgroupVersion, resourcesC1, resourcesC2)
  1299  				return
  1300  			}
  1301  
  1302  			c1ExpectedSwap := calcSwapForBurstablePods(resourceReqsC1.Requests.Memory().Value())
  1303  			c2ExpectedSwap := int64(0)
  1304  			if !tc.addContainerWithoutRequests && !tc.addGuaranteedContainer {
  1305  				c2ExpectedSwap = calcSwapForBurstablePods(resourceReqsC2.Requests.Memory().Value())
  1306  			}
  1307  
  1308  			expectSwap(tc.cgroupVersion, c1ExpectedSwap, resourcesC1)
  1309  			expectSwap(tc.cgroupVersion, c2ExpectedSwap, resourcesC2)
  1310  		})
  1311  	}
  1312  }
  1313  
  1314  type CgroupVersion string
  1315  
  1316  const (
  1317  	cgroupV1 CgroupVersion = "v1"
  1318  	cgroupV2 CgroupVersion = "v2"
  1319  )
  1320  
  1321  func setCgroupVersionDuringTest(version CgroupVersion) {
  1322  	isCgroup2UnifiedMode = func() bool {
  1323  		return version == cgroupV2
  1324  	}
  1325  }
  1326  
  1327  func setSwapControllerAvailableDuringTest(available bool) func() {
  1328  	original := swapControllerAvailable
  1329  	swapControllerAvailable = func() bool {
  1330  		return available
  1331  	}
  1332  
  1333  	return func() {
  1334  		swapControllerAvailable = original
  1335  	}
  1336  }