PageRenderTime 35ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/test/integration/extender_test.go

https://gitlab.com/shiphitchcock3/kubernetes
Go | 309 lines | 246 code | 38 blank | 25 comment | 62 complexity | 5b625621af0a2e5d851b58483a6dbf7c MD5 | raw file
  1. // +build integration,!no-etcd
  2. /*
  3. Copyright 2015 The Kubernetes Authors All rights reserved.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package integration
  15. // This file tests scheduler extender.
  16. import (
  17. "encoding/json"
  18. "fmt"
  19. "net/http"
  20. "net/http/httptest"
  21. "strings"
  22. "testing"
  23. "time"
  24. "k8s.io/kubernetes/pkg/api"
  25. "k8s.io/kubernetes/pkg/api/resource"
  26. "k8s.io/kubernetes/pkg/api/testapi"
  27. "k8s.io/kubernetes/pkg/api/unversioned"
  28. "k8s.io/kubernetes/pkg/client/record"
  29. "k8s.io/kubernetes/pkg/client/restclient"
  30. client "k8s.io/kubernetes/pkg/client/unversioned"
  31. "k8s.io/kubernetes/pkg/master"
  32. "k8s.io/kubernetes/pkg/util/wait"
  33. "k8s.io/kubernetes/plugin/pkg/scheduler"
  34. _ "k8s.io/kubernetes/plugin/pkg/scheduler/algorithmprovider"
  35. schedulerapi "k8s.io/kubernetes/plugin/pkg/scheduler/api"
  36. "k8s.io/kubernetes/plugin/pkg/scheduler/factory"
  37. "k8s.io/kubernetes/test/integration/framework"
  38. )
  39. const (
  40. filter = "filter"
  41. prioritize = "prioritize"
  42. )
  43. type fitPredicate func(pod *api.Pod, node *api.Node) (bool, error)
  44. type priorityFunc func(pod *api.Pod, nodes *api.NodeList) (*schedulerapi.HostPriorityList, error)
  45. type priorityConfig struct {
  46. function priorityFunc
  47. weight int
  48. }
  49. type Extender struct {
  50. name string
  51. predicates []fitPredicate
  52. prioritizers []priorityConfig
  53. }
  54. func (e *Extender) serveHTTP(t *testing.T, w http.ResponseWriter, req *http.Request) {
  55. var args schedulerapi.ExtenderArgs
  56. decoder := json.NewDecoder(req.Body)
  57. defer req.Body.Close()
  58. if err := decoder.Decode(&args); err != nil {
  59. http.Error(w, "Decode error", http.StatusBadRequest)
  60. return
  61. }
  62. encoder := json.NewEncoder(w)
  63. if strings.Contains(req.URL.Path, filter) {
  64. resp := &schedulerapi.ExtenderFilterResult{}
  65. nodes, err := e.Filter(&args.Pod, &args.Nodes)
  66. if err != nil {
  67. resp.Error = err.Error()
  68. } else {
  69. resp.Nodes = *nodes
  70. }
  71. if err := encoder.Encode(resp); err != nil {
  72. t.Fatalf("Failed to encode %+v", resp)
  73. }
  74. } else if strings.Contains(req.URL.Path, prioritize) {
  75. // Prioritize errors are ignored. Default k8s priorities or another extender's
  76. // priorities may be applied.
  77. priorities, _ := e.Prioritize(&args.Pod, &args.Nodes)
  78. if err := encoder.Encode(priorities); err != nil {
  79. t.Fatalf("Failed to encode %+v", priorities)
  80. }
  81. } else {
  82. http.Error(w, "Unknown method", http.StatusNotFound)
  83. }
  84. }
  85. func (e *Extender) Filter(pod *api.Pod, nodes *api.NodeList) (*api.NodeList, error) {
  86. filtered := []api.Node{}
  87. for _, node := range nodes.Items {
  88. fits := true
  89. for _, predicate := range e.predicates {
  90. fit, err := predicate(pod, &node)
  91. if err != nil {
  92. return &api.NodeList{}, err
  93. }
  94. if !fit {
  95. fits = false
  96. break
  97. }
  98. }
  99. if fits {
  100. filtered = append(filtered, node)
  101. }
  102. }
  103. return &api.NodeList{Items: filtered}, nil
  104. }
  105. func (e *Extender) Prioritize(pod *api.Pod, nodes *api.NodeList) (*schedulerapi.HostPriorityList, error) {
  106. result := schedulerapi.HostPriorityList{}
  107. combinedScores := map[string]int{}
  108. for _, prioritizer := range e.prioritizers {
  109. weight := prioritizer.weight
  110. if weight == 0 {
  111. continue
  112. }
  113. priorityFunc := prioritizer.function
  114. prioritizedList, err := priorityFunc(pod, nodes)
  115. if err != nil {
  116. return &schedulerapi.HostPriorityList{}, err
  117. }
  118. for _, hostEntry := range *prioritizedList {
  119. combinedScores[hostEntry.Host] += hostEntry.Score * weight
  120. }
  121. }
  122. for host, score := range combinedScores {
  123. result = append(result, schedulerapi.HostPriority{Host: host, Score: score})
  124. }
  125. return &result, nil
  126. }
  127. func machine_1_2_3_Predicate(pod *api.Pod, node *api.Node) (bool, error) {
  128. if node.Name == "machine1" || node.Name == "machine2" || node.Name == "machine3" {
  129. return true, nil
  130. }
  131. return false, nil
  132. }
  133. func machine_2_3_5_Predicate(pod *api.Pod, node *api.Node) (bool, error) {
  134. if node.Name == "machine2" || node.Name == "machine3" || node.Name == "machine5" {
  135. return true, nil
  136. }
  137. return false, nil
  138. }
  139. func machine_2_Prioritizer(pod *api.Pod, nodes *api.NodeList) (*schedulerapi.HostPriorityList, error) {
  140. result := schedulerapi.HostPriorityList{}
  141. for _, node := range nodes.Items {
  142. score := 1
  143. if node.Name == "machine2" {
  144. score = 10
  145. }
  146. result = append(result, schedulerapi.HostPriority{node.Name, score})
  147. }
  148. return &result, nil
  149. }
  150. func machine_3_Prioritizer(pod *api.Pod, nodes *api.NodeList) (*schedulerapi.HostPriorityList, error) {
  151. result := schedulerapi.HostPriorityList{}
  152. for _, node := range nodes.Items {
  153. score := 1
  154. if node.Name == "machine3" {
  155. score = 10
  156. }
  157. result = append(result, schedulerapi.HostPriority{node.Name, score})
  158. }
  159. return &result, nil
  160. }
  161. func TestSchedulerExtender(t *testing.T) {
  162. framework.DeleteAllEtcdKeys()
  163. var m *master.Master
  164. s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  165. m.Handler.ServeHTTP(w, req)
  166. }))
  167. // TODO: Uncomment when fix #19254
  168. // defer s.Close()
  169. masterConfig := framework.NewIntegrationTestMasterConfig()
  170. m, err := master.New(masterConfig)
  171. if err != nil {
  172. t.Fatalf("error in bringing up the master: %v", err)
  173. }
  174. restClient := client.NewOrDie(&restclient.Config{Host: s.URL, ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}})
  175. extender1 := &Extender{
  176. name: "extender1",
  177. predicates: []fitPredicate{machine_1_2_3_Predicate},
  178. prioritizers: []priorityConfig{{machine_2_Prioritizer, 1}},
  179. }
  180. es1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  181. extender1.serveHTTP(t, w, req)
  182. }))
  183. // TODO: Uncomment when fix #19254
  184. // defer es1.Close()
  185. extender2 := &Extender{
  186. name: "extender2",
  187. predicates: []fitPredicate{machine_2_3_5_Predicate},
  188. prioritizers: []priorityConfig{{machine_3_Prioritizer, 1}},
  189. }
  190. es2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  191. extender2.serveHTTP(t, w, req)
  192. }))
  193. // TODO: Uncomment when fix #19254
  194. // defer es2.Close()
  195. policy := schedulerapi.Policy{
  196. ExtenderConfigs: []schedulerapi.ExtenderConfig{
  197. {
  198. URLPrefix: es1.URL,
  199. FilterVerb: filter,
  200. PrioritizeVerb: prioritize,
  201. Weight: 3,
  202. EnableHttps: false,
  203. },
  204. {
  205. URLPrefix: es2.URL,
  206. FilterVerb: filter,
  207. PrioritizeVerb: prioritize,
  208. Weight: 4,
  209. EnableHttps: false,
  210. },
  211. },
  212. }
  213. policy.APIVersion = testapi.Default.GroupVersion().String()
  214. schedulerConfigFactory := factory.NewConfigFactory(restClient, api.DefaultSchedulerName)
  215. schedulerConfig, err := schedulerConfigFactory.CreateFromConfig(policy)
  216. if err != nil {
  217. t.Fatalf("Couldn't create scheduler config: %v", err)
  218. }
  219. eventBroadcaster := record.NewBroadcaster()
  220. schedulerConfig.Recorder = eventBroadcaster.NewRecorder(api.EventSource{Component: api.DefaultSchedulerName})
  221. eventBroadcaster.StartRecordingToSink(restClient.Events(""))
  222. scheduler.New(schedulerConfig).Run()
  223. defer close(schedulerConfig.StopEverything)
  224. DoTestPodScheduling(t, restClient)
  225. }
  226. func DoTestPodScheduling(t *testing.T, restClient *client.Client) {
  227. goodCondition := api.NodeCondition{
  228. Type: api.NodeReady,
  229. Status: api.ConditionTrue,
  230. Reason: fmt.Sprintf("schedulable condition"),
  231. LastHeartbeatTime: unversioned.Time{time.Now()},
  232. }
  233. node := &api.Node{
  234. Spec: api.NodeSpec{Unschedulable: false},
  235. Status: api.NodeStatus{
  236. Capacity: api.ResourceList{
  237. api.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI),
  238. },
  239. Conditions: []api.NodeCondition{goodCondition},
  240. },
  241. }
  242. for ii := 0; ii < 5; ii++ {
  243. node.Name = fmt.Sprintf("machine%d", ii+1)
  244. if _, err := restClient.Nodes().Create(node); err != nil {
  245. t.Fatalf("Failed to create nodes: %v", err)
  246. }
  247. }
  248. pod := &api.Pod{
  249. ObjectMeta: api.ObjectMeta{Name: "extender-test-pod"},
  250. Spec: api.PodSpec{
  251. Containers: []api.Container{{Name: "container", Image: "kubernetes/pause:go"}},
  252. },
  253. }
  254. myPod, err := restClient.Pods(api.NamespaceDefault).Create(pod)
  255. if err != nil {
  256. t.Fatalf("Failed to create pod: %v", err)
  257. }
  258. err = wait.Poll(time.Second, wait.ForeverTestTimeout, podScheduled(restClient, myPod.Namespace, myPod.Name))
  259. if err != nil {
  260. t.Fatalf("Failed to schedule pod: %v", err)
  261. }
  262. if myPod, err := restClient.Pods(api.NamespaceDefault).Get(myPod.Name); err != nil {
  263. t.Fatalf("Failed to get pod: %v", err)
  264. } else if myPod.Spec.NodeName != "machine3" {
  265. t.Fatalf("Failed to schedule using extender, expected machine3, got %v", myPod.Spec.NodeName)
  266. }
  267. t.Logf("Scheduled pod using extenders")
  268. }