fieldpath.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /*
  2. Copyright 2015 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 fieldpath
  14. import (
  15. "fmt"
  16. "math"
  17. "strconv"
  18. "strings"
  19. "k8s.io/kubernetes/pkg/api"
  20. "k8s.io/kubernetes/pkg/api/meta"
  21. "k8s.io/kubernetes/pkg/api/resource"
  22. )
  23. // formatMap formats map[string]string to a string.
  24. func FormatMap(m map[string]string) (fmtStr string) {
  25. for key, value := range m {
  26. fmtStr += fmt.Sprintf("%v=%q\n", key, value)
  27. }
  28. fmtStr = strings.TrimSuffix(fmtStr, "\n")
  29. return
  30. }
  31. // ExtractFieldPathAsString extracts the field from the given object
  32. // and returns it as a string. The object must be a pointer to an
  33. // API type.
  34. //
  35. // Currently, this API is limited to supporting the fieldpaths:
  36. //
  37. // 1. metadata.name - The name of an API object
  38. // 2. metadata.namespace - The namespace of an API object
  39. func ExtractFieldPathAsString(obj interface{}, fieldPath string) (string, error) {
  40. accessor, err := meta.Accessor(obj)
  41. if err != nil {
  42. return "", nil
  43. }
  44. switch fieldPath {
  45. case "metadata.annotations":
  46. return FormatMap(accessor.GetAnnotations()), nil
  47. case "metadata.labels":
  48. return FormatMap(accessor.GetLabels()), nil
  49. case "metadata.name":
  50. return accessor.GetName(), nil
  51. case "metadata.namespace":
  52. return accessor.GetNamespace(), nil
  53. }
  54. return "", fmt.Errorf("Unsupported fieldPath: %v", fieldPath)
  55. }
  56. // ExtractResourceValueByContainerName extracts the value of a resource
  57. // by providing container name
  58. func ExtractResourceValueByContainerName(fs *api.ResourceFieldSelector, pod *api.Pod, containerName string) (string, error) {
  59. container, err := findContainerInPod(pod, containerName)
  60. if err != nil {
  61. return "", err
  62. }
  63. return ExtractContainerResourceValue(fs, container)
  64. }
  65. // ExtractResourceValueByContainerNameAndNodeAllocatable extracts the value of a resource
  66. // by providing container name and node allocatable
  67. func ExtractResourceValueByContainerNameAndNodeAllocatable(fs *api.ResourceFieldSelector, pod *api.Pod, containerName string, nodeAllocatable api.ResourceList) (string, error) {
  68. realContainer, err := findContainerInPod(pod, containerName)
  69. if err != nil {
  70. return "", err
  71. }
  72. containerCopy, err := api.Scheme.DeepCopy(realContainer)
  73. if err != nil {
  74. return "", fmt.Errorf("failed to perform a deep copy of container object: %v", err)
  75. }
  76. container, ok := containerCopy.(*api.Container)
  77. if !ok {
  78. return "", fmt.Errorf("unexpected type returned from deep copy of container object")
  79. }
  80. MergeContainerResourceLimits(container, nodeAllocatable)
  81. return ExtractContainerResourceValue(fs, container)
  82. }
  83. // ExtractContainerResourceValue extracts the value of a resource
  84. // in an already known container
  85. func ExtractContainerResourceValue(fs *api.ResourceFieldSelector, container *api.Container) (string, error) {
  86. divisor := resource.Quantity{}
  87. if divisor.Cmp(fs.Divisor) == 0 {
  88. divisor = resource.MustParse("1")
  89. } else {
  90. divisor = fs.Divisor
  91. }
  92. switch fs.Resource {
  93. case "limits.cpu":
  94. return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor)
  95. case "limits.memory":
  96. return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor)
  97. case "requests.cpu":
  98. return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor)
  99. case "requests.memory":
  100. return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor)
  101. }
  102. return "", fmt.Errorf("Unsupported container resource : %v", fs.Resource)
  103. }
  104. // findContainerInPod finds a container by its name in the provided pod
  105. func findContainerInPod(pod *api.Pod, containerName string) (*api.Container, error) {
  106. for _, container := range pod.Spec.Containers {
  107. if container.Name == containerName {
  108. return &container, nil
  109. }
  110. }
  111. return nil, fmt.Errorf("container %s not found", containerName)
  112. }
  113. // convertResourceCPUTOString converts cpu value to the format of divisor and returns
  114. // ceiling of the value.
  115. func convertResourceCPUToString(cpu *resource.Quantity, divisor resource.Quantity) (string, error) {
  116. c := int64(math.Ceil(float64(cpu.MilliValue()) / float64(divisor.MilliValue())))
  117. return strconv.FormatInt(c, 10), nil
  118. }
  119. // convertResourceMemoryToString converts memory value to the format of divisor and returns
  120. // ceiling of the value.
  121. func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Quantity) (string, error) {
  122. m := int64(math.Ceil(float64(memory.Value()) / float64(divisor.Value())))
  123. return strconv.FormatInt(m, 10), nil
  124. }
  125. // MergeContainerResourceLimits checks if a limit is applied for
  126. // the container, and if not, it sets the limit to the passed resource list.
  127. func MergeContainerResourceLimits(container *api.Container,
  128. allocatable api.ResourceList) {
  129. if container.Resources.Limits == nil {
  130. container.Resources.Limits = make(api.ResourceList)
  131. }
  132. for _, resource := range []api.ResourceName{api.ResourceCPU, api.ResourceMemory} {
  133. if quantity, exists := container.Resources.Limits[resource]; !exists || quantity.IsZero() {
  134. if cap, exists := allocatable[resource]; exists {
  135. container.Resources.Limits[resource] = *cap.Copy()
  136. }
  137. }
  138. }
  139. }