1
16
17 package kubernetesservice
18
19 import (
20 "reflect"
21 "testing"
22 "time"
23
24 corev1 "k8s.io/api/core/v1"
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 "k8s.io/apimachinery/pkg/util/intstr"
27 v1informers "k8s.io/client-go/informers/core/v1"
28 "k8s.io/client-go/kubernetes/fake"
29 v1listers "k8s.io/client-go/listers/core/v1"
30 core "k8s.io/client-go/testing"
31 "k8s.io/client-go/tools/cache"
32 netutils "k8s.io/utils/net"
33 )
34
35 func TestCreateOrUpdateMasterService(t *testing.T) {
36 singleStack := corev1.IPFamilyPolicySingleStack
37 ns := metav1.NamespaceDefault
38 om := func(name string) metav1.ObjectMeta {
39 return metav1.ObjectMeta{Namespace: ns, Name: name}
40 }
41
42 createTests := []struct {
43 testName string
44 serviceName string
45 servicePorts []corev1.ServicePort
46 serviceType corev1.ServiceType
47 expectCreate *corev1.Service
48 }{
49 {
50 testName: "service does not exist",
51 serviceName: "foo",
52 servicePorts: []corev1.ServicePort{
53 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
54 },
55 serviceType: corev1.ServiceTypeClusterIP,
56 expectCreate: &corev1.Service{
57 ObjectMeta: om("foo"),
58 Spec: corev1.ServiceSpec{
59 Ports: []corev1.ServicePort{
60 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
61 },
62 Selector: nil,
63 ClusterIP: "1.2.3.4",
64 IPFamilyPolicy: &singleStack,
65 SessionAffinity: corev1.ServiceAffinityNone,
66 Type: corev1.ServiceTypeClusterIP,
67 },
68 },
69 },
70 }
71 for _, test := range createTests {
72 t.Run(test.testName, func(t *testing.T) {
73 master := Controller{}
74 fakeClient := fake.NewSimpleClientset()
75 serviceStore := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
76 master.serviceLister = v1listers.NewServiceLister(serviceStore)
77 master.client = fakeClient
78 master.CreateOrUpdateMasterServiceIfNeeded(test.serviceName, netutils.ParseIPSloppy("1.2.3.4"), test.servicePorts, test.serviceType, false)
79 creates := []core.CreateAction{}
80 for _, action := range fakeClient.Actions() {
81 if action.GetVerb() == "create" {
82 creates = append(creates, action.(core.CreateAction))
83 }
84 }
85 if test.expectCreate != nil {
86 if len(creates) != 1 {
87 t.Errorf("case %q: unexpected creations: %v", test.testName, creates)
88 } else {
89 obj := creates[0].GetObject()
90 if e, a := test.expectCreate.Spec, obj.(*corev1.Service).Spec; !reflect.DeepEqual(e, a) {
91 t.Errorf("case %q: expected create:\n%#v\ngot:\n%#v\n", test.testName, e, a)
92 }
93 }
94 }
95 if test.expectCreate == nil && len(creates) > 1 {
96 t.Errorf("case %q: no create expected, yet saw: %v", test.testName, creates)
97 }
98 })
99 }
100
101 reconcileTests := []struct {
102 testName string
103 serviceName string
104 servicePorts []corev1.ServicePort
105 serviceType corev1.ServiceType
106 service *corev1.Service
107 expectUpdate *corev1.Service
108 }{
109 {
110 testName: "service definition wrong port",
111 serviceName: "foo",
112 servicePorts: []corev1.ServicePort{
113 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
114 },
115 serviceType: corev1.ServiceTypeClusterIP,
116 service: &corev1.Service{
117 ObjectMeta: om("foo"),
118 Spec: corev1.ServiceSpec{
119 Ports: []corev1.ServicePort{
120 {Name: "foo", Port: 8000, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
121 },
122 Selector: nil,
123 ClusterIP: "1.2.3.4",
124 SessionAffinity: corev1.ServiceAffinityNone,
125 Type: corev1.ServiceTypeClusterIP,
126 },
127 },
128 expectUpdate: &corev1.Service{
129 ObjectMeta: om("foo"),
130 Spec: corev1.ServiceSpec{
131 Ports: []corev1.ServicePort{
132 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
133 },
134 Selector: nil,
135 ClusterIP: "1.2.3.4",
136 SessionAffinity: corev1.ServiceAffinityNone,
137 Type: corev1.ServiceTypeClusterIP,
138 },
139 },
140 },
141 {
142 testName: "service definition missing port",
143 serviceName: "foo",
144 servicePorts: []corev1.ServicePort{
145 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
146 {Name: "baz", Port: 1000, Protocol: "TCP", TargetPort: intstr.FromInt32(1000)},
147 },
148 serviceType: corev1.ServiceTypeClusterIP,
149 service: &corev1.Service{
150 ObjectMeta: om("foo"),
151 Spec: corev1.ServiceSpec{
152 Ports: []corev1.ServicePort{
153 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
154 },
155 Selector: nil,
156 ClusterIP: "1.2.3.4",
157 SessionAffinity: corev1.ServiceAffinityNone,
158 Type: corev1.ServiceTypeClusterIP,
159 },
160 },
161 expectUpdate: &corev1.Service{
162 ObjectMeta: om("foo"),
163 Spec: corev1.ServiceSpec{
164 Ports: []corev1.ServicePort{
165 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
166 {Name: "baz", Port: 1000, Protocol: "TCP", TargetPort: intstr.FromInt32(1000)},
167 },
168 Selector: nil,
169 ClusterIP: "1.2.3.4",
170 SessionAffinity: corev1.ServiceAffinityNone,
171 Type: corev1.ServiceTypeClusterIP,
172 },
173 },
174 },
175 {
176 testName: "service definition incorrect port",
177 serviceName: "foo",
178 servicePorts: []corev1.ServicePort{
179 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
180 },
181 serviceType: corev1.ServiceTypeClusterIP,
182 service: &corev1.Service{
183 ObjectMeta: om("foo"),
184 Spec: corev1.ServiceSpec{
185 Ports: []corev1.ServicePort{
186 {Name: "bar", Port: 1000, Protocol: "UDP", TargetPort: intstr.FromInt32(1000)},
187 },
188 Selector: nil,
189 ClusterIP: "1.2.3.4",
190 SessionAffinity: corev1.ServiceAffinityNone,
191 Type: corev1.ServiceTypeClusterIP,
192 },
193 },
194 expectUpdate: &corev1.Service{
195 ObjectMeta: om("foo"),
196 Spec: corev1.ServiceSpec{
197 Ports: []corev1.ServicePort{
198 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
199 },
200 Selector: nil,
201 ClusterIP: "1.2.3.4",
202 SessionAffinity: corev1.ServiceAffinityNone,
203 Type: corev1.ServiceTypeClusterIP,
204 },
205 },
206 },
207 {
208 testName: "service definition incorrect port name",
209 serviceName: "foo",
210 servicePorts: []corev1.ServicePort{
211 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
212 },
213 serviceType: corev1.ServiceTypeClusterIP,
214 service: &corev1.Service{
215 ObjectMeta: om("foo"),
216 Spec: corev1.ServiceSpec{
217 Ports: []corev1.ServicePort{
218 {Name: "foo", Port: 1000, Protocol: "UDP", TargetPort: intstr.FromInt32(1000)},
219 },
220 Selector: nil,
221 ClusterIP: "1.2.3.4",
222 SessionAffinity: corev1.ServiceAffinityNone,
223 Type: corev1.ServiceTypeClusterIP,
224 },
225 },
226 expectUpdate: &corev1.Service{
227 ObjectMeta: om("foo"),
228 Spec: corev1.ServiceSpec{
229 Ports: []corev1.ServicePort{
230 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
231 },
232 Selector: nil,
233 ClusterIP: "1.2.3.4",
234 SessionAffinity: corev1.ServiceAffinityNone,
235 Type: corev1.ServiceTypeClusterIP,
236 },
237 },
238 },
239 {
240 testName: "service definition incorrect target port",
241 serviceName: "foo",
242 servicePorts: []corev1.ServicePort{
243 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
244 },
245 serviceType: corev1.ServiceTypeClusterIP,
246 service: &corev1.Service{
247 ObjectMeta: om("foo"),
248 Spec: corev1.ServiceSpec{
249 Ports: []corev1.ServicePort{
250 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(1000)},
251 },
252 Selector: nil,
253 ClusterIP: "1.2.3.4",
254 SessionAffinity: corev1.ServiceAffinityNone,
255 Type: corev1.ServiceTypeClusterIP,
256 },
257 },
258 expectUpdate: &corev1.Service{
259 ObjectMeta: om("foo"),
260 Spec: corev1.ServiceSpec{
261 Ports: []corev1.ServicePort{
262 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
263 },
264 Selector: nil,
265 ClusterIP: "1.2.3.4",
266 SessionAffinity: corev1.ServiceAffinityNone,
267 Type: corev1.ServiceTypeClusterIP,
268 },
269 },
270 },
271 {
272 testName: "service definition incorrect protocol",
273 serviceName: "foo",
274 servicePorts: []corev1.ServicePort{
275 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
276 },
277 serviceType: corev1.ServiceTypeClusterIP,
278 service: &corev1.Service{
279 ObjectMeta: om("foo"),
280 Spec: corev1.ServiceSpec{
281 Ports: []corev1.ServicePort{
282 {Name: "foo", Port: 8080, Protocol: "UDP", TargetPort: intstr.FromInt32(8080)},
283 },
284 Selector: nil,
285 ClusterIP: "1.2.3.4",
286 SessionAffinity: corev1.ServiceAffinityNone,
287 Type: corev1.ServiceTypeClusterIP,
288 },
289 },
290 expectUpdate: &corev1.Service{
291 ObjectMeta: om("foo"),
292 Spec: corev1.ServiceSpec{
293 Ports: []corev1.ServicePort{
294 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
295 },
296 Selector: nil,
297 ClusterIP: "1.2.3.4",
298 SessionAffinity: corev1.ServiceAffinityNone,
299 Type: corev1.ServiceTypeClusterIP,
300 },
301 },
302 },
303 {
304 testName: "service definition has incorrect type",
305 serviceName: "foo",
306 servicePorts: []corev1.ServicePort{
307 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
308 },
309 serviceType: corev1.ServiceTypeClusterIP,
310 service: &corev1.Service{
311 ObjectMeta: om("foo"),
312 Spec: corev1.ServiceSpec{
313 Ports: []corev1.ServicePort{
314 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
315 },
316 Selector: nil,
317 ClusterIP: "1.2.3.4",
318 SessionAffinity: corev1.ServiceAffinityNone,
319 Type: corev1.ServiceTypeNodePort,
320 },
321 },
322 expectUpdate: &corev1.Service{
323 ObjectMeta: om("foo"),
324 Spec: corev1.ServiceSpec{
325 Ports: []corev1.ServicePort{
326 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
327 },
328 Selector: nil,
329 ClusterIP: "1.2.3.4",
330 SessionAffinity: corev1.ServiceAffinityNone,
331 Type: corev1.ServiceTypeClusterIP,
332 },
333 },
334 },
335 {
336 testName: "service definition satisfies",
337 serviceName: "foo",
338 servicePorts: []corev1.ServicePort{
339 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
340 },
341 serviceType: corev1.ServiceTypeClusterIP,
342 service: &corev1.Service{
343 ObjectMeta: om("foo"),
344 Spec: corev1.ServiceSpec{
345 Ports: []corev1.ServicePort{
346 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
347 },
348 Selector: nil,
349 ClusterIP: "1.2.3.4",
350 SessionAffinity: corev1.ServiceAffinityNone,
351 Type: corev1.ServiceTypeClusterIP,
352 },
353 },
354 expectUpdate: nil,
355 },
356 }
357 for _, test := range reconcileTests {
358 t.Run(test.testName, func(t *testing.T) {
359 master := Controller{}
360 fakeClient := fake.NewSimpleClientset(test.service)
361 serviceInformer := v1informers.NewServiceInformer(fakeClient, metav1.NamespaceDefault, 12*time.Hour, cache.Indexers{})
362 serviceStore := serviceInformer.GetIndexer()
363 err := serviceStore.Add(test.service)
364 if err != nil {
365 t.Fatalf("unexpected error adding service %v to the store: %v", test.service, err)
366 }
367 master.serviceLister = v1listers.NewServiceLister(serviceStore)
368 master.client = fakeClient
369 err = master.CreateOrUpdateMasterServiceIfNeeded(test.serviceName, netutils.ParseIPSloppy("1.2.3.4"), test.servicePorts, test.serviceType, true)
370 if err != nil {
371 t.Errorf("case %q: unexpected error: %v", test.testName, err)
372 }
373 updates := []core.UpdateAction{}
374 for _, action := range fakeClient.Actions() {
375 if action.GetVerb() == "update" {
376 updates = append(updates, action.(core.UpdateAction))
377 }
378 }
379 if test.expectUpdate != nil {
380 if len(updates) != 1 {
381 t.Errorf("case %q: unexpected updates: %v", test.testName, updates)
382 } else {
383 obj := updates[0].GetObject()
384 if e, a := test.expectUpdate.Spec, obj.(*corev1.Service).Spec; !reflect.DeepEqual(e, a) {
385 t.Errorf("case %q: expected update:\n%#v\ngot:\n%#v\n", test.testName, e, a)
386 }
387 }
388 }
389 if test.expectUpdate == nil && len(updates) > 0 {
390 t.Errorf("case %q: no update expected, yet saw: %v", test.testName, updates)
391 }
392 })
393 }
394
395 nonReconcileTests := []struct {
396 testName string
397 serviceName string
398 servicePorts []corev1.ServicePort
399 serviceType corev1.ServiceType
400 service *corev1.Service
401 expectUpdate *corev1.Service
402 }{
403 {
404 testName: "service definition wrong port, no expected update",
405 serviceName: "foo",
406 servicePorts: []corev1.ServicePort{
407 {Name: "foo", Port: 8080, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
408 },
409 serviceType: corev1.ServiceTypeClusterIP,
410 service: &corev1.Service{
411 ObjectMeta: om("foo"),
412 Spec: corev1.ServiceSpec{
413 Ports: []corev1.ServicePort{
414 {Name: "foo", Port: 1000, Protocol: "TCP", TargetPort: intstr.FromInt32(1000)},
415 },
416 Selector: nil,
417 ClusterIP: "1.2.3.4",
418 SessionAffinity: corev1.ServiceAffinityNone,
419 Type: corev1.ServiceTypeClusterIP,
420 },
421 },
422 expectUpdate: nil,
423 },
424 }
425 for _, test := range nonReconcileTests {
426 t.Run(test.testName, func(t *testing.T) {
427 master := Controller{}
428 fakeClient := fake.NewSimpleClientset(test.service)
429 master.client = fakeClient
430 serviceInformer := v1informers.NewServiceInformer(fakeClient, metav1.NamespaceDefault, 12*time.Hour, cache.Indexers{})
431 serviceStore := serviceInformer.GetIndexer()
432 err := serviceStore.Add(test.service)
433 if err != nil {
434 t.Fatalf("unexpected error adding service %v to the store: %v", test.service, err)
435 }
436 master.serviceLister = v1listers.NewServiceLister(serviceStore)
437
438 err = master.CreateOrUpdateMasterServiceIfNeeded(test.serviceName, netutils.ParseIPSloppy("1.2.3.4"), test.servicePorts, test.serviceType, false)
439 if err != nil {
440 t.Errorf("case %q: unexpected error: %v", test.testName, err)
441 }
442 updates := []core.UpdateAction{}
443 for _, action := range fakeClient.Actions() {
444 if action.GetVerb() == "update" {
445 updates = append(updates, action.(core.UpdateAction))
446 }
447 }
448 if test.expectUpdate != nil {
449 if len(updates) != 1 {
450 t.Errorf("case %q: unexpected updates: %v", test.testName, updates)
451 } else {
452 obj := updates[0].GetObject()
453 if e, a := test.expectUpdate.Spec, obj.(*corev1.Service).Spec; !reflect.DeepEqual(e, a) {
454 t.Errorf("case %q: expected update:\n%#v\ngot:\n%#v\n", test.testName, e, a)
455 }
456 }
457 }
458 if test.expectUpdate == nil && len(updates) > 0 {
459 t.Errorf("case %q: no update expected, yet saw: %v", test.testName, updates)
460 }
461 })
462 }
463 }
464
View as plain text