Unit tests for pod level hugepage resources

This commit is contained in:
Kevin Torres 2025-02-26 19:38:32 +00:00
parent 51db93c3fb
commit b9e0d4ad66
6 changed files with 745 additions and 61 deletions

View File

@ -1222,6 +1222,298 @@ func TestPodResourcesDefaults(t *testing.T) {
},
},
},
}, {
name: "pod hugepages requests=unset limits=unset, container hugepages requests=unset limits=set",
podLevelResourcesEnabled: true,
podResources: &v1.ResourceRequirements{
Limits: v1.ResourceList{
"cpu": resource.MustParse("5m"),
},
},
containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
"cpu": resource.MustParse("2m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("4Mi"),
},
},
}, {
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
"cpu": resource.MustParse("1m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"),
},
},
},
},
expectedPodSpec: v1.PodSpec{
Resources: &v1.ResourceRequirements{
Requests: v1.ResourceList{
"cpu": resource.MustParse("3m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("6Mi"),
},
Limits: v1.ResourceList{
"cpu": resource.MustParse("5m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("6Mi"),
},
},
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
"cpu": resource.MustParse("2m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("4Mi"),
},
Limits: v1.ResourceList{
"cpu": resource.MustParse("2m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("4Mi"),
},
},
}, {
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
"cpu": resource.MustParse("1m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"),
},
Limits: v1.ResourceList{
"cpu": resource.MustParse("1m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"),
},
},
},
},
},
}, {
name: "pod hugepages requests=unset limits=set, container hugepages requests=unset limits=set",
podLevelResourcesEnabled: true,
podResources: &v1.ResourceRequirements{
Limits: v1.ResourceList{
"cpu": resource.MustParse("5m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
},
},
containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
"cpu": resource.MustParse("2m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("4Mi"),
},
},
}, {
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
"cpu": resource.MustParse("1m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"),
},
},
},
},
expectedPodSpec: v1.PodSpec{
Resources: &v1.ResourceRequirements{
Requests: v1.ResourceList{
"cpu": resource.MustParse("3m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
},
Limits: v1.ResourceList{
"cpu": resource.MustParse("5m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
},
},
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
"cpu": resource.MustParse("2m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("4Mi"),
},
Limits: v1.ResourceList{
"cpu": resource.MustParse("2m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("4Mi"),
},
},
}, {
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
"cpu": resource.MustParse("1m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"),
},
Limits: v1.ResourceList{
"cpu": resource.MustParse("1m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"),
},
},
},
},
},
}, {
name: "pod hugepages requests=set limits=set, container hugepages requests=unset limits=set",
podLevelResourcesEnabled: true,
podResources: &v1.ResourceRequirements{
Limits: v1.ResourceList{
"cpu": resource.MustParse("5m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
},
Requests: v1.ResourceList{
"cpu": resource.MustParse("5m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
},
},
containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
"cpu": resource.MustParse("2m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("4Mi"),
},
},
}, {
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
"cpu": resource.MustParse("1m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"),
},
},
},
},
expectedPodSpec: v1.PodSpec{
Resources: &v1.ResourceRequirements{
Requests: v1.ResourceList{
"cpu": resource.MustParse("5m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
},
Limits: v1.ResourceList{
"cpu": resource.MustParse("5m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
},
},
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
"cpu": resource.MustParse("2m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("4Mi"),
},
Limits: v1.ResourceList{
"cpu": resource.MustParse("2m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("4Mi"),
},
},
}, {
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
"cpu": resource.MustParse("1m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"),
},
Limits: v1.ResourceList{
"cpu": resource.MustParse("1m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"),
},
},
},
},
},
}, {
name: "pod hugepages requests=set limits=set, container hugepages requests=unset limits=unset",
podLevelResourcesEnabled: true,
podResources: &v1.ResourceRequirements{
Limits: v1.ResourceList{
"cpu": resource.MustParse("5m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
},
Requests: v1.ResourceList{
"cpu": resource.MustParse("5m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
},
},
containers: []v1.Container{
{
Resources: v1.ResourceRequirements{},
},
},
expectedPodSpec: v1.PodSpec{
Resources: &v1.ResourceRequirements{
Requests: v1.ResourceList{
"cpu": resource.MustParse("5m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
},
Limits: v1.ResourceList{
"cpu": resource.MustParse("5m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
},
},
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{},
},
},
},
}, {
name: "pod hugepages requests=unset limits=set, container hugepages requests=unset limits=set different hugepagesizes between pod and container level",
podLevelResourcesEnabled: true,
podResources: &v1.ResourceRequirements{
Limits: v1.ResourceList{
"cpu": resource.MustParse("5m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
},
},
containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
"cpu": resource.MustParse("2m"),
v1.ResourceHugePagesPrefix + "1Gi": resource.MustParse("1Gi"),
},
},
}, {
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
"cpu": resource.MustParse("1m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"),
},
},
},
},
expectedPodSpec: v1.PodSpec{
Resources: &v1.ResourceRequirements{
Requests: v1.ResourceList{
"cpu": resource.MustParse("3m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
v1.ResourceHugePagesPrefix + "1Gi": resource.MustParse("1Gi"),
},
Limits: v1.ResourceList{
"cpu": resource.MustParse("5m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
v1.ResourceHugePagesPrefix + "1Gi": resource.MustParse("1Gi"),
},
},
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
"cpu": resource.MustParse("2m"),
v1.ResourceHugePagesPrefix + "1Gi": resource.MustParse("1Gi"),
},
Limits: v1.ResourceList{
"cpu": resource.MustParse("2m"),
v1.ResourceHugePagesPrefix + "1Gi": resource.MustParse("1Gi"),
},
},
}, {
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
"cpu": resource.MustParse("1m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"),
},
Limits: v1.ResourceList{
"cpu": resource.MustParse("1m"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"),
},
},
},
},
},
}}
for _, tc := range cases {

View File

@ -19024,7 +19024,7 @@ func TestValidatePodResourceConsistency(t *testing.T) {
},
},
}, {
name: "indivdual container limits greater than pod limits",
name: "individual container limits greater than pod limits",
podResources: core.ResourceRequirements{
Limits: core.ResourceList{
core.ResourceCPU: resource.MustParse("10"),
@ -19084,6 +19084,8 @@ func TestValidatePodResourceNames(t *testing.T) {
{"memory", false},
{"cpu", false},
{"storage", true},
{v1.ResourceHugePagesPrefix + "2Mi", false},
{v1.ResourceHugePagesPrefix + "1Gi", false},
{"requests.cpu", true},
{"requests.memory", true},
{"requests.storage", true},
@ -19128,6 +19130,8 @@ func TestValidateResourceNames(t *testing.T) {
{"memory", true, ""},
{"cpu", true, ""},
{"storage", true, ""},
{v1.ResourceHugePagesPrefix + "2Mi", true, ""},
{v1.ResourceHugePagesPrefix + "1Gi", true, ""},
{"requests.cpu", true, ""},
{"requests.memory", true, ""},
{"requests.storage", true, ""},
@ -23624,6 +23628,48 @@ func TestValidateResourceRequirements(t *testing.T) {
},
},
validateFn: ValidateContainerResourceRequirements,
}, {
name: "container resource hugepage with cpu or memory",
requirements: core.ResourceRequirements{
Limits: core.ResourceList{
core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
core.ResourceCPU: resource.MustParse("10"),
},
Requests: core.ResourceList{
core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
},
},
validateFn: ValidateContainerResourceRequirements,
}, {
name: "container resource hugepage limit without request",
requirements: core.ResourceRequirements{
Limits: core.ResourceList{
core.ResourceMemory: resource.MustParse("2Mi"),
core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
},
},
validateFn: ValidateContainerResourceRequirements,
}, {
name: "pod resource hugepages with cpu or memory",
requirements: core.ResourceRequirements{
Limits: core.ResourceList{
core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
},
Requests: core.ResourceList{
core.ResourceMemory: resource.MustParse("2Mi"),
core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
},
},
validateFn: validatePodResourceRequirements,
}, {
name: "pod resource hugepages limit without request",
requirements: core.ResourceRequirements{
Limits: core.ResourceList{
core.ResourceMemory: resource.MustParse("2Mi"),
core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
},
},
validateFn: validatePodResourceRequirements,
}, {
name: "limits and requests of memory resource are equal",
requirements: core.ResourceRequirements{
@ -23686,62 +23732,81 @@ func TestValidateResourceRequirements(t *testing.T) {
validateFn func(requirements *core.ResourceRequirements,
podClaimNames sets.Set[string], fldPath *field.Path,
opts PodValidationOptions) field.ErrorList
}{{
name: "hugepage resource without cpu or memory",
requirements: core.ResourceRequirements{
Limits: core.ResourceList{
core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
}{
{
name: "container resource hugepage without cpu or memory",
requirements: core.ResourceRequirements{
Limits: core.ResourceList{
core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
},
Requests: core.ResourceList{
core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
},
},
Requests: core.ResourceList{
core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
validateFn: ValidateContainerResourceRequirements,
}, {
name: "container resource hugepage without limit",
requirements: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceMemory: resource.MustParse("2Mi"),
core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
},
},
validateFn: ValidateContainerResourceRequirements,
}, {
name: "pod resource hugepages without cpu or memory",
requirements: core.ResourceRequirements{
Limits: core.ResourceList{
core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
},
Requests: core.ResourceList{
core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
},
},
validateFn: validatePodResourceRequirements,
}, {
name: "pod resource hugepages request without limit",
requirements: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceMemory: resource.MustParse("2Mi"),
core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
},
},
validateFn: validatePodResourceRequirements,
}, {
name: "pod resource with ephemeral-storage",
requirements: core.ResourceRequirements{
Limits: core.ResourceList{
core.ResourceName(core.ResourceEphemeralStorage): resource.MustParse("2Mi"),
},
Requests: core.ResourceList{
core.ResourceName(core.ResourceEphemeralStorage + "2Mi"): resource.MustParse("2Mi"),
},
},
validateFn: validatePodResourceRequirements,
}, {
name: "pod resource with unsupported prefixed resources",
requirements: core.ResourceRequirements{
Limits: core.ResourceList{
core.ResourceName("kubernetesio/" + core.ResourceCPU): resource.MustParse("2"),
},
Requests: core.ResourceList{
core.ResourceName("kubernetesio/" + core.ResourceMemory): resource.MustParse("2"),
},
},
validateFn: validatePodResourceRequirements,
}, {
name: "pod resource with unsupported native resources",
requirements: core.ResourceRequirements{
Limits: core.ResourceList{
core.ResourceName("kubernetes.io/" + strings.Repeat("a", 63)): resource.MustParse("2"),
},
Requests: core.ResourceList{
core.ResourceName("kubernetes.io/" + strings.Repeat("a", 63)): resource.MustParse("2"),
},
},
validateFn: validatePodResourceRequirements,
},
validateFn: ValidateContainerResourceRequirements,
}, {
name: "pod resource with hugepages",
requirements: core.ResourceRequirements{
Limits: core.ResourceList{
core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
},
Requests: core.ResourceList{
core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
},
},
validateFn: validatePodResourceRequirements,
}, {
name: "pod resource with ephemeral-storage",
requirements: core.ResourceRequirements{
Limits: core.ResourceList{
core.ResourceName(core.ResourceEphemeralStorage): resource.MustParse("2Mi"),
},
Requests: core.ResourceList{
core.ResourceName(core.ResourceEphemeralStorage + "2Mi"): resource.MustParse("2Mi"),
},
},
validateFn: validatePodResourceRequirements,
}, {
name: "pod resource with unsupported prefixed resources",
requirements: core.ResourceRequirements{
Limits: core.ResourceList{
core.ResourceName("kubernetesio/" + core.ResourceCPU): resource.MustParse("2"),
},
Requests: core.ResourceList{
core.ResourceName("kubernetesio/" + core.ResourceMemory): resource.MustParse("2"),
},
},
validateFn: validatePodResourceRequirements,
}, {
name: "pod resource with unsupported native resources",
requirements: core.ResourceRequirements{
Limits: core.ResourceList{
core.ResourceName("kubernetes.io/" + strings.Repeat("a", 63)): resource.MustParse("2"),
},
Requests: core.ResourceList{
core.ResourceName("kubernetes.io/" + strings.Repeat("a", 63)): resource.MustParse("2"),
},
},
validateFn: validatePodResourceRequirements,
},
{
name: "pod resource with unsupported empty native resource name",
requirements: core.ResourceRequirements{

View File

@ -665,7 +665,7 @@ func TestGetHugepageLimitsFromResources(t *testing.T) {
}
}
results := GetHugepageLimitsFromResources(test.resources)
results := GetHugepageLimitsFromResources(&v1.Pod{}, test.resources)
if !reflect.DeepEqual(expectedHugepages, results) {
t.Errorf("%s test failed. Expected %v but got %v", test.name, expectedHugepages, results)
}

View File

@ -543,6 +543,45 @@ func TestEnoughRequests(t *testing.T) {
ResourceName: v1.ResourceMemory, Reason: getErrReason(v1.ResourceMemory), Requested: 2, Used: 19, Capacity: 20},
},
},
{
podLevelResourcesEnabled: true,
pod: newPodLevelResourcesPod(
newResourcePod(),
v1.ResourceRequirements{
Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("3m"), v1.ResourceMemory: resource.MustParse("2"), hugePageResourceA: *resource.NewQuantity(5, resource.BinarySI)},
},
),
nodeInfo: framework.NewNodeInfo(),
name: "pod-level hugepages resource fit",
wantInsufficientResources: []InsufficientResource{},
},
{
podLevelResourcesEnabled: true,
pod: newPodLevelResourcesPod(
newResourcePod(framework.Resource{MilliCPU: 1, Memory: 1, ScalarResources: map[v1.ResourceName]int64{hugePageResourceA: 3}}),
v1.ResourceRequirements{
Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("3m"), v1.ResourceMemory: resource.MustParse("2"), hugePageResourceA: *resource.NewQuantity(5, resource.BinarySI)},
},
),
nodeInfo: framework.NewNodeInfo(),
name: "both pod-level and container-level hugepages resource fit",
wantInsufficientResources: []InsufficientResource{},
},
{
podLevelResourcesEnabled: true,
pod: newPodLevelResourcesPod(
newResourcePod(),
v1.ResourceRequirements{
Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("3m"), v1.ResourceMemory: resource.MustParse("2"), hugePageResourceA: *resource.NewQuantity(10, resource.BinarySI)},
},
),
nodeInfo: framework.NewNodeInfo(),
name: "pod-level hugepages resource not fit",
wantStatus: framework.NewStatus(framework.Unschedulable, getErrReason(hugePageResourceA)),
wantInsufficientResources: []InsufficientResource{
{ResourceName: hugePageResourceA, Reason: getErrReason(hugePageResourceA), Requested: 10, Used: 0, Capacity: 5},
},
},
{
podLevelResourcesEnabled: true,
pod: newResourceInitPod(newPodLevelResourcesPod(
@ -1547,8 +1586,25 @@ func TestIsFit(t *testing.T) {
pod: st.MakePod().Resources(
v1.ResourceRequirements{Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("2")}},
).Obj(),
node: st.MakeNode().Capacity(map[v1.ResourceName]string{v1.ResourceCPU: "2"}).Obj(),
expected: true,
node: st.MakeNode().Capacity(map[v1.ResourceName]string{v1.ResourceCPU: "2"}).Obj(),
podLevelResourcesEnabled: true,
expected: true,
},
"sufficient pod-level resource hugepages": {
pod: st.MakePod().Resources(
v1.ResourceRequirements{Requests: v1.ResourceList{hugePageResourceA: resource.MustParse("2Mi")}},
).Obj(),
node: st.MakeNode().Capacity(map[v1.ResourceName]string{hugePageResourceA: "2Mi"}).Obj(),
podLevelResourcesEnabled: true,
expected: true,
},
"insufficient pod-level resource hugepages": {
pod: st.MakePod().Resources(
v1.ResourceRequirements{Requests: v1.ResourceList{hugePageResourceA: resource.MustParse("4Mi")}},
).Obj(),
node: st.MakeNode().Capacity(map[v1.ResourceName]string{hugePageResourceA: "2Mi"}).Obj(),
podLevelResourcesEnabled: true,
expected: false,
},
}

View File

@ -26,6 +26,9 @@ import (
"strings"
"testing"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/kubelet/util/swap"
v1 "k8s.io/api/core/v1"
@ -373,10 +376,11 @@ func TestMetrics(t *testing.T) {
func TestGetHugePagesMountOptions(t *testing.T) {
testCases := map[string]struct {
pod *v1.Pod
medium v1.StorageMedium
shouldFail bool
expectedResult string
pod *v1.Pod
medium v1.StorageMedium
shouldFail bool
expectedResult string
podLevelResourcesEnabled bool
}{
"ProperValues": {
pod: &v1.Pod{
@ -605,10 +609,124 @@ func TestGetHugePagesMountOptions(t *testing.T) {
shouldFail: true,
expectedResult: "",
},
"PodLevelResourcesSinglePageSize": {
podLevelResourcesEnabled: true,
pod: &v1.Pod{
Spec: v1.PodSpec{
Resources: &v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
},
},
},
},
medium: v1.StorageMediumHugePages,
shouldFail: false,
expectedResult: "pagesize=2Mi",
},
"PodLevelResourcesSinglePageSizeMediumPrefix": {
podLevelResourcesEnabled: true,
pod: &v1.Pod{
Spec: v1.PodSpec{
Resources: &v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
},
},
},
},
medium: v1.StorageMediumHugePagesPrefix + "2Mi",
shouldFail: false,
expectedResult: "pagesize=2Mi",
},
"PodLevelResourcesMultiPageSize": {
podLevelResourcesEnabled: true,
pod: &v1.Pod{
Spec: v1.PodSpec{
Resources: &v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
},
},
},
},
medium: v1.StorageMediumHugePages,
shouldFail: true,
expectedResult: "",
},
"PodLevelResourcesMultiPageSizeMediumPrefix": {
podLevelResourcesEnabled: true,
pod: &v1.Pod{
Spec: v1.PodSpec{
Resources: &v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
},
},
},
},
medium: v1.StorageMediumHugePagesPrefix + "2Mi",
shouldFail: false,
expectedResult: "pagesize=2Mi",
},
"PodAndContainerLevelResourcesMultiPageSizeHugePagesMedium": {
podLevelResourcesEnabled: true,
pod: &v1.Pod{
Spec: v1.PodSpec{
Resources: &v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
},
},
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
},
},
},
},
},
},
medium: v1.StorageMediumHugePages,
shouldFail: true,
expectedResult: "",
},
"PodAndContainerLevelResourcesMultiPageSizeHugePagesMediumPrefix": {
podLevelResourcesEnabled: true,
pod: &v1.Pod{
Spec: v1.PodSpec{
Resources: &v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
},
},
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
},
},
},
},
},
},
medium: v1.StorageMediumHugePagesPrefix + "2Mi",
shouldFail: false,
expectedResult: "pagesize=2Mi",
},
}
for testCaseName, testCase := range testCases {
t.Run(testCaseName, func(t *testing.T) {
if testCase.podLevelResourcesEnabled {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodLevelResources, true)
}
value, err := getPageSizeMountOption(testCase.medium, testCase.pod)
if testCase.shouldFail && err == nil {
t.Errorf("%s: Unexpected success", testCaseName)

View File

@ -1710,6 +1710,118 @@ func TestPodLevelResourceRequests(t *testing.T) {
opts: PodResourcesOptions{SkipPodLevelResources: false},
expectedRequests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("15Mi"), v1.ResourceCPU: resource.MustParse("18m")},
},
{
name: "pod-level resources, hugepage request/limit single page size",
podResources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"),
},
Requests: v1.ResourceList{
v1.ResourceMemory: resource.MustParse("10Mi"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"),
},
},
opts: PodResourcesOptions{SkipPodLevelResources: false},
expectedRequests: v1.ResourceList{v1.ResourceMemory: resource.MustParse("10Mi"), v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi")},
},
{
name: "pod-level resources, hugepage request/limit multiple page sizes",
podResources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"),
v1.ResourceHugePagesPrefix + "1Gi": resource.MustParse("1Gi"),
},
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("1"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"),
v1.ResourceHugePagesPrefix + "1Gi": resource.MustParse("1Gi"),
},
},
opts: PodResourcesOptions{SkipPodLevelResources: false},
expectedRequests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("1"), v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("2Mi"), v1.ResourceHugePagesPrefix + "1Gi": resource.MustParse("1Gi")},
},
{
name: "pod-level resources, container-level resources, hugepage request/limit single page size",
podResources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
},
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("1"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
},
},
containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("6Mi"),
},
Requests: v1.ResourceList{
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("6Mi"),
},
},
},
},
opts: PodResourcesOptions{SkipPodLevelResources: false},
expectedRequests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("1"), v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi")},
},
{
name: "pod-level resources, container-level resources, hugepage request/limit multiple page sizes",
podResources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
v1.ResourceHugePagesPrefix + "1Gi": resource.MustParse("2Gi"),
},
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("1"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
v1.ResourceHugePagesPrefix + "1Gi": resource.MustParse("2Gi"),
},
},
containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceHugePagesPrefix + "1Gi": resource.MustParse("2Gi"),
},
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("1"),
v1.ResourceHugePagesPrefix + "1Gi": resource.MustParse("2Gi"),
},
},
},
},
opts: PodResourcesOptions{SkipPodLevelResources: false},
expectedRequests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("1"), v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"), v1.ResourceHugePagesPrefix + "1Gi": resource.MustParse("2Gi")},
},
{
name: "pod-level resources, container-level resources, hugepage request/limit multiple page sizes between pod-level and container-level",
podResources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
},
Requests: v1.ResourceList{
v1.ResourceCPU: resource.MustParse("1"),
v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"),
},
},
containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Limits: v1.ResourceList{
v1.ResourceHugePagesPrefix + "1Gi": resource.MustParse("1Gi"),
},
Requests: v1.ResourceList{
v1.ResourceMemory: resource.MustParse("4Mi"),
v1.ResourceHugePagesPrefix + "1Gi": resource.MustParse("1Gi"),
},
},
},
},
opts: PodResourcesOptions{SkipPodLevelResources: false},
expectedRequests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("1"), v1.ResourceMemory: resource.MustParse("4Mi"), v1.ResourceHugePagesPrefix + "2Mi": resource.MustParse("10Mi"), v1.ResourceHugePagesPrefix + "1Gi": resource.MustParse("1Gi")},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
@ -1721,6 +1833,47 @@ func TestPodLevelResourceRequests(t *testing.T) {
}
}
func TestIsSupportedPodLevelResource(t *testing.T) {
testCases := []struct {
name string
resource v1.ResourceName
expected bool
}{
{
name: v1.ResourceCPU.String(),
resource: v1.ResourceCPU,
expected: true,
},
{
name: v1.ResourceMemory.String(),
resource: v1.ResourceMemory,
expected: true,
},
{
name: v1.ResourceEphemeralStorage.String(),
resource: v1.ResourceEphemeralStorage,
expected: false,
},
{
name: v1.ResourceHugePagesPrefix + "2Mi",
resource: v1.ResourceHugePagesPrefix + "2Mi",
expected: true,
},
{
name: v1.ResourceHugePagesPrefix + "1Gi",
resource: v1.ResourceHugePagesPrefix + "1Gi",
expected: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if got := IsSupportedPodLevelResource(tc.resource); got != tc.expected {
t.Errorf("Supported pod level resource %s: got=%t, want=%t", tc.resource.String(), got, tc.expected)
}
})
}
}
func TestAggregateContainerRequestsAndLimits(t *testing.T) {
restartAlways := v1.ContainerRestartPolicyAlways
cases := []struct {