@@ -21,7 +21,9 @@ package qos
2121import (
2222 "k8s.io/apimachinery/pkg/api/resource"
2323 "k8s.io/apimachinery/pkg/util/sets"
24+ utilfeature "k8s.io/apiserver/pkg/util/feature"
2425 "k8s.io/kubernetes/pkg/apis/core"
26+ "k8s.io/kubernetes/pkg/features"
2527)
2628
2729var supportedQoSComputeResources = sets .NewString (string (core .ResourceCPU ), string (core .ResourceMemory ))
@@ -39,6 +41,45 @@ func GetPodQOS(pod *core.Pod) core.PodQOSClass {
3941 return ComputePodQOS (pod )
4042}
4143
44+ // zeroQuantity represents a resource.Quantity with value "0", used as a baseline
45+ // for resource comparisons.
46+ var zeroQuantity = resource .MustParse ("0" )
47+
48+ // processResourceList adds non-zero quantities for supported QoS compute resources
49+ // quantities from newList to list.
50+ func processResourceList (list , newList core.ResourceList ) {
51+ for name , quantity := range newList {
52+ if ! isSupportedQoSComputeResource (name ) {
53+ continue
54+ }
55+ if quantity .Cmp (zeroQuantity ) == 1 {
56+ delta := quantity .DeepCopy ()
57+ if _ , exists := list [name ]; ! exists {
58+ list [name ] = delta
59+ } else {
60+ delta .Add (list [name ])
61+ list [name ] = delta
62+ }
63+ }
64+ }
65+ }
66+
67+ // getQOSResources returns a set of resource names from the provided resource list that:
68+ // 1. Are supported QoS compute resources
69+ // 2. Have quantities greater than zero
70+ func getQOSResources (list core.ResourceList ) sets.Set [string ] {
71+ qosResources := sets .New [string ]()
72+ for name , quantity := range list {
73+ if ! isSupportedQoSComputeResource (name ) {
74+ continue
75+ }
76+ if quantity .Cmp (zeroQuantity ) == 1 {
77+ qosResources .Insert (string (name ))
78+ }
79+ }
80+ return qosResources
81+ }
82+
4283// ComputePodQOS evaluates the list of containers to determine a pod's QoS class. This function is more
4384// expensive than GetPodQOS which should be used for pods having a non-empty .Status.QOSClass.
4485// A pod is besteffort if none of its containers have specified any requests or limits.
@@ -48,54 +89,44 @@ func GetPodQOS(pod *core.Pod) core.PodQOSClass {
4889func ComputePodQOS (pod * core.Pod ) core.PodQOSClass {
4990 requests := core.ResourceList {}
5091 limits := core.ResourceList {}
51- zeroQuantity := resource .MustParse ("0" )
5292 isGuaranteed := true
53- // note, ephemeral containers are not considered for QoS as they cannot define resources
54- allContainers := []core.Container {}
55- allContainers = append (allContainers , pod .Spec .Containers ... )
56- allContainers = append (allContainers , pod .Spec .InitContainers ... )
57- for _ , container := range allContainers {
58- // process requests
59- for name , quantity := range container .Resources .Requests {
60- if ! isSupportedQoSComputeResource (name ) {
61- continue
62- }
63- if quantity .Cmp (zeroQuantity ) == 1 {
64- delta := quantity .DeepCopy ()
65- if _ , exists := requests [name ]; ! exists {
66- requests [name ] = delta
67- } else {
68- delta .Add (requests [name ])
69- requests [name ] = delta
70- }
71- }
93+ // When pod-level resources are specified, we use them to determine QoS class.
94+ if utilfeature .DefaultFeatureGate .Enabled (features .PodLevelResources ) &&
95+ pod .Spec .Resources != nil {
96+ if len (pod .Spec .Resources .Requests ) > 0 {
97+ // process requests
98+ processResourceList (requests , pod .Spec .Resources .Requests )
7299 }
73- // process limits
74- qosLimitsFound := sets .NewString ()
75- for name , quantity := range container .Resources .Limits {
76- if ! isSupportedQoSComputeResource (name ) {
77- continue
78- }
79- if quantity .Cmp (zeroQuantity ) == 1 {
80- qosLimitsFound .Insert (string (name ))
81- delta := quantity .DeepCopy ()
82- if _ , exists := limits [name ]; ! exists {
83- limits [name ] = delta
84- } else {
85- delta .Add (limits [name ])
86- limits [name ] = delta
87- }
100+
101+ if len (pod .Spec .Resources .Limits ) > 0 {
102+ // process limits
103+ processResourceList (limits , pod .Spec .Resources .Limits )
104+ qosLimitResources := getQOSResources (pod .Spec .Resources .Limits )
105+ if ! qosLimitResources .HasAll (string (core .ResourceMemory ), string (core .ResourceCPU )) {
106+ isGuaranteed = false
88107 }
89108 }
90-
91- if ! qosLimitsFound .HasAll (string (core .ResourceMemory ), string (core .ResourceCPU )) {
92- isGuaranteed = false
109+ } else {
110+ // note, ephemeral containers are not considered for QoS as they cannot define resources
111+ allContainers := []core.Container {}
112+ allContainers = append (allContainers , pod .Spec .Containers ... )
113+ allContainers = append (allContainers , pod .Spec .InitContainers ... )
114+ for _ , container := range allContainers {
115+ // process requests
116+ processResourceList (requests , container .Resources .Requests )
117+ // process limits
118+ processResourceList (limits , container .Resources .Limits )
119+ qosLimitResources := getQOSResources (container .Resources .Limits )
120+ if ! qosLimitResources .HasAll (string (core .ResourceMemory ), string (core .ResourceCPU )) {
121+ isGuaranteed = false
122+ }
93123 }
94124 }
125+
95126 if len (requests ) == 0 && len (limits ) == 0 {
96127 return core .PodQOSBestEffort
97128 }
98- // Check is requests match limits for all resources.
129+ // Check if requests match limits for all resources.
99130 if isGuaranteed {
100131 for name , req := range requests {
101132 if lim , exists := limits [name ]; ! exists || lim .Cmp (req ) != 0 {
0 commit comments