Skip to content

Commit a1f8645

Browse files
author
yujiel
committed
Fixed the issue where the service could not change from Loadbalancer to ClusterIP/NodePort
Signed-off-by: yujiel <yujiel@ebtech.com>
1 parent 6f5d651 commit a1f8645

File tree

2 files changed

+188
-0
lines changed

2 files changed

+188
-0
lines changed

pkg/controllers/resources/services/syncer.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ func (s *serviceSyncer) Sync(ctx *synccontext.SyncContext, event *synccontext.Sy
192192

193193
// update status
194194
event.Virtual.Status = event.Host.Status
195+
ensureLoadBalancerStatus(event.Virtual)
195196

196197
// bi-directional sync of annotations and labels
197198
event.Virtual.Annotations, event.Host.Annotations = translate.AnnotationsBidirectionalUpdate(event, s.excludedAnnotations...)
@@ -299,3 +300,17 @@ func TranslateServicePorts(ports []corev1.ServicePort) []corev1.ServicePort {
299300

300301
return retPorts
301302
}
303+
304+
// ensureLoadBalancerStatus removes any LoadBalancer-related fields from the Service
305+
// if it is of type ClusterIP.
306+
//
307+
// This is necessary to ensure consistency when syncing services from a virtual
308+
// cluster to the host cluster. ClusterIP services should not carry LoadBalancer
309+
// settings such as LoadBalancerIP or Status.LoadBalancer.Ingress, which are
310+
// specific to LoadBalancer-type services and can cause incorrect behavior if retained.
311+
func ensureLoadBalancerStatus(vObj *corev1.Service) {
312+
if vObj.Spec.Type != corev1.ServiceTypeLoadBalancer {
313+
vObj.Spec.LoadBalancerIP = ""
314+
vObj.Status.LoadBalancer.Ingress = make([]corev1.LoadBalancerIngress, 0)
315+
}
316+
}

pkg/controllers/resources/services/syncer_test.go

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,139 @@ func TestSync(t *testing.T) {
307307
Ports: vServiceNodePortFromExternal.Spec.Ports,
308308
},
309309
}
310+
vServiceNodePortFromLoadBalancer := &corev1.Service{
311+
ObjectMeta: metav1.ObjectMeta{
312+
Name: "kubernetes",
313+
Namespace: "default",
314+
},
315+
Spec: corev1.ServiceSpec{
316+
Type: corev1.ServiceTypeNodePort,
317+
Ports: []corev1.ServicePort{
318+
{
319+
Name: "http",
320+
Port: 8080,
321+
},
322+
},
323+
},
324+
}
325+
vServiceNodePortFromLoadBalancerBefore := &corev1.Service{
326+
ObjectMeta: metav1.ObjectMeta{
327+
Name: "kubernetes",
328+
Namespace: "default",
329+
},
330+
Spec: corev1.ServiceSpec{
331+
Type: corev1.ServiceTypeLoadBalancer,
332+
Ports: []corev1.ServicePort{
333+
{
334+
Name: "http",
335+
Port: 8080,
336+
},
337+
},
338+
},
339+
Status: corev1.ServiceStatus{
340+
LoadBalancer: corev1.LoadBalancerStatus{
341+
Ingress: []corev1.LoadBalancerIngress{
342+
{
343+
IP: "1.2.3.4",
344+
},
345+
},
346+
},
347+
},
348+
}
349+
350+
pServiceLoadBalancer := &corev1.Service{
351+
ObjectMeta: metav1.ObjectMeta{
352+
Name: "kubernetes",
353+
Namespace: "default",
354+
},
355+
Spec: corev1.ServiceSpec{
356+
Type: corev1.ServiceTypeLoadBalancer,
357+
Ports: []corev1.ServicePort{
358+
{
359+
Name: "http",
360+
Port: 8080,
361+
},
362+
},
363+
},
364+
Status: corev1.ServiceStatus{
365+
LoadBalancer: corev1.LoadBalancerStatus{
366+
Ingress: []corev1.LoadBalancerIngress{
367+
{
368+
IP: "1.2.3.4",
369+
},
370+
},
371+
},
372+
},
373+
}
374+
pServiceNodePortFromLoadBalancer := &corev1.Service{
375+
ObjectMeta: metav1.ObjectMeta{
376+
Name: "kubernetes",
377+
Namespace: "default",
378+
},
379+
Spec: corev1.ServiceSpec{
380+
Type: corev1.ServiceTypeNodePort,
381+
Ports: []corev1.ServicePort{
382+
{
383+
Name: "http",
384+
Port: 8080,
385+
},
386+
},
387+
},
388+
}
389+
vServiceClusterIPFromLoadBalancer := &corev1.Service{
390+
ObjectMeta: metav1.ObjectMeta{
391+
Name: "kubernetes",
392+
Namespace: "default",
393+
},
394+
Spec: corev1.ServiceSpec{
395+
Type: corev1.ServiceTypeClusterIP,
396+
Ports: []corev1.ServicePort{
397+
{
398+
Name: "http",
399+
Port: 9090,
400+
},
401+
},
402+
},
403+
}
404+
vServiceClusterIPFromLoadBalancerBefore := &corev1.Service{
405+
ObjectMeta: metav1.ObjectMeta{
406+
Name: "kubernetes",
407+
Namespace: "default",
408+
},
409+
Spec: corev1.ServiceSpec{
410+
Type: corev1.ServiceTypeLoadBalancer,
411+
Ports: []corev1.ServicePort{
412+
{
413+
Name: "http",
414+
Port: 9090,
415+
},
416+
},
417+
},
418+
Status: corev1.ServiceStatus{
419+
LoadBalancer: corev1.LoadBalancerStatus{
420+
Ingress: []corev1.LoadBalancerIngress{
421+
{
422+
IP: "1.2.3.4",
423+
},
424+
},
425+
},
426+
},
427+
}
428+
pServiceClusterIPFromLoadBalancer := &corev1.Service{
429+
ObjectMeta: metav1.ObjectMeta{
430+
Name: "kubernetes",
431+
Namespace: "default",
432+
},
433+
Spec: corev1.ServiceSpec{
434+
Type: corev1.ServiceTypeClusterIP,
435+
Ports: []corev1.ServicePort{
436+
{
437+
Name: "http",
438+
Port: 9090,
439+
},
440+
},
441+
},
442+
}
310443

