ttl_controller_test.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /*
  2. Copyright 2017 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 ttl
  14. import (
  15. "testing"
  16. "k8s.io/api/core/v1"
  17. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  18. "k8s.io/client-go/kubernetes/fake"
  19. listers "k8s.io/client-go/listers/core/v1"
  20. core "k8s.io/client-go/testing"
  21. "k8s.io/client-go/tools/cache"
  22. "k8s.io/client-go/util/workqueue"
  23. "github.com/stretchr/testify/assert"
  24. )
  25. func TestPatchNode(t *testing.T) {
  26. testCases := []struct {
  27. node *v1.Node
  28. ttlSeconds int
  29. patch string
  30. }{
  31. {
  32. node: &v1.Node{},
  33. ttlSeconds: 0,
  34. patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"0\"}}}",
  35. },
  36. {
  37. node: &v1.Node{},
  38. ttlSeconds: 10,
  39. patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"10\"}}}",
  40. },
  41. {
  42. node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "name"}},
  43. ttlSeconds: 10,
  44. patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"10\"}}}",
  45. },
  46. {
  47. node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}}},
  48. ttlSeconds: 10,
  49. patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"10\"}}}",
  50. },
  51. {
  52. node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"node.alpha.kubernetes.io/ttl": "0"}}},
  53. ttlSeconds: 10,
  54. patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"10\"}}}",
  55. },
  56. {
  57. node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"node.alpha.kubernetes.io/ttl": "0", "a": "b"}}},
  58. ttlSeconds: 10,
  59. patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"10\"}}}",
  60. },
  61. {
  62. node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"node.alpha.kubernetes.io/ttl": "10", "a": "b"}}},
  63. ttlSeconds: 10,
  64. patch: "{}",
  65. },
  66. }
  67. for i, testCase := range testCases {
  68. fakeClient := &fake.Clientset{}
  69. ttlController := &TTLController{
  70. kubeClient: fakeClient,
  71. }
  72. err := ttlController.patchNodeWithAnnotation(testCase.node, v1.ObjectTTLAnnotationKey, testCase.ttlSeconds)
  73. if err != nil {
  74. t.Errorf("%d: unexpected error: %v", i, err)
  75. continue
  76. }
  77. actions := fakeClient.Actions()
  78. assert.Equal(t, 1, len(actions), "unexpected actions: %#v", actions)
  79. patchAction := actions[0].(core.PatchActionImpl)
  80. assert.Equal(t, testCase.patch, string(patchAction.Patch), "%d: unexpected patch: %s", i, string(patchAction.Patch))
  81. }
  82. }
  83. func TestUpdateNodeIfNeeded(t *testing.T) {
  84. testCases := []struct {
  85. node *v1.Node
  86. desiredTTL int
  87. patch string
  88. }{
  89. {
  90. node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "name"}},
  91. desiredTTL: 0,
  92. patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"0\"}}}",
  93. },
  94. {
  95. node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "name"}},
  96. desiredTTL: 15,
  97. patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"15\"}}}",
  98. },
  99. {
  100. node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "name"}},
  101. desiredTTL: 30,
  102. patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"30\"}}}",
  103. },
  104. {
  105. node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "name", Annotations: map[string]string{"node.alpha.kubernetes.io/ttl": "0"}}},
  106. desiredTTL: 60,
  107. patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"60\"}}}",
  108. },
  109. {
  110. node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "name", Annotations: map[string]string{"node.alpha.kubernetes.io/ttl": "60"}}},
  111. desiredTTL: 60,
  112. patch: "",
  113. },
  114. {
  115. node: &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "name", Annotations: map[string]string{"node.alpha.kubernetes.io/ttl": "60"}}},
  116. desiredTTL: 30,
  117. patch: "{\"metadata\":{\"annotations\":{\"node.alpha.kubernetes.io/ttl\":\"30\"}}}",
  118. },
  119. }
  120. for i, testCase := range testCases {
  121. fakeClient := &fake.Clientset{}
  122. nodeStore := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
  123. nodeStore.Add(testCase.node)
  124. ttlController := &TTLController{
  125. kubeClient: fakeClient,
  126. nodeStore: listers.NewNodeLister(nodeStore),
  127. desiredTTLSeconds: testCase.desiredTTL,
  128. }
  129. if err := ttlController.updateNodeIfNeeded(testCase.node.Name); err != nil {
  130. t.Errorf("%d: unexpected error: %v", i, err)
  131. continue
  132. }
  133. actions := fakeClient.Actions()
  134. if testCase.patch == "" {
  135. assert.Equal(t, 0, len(actions), "unexpected actions: %#v", actions)
  136. } else {
  137. assert.Equal(t, 1, len(actions), "unexpected actions: %#v", actions)
  138. patchAction := actions[0].(core.PatchActionImpl)
  139. assert.Equal(t, testCase.patch, string(patchAction.Patch), "%d: unexpected patch: %s", i, string(patchAction.Patch))
  140. }
  141. }
  142. }
  143. func TestDesiredTTL(t *testing.T) {
  144. testCases := []struct {
  145. addNode bool
  146. deleteNode bool
  147. nodeCount int
  148. desiredTTL int
  149. boundaryStep int
  150. expectedTTL int
  151. }{
  152. {
  153. addNode: true,
  154. nodeCount: 0,
  155. desiredTTL: 0,
  156. boundaryStep: 0,
  157. expectedTTL: 0,
  158. },
  159. {
  160. addNode: true,
  161. nodeCount: 99,
  162. desiredTTL: 0,
  163. boundaryStep: 0,
  164. expectedTTL: 0,
  165. },
  166. {
  167. addNode: true,
  168. nodeCount: 100,
  169. desiredTTL: 0,
  170. boundaryStep: 0,
  171. expectedTTL: 15,
  172. },
  173. {
  174. deleteNode: true,
  175. nodeCount: 101,
  176. desiredTTL: 15,
  177. boundaryStep: 1,
  178. expectedTTL: 15,
  179. },
  180. {
  181. deleteNode: true,
  182. nodeCount: 91,
  183. desiredTTL: 15,
  184. boundaryStep: 1,
  185. expectedTTL: 15,
  186. },
  187. {
  188. addNode: true,
  189. nodeCount: 91,
  190. desiredTTL: 15,
  191. boundaryStep: 1,
  192. expectedTTL: 15,
  193. },
  194. {
  195. deleteNode: true,
  196. nodeCount: 90,
  197. desiredTTL: 15,
  198. boundaryStep: 1,
  199. expectedTTL: 0,
  200. },
  201. }
  202. for i, testCase := range testCases {
  203. ttlController := &TTLController{
  204. queue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
  205. nodeCount: testCase.nodeCount,
  206. desiredTTLSeconds: testCase.desiredTTL,
  207. boundaryStep: testCase.boundaryStep,
  208. }
  209. if testCase.addNode {
  210. ttlController.addNode(&v1.Node{})
  211. }
  212. if testCase.deleteNode {
  213. ttlController.deleteNode(&v1.Node{})
  214. }
  215. assert.Equal(t, testCase.expectedTTL, ttlController.getDesiredTTLSeconds(),
  216. "%d: unexpected ttl: %d", i, ttlController.getDesiredTTLSeconds())
  217. }
  218. }