Skip to content

Commit

Permalink
Merge pull request kubernetes#84051 from bart0sh/PR0079-multiple-size…
Browse files Browse the repository at this point in the history
…s-hugepages

Implement support for multiple sizes huge pages
  • Loading branch information
k8s-ci-robot authored Feb 25, 2020
2 parents b5e95fc + 03ecc20 commit 851efa8
Show file tree
Hide file tree
Showing 18 changed files with 955 additions and 180 deletions.
7 changes: 4 additions & 3 deletions pkg/apis/core/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -583,9 +583,10 @@ type StorageMedium string

// These are the valid value for StorageMedium
const (
StorageMediumDefault StorageMedium = "" // use whatever the default is for the node
StorageMediumMemory StorageMedium = "Memory" // use memory (tmpfs)
StorageMediumHugePages StorageMedium = "HugePages" // use hugepages
StorageMediumDefault StorageMedium = "" // use whatever the default is for the node
StorageMediumMemory StorageMedium = "Memory" // use memory (tmpfs)
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.
Expand Down
21 changes: 21 additions & 0 deletions pkg/apis/core/v1/helper/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,27 @@ func HugePageUnitSizeFromByteSize(size int64) (string, error) {
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
// namespace and is not hugepages.
func IsOvercommitAllowed(name v1.ResourceName) bool {
Expand Down
67 changes: 67 additions & 0 deletions pkg/apis/core/v1/helper/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,73 @@ func TestHugePageSizeFromResourceName(t *testing.T) {
}
}

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

func TestIsOvercommitAllowed(t *testing.T) {
testCases := []struct {
resourceName v1.ResourceName
Expand Down
50 changes: 35 additions & 15 deletions pkg/apis/core/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -3083,8 +3083,33 @@ func validateContainersOnlyForPod(containers []core.Container, fldPath *field.Pa
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.
func ValidatePod(pod *core.Pod) field.ErrorList {
func ValidatePod(pod *core.Pod, opts PodValidationOptions) field.ErrorList {
fldPath := field.NewPath("metadata")
allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, fldPath)
allErrs = append(allErrs, ValidatePodSpecificAnnotations(pod.ObjectMeta.Annotations, &pod.Spec, fldPath.Child("annotations"))...)
Expand All @@ -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.InitContainers, specPath.Child("initContainers"))...)

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, "must use a single hugepage size in a pod spec"))
if !opts.AllowMultipleHugePageResources {
allErrs = append(allErrs, ValidatePodSingleHugePageResources(pod, specPath)...)
}

podIPsField := field.NewPath("status", "podIPs")
Expand Down Expand Up @@ -3679,8 +3695,8 @@ func ValidateContainerUpdates(newContainers, oldContainers []core.Container, fld
}

// ValidatePodCreate validates a pod in the context of its initial create
func ValidatePodCreate(pod *core.Pod) field.ErrorList {
allErrs := ValidatePod(pod)
func ValidatePodCreate(pod *core.Pod, opts PodValidationOptions) field.ErrorList {
allErrs := ValidatePod(pod, opts)

fldPath := field.NewPath("spec")
// EphemeralContainers can only be set on update using the ephemeralcontainers subresource
Expand All @@ -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
// 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")
allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath)
allErrs = append(allErrs, ValidatePodSpecificAnnotationUpdates(newPod, oldPod, fldPath.Child("annotations"))...)
specPath := field.NewPath("spec")

if !opts.AllowMultipleHugePageResources {
allErrs = append(allErrs, ValidatePodSingleHugePageResources(newPod, specPath)...)
}

// validate updateable fields:
// 1. spec.containers[*].image
// 2. spec.initContainers[*].image
Expand Down
Loading

0 comments on commit 851efa8

Please sign in to comment.