Implement tests for multiple sizes huge pages

Co-Authored-By: Odin Ugedal <odin@ugedal.com>
This commit is contained in:
Ed Bartosh 2019-10-17 16:38:37 +03:00
parent 0eb65bd7da
commit 882d6e93af
3 changed files with 409 additions and 130 deletions

View File

@ -113,6 +113,73 @@ func TestHugePageSizeFromResourceName(t *testing.T) {
}
}
func TestHugePageSizeFromMedium(t *testing.T) {
testCases := []struct {
description string
medium v1.StorageMedium
expectVal resource.Quantity
expectErr bool
}{
{
description: "Invalid hugepages medium",
medium: "Memory",
expectVal: resource.Quantity{},
expectErr: true,
},
{
description: "Invalid hugepages medium",
medium: "Memory",
expectVal: resource.Quantity{},
expectErr: true,
},
{
description: "Invalid: HugePages without size",
medium: "HugePages",
expectVal: resource.Quantity{},
expectErr: true,
},
{
description: "Invalid: HugePages without size",
medium: "HugePages",
expectVal: resource.Quantity{},
expectErr: true,
},
{
description: "Valid: HugePages-1Gi",
medium: "HugePages-1Gi",
expectVal: resource.MustParse("1Gi"),
expectErr: false,
},
{
description: "Valid: HugePages-2Mi",
medium: "HugePages-2Mi",
expectVal: resource.MustParse("2Mi"),
expectErr: false,
},
{
description: "Valid: HugePages-64Ki",
medium: "HugePages-64Ki",
expectVal: resource.MustParse("64Ki"),
expectErr: false,
},
}
for i, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
t.Parallel()
v, err := HugePageSizeFromMedium(tc.medium)
if err == nil && tc.expectErr {
t.Errorf("[%v]expected error but got none.", i)
}
if err != nil && !tc.expectErr {
t.Errorf("[%v]did not expect error but got: %v", i, err)
}
if v != tc.expectVal {
t.Errorf("Got %v but expected %v", v, tc.expectVal)
}
})
}
}
func TestIsOvercommitAllowed(t *testing.T) {
testCases := []struct {
resourceName v1.ResourceName

View File

@ -24,7 +24,7 @@ import (
"strings"
"testing"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
@ -3908,114 +3908,186 @@ func TestValidateVolumes(t *testing.T) {
}
func TestHugePagesIsolation(t *testing.T) {
successCases := []core.Pod{
{ // Basic fields.
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
Spec: core.PodSpec{
Containers: []core.Container{
{
Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
},
Limits: core.ResourceList{
core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
testCases := map[string]struct {
pod *core.Pod
enableHugePageStorageMediumSize bool
expectError bool
}{
"Valid: request hugepages-2Mi": {
pod: &core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
Spec: core.PodSpec{
Containers: []core.Container{
{
Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
},
Limits: core.ResourceList{
core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
},
},
},
},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
},
"Valid: HugePageStorageMediumSize enabled: request more than one hugepages size": {
pod: &core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "hugepages-shared", Namespace: "ns"},
Spec: core.PodSpec{
Containers: []core.Container{
{
Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
},
Limits: core.ResourceList{
core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
},
},
},
},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
},
enableHugePageStorageMediumSize: true,
expectError: false,
},
"Invalid: HugePageStorageMediumSize disabled: request hugepages-1Gi, limit hugepages-2Mi and hugepages-1Gi": {
pod: &core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "hugepages-multiple", Namespace: "ns"},
Spec: core.PodSpec{
Containers: []core.Container{
{
Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
},
Limits: core.ResourceList{
core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
},
},
},
},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
},
expectError: true,
},
"Invalid: not requesting cpu and memory": {
pod: &core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "hugepages-requireCpuOrMemory", Namespace: "ns"},
Spec: core.PodSpec{
Containers: []core.Container{
{
Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
},
Limits: core.ResourceList{
core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
},
},
},
},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
},
expectError: true,
},
"Invalid: request 1Gi hugepages-2Mi but limit 2Gi": {
pod: &core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "hugepages-shared", Namespace: "ns"},
Spec: core.PodSpec{
Containers: []core.Container{
{
Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
},
Limits: core.ResourceList{
core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
core.ResourceName("hugepages-2Mi"): resource.MustParse("2Gi"),
},
},
},
},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
},
expectError: true,
},
"Invalid: HugePageStorageMediumSize disabled: request more than one hugepages size": {
pod: &core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "hugepages-shared", Namespace: "ns"},
Spec: core.PodSpec{
Containers: []core.Container{
{
Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
core.ResourceName("hugepages-1Gi"): resource.MustParse("1Gi"),
},
Limits: core.ResourceList{
core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
core.ResourceName("hugepages-1Gi"): resource.MustParse("1Gi"),
},
},
},
},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
},
expectError: true,
},
}
failureCases := []core.Pod{
{ // Basic fields.
ObjectMeta: metav1.ObjectMeta{Name: "hugepages-requireCpuOrMemory", Namespace: "ns"},
Spec: core.PodSpec{
Containers: []core.Container{
{
Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
},
Limits: core.ResourceList{
core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
},
},
},
},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
},
{ // Basic fields.
ObjectMeta: metav1.ObjectMeta{Name: "hugepages-shared", Namespace: "ns"},
Spec: core.PodSpec{
Containers: []core.Container{
{
Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
},
Limits: core.ResourceList{
core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
core.ResourceName("hugepages-2Mi"): resource.MustParse("2Gi"),
},
},
},
},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
},
{ // Basic fields.
ObjectMeta: metav1.ObjectMeta{Name: "hugepages-multiple", Namespace: "ns"},
Spec: core.PodSpec{
Containers: []core.Container{
{
Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
},
Limits: core.ResourceList{
core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
},
},
},
},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
},
}
for i := range successCases {
pod := &successCases[i]
if errs := ValidatePod(pod); len(errs) != 0 {
t.Errorf("Unexpected error for case[%d], err: %v", i, errs)
}
}
for i := range failureCases {
pod := &failureCases[i]
if errs := ValidatePod(pod); len(errs) == 0 {
t.Errorf("Expected error for case[%d], pod: %v", i, pod.Name)
}
for tcName, tc := range testCases {
t.Run(tcName, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HugePageStorageMediumSize, tc.enableHugePageStorageMediumSize)()
errs := ValidatePod(tc.pod, PodValidationOptions{tc.enableHugePageStorageMediumSize})
if tc.expectError && len(errs) == 0 {
t.Errorf("Unexpected success")
}
if !tc.expectError && len(errs) != 0 {
t.Errorf("Unexpected error(s): %v", errs)
}
})
}
}
@ -7375,7 +7447,7 @@ func TestValidatePod(t *testing.T) {
},
}
for _, pod := range successCases {
if errs := ValidatePod(&pod); len(errs) != 0 {
if errs := ValidatePod(&pod, PodValidationOptions{}); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
}
@ -8225,7 +8297,7 @@ func TestValidatePod(t *testing.T) {
},
}
for k, v := range errorCases {
if errs := ValidatePod(&v.spec); len(errs) == 0 {
if errs := ValidatePod(&v.spec, PodValidationOptions{}); len(errs) == 0 {
t.Errorf("expected failure for %q", k)
} else if v.expectedError == "" {
t.Errorf("missing expectedError for %q, got %q", k, errs.ToAggregate().Error())
@ -8965,7 +9037,7 @@ func TestValidatePodUpdate(t *testing.T) {
for _, test := range tests {
test.new.ObjectMeta.ResourceVersion = "1"
test.old.ObjectMeta.ResourceVersion = "1"
errs := ValidatePodUpdate(&test.new, &test.old)
errs := ValidatePodUpdate(&test.new, &test.old, PodValidationOptions{})
if test.err == "" {
if len(errs) != 0 {
t.Errorf("unexpected invalid: %s (%+v)\nA: %+v\nB: %+v", test.test, errs, test.new, test.old)
@ -14876,7 +14948,7 @@ func TestPodIPsValidation(t *testing.T) {
}
for _, testCase := range testCases {
errs := ValidatePod(&testCase.pod)
errs := ValidatePod(&testCase.pod, PodValidationOptions{})
if len(errs) == 0 && testCase.expectError {
t.Errorf("expected failure for %s, but there were none", testCase.pod.Name)
return

View File

@ -23,16 +23,18 @@ import (
"path/filepath"
"testing"
"k8s.io/utils/mount"
v1 "k8s.io/api/core/v1"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utiltesting "k8s.io/client-go/util/testing"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/volume"
volumetest "k8s.io/kubernetes/pkg/volume/testing"
"k8s.io/kubernetes/pkg/volume/util"
"k8s.io/utils/mount"
)
// Construct an instance of a plugin, by name.
@ -83,12 +85,33 @@ func TestPluginEmptyRootContext(t *testing.T) {
}
func TestPluginHugetlbfs(t *testing.T) {
doTestPlugin(t, pluginTestConfig{
medium: v1.StorageMediumHugePages,
expectedSetupMounts: 1,
expectedTeardownMounts: 0,
shouldBeMountedBeforeTeardown: true,
})
testCases := map[string]struct {
medium v1.StorageMedium
enableHugePageStorageMediumSize bool
}{
"HugePageStorageMediumSize enabled: medium without size": {
medium: "HugePages",
enableHugePageStorageMediumSize: true,
},
"HugePageStorageMediumSize disabled: medium without size": {
medium: "HugePages",
},
"HugePageStorageMediumSize enabled: medium with size": {
medium: "HugePages-2Mi",
enableHugePageStorageMediumSize: true,
},
}
for tcName, tc := range testCases {
t.Run(tcName, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HugePageStorageMediumSize, tc.enableHugePageStorageMediumSize)()
doTestPlugin(t, pluginTestConfig{
medium: tc.medium,
expectedSetupMounts: 1,
expectedTeardownMounts: 0,
shouldBeMountedBeforeTeardown: true,
})
})
}
}
type pluginTestConfig struct {
@ -307,11 +330,13 @@ func TestMetrics(t *testing.T) {
func TestGetHugePagesMountOptions(t *testing.T) {
testCases := map[string]struct {
pod *v1.Pod
shouldFail bool
expectedResult string
pod *v1.Pod
medium v1.StorageMedium
shouldFail bool
expectedResult string
enableHugePageStorageMediumSize bool
}{
"testWithProperValues": {
"ProperValues": {
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{
@ -325,10 +350,11 @@ func TestGetHugePagesMountOptions(t *testing.T) {
},
},
},
medium: v1.StorageMediumHugePages,
shouldFail: false,
expectedResult: "pagesize=2Mi",
},
"testWithProperValuesAndDifferentPageSize": {
"ProperValuesAndDifferentPageSize": {
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{
@ -349,6 +375,7 @@ func TestGetHugePagesMountOptions(t *testing.T) {
},
},
},
medium: v1.StorageMediumHugePages,
shouldFail: false,
expectedResult: "pagesize=1Gi",
},
@ -373,6 +400,7 @@ func TestGetHugePagesMountOptions(t *testing.T) {
},
},
},
medium: v1.StorageMediumHugePages,
shouldFail: false,
expectedResult: "pagesize=1Gi",
},
@ -397,6 +425,7 @@ func TestGetHugePagesMountOptions(t *testing.T) {
},
},
},
medium: v1.StorageMediumHugePages,
shouldFail: true,
expectedResult: "",
},
@ -421,24 +450,135 @@ func TestGetHugePagesMountOptions(t *testing.T) {
},
},
},
shouldFail: true,
expectedResult: "",
medium: v1.StorageMediumHugePages,
shouldFail: true,
expectedResult: "",
enableHugePageStorageMediumSize: true,
},
"PodWithNoHugePagesRequest": {
pod: &v1.Pod{},
medium: v1.StorageMediumHugePages,
shouldFail: true,
expectedResult: "",
},
"ProperValuesMultipleSizes": {
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
v1.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
},
},
},
},
},
},
medium: v1.StorageMediumHugePagesPrefix + "1Gi",
shouldFail: false,
expectedResult: "pagesize=1Gi",
enableHugePageStorageMediumSize: true,
},
"InitContainerAndContainerHasProperValuesMultipleSizes": {
pod: &v1.Pod{
Spec: v1.PodSpec{
InitContainers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
},
},
},
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-1Gi"): resource.MustParse("4Gi"),
v1.ResourceName("hugepages-2Mi"): resource.MustParse("50Mi"),
},
},
},
},
},
},
medium: v1.StorageMediumHugePagesPrefix + "2Mi",
shouldFail: false,
expectedResult: "pagesize=2Mi",
enableHugePageStorageMediumSize: true,
},
"MediumWithoutSizeMultipleSizes": {
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
v1.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
},
},
},
},
},
},
medium: v1.StorageMediumHugePagesPrefix,
shouldFail: true,
expectedResult: "",
},
"IncorrectMediumFormatMultipleSizes": {
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
v1.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
},
},
},
},
},
},
medium: "foo",
shouldFail: true,
expectedResult: "",
},
"MediumSizeDoesntMatchResourcesMultipleSizes": {
pod: &v1.Pod{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
v1.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
},
},
},
},
},
},
medium: v1.StorageMediumHugePagesPrefix + "1Mi",
shouldFail: true,
expectedResult: "",
},
}
for testCaseName, testCase := range testCases {
value, err := getPageSizeMountOptionFromPod(testCase.pod)
if testCase.shouldFail && err == nil {
t.Errorf("Expected an error in %v", testCaseName)
} else if !testCase.shouldFail && err != nil {
t.Errorf("Unexpected error in %v, got %v", testCaseName, err)
} else if testCase.expectedResult != value {
t.Errorf("Unexpected mountOptions for Pod. Expected %v, got %v", testCase.expectedResult, value)
}
t.Run(testCaseName, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HugePageStorageMediumSize, testCase.enableHugePageStorageMediumSize)()
value, err := getPageSizeMountOption(testCase.medium, testCase.pod)
if testCase.shouldFail && err == nil {
t.Errorf("%s: Unexpected success", testCaseName)
} else if !testCase.shouldFail && err != nil {
t.Errorf("%s: Unexpected error: %v", testCaseName, err)
} else if testCase.expectedResult != value {
t.Errorf("%s: Unexpected mountOptions for Pod. Expected %v, got %v", testCaseName, testCase.expectedResult, value)
}
})
}
}