@@ -21,7 +21,9 @@ package qos
21
21
import (
22
22
"k8s.io/apimachinery/pkg/api/resource"
23
23
"k8s.io/apimachinery/pkg/util/sets"
24
+ utilfeature "k8s.io/apiserver/pkg/util/feature"
24
25
"k8s.io/kubernetes/pkg/apis/core"
26
+ "k8s.io/kubernetes/pkg/features"
25
27
)
26
28
27
29
var supportedQoSComputeResources = sets .NewString (string (core .ResourceCPU ), string (core .ResourceMemory ))
@@ -39,6 +41,45 @@ func GetPodQOS(pod *core.Pod) core.PodQOSClass {
39
41
return ComputePodQOS (pod )
40
42
}
41
43
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
+
42
83
// ComputePodQOS evaluates the list of containers to determine a pod's QoS class. This function is more
43
84
// expensive than GetPodQOS which should be used for pods having a non-empty .Status.QOSClass.
44
85
// 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 {
48
89
func ComputePodQOS (pod * core.Pod ) core.PodQOSClass {
49
90
requests := core.ResourceList {}
50
91
limits := core.ResourceList {}
51
- zeroQuantity := resource .MustParse ("0" )
52
92
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 )
72
99
}
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
88
107
}
89
108
}
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
+ }
93
123
}
94
124
}
125
+
95
126
if len (requests ) == 0 && len (limits ) == 0 {
96
127
return core .PodQOSBestEffort
97
128
}
98
- // Check is requests match limits for all resources.
129
+ // Check if requests match limits for all resources.
99
130
if isGuaranteed {
100
131
for name , req := range requests {
101
132
if lim , exists := limits [name ]; ! exists || lim .Cmp (req ) != 0 {
0 commit comments