Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 073ac49

Browse files
author
PiotrProkop
committed
Adding getHugePagesMountOptions function and tests
1 parent 788bcaa commit 073ac49

6 files changed

Lines changed: 229 additions & 7 deletions

File tree

pkg/api/types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ type StorageMedium string
660660
const (
661661
StorageMediumDefault StorageMedium = "" // use whatever the default is for the node
662662
StorageMediumMemory StorageMedium = "Memory" // use memory (tmpfs)
663-
StorageMediumHugepages StorageMedium = "Hugepages" // use hugepages
663+
StorageMediumHugePages StorageMedium = "HugePages" // use hugepages
664664
)
665665

666666
// Protocol defines network protocols supported for things like container ports.

pkg/api/validation/validation.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,9 @@ func validateVolumeSource(source *api.VolumeSource, fldPath *field.Path, volName
394394
allErrs = append(allErrs, field.Forbidden(fldPath.Child("emptyDir").Child("sizeLimit"), "SizeLimit field must be a valid resource quantity"))
395395
}
396396
}
397+
if !utilfeature.DefaultFeatureGate.Enabled(features.HugePages) && source.EmptyDir.Medium == api.StorageMediumHugePages {
398+
allErrs = append(allErrs, field.Forbidden(fldPath.Child("emptyDir").Child("medium"), "HugePages medium is disabled by feature-gate for EmptyDir volumes"))
399+
}
397400
}
398401
if source.HostPath != nil {
399402
if numVolumes > 0 {

pkg/api/validation/validation_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2651,6 +2651,28 @@ func TestValidateVolumes(t *testing.T) {
26512651
} else if errs[0].Type != field.ErrorTypeDuplicate {
26522652
t.Errorf("expected error type %v, got %v", field.ErrorTypeDuplicate, errs[0].Type)
26532653
}
2654+
2655+
// Validate HugePages medium type for EmptyDir when HugePages feature is enabled/disabled
2656+
hugePagesCase := api.VolumeSource{EmptyDir: &api.EmptyDirVolumeSource{Medium: api.StorageMediumHugePages}}
2657+
2658+
// Enable alpha feature HugePages
2659+
err := utilfeature.DefaultFeatureGate.Set("HugePages=true")
2660+
if err != nil {
2661+
t.Errorf("Failed to enable feature gate for HugePages: %v", err)
2662+
}
2663+
if errs := validateVolumeSource(&hugePagesCase, field.NewPath("field").Index(0), "working"); len(errs) != 0 {
2664+
t.Errorf("Unexpected error when HugePages feature is enabled.")
2665+
}
2666+
2667+
// Disable alpha feature HugePages
2668+
err = utilfeature.DefaultFeatureGate.Set("HugePages=false")
2669+
if err != nil {
2670+
t.Errorf("Failed to disable feature gate for HugePages: %v", err)
2671+
}
2672+
if errs := validateVolumeSource(&hugePagesCase, field.NewPath("field").Index(0), "failing"); len(errs) == 0 {
2673+
t.Errorf("Expected error when HugePages feature is disabled got nothing.")
2674+
}
2675+
26542676
}
26552677

26562678
func TestAlphaHugePagesIsolation(t *testing.T) {

pkg/volume/empty_dir/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ go_library(
1919
"//conditions:default": [],
2020
}),
2121
deps = [
22+
"//pkg/api/v1/helper:go_default_library",
2223
"//pkg/util/mount:go_default_library",
2324
"//pkg/util/strings:go_default_library",
2425
"//pkg/volume:go_default_library",
2526
"//pkg/volume/util:go_default_library",
2627
"//vendor/github.com/golang/glog:go_default_library",
2728
"//vendor/k8s.io/api/core/v1:go_default_library",
29+
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
2830
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
2931
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
3032
] + select({
@@ -51,6 +53,7 @@ go_test(
5153
"//pkg/volume/testing:go_default_library",
5254
"//pkg/volume/util:go_default_library",
5355
"//vendor/k8s.io/api/core/v1:go_default_library",
56+
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
5457
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
5558
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
5659
"//vendor/k8s.io/client-go/util/testing:go_default_library",

pkg/volume/empty_dir/empty_dir.go

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ import (
2323

2424
"github.com/golang/glog"
2525
"k8s.io/api/core/v1"
26+
"k8s.io/apimachinery/pkg/api/resource"
2627
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2728
"k8s.io/apimachinery/pkg/types"
29+
v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
2830
"k8s.io/kubernetes/pkg/util/mount"
29-
"k8s.io/kubernetes/pkg/util/strings"
31+
stringsutil "k8s.io/kubernetes/pkg/util/strings"
3032
"k8s.io/kubernetes/pkg/volume"
3133
volumeutil "k8s.io/kubernetes/pkg/volume/util"
3234
)
@@ -56,7 +58,7 @@ const (
5658
)
5759

5860
func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
59-
return host.GetPodVolumeDir(uid, strings.EscapeQualifiedNameForDisk(emptyDirPluginName), volName)
61+
return host.GetPodVolumeDir(uid, stringsutil.EscapeQualifiedNameForDisk(emptyDirPluginName), volName)
6062
}
6163

6264
func (plugin *emptyDirPlugin) Init(host volume.VolumeHost) error {
@@ -275,14 +277,52 @@ func (ed *emptyDir) setupHugepages(dir string) error {
275277
if err != nil {
276278
return err
277279
}
278-
// If the directory is a mountpoint with medium memory, there is no
280+
// If the directory is a mountpoint with medium hugepages, there is no
279281
// work to do since we are already in the desired state.
280282
if isMnt && medium == mediumHugepages {
281283
return nil
282284
}
283285

286+
pageSizeMountOption, err := getPageSizeMountOptionFromPod(ed.pod)
287+
if err != nil {
288+
return err
289+
}
290+
284291
glog.V(3).Infof("pod %v: mounting hugepages for volume %v", ed.pod.UID, ed.volName)
285-
return ed.mounter.Mount("nodev", dir, "hugetlbfs", []string{})
292+
return ed.mounter.Mount("nodev", dir, "hugetlbfs", []string{pageSizeMountOption})
293+
}
294+
295+
// getPageSizeMountOptionFromPod retrieves pageSize mount option from Pod's resources
296+
// and validates pageSize options in all containers of given Pod.
297+
func getPageSizeMountOptionFromPod(pod *v1.Pod) (string, error) {
298+
pageSizeFound := false
299+
pageSize := resource.Quantity{}
300+
// In some rare cases init containers can also consume Huge pages.
301+
containers := append(pod.Spec.Containers, pod.Spec.InitContainers...)
302+
for _, container := range containers {
303+
// We can take request because limit and requests must match.
304+
for requestName := range container.Resources.Requests {
305+
if v1helper.IsHugePageResourceName(requestName) {
306+
currentPageSize, err := v1helper.HugePageSizeFromResourceName(requestName)
307+
if err != nil {
308+
return "", err
309+
}
310+
// PageSize for all volumes in a POD are equal, except for the first one discovered.
311+
if pageSizeFound && pageSize.Cmp(currentPageSize) != 0 {
312+
return "", fmt.Errorf("multiple pageSizes for huge pages in a single PodSpec")
313+
}
314+
pageSize = currentPageSize
315+
pageSizeFound = true
316+
}
317+
}
318+
}
319+
320+
if !pageSizeFound {
321+
return "", fmt.Errorf("hugePages storage requested, but there is no resource request for huge pages.")
322+
}
323+
324+
return fmt.Sprintf("pageSize=%s", pageSize.String()), nil
325+
286326
}
287327

288328
// setupDir creates the directory with the default permissions specified by the perm constant.
@@ -383,7 +423,7 @@ func (ed *emptyDir) teardownTmpfsOrHugetlbfs(dir string) error {
383423
}
384424

385425
func (ed *emptyDir) getMetaDir() string {
386-
return path.Join(ed.plugin.host.GetPodPluginDir(ed.pod.UID, strings.EscapeQualifiedNameForDisk(emptyDirPluginName)), ed.volName)
426+
return path.Join(ed.plugin.host.GetPodPluginDir(ed.pod.UID, stringsutil.EscapeQualifiedNameForDisk(emptyDirPluginName)), ed.volName)
387427
}
388428

389429
func getVolumeSource(spec *volume.Spec) (*v1.EmptyDirVolumeSource, bool) {

pkg/volume/empty_dir/empty_dir_test.go

Lines changed: 155 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"testing"
2525

2626
"k8s.io/api/core/v1"
27+
"k8s.io/apimachinery/pkg/api/resource"
2728
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2829
"k8s.io/apimachinery/pkg/types"
2930
utiltesting "k8s.io/client-go/util/testing"
@@ -118,7 +119,22 @@ func doTestPlugin(t *testing.T, config pluginTestConfig) {
118119

119120
physicalMounter = mount.FakeMounter{}
120121
mountDetector = fakeMountDetector{}
121-
pod = &v1.Pod{ObjectMeta: metav1.ObjectMeta{UID: types.UID("poduid")}}
122+
pod = &v1.Pod{
123+
ObjectMeta: metav1.ObjectMeta{
124+
UID: types.UID("poduid"),
125+
},
126+
Spec: v1.PodSpec{
127+
Containers: []v1.Container{
128+
{
129+
Resources: v1.ResourceRequirements{
130+
Requests: v1.ResourceList{
131+
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
132+
},
133+
},
134+
},
135+
},
136+
},
137+
}
122138
)
123139

124140
if config.idempotent {
@@ -285,3 +301,141 @@ func TestMetrics(t *testing.T) {
285301
t.Errorf("Expected Available to be greater than 0")
286302
}
287303
}
304+
305+
func TestGetHugePagesMountOptions(t *testing.T) {
306+
testCases := map[string]struct {
307+
pod *v1.Pod
308+
shouldFail bool
309+
expectedResult string
310+
}{
311+
"testWithProperValues": {
312+
pod: &v1.Pod{
313+
Spec: v1.PodSpec{
314+
Containers: []v1.Container{
315+
{
316+
Resources: v1.ResourceRequirements{
317+
Requests: v1.ResourceList{
318+
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
319+
},
320+
},
321+
},
322+
},
323+
},
324+
},
325+
shouldFail: false,
326+
expectedResult: "pageSize=2Mi",
327+
},
328+
"testWithProperValuesAndDifferentPageSize": {
329+
pod: &v1.Pod{
330+
Spec: v1.PodSpec{
331+
Containers: []v1.Container{
332+
{
333+
Resources: v1.ResourceRequirements{
334+
Requests: v1.ResourceList{
335+
v1.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
336+
},
337+
},
338+
},
339+
{
340+
Resources: v1.ResourceRequirements{
341+
Requests: v1.ResourceList{
342+
v1.ResourceName("hugepages-1Gi"): resource.MustParse("4Gi"),
343+
},
344+
},
345+
},
346+
},
347+
},
348+
},
349+
shouldFail: false,
350+
expectedResult: "pageSize=1Gi",
351+
},
352+
"InitContainerAndContainerHasProperValues": {
353+
pod: &v1.Pod{
354+
Spec: v1.PodSpec{
355+
InitContainers: []v1.Container{
356+
{
357+
Resources: v1.ResourceRequirements{
358+
Requests: v1.ResourceList{
359+
v1.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
360+
},
361+
},
362+
},
363+
{
364+
Resources: v1.ResourceRequirements{
365+
Requests: v1.ResourceList{
366+
v1.ResourceName("hugepages-1Gi"): resource.MustParse("4Gi"),
367+
},
368+
},
369+
},
370+
},
371+
},
372+
},
373+
shouldFail: false,
374+
expectedResult: "pageSize=1Gi",
375+
},
376+
"InitContainerAndContainerHasDifferentPageSizes": {
377+
pod: &v1.Pod{
378+
Spec: v1.PodSpec{
379+
InitContainers: []v1.Container{
380+
{
381+
Resources: v1.ResourceRequirements{
382+
Requests: v1.ResourceList{
383+
v1.ResourceName("hugepages-2Mi"): resource.MustParse("2Gi"),
384+
},
385+
},
386+
},
387+
{
388+
Resources: v1.ResourceRequirements{
389+
Requests: v1.ResourceList{
390+
v1.ResourceName("hugepages-1Gi"): resource.MustParse("4Gi"),
391+
},
392+
},
393+
},
394+
},
395+
},
396+
},
397+
shouldFail: true,
398+
expectedResult: "",
399+
},
400+
"ContainersWithMultiplePageSizes": {
401+
pod: &v1.Pod{
402+
Spec: v1.PodSpec{
403+
Containers: []v1.Container{
404+
{
405+
Resources: v1.ResourceRequirements{
406+
Requests: v1.ResourceList{
407+
v1.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
408+
},
409+
},
410+
},
411+
{
412+
Resources: v1.ResourceRequirements{
413+
Requests: v1.ResourceList{
414+
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
415+
},
416+
},
417+
},
418+
},
419+
},
420+
},
421+
shouldFail: true,
422+
expectedResult: "",
423+
},
424+
"PodWithNoHugePagesRequest": {
425+
pod: &v1.Pod{},
426+
shouldFail: true,
427+
expectedResult: "",
428+
},
429+
}
430+
431+
for testCaseName, testCase := range testCases {
432+
value, err := getPageSizeMountOptionFromPod(testCase.pod)
433+
if testCase.shouldFail && err == nil {
434+
t.Errorf("Expected an error in %v", testCaseName)
435+
} else if !testCase.shouldFail && err != nil {
436+
t.Errorf("Unexpected error in %v, got %v", testCaseName, err)
437+
} else if testCase.expectedResult != value {
438+
t.Errorf("Unexpected mountOptions for Pod. Expected %v, got %v", testCase.expectedResult, value)
439+
}
440+
}
441+
}

0 commit comments

Comments
 (0)