1
16
17 package resource
18
19 import (
20 "fmt"
21 "math"
22 "strconv"
23 "strings"
24
25 corev1 "k8s.io/api/core/v1"
26 "k8s.io/apimachinery/pkg/api/resource"
27 "k8s.io/apimachinery/pkg/util/sets"
28 )
29
30
31
32
33
34 func PodRequestsAndLimits(pod *corev1.Pod) (reqs, limits corev1.ResourceList) {
35 return podRequests(pod), podLimits(pod)
36 }
37
38
39
40 func podRequests(pod *corev1.Pod) corev1.ResourceList {
41
42 reqs := corev1.ResourceList{}
43
44 containerStatuses := map[string]*corev1.ContainerStatus{}
45 for i := range pod.Status.ContainerStatuses {
46 containerStatuses[pod.Status.ContainerStatuses[i].Name] = &pod.Status.ContainerStatuses[i]
47 }
48
49 for _, container := range pod.Spec.Containers {
50 containerReqs := container.Resources.Requests
51 cs, found := containerStatuses[container.Name]
52 if found {
53 if pod.Status.Resize == corev1.PodResizeStatusInfeasible {
54 containerReqs = cs.AllocatedResources.DeepCopy()
55 } else {
56 containerReqs = max(container.Resources.Requests, cs.AllocatedResources)
57 }
58 }
59 addResourceList(reqs, containerReqs)
60 }
61
62 restartableInitContainerReqs := corev1.ResourceList{}
63 initContainerReqs := corev1.ResourceList{}
64
65 for _, container := range pod.Spec.InitContainers {
66 containerReqs := container.Resources.Requests
67
68 if container.RestartPolicy != nil && *container.RestartPolicy == corev1.ContainerRestartPolicyAlways {
69
70 addResourceList(reqs, containerReqs)
71
72
73 addResourceList(restartableInitContainerReqs, containerReqs)
74 containerReqs = restartableInitContainerReqs
75 } else {
76 tmp := corev1.ResourceList{}
77 addResourceList(tmp, containerReqs)
78 addResourceList(tmp, restartableInitContainerReqs)
79 containerReqs = tmp
80 }
81 maxResourceList(initContainerReqs, containerReqs)
82 }
83
84 maxResourceList(reqs, initContainerReqs)
85
86
87 if pod.Spec.Overhead != nil {
88 addResourceList(reqs, pod.Spec.Overhead)
89 }
90
91 return reqs
92 }
93
94
95
96 func podLimits(pod *corev1.Pod) corev1.ResourceList {
97 limits := corev1.ResourceList{}
98
99 for _, container := range pod.Spec.Containers {
100 addResourceList(limits, container.Resources.Limits)
101 }
102
103 restartableInitContainerLimits := corev1.ResourceList{}
104 initContainerLimits := corev1.ResourceList{}
105
106 for _, container := range pod.Spec.InitContainers {
107 containerLimits := container.Resources.Limits
108
109 if container.RestartPolicy != nil && *container.RestartPolicy == corev1.ContainerRestartPolicyAlways {
110 addResourceList(limits, containerLimits)
111
112
113 addResourceList(restartableInitContainerLimits, containerLimits)
114 containerLimits = restartableInitContainerLimits
115 } else {
116 tmp := corev1.ResourceList{}
117 addResourceList(tmp, containerLimits)
118 addResourceList(tmp, restartableInitContainerLimits)
119 containerLimits = tmp
120 }
121
122 maxResourceList(initContainerLimits, containerLimits)
123 }
124
125 maxResourceList(limits, initContainerLimits)
126
127
128 if pod.Spec.Overhead != nil {
129 for name, quantity := range pod.Spec.Overhead {
130 if value, ok := limits[name]; ok && !value.IsZero() {
131 value.Add(quantity)
132 limits[name] = value
133 }
134 }
135 }
136
137 return limits
138 }
139
140
141
142 func max(a corev1.ResourceList, b corev1.ResourceList) corev1.ResourceList {
143 result := corev1.ResourceList{}
144 for key, value := range a {
145 if other, found := b[key]; found {
146 if value.Cmp(other) <= 0 {
147 result[key] = other.DeepCopy()
148 continue
149 }
150 }
151 result[key] = value.DeepCopy()
152 }
153 for key, value := range b {
154 if _, found := result[key]; !found {
155 result[key] = value.DeepCopy()
156 }
157 }
158 return result
159 }
160
161
162 func addResourceList(list, new corev1.ResourceList) {
163 for name, quantity := range new {
164 if value, ok := list[name]; !ok {
165 list[name] = quantity.DeepCopy()
166 } else {
167 value.Add(quantity)
168 list[name] = value
169 }
170 }
171 }
172
173
174
175 func maxResourceList(list, new corev1.ResourceList) {
176 for name, quantity := range new {
177 if value, ok := list[name]; !ok {
178 list[name] = quantity.DeepCopy()
179 continue
180 } else {
181 if quantity.Cmp(value) > 0 {
182 list[name] = quantity.DeepCopy()
183 }
184 }
185 }
186 }
187
188
189
190 func ExtractContainerResourceValue(fs *corev1.ResourceFieldSelector, container *corev1.Container) (string, error) {
191 divisor := resource.Quantity{}
192 if divisor.Cmp(fs.Divisor) == 0 {
193 divisor = resource.MustParse("1")
194 } else {
195 divisor = fs.Divisor
196 }
197
198 switch fs.Resource {
199 case "limits.cpu":
200 return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor)
201 case "limits.memory":
202 return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor)
203 case "limits.ephemeral-storage":
204 return convertResourceEphemeralStorageToString(container.Resources.Limits.StorageEphemeral(), divisor)
205 case "requests.cpu":
206 return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor)
207 case "requests.memory":
208 return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor)
209 case "requests.ephemeral-storage":
210 return convertResourceEphemeralStorageToString(container.Resources.Requests.StorageEphemeral(), divisor)
211 }
212
213
214 if strings.HasPrefix(fs.Resource, "requests.") {
215 resourceName := corev1.ResourceName(strings.TrimPrefix(fs.Resource, "requests."))
216 if IsHugePageResourceName(resourceName) {
217 return convertResourceHugePagesToString(container.Resources.Requests.Name(resourceName, resource.BinarySI), divisor)
218 }
219 }
220 if strings.HasPrefix(fs.Resource, "limits.") {
221 resourceName := corev1.ResourceName(strings.TrimPrefix(fs.Resource, "limits."))
222 if IsHugePageResourceName(resourceName) {
223 return convertResourceHugePagesToString(container.Resources.Limits.Name(resourceName, resource.BinarySI), divisor)
224 }
225 }
226 return "", fmt.Errorf("Unsupported container resource : %v", fs.Resource)
227 }
228
229
230
231 func convertResourceCPUToString(cpu *resource.Quantity, divisor resource.Quantity) (string, error) {
232 c := int64(math.Ceil(float64(cpu.MilliValue()) / float64(divisor.MilliValue())))
233 return strconv.FormatInt(c, 10), nil
234 }
235
236
237
238 func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Quantity) (string, error) {
239 m := int64(math.Ceil(float64(memory.Value()) / float64(divisor.Value())))
240 return strconv.FormatInt(m, 10), nil
241 }
242
243
244
245 func convertResourceHugePagesToString(hugePages *resource.Quantity, divisor resource.Quantity) (string, error) {
246 m := int64(math.Ceil(float64(hugePages.Value()) / float64(divisor.Value())))
247 return strconv.FormatInt(m, 10), nil
248 }
249
250
251
252 func convertResourceEphemeralStorageToString(ephemeralStorage *resource.Quantity, divisor resource.Quantity) (string, error) {
253 m := int64(math.Ceil(float64(ephemeralStorage.Value()) / float64(divisor.Value())))
254 return strconv.FormatInt(m, 10), nil
255 }
256
257 var standardContainerResources = sets.NewString(
258 string(corev1.ResourceCPU),
259 string(corev1.ResourceMemory),
260 string(corev1.ResourceEphemeralStorage),
261 )
262
263
264
265 func IsStandardContainerResourceName(str string) bool {
266 return standardContainerResources.Has(str) || IsHugePageResourceName(corev1.ResourceName(str))
267 }
268
269
270
271 func IsHugePageResourceName(name corev1.ResourceName) bool {
272 return strings.HasPrefix(string(name), corev1.ResourceHugePagesPrefix)
273 }
274
View as plain text