controller_utils_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. /*
  2. Copyright 2019 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 endpoint
  14. import (
  15. "fmt"
  16. "reflect"
  17. "testing"
  18. "time"
  19. v1 "k8s.io/api/core/v1"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. "k8s.io/apimachinery/pkg/labels"
  22. "k8s.io/apimachinery/pkg/util/sets"
  23. "k8s.io/client-go/informers"
  24. "k8s.io/client-go/kubernetes/fake"
  25. )
  26. func TestDetermineNeededServiceUpdates(t *testing.T) {
  27. testCases := []struct {
  28. name string
  29. a sets.String
  30. b sets.String
  31. union sets.String
  32. xor sets.String
  33. }{
  34. {
  35. name: "no services changed",
  36. a: sets.NewString("a", "b", "c"),
  37. b: sets.NewString("a", "b", "c"),
  38. xor: sets.NewString(),
  39. union: sets.NewString("a", "b", "c"),
  40. },
  41. {
  42. name: "all old services removed, new services added",
  43. a: sets.NewString("a", "b", "c"),
  44. b: sets.NewString("d", "e", "f"),
  45. xor: sets.NewString("a", "b", "c", "d", "e", "f"),
  46. union: sets.NewString("a", "b", "c", "d", "e", "f"),
  47. },
  48. {
  49. name: "all old services removed, no new services added",
  50. a: sets.NewString("a", "b", "c"),
  51. b: sets.NewString(),
  52. xor: sets.NewString("a", "b", "c"),
  53. union: sets.NewString("a", "b", "c"),
  54. },
  55. {
  56. name: "no old services, but new services added",
  57. a: sets.NewString(),
  58. b: sets.NewString("a", "b", "c"),
  59. xor: sets.NewString("a", "b", "c"),
  60. union: sets.NewString("a", "b", "c"),
  61. },
  62. {
  63. name: "one service removed, one service added, two unchanged",
  64. a: sets.NewString("a", "b", "c"),
  65. b: sets.NewString("b", "c", "d"),
  66. xor: sets.NewString("a", "d"),
  67. union: sets.NewString("a", "b", "c", "d"),
  68. },
  69. {
  70. name: "no services",
  71. a: sets.NewString(),
  72. b: sets.NewString(),
  73. xor: sets.NewString(),
  74. union: sets.NewString(),
  75. },
  76. }
  77. for _, testCase := range testCases {
  78. t.Run(testCase.name, func(t *testing.T) {
  79. retval := determineNeededServiceUpdates(testCase.a, testCase.b, false)
  80. if !retval.Equal(testCase.xor) {
  81. t.Errorf("%s (with podChanged=false): expected: %v got: %v", testCase.name, testCase.xor.List(), retval.List())
  82. }
  83. retval = determineNeededServiceUpdates(testCase.a, testCase.b, true)
  84. if !retval.Equal(testCase.union) {
  85. t.Errorf("%s (with podChanged=true): expected: %v got: %v", testCase.name, testCase.union.List(), retval.List())
  86. }
  87. })
  88. }
  89. }
  90. // There are 3*5 possibilities(3 types of RestartPolicy by 5 types of PodPhase).
  91. // Not listing them all here. Just listing all of the 3 false cases and 3 of the
  92. // 12 true cases.
  93. func TestShouldPodBeInEndpoints(t *testing.T) {
  94. testCases := []struct {
  95. name string
  96. pod *v1.Pod
  97. expected bool
  98. }{
  99. // Pod should not be in endpoints:
  100. {
  101. name: "Failed pod with Never RestartPolicy",
  102. pod: &v1.Pod{
  103. Spec: v1.PodSpec{
  104. RestartPolicy: v1.RestartPolicyNever,
  105. },
  106. Status: v1.PodStatus{
  107. Phase: v1.PodFailed,
  108. PodIP: "1.2.3.4",
  109. },
  110. },
  111. expected: false,
  112. },
  113. {
  114. name: "Succeeded pod with Never RestartPolicy",
  115. pod: &v1.Pod{
  116. Spec: v1.PodSpec{
  117. RestartPolicy: v1.RestartPolicyNever,
  118. },
  119. Status: v1.PodStatus{
  120. Phase: v1.PodSucceeded,
  121. PodIP: "1.2.3.4",
  122. },
  123. },
  124. expected: false,
  125. },
  126. {
  127. name: "Succeeded pod with OnFailure RestartPolicy",
  128. pod: &v1.Pod{
  129. Spec: v1.PodSpec{
  130. RestartPolicy: v1.RestartPolicyOnFailure,
  131. },
  132. Status: v1.PodStatus{
  133. Phase: v1.PodSucceeded,
  134. PodIP: "1.2.3.4",
  135. },
  136. },
  137. expected: false,
  138. },
  139. {
  140. name: "Empty Pod IPs, Running pod with OnFailure RestartPolicy",
  141. pod: &v1.Pod{
  142. Spec: v1.PodSpec{
  143. RestartPolicy: v1.RestartPolicyNever,
  144. },
  145. Status: v1.PodStatus{
  146. Phase: v1.PodRunning,
  147. PodIP: "",
  148. PodIPs: []v1.PodIP{},
  149. },
  150. },
  151. expected: false,
  152. },
  153. // Pod should be in endpoints:
  154. {
  155. name: "Failed pod with Always RestartPolicy",
  156. pod: &v1.Pod{
  157. Spec: v1.PodSpec{
  158. RestartPolicy: v1.RestartPolicyAlways,
  159. },
  160. Status: v1.PodStatus{
  161. Phase: v1.PodFailed,
  162. PodIP: "1.2.3.4",
  163. },
  164. },
  165. expected: true,
  166. },
  167. {
  168. name: "Pending pod with Never RestartPolicy",
  169. pod: &v1.Pod{
  170. Spec: v1.PodSpec{
  171. RestartPolicy: v1.RestartPolicyNever,
  172. },
  173. Status: v1.PodStatus{
  174. Phase: v1.PodPending,
  175. PodIP: "1.2.3.4",
  176. },
  177. },
  178. expected: true,
  179. },
  180. {
  181. name: "Unknown pod with OnFailure RestartPolicy",
  182. pod: &v1.Pod{
  183. Spec: v1.PodSpec{
  184. RestartPolicy: v1.RestartPolicyOnFailure,
  185. },
  186. Status: v1.PodStatus{
  187. Phase: v1.PodUnknown,
  188. PodIP: "1.2.3.4",
  189. },
  190. },
  191. expected: true,
  192. },
  193. {
  194. name: "Running pod with Never RestartPolicy",
  195. pod: &v1.Pod{
  196. Spec: v1.PodSpec{
  197. RestartPolicy: v1.RestartPolicyNever,
  198. },
  199. Status: v1.PodStatus{
  200. Phase: v1.PodRunning,
  201. PodIP: "1.2.3.4",
  202. },
  203. },
  204. expected: true,
  205. },
  206. {
  207. name: "Multiple Pod IPs, Running pod with OnFailure RestartPolicy",
  208. pod: &v1.Pod{
  209. Spec: v1.PodSpec{
  210. RestartPolicy: v1.RestartPolicyNever,
  211. },
  212. Status: v1.PodStatus{
  213. Phase: v1.PodRunning,
  214. PodIPs: []v1.PodIP{{IP: "1.2.3.4"}, {IP: "1234::5678:0000:0000:9abc:def0"}},
  215. },
  216. },
  217. expected: true,
  218. },
  219. }
  220. for _, test := range testCases {
  221. t.Run(test.name, func(t *testing.T) {
  222. result := ShouldPodBeInEndpoints(test.pod)
  223. if result != test.expected {
  224. t.Errorf("expected: %t, got: %t", test.expected, result)
  225. }
  226. })
  227. }
  228. }
  229. func TestShouldSetHostname(t *testing.T) {
  230. testCases := map[string]struct {
  231. pod *v1.Pod
  232. service *v1.Service
  233. expected bool
  234. }{
  235. "all matching": {
  236. pod: genSimplePod("ns", "foo", "svc-name"),
  237. service: genSimpleSvc("ns", "svc-name"),
  238. expected: true,
  239. },
  240. "all matching, hostname not set": {
  241. pod: genSimplePod("ns", "", "svc-name"),
  242. service: genSimpleSvc("ns", "svc-name"),
  243. expected: false,
  244. },
  245. "all set, different name/subdomain": {
  246. pod: genSimplePod("ns", "hostname", "subdomain"),
  247. service: genSimpleSvc("ns", "name"),
  248. expected: false,
  249. },
  250. "all set, different namespace": {
  251. pod: genSimplePod("ns1", "hostname", "svc-name"),
  252. service: genSimpleSvc("ns2", "svc-name"),
  253. expected: false,
  254. },
  255. }
  256. for name, testCase := range testCases {
  257. t.Run(name, func(t *testing.T) {
  258. result := ShouldSetHostname(testCase.pod, testCase.service)
  259. if result != testCase.expected {
  260. t.Errorf("expected: %t, got: %t", testCase.expected, result)
  261. }
  262. })
  263. }
  264. }
  265. func genSimplePod(namespace, hostname, subdomain string) *v1.Pod {
  266. return &v1.Pod{
  267. ObjectMeta: metav1.ObjectMeta{
  268. Namespace: namespace,
  269. },
  270. Spec: v1.PodSpec{
  271. Hostname: hostname,
  272. Subdomain: subdomain,
  273. },
  274. }
  275. }
  276. func genSimpleSvc(namespace, name string) *v1.Service {
  277. return &v1.Service{
  278. ObjectMeta: metav1.ObjectMeta{
  279. Name: name,
  280. Namespace: namespace,
  281. },
  282. }
  283. }
  284. func TestServiceSelectorCache_GetPodServiceMemberships(t *testing.T) {
  285. fakeInformerFactory := informers.NewSharedInformerFactory(&fake.Clientset{}, 0*time.Second)
  286. for i := 0; i < 3; i++ {
  287. service := &v1.Service{
  288. ObjectMeta: metav1.ObjectMeta{
  289. Name: fmt.Sprintf("service-%d", i),
  290. Namespace: "test",
  291. },
  292. Spec: v1.ServiceSpec{
  293. Selector: map[string]string{
  294. "app": fmt.Sprintf("test-%d", i),
  295. },
  296. },
  297. }
  298. fakeInformerFactory.Core().V1().Services().Informer().GetStore().Add(service)
  299. }
  300. var pods []*v1.Pod
  301. for i := 0; i < 5; i++ {
  302. pod := &v1.Pod{
  303. ObjectMeta: metav1.ObjectMeta{
  304. Namespace: "test",
  305. Name: fmt.Sprintf("test-pod-%d", i),
  306. Labels: map[string]string{
  307. "app": fmt.Sprintf("test-%d", i),
  308. "label": fmt.Sprintf("label-%d", i),
  309. },
  310. },
  311. }
  312. pods = append(pods, pod)
  313. }
  314. cache := NewServiceSelectorCache()
  315. tests := []struct {
  316. name string
  317. pod *v1.Pod
  318. expect sets.String
  319. }{
  320. {
  321. name: "get servicesMemberships for pod-0",
  322. pod: pods[0],
  323. expect: sets.NewString("test/service-0"),
  324. },
  325. {
  326. name: "get servicesMemberships for pod-1",
  327. pod: pods[1],
  328. expect: sets.NewString("test/service-1"),
  329. },
  330. {
  331. name: "get servicesMemberships for pod-2",
  332. pod: pods[2],
  333. expect: sets.NewString("test/service-2"),
  334. },
  335. {
  336. name: "get servicesMemberships for pod-3",
  337. pod: pods[3],
  338. expect: sets.NewString(),
  339. },
  340. {
  341. name: "get servicesMemberships for pod-4",
  342. pod: pods[4],
  343. expect: sets.NewString(),
  344. },
  345. }
  346. for _, test := range tests {
  347. t.Run(test.name, func(t *testing.T) {
  348. services, err := cache.GetPodServiceMemberships(fakeInformerFactory.Core().V1().Services().Lister(), test.pod)
  349. if err != nil {
  350. t.Errorf("Error from cache.GetPodServiceMemberships: %v", err)
  351. } else if !services.Equal(test.expect) {
  352. t.Errorf("Expect service %v, but got %v", test.expect, services)
  353. }
  354. })
  355. }
  356. }
  357. func TestServiceSelectorCache_Update(t *testing.T) {
  358. var selectors []labels.Selector
  359. for i := 0; i < 5; i++ {
  360. selector := labels.Set(map[string]string{"app": fmt.Sprintf("test-%d", i)}).AsSelectorPreValidated()
  361. selectors = append(selectors, selector)
  362. }
  363. tests := []struct {
  364. name string
  365. key string
  366. cache *ServiceSelectorCache
  367. update map[string]string
  368. expect labels.Selector
  369. }{
  370. {
  371. name: "add test/service-0",
  372. key: "test/service-0",
  373. cache: generateServiceSelectorCache(map[string]labels.Selector{}),
  374. update: map[string]string{"app": "test-0"},
  375. expect: selectors[0],
  376. },
  377. {
  378. name: "add test/service-1",
  379. key: "test/service-1",
  380. cache: generateServiceSelectorCache(map[string]labels.Selector{"test/service-0": selectors[0]}),
  381. update: map[string]string{"app": "test-1"},
  382. expect: selectors[1],
  383. },
  384. {
  385. name: "update test/service-2",
  386. key: "test/service-2",
  387. cache: generateServiceSelectorCache(map[string]labels.Selector{"test/service-2": selectors[2]}),
  388. update: map[string]string{"app": "test-0"},
  389. expect: selectors[0],
  390. },
  391. }
  392. for _, test := range tests {
  393. t.Run(test.name, func(t *testing.T) {
  394. selector := test.cache.Update(test.key, test.update)
  395. if !reflect.DeepEqual(selector, test.expect) {
  396. t.Errorf("Expect selector %v , but got %v", test.expect, selector)
  397. }
  398. })
  399. }
  400. }
  401. func generateServiceSelectorCache(cache map[string]labels.Selector) *ServiceSelectorCache {
  402. return &ServiceSelectorCache{
  403. cache: cache,
  404. }
  405. }
  406. func BenchmarkGetPodServiceMemberships(b *testing.B) {
  407. // init fake service informer.
  408. fakeInformerFactory := informers.NewSharedInformerFactory(&fake.Clientset{}, 0*time.Second)
  409. for i := 0; i < 1000; i++ {
  410. service := &v1.Service{
  411. ObjectMeta: metav1.ObjectMeta{
  412. Name: fmt.Sprintf("service-%d", i),
  413. Namespace: "test",
  414. },
  415. Spec: v1.ServiceSpec{
  416. Selector: map[string]string{
  417. "app": fmt.Sprintf("test-%d", i),
  418. },
  419. },
  420. }
  421. fakeInformerFactory.Core().V1().Services().Informer().GetStore().Add(service)
  422. }
  423. pod := &v1.Pod{
  424. ObjectMeta: metav1.ObjectMeta{
  425. Namespace: "test",
  426. Name: "test-pod-0",
  427. Labels: map[string]string{
  428. "app": "test-0",
  429. },
  430. },
  431. }
  432. cache := NewServiceSelectorCache()
  433. expect := sets.NewString("test/service-0")
  434. b.ResetTimer()
  435. for i := 0; i < b.N; i++ {
  436. services, err := cache.GetPodServiceMemberships(fakeInformerFactory.Core().V1().Services().Lister(), pod)
  437. if err != nil {
  438. b.Fatalf("Error from GetPodServiceMemberships(): %v", err)
  439. }
  440. if len(services) != len(expect) {
  441. b.Errorf("Expect services size %d, but got: %v", len(expect), len(services))
  442. }
  443. }
  444. }