From 97b7df265f2a546c4aec8c16bc40d710c7015ee5 Mon Sep 17 00:00:00 2001 From: Oleg Isakov Date: Fri, 23 Jan 2026 10:44:35 +0200 Subject: [PATCH 1/2] add find lb by label first --- serverscom/loadbalancers.go | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/serverscom/loadbalancers.go b/serverscom/loadbalancers.go index 4f4578d..2e113a6 100644 --- a/serverscom/loadbalancers.go +++ b/serverscom/loadbalancers.go @@ -34,7 +34,7 @@ func newLoadBalancers(client *cli.Client, defaultLocationID *int64) cloudprovide } func (l *loadBalancers) GetLoadBalancer(ctx context.Context, clusterName string, service *v1.Service) (*v1.LoadBalancerStatus, bool, error) { - loadBalancer, err := l.findLoadBalancerByName(ctx, clusterName, service) + loadBalancer, err := l.findLoadBalancer(ctx, clusterName, service) if err != nil && isNotFoundError(err) { return nil, false, nil @@ -59,7 +59,7 @@ func (l *loadBalancers) GetLoadBalancerName(ctx context.Context, clusterName str } func (l *loadBalancers) EnsureLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) (*v1.LoadBalancerStatus, error) { - loadBalancer, err := l.findLoadBalancerByName(ctx, clusterName, service) + loadBalancer, err := l.findLoadBalancer(ctx, clusterName, service) if err != nil { if !isNotFoundError(err) { return nil, err @@ -130,7 +130,7 @@ func (l *loadBalancers) EnsureLoadBalancer(ctx context.Context, clusterName stri } func (l *loadBalancers) UpdateLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) error { - loadBalancer, err := l.findLoadBalancerByName(ctx, clusterName, service) + loadBalancer, err := l.findLoadBalancer(ctx, clusterName, service) if err != nil { return err } @@ -166,7 +166,7 @@ func (l *loadBalancers) UpdateLoadBalancer(ctx context.Context, clusterName stri } func (l *loadBalancers) EnsureLoadBalancerDeleted(ctx context.Context, clusterName string, service *v1.Service) error { - loadBalancer, err := l.findLoadBalancerByName(ctx, clusterName, service) + loadBalancer, err := l.findLoadBalancer(ctx, clusterName, service) if err != nil { if isNotFoundError(err) { return nil @@ -178,10 +178,29 @@ func (l *loadBalancers) EnsureLoadBalancerDeleted(ctx context.Context, clusterNa return l.client.LoadBalancers.DeleteL4LoadBalancer(ctx, loadBalancer.ID) } -func (l *loadBalancers) findLoadBalancerByName(ctx context.Context, clusterName string, service *v1.Service) (*cli.L4LoadBalancer, error) { - name := l.GetLoadBalancerName(ctx, clusterName, service) - +// findLoadBalancer tries to find load balancer by label selector first, then by name +func (l *loadBalancers) findLoadBalancer(ctx context.Context, clusterName string, service *v1.Service) (*cli.L4LoadBalancer, error) { + // first try to find by label selector + labelSelector := loadBalancerServiceUUIDLabel + "=" + string(service.UID) loadBalancers, err := l.client.LoadBalancers.Collection(). + SetPerPage(100). + SetParam(typeParamKey, "l4"). + SetParam("label_selector", labelSelector). + Collect(ctx) + + if err != nil { + return nil, err + } + + if len(loadBalancers) == 1 { + return l.client.LoadBalancers.GetL4LoadBalancer(ctx, loadBalancers[0].ID) + } else if len(loadBalancers) > 1 { + return nil, fmt.Errorf("found more than one load balancer with the same label selector: %s", labelSelector) + } + + // fallback to name-based search + name := l.GetLoadBalancerName(ctx, clusterName, service) + loadBalancers, err = l.client.LoadBalancers.Collection(). SetPerPage(100). SetParam(typeParamKey, "l4"). SetParam(searchPatternParamKey, name). From 25fdbba4fffaeb850388de66d81cf8b9d6146c47 Mon Sep 17 00:00:00 2001 From: Oleg Isakov Date: Fri, 23 Jan 2026 11:31:20 +0200 Subject: [PATCH 2/2] fix tests --- serverscom/loadbalancers_test.go | 107 ++++++++++++++++++++++++++----- 1 file changed, 90 insertions(+), 17 deletions(-) diff --git a/serverscom/loadbalancers_test.go b/serverscom/loadbalancers_test.go index 1cab72b..598ea1c 100644 --- a/serverscom/loadbalancers_test.go +++ b/serverscom/loadbalancers_test.go @@ -37,9 +37,18 @@ func TestLoadBalancers_GetLoadBalancer(t *testing.T) { ctx := context.TODO() + // First search by label_selector (should return empty) collection.EXPECT().SetPerPage(100).Return(collection) - collection.EXPECT().SetParam("search_pattern", balancerName).Return(collection) collection.EXPECT().SetParam("type", "l4").Return(collection) + collection.EXPECT().SetParam("label_selector", "k8s.servers.com/service-id=123").Return(collection) + collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{}, nil) + + service.EXPECT().Collection().Return(collection) + + // Second search by name + collection.EXPECT().SetPerPage(100).Return(collection) + collection.EXPECT().SetParam("type", "l4").Return(collection) + collection.EXPECT().SetParam("search_pattern", balancerName).Return(collection) collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{balancer}, nil) service.EXPECT().Collection().Return(collection) @@ -81,9 +90,18 @@ func TestLoadBalancers_GetLoadBalancerNonActive(t *testing.T) { ctx := context.TODO() + // First search by label_selector (should return empty) collection.EXPECT().SetPerPage(100).Return(collection) - collection.EXPECT().SetParam("search_pattern", balancerName).Return(collection) collection.EXPECT().SetParam("type", "l4").Return(collection) + collection.EXPECT().SetParam("label_selector", "k8s.servers.com/service-id=123").Return(collection) + collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{}, nil) + + service.EXPECT().Collection().Return(collection) + + // Second search by name + collection.EXPECT().SetPerPage(100).Return(collection) + collection.EXPECT().SetParam("type", "l4").Return(collection) + collection.EXPECT().SetParam("search_pattern", balancerName).Return(collection) collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{balancer}, nil) service.EXPECT().Collection().Return(collection) @@ -118,9 +136,18 @@ func TestLoadBalancers_GetLoadBalancerEmptyList(t *testing.T) { ctx := context.TODO() + // First search by label_selector (should return empty) collection.EXPECT().SetPerPage(100).Return(collection) - collection.EXPECT().SetParam("search_pattern", balancerName).Return(collection) collection.EXPECT().SetParam("type", "l4").Return(collection) + collection.EXPECT().SetParam("label_selector", "k8s.servers.com/service-id=123").Return(collection) + collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{}, nil) + + service.EXPECT().Collection().Return(collection) + + // Second search by name (should return empty) + collection.EXPECT().SetPerPage(100).Return(collection) + collection.EXPECT().SetParam("type", "l4").Return(collection) + collection.EXPECT().SetParam("search_pattern", balancerName).Return(collection) collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{}, nil) service.EXPECT().Collection().Return(collection) @@ -254,9 +281,10 @@ func TestLoadBalancers_EnsureLoadBalancer(t *testing.T) { Labels: defaultLabels, } + // First search by label_selector (should find the balancer) collection.EXPECT().SetPerPage(100).Return(collection) - collection.EXPECT().SetParam("search_pattern", balancerName).Return(collection) collection.EXPECT().SetParam("type", "l4").Return(collection) + collection.EXPECT().SetParam("label_selector", "k8s.servers.com/service-id=123").Return(collection) collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{balancer}, nil) service.EXPECT().Collection().Return(collection) @@ -362,12 +390,21 @@ func TestLoadBalancers_EnsureLoadBalancerWithCreate(t *testing.T) { Labels: defaultLabels, } - collection.EXPECT().SetPerPage(100).Return(collection).Times(2) - collection.EXPECT().SetParam("search_pattern", balancerName).Return(collection).Times(2) - collection.EXPECT().SetParam("type", "l4").Return(collection).Times(2) - collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{}, nil).Times(2) + // First search by label_selector (should return empty) + collection.EXPECT().SetPerPage(100).Return(collection) + collection.EXPECT().SetParam("type", "l4").Return(collection) + collection.EXPECT().SetParam("label_selector", "k8s.servers.com/service-id=123").Return(collection) + collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{}, nil) + + service.EXPECT().Collection().Return(collection) + + // Second search by name (should return empty) + collection.EXPECT().SetPerPage(100).Return(collection) + collection.EXPECT().SetParam("type", "l4").Return(collection) + collection.EXPECT().SetParam("search_pattern", balancerName).Return(collection) + collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{}, nil) - service.EXPECT().Collection().Return(collection).Times(2) + service.EXPECT().Collection().Return(collection) service.EXPECT().CreateL4LoadBalancer(ctx, input).Return(&l4Balancer, nil) client := cli.NewClient("some") @@ -400,6 +437,22 @@ func TestLoadBalancers_EnsureLoadBalancerWithCreate(t *testing.T) { } input.ClusterID = &clusterID + + // First search by label_selector (should return empty) + collection.EXPECT().SetPerPage(100).Return(collection) + collection.EXPECT().SetParam("type", "l4").Return(collection) + collection.EXPECT().SetParam("label_selector", "k8s.servers.com/service-id=123").Return(collection) + collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{}, nil) + + service.EXPECT().Collection().Return(collection) + + // Second search by name (should return empty) + collection.EXPECT().SetPerPage(100).Return(collection) + collection.EXPECT().SetParam("type", "l4").Return(collection) + collection.EXPECT().SetParam("search_pattern", balancerName).Return(collection) + collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{}, nil) + + service.EXPECT().Collection().Return(collection) service.EXPECT().CreateL4LoadBalancer(ctx, input).Return(&l4Balancer, nil) status, err = balancerInterface.EnsureLoadBalancer(ctx, "cluster", &srv, []*v1.Node{&node}) @@ -488,13 +541,14 @@ func TestLoadBalancers_UpdateLoadBalancer(t *testing.T) { Labels: defaultLabels, } - collection.EXPECT().SetPerPage(100).Return(collection).Times(2) - collection.EXPECT().SetParam("search_pattern", balancerName).Return(collection).Times(2) - collection.EXPECT().SetParam("type", "l4").Return(collection).Times(2) - collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{balancer}, nil).Times(2) + // First call: search by label_selector (should find the balancer) + collection.EXPECT().SetPerPage(100).Return(collection) + collection.EXPECT().SetParam("type", "l4").Return(collection) + collection.EXPECT().SetParam("label_selector", "k8s.servers.com/service-id=123").Return(collection) + collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{balancer}, nil) - service.EXPECT().Collection().Return(collection).Times(2) - service.EXPECT().GetL4LoadBalancer(ctx, "a").Return(&l4Balancer, nil).Times(2) + service.EXPECT().Collection().Return(collection) + service.EXPECT().GetL4LoadBalancer(ctx, "a").Return(&l4Balancer, nil) service.EXPECT().UpdateL4LoadBalancer(ctx, "a", input).Return(&l4Balancer, nil) client := cli.NewClient("some") @@ -528,6 +582,15 @@ func TestLoadBalancers_UpdateLoadBalancer(t *testing.T) { input.ClusterID = &clusterID input.SharedCluster = nil + + // Second call: search by label_selector (should find the balancer) + collection.EXPECT().SetPerPage(100).Return(collection) + collection.EXPECT().SetParam("type", "l4").Return(collection) + collection.EXPECT().SetParam("label_selector", "k8s.servers.com/service-id=123").Return(collection) + collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{balancer}, nil) + + service.EXPECT().Collection().Return(collection) + service.EXPECT().GetL4LoadBalancer(ctx, "a").Return(&l4Balancer, nil) service.EXPECT().UpdateL4LoadBalancer(ctx, "a", input).Return(&l4Balancer, nil) status, err = balancerInterface.EnsureLoadBalancer(ctx, "cluster", &srv, []*v1.Node{&node}) @@ -553,9 +616,10 @@ func TestLoadBalancers_EnsureLoadBalancerDeleted(t *testing.T) { ctx := context.TODO() + // First search by label_selector (should find the balancer) collection.EXPECT().SetPerPage(100).Return(collection) - collection.EXPECT().SetParam("search_pattern", balancerName).Return(collection) collection.EXPECT().SetParam("type", "l4").Return(collection) + collection.EXPECT().SetParam("label_selector", "k8s.servers.com/service-id=123").Return(collection) collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{balancer}, nil) service.EXPECT().Collection().Return(collection) @@ -591,9 +655,18 @@ func TestLoadBalancers_EnsureLoadBalancerDeletedWhenBalancerAlreadyDeleted(t *te ctx := context.TODO() + // First search by label_selector (should return empty) + collection.EXPECT().SetPerPage(100).Return(collection) + collection.EXPECT().SetParam("type", "l4").Return(collection) + collection.EXPECT().SetParam("label_selector", "k8s.servers.com/service-id=123").Return(collection) + collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{}, nil) + + service.EXPECT().Collection().Return(collection) + + // Second search by name (should return empty) collection.EXPECT().SetPerPage(100).Return(collection) - collection.EXPECT().SetParam("search_pattern", balancerName).Return(collection) collection.EXPECT().SetParam("type", "l4").Return(collection) + collection.EXPECT().SetParam("search_pattern", balancerName).Return(collection) collection.EXPECT().Collect(ctx).Return([]cli.LoadBalancer{}, nil) service.EXPECT().Collection().Return(collection)