service_controller_test.go 36 KB

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