mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
Add request value verification for hugepage
This commit is contained in:
parent
466e730259
commit
e162fcc1bf
@ -345,6 +345,52 @@ func usesMultipleHugePageResources(podSpec *api.PodSpec) bool {
|
||||
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
|
||||
func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, podMeta, oldPodMeta *metav1.ObjectMeta) apivalidation.PodValidationOptions {
|
||||
// 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
|
||||
AllowDownwardAPIHugePages: utilfeature.DefaultFeatureGate.Enabled(features.DownwardAPIHugePages),
|
||||
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 {
|
||||
@ -368,12 +416,16 @@ func GetValidationOptionsFromPodSpecAndMeta(podSpec, oldPodSpec *api.PodSpec, po
|
||||
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 {
|
||||
// This is an update, so validate only if the existing object was valid.
|
||||
_, err := helper.GetDeletionCostFromPodAnnotations(oldPodMeta.Annotations)
|
||||
opts.AllowInvalidPodDeletionCost = err != nil
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,21 @@ func IsHugePageResourceName(name core.ResourceName) bool {
|
||||
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
|
||||
// related huge page resource prefix.
|
||||
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) {
|
||||
testCases := []struct {
|
||||
pageSize resource.Quantity
|
||||
|
@ -292,9 +292,9 @@ func ValidateRuntimeClassName(name string, fldPath *field.Path) field.ErrorList
|
||||
}
|
||||
|
||||
// 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
|
||||
return ValidateResourceRequirements(&core.ResourceRequirements{Limits: overhead}, fldPath)
|
||||
return ValidateResourceRequirements(&core.ResourceRequirements{Limits: overhead}, fldPath, opts)
|
||||
}
|
||||
|
||||
// Validates that given value is not negative.
|
||||
@ -2880,7 +2880,7 @@ func validateContainers(containers []core.Container, isInitContainers bool, volu
|
||||
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, 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"))...)
|
||||
}
|
||||
|
||||
@ -3193,6 +3193,8 @@ type PodValidationOptions struct {
|
||||
AllowDownwardAPIHugePages bool
|
||||
// Allow invalid pod-deletion-cost annotation value for backward compatibility.
|
||||
AllowInvalidPodDeletionCost bool
|
||||
// Allow pod spec to use non-integer multiple of huge page unit size
|
||||
AllowIndivisibleHugePagesValues bool
|
||||
}
|
||||
|
||||
// ValidatePodSingleHugePageResources checks if there are multiple huge
|
||||
@ -3366,7 +3368,7 @@ func ValidatePodSpec(spec *core.PodSpec, podMeta *metav1.ObjectMeta, fldPath *fi
|
||||
}
|
||||
|
||||
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
|
||||
@ -5321,7 +5323,7 @@ func validateBasicResource(quantity resource.Quantity, fldPath *field.Path) fiel
|
||||
}
|
||||
|
||||
// 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{}
|
||||
limPath := fldPath.Child("limits")
|
||||
reqPath := fldPath.Child("requests")
|
||||
@ -5341,6 +5343,9 @@ func ValidateResourceRequirements(requirements *core.ResourceRequirements, fldPa
|
||||
|
||||
if helper.IsHugePageResourceName(resourceName) {
|
||||
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)) {
|
||||
@ -5368,6 +5373,9 @@ func ValidateResourceRequirements(requirements *core.ResourceRequirements, fldPa
|
||||
}
|
||||
if helper.IsHugePageResourceName(resourceName) {
|
||||
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)) {
|
||||
reqContainsCPUOrMemory = true
|
||||
@ -5381,6 +5389,18 @@ func ValidateResourceRequirements(requirements *core.ResourceRequirements, fldPa
|
||||
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
|
||||
func validateResourceQuotaScopes(resourceQuotaSpec *core.ResourceQuotaSpec, opts ResourceQuotaValidationOptions, fld *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
@ -4446,7 +4446,7 @@ func TestAlphaLocalStorageCapacityIsolation(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -16410,7 +16410,7 @@ func TestValidateOverhead(t *testing.T) {
|
||||
},
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -16427,7 +16427,7 @@ func TestValidateOverhead(t *testing.T) {
|
||||
},
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -17087,3 +17087,86 @@ func TestValidatePodTemplateSpecSeccomp(t *testing.T) {
|
||||
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 {
|
||||
// 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 {
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user