mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	Merge pull request #98515 from lala123912/huge_page
Add request value verification for hugepage
This commit is contained in:
		@@ -345,6 +345,52 @@ func usesMultipleHugePageResources(podSpec *api.PodSpec) bool {
 | 
				
			|||||||
	return len(hugePageResources) > 1
 | 
						return len(hugePageResources) > 1
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func checkContainerUseIndivisibleHugePagesValues(container api.Container) bool {
 | 
				
			||||||
 | 
						for resourceName, quantity := range container.Resources.Limits {
 | 
				
			||||||
 | 
							if helper.IsHugePageResourceName(resourceName) {
 | 
				
			||||||
 | 
								if !helper.IsHugePageResourceValueDivisible(resourceName, quantity) {
 | 
				
			||||||
 | 
									return true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for resourceName, quantity := range container.Resources.Requests {
 | 
				
			||||||
 | 
							if helper.IsHugePageResourceName(resourceName) {
 | 
				
			||||||
 | 
								if !helper.IsHugePageResourceValueDivisible(resourceName, quantity) {
 | 
				
			||||||
 | 
									return true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// usesIndivisibleHugePagesValues returns true if the one of the containers uses non-integer multiple
 | 
				
			||||||
 | 
					// of huge page unit size
 | 
				
			||||||
 | 
					func usesIndivisibleHugePagesValues(podSpec *api.PodSpec) bool {
 | 
				
			||||||
 | 
						foundIndivisibleHugePagesValue := false
 | 
				
			||||||
 | 
						VisitContainers(podSpec, AllContainers, func(c *api.Container, containerType ContainerType) bool {
 | 
				
			||||||
 | 
							if checkContainerUseIndivisibleHugePagesValues(*c) {
 | 
				
			||||||
 | 
								foundIndivisibleHugePagesValue = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return !foundIndivisibleHugePagesValue // continue visiting if we haven't seen an invalid value yet
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if foundIndivisibleHugePagesValue {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for resourceName, quantity := range podSpec.Overhead {
 | 
				
			||||||
 | 
							if helper.IsHugePageResourceName(resourceName) {
 | 
				
			||||||
 | 
								if !helper.IsHugePageResourceValueDivisible(resourceName, quantity) {
 | 
				
			||||||
 | 
									return true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetValidationOptionsFromPodSpecAndMeta returns validation options based on pod specs and metadata
 | 
					// GetValidationOptionsFromPodSpecAndMeta returns validation options based on pod specs and metadata
 | 
				
			||||||
func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, podMeta, oldPodMeta *metav1.ObjectMeta) apivalidation.PodValidationOptions {
 | 
					func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, podMeta, oldPodMeta *metav1.ObjectMeta) apivalidation.PodValidationOptions {
 | 
				
			||||||
	// default pod validation options based on feature gate
 | 
						// default pod validation options based on feature gate
 | 
				
			||||||
@@ -354,6 +400,8 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po
 | 
				
			|||||||
		// Allow pod spec to use hugepages in downward API if feature is enabled
 | 
							// Allow pod spec to use hugepages in downward API if feature is enabled
 | 
				
			||||||
		AllowDownwardAPIHugePages:   utilfeature.DefaultFeatureGate.Enabled(features.DownwardAPIHugePages),
 | 
							AllowDownwardAPIHugePages:   utilfeature.DefaultFeatureGate.Enabled(features.DownwardAPIHugePages),
 | 
				
			||||||
		AllowInvalidPodDeletionCost: !utilfeature.DefaultFeatureGate.Enabled(features.PodDeletionCost),
 | 
							AllowInvalidPodDeletionCost: !utilfeature.DefaultFeatureGate.Enabled(features.PodDeletionCost),
 | 
				
			||||||
 | 
							// Do not allow pod spec to use non-integer multiple of huge page unit size default
 | 
				
			||||||
 | 
							AllowIndivisibleHugePagesValues: false,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if oldPodSpec != nil {
 | 
						if oldPodSpec != nil {
 | 
				
			||||||
@@ -368,12 +416,16 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po
 | 
				
			|||||||
				return !opts.AllowDownwardAPIHugePages
 | 
									return !opts.AllowDownwardAPIHugePages
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// if old spec used non-integer multiple of huge page unit size, we must allow it
 | 
				
			||||||
 | 
							opts.AllowIndivisibleHugePagesValues = usesIndivisibleHugePagesValues(oldPodSpec)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if oldPodMeta != nil && !opts.AllowInvalidPodDeletionCost {
 | 
						if oldPodMeta != nil && !opts.AllowInvalidPodDeletionCost {
 | 
				
			||||||
		// This is an update, so validate only if the existing object was valid.
 | 
							// This is an update, so validate only if the existing object was valid.
 | 
				
			||||||
		_, err := helper.GetDeletionCostFromPodAnnotations(oldPodMeta.Annotations)
 | 
							_, err := helper.GetDeletionCostFromPodAnnotations(oldPodMeta.Annotations)
 | 
				
			||||||
		opts.AllowInvalidPodDeletionCost = err != nil
 | 
							opts.AllowInvalidPodDeletionCost = err != nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return opts
 | 
						return opts
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,6 +39,21 @@ func IsHugePageResourceName(name core.ResourceName) bool {
 | 
				
			|||||||
	return strings.HasPrefix(string(name), core.ResourceHugePagesPrefix)
 | 
						return strings.HasPrefix(string(name), core.ResourceHugePagesPrefix)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsHugePageResourceValueDivisible returns true if the resource value of storage is
 | 
				
			||||||
 | 
					// integer multiple of page size.
 | 
				
			||||||
 | 
					func IsHugePageResourceValueDivisible(name core.ResourceName, quantity resource.Quantity) bool {
 | 
				
			||||||
 | 
						pageSize, err := HugePageSizeFromResourceName(name)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if pageSize.Sign() <= 0 || pageSize.MilliValue()%int64(1000) != int64(0) {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return quantity.Value()%pageSize.Value() == 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsQuotaHugePageResourceName returns true if the resource name has the quota
 | 
					// IsQuotaHugePageResourceName returns true if the resource name has the quota
 | 
				
			||||||
// related huge page resource prefix.
 | 
					// related huge page resource prefix.
 | 
				
			||||||
func IsQuotaHugePageResourceName(name core.ResourceName) bool {
 | 
					func IsQuotaHugePageResourceName(name core.ResourceName) bool {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -211,6 +211,60 @@ func TestIsHugePageResourceName(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestIsHugePageResourceValueDivisible(t *testing.T) {
 | 
				
			||||||
 | 
						testCases := []struct {
 | 
				
			||||||
 | 
							name     core.ResourceName
 | 
				
			||||||
 | 
							quantity resource.Quantity
 | 
				
			||||||
 | 
							result   bool
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     core.ResourceName("hugepages-2Mi"),
 | 
				
			||||||
 | 
								quantity: resource.MustParse("4Mi"),
 | 
				
			||||||
 | 
								result:   true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     core.ResourceName("hugepages-2Mi"),
 | 
				
			||||||
 | 
								quantity: resource.MustParse("5Mi"),
 | 
				
			||||||
 | 
								result:   false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     core.ResourceName("hugepages-1Gi"),
 | 
				
			||||||
 | 
								quantity: resource.MustParse("2Gi"),
 | 
				
			||||||
 | 
								result:   true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     core.ResourceName("hugepages-1Gi"),
 | 
				
			||||||
 | 
								quantity: resource.MustParse("2.1Gi"),
 | 
				
			||||||
 | 
								result:   false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     core.ResourceName("hugepages-1Mi"),
 | 
				
			||||||
 | 
								quantity: resource.MustParse("2.1Mi"),
 | 
				
			||||||
 | 
								result:   false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     core.ResourceName("hugepages-64Ki"),
 | 
				
			||||||
 | 
								quantity: resource.MustParse("128Ki"),
 | 
				
			||||||
 | 
								result:   true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     core.ResourceName("hugepages-"),
 | 
				
			||||||
 | 
								quantity: resource.MustParse("128Ki"),
 | 
				
			||||||
 | 
								result:   false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     core.ResourceName("hugepages"),
 | 
				
			||||||
 | 
								quantity: resource.MustParse("128Ki"),
 | 
				
			||||||
 | 
								result:   false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, testCase := range testCases {
 | 
				
			||||||
 | 
							if testCase.result != IsHugePageResourceValueDivisible(testCase.name, testCase.quantity) {
 | 
				
			||||||
 | 
								t.Errorf("resource: %v storage:%v expected result: %v", testCase.name, testCase.quantity, testCase.result)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestHugePageResourceName(t *testing.T) {
 | 
					func TestHugePageResourceName(t *testing.T) {
 | 
				
			||||||
	testCases := []struct {
 | 
						testCases := []struct {
 | 
				
			||||||
		pageSize resource.Quantity
 | 
							pageSize resource.Quantity
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -301,9 +301,9 @@ func ValidateRuntimeClassName(name string, fldPath *field.Path) field.ErrorList
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// validateOverhead can be used to check whether the given Overhead is valid.
 | 
					// validateOverhead can be used to check whether the given Overhead is valid.
 | 
				
			||||||
func validateOverhead(overhead core.ResourceList, fldPath *field.Path) field.ErrorList {
 | 
					func validateOverhead(overhead core.ResourceList, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
 | 
				
			||||||
	// reuse the ResourceRequirements validation logic
 | 
						// reuse the ResourceRequirements validation logic
 | 
				
			||||||
	return ValidateResourceRequirements(&core.ResourceRequirements{Limits: overhead}, fldPath)
 | 
						return ValidateResourceRequirements(&core.ResourceRequirements{Limits: overhead}, fldPath, opts)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validates that given value is not negative.
 | 
					// Validates that given value is not negative.
 | 
				
			||||||
@@ -2889,7 +2889,7 @@ func validateContainers(containers []core.Container, isInitContainers bool, volu
 | 
				
			|||||||
		allErrs = append(allErrs, ValidateVolumeMounts(ctr.VolumeMounts, volDevices, volumes, &ctr, idxPath.Child("volumeMounts"))...)
 | 
							allErrs = append(allErrs, ValidateVolumeMounts(ctr.VolumeMounts, volDevices, volumes, &ctr, idxPath.Child("volumeMounts"))...)
 | 
				
			||||||
		allErrs = append(allErrs, ValidateVolumeDevices(ctr.VolumeDevices, volMounts, volumes, idxPath.Child("volumeDevices"))...)
 | 
							allErrs = append(allErrs, ValidateVolumeDevices(ctr.VolumeDevices, volMounts, volumes, idxPath.Child("volumeDevices"))...)
 | 
				
			||||||
		allErrs = append(allErrs, validatePullPolicy(ctr.ImagePullPolicy, idxPath.Child("imagePullPolicy"))...)
 | 
							allErrs = append(allErrs, validatePullPolicy(ctr.ImagePullPolicy, idxPath.Child("imagePullPolicy"))...)
 | 
				
			||||||
		allErrs = append(allErrs, ValidateResourceRequirements(&ctr.Resources, idxPath.Child("resources"))...)
 | 
							allErrs = append(allErrs, ValidateResourceRequirements(&ctr.Resources, idxPath.Child("resources"), opts)...)
 | 
				
			||||||
		allErrs = append(allErrs, ValidateSecurityContext(ctr.SecurityContext, idxPath.Child("securityContext"))...)
 | 
							allErrs = append(allErrs, ValidateSecurityContext(ctr.SecurityContext, idxPath.Child("securityContext"))...)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -3202,6 +3202,8 @@ type PodValidationOptions struct {
 | 
				
			|||||||
	AllowDownwardAPIHugePages bool
 | 
						AllowDownwardAPIHugePages bool
 | 
				
			||||||
	// Allow invalid pod-deletion-cost annotation value for backward compatibility.
 | 
						// Allow invalid pod-deletion-cost annotation value for backward compatibility.
 | 
				
			||||||
	AllowInvalidPodDeletionCost bool
 | 
						AllowInvalidPodDeletionCost bool
 | 
				
			||||||
 | 
						// Allow pod spec to use non-integer multiple of huge page unit size
 | 
				
			||||||
 | 
						AllowIndivisibleHugePagesValues bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ValidatePodSingleHugePageResources checks if there are multiple huge
 | 
					// ValidatePodSingleHugePageResources checks if there are multiple huge
 | 
				
			||||||
@@ -3375,7 +3377,7 @@ func ValidatePodSpec(spec *core.PodSpec, podMeta *metav1.ObjectMeta, fldPath *fi
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if spec.Overhead != nil {
 | 
						if spec.Overhead != nil {
 | 
				
			||||||
		allErrs = append(allErrs, validateOverhead(spec.Overhead, fldPath.Child("overhead"))...)
 | 
							allErrs = append(allErrs, validateOverhead(spec.Overhead, fldPath.Child("overhead"), opts)...)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return allErrs
 | 
						return allErrs
 | 
				
			||||||
@@ -5336,7 +5338,7 @@ func validateBasicResource(quantity resource.Quantity, fldPath *field.Path) fiel
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validates resource requirement spec.
 | 
					// Validates resource requirement spec.
 | 
				
			||||||
func ValidateResourceRequirements(requirements *core.ResourceRequirements, fldPath *field.Path) field.ErrorList {
 | 
					func ValidateResourceRequirements(requirements *core.ResourceRequirements, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
 | 
				
			||||||
	allErrs := field.ErrorList{}
 | 
						allErrs := field.ErrorList{}
 | 
				
			||||||
	limPath := fldPath.Child("limits")
 | 
						limPath := fldPath.Child("limits")
 | 
				
			||||||
	reqPath := fldPath.Child("requests")
 | 
						reqPath := fldPath.Child("requests")
 | 
				
			||||||
@@ -5356,6 +5358,9 @@ func ValidateResourceRequirements(requirements *core.ResourceRequirements, fldPa
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if helper.IsHugePageResourceName(resourceName) {
 | 
							if helper.IsHugePageResourceName(resourceName) {
 | 
				
			||||||
			limContainsHugePages = true
 | 
								limContainsHugePages = true
 | 
				
			||||||
 | 
								if err := validateResourceQuantityHugePageValue(resourceName, quantity, opts); err != nil {
 | 
				
			||||||
 | 
									allErrs = append(allErrs, field.Invalid(fldPath, quantity.String(), err.Error()))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if supportedQoSComputeResources.Has(string(resourceName)) {
 | 
							if supportedQoSComputeResources.Has(string(resourceName)) {
 | 
				
			||||||
@@ -5383,6 +5388,9 @@ func ValidateResourceRequirements(requirements *core.ResourceRequirements, fldPa
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		if helper.IsHugePageResourceName(resourceName) {
 | 
							if helper.IsHugePageResourceName(resourceName) {
 | 
				
			||||||
			reqContainsHugePages = true
 | 
								reqContainsHugePages = true
 | 
				
			||||||
 | 
								if err := validateResourceQuantityHugePageValue(resourceName, quantity, opts); err != nil {
 | 
				
			||||||
 | 
									allErrs = append(allErrs, field.Invalid(fldPath, quantity.String(), err.Error()))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if supportedQoSComputeResources.Has(string(resourceName)) {
 | 
							if supportedQoSComputeResources.Has(string(resourceName)) {
 | 
				
			||||||
			reqContainsCPUOrMemory = true
 | 
								reqContainsCPUOrMemory = true
 | 
				
			||||||
@@ -5396,6 +5404,18 @@ func ValidateResourceRequirements(requirements *core.ResourceRequirements, fldPa
 | 
				
			|||||||
	return allErrs
 | 
						return allErrs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func validateResourceQuantityHugePageValue(name core.ResourceName, quantity resource.Quantity, opts PodValidationOptions) error {
 | 
				
			||||||
 | 
						if !helper.IsHugePageResourceName(name) {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if !opts.AllowIndivisibleHugePagesValues && !helper.IsHugePageResourceValueDivisible(name, quantity) {
 | 
				
			||||||
 | 
							return fmt.Errorf("%s is not positive integer multiple of %s", quantity.String(), name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// validateResourceQuotaScopes ensures that each enumerated hard resource constraint is valid for set of scopes
 | 
					// validateResourceQuotaScopes ensures that each enumerated hard resource constraint is valid for set of scopes
 | 
				
			||||||
func validateResourceQuotaScopes(resourceQuotaSpec *core.ResourceQuotaSpec, opts ResourceQuotaValidationOptions, fld *field.Path) field.ErrorList {
 | 
					func validateResourceQuotaScopes(resourceQuotaSpec *core.ResourceQuotaSpec, opts ResourceQuotaValidationOptions, fld *field.Path) field.ErrorList {
 | 
				
			||||||
	allErrs := field.ErrorList{}
 | 
						allErrs := field.ErrorList{}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4446,7 +4446,7 @@ func TestAlphaLocalStorageCapacityIsolation(t *testing.T) {
 | 
				
			|||||||
				resource.BinarySI),
 | 
									resource.BinarySI),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if errs := ValidateResourceRequirements(&containerLimitCase, field.NewPath("resources")); len(errs) != 0 {
 | 
						if errs := ValidateResourceRequirements(&containerLimitCase, field.NewPath("resources"), PodValidationOptions{}); len(errs) != 0 {
 | 
				
			||||||
		t.Errorf("expected success: %v", errs)
 | 
							t.Errorf("expected success: %v", errs)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -16571,7 +16571,7 @@ func TestValidateOverhead(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tc := range successCase {
 | 
						for _, tc := range successCase {
 | 
				
			||||||
		if errs := validateOverhead(tc.overhead, field.NewPath("overheads")); len(errs) != 0 {
 | 
							if errs := validateOverhead(tc.overhead, field.NewPath("overheads"), PodValidationOptions{}); len(errs) != 0 {
 | 
				
			||||||
			t.Errorf("%q unexpected error: %v", tc.Name, errs)
 | 
								t.Errorf("%q unexpected error: %v", tc.Name, errs)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -16588,7 +16588,7 @@ func TestValidateOverhead(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tc := range errorCase {
 | 
						for _, tc := range errorCase {
 | 
				
			||||||
		if errs := validateOverhead(tc.overhead, field.NewPath("resources")); len(errs) == 0 {
 | 
							if errs := validateOverhead(tc.overhead, field.NewPath("resources"), PodValidationOptions{}); len(errs) == 0 {
 | 
				
			||||||
			t.Errorf("%q expected error", tc.Name)
 | 
								t.Errorf("%q expected error", tc.Name)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -17248,3 +17248,86 @@ func TestValidatePodTemplateSpecSeccomp(t *testing.T) {
 | 
				
			|||||||
		asserttestify.Equal(t, test.expectedErr, err, "TestCase[%d]: %s", i, test.description)
 | 
							asserttestify.Equal(t, test.expectedErr, err, "TestCase[%d]: %s", i, test.description)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestValidateResourceRequirements(t *testing.T) {
 | 
				
			||||||
 | 
						path := field.NewPath("resources")
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name         string
 | 
				
			||||||
 | 
							requirements core.ResourceRequirements
 | 
				
			||||||
 | 
							opts         PodValidationOptions
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "limits and requests of hugepage resource are equal",
 | 
				
			||||||
 | 
								requirements: core.ResourceRequirements{
 | 
				
			||||||
 | 
									Limits: core.ResourceList{
 | 
				
			||||||
 | 
										core.ResourceCPU: resource.MustParse("10"),
 | 
				
			||||||
 | 
										core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Requests: core.ResourceList{
 | 
				
			||||||
 | 
										core.ResourceCPU: resource.MustParse("10"),
 | 
				
			||||||
 | 
										core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								opts: PodValidationOptions{},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "limits and requests of memory resource are equal",
 | 
				
			||||||
 | 
								requirements: core.ResourceRequirements{
 | 
				
			||||||
 | 
									Limits: core.ResourceList{
 | 
				
			||||||
 | 
										core.ResourceMemory: resource.MustParse("2Mi"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Requests: core.ResourceList{
 | 
				
			||||||
 | 
										core.ResourceMemory: resource.MustParse("2Mi"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								opts: PodValidationOptions{},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "limits and requests of cpu resource are equal",
 | 
				
			||||||
 | 
								requirements: core.ResourceRequirements{
 | 
				
			||||||
 | 
									Limits: core.ResourceList{
 | 
				
			||||||
 | 
										core.ResourceCPU: resource.MustParse("10"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Requests: core.ResourceList{
 | 
				
			||||||
 | 
										core.ResourceCPU: resource.MustParse("10"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								opts: PodValidationOptions{},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range tests {
 | 
				
			||||||
 | 
							t.Run(tc.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								if errs := ValidateResourceRequirements(&tc.requirements, path, tc.opts); len(errs) != 0 {
 | 
				
			||||||
 | 
									t.Errorf("unexpected errors: %v", errs)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						errTests := []struct {
 | 
				
			||||||
 | 
							name         string
 | 
				
			||||||
 | 
							requirements core.ResourceRequirements
 | 
				
			||||||
 | 
							opts         PodValidationOptions
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "hugepage resource without cpu or memory",
 | 
				
			||||||
 | 
								requirements: core.ResourceRequirements{
 | 
				
			||||||
 | 
									Limits: core.ResourceList{
 | 
				
			||||||
 | 
										core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Requests: core.ResourceList{
 | 
				
			||||||
 | 
										core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								opts: PodValidationOptions{},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range errTests {
 | 
				
			||||||
 | 
							t.Run(tc.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								if errs := ValidateResourceRequirements(&tc.requirements, path, tc.opts); len(errs) == 0 {
 | 
				
			||||||
 | 
									t.Error("expected errors")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,7 +54,8 @@ func ValidateRuntimeClassUpdate(new, old *node.RuntimeClass) field.ErrorList {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func validateOverhead(overhead *node.Overhead, fldPath *field.Path) field.ErrorList {
 | 
					func validateOverhead(overhead *node.Overhead, fldPath *field.Path) field.ErrorList {
 | 
				
			||||||
	// reuse the ResourceRequirements validation logic
 | 
						// reuse the ResourceRequirements validation logic
 | 
				
			||||||
	return corevalidation.ValidateResourceRequirements(&core.ResourceRequirements{Limits: overhead.PodFixed}, fldPath)
 | 
						return corevalidation.ValidateResourceRequirements(&core.ResourceRequirements{Limits: overhead.PodFixed}, fldPath,
 | 
				
			||||||
 | 
							corevalidation.PodValidationOptions{})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func validateScheduling(s *node.Scheduling, fldPath *field.Path) field.ErrorList {
 | 
					func validateScheduling(s *node.Scheduling, fldPath *field.Path) field.ErrorList {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,7 @@ import (
 | 
				
			|||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"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/runtime"
 | 
						"k8s.io/apimachinery/pkg/runtime"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/util/intstr"
 | 
						"k8s.io/apimachinery/pkg/util/intstr"
 | 
				
			||||||
@@ -247,3 +248,114 @@ func TestDeploymentDefaultGarbageCollectionPolicy(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newDeploymentWithHugePageValue(reousreceName api.ResourceName, value resource.Quantity) *apps.Deployment {
 | 
				
			||||||
 | 
						return &apps.Deployment{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Name:            deploymentName,
 | 
				
			||||||
 | 
								Namespace:       namespace,
 | 
				
			||||||
 | 
								ResourceVersion: "1",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Spec: apps.DeploymentSpec{
 | 
				
			||||||
 | 
								Selector: &metav1.LabelSelector{
 | 
				
			||||||
 | 
									MatchLabels:      map[string]string{"foo": "bar"},
 | 
				
			||||||
 | 
									MatchExpressions: []metav1.LabelSelectorRequirement{},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Strategy: apps.DeploymentStrategy{
 | 
				
			||||||
 | 
									Type: apps.RollingUpdateDeploymentStrategyType,
 | 
				
			||||||
 | 
									RollingUpdate: &apps.RollingUpdateDeployment{
 | 
				
			||||||
 | 
										MaxSurge:       intstr.FromInt(1),
 | 
				
			||||||
 | 
										MaxUnavailable: intstr.FromInt(1),
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Template: api.PodTemplateSpec{
 | 
				
			||||||
 | 
									ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
										Namespace: "default",
 | 
				
			||||||
 | 
										Name:      "foo",
 | 
				
			||||||
 | 
										Labels:    map[string]string{"foo": "bar"},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
 | 
										RestartPolicy: api.RestartPolicyAlways,
 | 
				
			||||||
 | 
										DNSPolicy:     api.DNSDefault,
 | 
				
			||||||
 | 
										Containers: []api.Container{{
 | 
				
			||||||
 | 
											Name:                     fakeImageName,
 | 
				
			||||||
 | 
											Image:                    fakeImage,
 | 
				
			||||||
 | 
											ImagePullPolicy:          api.PullNever,
 | 
				
			||||||
 | 
											TerminationMessagePolicy: api.TerminationMessageReadFile,
 | 
				
			||||||
 | 
											Resources: api.ResourceRequirements{
 | 
				
			||||||
 | 
												Requests: api.ResourceList{
 | 
				
			||||||
 | 
													api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
 | 
				
			||||||
 | 
													api.ResourceName(reousreceName):   value,
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
												Limits: api.ResourceList{
 | 
				
			||||||
 | 
													api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
 | 
				
			||||||
 | 
													api.ResourceName(reousreceName):   value,
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											}},
 | 
				
			||||||
 | 
										}},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDeploymentStrategyValidate(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name       string
 | 
				
			||||||
 | 
							deployment *apps.Deployment
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:       "validation on a new deployment with indivisible hugepages values",
 | 
				
			||||||
 | 
								deployment: newDeploymentWithHugePageValue(api.ResourceHugePagesPrefix+"2Mi", resource.MustParse("2.1Mi")),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range tests {
 | 
				
			||||||
 | 
							t.Run(tc.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								if errs := Strategy.Validate(genericapirequest.NewContext(), tc.deployment); len(errs) == 0 {
 | 
				
			||||||
 | 
									t.Error("expected failure")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDeploymentStrategyValidateUpdate(t *testing.T) {
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name          string
 | 
				
			||||||
 | 
							newDeployment *apps.Deployment
 | 
				
			||||||
 | 
							oldDeployment *apps.Deployment
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:          "validation on an existing deployment with indivisible hugepages values to a new deployment with indivisible hugepages values",
 | 
				
			||||||
 | 
								newDeployment: newDeploymentWithHugePageValue(api.ResourceHugePagesPrefix+"2Mi", resource.MustParse("2.1Mi")),
 | 
				
			||||||
 | 
								oldDeployment: newDeploymentWithHugePageValue(api.ResourceHugePagesPrefix+"1Gi", resource.MustParse("1.1Gi")),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range tests {
 | 
				
			||||||
 | 
							t.Run(tc.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								if errs := Strategy.ValidateUpdate(genericapirequest.NewContext(), tc.newDeployment, tc.oldDeployment); len(errs) != 0 {
 | 
				
			||||||
 | 
									t.Errorf("unexpected error:%v", errs)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						errTests := []struct {
 | 
				
			||||||
 | 
							name          string
 | 
				
			||||||
 | 
							newDeployment *apps.Deployment
 | 
				
			||||||
 | 
							oldDeployment *apps.Deployment
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:          "validation on an existing deployment with divisible hugepages values to a new deployment with indivisible hugepages values",
 | 
				
			||||||
 | 
								newDeployment: newDeploymentWithHugePageValue(api.ResourceHugePagesPrefix+"2Mi", resource.MustParse("2.1Mi")),
 | 
				
			||||||
 | 
								oldDeployment: newDeploymentWithHugePageValue(api.ResourceHugePagesPrefix+"1Gi", resource.MustParse("2Gi")),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range errTests {
 | 
				
			||||||
 | 
							t.Run(tc.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								if errs := Strategy.ValidateUpdate(genericapirequest.NewContext(), tc.newDeployment, tc.oldDeployment); len(errs) == 0 {
 | 
				
			||||||
 | 
									t.Error("expected failure")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1197,3 +1197,187 @@ func createPodWithGenericEphemeralVolume() *api.Pod {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func newPodtWithHugePageValue(reousreceName api.ResourceName, value resource.Quantity) *api.Pod {
 | 
				
			||||||
 | 
						return &api.Pod{
 | 
				
			||||||
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
								Namespace:       "default",
 | 
				
			||||||
 | 
								Name:            "foo",
 | 
				
			||||||
 | 
								ResourceVersion: "1",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							Spec: api.PodSpec{
 | 
				
			||||||
 | 
								RestartPolicy: api.RestartPolicyAlways,
 | 
				
			||||||
 | 
								DNSPolicy:     api.DNSDefault,
 | 
				
			||||||
 | 
								Containers: []api.Container{{
 | 
				
			||||||
 | 
									Name:                     "foo",
 | 
				
			||||||
 | 
									Image:                    "image",
 | 
				
			||||||
 | 
									ImagePullPolicy:          "IfNotPresent",
 | 
				
			||||||
 | 
									TerminationMessagePolicy: "File",
 | 
				
			||||||
 | 
									Resources: api.ResourceRequirements{
 | 
				
			||||||
 | 
										Requests: api.ResourceList{
 | 
				
			||||||
 | 
											api.ResourceCPU: resource.MustParse("10"),
 | 
				
			||||||
 | 
											reousreceName:   value,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										Limits: api.ResourceList{
 | 
				
			||||||
 | 
											api.ResourceCPU: resource.MustParse("10"),
 | 
				
			||||||
 | 
											reousreceName:   value,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									}},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestPodStrategyValidate(t *testing.T) {
 | 
				
			||||||
 | 
						const containerName = "container"
 | 
				
			||||||
 | 
						errTest := []struct {
 | 
				
			||||||
 | 
							name string
 | 
				
			||||||
 | 
							pod  *api.Pod
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "a new pod setting container with indivisible hugepages values",
 | 
				
			||||||
 | 
								pod:  newPodtWithHugePageValue(api.ResourceHugePagesPrefix+"1Mi", resource.MustParse("1.1Mi")),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "a new pod setting init-container with indivisible hugepages values",
 | 
				
			||||||
 | 
								pod: &api.Pod{
 | 
				
			||||||
 | 
									ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
										Namespace: "default",
 | 
				
			||||||
 | 
										Name:      "foo",
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
 | 
										RestartPolicy: api.RestartPolicyAlways,
 | 
				
			||||||
 | 
										DNSPolicy:     api.DNSDefault,
 | 
				
			||||||
 | 
										InitContainers: []api.Container{{
 | 
				
			||||||
 | 
											Name:                     containerName,
 | 
				
			||||||
 | 
											Image:                    "image",
 | 
				
			||||||
 | 
											ImagePullPolicy:          "IfNotPresent",
 | 
				
			||||||
 | 
											TerminationMessagePolicy: "File",
 | 
				
			||||||
 | 
											Resources: api.ResourceRequirements{
 | 
				
			||||||
 | 
												Requests: api.ResourceList{
 | 
				
			||||||
 | 
													api.ResourceName(api.ResourceHugePagesPrefix + "64Ki"): resource.MustParse("127Ki"),
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
												Limits: api.ResourceList{
 | 
				
			||||||
 | 
													api.ResourceName(api.ResourceHugePagesPrefix + "64Ki"): resource.MustParse("127Ki"),
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											}},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "a new pod setting init-container with indivisible hugepages values while container with divisible hugepages values",
 | 
				
			||||||
 | 
								pod: &api.Pod{
 | 
				
			||||||
 | 
									ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
										Namespace: "default",
 | 
				
			||||||
 | 
										Name:      "foo",
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
 | 
										RestartPolicy: api.RestartPolicyAlways,
 | 
				
			||||||
 | 
										DNSPolicy:     api.DNSDefault,
 | 
				
			||||||
 | 
										InitContainers: []api.Container{{
 | 
				
			||||||
 | 
											Name:                     containerName,
 | 
				
			||||||
 | 
											Image:                    "image",
 | 
				
			||||||
 | 
											ImagePullPolicy:          "IfNotPresent",
 | 
				
			||||||
 | 
											TerminationMessagePolicy: "File",
 | 
				
			||||||
 | 
											Resources: api.ResourceRequirements{
 | 
				
			||||||
 | 
												Requests: api.ResourceList{
 | 
				
			||||||
 | 
													api.ResourceName(api.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("5.1Mi"),
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
												Limits: api.ResourceList{
 | 
				
			||||||
 | 
													api.ResourceName(api.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("5.1Mi"),
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											}},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
										Containers: []api.Container{{
 | 
				
			||||||
 | 
											Name:                     containerName,
 | 
				
			||||||
 | 
											Image:                    "image",
 | 
				
			||||||
 | 
											ImagePullPolicy:          "IfNotPresent",
 | 
				
			||||||
 | 
											TerminationMessagePolicy: "File",
 | 
				
			||||||
 | 
											Resources: api.ResourceRequirements{
 | 
				
			||||||
 | 
												Requests: api.ResourceList{
 | 
				
			||||||
 | 
													api.ResourceName(api.ResourceHugePagesPrefix + "1Gi"): resource.MustParse("2Gi"),
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
												Limits: api.ResourceList{
 | 
				
			||||||
 | 
													api.ResourceName(api.ResourceHugePagesPrefix + "1Gi"): resource.MustParse("2Gi"),
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											}},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range errTest {
 | 
				
			||||||
 | 
							t.Run(tc.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								if errs := Strategy.Validate(genericapirequest.NewContext(), tc.pod); len(errs) == 0 {
 | 
				
			||||||
 | 
									t.Error("expected failure")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							name string
 | 
				
			||||||
 | 
							pod  *api.Pod
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "a new pod setting container with divisible hugepages values",
 | 
				
			||||||
 | 
								pod: &api.Pod{
 | 
				
			||||||
 | 
									ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
 | 
										Namespace: "default",
 | 
				
			||||||
 | 
										Name:      "foo",
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Spec: api.PodSpec{
 | 
				
			||||||
 | 
										RestartPolicy: api.RestartPolicyAlways,
 | 
				
			||||||
 | 
										DNSPolicy:     api.DNSDefault,
 | 
				
			||||||
 | 
										Containers: []api.Container{{
 | 
				
			||||||
 | 
											Name:                     containerName,
 | 
				
			||||||
 | 
											Image:                    "image",
 | 
				
			||||||
 | 
											ImagePullPolicy:          "IfNotPresent",
 | 
				
			||||||
 | 
											TerminationMessagePolicy: "File",
 | 
				
			||||||
 | 
											Resources: api.ResourceRequirements{
 | 
				
			||||||
 | 
												Requests: api.ResourceList{
 | 
				
			||||||
 | 
													api.ResourceName(api.ResourceCPU):                     resource.MustParse("10"),
 | 
				
			||||||
 | 
													api.ResourceName(api.ResourceHugePagesPrefix + "1Mi"): resource.MustParse("2Mi"),
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
												Limits: api.ResourceList{
 | 
				
			||||||
 | 
													api.ResourceName(api.ResourceCPU):                     resource.MustParse("10"),
 | 
				
			||||||
 | 
													api.ResourceName(api.ResourceHugePagesPrefix + "1Mi"): resource.MustParse("2Mi"),
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
 | 
											}},
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range tests {
 | 
				
			||||||
 | 
							t.Run(tc.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								if errs := Strategy.Validate(genericapirequest.NewContext(), tc.pod); len(errs) != 0 {
 | 
				
			||||||
 | 
									t.Errorf("unexpected error:%v", errs)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestPodStrategyValidateUpdate(t *testing.T) {
 | 
				
			||||||
 | 
						test := []struct {
 | 
				
			||||||
 | 
							name   string
 | 
				
			||||||
 | 
							newPod *api.Pod
 | 
				
			||||||
 | 
							oldPod *api.Pod
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:   "an existing pod with indivisible hugepages values to a new pod with indivisible hugepages values",
 | 
				
			||||||
 | 
								newPod: newPodtWithHugePageValue(api.ResourceHugePagesPrefix+"2Mi", resource.MustParse("2.1Mi")),
 | 
				
			||||||
 | 
								oldPod: newPodtWithHugePageValue(api.ResourceHugePagesPrefix+"2Mi", resource.MustParse("2.1Mi")),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range test {
 | 
				
			||||||
 | 
							t.Run(tc.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								if errs := Strategy.ValidateUpdate(genericapirequest.NewContext(), tc.newPod, tc.oldPod); len(errs) != 0 {
 | 
				
			||||||
 | 
									t.Errorf("unexpected error:%v", errs)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user