controller_utils_test.go 26 KB

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