helpers.go 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package resource
  14. import (
  15. "fmt"
  16. "math"
  17. "strconv"
  18. "k8s.io/api/core/v1"
  19. "k8s.io/apimachinery/pkg/api/resource"
  20. )
  21. // addResourceList adds the resources in newList to list
  22. func addResourceList(list, new v1.ResourceList) {
  23. for name, quantity := range new {
  24. if value, ok := list[name]; !ok {
  25. list[name] = *quantity.Copy()
  26. } else {
  27. value.Add(quantity)
  28. list[name] = value
  29. }
  30. }
  31. }
  32. // maxResourceList sets list to the greater of list/newList for every resource
  33. // either list
  34. func maxResourceList(list, new v1.ResourceList) {
  35. for name, quantity := range new {
  36. if value, ok := list[name]; !ok {
  37. list[name] = *quantity.Copy()
  38. continue
  39. } else {
  40. if quantity.Cmp(value) > 0 {
  41. list[name] = *quantity.Copy()
  42. }
  43. }
  44. }
  45. }
  46. // PodRequestsAndLimits returns a dictionary of all defined resources summed up for all
  47. // containers of the pod.
  48. func PodRequestsAndLimits(pod *v1.Pod) (reqs, limits v1.ResourceList) {
  49. reqs, limits = v1.ResourceList{}, v1.ResourceList{}
  50. for _, container := range pod.Spec.Containers {
  51. addResourceList(reqs, container.Resources.Requests)
  52. addResourceList(limits, container.Resources.Limits)
  53. }
  54. // init containers define the minimum of any resource
  55. for _, container := range pod.Spec.InitContainers {
  56. maxResourceList(reqs, container.Resources.Requests)
  57. maxResourceList(limits, container.Resources.Limits)
  58. }
  59. return
  60. }
  61. // GetResourceRequest finds and returns the request for a specific resource.
  62. func GetResourceRequest(pod *v1.Pod, resource v1.ResourceName) int64 {
  63. if resource == v1.ResourcePods {
  64. return 1
  65. }
  66. totalResources := int64(0)
  67. for _, container := range pod.Spec.Containers {
  68. if rQuantity, ok := container.Resources.Requests[resource]; ok {
  69. if resource == v1.ResourceCPU {
  70. totalResources += rQuantity.MilliValue()
  71. } else {
  72. totalResources += rQuantity.Value()
  73. }
  74. }
  75. }
  76. // take max_resource(sum_pod, any_init_container)
  77. for _, container := range pod.Spec.InitContainers {
  78. if rQuantity, ok := container.Resources.Requests[resource]; ok {
  79. if resource == v1.ResourceCPU && rQuantity.MilliValue() > totalResources {
  80. totalResources = rQuantity.MilliValue()
  81. } else if rQuantity.Value() > totalResources {
  82. totalResources = rQuantity.Value()
  83. }
  84. }
  85. }
  86. return totalResources
  87. }
  88. // ExtractResourceValueByContainerName extracts the value of a resource
  89. // by providing container name
  90. func ExtractResourceValueByContainerName(fs *v1.ResourceFieldSelector, pod *v1.Pod, containerName string) (string, error) {
  91. container, err := findContainerInPod(pod, containerName)
  92. if err != nil {
  93. return "", err
  94. }
  95. return ExtractContainerResourceValue(fs, container)
  96. }
  97. // ExtractResourceValueByContainerNameAndNodeAllocatable extracts the value of a resource
  98. // by providing container name and node allocatable
  99. func ExtractResourceValueByContainerNameAndNodeAllocatable(fs *v1.ResourceFieldSelector, pod *v1.Pod, containerName string, nodeAllocatable v1.ResourceList) (string, error) {
  100. realContainer, err := findContainerInPod(pod, containerName)
  101. if err != nil {
  102. return "", err
  103. }
  104. container := realContainer.DeepCopy()
  105. MergeContainerResourceLimits(container, nodeAllocatable)
  106. return ExtractContainerResourceValue(fs, container)
  107. }
  108. // ExtractContainerResourceValue extracts the value of a resource
  109. // in an already known container
  110. func ExtractContainerResourceValue(fs *v1.ResourceFieldSelector, container *v1.Container) (string, error) {
  111. divisor := resource.Quantity{}
  112. if divisor.Cmp(fs.Divisor) == 0 {
  113. divisor = resource.MustParse("1")
  114. } else {
  115. divisor = fs.Divisor
  116. }
  117. switch fs.Resource {
  118. case "limits.cpu":
  119. return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor)
  120. case "limits.memory":
  121. return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor)
  122. case "limits.ephemeral-storage":
  123. return convertResourceEphemeralStorageToString(container.Resources.Limits.StorageEphemeral(), divisor)
  124. case "requests.cpu":
  125. return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor)
  126. case "requests.memory":
  127. return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor)
  128. case "requests.ephemeral-storage":
  129. return convertResourceEphemeralStorageToString(container.Resources.Requests.StorageEphemeral(), divisor)
  130. }
  131. return "", fmt.Errorf("Unsupported container resource : %v", fs.Resource)
  132. }
  133. // convertResourceCPUToString converts cpu value to the format of divisor and returns
  134. // ceiling of the value.
  135. func convertResourceCPUToString(cpu *resource.Quantity, divisor resource.Quantity) (string, error) {
  136. c := int64(math.Ceil(float64(cpu.MilliValue()) / float64(divisor.MilliValue())))
  137. return strconv.FormatInt(c, 10), nil
  138. }
  139. // convertResourceMemoryToString converts memory value to the format of divisor and returns
  140. // ceiling of the value.
  141. func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Quantity) (string, error) {
  142. m := int64(math.Ceil(float64(memory.Value()) / float64(divisor.Value())))
  143. return strconv.FormatInt(m, 10), nil
  144. }
  145. // convertResourceEphemeralStorageToString converts ephemeral storage value to the format of divisor and returns
  146. // ceiling of the value.
  147. func convertResourceEphemeralStorageToString(ephemeralStorage *resource.Quantity, divisor resource.Quantity) (string, error) {
  148. m := int64(math.Ceil(float64(ephemeralStorage.Value()) / float64(divisor.Value())))
  149. return strconv.FormatInt(m, 10), nil
  150. }
  151. // findContainerInPod finds a container by its name in the provided pod
  152. func findContainerInPod(pod *v1.Pod, containerName string) (*v1.Container, error) {
  153. for _, container := range pod.Spec.Containers {
  154. if container.Name == containerName {
  155. return &container, nil
  156. }
  157. }
  158. for _, container := range pod.Spec.InitContainers {
  159. if container.Name == containerName {
  160. return &container, nil
  161. }
  162. }
  163. return nil, fmt.Errorf("container %s not found", containerName)
  164. }
  165. // MergeContainerResourceLimits checks if a limit is applied for
  166. // the container, and if not, it sets the limit to the passed resource list.
  167. func MergeContainerResourceLimits(container *v1.Container,
  168. allocatable v1.ResourceList) {
  169. if container.Resources.Limits == nil {
  170. container.Resources.Limits = make(v1.ResourceList)
  171. }
  172. for _, resource := range []v1.ResourceName{v1.ResourceCPU, v1.ResourceMemory, v1.ResourceEphemeralStorage} {
  173. if quantity, exists := container.Resources.Limits[resource]; !exists || quantity.IsZero() {
  174. if cap, exists := allocatable[resource]; exists {
  175. container.Resources.Limits[resource] = *cap.Copy()
  176. }
  177. }
  178. }
  179. }