mirror of
https://github.com/kubernetes/kubernetes.git
synced 2026-07-16 13:14:10 -04:00
Fix handling of empty pod-level resources
This commit is contained in:
parent
473f341c9e
commit
eff8673537
10 changed files with 522 additions and 10 deletions
|
|
@ -22,6 +22,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/conversion"
|
||||
|
|
@ -29,9 +30,37 @@ import (
|
|||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
resourcehelper "k8s.io/component-helpers/resource"
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
)
|
||||
|
||||
// IsPodLevelResourcesSet check if PodLevelResources pod-level resources are set.
|
||||
// It returns true if either the Requests or Limits maps are non-empty.
|
||||
// Note: keep this in sync with k8s.io/component-helpers/resource.IsPodLevelResourcesSet
|
||||
func IsPodLevelResourcesSet(pod *core.Pod) bool {
|
||||
if pod.Spec.Resources == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if (len(pod.Spec.Resources.Requests) + len(pod.Spec.Resources.Limits)) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for name := range pod.Spec.Resources.Requests {
|
||||
if resourcehelper.IsSupportedPodLevelResource(v1.ResourceName(name)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for name := range pod.Spec.Resources.Limits {
|
||||
if resourcehelper.IsSupportedPodLevelResource(v1.ResourceName(name)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsHugePageResourceName returns true if the resource name has the huge page
|
||||
// resource prefix.
|
||||
func IsHugePageResourceName(name core.ResourceName) bool {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/helper"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
|
|
@ -45,7 +46,7 @@ func GetPodQOS(pod *core.Pod) core.PodQOSClass {
|
|||
func ComputePodQOS(pod *core.Pod) core.PodQOSClass {
|
||||
// When pod-level resources are specified, we use them to determine QoS class.
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources) &&
|
||||
pod.Spec.Resources != nil {
|
||||
(pod.Spec.Resources != nil && (!utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResourcesFixKubeletQOSClass) || helper.IsPodLevelResourcesSet(pod))) {
|
||||
return requirementsQOS(pod.Spec.Resources)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
resourcehelper "k8s.io/component-helpers/resource"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
)
|
||||
|
||||
|
|
@ -42,7 +43,7 @@ func GetPodQOS(pod *v1.Pod) v1.PodQOSClass {
|
|||
func ComputePodQOS(pod *v1.Pod) v1.PodQOSClass {
|
||||
// When pod-level resources are specified, we use them to determine QoS class.
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResources) &&
|
||||
pod.Spec.Resources != nil {
|
||||
(pod.Spec.Resources != nil && (!utilfeature.DefaultFeatureGate.Enabled(features.PodLevelResourcesFixKubeletQOSClass) || resourcehelper.IsPodLevelResourcesSet(pod))) {
|
||||
return requirementsQOS(pod.Spec.Resources)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
resourcehelper "k8s.io/component-helpers/resource"
|
||||
"k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/core/helper/qos"
|
||||
corev1 "k8s.io/kubernetes/pkg/apis/core/v1"
|
||||
|
|
@ -35,6 +36,13 @@ import (
|
|||
)
|
||||
|
||||
func TestComputePodQOS(t *testing.T) {
|
||||
type testCase struct {
|
||||
pod *v1.Pod
|
||||
expected v1.PodQOSClass
|
||||
podLevelResourcesEnabled bool
|
||||
podLevelResourcesFixKubeletQOSClassEnabled bool
|
||||
}
|
||||
|
||||
guaranteedResources := v1.ResourceRequirements{
|
||||
// Ephemeral storage request should not affect QOS.
|
||||
Requests: v1.ResourceList{
|
||||
|
|
@ -78,12 +86,6 @@ func TestComputePodQOS(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
type testCase struct {
|
||||
pod *v1.Pod
|
||||
expected v1.PodQOSClass
|
||||
podLevelResourcesEnabled bool
|
||||
}
|
||||
|
||||
// Container level test cases
|
||||
singleContainerTests := []testCase{{
|
||||
pod: newPod("guaranteed", []v1.Container{
|
||||
|
|
@ -178,6 +180,7 @@ func TestComputePodQOS(t *testing.T) {
|
|||
plrTest := tc
|
||||
plrTest.pod = pod
|
||||
plrTest.podLevelResourcesEnabled = true
|
||||
plrTest.podLevelResourcesFixKubeletQOSClassEnabled = true
|
||||
tests = append(tests, plrTest)
|
||||
}
|
||||
|
||||
|
|
@ -188,10 +191,16 @@ func TestComputePodQOS(t *testing.T) {
|
|||
pod.Name = "pod-" + pod.Name + "-plr"
|
||||
pod.Spec.Resources = pod.Spec.Containers[0].Resources.DeepCopy()
|
||||
pod.Spec.Containers[0].Resources = guaranteedResources
|
||||
expectedQOS := tc.expected
|
||||
if !resourcehelper.IsPodLevelResourcesSet(pod) {
|
||||
expectedQOS = v1.PodQOSGuaranteed
|
||||
pod.Name += "-fallback"
|
||||
}
|
||||
tests = append(tests, testCase{
|
||||
pod: pod,
|
||||
expected: tc.expected,
|
||||
expected: expectedQOS,
|
||||
podLevelResourcesEnabled: true,
|
||||
podLevelResourcesFixKubeletQOSClassEnabled: true,
|
||||
})
|
||||
|
||||
// variant 2: PLR disabled, guaranteed pod
|
||||
|
|
@ -202,12 +211,175 @@ func TestComputePodQOS(t *testing.T) {
|
|||
pod: pod,
|
||||
expected: tc.expected,
|
||||
podLevelResourcesEnabled: false,
|
||||
podLevelResourcesFixKubeletQOSClassEnabled: false,
|
||||
})
|
||||
}
|
||||
|
||||
// Explicit empty and zero pod-level resource test cases
|
||||
explicitPLRTests := []testCase{
|
||||
{
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "empty-pod-level-resources-should-fallback-to-containers"},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "guaranteed", Resources: v1.ResourceRequirements{Requests: getResourceList("100m", "100Mi"), Limits: getResourceList("100m", "100Mi")}},
|
||||
},
|
||||
Resources: &v1.ResourceRequirements{
|
||||
Requests: v1.ResourceList{},
|
||||
Limits: v1.ResourceList{},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: v1.PodQOSGuaranteed,
|
||||
podLevelResourcesEnabled: true,
|
||||
podLevelResourcesFixKubeletQOSClassEnabled: true,
|
||||
},
|
||||
{
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "completely-empty-pod-level-resources-should-fallback-to-containers"},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "guaranteed", Resources: v1.ResourceRequirements{Requests: getResourceList("100m", "100Mi"), Limits: getResourceList("100m", "100Mi")}},
|
||||
},
|
||||
Resources: &v1.ResourceRequirements{},
|
||||
},
|
||||
},
|
||||
expected: v1.PodQOSGuaranteed,
|
||||
podLevelResourcesEnabled: true,
|
||||
podLevelResourcesFixKubeletQOSClassEnabled: true,
|
||||
},
|
||||
{
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "completely-empty-pod-level-resources-old-behavior"},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "guaranteed", Resources: v1.ResourceRequirements{Requests: getResourceList("100m", "100Mi"), Limits: getResourceList("100m", "100Mi")}},
|
||||
},
|
||||
Resources: &v1.ResourceRequirements{},
|
||||
},
|
||||
},
|
||||
expected: v1.PodQOSBestEffort,
|
||||
podLevelResourcesEnabled: true,
|
||||
podLevelResourcesFixKubeletQOSClassEnabled: false,
|
||||
},
|
||||
{
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "nil-pod-level-resources-should-fallback-to-containers"},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "guaranteed", Resources: v1.ResourceRequirements{Requests: getResourceList("100m", "100Mi"), Limits: getResourceList("100m", "100Mi")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: v1.PodQOSGuaranteed,
|
||||
podLevelResourcesEnabled: true,
|
||||
podLevelResourcesFixKubeletQOSClassEnabled: true,
|
||||
},
|
||||
{
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "zero-pod-level-resources-should-not-fallback-to-containers"},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "guaranteed", Resources: v1.ResourceRequirements{Requests: getResourceList("100m", "100Mi"), Limits: getResourceList("100m", "100Mi")}},
|
||||
},
|
||||
Resources: &v1.ResourceRequirements{
|
||||
Requests: getResourceList("0", "0"),
|
||||
Limits: getResourceList("0", "0"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: v1.PodQOSBestEffort,
|
||||
podLevelResourcesEnabled: true,
|
||||
podLevelResourcesFixKubeletQOSClassEnabled: true,
|
||||
},
|
||||
{
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "zero-pod-level-resources-should-not-fallback-to-containers-fix-disabled"},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "guaranteed", Resources: v1.ResourceRequirements{Requests: getResourceList("100m", "100Mi"), Limits: getResourceList("100m", "100Mi")}},
|
||||
},
|
||||
Resources: &v1.ResourceRequirements{
|
||||
Requests: getResourceList("0", "0"),
|
||||
Limits: getResourceList("0", "0"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: v1.PodQOSBestEffort,
|
||||
podLevelResourcesEnabled: true,
|
||||
podLevelResourcesFixKubeletQOSClassEnabled: false,
|
||||
},
|
||||
{
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "empty-pod-level-resources-old-behavior"},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "guaranteed", Resources: v1.ResourceRequirements{Requests: getResourceList("100m", "100Mi"), Limits: getResourceList("100m", "100Mi")}},
|
||||
},
|
||||
Resources: &v1.ResourceRequirements{
|
||||
Requests: v1.ResourceList{},
|
||||
Limits: v1.ResourceList{},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: v1.PodQOSBestEffort,
|
||||
podLevelResourcesEnabled: true,
|
||||
podLevelResourcesFixKubeletQOSClassEnabled: false,
|
||||
},
|
||||
{
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "guaranteed-with-pod-level-resources-and-hugepages"},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "best-effort"},
|
||||
},
|
||||
Resources: &v1.ResourceRequirements{
|
||||
Requests: v1.ResourceList{
|
||||
v1.ResourceCPU: resource.MustParse("10m"),
|
||||
v1.ResourceMemory: resource.MustParse("100Mi"),
|
||||
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
|
||||
},
|
||||
Limits: v1.ResourceList{
|
||||
v1.ResourceCPU: resource.MustParse("10m"),
|
||||
v1.ResourceMemory: resource.MustParse("100Mi"),
|
||||
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: v1.PodQOSGuaranteed,
|
||||
podLevelResourcesEnabled: true,
|
||||
podLevelResourcesFixKubeletQOSClassEnabled: true,
|
||||
},
|
||||
{
|
||||
pod: &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "burstable-with-pod-level-resources-and-hugepages"},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{Name: "best-effort"},
|
||||
},
|
||||
Resources: &v1.ResourceRequirements{
|
||||
Requests: v1.ResourceList{
|
||||
v1.ResourceCPU: resource.MustParse("10m"),
|
||||
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
|
||||
},
|
||||
Limits: v1.ResourceList{
|
||||
v1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: v1.PodQOSBurstable,
|
||||
podLevelResourcesEnabled: true,
|
||||
podLevelResourcesFixKubeletQOSClassEnabled: true,
|
||||
},
|
||||
}
|
||||
tests = append(tests, explicitPLRTests...)
|
||||
|
||||
for _, testCase := range tests {
|
||||
t.Run(testCase.pod.Name, func(t *testing.T) {
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodLevelResources, testCase.podLevelResourcesEnabled)
|
||||
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodLevelResourcesFixKubeletQOSClass, testCase.podLevelResourcesFixKubeletQOSClassEnabled)
|
||||
if actual := ComputePodQOS(testCase.pod); testCase.expected != actual {
|
||||
t.Errorf("ComputePodQOS error: expected: %s, actual: %s;\npod = %s", testCase.expected, actual, prettyPrintPod(testCase.pod))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -767,6 +767,12 @@ const (
|
|||
// Enables specifying resources at pod-level.
|
||||
PodLevelResources featuregate.Feature = "PodLevelResources"
|
||||
|
||||
// owner: @KevinTMtz
|
||||
// key: https://kep.k8s.io/2837
|
||||
//
|
||||
// Fixes handling of nil/empty pod-level resources values for QoS classes.
|
||||
PodLevelResourcesFixKubeletQOSClass featuregate.Feature = "PodLevelResourcesFixKubeletQOSClass"
|
||||
|
||||
// owner: @knight42
|
||||
// kep: https://kep.k8s.io/3288
|
||||
//
|
||||
|
|
@ -1730,6 +1736,10 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
|
|||
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.Beta},
|
||||
},
|
||||
|
||||
PodLevelResourcesFixKubeletQOSClass: {
|
||||
{Version: version.MustParse("1.37"), Default: true, PreRelease: featuregate.Beta},
|
||||
},
|
||||
|
||||
PodLogsQuerySplitStreams: {
|
||||
{Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Alpha},
|
||||
},
|
||||
|
|
@ -2484,6 +2494,8 @@ var defaultKubernetesFeatureGateDependencies = map[featuregate.Feature][]feature
|
|||
|
||||
PodLevelResources: {},
|
||||
|
||||
PodLevelResourcesFixKubeletQOSClass: {PodLevelResources},
|
||||
|
||||
PodLogsQuerySplitStreams: {},
|
||||
|
||||
PodObservedGenerationTracking: {},
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ func IsSupportedPodLevelResource(name v1.ResourceName) bool {
|
|||
|
||||
// IsPodLevelResourcesSet check if PodLevelResources pod-level resources are set.
|
||||
// It returns true if either the Requests or Limits maps are non-empty.
|
||||
// Note: keep this in sync with k8s.io/kubernetes/pkg/apis/core/helper.IsPodLevelResourcesSet
|
||||
func IsPodLevelResourcesSet(pod *v1.Pod) bool {
|
||||
if pod.Spec.Resources == nil {
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package qos
|
|||
import (
|
||||
core "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
resourcehelper "k8s.io/component-helpers/resource"
|
||||
)
|
||||
|
||||
var supportedQoSComputeResources = sets.New(core.ResourceCPU, core.ResourceMemory)
|
||||
|
|
@ -39,7 +40,7 @@ func GetPodQOS(pod *core.Pod) core.PodQOSClass {
|
|||
// A pod is Burstable if cpu & memory limits and requests do not match across all containers.
|
||||
func ComputePodQOS(pod *core.Pod) core.PodQOSClass {
|
||||
// When pod-level resources are specified, we use them to determine QoS class.
|
||||
if pod.Spec.Resources != nil {
|
||||
if resourcehelper.IsPodLevelResourcesSet(pod) {
|
||||
return requirementsQOS(pod.Spec.Resources)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -189,6 +189,294 @@ func TestGetPodQOS(t *testing.T) {
|
|||
},
|
||||
want: corev1.PodQOSBestEffort,
|
||||
},
|
||||
{
|
||||
name: "empty pod level resources should fallback to containers",
|
||||
pod: &corev1.Pod{
|
||||
Spec: corev1.PodSpec{
|
||||
Resources: &corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{},
|
||||
Limits: corev1.ResourceList{},
|
||||
},
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "container1",
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: corev1.PodQOSGuaranteed,
|
||||
},
|
||||
{
|
||||
name: "completely empty pod level resources should fallback to containers",
|
||||
pod: &corev1.Pod{
|
||||
Spec: corev1.PodSpec{
|
||||
Resources: &corev1.ResourceRequirements{},
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "container1",
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: corev1.PodQOSGuaranteed,
|
||||
},
|
||||
{
|
||||
name: "nil pod level resources should fallback to containers",
|
||||
pod: &corev1.Pod{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "container1",
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: corev1.PodQOSGuaranteed,
|
||||
},
|
||||
{
|
||||
name: "zero pod level resources should not fallback to containers",
|
||||
pod: &corev1.Pod{
|
||||
Spec: corev1.PodSpec{
|
||||
Resources: &corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("0"),
|
||||
corev1.ResourceMemory: resource.MustParse("0"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("0"),
|
||||
corev1.ResourceMemory: resource.MustParse("0"),
|
||||
},
|
||||
},
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "container1",
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: corev1.PodQOSBestEffort,
|
||||
},
|
||||
{
|
||||
name: "guaranteed with pod level resources",
|
||||
pod: &corev1.Pod{
|
||||
Spec: corev1.PodSpec{
|
||||
Resources: &corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
},
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "container1",
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("100m"),
|
||||
corev1.ResourceMemory: resource.MustParse("100Mi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: corev1.PodQOSGuaranteed,
|
||||
},
|
||||
{
|
||||
name: "burstable with pod level resources",
|
||||
pod: &corev1.Pod{
|
||||
Spec: corev1.PodSpec{
|
||||
Resources: &corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("100m"),
|
||||
corev1.ResourceMemory: resource.MustParse("100Mi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
},
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "container1",
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: corev1.PodQOSBurstable,
|
||||
},
|
||||
{
|
||||
name: "burstable with pod level requests only",
|
||||
pod: &corev1.Pod{
|
||||
Spec: corev1.PodSpec{
|
||||
Resources: &corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("100m"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
},
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "container1",
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: corev1.PodQOSBurstable,
|
||||
},
|
||||
{
|
||||
name: "container with zero requests limits",
|
||||
pod: &corev1.Pod{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "container1",
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("0"),
|
||||
corev1.ResourceMemory: resource.MustParse("0"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("0"),
|
||||
corev1.ResourceMemory: resource.MustParse("0"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: corev1.PodQOSBestEffort,
|
||||
},
|
||||
{
|
||||
name: "guaranteed with pod level resources and hugepages",
|
||||
pod: &corev1.Pod{
|
||||
Spec: corev1.PodSpec{
|
||||
Resources: &corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("10m"),
|
||||
corev1.ResourceMemory: resource.MustParse("100Mi"),
|
||||
corev1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("10m"),
|
||||
corev1.ResourceMemory: resource.MustParse("100Mi"),
|
||||
corev1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
|
||||
},
|
||||
},
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "container1",
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: corev1.PodQOSGuaranteed,
|
||||
},
|
||||
{
|
||||
name: "burstable with pod level resources and hugepages",
|
||||
pod: &corev1.Pod{
|
||||
Spec: corev1.PodSpec{
|
||||
Resources: &corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("10m"),
|
||||
corev1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceName("hugepages-2Mi"): resource.MustParse("100Mi"),
|
||||
},
|
||||
},
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "container1",
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: corev1.PodQOSBurstable,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@
|
|||
| PodGroupPreemptionPolicy | | | 1.37– | | | | GenericWorkload | [code](https://cs.k8s.io/?q=%5CbPodGroupPreemptionPolicy%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbPodGroupPreemptionPolicy%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| PodLevelResourceManagers | | | 1.36– | | | | PodLevelResources | [code](https://cs.k8s.io/?q=%5CbPodLevelResourceManagers%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbPodLevelResourceManagers%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| PodLevelResources | :ballot_box_with_check: 1.34+ | | 1.32–1.33 | 1.34– | | | | [code](https://cs.k8s.io/?q=%5CbPodLevelResources%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbPodLevelResources%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| PodLevelResourcesFixKubeletQOSClass | :ballot_box_with_check: 1.37+ | | | 1.37– | | | PodLevelResources | [code](https://cs.k8s.io/?q=%5CbPodLevelResourcesFixKubeletQOSClass%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbPodLevelResourcesFixKubeletQOSClass%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| PodLogsQuerySplitStreams | | | 1.32– | | | | | [code](https://cs.k8s.io/?q=%5CbPodLogsQuerySplitStreams%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbPodLogsQuerySplitStreams%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| PodObservedGenerationTracking | :ballot_box_with_check: 1.34+ | :closed_lock_with_key: 1.35+ | 1.33 | 1.34 | 1.35– | | | [code](https://cs.k8s.io/?q=%5CbPodObservedGenerationTracking%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbPodObservedGenerationTracking%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
| PodReadyToStartContainersCondition | :ballot_box_with_check: 1.29+ | | 1.28 | 1.29– | | | | [code](https://cs.k8s.io/?q=%5CbPodReadyToStartContainersCondition%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbPodReadyToStartContainersCondition%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) |
|
||||
|
|
|
|||
|
|
@ -1375,6 +1375,12 @@
|
|||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.34"
|
||||
- name: PodLevelResourcesFixKubeletQOSClass
|
||||
versionedSpecs:
|
||||
- default: true
|
||||
lockToDefault: false
|
||||
preRelease: Beta
|
||||
version: "1.37"
|
||||
- name: PodLogsQuerySplitStreams
|
||||
versionedSpecs:
|
||||
- default: false
|
||||
|
|
|
|||
Loading…
Reference in a new issue