mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Merge pull request #84051 from bart0sh/PR0079-multiple-sizes-hugepages
Implement support for multiple sizes huge pages
This commit is contained in:
		@@ -583,9 +583,10 @@ type StorageMedium string
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// These are the valid value for StorageMedium
 | 
					// These are the valid value for StorageMedium
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	StorageMediumDefault   StorageMedium = ""          // use whatever the default is for the node
 | 
						StorageMediumDefault         StorageMedium = ""           // use whatever the default is for the node
 | 
				
			||||||
	StorageMediumMemory    StorageMedium = "Memory"    // use memory (tmpfs)
 | 
						StorageMediumMemory          StorageMedium = "Memory"     // use memory (tmpfs)
 | 
				
			||||||
	StorageMediumHugePages StorageMedium = "HugePages" // use hugepages
 | 
						StorageMediumHugePages       StorageMedium = "HugePages"  // use hugepages
 | 
				
			||||||
 | 
						StorageMediumHugePagesPrefix StorageMedium = "HugePages-" // prefix for full medium notation HugePages-<size>
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Protocol defines network protocols supported for things like container ports.
 | 
					// Protocol defines network protocols supported for things like container ports.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -103,6 +103,27 @@ func HugePageUnitSizeFromByteSize(size int64) (string, error) {
 | 
				
			|||||||
	return fmt.Sprintf("%d%s", size, hugePageSizeUnitList[idx]), nil
 | 
						return fmt.Sprintf("%d%s", size, hugePageSizeUnitList[idx]), nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsHugePageMedium returns true if the volume medium is in 'HugePages[-size]' format
 | 
				
			||||||
 | 
					func IsHugePageMedium(medium v1.StorageMedium) bool {
 | 
				
			||||||
 | 
						if medium == v1.StorageMediumHugePages {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return strings.HasPrefix(string(medium), string(v1.StorageMediumHugePagesPrefix))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// HugePageSizeFromMedium returns the page size for the specified huge page medium.
 | 
				
			||||||
 | 
					// If the specified input is not a valid huge page medium an error is returned.
 | 
				
			||||||
 | 
					func HugePageSizeFromMedium(medium v1.StorageMedium) (resource.Quantity, error) {
 | 
				
			||||||
 | 
						if !IsHugePageMedium(medium) {
 | 
				
			||||||
 | 
							return resource.Quantity{}, fmt.Errorf("medium: %s is not a hugepage medium", medium)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if medium == v1.StorageMediumHugePages {
 | 
				
			||||||
 | 
							return resource.Quantity{}, fmt.Errorf("medium: %s doesn't have size information", medium)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pageSize := strings.TrimPrefix(string(medium), string(v1.StorageMediumHugePagesPrefix))
 | 
				
			||||||
 | 
						return resource.ParseQuantity(pageSize)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsOvercommitAllowed returns true if the resource is in the default
 | 
					// IsOvercommitAllowed returns true if the resource is in the default
 | 
				
			||||||
// namespace and is not hugepages.
 | 
					// namespace and is not hugepages.
 | 
				
			||||||
func IsOvercommitAllowed(name v1.ResourceName) bool {
 | 
					func IsOvercommitAllowed(name v1.ResourceName) bool {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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) {
 | 
					func TestIsOvercommitAllowed(t *testing.T) {
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		resourceName v1.ResourceName
 | 
							resourceName v1.ResourceName
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3083,8 +3083,33 @@ func validateContainersOnlyForPod(containers []core.Container, fldPath *field.Pa
 | 
				
			|||||||
	return allErrs
 | 
						return allErrs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PodValidationOptions contains the different settings for pod validation
 | 
				
			||||||
 | 
					type PodValidationOptions struct {
 | 
				
			||||||
 | 
						// Allow pod spec to have more than one huge page resource (with different sizes)
 | 
				
			||||||
 | 
						AllowMultipleHugePageResources bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ValidatePodSingleHugePageResources checks if there are multiple huge
 | 
				
			||||||
 | 
					// pages resources in the pod object.
 | 
				
			||||||
 | 
					func ValidatePodSingleHugePageResources(pod *core.Pod, specPath *field.Path) field.ErrorList {
 | 
				
			||||||
 | 
						allErrs := field.ErrorList{}
 | 
				
			||||||
 | 
						hugePageResources := sets.NewString()
 | 
				
			||||||
 | 
						for i := range pod.Spec.Containers {
 | 
				
			||||||
 | 
							resourceSet := toContainerResourcesSet(&pod.Spec.Containers[i])
 | 
				
			||||||
 | 
							for resourceStr := range resourceSet {
 | 
				
			||||||
 | 
								if v1helper.IsHugePageResourceName(v1.ResourceName(resourceStr)) {
 | 
				
			||||||
 | 
									hugePageResources.Insert(resourceStr)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(hugePageResources) > 1 {
 | 
				
			||||||
 | 
							allErrs = append(allErrs, field.Invalid(specPath, hugePageResources.List(), "must use a single hugepage size in a pod spec"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return allErrs
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ValidatePod tests if required fields in the pod are set.
 | 
					// ValidatePod tests if required fields in the pod are set.
 | 
				
			||||||
func ValidatePod(pod *core.Pod) field.ErrorList {
 | 
					func ValidatePod(pod *core.Pod, opts PodValidationOptions) field.ErrorList {
 | 
				
			||||||
	fldPath := field.NewPath("metadata")
 | 
						fldPath := field.NewPath("metadata")
 | 
				
			||||||
	allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, fldPath)
 | 
						allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, fldPath)
 | 
				
			||||||
	allErrs = append(allErrs, ValidatePodSpecificAnnotations(pod.ObjectMeta.Annotations, &pod.Spec, fldPath.Child("annotations"))...)
 | 
						allErrs = append(allErrs, ValidatePodSpecificAnnotations(pod.ObjectMeta.Annotations, &pod.Spec, fldPath.Child("annotations"))...)
 | 
				
			||||||
@@ -3111,17 +3136,8 @@ func ValidatePod(pod *core.Pod) field.ErrorList {
 | 
				
			|||||||
	allErrs = append(allErrs, validateContainersOnlyForPod(pod.Spec.Containers, specPath.Child("containers"))...)
 | 
						allErrs = append(allErrs, validateContainersOnlyForPod(pod.Spec.Containers, specPath.Child("containers"))...)
 | 
				
			||||||
	allErrs = append(allErrs, validateContainersOnlyForPod(pod.Spec.InitContainers, specPath.Child("initContainers"))...)
 | 
						allErrs = append(allErrs, validateContainersOnlyForPod(pod.Spec.InitContainers, specPath.Child("initContainers"))...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hugePageResources := sets.NewString()
 | 
						if !opts.AllowMultipleHugePageResources {
 | 
				
			||||||
	for i := range pod.Spec.Containers {
 | 
							allErrs = append(allErrs, ValidatePodSingleHugePageResources(pod, specPath)...)
 | 
				
			||||||
		resourceSet := toContainerResourcesSet(&pod.Spec.Containers[i])
 | 
					 | 
				
			||||||
		for resourceStr := range resourceSet {
 | 
					 | 
				
			||||||
			if v1helper.IsHugePageResourceName(v1.ResourceName(resourceStr)) {
 | 
					 | 
				
			||||||
				hugePageResources.Insert(resourceStr)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(hugePageResources) > 1 {
 | 
					 | 
				
			||||||
		allErrs = append(allErrs, field.Invalid(specPath, hugePageResources, "must use a single hugepage size in a pod spec"))
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	podIPsField := field.NewPath("status", "podIPs")
 | 
						podIPsField := field.NewPath("status", "podIPs")
 | 
				
			||||||
@@ -3679,8 +3695,8 @@ func ValidateContainerUpdates(newContainers, oldContainers []core.Container, fld
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ValidatePodCreate validates a pod in the context of its initial create
 | 
					// ValidatePodCreate validates a pod in the context of its initial create
 | 
				
			||||||
func ValidatePodCreate(pod *core.Pod) field.ErrorList {
 | 
					func ValidatePodCreate(pod *core.Pod, opts PodValidationOptions) field.ErrorList {
 | 
				
			||||||
	allErrs := ValidatePod(pod)
 | 
						allErrs := ValidatePod(pod, opts)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fldPath := field.NewPath("spec")
 | 
						fldPath := field.NewPath("spec")
 | 
				
			||||||
	// EphemeralContainers can only be set on update using the ephemeralcontainers subresource
 | 
						// EphemeralContainers can only be set on update using the ephemeralcontainers subresource
 | 
				
			||||||
@@ -3693,12 +3709,16 @@ func ValidatePodCreate(pod *core.Pod) field.ErrorList {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// ValidatePodUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
 | 
					// ValidatePodUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
 | 
				
			||||||
// that cannot be changed.
 | 
					// that cannot be changed.
 | 
				
			||||||
func ValidatePodUpdate(newPod, oldPod *core.Pod) field.ErrorList {
 | 
					func ValidatePodUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions) field.ErrorList {
 | 
				
			||||||
	fldPath := field.NewPath("metadata")
 | 
						fldPath := field.NewPath("metadata")
 | 
				
			||||||
	allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath)
 | 
						allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath)
 | 
				
			||||||
	allErrs = append(allErrs, ValidatePodSpecificAnnotationUpdates(newPod, oldPod, fldPath.Child("annotations"))...)
 | 
						allErrs = append(allErrs, ValidatePodSpecificAnnotationUpdates(newPod, oldPod, fldPath.Child("annotations"))...)
 | 
				
			||||||
	specPath := field.NewPath("spec")
 | 
						specPath := field.NewPath("spec")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !opts.AllowMultipleHugePageResources {
 | 
				
			||||||
 | 
							allErrs = append(allErrs, ValidatePodSingleHugePageResources(newPod, specPath)...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// validate updateable fields:
 | 
						// validate updateable fields:
 | 
				
			||||||
	// 1.  spec.containers[*].image
 | 
						// 1.  spec.containers[*].image
 | 
				
			||||||
	// 2.  spec.initContainers[*].image
 | 
						// 2.  spec.initContainers[*].image
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,7 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/api/core/v1"
 | 
						v1 "k8s.io/api/core/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/api/resource"
 | 
						"k8s.io/apimachinery/pkg/api/resource"
 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/util/intstr"
 | 
						"k8s.io/apimachinery/pkg/util/intstr"
 | 
				
			||||||
@@ -3908,114 +3908,186 @@ func TestValidateVolumes(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestHugePagesIsolation(t *testing.T) {
 | 
					func TestHugePagesIsolation(t *testing.T) {
 | 
				
			||||||
	successCases := []core.Pod{
 | 
						testCases := map[string]struct {
 | 
				
			||||||
		{ // Basic fields.
 | 
							pod                             *core.Pod
 | 
				
			||||||
			ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
 | 
							enableHugePageStorageMediumSize bool
 | 
				
			||||||
			Spec: core.PodSpec{
 | 
							expectError                     bool
 | 
				
			||||||
				Containers: []core.Container{
 | 
						}{
 | 
				
			||||||
					{
 | 
							"Valid: request hugepages-2Mi": {
 | 
				
			||||||
						Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
 | 
								pod: &core.Pod{
 | 
				
			||||||
						Resources: core.ResourceRequirements{
 | 
									ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
 | 
				
			||||||
							Requests: core.ResourceList{
 | 
									Spec: core.PodSpec{
 | 
				
			||||||
								core.ResourceName(core.ResourceCPU):    resource.MustParse("10"),
 | 
										Containers: []core.Container{
 | 
				
			||||||
								core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
 | 
											{
 | 
				
			||||||
								core.ResourceName("hugepages-2Mi"):     resource.MustParse("1Gi"),
 | 
												Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
 | 
				
			||||||
							},
 | 
												Resources: core.ResourceRequirements{
 | 
				
			||||||
							Limits: core.ResourceList{
 | 
													Requests: core.ResourceList{
 | 
				
			||||||
								core.ResourceName(core.ResourceCPU):    resource.MustParse("10"),
 | 
														core.ResourceName(core.ResourceCPU):    resource.MustParse("10"),
 | 
				
			||||||
								core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
 | 
														core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
 | 
				
			||||||
								core.ResourceName("hugepages-2Mi"):     resource.MustParse("1Gi"),
 | 
														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{
 | 
						for tcName, tc := range testCases {
 | 
				
			||||||
		{ // Basic fields.
 | 
							t.Run(tcName, func(t *testing.T) {
 | 
				
			||||||
			ObjectMeta: metav1.ObjectMeta{Name: "hugepages-requireCpuOrMemory", Namespace: "ns"},
 | 
								defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HugePageStorageMediumSize, tc.enableHugePageStorageMediumSize)()
 | 
				
			||||||
			Spec: core.PodSpec{
 | 
								errs := ValidatePod(tc.pod, PodValidationOptions{tc.enableHugePageStorageMediumSize})
 | 
				
			||||||
				Containers: []core.Container{
 | 
								if tc.expectError && len(errs) == 0 {
 | 
				
			||||||
					{
 | 
									t.Errorf("Unexpected success")
 | 
				
			||||||
						Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
 | 
								}
 | 
				
			||||||
						Resources: core.ResourceRequirements{
 | 
								if !tc.expectError && len(errs) != 0 {
 | 
				
			||||||
							Requests: core.ResourceList{
 | 
									t.Errorf("Unexpected error(s): %v", errs)
 | 
				
			||||||
								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)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -7375,7 +7447,7 @@ func TestValidatePod(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, pod := range successCases {
 | 
						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)
 | 
								t.Errorf("expected success: %v", errs)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -8225,7 +8297,7 @@ func TestValidatePod(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for k, v := range errorCases {
 | 
						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)
 | 
								t.Errorf("expected failure for %q", k)
 | 
				
			||||||
		} else if v.expectedError == "" {
 | 
							} else if v.expectedError == "" {
 | 
				
			||||||
			t.Errorf("missing expectedError for %q, got %q", k, errs.ToAggregate().Error())
 | 
								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 {
 | 
						for _, test := range tests {
 | 
				
			||||||
		test.new.ObjectMeta.ResourceVersion = "1"
 | 
							test.new.ObjectMeta.ResourceVersion = "1"
 | 
				
			||||||
		test.old.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 test.err == "" {
 | 
				
			||||||
			if len(errs) != 0 {
 | 
								if len(errs) != 0 {
 | 
				
			||||||
				t.Errorf("unexpected invalid: %s (%+v)\nA: %+v\nB: %+v", test.test, errs, test.new, test.old)
 | 
									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 {
 | 
						for _, testCase := range testCases {
 | 
				
			||||||
		errs := ValidatePod(&testCase.pod)
 | 
							errs := ValidatePod(&testCase.pod, PodValidationOptions{})
 | 
				
			||||||
		if len(errs) == 0 && testCase.expectError {
 | 
							if len(errs) == 0 && testCase.expectError {
 | 
				
			||||||
			t.Errorf("expected failure for %s, but there were none", testCase.pod.Name)
 | 
								t.Errorf("expected failure for %s, but there were none", testCase.pod.Name)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -540,6 +540,14 @@ const (
 | 
				
			|||||||
	//
 | 
						//
 | 
				
			||||||
	// Enables a feature to make secrets and configmaps data immutable.
 | 
						// Enables a feature to make secrets and configmaps data immutable.
 | 
				
			||||||
	ImmutableEphemeralVolumes featuregate.Feature = "ImmutableEphemeralVolumes"
 | 
						ImmutableEphemeralVolumes featuregate.Feature = "ImmutableEphemeralVolumes"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// owner: @bart0sh
 | 
				
			||||||
 | 
						// alpha: v1.18
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// Enables usage of HugePages-<size> in a volume medium,
 | 
				
			||||||
 | 
						// e.g. emptyDir:
 | 
				
			||||||
 | 
						//        medium: HugePages-1Gi
 | 
				
			||||||
 | 
						HugePageStorageMediumSize featuregate.Feature = "HugePageStorageMediumSize"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
@@ -625,6 +633,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
 | 
				
			|||||||
	PodDisruptionBudget:                            {Default: true, PreRelease: featuregate.Beta},
 | 
						PodDisruptionBudget:                            {Default: true, PreRelease: featuregate.Beta},
 | 
				
			||||||
	ServiceTopology:                                {Default: false, PreRelease: featuregate.Alpha},
 | 
						ServiceTopology:                                {Default: false, PreRelease: featuregate.Alpha},
 | 
				
			||||||
	ImmutableEphemeralVolumes:                      {Default: false, PreRelease: featuregate.Alpha},
 | 
						ImmutableEphemeralVolumes:                      {Default: false, PreRelease: featuregate.Alpha},
 | 
				
			||||||
 | 
						HugePageStorageMediumSize:                      {Default: false, PreRelease: featuregate.Alpha},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// inherited features from generic apiserver, relisted here to get a conflict if it is changed
 | 
						// inherited features from generic apiserver, relisted here to get a conflict if it is changed
 | 
				
			||||||
	// unintentionally on either side:
 | 
						// unintentionally on either side:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,7 @@ go_library(
 | 
				
			|||||||
        "//pkg/apis/core/install:go_default_library",
 | 
					        "//pkg/apis/core/install:go_default_library",
 | 
				
			||||||
        "//pkg/apis/core/v1:go_default_library",
 | 
					        "//pkg/apis/core/v1:go_default_library",
 | 
				
			||||||
        "//pkg/apis/core/validation:go_default_library",
 | 
					        "//pkg/apis/core/validation:go_default_library",
 | 
				
			||||||
 | 
					        "//pkg/features:go_default_library",
 | 
				
			||||||
        "//pkg/kubelet/checkpoint:go_default_library",
 | 
					        "//pkg/kubelet/checkpoint:go_default_library",
 | 
				
			||||||
        "//pkg/kubelet/checkpointmanager:go_default_library",
 | 
					        "//pkg/kubelet/checkpointmanager:go_default_library",
 | 
				
			||||||
        "//pkg/kubelet/container:go_default_library",
 | 
					        "//pkg/kubelet/container:go_default_library",
 | 
				
			||||||
@@ -40,6 +41,7 @@ go_library(
 | 
				
			|||||||
        "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
 | 
					        "//staging/src/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
 | 
				
			||||||
 | 
					        "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/client-go/kubernetes:go_default_library",
 | 
					        "//staging/src/k8s.io/client-go/kubernetes:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/client-go/tools/cache:go_default_library",
 | 
					        "//staging/src/k8s.io/client-go/tools/cache:go_default_library",
 | 
				
			||||||
        "//staging/src/k8s.io/client-go/tools/record:go_default_library",
 | 
					        "//staging/src/k8s.io/client-go/tools/record:go_default_library",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,8 +27,10 @@ import (
 | 
				
			|||||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
						"k8s.io/apimachinery/pkg/runtime"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/types"
 | 
						"k8s.io/apimachinery/pkg/types"
 | 
				
			||||||
	utilyaml "k8s.io/apimachinery/pkg/util/yaml"
 | 
						utilyaml "k8s.io/apimachinery/pkg/util/yaml"
 | 
				
			||||||
 | 
						utilfeature "k8s.io/apiserver/pkg/util/feature"
 | 
				
			||||||
	api "k8s.io/kubernetes/pkg/apis/core"
 | 
						api "k8s.io/kubernetes/pkg/apis/core"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/apis/core/helper"
 | 
						"k8s.io/kubernetes/pkg/apis/core/helper"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/features"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: remove this import if
 | 
						// TODO: remove this import if
 | 
				
			||||||
	// api.Registry.GroupOrDie(v1.GroupName).GroupVersion.String() is changed
 | 
						// api.Registry.GroupOrDie(v1.GroupName).GroupVersion.String() is changed
 | 
				
			||||||
@@ -133,7 +135,10 @@ func tryDecodeSinglePod(data []byte, defaultFn defaultFunc) (parsed bool, pod *v
 | 
				
			|||||||
	if err = defaultFn(newPod); err != nil {
 | 
						if err = defaultFn(newPod); err != nil {
 | 
				
			||||||
		return true, pod, err
 | 
							return true, pod, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if errs := validation.ValidatePod(newPod); len(errs) > 0 {
 | 
						opts := validation.PodValidationOptions{
 | 
				
			||||||
 | 
							AllowMultipleHugePageResources: utilfeature.DefaultFeatureGate.Enabled(features.HugePageStorageMediumSize),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if errs := validation.ValidatePod(newPod, opts); len(errs) > 0 {
 | 
				
			||||||
		return true, pod, fmt.Errorf("invalid pod: %v", errs)
 | 
							return true, pod, fmt.Errorf("invalid pod: %v", errs)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	v1Pod := &v1.Pod{}
 | 
						v1Pod := &v1.Pod{}
 | 
				
			||||||
@@ -157,13 +162,17 @@ func tryDecodePodList(data []byte, defaultFn defaultFunc) (parsed bool, pods v1.
 | 
				
			|||||||
		return false, pods, err
 | 
							return false, pods, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						opts := validation.PodValidationOptions{
 | 
				
			||||||
 | 
							AllowMultipleHugePageResources: utilfeature.DefaultFeatureGate.Enabled(features.HugePageStorageMediumSize),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Apply default values and validate pods.
 | 
						// Apply default values and validate pods.
 | 
				
			||||||
	for i := range newPods.Items {
 | 
						for i := range newPods.Items {
 | 
				
			||||||
		newPod := &newPods.Items[i]
 | 
							newPod := &newPods.Items[i]
 | 
				
			||||||
		if err = defaultFn(newPod); err != nil {
 | 
							if err = defaultFn(newPod); err != nil {
 | 
				
			||||||
			return true, pods, err
 | 
								return true, pods, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if errs := validation.ValidatePod(newPod); len(errs) > 0 {
 | 
							if errs := validation.ValidatePod(newPod, opts); len(errs) > 0 {
 | 
				
			||||||
			err = fmt.Errorf("invalid pod: %v", errs)
 | 
								err = fmt.Errorf("invalid pod: %v", errs)
 | 
				
			||||||
			return true, pods, err
 | 
								return true, pods, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -251,7 +251,7 @@ func TestStaticPodNameGenerate(t *testing.T) {
 | 
				
			|||||||
		if c.overwrite != "" {
 | 
							if c.overwrite != "" {
 | 
				
			||||||
			pod.Name = c.overwrite
 | 
								pod.Name = c.overwrite
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		errs := validation.ValidatePod(pod)
 | 
							errs := validation.ValidatePod(pod, validation.PodValidationOptions{})
 | 
				
			||||||
		if c.shouldErr {
 | 
							if c.shouldErr {
 | 
				
			||||||
			specNameErrored := false
 | 
								specNameErrored := false
 | 
				
			||||||
			for _, err := range errs {
 | 
								for _, err := range errs {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -90,7 +90,7 @@ func TestReadPodsFromFileExistAlready(t *testing.T) {
 | 
				
			|||||||
					if err := k8s_api_v1.Convert_v1_Pod_To_core_Pod(pod, internalPod, nil); err != nil {
 | 
										if err := k8s_api_v1.Convert_v1_Pod_To_core_Pod(pod, internalPod, nil); err != nil {
 | 
				
			||||||
						t.Fatalf("%s: Cannot convert pod %#v, %#v", testCase.desc, pod, err)
 | 
											t.Fatalf("%s: Cannot convert pod %#v, %#v", testCase.desc, pod, err)
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					if errs := validation.ValidatePod(internalPod); len(errs) > 0 {
 | 
										if errs := validation.ValidatePod(internalPod, validation.PodValidationOptions{}); len(errs) > 0 {
 | 
				
			||||||
						t.Fatalf("%s: Invalid pod %#v, %#v", testCase.desc, internalPod, errs)
 | 
											t.Fatalf("%s: Invalid pod %#v, %#v", testCase.desc, internalPod, errs)
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -369,7 +369,7 @@ func expectUpdate(t *testing.T, ch chan interface{}, testCase *testCase) {
 | 
				
			|||||||
				if err := k8s_api_v1.Convert_v1_Pod_To_core_Pod(pod, internalPod, nil); err != nil {
 | 
									if err := k8s_api_v1.Convert_v1_Pod_To_core_Pod(pod, internalPod, nil); err != nil {
 | 
				
			||||||
					t.Fatalf("%s: Cannot convert pod %#v, %#v", testCase.desc, pod, err)
 | 
										t.Fatalf("%s: Cannot convert pod %#v, %#v", testCase.desc, pod, err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				if errs := validation.ValidatePod(internalPod); len(errs) > 0 {
 | 
									if errs := validation.ValidatePod(internalPod, validation.PodValidationOptions{}); len(errs) > 0 {
 | 
				
			||||||
					t.Fatalf("%s: Invalid pod %#v, %#v", testCase.desc, internalPod, errs)
 | 
										t.Fatalf("%s: Invalid pod %#v, %#v", testCase.desc, internalPod, errs)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -319,7 +319,7 @@ func TestExtractPodsFromHTTP(t *testing.T) {
 | 
				
			|||||||
			if err := k8s_api_v1.Convert_v1_Pod_To_core_Pod(pod, internalPod, nil); err != nil {
 | 
								if err := k8s_api_v1.Convert_v1_Pod_To_core_Pod(pod, internalPod, nil); err != nil {
 | 
				
			||||||
				t.Fatalf("%s: Cannot convert pod %#v, %#v", testCase.desc, pod, err)
 | 
									t.Fatalf("%s: Cannot convert pod %#v, %#v", testCase.desc, pod, err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if errs := validation.ValidatePod(internalPod); len(errs) != 0 {
 | 
								if errs := validation.ValidatePod(internalPod, validation.PodValidationOptions{}); len(errs) != 0 {
 | 
				
			||||||
				t.Errorf("%s: Expected no validation errors on %#v, Got %v", testCase.desc, pod, errs.ToAggregate())
 | 
									t.Errorf("%s: Expected no validation errors on %#v, Got %v", testCase.desc, pod, errs.ToAggregate())
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -88,7 +88,11 @@ func (podStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object
 | 
				
			|||||||
// Validate validates a new pod.
 | 
					// Validate validates a new pod.
 | 
				
			||||||
func (podStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
 | 
					func (podStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
 | 
				
			||||||
	pod := obj.(*api.Pod)
 | 
						pod := obj.(*api.Pod)
 | 
				
			||||||
	allErrs := validation.ValidatePodCreate(pod)
 | 
						opts := validation.PodValidationOptions{
 | 
				
			||||||
 | 
							// Allow multiple huge pages on pod create if feature is enabled
 | 
				
			||||||
 | 
							AllowMultipleHugePageResources: utilfeature.DefaultFeatureGate.Enabled(features.HugePageStorageMediumSize),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						allErrs := validation.ValidatePodCreate(pod, opts)
 | 
				
			||||||
	allErrs = append(allErrs, validation.ValidateConditionalPod(pod, nil, field.NewPath(""))...)
 | 
						allErrs = append(allErrs, validation.ValidateConditionalPod(pod, nil, field.NewPath(""))...)
 | 
				
			||||||
	return allErrs
 | 
						return allErrs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -104,8 +108,13 @@ func (podStrategy) AllowCreateOnUpdate() bool {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// ValidateUpdate is the default update validation for an end user.
 | 
					// ValidateUpdate is the default update validation for an end user.
 | 
				
			||||||
func (podStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
 | 
					func (podStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
 | 
				
			||||||
	errorList := validation.ValidatePod(obj.(*api.Pod))
 | 
						oldFailsSingleHugepagesValidation := len(validation.ValidatePodSingleHugePageResources(old.(*api.Pod), field.NewPath("spec"))) > 0
 | 
				
			||||||
	errorList = append(errorList, validation.ValidatePodUpdate(obj.(*api.Pod), old.(*api.Pod))...)
 | 
						opts := validation.PodValidationOptions{
 | 
				
			||||||
 | 
							// Allow multiple huge pages on pod create if feature is enabled or if the old pod already has multiple hugepages specified
 | 
				
			||||||
 | 
							AllowMultipleHugePageResources: oldFailsSingleHugepagesValidation || utilfeature.DefaultFeatureGate.Enabled(features.HugePageStorageMediumSize),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						errorList := validation.ValidatePod(obj.(*api.Pod), opts)
 | 
				
			||||||
 | 
						errorList = append(errorList, validation.ValidatePodUpdate(obj.(*api.Pod), old.(*api.Pod), opts)...)
 | 
				
			||||||
	errorList = append(errorList, validation.ValidateConditionalPod(obj.(*api.Pod), old.(*api.Pod), field.NewPath(""))...)
 | 
						errorList = append(errorList, validation.ValidateConditionalPod(obj.(*api.Pod), old.(*api.Pod), field.NewPath(""))...)
 | 
				
			||||||
	return errorList
 | 
						return errorList
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,6 +44,7 @@ go_test(
 | 
				
			|||||||
    embed = [":go_default_library"],
 | 
					    embed = [":go_default_library"],
 | 
				
			||||||
    deps = select({
 | 
					    deps = select({
 | 
				
			||||||
        "@io_bazel_rules_go//go/platform:android": [
 | 
					        "@io_bazel_rules_go//go/platform:android": [
 | 
				
			||||||
 | 
					            "//pkg/features:go_default_library",
 | 
				
			||||||
            "//pkg/volume:go_default_library",
 | 
					            "//pkg/volume:go_default_library",
 | 
				
			||||||
            "//pkg/volume/testing:go_default_library",
 | 
					            "//pkg/volume/testing:go_default_library",
 | 
				
			||||||
            "//pkg/volume/util:go_default_library",
 | 
					            "//pkg/volume/util:go_default_library",
 | 
				
			||||||
@@ -51,10 +52,13 @@ go_test(
 | 
				
			|||||||
            "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
 | 
					            "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
 | 
				
			||||||
            "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
					            "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
				
			||||||
            "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
 | 
					            "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
 | 
				
			||||||
 | 
					            "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
 | 
				
			||||||
            "//staging/src/k8s.io/client-go/util/testing:go_default_library",
 | 
					            "//staging/src/k8s.io/client-go/util/testing:go_default_library",
 | 
				
			||||||
 | 
					            "//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
 | 
				
			||||||
            "//vendor/k8s.io/utils/mount:go_default_library",
 | 
					            "//vendor/k8s.io/utils/mount:go_default_library",
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "@io_bazel_rules_go//go/platform:linux": [
 | 
					        "@io_bazel_rules_go//go/platform:linux": [
 | 
				
			||||||
 | 
					            "//pkg/features:go_default_library",
 | 
				
			||||||
            "//pkg/volume:go_default_library",
 | 
					            "//pkg/volume:go_default_library",
 | 
				
			||||||
            "//pkg/volume/testing:go_default_library",
 | 
					            "//pkg/volume/testing:go_default_library",
 | 
				
			||||||
            "//pkg/volume/util:go_default_library",
 | 
					            "//pkg/volume/util:go_default_library",
 | 
				
			||||||
@@ -62,7 +66,9 @@ go_test(
 | 
				
			|||||||
            "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
 | 
					            "//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
 | 
				
			||||||
            "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
					            "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
				
			||||||
            "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
 | 
					            "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
 | 
				
			||||||
 | 
					            "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
 | 
				
			||||||
            "//staging/src/k8s.io/client-go/util/testing:go_default_library",
 | 
					            "//staging/src/k8s.io/client-go/util/testing:go_default_library",
 | 
				
			||||||
 | 
					            "//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
 | 
				
			||||||
            "//vendor/k8s.io/utils/mount:go_default_library",
 | 
					            "//vendor/k8s.io/utils/mount:go_default_library",
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "//conditions:default": [],
 | 
					        "//conditions:default": [],
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,7 +25,7 @@ import (
 | 
				
			|||||||
	"k8s.io/utils/mount"
 | 
						"k8s.io/utils/mount"
 | 
				
			||||||
	utilstrings "k8s.io/utils/strings"
 | 
						utilstrings "k8s.io/utils/strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
						"k8s.io/api/core/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/api/resource"
 | 
						"k8s.io/apimachinery/pkg/api/resource"
 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/types"
 | 
						"k8s.io/apimachinery/pkg/types"
 | 
				
			||||||
@@ -160,7 +160,7 @@ type mountDetector interface {
 | 
				
			|||||||
	// returns (v1.StorageMediumMemory, false, nil), the caller knows that the path is
 | 
						// returns (v1.StorageMediumMemory, false, nil), the caller knows that the path is
 | 
				
			||||||
	// on a memory FS (tmpfs on Linux) but is not the root mountpoint of
 | 
						// on a memory FS (tmpfs on Linux) but is not the root mountpoint of
 | 
				
			||||||
	// that tmpfs.
 | 
						// that tmpfs.
 | 
				
			||||||
	GetMountMedium(path string) (v1.StorageMedium, bool, error)
 | 
						GetMountMedium(path string, requestedMedium v1.StorageMedium) (v1.StorageMedium, bool, *resource.Quantity, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// EmptyDir volumes are temporary directories exposed to the pod.
 | 
					// EmptyDir volumes are temporary directories exposed to the pod.
 | 
				
			||||||
@@ -216,12 +216,12 @@ func (ed *emptyDir) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch ed.medium {
 | 
						switch {
 | 
				
			||||||
	case v1.StorageMediumDefault:
 | 
						case ed.medium == v1.StorageMediumDefault:
 | 
				
			||||||
		err = ed.setupDir(dir)
 | 
							err = ed.setupDir(dir)
 | 
				
			||||||
	case v1.StorageMediumMemory:
 | 
						case ed.medium == v1.StorageMediumMemory:
 | 
				
			||||||
		err = ed.setupTmpfs(dir)
 | 
							err = ed.setupTmpfs(dir)
 | 
				
			||||||
	case v1.StorageMediumHugePages:
 | 
						case v1helper.IsHugePageMedium(ed.medium):
 | 
				
			||||||
		err = ed.setupHugepages(dir)
 | 
							err = ed.setupHugepages(dir)
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		err = fmt.Errorf("unknown storage medium %q", ed.medium)
 | 
							err = fmt.Errorf("unknown storage medium %q", ed.medium)
 | 
				
			||||||
@@ -261,7 +261,7 @@ func (ed *emptyDir) setupTmpfs(dir string) error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Make SetUp idempotent.
 | 
						// Make SetUp idempotent.
 | 
				
			||||||
	medium, isMnt, err := ed.mountDetector.GetMountMedium(dir)
 | 
						medium, isMnt, _, err := ed.mountDetector.GetMountMedium(dir, ed.medium)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -284,17 +284,34 @@ func (ed *emptyDir) setupHugepages(dir string) error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// Make SetUp idempotent.
 | 
						// Make SetUp idempotent.
 | 
				
			||||||
	medium, isMnt, err := ed.mountDetector.GetMountMedium(dir)
 | 
						medium, isMnt, mountPageSize, err := ed.mountDetector.GetMountMedium(dir, ed.medium)
 | 
				
			||||||
 | 
						klog.V(3).Infof("pod %v: setupHugepages: medium: %s, isMnt: %v, dir: %s, err: %v", ed.pod.UID, medium, isMnt, dir, err)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	// If the directory is a mountpoint with medium hugepages, there is no
 | 
						// If the directory is a mountpoint with medium hugepages of the same page size,
 | 
				
			||||||
	// work to do since we are already in the desired state.
 | 
						// there is no work to do since we are already in the desired state.
 | 
				
			||||||
	if isMnt && medium == v1.StorageMediumHugePages {
 | 
						if isMnt && v1helper.IsHugePageMedium(medium) {
 | 
				
			||||||
 | 
							// Medium is: Hugepages
 | 
				
			||||||
 | 
							if ed.medium == v1.StorageMediumHugePages {
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if mountPageSize == nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("pod %v: mounted dir %s pagesize is not determined", ed.pod.UID, dir)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Medium is: Hugepages-<size>
 | 
				
			||||||
 | 
							// Mounted page size and medium size must be equal
 | 
				
			||||||
 | 
							mediumSize, err := v1helper.HugePageSizeFromMedium(ed.medium)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if mountPageSize == nil || mediumSize.Cmp(*mountPageSize) != 0 {
 | 
				
			||||||
 | 
								return fmt.Errorf("pod %v: mounted dir %s pagesize '%s' and requested medium size '%s' differ", ed.pod.UID, dir, mountPageSize.String(), mediumSize.String())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pageSizeMountOption, err := getPageSizeMountOptionFromPod(ed.pod)
 | 
						pageSizeMountOption, err := getPageSizeMountOption(ed.medium, ed.pod)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -303,33 +320,52 @@ func (ed *emptyDir) setupHugepages(dir string) error {
 | 
				
			|||||||
	return ed.mounter.Mount("nodev", dir, "hugetlbfs", []string{pageSizeMountOption})
 | 
						return ed.mounter.Mount("nodev", dir, "hugetlbfs", []string{pageSizeMountOption})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getPageSizeMountOptionFromPod retrieves pageSize mount option from Pod's resources
 | 
					// getPageSizeMountOption retrieves pageSize mount option from Pod's resources
 | 
				
			||||||
// and validates pageSize options in all containers of given Pod.
 | 
					// and medium and validates pageSize options in all containers of given Pod.
 | 
				
			||||||
func getPageSizeMountOptionFromPod(pod *v1.Pod) (string, error) {
 | 
					func getPageSizeMountOption(medium v1.StorageMedium, pod *v1.Pod) (string, error) {
 | 
				
			||||||
	pageSizeFound := false
 | 
						pageSizeFound := false
 | 
				
			||||||
	pageSize := resource.Quantity{}
 | 
						pageSize := resource.Quantity{}
 | 
				
			||||||
	// In some rare cases init containers can also consume Huge pages.
 | 
					
 | 
				
			||||||
	containers := append(pod.Spec.Containers, pod.Spec.InitContainers...)
 | 
						var mediumPageSize resource.Quantity
 | 
				
			||||||
	for _, container := range containers {
 | 
						if medium != v1.StorageMediumHugePages {
 | 
				
			||||||
 | 
							// medium is: Hugepages-<size>
 | 
				
			||||||
 | 
							var err error
 | 
				
			||||||
 | 
							mediumPageSize, err = v1helper.HugePageSizeFromMedium(medium)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// In some rare cases init containers can also consume Huge pages
 | 
				
			||||||
 | 
						for _, container := range append(pod.Spec.Containers, pod.Spec.InitContainers...) {
 | 
				
			||||||
		// We can take request because limit and requests must match.
 | 
							// We can take request because limit and requests must match.
 | 
				
			||||||
		for requestName := range container.Resources.Requests {
 | 
							for requestName := range container.Resources.Requests {
 | 
				
			||||||
			if v1helper.IsHugePageResourceName(requestName) {
 | 
								if !v1helper.IsHugePageResourceName(requestName) {
 | 
				
			||||||
				currentPageSize, err := v1helper.HugePageSizeFromResourceName(requestName)
 | 
									continue
 | 
				
			||||||
				if err != nil {
 | 
								}
 | 
				
			||||||
					return "", err
 | 
								currentPageSize, err := v1helper.HugePageSizeFromResourceName(requestName)
 | 
				
			||||||
				}
 | 
								if err != nil {
 | 
				
			||||||
				// PageSize for all volumes in a POD are equal, except for the first one discovered.
 | 
									return "", err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if medium == v1.StorageMediumHugePages { // medium is: Hugepages, size is not specified
 | 
				
			||||||
 | 
									// PageSize for all volumes in a POD must be equal if medium is "Hugepages"
 | 
				
			||||||
				if pageSizeFound && pageSize.Cmp(currentPageSize) != 0 {
 | 
									if pageSizeFound && pageSize.Cmp(currentPageSize) != 0 {
 | 
				
			||||||
					return "", fmt.Errorf("multiple pageSizes for huge pages in a single PodSpec")
 | 
										return "", fmt.Errorf("medium: %s can't be used if container requests multiple huge page sizes", medium)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				pageSize = currentPageSize
 | 
					
 | 
				
			||||||
				pageSizeFound = true
 | 
									pageSizeFound = true
 | 
				
			||||||
 | 
									pageSize = currentPageSize
 | 
				
			||||||
 | 
								} else { // medium is: Hugepages-<size>
 | 
				
			||||||
 | 
									if currentPageSize.Cmp(mediumPageSize) == 0 {
 | 
				
			||||||
 | 
										pageSizeFound = true
 | 
				
			||||||
 | 
										pageSize = currentPageSize
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !pageSizeFound {
 | 
						if !pageSizeFound {
 | 
				
			||||||
		return "", fmt.Errorf("hugePages storage requested, but there is no resource request for huge pages")
 | 
							return "", fmt.Errorf("medium %s: hugePages storage requested, but there is no resource request for huge pages", medium)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return fmt.Sprintf("%s=%s", hugePagesPageSizeMountOption, pageSize.String()), nil
 | 
						return fmt.Sprintf("%s=%s", hugePagesPageSizeMountOption, pageSize.String()), nil
 | 
				
			||||||
@@ -393,7 +429,7 @@ func (ed *emptyDir) TearDownAt(dir string) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Figure out the medium.
 | 
						// Figure out the medium.
 | 
				
			||||||
	medium, isMnt, err := ed.mountDetector.GetMountMedium(dir)
 | 
						medium, isMnt, _, err := ed.mountDetector.GetMountMedium(dir, ed.medium)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,12 +20,14 @@ package emptydir
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"golang.org/x/sys/unix"
 | 
						"golang.org/x/sys/unix"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/api/resource"
 | 
				
			||||||
	"k8s.io/klog"
 | 
						"k8s.io/klog"
 | 
				
			||||||
	"k8s.io/utils/mount"
 | 
						"k8s.io/utils/mount"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
						"k8s.io/api/core/v1"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Defined by Linux - the type number for tmpfs mounts.
 | 
					// Defined by Linux - the type number for tmpfs mounts.
 | 
				
			||||||
@@ -39,22 +41,71 @@ type realMountDetector struct {
 | 
				
			|||||||
	mounter mount.Interface
 | 
						mounter mount.Interface
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *realMountDetector) GetMountMedium(path string) (v1.StorageMedium, bool, error) {
 | 
					// getPageSize obtains page size from the 'pagesize' mount option of the
 | 
				
			||||||
 | 
					// mounted volume
 | 
				
			||||||
 | 
					func getPageSize(path string, mounter mount.Interface) (*resource.Quantity, error) {
 | 
				
			||||||
 | 
						// Get mount point data for the path
 | 
				
			||||||
 | 
						mountPoints, err := mounter.List()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("error listing mount points: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Find mount point for the path
 | 
				
			||||||
 | 
						mountPoint, err := func(mps []mount.MountPoint, mpPath string) (*mount.MountPoint, error) {
 | 
				
			||||||
 | 
							for _, mp := range mps {
 | 
				
			||||||
 | 
								if mp.Path == mpPath {
 | 
				
			||||||
 | 
									return &mp, nil
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("mount point for %s not found", mpPath)
 | 
				
			||||||
 | 
						}(mountPoints, path)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Get page size from the 'pagesize' option value
 | 
				
			||||||
 | 
						for _, opt := range mountPoint.Opts {
 | 
				
			||||||
 | 
							opt = strings.TrimSpace(opt)
 | 
				
			||||||
 | 
							prefix := "pagesize="
 | 
				
			||||||
 | 
							if strings.HasPrefix(opt, prefix) {
 | 
				
			||||||
 | 
								// NOTE: Adding suffix 'i' as result should be comparable with a medium size.
 | 
				
			||||||
 | 
								// pagesize mount option is specified without a suffix,
 | 
				
			||||||
 | 
								// e.g. pagesize=2M or pagesize=1024M for x86 CPUs
 | 
				
			||||||
 | 
								pageSize, err := resource.ParseQuantity(strings.TrimPrefix(opt, prefix) + "i")
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return nil, fmt.Errorf("error getting page size from '%s' mount option: %v", opt, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return &pageSize, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil, fmt.Errorf("no pagesize option specified for %s mount", mountPoint.Path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *realMountDetector) GetMountMedium(path string, requestedMedium v1.StorageMedium) (v1.StorageMedium, bool, *resource.Quantity, error) {
 | 
				
			||||||
	klog.V(5).Infof("Determining mount medium of %v", path)
 | 
						klog.V(5).Infof("Determining mount medium of %v", path)
 | 
				
			||||||
	notMnt, err := m.mounter.IsLikelyNotMountPoint(path)
 | 
						notMnt, err := m.mounter.IsLikelyNotMountPoint(path)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return v1.StorageMediumDefault, false, fmt.Errorf("IsLikelyNotMountPoint(%q): %v", path, err)
 | 
							return v1.StorageMediumDefault, false, nil, fmt.Errorf("IsLikelyNotMountPoint(%q): %v", path, err)
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	buf := unix.Statfs_t{}
 | 
					 | 
				
			||||||
	if err := unix.Statfs(path, &buf); err != nil {
 | 
					 | 
				
			||||||
		return v1.StorageMediumDefault, false, fmt.Errorf("statfs(%q): %v", path, err)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	klog.V(5).Infof("Statfs_t of %v: %+v", path, buf)
 | 
						buf := unix.Statfs_t{}
 | 
				
			||||||
	if buf.Type == linuxTmpfsMagic {
 | 
						if err := unix.Statfs(path, &buf); err != nil {
 | 
				
			||||||
		return v1.StorageMediumMemory, !notMnt, nil
 | 
							return v1.StorageMediumDefault, false, nil, fmt.Errorf("statfs(%q): %v", path, err)
 | 
				
			||||||
	} else if int64(buf.Type) == linuxHugetlbfsMagic {
 | 
					 | 
				
			||||||
		return v1.StorageMediumHugePages, !notMnt, nil
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return v1.StorageMediumDefault, !notMnt, nil
 | 
					
 | 
				
			||||||
 | 
						klog.V(3).Infof("Statfs_t of %v: %+v", path, buf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if buf.Type == linuxTmpfsMagic {
 | 
				
			||||||
 | 
							return v1.StorageMediumMemory, !notMnt, nil, nil
 | 
				
			||||||
 | 
						} else if int64(buf.Type) == linuxHugetlbfsMagic {
 | 
				
			||||||
 | 
							// Skip page size detection if requested medium doesn't have size specified
 | 
				
			||||||
 | 
							if requestedMedium == v1.StorageMediumHugePages {
 | 
				
			||||||
 | 
								return v1.StorageMediumHugePages, !notMnt, nil, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// Get page size for the volume mount
 | 
				
			||||||
 | 
							pageSize, err := getPageSize(path, m.mounter)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return v1.StorageMediumHugePages, !notMnt, nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return v1.StorageMediumHugePages, !notMnt, pageSize, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return v1.StorageMediumDefault, !notMnt, nil, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,20 +19,24 @@ limitations under the License.
 | 
				
			|||||||
package emptydir
 | 
					package emptydir
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"k8s.io/utils/mount"
 | 
						"k8s.io/api/core/v1"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
					 | 
				
			||||||
	"k8s.io/apimachinery/pkg/api/resource"
 | 
						"k8s.io/apimachinery/pkg/api/resource"
 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/types"
 | 
						"k8s.io/apimachinery/pkg/types"
 | 
				
			||||||
 | 
						utilfeature "k8s.io/apiserver/pkg/util/feature"
 | 
				
			||||||
	utiltesting "k8s.io/client-go/util/testing"
 | 
						utiltesting "k8s.io/client-go/util/testing"
 | 
				
			||||||
 | 
						featuregatetesting "k8s.io/component-base/featuregate/testing"
 | 
				
			||||||
 | 
						"k8s.io/kubernetes/pkg/features"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/volume"
 | 
						"k8s.io/kubernetes/pkg/volume"
 | 
				
			||||||
	volumetest "k8s.io/kubernetes/pkg/volume/testing"
 | 
						volumetest "k8s.io/kubernetes/pkg/volume/testing"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/volume/util"
 | 
						"k8s.io/kubernetes/pkg/volume/util"
 | 
				
			||||||
 | 
						"k8s.io/utils/mount"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Construct an instance of a plugin, by name.
 | 
					// Construct an instance of a plugin, by name.
 | 
				
			||||||
@@ -71,8 +75,8 @@ type fakeMountDetector struct {
 | 
				
			|||||||
	isMount bool
 | 
						isMount bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (fake *fakeMountDetector) GetMountMedium(path string) (v1.StorageMedium, bool, error) {
 | 
					func (fake *fakeMountDetector) GetMountMedium(path string, requestedMedium v1.StorageMedium) (v1.StorageMedium, bool, *resource.Quantity, error) {
 | 
				
			||||||
	return fake.medium, fake.isMount, nil
 | 
						return fake.medium, fake.isMount, nil, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestPluginEmptyRootContext(t *testing.T) {
 | 
					func TestPluginEmptyRootContext(t *testing.T) {
 | 
				
			||||||
@@ -83,12 +87,33 @@ func TestPluginEmptyRootContext(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestPluginHugetlbfs(t *testing.T) {
 | 
					func TestPluginHugetlbfs(t *testing.T) {
 | 
				
			||||||
	doTestPlugin(t, pluginTestConfig{
 | 
						testCases := map[string]struct {
 | 
				
			||||||
		medium:                        v1.StorageMediumHugePages,
 | 
							medium                          v1.StorageMedium
 | 
				
			||||||
		expectedSetupMounts:           1,
 | 
							enableHugePageStorageMediumSize bool
 | 
				
			||||||
		expectedTeardownMounts:        0,
 | 
						}{
 | 
				
			||||||
		shouldBeMountedBeforeTeardown: true,
 | 
							"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 {
 | 
					type pluginTestConfig struct {
 | 
				
			||||||
@@ -307,11 +332,13 @@ func TestMetrics(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestGetHugePagesMountOptions(t *testing.T) {
 | 
					func TestGetHugePagesMountOptions(t *testing.T) {
 | 
				
			||||||
	testCases := map[string]struct {
 | 
						testCases := map[string]struct {
 | 
				
			||||||
		pod            *v1.Pod
 | 
							pod                             *v1.Pod
 | 
				
			||||||
		shouldFail     bool
 | 
							medium                          v1.StorageMedium
 | 
				
			||||||
		expectedResult string
 | 
							shouldFail                      bool
 | 
				
			||||||
 | 
							expectedResult                  string
 | 
				
			||||||
 | 
							enableHugePageStorageMediumSize bool
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		"testWithProperValues": {
 | 
							"ProperValues": {
 | 
				
			||||||
			pod: &v1.Pod{
 | 
								pod: &v1.Pod{
 | 
				
			||||||
				Spec: v1.PodSpec{
 | 
									Spec: v1.PodSpec{
 | 
				
			||||||
					Containers: []v1.Container{
 | 
										Containers: []v1.Container{
 | 
				
			||||||
@@ -325,10 +352,11 @@ func TestGetHugePagesMountOptions(t *testing.T) {
 | 
				
			|||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
 | 
								medium:         v1.StorageMediumHugePages,
 | 
				
			||||||
			shouldFail:     false,
 | 
								shouldFail:     false,
 | 
				
			||||||
			expectedResult: "pagesize=2Mi",
 | 
								expectedResult: "pagesize=2Mi",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"testWithProperValuesAndDifferentPageSize": {
 | 
							"ProperValuesAndDifferentPageSize": {
 | 
				
			||||||
			pod: &v1.Pod{
 | 
								pod: &v1.Pod{
 | 
				
			||||||
				Spec: v1.PodSpec{
 | 
									Spec: v1.PodSpec{
 | 
				
			||||||
					Containers: []v1.Container{
 | 
										Containers: []v1.Container{
 | 
				
			||||||
@@ -349,6 +377,7 @@ func TestGetHugePagesMountOptions(t *testing.T) {
 | 
				
			|||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
 | 
								medium:         v1.StorageMediumHugePages,
 | 
				
			||||||
			shouldFail:     false,
 | 
								shouldFail:     false,
 | 
				
			||||||
			expectedResult: "pagesize=1Gi",
 | 
								expectedResult: "pagesize=1Gi",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -373,6 +402,7 @@ func TestGetHugePagesMountOptions(t *testing.T) {
 | 
				
			|||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
 | 
								medium:         v1.StorageMediumHugePages,
 | 
				
			||||||
			shouldFail:     false,
 | 
								shouldFail:     false,
 | 
				
			||||||
			expectedResult: "pagesize=1Gi",
 | 
								expectedResult: "pagesize=1Gi",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -397,6 +427,7 @@ func TestGetHugePagesMountOptions(t *testing.T) {
 | 
				
			|||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
 | 
								medium:         v1.StorageMediumHugePages,
 | 
				
			||||||
			shouldFail:     true,
 | 
								shouldFail:     true,
 | 
				
			||||||
			expectedResult: "",
 | 
								expectedResult: "",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -421,24 +452,463 @@ func TestGetHugePagesMountOptions(t *testing.T) {
 | 
				
			|||||||
					},
 | 
										},
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			shouldFail:     true,
 | 
								medium:                          v1.StorageMediumHugePages,
 | 
				
			||||||
			expectedResult: "",
 | 
								shouldFail:                      true,
 | 
				
			||||||
 | 
								expectedResult:                  "",
 | 
				
			||||||
 | 
								enableHugePageStorageMediumSize: true,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"PodWithNoHugePagesRequest": {
 | 
							"PodWithNoHugePagesRequest": {
 | 
				
			||||||
			pod:            &v1.Pod{},
 | 
								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,
 | 
								shouldFail:     true,
 | 
				
			||||||
			expectedResult: "",
 | 
								expectedResult: "",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for testCaseName, testCase := range testCases {
 | 
						for testCaseName, testCase := range testCases {
 | 
				
			||||||
		value, err := getPageSizeMountOptionFromPod(testCase.pod)
 | 
							t.Run(testCaseName, func(t *testing.T) {
 | 
				
			||||||
		if testCase.shouldFail && err == nil {
 | 
								defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.HugePageStorageMediumSize, testCase.enableHugePageStorageMediumSize)()
 | 
				
			||||||
			t.Errorf("Expected an error in %v", testCaseName)
 | 
								value, err := getPageSizeMountOption(testCase.medium, testCase.pod)
 | 
				
			||||||
		} else if !testCase.shouldFail && err != nil {
 | 
								if testCase.shouldFail && err == nil {
 | 
				
			||||||
			t.Errorf("Unexpected error in %v, got %v", testCaseName, err)
 | 
									t.Errorf("%s: Unexpected success", testCaseName)
 | 
				
			||||||
		} else if testCase.expectedResult != value {
 | 
								} else if !testCase.shouldFail && err != nil {
 | 
				
			||||||
			t.Errorf("Unexpected mountOptions for Pod. Expected %v, got %v", testCase.expectedResult, value)
 | 
									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)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type testMountDetector struct {
 | 
				
			||||||
 | 
						pageSize *resource.Quantity
 | 
				
			||||||
 | 
						isMnt    bool
 | 
				
			||||||
 | 
						err      error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (md *testMountDetector) GetMountMedium(path string, requestedMedium v1.StorageMedium) (v1.StorageMedium, bool, *resource.Quantity, error) {
 | 
				
			||||||
 | 
						return v1.StorageMediumHugePages, md.isMnt, md.pageSize, md.err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSetupHugepages(t *testing.T) {
 | 
				
			||||||
 | 
						tmpdir, err := ioutil.TempDir("", "TestSetupHugepages")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer os.RemoveAll(tmpdir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pageSize2Mi := resource.MustParse("2Mi")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						testCases := map[string]struct {
 | 
				
			||||||
 | 
							path       string
 | 
				
			||||||
 | 
							ed         *emptyDir
 | 
				
			||||||
 | 
							shouldFail bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							"Valid: mount expected": {
 | 
				
			||||||
 | 
								path: tmpdir,
 | 
				
			||||||
 | 
								ed: &emptyDir{
 | 
				
			||||||
 | 
									medium: v1.StorageMediumHugePages,
 | 
				
			||||||
 | 
									pod: &v1.Pod{
 | 
				
			||||||
 | 
										Spec: v1.PodSpec{
 | 
				
			||||||
 | 
											Containers: []v1.Container{
 | 
				
			||||||
 | 
												{
 | 
				
			||||||
 | 
													Resources: v1.ResourceRequirements{
 | 
				
			||||||
 | 
														Requests: v1.ResourceList{
 | 
				
			||||||
 | 
															v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
													},
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									mounter: &mount.FakeMounter{},
 | 
				
			||||||
 | 
									mountDetector: &testMountDetector{
 | 
				
			||||||
 | 
										pageSize: &resource.Quantity{},
 | 
				
			||||||
 | 
										isMnt:    false,
 | 
				
			||||||
 | 
										err:      nil,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								shouldFail: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"Valid: already mounted with correct pagesize": {
 | 
				
			||||||
 | 
								path: tmpdir,
 | 
				
			||||||
 | 
								ed: &emptyDir{
 | 
				
			||||||
 | 
									medium: "HugePages-2Mi",
 | 
				
			||||||
 | 
									pod: &v1.Pod{
 | 
				
			||||||
 | 
										Spec: v1.PodSpec{
 | 
				
			||||||
 | 
											Containers: []v1.Container{
 | 
				
			||||||
 | 
												{
 | 
				
			||||||
 | 
													Resources: v1.ResourceRequirements{
 | 
				
			||||||
 | 
														Requests: v1.ResourceList{
 | 
				
			||||||
 | 
															v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
													},
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									mounter: mount.NewFakeMounter([]mount.MountPoint{{Path: tmpdir, Opts: []string{"rw", "pagesize=2M", "realtime"}}}),
 | 
				
			||||||
 | 
									mountDetector: &testMountDetector{
 | 
				
			||||||
 | 
										pageSize: &pageSize2Mi,
 | 
				
			||||||
 | 
										isMnt:    true,
 | 
				
			||||||
 | 
										err:      nil,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								shouldFail: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"Valid: already mounted": {
 | 
				
			||||||
 | 
								path: tmpdir,
 | 
				
			||||||
 | 
								ed: &emptyDir{
 | 
				
			||||||
 | 
									medium: "HugePages",
 | 
				
			||||||
 | 
									pod: &v1.Pod{
 | 
				
			||||||
 | 
										Spec: v1.PodSpec{
 | 
				
			||||||
 | 
											Containers: []v1.Container{
 | 
				
			||||||
 | 
												{
 | 
				
			||||||
 | 
													Resources: v1.ResourceRequirements{
 | 
				
			||||||
 | 
														Requests: v1.ResourceList{
 | 
				
			||||||
 | 
															v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
													},
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									mounter: mount.NewFakeMounter([]mount.MountPoint{{Path: tmpdir, Opts: []string{"rw", "pagesize=2M", "realtime"}}}),
 | 
				
			||||||
 | 
									mountDetector: &testMountDetector{
 | 
				
			||||||
 | 
										pageSize: nil,
 | 
				
			||||||
 | 
										isMnt:    true,
 | 
				
			||||||
 | 
										err:      nil,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								shouldFail: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"Invalid: mounter is nil": {
 | 
				
			||||||
 | 
								path: tmpdir,
 | 
				
			||||||
 | 
								ed: &emptyDir{
 | 
				
			||||||
 | 
									medium: "HugePages-2Mi",
 | 
				
			||||||
 | 
									pod: &v1.Pod{
 | 
				
			||||||
 | 
										Spec: v1.PodSpec{
 | 
				
			||||||
 | 
											Containers: []v1.Container{
 | 
				
			||||||
 | 
												{
 | 
				
			||||||
 | 
													Resources: v1.ResourceRequirements{
 | 
				
			||||||
 | 
														Requests: v1.ResourceList{
 | 
				
			||||||
 | 
															v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
													},
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									mounter: nil,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								shouldFail: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"Invalid: GetMountMedium error": {
 | 
				
			||||||
 | 
								path: tmpdir,
 | 
				
			||||||
 | 
								ed: &emptyDir{
 | 
				
			||||||
 | 
									medium: "HugePages-2Mi",
 | 
				
			||||||
 | 
									pod: &v1.Pod{
 | 
				
			||||||
 | 
										Spec: v1.PodSpec{
 | 
				
			||||||
 | 
											Containers: []v1.Container{
 | 
				
			||||||
 | 
												{
 | 
				
			||||||
 | 
													Resources: v1.ResourceRequirements{
 | 
				
			||||||
 | 
														Requests: v1.ResourceList{
 | 
				
			||||||
 | 
															v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
													},
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									mounter: mount.NewFakeMounter([]mount.MountPoint{{Path: tmpdir, Opts: []string{"rw", "pagesize=2M", "realtime"}}}),
 | 
				
			||||||
 | 
									mountDetector: &testMountDetector{
 | 
				
			||||||
 | 
										pageSize: &pageSize2Mi,
 | 
				
			||||||
 | 
										isMnt:    true,
 | 
				
			||||||
 | 
										err:      fmt.Errorf("GetMountMedium error"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								shouldFail: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"Invalid: medium and page size differ": {
 | 
				
			||||||
 | 
								path: tmpdir,
 | 
				
			||||||
 | 
								ed: &emptyDir{
 | 
				
			||||||
 | 
									medium: "HugePages-1Gi",
 | 
				
			||||||
 | 
									pod: &v1.Pod{
 | 
				
			||||||
 | 
										Spec: v1.PodSpec{
 | 
				
			||||||
 | 
											Containers: []v1.Container{
 | 
				
			||||||
 | 
												{
 | 
				
			||||||
 | 
													Resources: v1.ResourceRequirements{
 | 
				
			||||||
 | 
														Requests: v1.ResourceList{
 | 
				
			||||||
 | 
															v1.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
													},
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									mounter: mount.NewFakeMounter([]mount.MountPoint{{Path: tmpdir, Opts: []string{"rw", "pagesize=2M", "realtime"}}}),
 | 
				
			||||||
 | 
									mountDetector: &testMountDetector{
 | 
				
			||||||
 | 
										pageSize: &pageSize2Mi,
 | 
				
			||||||
 | 
										isMnt:    true,
 | 
				
			||||||
 | 
										err:      nil,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								shouldFail: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"Invalid medium": {
 | 
				
			||||||
 | 
								path: tmpdir,
 | 
				
			||||||
 | 
								ed: &emptyDir{
 | 
				
			||||||
 | 
									medium: "HugePages-NN",
 | 
				
			||||||
 | 
									pod: &v1.Pod{
 | 
				
			||||||
 | 
										Spec: v1.PodSpec{
 | 
				
			||||||
 | 
											Containers: []v1.Container{
 | 
				
			||||||
 | 
												{
 | 
				
			||||||
 | 
													Resources: v1.ResourceRequirements{
 | 
				
			||||||
 | 
														Requests: v1.ResourceList{
 | 
				
			||||||
 | 
															v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
													},
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									mounter: &mount.FakeMounter{},
 | 
				
			||||||
 | 
									mountDetector: &testMountDetector{
 | 
				
			||||||
 | 
										pageSize: &resource.Quantity{},
 | 
				
			||||||
 | 
										isMnt:    false,
 | 
				
			||||||
 | 
										err:      nil,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								shouldFail: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"Invalid: setupDir fails": {
 | 
				
			||||||
 | 
								path: "",
 | 
				
			||||||
 | 
								ed: &emptyDir{
 | 
				
			||||||
 | 
									medium: v1.StorageMediumHugePages,
 | 
				
			||||||
 | 
									pod: &v1.Pod{
 | 
				
			||||||
 | 
										Spec: v1.PodSpec{
 | 
				
			||||||
 | 
											Containers: []v1.Container{
 | 
				
			||||||
 | 
												{
 | 
				
			||||||
 | 
													Resources: v1.ResourceRequirements{
 | 
				
			||||||
 | 
														Requests: v1.ResourceList{
 | 
				
			||||||
 | 
															v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
 | 
				
			||||||
 | 
														},
 | 
				
			||||||
 | 
													},
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									mounter: &mount.FakeMounter{},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								shouldFail: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for testCaseName, testCase := range testCases {
 | 
				
			||||||
 | 
							t.Run(testCaseName, func(t *testing.T) {
 | 
				
			||||||
 | 
								err := testCase.ed.setupHugepages(testCase.path)
 | 
				
			||||||
 | 
								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)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetPageSize(t *testing.T) {
 | 
				
			||||||
 | 
						mounter := &mount.FakeMounter{
 | 
				
			||||||
 | 
							MountPoints: []mount.MountPoint{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Device: "/dev/sda2",
 | 
				
			||||||
 | 
									Type:   "ext4",
 | 
				
			||||||
 | 
									Path:   "/",
 | 
				
			||||||
 | 
									Opts:   []string{"rw", "relatime", "errors=remount-ro"},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Device: "/dev/hugepages",
 | 
				
			||||||
 | 
									Type:   "hugetlbfs",
 | 
				
			||||||
 | 
									Path:   "/mnt/hugepages-2Mi",
 | 
				
			||||||
 | 
									Opts:   []string{"rw", "relatime", "pagesize=2M"},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Device: "sysfs",
 | 
				
			||||||
 | 
									Type:   "sysfs",
 | 
				
			||||||
 | 
									Path:   "/sys",
 | 
				
			||||||
 | 
									Opts:   []string{"rw", "nosuid", "nodev", "noexec", "relatime"},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Device: "/dev/hugepages",
 | 
				
			||||||
 | 
									Type:   "hugetlbfs",
 | 
				
			||||||
 | 
									Path:   "/mnt/hugepages-1Gi",
 | 
				
			||||||
 | 
									Opts:   []string{"rw", "relatime", "pagesize=1024M"},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Device: "/dev/hugepages",
 | 
				
			||||||
 | 
									Type:   "hugetlbfs",
 | 
				
			||||||
 | 
									Path:   "/mnt/noopt",
 | 
				
			||||||
 | 
									Opts:   []string{"rw", "relatime"},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Device: "/dev/hugepages",
 | 
				
			||||||
 | 
									Type:   "hugetlbfs",
 | 
				
			||||||
 | 
									Path:   "/mnt/badopt",
 | 
				
			||||||
 | 
									Opts:   []string{"rw", "relatime", "pagesize=NN"},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						testCases := map[string]struct {
 | 
				
			||||||
 | 
							path           string
 | 
				
			||||||
 | 
							mounter        mount.Interface
 | 
				
			||||||
 | 
							expectedResult resource.Quantity
 | 
				
			||||||
 | 
							shouldFail     bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							"Valid: existing 2Mi mount": {
 | 
				
			||||||
 | 
								path:           "/mnt/hugepages-2Mi",
 | 
				
			||||||
 | 
								mounter:        mounter,
 | 
				
			||||||
 | 
								shouldFail:     false,
 | 
				
			||||||
 | 
								expectedResult: resource.MustParse("2Mi"),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"Valid: existing 1Gi mount": {
 | 
				
			||||||
 | 
								path:           "/mnt/hugepages-1Gi",
 | 
				
			||||||
 | 
								mounter:        mounter,
 | 
				
			||||||
 | 
								shouldFail:     false,
 | 
				
			||||||
 | 
								expectedResult: resource.MustParse("1Gi"),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"Invalid: mount point doesn't exist": {
 | 
				
			||||||
 | 
								path:       "/mnt/nomp",
 | 
				
			||||||
 | 
								mounter:    mounter,
 | 
				
			||||||
 | 
								shouldFail: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"Invalid: no pagesize option": {
 | 
				
			||||||
 | 
								path:       "/mnt/noopt",
 | 
				
			||||||
 | 
								mounter:    mounter,
 | 
				
			||||||
 | 
								shouldFail: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"Invalid: incorrect pagesize option": {
 | 
				
			||||||
 | 
								path:       "/mnt/badopt",
 | 
				
			||||||
 | 
								mounter:    mounter,
 | 
				
			||||||
 | 
								shouldFail: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for testCaseName, testCase := range testCases {
 | 
				
			||||||
 | 
							t.Run(testCaseName, func(t *testing.T) {
 | 
				
			||||||
 | 
								pageSize, err := getPageSize(testCase.path, testCase.mounter)
 | 
				
			||||||
 | 
								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)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if err == nil && pageSize.Cmp(testCase.expectedResult) != 0 {
 | 
				
			||||||
 | 
									t.Errorf("%s: Unexpected result: %s, expected: %s", testCaseName, pageSize.String(), testCase.expectedResult.String())
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,7 @@ limitations under the License.
 | 
				
			|||||||
package emptydir
 | 
					package emptydir
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/api/resource"
 | 
				
			||||||
	"k8s.io/utils/mount"
 | 
						"k8s.io/utils/mount"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
						v1 "k8s.io/api/core/v1"
 | 
				
			||||||
@@ -29,6 +30,6 @@ type realMountDetector struct {
 | 
				
			|||||||
	mounter mount.Interface
 | 
						mounter mount.Interface
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *realMountDetector) GetMountMedium(path string) (v1.StorageMedium, bool, error) {
 | 
					func (m *realMountDetector) GetMountMedium(path string, requestedMedium v1.StorageMedium) (v1.StorageMedium, bool, *resource.Quantity, error) {
 | 
				
			||||||
	return v1.StorageMediumDefault, false, nil
 | 
						return v1.StorageMediumDefault, false, nil, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -887,9 +887,10 @@ type FlockerVolumeSource struct {
 | 
				
			|||||||
type StorageMedium string
 | 
					type StorageMedium string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	StorageMediumDefault   StorageMedium = ""          // use whatever the default is for the node, assume anything we don't explicitly handle is this
 | 
						StorageMediumDefault         StorageMedium = ""           // use whatever the default is for the node, assume anything we don't explicitly handle is this
 | 
				
			||||||
	StorageMediumMemory    StorageMedium = "Memory"    // use memory (e.g. tmpfs on linux)
 | 
						StorageMediumMemory          StorageMedium = "Memory"     // use memory (e.g. tmpfs on linux)
 | 
				
			||||||
	StorageMediumHugePages StorageMedium = "HugePages" // use hugepages
 | 
						StorageMediumHugePages       StorageMedium = "HugePages"  // use hugepages
 | 
				
			||||||
 | 
						StorageMediumHugePagesPrefix StorageMedium = "HugePages-" // prefix for full medium notation HugePages-<size>
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Protocol defines network protocols supported for things like container ports.
 | 
					// Protocol defines network protocols supported for things like container ports.
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user