controller_utils_test.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926
  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 controller
  14. import (
  15. "context"
  16. "encoding/json"
  17. "fmt"
  18. "math"
  19. "math/rand"
  20. "net/http/httptest"
  21. "sort"
  22. "sync"
  23. "testing"
  24. "time"
  25. apps "k8s.io/api/apps/v1"
  26. v1 "k8s.io/api/core/v1"
  27. apiequality "k8s.io/apimachinery/pkg/api/equality"
  28. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  29. "k8s.io/apimachinery/pkg/runtime"
  30. "k8s.io/apimachinery/pkg/runtime/schema"
  31. "k8s.io/apimachinery/pkg/util/clock"
  32. "k8s.io/apimachinery/pkg/util/sets"
  33. "k8s.io/apimachinery/pkg/util/uuid"
  34. clientset "k8s.io/client-go/kubernetes"
  35. "k8s.io/client-go/kubernetes/fake"
  36. clientscheme "k8s.io/client-go/kubernetes/scheme"
  37. restclient "k8s.io/client-go/rest"
  38. "k8s.io/client-go/tools/cache"
  39. "k8s.io/client-go/tools/record"
  40. utiltesting "k8s.io/client-go/util/testing"
  41. _ "k8s.io/kubernetes/pkg/apis/core/install"
  42. "k8s.io/kubernetes/pkg/controller/testutil"
  43. "k8s.io/kubernetes/pkg/securitycontext"
  44. "github.com/stretchr/testify/assert"
  45. )
  46. // NewFakeControllerExpectationsLookup creates a fake store for PodExpectations.
  47. func NewFakeControllerExpectationsLookup(ttl time.Duration) (*ControllerExpectations, *clock.FakeClock) {
  48. fakeTime := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
  49. fakeClock := clock.NewFakeClock(fakeTime)
  50. ttlPolicy := &cache.TTLPolicy{TTL: ttl, Clock: fakeClock}
  51. ttlStore := cache.NewFakeExpirationStore(
  52. ExpKeyFunc, nil, ttlPolicy, fakeClock)
  53. return &ControllerExpectations{ttlStore}, fakeClock
  54. }
  55. func newReplicationController(replicas int) *v1.ReplicationController {
  56. rc := &v1.ReplicationController{
  57. TypeMeta: metav1.TypeMeta{APIVersion: "v1"},
  58. ObjectMeta: metav1.ObjectMeta{
  59. UID: uuid.NewUUID(),
  60. Name: "foobar",
  61. Namespace: metav1.NamespaceDefault,
  62. ResourceVersion: "18",
  63. },
  64. Spec: v1.ReplicationControllerSpec{
  65. Replicas: func() *int32 { i := int32(replicas); return &i }(),
  66. Selector: map[string]string{"foo": "bar"},
  67. Template: &v1.PodTemplateSpec{
  68. ObjectMeta: metav1.ObjectMeta{
  69. Labels: map[string]string{
  70. "name": "foo",
  71. "type": "production",
  72. },
  73. },
  74. Spec: v1.PodSpec{
  75. Containers: []v1.Container{
  76. {
  77. Image: "foo/bar",
  78. TerminationMessagePath: v1.TerminationMessagePathDefault,
  79. ImagePullPolicy: v1.PullIfNotPresent,
  80. SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
  81. },
  82. },
  83. RestartPolicy: v1.RestartPolicyAlways,
  84. DNSPolicy: v1.DNSDefault,
  85. NodeSelector: map[string]string{
  86. "baz": "blah",
  87. },
  88. },
  89. },
  90. },
  91. }
  92. return rc
  93. }
  94. // create count pods with the given phase for the given rc (same selectors and namespace), and add them to the store.
  95. func newPodList(store cache.Store, count int, status v1.PodPhase, rc *v1.ReplicationController) *v1.PodList {
  96. pods := []v1.Pod{}
  97. for i := 0; i < count; i++ {
  98. newPod := v1.Pod{
  99. ObjectMeta: metav1.ObjectMeta{
  100. Name: fmt.Sprintf("pod%d", i),
  101. Labels: rc.Spec.Selector,
  102. Namespace: rc.Namespace,
  103. },
  104. Status: v1.PodStatus{Phase: status},
  105. }
  106. if store != nil {
  107. store.Add(&newPod)
  108. }
  109. pods = append(pods, newPod)
  110. }
  111. return &v1.PodList{
  112. Items: pods,
  113. }
  114. }
  115. func newReplicaSet(name string, replicas int) *apps.ReplicaSet {
  116. return &apps.ReplicaSet{
  117. TypeMeta: metav1.TypeMeta{APIVersion: "v1"},
  118. ObjectMeta: metav1.ObjectMeta{
  119. UID: uuid.NewUUID(),
  120. Name: name,
  121. Namespace: metav1.NamespaceDefault,
  122. ResourceVersion: "18",
  123. },
  124. Spec: apps.ReplicaSetSpec{
  125. Replicas: func() *int32 { i := int32(replicas); return &i }(),
  126. Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}},
  127. Template: v1.PodTemplateSpec{
  128. ObjectMeta: metav1.ObjectMeta{
  129. Labels: map[string]string{
  130. "name": "foo",
  131. "type": "production",
  132. },
  133. },
  134. Spec: v1.PodSpec{
  135. Containers: []v1.Container{
  136. {
  137. Image: "foo/bar",
  138. TerminationMessagePath: v1.TerminationMessagePathDefault,
  139. ImagePullPolicy: v1.PullIfNotPresent,
  140. SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
  141. },
  142. },
  143. RestartPolicy: v1.RestartPolicyAlways,
  144. DNSPolicy: v1.DNSDefault,
  145. NodeSelector: map[string]string{
  146. "baz": "blah",
  147. },
  148. },
  149. },
  150. },
  151. }
  152. }
  153. func TestControllerExpectations(t *testing.T) {
  154. ttl := 30 * time.Second
  155. e, fakeClock := NewFakeControllerExpectationsLookup(ttl)
  156. // In practice we can't really have add and delete expectations since we only either create or
  157. // delete replicas in one rc pass, and the rc goes to sleep soon after until the expectations are
  158. // either fulfilled or timeout.
  159. adds, dels := 10, 30
  160. rc := newReplicationController(1)
  161. // RC fires off adds and deletes at apiserver, then sets expectations
  162. rcKey, err := KeyFunc(rc)
  163. assert.NoError(t, err, "Couldn't get key for object %#v: %v", rc, err)
  164. e.SetExpectations(rcKey, adds, dels)
  165. var wg sync.WaitGroup
  166. for i := 0; i < adds+1; i++ {
  167. wg.Add(1)
  168. go func() {
  169. // In prod this can happen either because of a failed create by the rc
  170. // or after having observed a create via informer
  171. e.CreationObserved(rcKey)
  172. wg.Done()
  173. }()
  174. }
  175. wg.Wait()
  176. // There are still delete expectations
  177. assert.False(t, e.SatisfiedExpectations(rcKey), "Rc will sync before expectations are met")
  178. for i := 0; i < dels+1; i++ {
  179. wg.Add(1)
  180. go func() {
  181. e.DeletionObserved(rcKey)
  182. wg.Done()
  183. }()
  184. }
  185. wg.Wait()
  186. // Expectations have been surpassed
  187. podExp, exists, err := e.GetExpectations(rcKey)
  188. assert.NoError(t, err, "Could not get expectations for rc, exists %v and err %v", exists, err)
  189. assert.True(t, exists, "Could not get expectations for rc, exists %v and err %v", exists, err)
  190. add, del := podExp.GetExpectations()
  191. assert.Equal(t, int64(-1), add, "Unexpected pod expectations %#v", podExp)
  192. assert.Equal(t, int64(-1), del, "Unexpected pod expectations %#v", podExp)
  193. assert.True(t, e.SatisfiedExpectations(rcKey), "Expectations are met but the rc will not sync")
  194. // Next round of rc sync, old expectations are cleared
  195. e.SetExpectations(rcKey, 1, 2)
  196. podExp, exists, err = e.GetExpectations(rcKey)
  197. assert.NoError(t, err, "Could not get expectations for rc, exists %v and err %v", exists, err)
  198. assert.True(t, exists, "Could not get expectations for rc, exists %v and err %v", exists, err)
  199. add, del = podExp.GetExpectations()
  200. assert.Equal(t, int64(1), add, "Unexpected pod expectations %#v", podExp)
  201. assert.Equal(t, int64(2), del, "Unexpected pod expectations %#v", podExp)
  202. // Expectations have expired because of ttl
  203. fakeClock.Step(ttl + 1)
  204. assert.True(t, e.SatisfiedExpectations(rcKey),
  205. "Expectations should have expired but didn't")
  206. }
  207. func TestUIDExpectations(t *testing.T) {
  208. uidExp := NewUIDTrackingControllerExpectations(NewControllerExpectations())
  209. rcList := []*v1.ReplicationController{
  210. newReplicationController(2),
  211. newReplicationController(1),
  212. newReplicationController(0),
  213. newReplicationController(5),
  214. }
  215. rcToPods := map[string][]string{}
  216. rcKeys := []string{}
  217. for i := range rcList {
  218. rc := rcList[i]
  219. rcName := fmt.Sprintf("rc-%v", i)
  220. rc.Name = rcName
  221. rc.Spec.Selector[rcName] = rcName
  222. podList := newPodList(nil, 5, v1.PodRunning, rc)
  223. rcKey, err := KeyFunc(rc)
  224. if err != nil {
  225. t.Fatalf("Couldn't get key for object %#v: %v", rc, err)
  226. }
  227. rcKeys = append(rcKeys, rcKey)
  228. rcPodNames := []string{}
  229. for i := range podList.Items {
  230. p := &podList.Items[i]
  231. p.Name = fmt.Sprintf("%v-%v", p.Name, rc.Name)
  232. rcPodNames = append(rcPodNames, PodKey(p))
  233. }
  234. rcToPods[rcKey] = rcPodNames
  235. uidExp.ExpectDeletions(rcKey, rcPodNames)
  236. }
  237. for i := range rcKeys {
  238. j := rand.Intn(i + 1)
  239. rcKeys[i], rcKeys[j] = rcKeys[j], rcKeys[i]
  240. }
  241. for _, rcKey := range rcKeys {
  242. assert.False(t, uidExp.SatisfiedExpectations(rcKey),
  243. "Controller %v satisfied expectations before deletion", rcKey)
  244. for _, p := range rcToPods[rcKey] {
  245. uidExp.DeletionObserved(rcKey, p)
  246. }
  247. assert.True(t, uidExp.SatisfiedExpectations(rcKey),
  248. "Controller %v didn't satisfy expectations after deletion", rcKey)
  249. uidExp.DeleteExpectations(rcKey)
  250. assert.Nil(t, uidExp.GetUIDs(rcKey),
  251. "Failed to delete uid expectations for %v", rcKey)
  252. }
  253. }
  254. func TestCreatePods(t *testing.T) {
  255. ns := metav1.NamespaceDefault
  256. body := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "empty_pod"}})
  257. fakeHandler := utiltesting.FakeHandler{
  258. StatusCode: 200,
  259. ResponseBody: string(body),
  260. }
  261. testServer := httptest.NewServer(&fakeHandler)
  262. defer testServer.Close()
  263. clientset := clientset.NewForConfigOrDie(&restclient.Config{Host: testServer.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
  264. podControl := RealPodControl{
  265. KubeClient: clientset,
  266. Recorder: &record.FakeRecorder{},
  267. }
  268. controllerSpec := newReplicationController(1)
  269. // Make sure createReplica sends a POST to the apiserver with a pod from the controllers pod template
  270. err := podControl.CreatePods(ns, controllerSpec.Spec.Template, controllerSpec)
  271. assert.NoError(t, err, "unexpected error: %v", err)
  272. expectedPod := v1.Pod{
  273. ObjectMeta: metav1.ObjectMeta{
  274. Labels: controllerSpec.Spec.Template.Labels,
  275. GenerateName: fmt.Sprintf("%s-", controllerSpec.Name),
  276. },
  277. Spec: controllerSpec.Spec.Template.Spec,
  278. }
  279. fakeHandler.ValidateRequest(t, "/api/v1/namespaces/default/pods", "POST", nil)
  280. var actualPod = &v1.Pod{}
  281. err = json.Unmarshal([]byte(fakeHandler.RequestBody), actualPod)
  282. assert.NoError(t, err, "unexpected error: %v", err)
  283. assert.True(t, apiequality.Semantic.DeepDerivative(&expectedPod, actualPod),
  284. "Body: %s", fakeHandler.RequestBody)
  285. }
  286. func TestDeletePodsAllowsMissing(t *testing.T) {
  287. fakeClient := fake.NewSimpleClientset()
  288. podControl := RealPodControl{
  289. KubeClient: fakeClient,
  290. Recorder: &record.FakeRecorder{},
  291. }
  292. controllerSpec := newReplicationController(1)
  293. err := podControl.DeletePod("namespace-name", "podName", controllerSpec)
  294. assert.NoError(t, err, "unexpected error: %v", err)
  295. }
  296. func TestActivePodFiltering(t *testing.T) {
  297. // This rc is not needed by the test, only the newPodList to give the pods labels/a namespace.
  298. rc := newReplicationController(0)
  299. podList := newPodList(nil, 5, v1.PodRunning, rc)
  300. podList.Items[0].Status.Phase = v1.PodSucceeded
  301. podList.Items[1].Status.Phase = v1.PodFailed
  302. expectedNames := sets.NewString()
  303. for _, pod := range podList.Items[2:] {
  304. expectedNames.Insert(pod.Name)
  305. }
  306. var podPointers []*v1.Pod
  307. for i := range podList.Items {
  308. podPointers = append(podPointers, &podList.Items[i])
  309. }
  310. got := FilterActivePods(podPointers)
  311. gotNames := sets.NewString()
  312. for _, pod := range got {
  313. gotNames.Insert(pod.Name)
  314. }
  315. assert.Equal(t, 0, expectedNames.Difference(gotNames).Len(),
  316. "expected %v, got %v", expectedNames.List(), gotNames.List())
  317. assert.Equal(t, 0, gotNames.Difference(expectedNames).Len(),
  318. "expected %v, got %v", expectedNames.List(), gotNames.List())
  319. }
  320. func TestSortingActivePods(t *testing.T) {
  321. numPods := 9
  322. // This rc is not needed by the test, only the newPodList to give the pods labels/a namespace.
  323. rc := newReplicationController(0)
  324. podList := newPodList(nil, numPods, v1.PodRunning, rc)
  325. pods := make([]*v1.Pod, len(podList.Items))
  326. for i := range podList.Items {
  327. pods[i] = &podList.Items[i]
  328. }
  329. // pods[0] is not scheduled yet.
  330. pods[0].Spec.NodeName = ""
  331. pods[0].Status.Phase = v1.PodPending
  332. // pods[1] is scheduled but pending.
  333. pods[1].Spec.NodeName = "bar"
  334. pods[1].Status.Phase = v1.PodPending
  335. // pods[2] is unknown.
  336. pods[2].Spec.NodeName = "foo"
  337. pods[2].Status.Phase = v1.PodUnknown
  338. // pods[3] is running but not ready.
  339. pods[3].Spec.NodeName = "foo"
  340. pods[3].Status.Phase = v1.PodRunning
  341. // pods[4] is running and ready but without LastTransitionTime.
  342. now := metav1.Now()
  343. pods[4].Spec.NodeName = "foo"
  344. pods[4].Status.Phase = v1.PodRunning
  345. pods[4].Status.Conditions = []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionTrue}}
  346. pods[4].Status.ContainerStatuses = []v1.ContainerStatus{{RestartCount: 3}, {RestartCount: 0}}
  347. // pods[5] is running and ready and with LastTransitionTime.
  348. pods[5].Spec.NodeName = "foo"
  349. pods[5].Status.Phase = v1.PodRunning
  350. pods[5].Status.Conditions = []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionTrue, LastTransitionTime: now}}
  351. pods[5].Status.ContainerStatuses = []v1.ContainerStatus{{RestartCount: 3}, {RestartCount: 0}}
  352. // pods[6] is running ready for a longer time than pods[5].
  353. then := metav1.Time{Time: now.AddDate(0, -1, 0)}
  354. pods[6].Spec.NodeName = "foo"
  355. pods[6].Status.Phase = v1.PodRunning
  356. pods[6].Status.Conditions = []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionTrue, LastTransitionTime: then}}
  357. pods[6].Status.ContainerStatuses = []v1.ContainerStatus{{RestartCount: 3}, {RestartCount: 0}}
  358. // pods[7] has lower container restart count than pods[6].
  359. pods[7].Spec.NodeName = "foo"
  360. pods[7].Status.Phase = v1.PodRunning
  361. pods[7].Status.Conditions = []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionTrue, LastTransitionTime: then}}
  362. pods[7].Status.ContainerStatuses = []v1.ContainerStatus{{RestartCount: 2}, {RestartCount: 1}}
  363. pods[7].CreationTimestamp = now
  364. // pods[8] is older than pods[7].
  365. pods[8].Spec.NodeName = "foo"
  366. pods[8].Status.Phase = v1.PodRunning
  367. pods[8].Status.Conditions = []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionTrue, LastTransitionTime: then}}
  368. pods[8].Status.ContainerStatuses = []v1.ContainerStatus{{RestartCount: 2}, {RestartCount: 1}}
  369. pods[8].CreationTimestamp = then
  370. getOrder := func(pods []*v1.Pod) []string {
  371. names := make([]string, len(pods))
  372. for i := range pods {
  373. names[i] = pods[i].Name
  374. }
  375. return names
  376. }
  377. expected := getOrder(pods)
  378. for i := 0; i < 20; i++ {
  379. idx := rand.Perm(numPods)
  380. randomizedPods := make([]*v1.Pod, numPods)
  381. for j := 0; j < numPods; j++ {
  382. randomizedPods[j] = pods[idx[j]]
  383. }
  384. sort.Sort(ActivePods(randomizedPods))
  385. actual := getOrder(randomizedPods)
  386. assert.EqualValues(t, expected, actual, "expected %v, got %v", expected, actual)
  387. }
  388. }
  389. func TestSortingActivePodsWithRanks(t *testing.T) {
  390. now := metav1.Now()
  391. then := metav1.Time{Time: now.AddDate(0, -1, 0)}
  392. zeroTime := metav1.Time{}
  393. pod := func(podName, nodeName string, phase v1.PodPhase, ready bool, restarts int32, readySince metav1.Time, created metav1.Time) *v1.Pod {
  394. var conditions []v1.PodCondition
  395. var containerStatuses []v1.ContainerStatus
  396. if ready {
  397. conditions = []v1.PodCondition{{Type: v1.PodReady, Status: v1.ConditionTrue, LastTransitionTime: readySince}}
  398. containerStatuses = []v1.ContainerStatus{{RestartCount: restarts}}
  399. }
  400. return &v1.Pod{
  401. ObjectMeta: metav1.ObjectMeta{
  402. CreationTimestamp: created,
  403. Name: podName,
  404. },
  405. Spec: v1.PodSpec{NodeName: nodeName},
  406. Status: v1.PodStatus{
  407. Conditions: conditions,
  408. ContainerStatuses: containerStatuses,
  409. Phase: phase,
  410. },
  411. }
  412. }
  413. var (
  414. unscheduledPod = pod("unscheduled", "", v1.PodPending, false, 0, zeroTime, zeroTime)
  415. scheduledPendingPod = pod("pending", "node", v1.PodPending, false, 0, zeroTime, zeroTime)
  416. unknownPhasePod = pod("unknown-phase", "node", v1.PodUnknown, false, 0, zeroTime, zeroTime)
  417. runningNotReadyPod = pod("not-ready", "node", v1.PodRunning, false, 0, zeroTime, zeroTime)
  418. runningReadyNoLastTransitionTimePod = pod("ready-no-last-transition-time", "node", v1.PodRunning, true, 0, zeroTime, zeroTime)
  419. runningReadyNow = pod("ready-now", "node", v1.PodRunning, true, 0, now, now)
  420. runningReadyThen = pod("ready-then", "node", v1.PodRunning, true, 0, then, then)
  421. runningReadyNowHighRestarts = pod("ready-high-restarts", "node", v1.PodRunning, true, 9001, now, now)
  422. runningReadyNowCreatedThen = pod("ready-now-created-then", "node", v1.PodRunning, true, 0, now, then)
  423. )
  424. equalityTests := []*v1.Pod{
  425. unscheduledPod,
  426. scheduledPendingPod,
  427. unknownPhasePod,
  428. runningNotReadyPod,
  429. runningReadyNowCreatedThen,
  430. runningReadyNow,
  431. runningReadyThen,
  432. runningReadyNowHighRestarts,
  433. runningReadyNowCreatedThen,
  434. }
  435. for _, pod := range equalityTests {
  436. podsWithRanks := ActivePodsWithRanks{
  437. Pods: []*v1.Pod{pod, pod},
  438. Rank: []int{1, 1},
  439. }
  440. if podsWithRanks.Less(0, 1) || podsWithRanks.Less(1, 0) {
  441. t.Errorf("expected pod %q not to be less than than itself", pod.Name)
  442. }
  443. }
  444. type podWithRank struct {
  445. pod *v1.Pod
  446. rank int
  447. }
  448. inequalityTests := []struct {
  449. lesser, greater podWithRank
  450. }{
  451. {podWithRank{unscheduledPod, 1}, podWithRank{scheduledPendingPod, 2}},
  452. {podWithRank{unscheduledPod, 2}, podWithRank{scheduledPendingPod, 1}},
  453. {podWithRank{scheduledPendingPod, 1}, podWithRank{unknownPhasePod, 2}},
  454. {podWithRank{unknownPhasePod, 1}, podWithRank{runningNotReadyPod, 2}},
  455. {podWithRank{runningNotReadyPod, 1}, podWithRank{runningReadyNoLastTransitionTimePod, 1}},
  456. {podWithRank{runningReadyNoLastTransitionTimePod, 1}, podWithRank{runningReadyNow, 1}},
  457. {podWithRank{runningReadyNow, 2}, podWithRank{runningReadyNoLastTransitionTimePod, 1}},
  458. {podWithRank{runningReadyNow, 1}, podWithRank{runningReadyThen, 1}},
  459. {podWithRank{runningReadyNow, 2}, podWithRank{runningReadyThen, 1}},
  460. {podWithRank{runningReadyNowHighRestarts, 1}, podWithRank{runningReadyNow, 1}},
  461. {podWithRank{runningReadyNow, 2}, podWithRank{runningReadyNowHighRestarts, 1}},
  462. {podWithRank{runningReadyNow, 1}, podWithRank{runningReadyNowCreatedThen, 1}},
  463. {podWithRank{runningReadyNowCreatedThen, 2}, podWithRank{runningReadyNow, 1}},
  464. }
  465. for _, test := range inequalityTests {
  466. podsWithRanks := ActivePodsWithRanks{
  467. Pods: []*v1.Pod{test.lesser.pod, test.greater.pod},
  468. Rank: []int{test.lesser.rank, test.greater.rank},
  469. }
  470. if !podsWithRanks.Less(0, 1) {
  471. t.Errorf("expected pod %q with rank %v to be less than %q with rank %v", podsWithRanks.Pods[0].Name, podsWithRanks.Rank[0], podsWithRanks.Pods[1].Name, podsWithRanks.Rank[1])
  472. }
  473. if podsWithRanks.Less(1, 0) {
  474. t.Errorf("expected pod %q with rank %v not to be less than %v with rank %v", podsWithRanks.Pods[1].Name, podsWithRanks.Rank[1], podsWithRanks.Pods[0].Name, podsWithRanks.Rank[0])
  475. }
  476. }
  477. }
  478. func TestActiveReplicaSetsFiltering(t *testing.T) {
  479. var replicaSets []*apps.ReplicaSet
  480. replicaSets = append(replicaSets, newReplicaSet("zero", 0))
  481. replicaSets = append(replicaSets, nil)
  482. replicaSets = append(replicaSets, newReplicaSet("foo", 1))
  483. replicaSets = append(replicaSets, newReplicaSet("bar", 2))
  484. expectedNames := sets.NewString()
  485. for _, rs := range replicaSets[2:] {
  486. expectedNames.Insert(rs.Name)
  487. }
  488. got := FilterActiveReplicaSets(replicaSets)
  489. gotNames := sets.NewString()
  490. for _, rs := range got {
  491. gotNames.Insert(rs.Name)
  492. }
  493. assert.Equal(t, 0, expectedNames.Difference(gotNames).Len(),
  494. "expected %v, got %v", expectedNames.List(), gotNames.List())
  495. assert.Equal(t, 0, gotNames.Difference(expectedNames).Len(),
  496. "expected %v, got %v", expectedNames.List(), gotNames.List())
  497. }
  498. func TestComputeHash(t *testing.T) {
  499. collisionCount := int32(1)
  500. otherCollisionCount := int32(2)
  501. maxCollisionCount := int32(math.MaxInt32)
  502. tests := []struct {
  503. name string
  504. template *v1.PodTemplateSpec
  505. collisionCount *int32
  506. otherCollisionCount *int32
  507. }{
  508. {
  509. name: "simple",
  510. template: &v1.PodTemplateSpec{},
  511. collisionCount: &collisionCount,
  512. otherCollisionCount: &otherCollisionCount,
  513. },
  514. {
  515. name: "using math.MaxInt64",
  516. template: &v1.PodTemplateSpec{},
  517. collisionCount: nil,
  518. otherCollisionCount: &maxCollisionCount,
  519. },
  520. }
  521. for _, test := range tests {
  522. hash := ComputeHash(test.template, test.collisionCount)
  523. otherHash := ComputeHash(test.template, test.otherCollisionCount)
  524. assert.NotEqual(t, hash, otherHash, "expected different hashes but got the same: %d", hash)
  525. }
  526. }
  527. func TestRemoveTaintOffNode(t *testing.T) {
  528. tests := []struct {
  529. name string
  530. nodeHandler *testutil.FakeNodeHandler
  531. nodeName string
  532. taintsToRemove []*v1.Taint
  533. expectedTaints []v1.Taint
  534. requestCount int
  535. }{
  536. {
  537. name: "remove one taint from node",
  538. nodeHandler: &testutil.FakeNodeHandler{
  539. Existing: []*v1.Node{
  540. {
  541. ObjectMeta: metav1.ObjectMeta{
  542. Name: "node1",
  543. },
  544. Spec: v1.NodeSpec{
  545. Taints: []v1.Taint{
  546. {Key: "key1", Value: "value1", Effect: "NoSchedule"},
  547. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  548. },
  549. },
  550. },
  551. },
  552. Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
  553. },
  554. nodeName: "node1",
  555. taintsToRemove: []*v1.Taint{
  556. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  557. },
  558. expectedTaints: []v1.Taint{
  559. {Key: "key1", Value: "value1", Effect: "NoSchedule"},
  560. },
  561. requestCount: 4,
  562. },
  563. {
  564. name: "remove multiple taints from node",
  565. nodeHandler: &testutil.FakeNodeHandler{
  566. Existing: []*v1.Node{
  567. {
  568. ObjectMeta: metav1.ObjectMeta{
  569. Name: "node1",
  570. },
  571. Spec: v1.NodeSpec{
  572. Taints: []v1.Taint{
  573. {Key: "key1", Value: "value1", Effect: "NoSchedule"},
  574. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  575. {Key: "key3", Value: "value3", Effect: "NoSchedule"},
  576. {Key: "key4", Value: "value4", Effect: "NoExecute"},
  577. },
  578. },
  579. },
  580. },
  581. Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
  582. },
  583. nodeName: "node1",
  584. taintsToRemove: []*v1.Taint{
  585. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  586. {Key: "key3", Value: "value3", Effect: "NoSchedule"},
  587. },
  588. expectedTaints: []v1.Taint{
  589. {Key: "key1", Value: "value1", Effect: "NoSchedule"},
  590. {Key: "key4", Value: "value4", Effect: "NoExecute"},
  591. },
  592. requestCount: 4,
  593. },
  594. {
  595. name: "remove no-exist taints from node",
  596. nodeHandler: &testutil.FakeNodeHandler{
  597. Existing: []*v1.Node{
  598. {
  599. ObjectMeta: metav1.ObjectMeta{
  600. Name: "node1",
  601. },
  602. Spec: v1.NodeSpec{
  603. Taints: []v1.Taint{
  604. {Key: "key1", Value: "value1", Effect: "NoSchedule"},
  605. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  606. },
  607. },
  608. },
  609. },
  610. Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
  611. },
  612. nodeName: "node1",
  613. taintsToRemove: []*v1.Taint{
  614. {Key: "key3", Value: "value3", Effect: "NoSchedule"},
  615. },
  616. expectedTaints: []v1.Taint{
  617. {Key: "key1", Value: "value1", Effect: "NoSchedule"},
  618. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  619. },
  620. requestCount: 2,
  621. },
  622. {
  623. name: "remove taint from node without taints",
  624. nodeHandler: &testutil.FakeNodeHandler{
  625. Existing: []*v1.Node{
  626. {
  627. ObjectMeta: metav1.ObjectMeta{
  628. Name: "node1",
  629. },
  630. },
  631. },
  632. Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
  633. },
  634. nodeName: "node1",
  635. taintsToRemove: []*v1.Taint{
  636. {Key: "key3", Value: "value3", Effect: "NoSchedule"},
  637. },
  638. expectedTaints: nil,
  639. requestCount: 2,
  640. },
  641. {
  642. name: "remove empty taint list from node without taints",
  643. nodeHandler: &testutil.FakeNodeHandler{
  644. Existing: []*v1.Node{
  645. {
  646. ObjectMeta: metav1.ObjectMeta{
  647. Name: "node1",
  648. },
  649. },
  650. },
  651. Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
  652. },
  653. nodeName: "node1",
  654. taintsToRemove: []*v1.Taint{},
  655. expectedTaints: nil,
  656. requestCount: 2,
  657. },
  658. {
  659. name: "remove empty taint list from node",
  660. nodeHandler: &testutil.FakeNodeHandler{
  661. Existing: []*v1.Node{
  662. {
  663. ObjectMeta: metav1.ObjectMeta{
  664. Name: "node1",
  665. },
  666. Spec: v1.NodeSpec{
  667. Taints: []v1.Taint{
  668. {Key: "key1", Value: "value1", Effect: "NoSchedule"},
  669. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  670. },
  671. },
  672. },
  673. },
  674. Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
  675. },
  676. nodeName: "node1",
  677. taintsToRemove: []*v1.Taint{},
  678. expectedTaints: []v1.Taint{
  679. {Key: "key1", Value: "value1", Effect: "NoSchedule"},
  680. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  681. },
  682. requestCount: 2,
  683. },
  684. }
  685. for _, test := range tests {
  686. node, _ := test.nodeHandler.Get(context.TODO(), test.nodeName, metav1.GetOptions{})
  687. err := RemoveTaintOffNode(test.nodeHandler, test.nodeName, node, test.taintsToRemove...)
  688. assert.NoError(t, err, "%s: RemoveTaintOffNode() error = %v", test.name, err)
  689. node, _ = test.nodeHandler.Get(context.TODO(), test.nodeName, metav1.GetOptions{})
  690. assert.EqualValues(t, test.expectedTaints, node.Spec.Taints,
  691. "%s: failed to remove taint off node: expected %+v, got %+v",
  692. test.name, test.expectedTaints, node.Spec.Taints)
  693. assert.Equal(t, test.requestCount, test.nodeHandler.RequestCount,
  694. "%s: unexpected request count: expected %+v, got %+v",
  695. test.name, test.requestCount, test.nodeHandler.RequestCount)
  696. }
  697. }
  698. func TestAddOrUpdateTaintOnNode(t *testing.T) {
  699. tests := []struct {
  700. name string
  701. nodeHandler *testutil.FakeNodeHandler
  702. nodeName string
  703. taintsToAdd []*v1.Taint
  704. expectedTaints []v1.Taint
  705. requestCount int
  706. }{
  707. {
  708. name: "add one taint on node",
  709. nodeHandler: &testutil.FakeNodeHandler{
  710. Existing: []*v1.Node{
  711. {
  712. ObjectMeta: metav1.ObjectMeta{
  713. Name: "node1",
  714. },
  715. Spec: v1.NodeSpec{
  716. Taints: []v1.Taint{
  717. {Key: "key1", Value: "value1", Effect: "NoSchedule"},
  718. },
  719. },
  720. },
  721. },
  722. Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
  723. },
  724. nodeName: "node1",
  725. taintsToAdd: []*v1.Taint{
  726. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  727. },
  728. expectedTaints: []v1.Taint{
  729. {Key: "key1", Value: "value1", Effect: "NoSchedule"},
  730. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  731. },
  732. requestCount: 3,
  733. },
  734. {
  735. name: "add multiple taints to node",
  736. nodeHandler: &testutil.FakeNodeHandler{
  737. Existing: []*v1.Node{
  738. {
  739. ObjectMeta: metav1.ObjectMeta{
  740. Name: "node1",
  741. },
  742. Spec: v1.NodeSpec{
  743. Taints: []v1.Taint{
  744. {Key: "key1", Value: "value1", Effect: "NoSchedule"},
  745. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  746. },
  747. },
  748. },
  749. },
  750. Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
  751. },
  752. nodeName: "node1",
  753. taintsToAdd: []*v1.Taint{
  754. {Key: "key3", Value: "value3", Effect: "NoSchedule"},
  755. {Key: "key4", Value: "value4", Effect: "NoExecute"},
  756. },
  757. expectedTaints: []v1.Taint{
  758. {Key: "key1", Value: "value1", Effect: "NoSchedule"},
  759. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  760. {Key: "key3", Value: "value3", Effect: "NoSchedule"},
  761. {Key: "key4", Value: "value4", Effect: "NoExecute"},
  762. },
  763. requestCount: 3,
  764. },
  765. {
  766. name: "add exist taints to node",
  767. nodeHandler: &testutil.FakeNodeHandler{
  768. Existing: []*v1.Node{
  769. {
  770. ObjectMeta: metav1.ObjectMeta{
  771. Name: "node1",
  772. },
  773. Spec: v1.NodeSpec{
  774. Taints: []v1.Taint{
  775. {Key: "key1", Value: "value1", Effect: "NoSchedule"},
  776. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  777. },
  778. },
  779. },
  780. },
  781. Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
  782. },
  783. nodeName: "node1",
  784. taintsToAdd: []*v1.Taint{
  785. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  786. },
  787. expectedTaints: []v1.Taint{
  788. {Key: "key1", Value: "value1", Effect: "NoSchedule"},
  789. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  790. },
  791. requestCount: 2,
  792. },
  793. {
  794. name: "add taint to node without taints",
  795. nodeHandler: &testutil.FakeNodeHandler{
  796. Existing: []*v1.Node{
  797. {
  798. ObjectMeta: metav1.ObjectMeta{
  799. Name: "node1",
  800. },
  801. },
  802. },
  803. Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
  804. },
  805. nodeName: "node1",
  806. taintsToAdd: []*v1.Taint{
  807. {Key: "key3", Value: "value3", Effect: "NoSchedule"},
  808. },
  809. expectedTaints: []v1.Taint{
  810. {Key: "key3", Value: "value3", Effect: "NoSchedule"},
  811. },
  812. requestCount: 3,
  813. },
  814. {
  815. name: "add empty taint list to node without taints",
  816. nodeHandler: &testutil.FakeNodeHandler{
  817. Existing: []*v1.Node{
  818. {
  819. ObjectMeta: metav1.ObjectMeta{
  820. Name: "node1",
  821. },
  822. },
  823. },
  824. Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
  825. },
  826. nodeName: "node1",
  827. taintsToAdd: []*v1.Taint{},
  828. expectedTaints: nil,
  829. requestCount: 1,
  830. },
  831. {
  832. name: "add empty taint list to node",
  833. nodeHandler: &testutil.FakeNodeHandler{
  834. Existing: []*v1.Node{
  835. {
  836. ObjectMeta: metav1.ObjectMeta{
  837. Name: "node1",
  838. },
  839. Spec: v1.NodeSpec{
  840. Taints: []v1.Taint{
  841. {Key: "key1", Value: "value1", Effect: "NoSchedule"},
  842. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  843. },
  844. },
  845. },
  846. },
  847. Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
  848. },
  849. nodeName: "node1",
  850. taintsToAdd: []*v1.Taint{},
  851. expectedTaints: []v1.Taint{
  852. {Key: "key1", Value: "value1", Effect: "NoSchedule"},
  853. {Key: "key2", Value: "value2", Effect: "NoExecute"},
  854. },
  855. requestCount: 1,
  856. },
  857. }
  858. for _, test := range tests {
  859. err := AddOrUpdateTaintOnNode(test.nodeHandler, test.nodeName, test.taintsToAdd...)
  860. assert.NoError(t, err, "%s: AddOrUpdateTaintOnNode() error = %v", test.name, err)
  861. node, _ := test.nodeHandler.Get(context.TODO(), test.nodeName, metav1.GetOptions{})
  862. assert.EqualValues(t, test.expectedTaints, node.Spec.Taints,
  863. "%s: failed to add taint to node: expected %+v, got %+v",
  864. test.name, test.expectedTaints, node.Spec.Taints)
  865. assert.Equal(t, test.requestCount, test.nodeHandler.RequestCount,
  866. "%s: unexpected request count: expected %+v, got %+v",
  867. test.name, test.requestCount, test.nodeHandler.RequestCount)
  868. }
  869. }