mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-27 21:26:03 +00:00
Downward API hugepages
This commit is contained in:
@@ -12,9 +12,13 @@ go_library(
|
||||
importpath = "k8s.io/kubernetes/pkg/api/pod",
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/apis/core/helper:go_default_library",
|
||||
"//pkg/apis/core/v1/helper:go_default_library",
|
||||
"//pkg/apis/core/validation:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
],
|
||||
)
|
||||
|
@@ -21,8 +21,13 @@ import (
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/helper"
|
||||
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
@@ -283,6 +288,91 @@ func UpdatePodCondition(status *api.PodStatus, condition *api.PodCondition) bool
|
||||
return !isEqual
|
||||
}
|
||||
|
||||
// usesHugePagesInProjectedVolume returns true if hugepages are used in downward api for volume
|
||||
func usesHugePagesInProjectedVolume(podSpec *api.PodSpec) bool {
|
||||
// determine if any container is using hugepages in downward api volume
|
||||
for _, volumeSource := range podSpec.Volumes {
|
||||
if volumeSource.DownwardAPI != nil {
|
||||
for _, item := range volumeSource.DownwardAPI.Items {
|
||||
if item.ResourceFieldRef != nil {
|
||||
if strings.HasPrefix(item.ResourceFieldRef.Resource, "requests.hugepages-") || strings.HasPrefix(item.ResourceFieldRef.Resource, "limits.hugepages-") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// usesHugePagesInProjectedEnv returns true if hugepages are used in downward api for volume
|
||||
func usesHugePagesInProjectedEnv(item api.Container) bool {
|
||||
for _, env := range item.Env {
|
||||
if env.ValueFrom != nil {
|
||||
if env.ValueFrom.ResourceFieldRef != nil {
|
||||
if strings.HasPrefix(env.ValueFrom.ResourceFieldRef.Resource, "requests.hugepages-") || strings.HasPrefix(env.ValueFrom.ResourceFieldRef.Resource, "limits.hugepages-") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// usesMultipleHugePageResources returns true if the pod spec uses more than
|
||||
// one size of hugepage
|
||||
func usesMultipleHugePageResources(podSpec *api.PodSpec) bool {
|
||||
hugePageResources := sets.NewString()
|
||||
resourceSet := helper.ToPodResourcesSet(podSpec)
|
||||
for resourceStr := range resourceSet {
|
||||
if v1helper.IsHugePageResourceName(v1.ResourceName(resourceStr)) {
|
||||
hugePageResources.Insert(resourceStr)
|
||||
}
|
||||
}
|
||||
return len(hugePageResources) > 1
|
||||
}
|
||||
|
||||
// GetValidationOptionsFromPodSpec returns validation options based on pod specs
|
||||
func GetValidationOptionsFromPodSpec(podSpec, oldPodSpec *api.PodSpec) apivalidation.PodValidationOptions {
|
||||
// default pod validation options based on feature gate
|
||||
opts := validation.PodValidationOptions{
|
||||
// Allow multiple huge pages on pod create if feature is enabled
|
||||
AllowMultipleHugePageResources: utilfeature.DefaultFeatureGate.Enabled(features.HugePageStorageMediumSize),
|
||||
// Allow pod spec to use hugepages in downward API if feature is enabled
|
||||
AllowDownwardAPIHugePages: utilfeature.DefaultFeatureGate.Enabled(features.DownwardAPIHugePages),
|
||||
}
|
||||
// if we are not doing an update operation, just return with default options
|
||||
if oldPodSpec == nil {
|
||||
return opts
|
||||
}
|
||||
// if old spec used multiple huge page sizes, we must allow it
|
||||
opts.AllowMultipleHugePageResources = opts.AllowMultipleHugePageResources || usesMultipleHugePageResources(oldPodSpec)
|
||||
// if old spec used hugepages in downward api, we must allow it
|
||||
opts.AllowDownwardAPIHugePages = opts.AllowDownwardAPIHugePages || usesHugePagesInProjectedVolume(oldPodSpec)
|
||||
// determine if any container is using hugepages in env var
|
||||
if !opts.AllowDownwardAPIHugePages {
|
||||
VisitContainers(oldPodSpec, AllContainers, func(c *api.Container, containerType ContainerType) bool {
|
||||
opts.AllowDownwardAPIHugePages = opts.AllowDownwardAPIHugePages || usesHugePagesInProjectedEnv(*c)
|
||||
return !opts.AllowDownwardAPIHugePages
|
||||
})
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
// GetValidationOptionsFromPodTemplate will return pod validation options for specified template.
|
||||
func GetValidationOptionsFromPodTemplate(podTemplate, oldPodTemplate *api.PodTemplateSpec) apivalidation.PodValidationOptions {
|
||||
var newPodSpec, oldPodSpec *api.PodSpec
|
||||
// we have to be careful about nil pointers here
|
||||
// replication controller in particular is prone to passing nil
|
||||
if podTemplate != nil {
|
||||
newPodSpec = &podTemplate.Spec
|
||||
}
|
||||
if oldPodTemplate != nil {
|
||||
oldPodSpec = &oldPodTemplate.Spec
|
||||
}
|
||||
return GetValidationOptionsFromPodSpec(newPodSpec, oldPodSpec)
|
||||
}
|
||||
|
||||
// DropDisabledTemplateFields removes disabled fields from the pod template metadata and spec.
|
||||
// This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a PodTemplateSpec
|
||||
func DropDisabledTemplateFields(podTemplate, oldPodTemplate *api.PodTemplateSpec) {
|
||||
|
@@ -98,6 +98,7 @@ go_test(
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/api/pod:go_default_library",
|
||||
"//pkg/api/testing/compat:go_default_library",
|
||||
"//pkg/apis/apps:go_default_library",
|
||||
"//pkg/apis/apps/v1:go_default_library",
|
||||
|
@@ -19,14 +19,14 @@ package testing
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
podutil "k8s.io/kubernetes/pkg/api/pod"
|
||||
"k8s.io/kubernetes/pkg/api/testing/compat"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
|
||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||
"k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
)
|
||||
|
||||
func TestCompatibility_v1_PodSecurityContext(t *testing.T) {
|
||||
@@ -159,7 +159,8 @@ func TestCompatibility_v1_PodSecurityContext(t *testing.T) {
|
||||
}
|
||||
|
||||
validator := func(obj runtime.Object) field.ErrorList {
|
||||
return validation.ValidatePodSpec(&(obj.(*api.Pod).Spec), &(obj.(*api.Pod).ObjectMeta), field.NewPath("spec"))
|
||||
opts := podutil.GetValidationOptionsFromPodSpec(&(obj.(*api.Pod).Spec), nil)
|
||||
return validation.ValidatePodSpec(&(obj.(*api.Pod).Spec), &(obj.(*api.Pod).ObjectMeta), field.NewPath("spec"), opts)
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
|
@@ -20,8 +20,9 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
@@ -193,7 +194,20 @@ func ExtractContainerResourceValue(fs *v1.ResourceFieldSelector, container *v1.C
|
||||
case "requests.ephemeral-storage":
|
||||
return convertResourceEphemeralStorageToString(container.Resources.Requests.StorageEphemeral(), divisor)
|
||||
}
|
||||
|
||||
// handle extended standard resources with dynamic names
|
||||
// example: requests.hugepages-<pageSize> or limits.hugepages-<pageSize>
|
||||
if strings.HasPrefix(fs.Resource, "requests.") {
|
||||
resourceName := v1.ResourceName(strings.TrimPrefix(fs.Resource, "requests."))
|
||||
if IsHugePageResourceName(resourceName) {
|
||||
return convertResourceHugePagesToString(container.Resources.Requests.Name(resourceName, resource.BinarySI), divisor)
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(fs.Resource, "limits.") {
|
||||
resourceName := v1.ResourceName(strings.TrimPrefix(fs.Resource, "limits."))
|
||||
if IsHugePageResourceName(resourceName) {
|
||||
return convertResourceHugePagesToString(container.Resources.Limits.Name(resourceName, resource.BinarySI), divisor)
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("unsupported container resource : %v", fs.Resource)
|
||||
}
|
||||
|
||||
@@ -211,6 +225,13 @@ func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Q
|
||||
return strconv.FormatInt(m, 10), nil
|
||||
}
|
||||
|
||||
// convertResourceHugePagesToString converts hugepages value to the format of divisor and returns
|
||||
// ceiling of the value.
|
||||
func convertResourceHugePagesToString(hugePages *resource.Quantity, divisor resource.Quantity) (string, error) {
|
||||
m := int64(math.Ceil(float64(hugePages.Value()) / float64(divisor.Value())))
|
||||
return strconv.FormatInt(m, 10), nil
|
||||
}
|
||||
|
||||
// convertResourceEphemeralStorageToString converts ephemeral storage value to the format of divisor and returns
|
||||
// ceiling of the value.
|
||||
func convertResourceEphemeralStorageToString(ephemeralStorage *resource.Quantity, divisor resource.Quantity) (string, error) {
|
||||
@@ -240,6 +261,8 @@ func MergeContainerResourceLimits(container *v1.Container,
|
||||
if container.Resources.Limits == nil {
|
||||
container.Resources.Limits = make(v1.ResourceList)
|
||||
}
|
||||
// NOTE: we exclude hugepages-* resources because hugepages are never overcommitted.
|
||||
// This means that the container always has a limit specified.
|
||||
for _, resource := range []v1.ResourceName{v1.ResourceCPU, v1.ResourceMemory, v1.ResourceEphemeralStorage} {
|
||||
if quantity, exists := container.Resources.Limits[resource]; !exists || quantity.IsZero() {
|
||||
if cap, exists := allocatable[resource]; exists {
|
||||
@@ -248,3 +271,9 @@ func MergeContainerResourceLimits(container *v1.Container,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsHugePageResourceName returns true if the resource name has the huge page
|
||||
// resource prefix.
|
||||
func IsHugePageResourceName(name v1.ResourceName) bool {
|
||||
return strings.HasPrefix(string(name), v1.ResourceHugePagesPrefix)
|
||||
}
|
||||
|
Reference in New Issue
Block a user