controller_test.go 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495
  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 service
  14. import (
  15. "context"
  16. "errors"
  17. "fmt"
  18. "reflect"
  19. "sort"
  20. "strings"
  21. "testing"
  22. "time"
  23. v1 "k8s.io/api/core/v1"
  24. apierrors "k8s.io/apimachinery/pkg/api/errors"
  25. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  26. "k8s.io/apimachinery/pkg/runtime"
  27. "k8s.io/apimachinery/pkg/runtime/schema"
  28. "k8s.io/apimachinery/pkg/types"
  29. "k8s.io/apimachinery/pkg/util/intstr"
  30. utilfeature "k8s.io/apiserver/pkg/util/feature"
  31. "k8s.io/client-go/informers"
  32. "k8s.io/client-go/kubernetes/fake"
  33. core "k8s.io/client-go/testing"
  34. "k8s.io/client-go/tools/record"
  35. fakecloud "k8s.io/cloud-provider/fake"
  36. servicehelper "k8s.io/cloud-provider/service/helpers"
  37. featuregatetesting "k8s.io/component-base/featuregate/testing"
  38. "k8s.io/kubernetes/pkg/controller"
  39. )
  40. const region = "us-central"
  41. func newService(name string, uid types.UID, serviceType v1.ServiceType) *v1.Service {
  42. return &v1.Service{
  43. ObjectMeta: metav1.ObjectMeta{
  44. Name: name,
  45. Namespace: "default",
  46. UID: uid,
  47. SelfLink: "/api/v1/namespaces/default/services/" + name,
  48. },
  49. Spec: v1.ServiceSpec{
  50. Type: serviceType,
  51. },
  52. }
  53. }
  54. //Wrap newService so that you don't have to call default arguments again and again.
  55. func defaultExternalService() *v1.Service {
  56. return newService("external-balancer", types.UID("123"), v1.ServiceTypeLoadBalancer)
  57. }
  58. func alwaysReady() bool { return true }
  59. func newController() (*Controller, *fakecloud.Cloud, *fake.Clientset) {
  60. cloud := &fakecloud.Cloud{}
  61. cloud.Region = region
  62. client := fake.NewSimpleClientset()
  63. informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
  64. serviceInformer := informerFactory.Core().V1().Services()
  65. nodeInformer := informerFactory.Core().V1().Nodes()
  66. controller, _ := New(cloud, client, serviceInformer, nodeInformer, "test-cluster")
  67. controller.nodeListerSynced = alwaysReady
  68. controller.serviceListerSynced = alwaysReady
  69. controller.eventRecorder = record.NewFakeRecorder(100)
  70. cloud.Calls = nil // ignore any cloud calls made in init()
  71. client.ClearActions() // ignore any client calls made in init()
  72. return controller, cloud, client
  73. }
  74. // TODO(@MrHohn): Verify the end state when below issue is resolved:
  75. // https://github.com/kubernetes/client-go/issues/607
  76. func TestSyncLoadBalancerIfNeeded(t *testing.T) {
  77. testCases := []struct {
  78. desc string
  79. service *v1.Service
  80. lbExists bool
  81. expectOp loadBalancerOperation
  82. expectCreateAttempt bool
  83. expectDeleteAttempt bool
  84. expectPatchStatus bool
  85. expectPatchFinalizer bool
  86. }{
  87. {
  88. desc: "service doesn't want LB",
  89. service: &v1.Service{
  90. ObjectMeta: metav1.ObjectMeta{
  91. Name: "no-external-balancer",
  92. Namespace: "default",
  93. },
  94. Spec: v1.ServiceSpec{
  95. Type: v1.ServiceTypeClusterIP,
  96. },
  97. },
  98. expectOp: deleteLoadBalancer,
  99. expectPatchStatus: false,
  100. },
  101. {
  102. desc: "service no longer wants LB",
  103. service: &v1.Service{
  104. ObjectMeta: metav1.ObjectMeta{
  105. Name: "no-external-balancer",
  106. Namespace: "default",
  107. },
  108. Spec: v1.ServiceSpec{
  109. Type: v1.ServiceTypeClusterIP,
  110. },
  111. Status: v1.ServiceStatus{
  112. LoadBalancer: v1.LoadBalancerStatus{
  113. Ingress: []v1.LoadBalancerIngress{
  114. {IP: "8.8.8.8"},
  115. },
  116. },
  117. },
  118. },
  119. lbExists: true,
  120. expectOp: deleteLoadBalancer,
  121. expectDeleteAttempt: true,
  122. expectPatchStatus: true,
  123. },
  124. {
  125. desc: "udp service that wants LB",
  126. service: &v1.Service{
  127. ObjectMeta: metav1.ObjectMeta{
  128. Name: "udp-service",
  129. Namespace: "default",
  130. SelfLink: "/api/v1/namespaces/default/services/udp-service",
  131. },
  132. Spec: v1.ServiceSpec{
  133. Ports: []v1.ServicePort{{
  134. Port: 80,
  135. Protocol: v1.ProtocolUDP,
  136. }},
  137. Type: v1.ServiceTypeLoadBalancer,
  138. },
  139. },
  140. expectOp: ensureLoadBalancer,
  141. expectCreateAttempt: true,
  142. expectPatchStatus: true,
  143. expectPatchFinalizer: true,
  144. },
  145. {
  146. desc: "tcp service that wants LB",
  147. service: &v1.Service{
  148. ObjectMeta: metav1.ObjectMeta{
  149. Name: "basic-service1",
  150. Namespace: "default",
  151. SelfLink: "/api/v1/namespaces/default/services/basic-service1",
  152. },
  153. Spec: v1.ServiceSpec{
  154. Ports: []v1.ServicePort{{
  155. Port: 80,
  156. Protocol: v1.ProtocolTCP,
  157. }},
  158. Type: v1.ServiceTypeLoadBalancer,
  159. },
  160. },
  161. expectOp: ensureLoadBalancer,
  162. expectCreateAttempt: true,
  163. expectPatchStatus: true,
  164. expectPatchFinalizer: true,
  165. },
  166. {
  167. desc: "sctp service that wants LB",
  168. service: &v1.Service{
  169. ObjectMeta: metav1.ObjectMeta{
  170. Name: "sctp-service",
  171. Namespace: "default",
  172. SelfLink: "/api/v1/namespaces/default/services/sctp-service",
  173. },
  174. Spec: v1.ServiceSpec{
  175. Ports: []v1.ServicePort{{
  176. Port: 80,
  177. Protocol: v1.ProtocolSCTP,
  178. }},
  179. Type: v1.ServiceTypeLoadBalancer,
  180. },
  181. },
  182. expectOp: ensureLoadBalancer,
  183. expectCreateAttempt: true,
  184. expectPatchStatus: true,
  185. expectPatchFinalizer: true,
  186. },
  187. // Finalizer test cases below.
  188. {
  189. desc: "service with finalizer that no longer wants LB",
  190. service: &v1.Service{
  191. ObjectMeta: metav1.ObjectMeta{
  192. Name: "no-external-balancer",
  193. Namespace: "default",
  194. Finalizers: []string{servicehelper.LoadBalancerCleanupFinalizer},
  195. },
  196. Spec: v1.ServiceSpec{
  197. Type: v1.ServiceTypeClusterIP,
  198. },
  199. Status: v1.ServiceStatus{
  200. LoadBalancer: v1.LoadBalancerStatus{
  201. Ingress: []v1.LoadBalancerIngress{
  202. {IP: "8.8.8.8"},
  203. },
  204. },
  205. },
  206. },
  207. lbExists: true,
  208. expectOp: deleteLoadBalancer,
  209. expectDeleteAttempt: true,
  210. expectPatchStatus: true,
  211. expectPatchFinalizer: true,
  212. },
  213. {
  214. desc: "service that needs cleanup",
  215. service: &v1.Service{
  216. ObjectMeta: metav1.ObjectMeta{
  217. Name: "basic-service1",
  218. Namespace: "default",
  219. SelfLink: "/api/v1/namespaces/default/services/basic-service1",
  220. DeletionTimestamp: &metav1.Time{
  221. Time: time.Now(),
  222. },
  223. Finalizers: []string{servicehelper.LoadBalancerCleanupFinalizer},
  224. },
  225. Spec: v1.ServiceSpec{
  226. Ports: []v1.ServicePort{{
  227. Port: 80,
  228. Protocol: v1.ProtocolTCP,
  229. }},
  230. Type: v1.ServiceTypeLoadBalancer,
  231. },
  232. Status: v1.ServiceStatus{
  233. LoadBalancer: v1.LoadBalancerStatus{
  234. Ingress: []v1.LoadBalancerIngress{
  235. {IP: "8.8.8.8"},
  236. },
  237. },
  238. },
  239. },
  240. lbExists: true,
  241. expectOp: deleteLoadBalancer,
  242. expectDeleteAttempt: true,
  243. expectPatchStatus: true,
  244. expectPatchFinalizer: true,
  245. },
  246. {
  247. desc: "service without finalizer that wants LB",
  248. service: &v1.Service{
  249. ObjectMeta: metav1.ObjectMeta{
  250. Name: "basic-service1",
  251. Namespace: "default",
  252. SelfLink: "/api/v1/namespaces/default/services/basic-service1",
  253. },
  254. Spec: v1.ServiceSpec{
  255. Ports: []v1.ServicePort{{
  256. Port: 80,
  257. Protocol: v1.ProtocolTCP,
  258. }},
  259. Type: v1.ServiceTypeLoadBalancer,
  260. },
  261. },
  262. expectOp: ensureLoadBalancer,
  263. expectCreateAttempt: true,
  264. expectPatchStatus: true,
  265. expectPatchFinalizer: true,
  266. },
  267. {
  268. desc: "service with finalizer that wants LB",
  269. service: &v1.Service{
  270. ObjectMeta: metav1.ObjectMeta{
  271. Name: "basic-service1",
  272. Namespace: "default",
  273. SelfLink: "/api/v1/namespaces/default/services/basic-service1",
  274. Finalizers: []string{servicehelper.LoadBalancerCleanupFinalizer},
  275. },
  276. Spec: v1.ServiceSpec{
  277. Ports: []v1.ServicePort{{
  278. Port: 80,
  279. Protocol: v1.ProtocolTCP,
  280. }},
  281. Type: v1.ServiceTypeLoadBalancer,
  282. },
  283. },
  284. expectOp: ensureLoadBalancer,
  285. expectCreateAttempt: true,
  286. expectPatchStatus: true,
  287. expectPatchFinalizer: false,
  288. },
  289. }
  290. for _, tc := range testCases {
  291. t.Run(tc.desc, func(t *testing.T) {
  292. controller, cloud, client := newController()
  293. cloud.Exists = tc.lbExists
  294. key := fmt.Sprintf("%s/%s", tc.service.Namespace, tc.service.Name)
  295. if _, err := client.CoreV1().Services(tc.service.Namespace).Create(context.TODO(), tc.service, metav1.CreateOptions{}); err != nil {
  296. t.Fatalf("Failed to prepare service %s for testing: %v", key, err)
  297. }
  298. client.ClearActions()
  299. op, err := controller.syncLoadBalancerIfNeeded(tc.service, key)
  300. if err != nil {
  301. t.Errorf("Got error: %v, want nil", err)
  302. }
  303. if op != tc.expectOp {
  304. t.Errorf("Got operation %v, want %v", op, tc.expectOp)
  305. }
  306. // Capture actions from test so it won't be messed up.
  307. actions := client.Actions()
  308. if !tc.expectCreateAttempt && !tc.expectDeleteAttempt {
  309. if len(cloud.Calls) > 0 {
  310. t.Errorf("Unexpected cloud provider calls: %v", cloud.Calls)
  311. }
  312. if len(actions) > 0 {
  313. t.Errorf("Unexpected client actions: %v", actions)
  314. }
  315. return
  316. }
  317. if tc.expectCreateAttempt {
  318. createCallFound := false
  319. for _, call := range cloud.Calls {
  320. if call == "create" {
  321. createCallFound = true
  322. }
  323. }
  324. if !createCallFound {
  325. t.Errorf("Got no create call for load balancer, expected one")
  326. }
  327. // TODO(@MrHohn): Clean up the awkward pattern here.
  328. var balancer *fakecloud.Balancer
  329. for k := range cloud.Balancers {
  330. if balancer == nil {
  331. b := cloud.Balancers[k]
  332. balancer = &b
  333. } else {
  334. t.Errorf("Got load balancer %v, expected one to be created", cloud.Balancers)
  335. break
  336. }
  337. }
  338. if balancer == nil {
  339. t.Errorf("Got no load balancer, expected one to be created")
  340. } else if balancer.Name != controller.balancer.GetLoadBalancerName(context.Background(), "", tc.service) ||
  341. balancer.Region != region ||
  342. balancer.Ports[0].Port != tc.service.Spec.Ports[0].Port {
  343. t.Errorf("Created load balancer has incorrect parameters: %v", balancer)
  344. }
  345. }
  346. if tc.expectDeleteAttempt {
  347. deleteCallFound := false
  348. for _, call := range cloud.Calls {
  349. if call == "delete" {
  350. deleteCallFound = true
  351. }
  352. }
  353. if !deleteCallFound {
  354. t.Errorf("Got no delete call for load balancer, expected one")
  355. }
  356. }
  357. expectNumPatches := 0
  358. if tc.expectPatchStatus {
  359. expectNumPatches++
  360. }
  361. if tc.expectPatchFinalizer {
  362. expectNumPatches++
  363. }
  364. numPatches := 0
  365. for _, action := range actions {
  366. if action.Matches("patch", "services") {
  367. numPatches++
  368. }
  369. }
  370. if numPatches != expectNumPatches {
  371. t.Errorf("Got %d patches, expect %d instead. Actions: %v", numPatches, expectNumPatches, actions)
  372. }
  373. })
  374. }
  375. }
  376. // TODO: Finish converting and update comments
  377. func TestUpdateNodesInExternalLoadBalancer(t *testing.T) {
  378. nodes := []*v1.Node{
  379. {ObjectMeta: metav1.ObjectMeta{Name: "node0"}},
  380. {ObjectMeta: metav1.ObjectMeta{Name: "node1"}},
  381. {ObjectMeta: metav1.ObjectMeta{Name: "node73"}},
  382. }
  383. table := []struct {
  384. services []*v1.Service
  385. expectedUpdateCalls []fakecloud.UpdateBalancerCall
  386. }{
  387. {
  388. // No services present: no calls should be made.
  389. services: []*v1.Service{},
  390. expectedUpdateCalls: nil,
  391. },
  392. {
  393. // Services do not have external load balancers: no calls should be made.
  394. services: []*v1.Service{
  395. newService("s0", "111", v1.ServiceTypeClusterIP),
  396. newService("s1", "222", v1.ServiceTypeNodePort),
  397. },
  398. expectedUpdateCalls: nil,
  399. },
  400. {
  401. // Services does have an external load balancer: one call should be made.
  402. services: []*v1.Service{
  403. newService("s0", "333", v1.ServiceTypeLoadBalancer),
  404. },
  405. expectedUpdateCalls: []fakecloud.UpdateBalancerCall{
  406. {Service: newService("s0", "333", v1.ServiceTypeLoadBalancer), Hosts: nodes},
  407. },
  408. },
  409. {
  410. // Three services have an external load balancer: three calls.
  411. services: []*v1.Service{
  412. newService("s0", "444", v1.ServiceTypeLoadBalancer),
  413. newService("s1", "555", v1.ServiceTypeLoadBalancer),
  414. newService("s2", "666", v1.ServiceTypeLoadBalancer),
  415. },
  416. expectedUpdateCalls: []fakecloud.UpdateBalancerCall{
  417. {Service: newService("s0", "444", v1.ServiceTypeLoadBalancer), Hosts: nodes},
  418. {Service: newService("s1", "555", v1.ServiceTypeLoadBalancer), Hosts: nodes},
  419. {Service: newService("s2", "666", v1.ServiceTypeLoadBalancer), Hosts: nodes},
  420. },
  421. },
  422. {
  423. // Two services have an external load balancer and two don't: two calls.
  424. services: []*v1.Service{
  425. newService("s0", "777", v1.ServiceTypeNodePort),
  426. newService("s1", "888", v1.ServiceTypeLoadBalancer),
  427. newService("s3", "999", v1.ServiceTypeLoadBalancer),
  428. newService("s4", "123", v1.ServiceTypeClusterIP),
  429. },
  430. expectedUpdateCalls: []fakecloud.UpdateBalancerCall{
  431. {Service: newService("s1", "888", v1.ServiceTypeLoadBalancer), Hosts: nodes},
  432. {Service: newService("s3", "999", v1.ServiceTypeLoadBalancer), Hosts: nodes},
  433. },
  434. },
  435. {
  436. // One service has an external load balancer and one is nil: one call.
  437. services: []*v1.Service{
  438. newService("s0", "234", v1.ServiceTypeLoadBalancer),
  439. nil,
  440. },
  441. expectedUpdateCalls: []fakecloud.UpdateBalancerCall{
  442. {Service: newService("s0", "234", v1.ServiceTypeLoadBalancer), Hosts: nodes},
  443. },
  444. },
  445. }
  446. for _, item := range table {
  447. controller, cloud, _ := newController()
  448. var services []*v1.Service
  449. services = append(services, item.services...)
  450. if err := controller.updateLoadBalancerHosts(services, nodes); err != nil {
  451. t.Errorf("unexpected error: %v", err)
  452. }
  453. if !reflect.DeepEqual(item.expectedUpdateCalls, cloud.UpdateCalls) {
  454. t.Errorf("expected update calls mismatch, expected %+v, got %+v", item.expectedUpdateCalls, cloud.UpdateCalls)
  455. }
  456. }
  457. }
  458. func TestGetNodeConditionPredicate(t *testing.T) {
  459. tests := []struct {
  460. node v1.Node
  461. expectAccept bool
  462. name string
  463. }{
  464. {
  465. node: v1.Node{},
  466. expectAccept: false,
  467. name: "empty",
  468. },
  469. {
  470. node: v1.Node{
  471. Status: v1.NodeStatus{
  472. Conditions: []v1.NodeCondition{
  473. {Type: v1.NodeReady, Status: v1.ConditionTrue},
  474. },
  475. },
  476. },
  477. expectAccept: true,
  478. name: "basic",
  479. },
  480. {
  481. node: v1.Node{
  482. Spec: v1.NodeSpec{Unschedulable: true},
  483. Status: v1.NodeStatus{
  484. Conditions: []v1.NodeCondition{
  485. {Type: v1.NodeReady, Status: v1.ConditionTrue},
  486. },
  487. },
  488. },
  489. expectAccept: false,
  490. name: "unschedulable",
  491. },
  492. }
  493. pred := getNodeConditionPredicate()
  494. for _, test := range tests {
  495. accept := pred(&test.node)
  496. if accept != test.expectAccept {
  497. t.Errorf("Test failed for %s, expected %v, saw %v", test.name, test.expectAccept, accept)
  498. }
  499. }
  500. }
  501. func TestProcessServiceCreateOrUpdate(t *testing.T) {
  502. controller, _, client := newController()
  503. //A pair of old and new loadbalancer IP address
  504. oldLBIP := "192.168.1.1"
  505. newLBIP := "192.168.1.11"
  506. testCases := []struct {
  507. testName string
  508. key string
  509. updateFn func(*v1.Service) *v1.Service //Manipulate the structure
  510. svc *v1.Service
  511. expectedFn func(*v1.Service, error) error //Error comparison function
  512. }{
  513. {
  514. testName: "If updating a valid service",
  515. key: "validKey",
  516. svc: defaultExternalService(),
  517. updateFn: func(svc *v1.Service) *v1.Service {
  518. controller.cache.getOrCreate("validKey")
  519. return svc
  520. },
  521. expectedFn: func(svc *v1.Service, err error) error {
  522. return err
  523. },
  524. },
  525. {
  526. testName: "If Updating Loadbalancer IP",
  527. key: "default/sync-test-name",
  528. svc: newService("sync-test-name", types.UID("sync-test-uid"), v1.ServiceTypeLoadBalancer),
  529. updateFn: func(svc *v1.Service) *v1.Service {
  530. svc.Spec.LoadBalancerIP = oldLBIP
  531. keyExpected := svc.GetObjectMeta().GetNamespace() + "/" + svc.GetObjectMeta().GetName()
  532. controller.enqueueService(svc)
  533. cachedServiceTest := controller.cache.getOrCreate(keyExpected)
  534. cachedServiceTest.state = svc
  535. controller.cache.set(keyExpected, cachedServiceTest)
  536. keyGot, quit := controller.queue.Get()
  537. if quit {
  538. t.Fatalf("get no queue element")
  539. }
  540. if keyExpected != keyGot.(string) {
  541. t.Fatalf("get service key error, expected: %s, got: %s", keyExpected, keyGot.(string))
  542. }
  543. newService := svc.DeepCopy()
  544. newService.Spec.LoadBalancerIP = newLBIP
  545. return newService
  546. },
  547. expectedFn: func(svc *v1.Service, err error) error {
  548. if err != nil {
  549. return err
  550. }
  551. keyExpected := svc.GetObjectMeta().GetNamespace() + "/" + svc.GetObjectMeta().GetName()
  552. cachedServiceGot, exist := controller.cache.get(keyExpected)
  553. if !exist {
  554. return fmt.Errorf("update service error, queue should contain service: %s", keyExpected)
  555. }
  556. if cachedServiceGot.state.Spec.LoadBalancerIP != newLBIP {
  557. return fmt.Errorf("update LoadBalancerIP error, expected: %s, got: %s", newLBIP, cachedServiceGot.state.Spec.LoadBalancerIP)
  558. }
  559. return nil
  560. },
  561. },
  562. }
  563. for _, tc := range testCases {
  564. newSvc := tc.updateFn(tc.svc)
  565. if _, err := client.CoreV1().Services(tc.svc.Namespace).Create(context.TODO(), tc.svc, metav1.CreateOptions{}); err != nil {
  566. t.Fatalf("Failed to prepare service %s for testing: %v", tc.key, err)
  567. }
  568. obtErr := controller.processServiceCreateOrUpdate(newSvc, tc.key)
  569. if err := tc.expectedFn(newSvc, obtErr); err != nil {
  570. t.Errorf("%v processServiceCreateOrUpdate() %v", tc.testName, err)
  571. }
  572. }
  573. }
  574. // TestProcessServiceCreateOrUpdateK8sError tests processServiceCreateOrUpdate
  575. // with various kubernetes errors when patching status.
  576. func TestProcessServiceCreateOrUpdateK8sError(t *testing.T) {
  577. svcName := "svc-k8s-err"
  578. conflictErr := apierrors.NewConflict(schema.GroupResource{}, svcName, errors.New("object conflict"))
  579. notFoundErr := apierrors.NewNotFound(schema.GroupResource{}, svcName)
  580. testCases := []struct {
  581. desc string
  582. k8sErr error
  583. expectErr error
  584. }{
  585. {
  586. desc: "conflict error",
  587. k8sErr: conflictErr,
  588. expectErr: fmt.Errorf("failed to update load balancer status: %v", conflictErr),
  589. },
  590. {
  591. desc: "not found error",
  592. k8sErr: notFoundErr,
  593. expectErr: nil,
  594. },
  595. }
  596. for _, tc := range testCases {
  597. t.Run(tc.desc, func(t *testing.T) {
  598. svc := newService(svcName, types.UID("123"), v1.ServiceTypeLoadBalancer)
  599. // Preset finalizer so k8s error only happens when patching status.
  600. svc.Finalizers = []string{servicehelper.LoadBalancerCleanupFinalizer}
  601. controller, _, client := newController()
  602. client.PrependReactor("patch", "services", func(action core.Action) (bool, runtime.Object, error) {
  603. return true, nil, tc.k8sErr
  604. })
  605. if err := controller.processServiceCreateOrUpdate(svc, svcName); !reflect.DeepEqual(err, tc.expectErr) {
  606. t.Fatalf("processServiceCreateOrUpdate() = %v, want %v", err, tc.expectErr)
  607. }
  608. if tc.expectErr == nil {
  609. return
  610. }
  611. errMsg := "Error syncing load balancer"
  612. if gotEvent := func() bool {
  613. events := controller.eventRecorder.(*record.FakeRecorder).Events
  614. for len(events) > 0 {
  615. e := <-events
  616. if strings.Contains(e, errMsg) {
  617. return true
  618. }
  619. }
  620. return false
  621. }(); !gotEvent {
  622. t.Errorf("processServiceCreateOrUpdate() = can't find sync error event, want event contains %q", errMsg)
  623. }
  624. })
  625. }
  626. }
  627. func TestSyncService(t *testing.T) {
  628. var controller *Controller
  629. testCases := []struct {
  630. testName string
  631. key string
  632. updateFn func() //Function to manipulate the controller element to simulate error
  633. expectedFn func(error) error //Expected function if returns nil then test passed, failed otherwise
  634. }{
  635. {
  636. testName: "if an invalid service name is synced",
  637. key: "invalid/key/string",
  638. updateFn: func() {
  639. controller, _, _ = newController()
  640. },
  641. expectedFn: func(e error) error {
  642. //TODO: should find a way to test for dependent package errors in such a way that it won't break
  643. //TODO: our tests, currently we only test if there is an error.
  644. //Error should be unexpected key format: "invalid/key/string"
  645. expectedError := fmt.Sprintf("unexpected key format: %q", "invalid/key/string")
  646. if e == nil || e.Error() != expectedError {
  647. return fmt.Errorf("Expected=unexpected key format: %q, Obtained=%v", "invalid/key/string", e)
  648. }
  649. return nil
  650. },
  651. },
  652. /* We cannot open this test case as syncService(key) currently runtime.HandleError(err) and suppresses frequently occurring errors
  653. {
  654. testName: "if an invalid service is synced",
  655. key: "somethingelse",
  656. updateFn: func() {
  657. controller, _, _ = newController()
  658. srv := controller.cache.getOrCreate("external-balancer")
  659. srv.state = defaultExternalService()
  660. },
  661. expectedErr: fmt.Errorf("service somethingelse not in cache even though the watcher thought it was. Ignoring the deletion."),
  662. },
  663. */
  664. //TODO: see if we can add a test for valid but error throwing service, its difficult right now because synCService() currently runtime.HandleError
  665. {
  666. testName: "if valid service",
  667. key: "external-balancer",
  668. updateFn: func() {
  669. testSvc := defaultExternalService()
  670. controller, _, _ = newController()
  671. controller.enqueueService(testSvc)
  672. svc := controller.cache.getOrCreate("external-balancer")
  673. svc.state = testSvc
  674. },
  675. expectedFn: func(e error) error {
  676. //error should be nil
  677. if e != nil {
  678. return fmt.Errorf("Expected=nil, Obtained=%v", e)
  679. }
  680. return nil
  681. },
  682. },
  683. }
  684. for _, tc := range testCases {
  685. tc.updateFn()
  686. obtainedErr := controller.syncService(tc.key)
  687. //expected matches obtained ??.
  688. if exp := tc.expectedFn(obtainedErr); exp != nil {
  689. t.Errorf("%v Error:%v", tc.testName, exp)
  690. }
  691. //Post processing, the element should not be in the sync queue.
  692. _, exist := controller.cache.get(tc.key)
  693. if exist {
  694. t.Fatalf("%v working Queue should be empty, but contains %s", tc.testName, tc.key)
  695. }
  696. }
  697. }
  698. func TestProcessServiceDeletion(t *testing.T) {
  699. var controller *Controller
  700. var cloud *fakecloud.Cloud
  701. // Add a global svcKey name
  702. svcKey := "external-balancer"
  703. testCases := []struct {
  704. testName string
  705. updateFn func(*Controller) // Update function used to manipulate srv and controller values
  706. expectedFn func(svcErr error) error // Function to check if the returned value is expected
  707. }{
  708. {
  709. testName: "If a non-existent service is deleted",
  710. updateFn: func(controller *Controller) {
  711. // Does not do anything
  712. },
  713. expectedFn: func(svcErr error) error {
  714. return svcErr
  715. },
  716. },
  717. {
  718. testName: "If cloudprovided failed to delete the service",
  719. updateFn: func(controller *Controller) {
  720. svc := controller.cache.getOrCreate(svcKey)
  721. svc.state = defaultExternalService()
  722. cloud.Err = fmt.Errorf("error Deleting the Loadbalancer")
  723. },
  724. expectedFn: func(svcErr error) error {
  725. expectedError := "error Deleting the Loadbalancer"
  726. if svcErr == nil || svcErr.Error() != expectedError {
  727. return fmt.Errorf("Expected=%v Obtained=%v", expectedError, svcErr)
  728. }
  729. return nil
  730. },
  731. },
  732. {
  733. testName: "If delete was successful",
  734. updateFn: func(controller *Controller) {
  735. testSvc := defaultExternalService()
  736. controller.enqueueService(testSvc)
  737. svc := controller.cache.getOrCreate(svcKey)
  738. svc.state = testSvc
  739. controller.cache.set(svcKey, svc)
  740. },
  741. expectedFn: func(svcErr error) error {
  742. if svcErr != nil {
  743. return fmt.Errorf("Expected=nil Obtained=%v", svcErr)
  744. }
  745. // It should no longer be in the workqueue.
  746. _, exist := controller.cache.get(svcKey)
  747. if exist {
  748. return fmt.Errorf("delete service error, queue should not contain service: %s any more", svcKey)
  749. }
  750. return nil
  751. },
  752. },
  753. }
  754. for _, tc := range testCases {
  755. //Create a new controller.
  756. controller, cloud, _ = newController()
  757. tc.updateFn(controller)
  758. obtainedErr := controller.processServiceDeletion(svcKey)
  759. if err := tc.expectedFn(obtainedErr); err != nil {
  760. t.Errorf("%v processServiceDeletion() %v", tc.testName, err)
  761. }
  762. }
  763. }
  764. // Test cases:
  765. // index finalizer timestamp wantLB | clean-up
  766. // 0 0 0 0 | false (No finalizer, no clean up)
  767. // 1 0 0 1 | false (Ignored as same with case 0)
  768. // 2 0 1 0 | false (Ignored as same with case 0)
  769. // 3 0 1 1 | false (Ignored as same with case 0)
  770. // 4 1 0 0 | true
  771. // 5 1 0 1 | false
  772. // 6 1 1 0 | true (Service is deleted, needs clean up)
  773. // 7 1 1 1 | true (Ignored as same with case 6)
  774. func TestNeedsCleanup(t *testing.T) {
  775. testCases := []struct {
  776. desc string
  777. svc *v1.Service
  778. expectNeedsCleanup bool
  779. }{
  780. {
  781. desc: "service without finalizer",
  782. svc: &v1.Service{},
  783. expectNeedsCleanup: false,
  784. },
  785. {
  786. desc: "service with finalizer without timestamp without LB",
  787. svc: &v1.Service{
  788. ObjectMeta: metav1.ObjectMeta{
  789. Finalizers: []string{servicehelper.LoadBalancerCleanupFinalizer},
  790. },
  791. Spec: v1.ServiceSpec{
  792. Type: v1.ServiceTypeNodePort,
  793. },
  794. },
  795. expectNeedsCleanup: true,
  796. },
  797. {
  798. desc: "service with finalizer without timestamp with LB",
  799. svc: &v1.Service{
  800. ObjectMeta: metav1.ObjectMeta{
  801. Finalizers: []string{servicehelper.LoadBalancerCleanupFinalizer},
  802. },
  803. Spec: v1.ServiceSpec{
  804. Type: v1.ServiceTypeLoadBalancer,
  805. },
  806. },
  807. expectNeedsCleanup: false,
  808. },
  809. {
  810. desc: "service with finalizer with timestamp",
  811. svc: &v1.Service{
  812. ObjectMeta: metav1.ObjectMeta{
  813. Finalizers: []string{servicehelper.LoadBalancerCleanupFinalizer},
  814. DeletionTimestamp: &metav1.Time{
  815. Time: time.Now(),
  816. },
  817. },
  818. },
  819. expectNeedsCleanup: true,
  820. },
  821. }
  822. for _, tc := range testCases {
  823. t.Run(tc.desc, func(t *testing.T) {
  824. if gotNeedsCleanup := needsCleanup(tc.svc); gotNeedsCleanup != tc.expectNeedsCleanup {
  825. t.Errorf("needsCleanup() = %t, want %t", gotNeedsCleanup, tc.expectNeedsCleanup)
  826. }
  827. })
  828. }
  829. }
  830. func TestNeedsUpdate(t *testing.T) {
  831. var oldSvc, newSvc *v1.Service
  832. testCases := []struct {
  833. testName string //Name of the test case
  834. updateFn func() //Function to update the service object
  835. expectedNeedsUpdate bool //needsupdate always returns bool
  836. }{
  837. {
  838. testName: "If the service type is changed from LoadBalancer to ClusterIP",
  839. updateFn: func() {
  840. oldSvc = defaultExternalService()
  841. newSvc = defaultExternalService()
  842. newSvc.Spec.Type = v1.ServiceTypeClusterIP
  843. },
  844. expectedNeedsUpdate: true,
  845. },
  846. {
  847. testName: "If the Ports are different",
  848. updateFn: func() {
  849. oldSvc = defaultExternalService()
  850. newSvc = defaultExternalService()
  851. oldSvc.Spec.Ports = []v1.ServicePort{
  852. {
  853. Port: 8000,
  854. },
  855. {
  856. Port: 9000,
  857. },
  858. {
  859. Port: 10000,
  860. },
  861. }
  862. newSvc.Spec.Ports = []v1.ServicePort{
  863. {
  864. Port: 8001,
  865. },
  866. {
  867. Port: 9001,
  868. },
  869. {
  870. Port: 10001,
  871. },
  872. }
  873. },
  874. expectedNeedsUpdate: true,
  875. },
  876. {
  877. testName: "If externel ip counts are different",
  878. updateFn: func() {
  879. oldSvc = defaultExternalService()
  880. newSvc = defaultExternalService()
  881. oldSvc.Spec.ExternalIPs = []string{"old.IP.1"}
  882. newSvc.Spec.ExternalIPs = []string{"new.IP.1", "new.IP.2"}
  883. },
  884. expectedNeedsUpdate: true,
  885. },
  886. {
  887. testName: "If externel ips are different",
  888. updateFn: func() {
  889. oldSvc = defaultExternalService()
  890. newSvc = defaultExternalService()
  891. oldSvc.Spec.ExternalIPs = []string{"old.IP.1", "old.IP.2"}
  892. newSvc.Spec.ExternalIPs = []string{"new.IP.1", "new.IP.2"}
  893. },
  894. expectedNeedsUpdate: true,
  895. },
  896. {
  897. testName: "If UID is different",
  898. updateFn: func() {
  899. oldSvc = defaultExternalService()
  900. newSvc = defaultExternalService()
  901. oldSvc.UID = types.UID("UID old")
  902. newSvc.UID = types.UID("UID new")
  903. },
  904. expectedNeedsUpdate: true,
  905. },
  906. {
  907. testName: "If ExternalTrafficPolicy is different",
  908. updateFn: func() {
  909. oldSvc = defaultExternalService()
  910. newSvc = defaultExternalService()
  911. newSvc.Spec.ExternalTrafficPolicy = v1.ServiceExternalTrafficPolicyTypeLocal
  912. },
  913. expectedNeedsUpdate: true,
  914. },
  915. {
  916. testName: "If HealthCheckNodePort is different",
  917. updateFn: func() {
  918. oldSvc = defaultExternalService()
  919. newSvc = defaultExternalService()
  920. newSvc.Spec.HealthCheckNodePort = 30123
  921. },
  922. expectedNeedsUpdate: true,
  923. },
  924. {
  925. testName: "If TargetGroup is different 1",
  926. updateFn: func() {
  927. oldSvc = &v1.Service{
  928. ObjectMeta: metav1.ObjectMeta{
  929. Name: "tcp-service",
  930. Namespace: "default",
  931. },
  932. Spec: v1.ServiceSpec{
  933. Ports: []v1.ServicePort{{
  934. Port: 80,
  935. Protocol: v1.ProtocolTCP,
  936. TargetPort: intstr.Parse("20"),
  937. }},
  938. Type: v1.ServiceTypeLoadBalancer,
  939. },
  940. }
  941. newSvc = oldSvc.DeepCopy()
  942. newSvc.Spec.Ports[0].TargetPort = intstr.Parse("21")
  943. },
  944. expectedNeedsUpdate: true,
  945. },
  946. {
  947. testName: "If TargetGroup is different 2",
  948. updateFn: func() {
  949. oldSvc = &v1.Service{
  950. ObjectMeta: metav1.ObjectMeta{
  951. Name: "tcp-service",
  952. Namespace: "default",
  953. },
  954. Spec: v1.ServiceSpec{
  955. Ports: []v1.ServicePort{{
  956. Port: 80,
  957. Protocol: v1.ProtocolTCP,
  958. TargetPort: intstr.Parse("22"),
  959. }},
  960. Type: v1.ServiceTypeLoadBalancer,
  961. },
  962. }
  963. newSvc = oldSvc.DeepCopy()
  964. newSvc.Spec.Ports[0].TargetPort = intstr.Parse("dns")
  965. },
  966. expectedNeedsUpdate: true,
  967. },
  968. }
  969. controller, _, _ := newController()
  970. for _, tc := range testCases {
  971. tc.updateFn()
  972. obtainedResult := controller.needsUpdate(oldSvc, newSvc)
  973. if obtainedResult != tc.expectedNeedsUpdate {
  974. t.Errorf("%v needsUpdate() should have returned %v but returned %v", tc.testName, tc.expectedNeedsUpdate, obtainedResult)
  975. }
  976. }
  977. }
  978. //All the test cases for ServiceCache uses a single cache, these below test cases should be run in order,
  979. //as tc1 (addCache would add elements to the cache)
  980. //and tc2 (delCache would remove element from the cache without it adding automatically)
  981. //Please keep this in mind while adding new test cases.
  982. func TestServiceCache(t *testing.T) {
  983. //ServiceCache a common service cache for all the test cases
  984. sc := &serviceCache{serviceMap: make(map[string]*cachedService)}
  985. testCases := []struct {
  986. testName string
  987. setCacheFn func()
  988. checkCacheFn func() error
  989. }{
  990. {
  991. testName: "Add",
  992. setCacheFn: func() {
  993. cS := sc.getOrCreate("addTest")
  994. cS.state = defaultExternalService()
  995. },
  996. checkCacheFn: func() error {
  997. //There must be exactly one element
  998. if len(sc.serviceMap) != 1 {
  999. return fmt.Errorf("Expected=1 Obtained=%d", len(sc.serviceMap))
  1000. }
  1001. return nil
  1002. },
  1003. },
  1004. {
  1005. testName: "Del",
  1006. setCacheFn: func() {
  1007. sc.delete("addTest")
  1008. },
  1009. checkCacheFn: func() error {
  1010. //Now it should have no element
  1011. if len(sc.serviceMap) != 0 {
  1012. return fmt.Errorf("Expected=0 Obtained=%d", len(sc.serviceMap))
  1013. }
  1014. return nil
  1015. },
  1016. },
  1017. {
  1018. testName: "Set and Get",
  1019. setCacheFn: func() {
  1020. sc.set("addTest", &cachedService{state: defaultExternalService()})
  1021. },
  1022. checkCacheFn: func() error {
  1023. //Now it should have one element
  1024. Cs, bool := sc.get("addTest")
  1025. if !bool {
  1026. return fmt.Errorf("is Available Expected=true Obtained=%v", bool)
  1027. }
  1028. if Cs == nil {
  1029. return fmt.Errorf("cachedService expected:non-nil Obtained=nil")
  1030. }
  1031. return nil
  1032. },
  1033. },
  1034. {
  1035. testName: "ListKeys",
  1036. setCacheFn: func() {
  1037. //Add one more entry here
  1038. sc.set("addTest1", &cachedService{state: defaultExternalService()})
  1039. },
  1040. checkCacheFn: func() error {
  1041. //It should have two elements
  1042. keys := sc.ListKeys()
  1043. if len(keys) != 2 {
  1044. return fmt.Errorf("elements Expected=2 Obtained=%v", len(keys))
  1045. }
  1046. return nil
  1047. },
  1048. },
  1049. {
  1050. testName: "GetbyKeys",
  1051. setCacheFn: nil, //Nothing to set
  1052. checkCacheFn: func() error {
  1053. //It should have two elements
  1054. svc, isKey, err := sc.GetByKey("addTest")
  1055. if svc == nil || isKey == false || err != nil {
  1056. return fmt.Errorf("Expected(non-nil, true, nil) Obtained(%v,%v,%v)", svc, isKey, err)
  1057. }
  1058. return nil
  1059. },
  1060. },
  1061. {
  1062. testName: "allServices",
  1063. setCacheFn: nil, //Nothing to set
  1064. checkCacheFn: func() error {
  1065. //It should return two elements
  1066. svcArray := sc.allServices()
  1067. if len(svcArray) != 2 {
  1068. return fmt.Errorf("Expected(2) Obtained(%v)", len(svcArray))
  1069. }
  1070. return nil
  1071. },
  1072. },
  1073. }
  1074. for _, tc := range testCases {
  1075. if tc.setCacheFn != nil {
  1076. tc.setCacheFn()
  1077. }
  1078. if err := tc.checkCacheFn(); err != nil {
  1079. t.Errorf("%v returned %v", tc.testName, err)
  1080. }
  1081. }
  1082. }
  1083. //Test a utility functions as it's not easy to unit test nodeSyncLoop directly
  1084. func TestNodeSlicesEqualForLB(t *testing.T) {
  1085. numNodes := 10
  1086. nArray := make([]*v1.Node, numNodes)
  1087. mArray := make([]*v1.Node, numNodes)
  1088. for i := 0; i < numNodes; i++ {
  1089. nArray[i] = &v1.Node{}
  1090. nArray[i].Name = fmt.Sprintf("node%d", i)
  1091. }
  1092. for i := 0; i < numNodes; i++ {
  1093. mArray[i] = &v1.Node{}
  1094. mArray[i].Name = fmt.Sprintf("node%d", i+1)
  1095. }
  1096. if !nodeSlicesEqualForLB(nArray, nArray) {
  1097. t.Errorf("nodeSlicesEqualForLB() Expected=true Obtained=false")
  1098. }
  1099. if nodeSlicesEqualForLB(nArray, mArray) {
  1100. t.Errorf("nodeSlicesEqualForLB() Expected=false Obtained=true")
  1101. }
  1102. }
  1103. // TODO(@MrHohn): Verify the end state when below issue is resolved:
  1104. // https://github.com/kubernetes/client-go/issues/607
  1105. func TestAddFinalizer(t *testing.T) {
  1106. testCases := []struct {
  1107. desc string
  1108. svc *v1.Service
  1109. expectPatch bool
  1110. }{
  1111. {
  1112. desc: "no-op add finalizer",
  1113. svc: &v1.Service{
  1114. ObjectMeta: metav1.ObjectMeta{
  1115. Name: "test-patch-finalizer",
  1116. Finalizers: []string{servicehelper.LoadBalancerCleanupFinalizer},
  1117. },
  1118. },
  1119. expectPatch: false,
  1120. },
  1121. {
  1122. desc: "add finalizer",
  1123. svc: &v1.Service{
  1124. ObjectMeta: metav1.ObjectMeta{
  1125. Name: "test-patch-finalizer",
  1126. },
  1127. },
  1128. expectPatch: true,
  1129. },
  1130. }
  1131. for _, tc := range testCases {
  1132. t.Run(tc.desc, func(t *testing.T) {
  1133. c := fake.NewSimpleClientset()
  1134. s := &Controller{
  1135. kubeClient: c,
  1136. }
  1137. if _, err := s.kubeClient.CoreV1().Services(tc.svc.Namespace).Create(context.TODO(), tc.svc, metav1.CreateOptions{}); err != nil {
  1138. t.Fatalf("Failed to prepare service for testing: %v", err)
  1139. }
  1140. if err := s.addFinalizer(tc.svc); err != nil {
  1141. t.Fatalf("addFinalizer() = %v, want nil", err)
  1142. }
  1143. patchActionFound := false
  1144. for _, action := range c.Actions() {
  1145. if action.Matches("patch", "services") {
  1146. patchActionFound = true
  1147. }
  1148. }
  1149. if patchActionFound != tc.expectPatch {
  1150. t.Errorf("Got patchActionFound = %t, want %t", patchActionFound, tc.expectPatch)
  1151. }
  1152. })
  1153. }
  1154. }
  1155. // TODO(@MrHohn): Verify the end state when below issue is resolved:
  1156. // https://github.com/kubernetes/client-go/issues/607
  1157. func TestRemoveFinalizer(t *testing.T) {
  1158. testCases := []struct {
  1159. desc string
  1160. svc *v1.Service
  1161. expectPatch bool
  1162. }{
  1163. {
  1164. desc: "no-op remove finalizer",
  1165. svc: &v1.Service{
  1166. ObjectMeta: metav1.ObjectMeta{
  1167. Name: "test-patch-finalizer",
  1168. },
  1169. },
  1170. expectPatch: false,
  1171. },
  1172. {
  1173. desc: "remove finalizer",
  1174. svc: &v1.Service{
  1175. ObjectMeta: metav1.ObjectMeta{
  1176. Name: "test-patch-finalizer",
  1177. Finalizers: []string{servicehelper.LoadBalancerCleanupFinalizer},
  1178. },
  1179. },
  1180. expectPatch: true,
  1181. },
  1182. }
  1183. for _, tc := range testCases {
  1184. t.Run(tc.desc, func(t *testing.T) {
  1185. c := fake.NewSimpleClientset()
  1186. s := &Controller{
  1187. kubeClient: c,
  1188. }
  1189. if _, err := s.kubeClient.CoreV1().Services(tc.svc.Namespace).Create(context.TODO(), tc.svc, metav1.CreateOptions{}); err != nil {
  1190. t.Fatalf("Failed to prepare service for testing: %v", err)
  1191. }
  1192. if err := s.removeFinalizer(tc.svc); err != nil {
  1193. t.Fatalf("removeFinalizer() = %v, want nil", err)
  1194. }
  1195. patchActionFound := false
  1196. for _, action := range c.Actions() {
  1197. if action.Matches("patch", "services") {
  1198. patchActionFound = true
  1199. }
  1200. }
  1201. if patchActionFound != tc.expectPatch {
  1202. t.Errorf("Got patchActionFound = %t, want %t", patchActionFound, tc.expectPatch)
  1203. }
  1204. })
  1205. }
  1206. }
  1207. // TODO(@MrHohn): Verify the end state when below issue is resolved:
  1208. // https://github.com/kubernetes/client-go/issues/607
  1209. func TestPatchStatus(t *testing.T) {
  1210. testCases := []struct {
  1211. desc string
  1212. svc *v1.Service
  1213. newStatus *v1.LoadBalancerStatus
  1214. expectPatch bool
  1215. }{
  1216. {
  1217. desc: "no-op add status",
  1218. svc: &v1.Service{
  1219. ObjectMeta: metav1.ObjectMeta{
  1220. Name: "test-patch-status",
  1221. },
  1222. Status: v1.ServiceStatus{
  1223. LoadBalancer: v1.LoadBalancerStatus{
  1224. Ingress: []v1.LoadBalancerIngress{
  1225. {IP: "8.8.8.8"},
  1226. },
  1227. },
  1228. },
  1229. },
  1230. newStatus: &v1.LoadBalancerStatus{
  1231. Ingress: []v1.LoadBalancerIngress{
  1232. {IP: "8.8.8.8"},
  1233. },
  1234. },
  1235. expectPatch: false,
  1236. },
  1237. {
  1238. desc: "add status",
  1239. svc: &v1.Service{
  1240. ObjectMeta: metav1.ObjectMeta{
  1241. Name: "test-patch-status",
  1242. },
  1243. Status: v1.ServiceStatus{},
  1244. },
  1245. newStatus: &v1.LoadBalancerStatus{
  1246. Ingress: []v1.LoadBalancerIngress{
  1247. {IP: "8.8.8.8"},
  1248. },
  1249. },
  1250. expectPatch: true,
  1251. },
  1252. {
  1253. desc: "no-op clear status",
  1254. svc: &v1.Service{
  1255. ObjectMeta: metav1.ObjectMeta{
  1256. Name: "test-patch-status",
  1257. },
  1258. Status: v1.ServiceStatus{},
  1259. },
  1260. newStatus: &v1.LoadBalancerStatus{},
  1261. expectPatch: false,
  1262. },
  1263. {
  1264. desc: "clear status",
  1265. svc: &v1.Service{
  1266. ObjectMeta: metav1.ObjectMeta{
  1267. Name: "test-patch-status",
  1268. },
  1269. Status: v1.ServiceStatus{
  1270. LoadBalancer: v1.LoadBalancerStatus{
  1271. Ingress: []v1.LoadBalancerIngress{
  1272. {IP: "8.8.8.8"},
  1273. },
  1274. },
  1275. },
  1276. },
  1277. newStatus: &v1.LoadBalancerStatus{},
  1278. expectPatch: true,
  1279. },
  1280. }
  1281. for _, tc := range testCases {
  1282. t.Run(tc.desc, func(t *testing.T) {
  1283. c := fake.NewSimpleClientset()
  1284. s := &Controller{
  1285. kubeClient: c,
  1286. }
  1287. if _, err := s.kubeClient.CoreV1().Services(tc.svc.Namespace).Create(context.TODO(), tc.svc, metav1.CreateOptions{}); err != nil {
  1288. t.Fatalf("Failed to prepare service for testing: %v", err)
  1289. }
  1290. if err := s.patchStatus(tc.svc, &tc.svc.Status.LoadBalancer, tc.newStatus); err != nil {
  1291. t.Fatalf("patchStatus() = %v, want nil", err)
  1292. }
  1293. patchActionFound := false
  1294. for _, action := range c.Actions() {
  1295. if action.Matches("patch", "services") {
  1296. patchActionFound = true
  1297. }
  1298. }
  1299. if patchActionFound != tc.expectPatch {
  1300. t.Errorf("Got patchActionFound = %t, want %t", patchActionFound, tc.expectPatch)
  1301. }
  1302. })
  1303. }
  1304. }
  1305. func Test_getNodeConditionPredicate(t *testing.T) {
  1306. validNodeStatus := v1.NodeStatus{Conditions: []v1.NodeCondition{{Type: "Test"}}}
  1307. tests := []struct {
  1308. name string
  1309. enableExclusion bool
  1310. enableLegacy bool
  1311. input *v1.Node
  1312. want bool
  1313. }{
  1314. {want: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{}}}},
  1315. {want: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelNodeRoleMaster: ""}}}},
  1316. {want: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelNodeRoleExcludeBalancer: ""}}}},
  1317. {want: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelAlphaNodeRoleExcludeBalancer: ""}}}},
  1318. {want: true, enableExclusion: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelNodeRoleMaster: ""}}}},
  1319. {want: true, enableLegacy: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelNodeRoleExcludeBalancer: ""}}}},
  1320. {want: false, enableLegacy: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelNodeRoleMaster: ""}}}},
  1321. {want: false, enableExclusion: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelAlphaNodeRoleExcludeBalancer: ""}}}},
  1322. {want: false, enableExclusion: true, input: &v1.Node{Status: validNodeStatus, ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{labelNodeRoleExcludeBalancer: ""}}}},
  1323. }
  1324. for _, tt := range tests {
  1325. t.Run(tt.name, func(t *testing.T) {
  1326. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, serviceNodeExclusionFeature, tt.enableExclusion)()
  1327. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, legacyNodeRoleBehaviorFeature, tt.enableLegacy)()
  1328. if result := getNodeConditionPredicate()(tt.input); result != tt.want {
  1329. t.Errorf("getNodeConditionPredicate() = %v, want %v", result, tt.want)
  1330. }
  1331. })
  1332. }
  1333. }
  1334. func TestListWithPredicate(t *testing.T) {
  1335. fakeInformerFactory := informers.NewSharedInformerFactory(&fake.Clientset{}, 0*time.Second)
  1336. var nodes []*v1.Node
  1337. for i := 0; i < 5; i++ {
  1338. var phase v1.NodePhase
  1339. if i%2 == 0 {
  1340. phase = v1.NodePending
  1341. } else {
  1342. phase = v1.NodeRunning
  1343. }
  1344. node := &v1.Node{
  1345. ObjectMeta: metav1.ObjectMeta{
  1346. Name: fmt.Sprintf("node-%d", i),
  1347. },
  1348. Status: v1.NodeStatus{
  1349. Phase: phase,
  1350. },
  1351. }
  1352. nodes = append(nodes, node)
  1353. fakeInformerFactory.Core().V1().Nodes().Informer().GetStore().Add(node)
  1354. }
  1355. tests := []struct {
  1356. name string
  1357. predicate NodeConditionPredicate
  1358. expect []*v1.Node
  1359. }{
  1360. {
  1361. name: "ListWithPredicate filter Running node",
  1362. predicate: func(node *v1.Node) bool {
  1363. return node.Status.Phase == v1.NodeRunning
  1364. },
  1365. expect: []*v1.Node{nodes[1], nodes[3]},
  1366. },
  1367. {
  1368. name: "ListWithPredicate filter Pending node",
  1369. predicate: func(node *v1.Node) bool {
  1370. return node.Status.Phase == v1.NodePending
  1371. },
  1372. expect: []*v1.Node{nodes[0], nodes[2], nodes[4]},
  1373. },
  1374. {
  1375. name: "ListWithPredicate filter Terminated node",
  1376. predicate: func(node *v1.Node) bool {
  1377. return node.Status.Phase == v1.NodeTerminated
  1378. },
  1379. expect: nil,
  1380. },
  1381. }
  1382. for _, test := range tests {
  1383. t.Run(test.name, func(t *testing.T) {
  1384. get, err := listWithPredicate(fakeInformerFactory.Core().V1().Nodes().Lister(), test.predicate)
  1385. sort.Slice(get, func(i, j int) bool {
  1386. return get[i].Name < get[j].Name
  1387. })
  1388. if err != nil {
  1389. t.Errorf("Error from ListWithPredicate: %v", err)
  1390. } else if !reflect.DeepEqual(get, test.expect) {
  1391. t.Errorf("Expect nodes %v, but got %v", test.expect, get)
  1392. }
  1393. })
  1394. }
  1395. }