image_locality_test.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /*
  2. Copyright 2016 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 priorities
  14. import (
  15. "crypto/sha256"
  16. "reflect"
  17. "sort"
  18. "testing"
  19. "encoding/hex"
  20. "k8s.io/api/core/v1"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
  23. schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
  24. "k8s.io/kubernetes/pkg/util/parsers"
  25. )
  26. func TestImageLocalityPriority(t *testing.T) {
  27. test40250 := v1.PodSpec{
  28. Containers: []v1.Container{
  29. {
  30. Image: "gcr.io/40",
  31. },
  32. {
  33. Image: "gcr.io/250",
  34. },
  35. },
  36. }
  37. test40300 := v1.PodSpec{
  38. Containers: []v1.Container{
  39. {
  40. Image: "gcr.io/40",
  41. },
  42. {
  43. Image: "gcr.io/300",
  44. },
  45. },
  46. }
  47. testMinMax := v1.PodSpec{
  48. Containers: []v1.Container{
  49. {
  50. Image: "gcr.io/10",
  51. },
  52. {
  53. Image: "gcr.io/2000",
  54. },
  55. },
  56. }
  57. node403002000 := v1.NodeStatus{
  58. Images: []v1.ContainerImage{
  59. {
  60. Names: []string{
  61. "gcr.io/40:" + parsers.DefaultImageTag,
  62. "gcr.io/40:v1",
  63. "gcr.io/40:v1",
  64. },
  65. SizeBytes: int64(40 * mb),
  66. },
  67. {
  68. Names: []string{
  69. "gcr.io/300:" + parsers.DefaultImageTag,
  70. "gcr.io/300:v1",
  71. },
  72. SizeBytes: int64(300 * mb),
  73. },
  74. {
  75. Names: []string{
  76. "gcr.io/2000:" + parsers.DefaultImageTag,
  77. },
  78. SizeBytes: int64(2000 * mb),
  79. },
  80. },
  81. }
  82. node25010 := v1.NodeStatus{
  83. Images: []v1.ContainerImage{
  84. {
  85. Names: []string{
  86. "gcr.io/250:" + parsers.DefaultImageTag,
  87. },
  88. SizeBytes: int64(250 * mb),
  89. },
  90. {
  91. Names: []string{
  92. "gcr.io/10:" + parsers.DefaultImageTag,
  93. "gcr.io/10:v1",
  94. },
  95. SizeBytes: int64(10 * mb),
  96. },
  97. },
  98. }
  99. tests := []struct {
  100. pod *v1.Pod
  101. pods []*v1.Pod
  102. nodes []*v1.Node
  103. expectedList schedulerapi.HostPriorityList
  104. name string
  105. }{
  106. {
  107. // Pod: gcr.io/40 gcr.io/250
  108. // Node1
  109. // Image: gcr.io/40:latest 40MB
  110. // Score: 0 (40M/2 < 23M, min-threshold)
  111. // Node2
  112. // Image: gcr.io/250:latest 250MB
  113. // Score: 10 * (250M/2 - 23M)/(1000M - 23M) = 1
  114. pod: &v1.Pod{Spec: test40250},
  115. nodes: []*v1.Node{makeImageNode("machine1", node403002000), makeImageNode("machine2", node25010)},
  116. expectedList: []schedulerapi.HostPriority{{Host: "machine1", Score: 0}, {Host: "machine2", Score: 1}},
  117. name: "two images spread on two nodes, prefer the larger image one",
  118. },
  119. {
  120. // Pod: gcr.io/40 gcr.io/300
  121. // Node1
  122. // Image: gcr.io/40:latest 40MB, gcr.io/300:latest 300MB
  123. // Score: 10 * ((40M + 300M)/2 - 23M)/(1000M - 23M) = 1
  124. // Node2
  125. // Image: not present
  126. // Score: 0
  127. pod: &v1.Pod{Spec: test40300},
  128. nodes: []*v1.Node{makeImageNode("machine1", node403002000), makeImageNode("machine2", node25010)},
  129. expectedList: []schedulerapi.HostPriority{{Host: "machine1", Score: 1}, {Host: "machine2", Score: 0}},
  130. name: "two images on one node, prefer this node",
  131. },
  132. {
  133. // Pod: gcr.io/2000 gcr.io/10
  134. // Node1
  135. // Image: gcr.io/2000:latest 2000MB
  136. // Score: 10 (2000M/2 >= 1000M, max-threshold)
  137. // Node2
  138. // Image: gcr.io/10:latest 10MB
  139. // Score: 0 (10M/2 < 23M, min-threshold)
  140. pod: &v1.Pod{Spec: testMinMax},
  141. nodes: []*v1.Node{makeImageNode("machine1", node403002000), makeImageNode("machine2", node25010)},
  142. expectedList: []schedulerapi.HostPriority{{Host: "machine1", Score: schedulerapi.MaxPriority}, {Host: "machine2", Score: 0}},
  143. name: "if exceed limit, use limit",
  144. },
  145. }
  146. for _, test := range tests {
  147. t.Run(test.name, func(t *testing.T) {
  148. nodeNameToInfo := schedulernodeinfo.CreateNodeNameToInfoMap(test.pods, test.nodes)
  149. list, err := priorityFunction(ImageLocalityPriorityMap, nil, &priorityMetadata{totalNumNodes: len(test.nodes)})(test.pod, nodeNameToInfo, test.nodes)
  150. if err != nil {
  151. t.Errorf("unexpected error: %v", err)
  152. }
  153. sort.Sort(test.expectedList)
  154. sort.Sort(list)
  155. if !reflect.DeepEqual(test.expectedList, list) {
  156. t.Errorf("expected %#v, got %#v", test.expectedList, list)
  157. }
  158. })
  159. }
  160. }
  161. func TestNormalizedImageName(t *testing.T) {
  162. for _, testCase := range []struct {
  163. Input string
  164. Output string
  165. }{
  166. {Input: "root", Output: "root:latest"},
  167. {Input: "root:tag", Output: "root:tag"},
  168. {Input: "gcr.io:5000/root", Output: "gcr.io:5000/root:latest"},
  169. {Input: "root@" + getImageFakeDigest("root"), Output: "root@" + getImageFakeDigest("root")},
  170. } {
  171. image := normalizedImageName(testCase.Input)
  172. if image != testCase.Output {
  173. t.Errorf("expected image reference: %q, got %q", testCase.Output, image)
  174. }
  175. }
  176. }
  177. func makeImageNode(node string, status v1.NodeStatus) *v1.Node {
  178. return &v1.Node{
  179. ObjectMeta: metav1.ObjectMeta{Name: node},
  180. Status: status,
  181. }
  182. }
  183. func getImageFakeDigest(fakeContent string) string {
  184. hash := sha256.Sum256([]byte(fakeContent))
  185. return "sha256:" + hex.EncodeToString(hash[:])
  186. }