311444
tests := []*syncertesting.SyncTest{
312445
{
@@ -609,6 +742,46 @@ func TestSync(t *testing.T) {
609742
assert.NilError(t, err)
610743
},
611744
},
745+
{
746+
Name: "Sync kubernetes service change type LoadBalancer to NodePort",
747+
InitialVirtualState: []runtime.Object{vServiceNodePortFromLoadBalancer.DeepCopy()},
748+
InitialPhysicalState: []runtime.Object{pServiceLoadBalancer.DeepCopy()},
749+
ExpectedVirtualState: map[schema.GroupVersionKind][]runtime.Object{
750+
corev1.SchemeGroupVersion.WithKind("Service"): {vServiceNodePortFromLoadBalancer.DeepCopy()},
751+
},
752+
ExpectedPhysicalState: map[schema.GroupVersionKind][]runtime.Object{
753+
corev1.SchemeGroupVersion.WithKind("Service"): {pServiceNodePortFromLoadBalancer.DeepCopy()},
754+
},
755+
Sync: func(ctx *synccontext.RegisterContext) {
756+
syncCtx, syncer := syncertesting.FakeStartSyncer(t, ctx, New)
757+
pObjOld := pServiceLoadBalancer.DeepCopy()
758+
pObjNew := pServiceLoadBalancer.DeepCopy()
759+
vObjOld := vServiceNodePortFromLoadBalancerBefore.DeepCopy()
760+
vObjNew := vServiceNodePortFromLoadBalancer.DeepCopy()
761+
_, err := syncer.(*serviceSyncer).Sync(syncCtx, synccontext.NewSyncEventWithOld(pObjOld, pObjNew, vObjOld, vObjNew))
762+
assert.NilError(t, err)
763+
},
764+
},
765+
{
766+
Name: "Sync kubernetes service change type LoadBalancer to ClusterIP",
767+
InitialVirtualState: []runtime.Object{vServiceClusterIPFromLoadBalancer.DeepCopy()},
768+
InitialPhysicalState: []runtime.Object{pServiceLoadBalancer.DeepCopy()},
769+
ExpectedVirtualState: map[schema.GroupVersionKind][]runtime.Object{
770+
corev1.SchemeGroupVersion.WithKind("Service"): {vServiceClusterIPFromLoadBalancer.DeepCopy()},
771+
},
772+
ExpectedPhysicalState: map[schema.GroupVersionKind][]runtime.Object{
773+
corev1.SchemeGroupVersion.WithKind("Service"): {pServiceClusterIPFromLoadBalancer.DeepCopy()},
774+
},
775+
Sync: func(ctx *synccontext.RegisterContext) {
776+
syncCtx, syncer := syncertesting.FakeStartSyncer(t, ctx, New)
777+
pObjOld := pServiceLoadBalancer.DeepCopy()
778+
pObjNew := pServiceLoadBalancer.DeepCopy()
779+
vObjOld := vServiceClusterIPFromLoadBalancerBefore.DeepCopy()
780+
vObjNew := vServiceClusterIPFromLoadBalancer.DeepCopy()
781+
_, err := syncer.(*serviceSyncer).Sync(syncCtx, synccontext.NewSyncEventWithOld(pObjOld, pObjNew, vObjOld, vObjNew))
782+
assert.NilError(t, err)
783+
},
784+
},
612785
}
613786
syncertesting.RunTests(t, tests)
614787
}

0 commit comments

Comments
 (0)