Skip to content

Commit d36fe7d

Browse files
committed
Add tests
1 parent 17a7c9f commit d36fe7d

File tree

2 files changed

+119
-28
lines changed

2 files changed

+119
-28
lines changed

pkg/lbmanager/lbmanager.go

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -39,23 +39,12 @@ func (m *LBManager) CreateSvcLBIfNone(ctx context.Context, in *api.Postgres) err
3939
return fmt.Errorf("failed to fetch Service of type LoadBalancer: %w", err)
4040
}
4141

42-
existingLBIP, nextFreePort, err := m.nextFreeSocket(ctx)
42+
nextFreePort, err := m.nextFreePort(ctx)
4343
if err != nil {
44-
return fmt.Errorf("failed to get a free port for creating Service of type LoadBalancer: %w", err)
45-
}
46-
var lbIPToUse string
47-
if m.LBIP != "" {
48-
// a specific IP was configured in the config, so use that one
49-
lbIPToUse = m.LBIP
50-
} else if existingLBIP != "" {
51-
// no ip was configured, but one is already in use, so use the existing one
52-
lbIPToUse = existingLBIP
53-
} else {
54-
// nothing was configured, nothing exists yet, so use an empty address so a new loadbalancer will be created and assigned
55-
lbIPToUse = ""
44+
return fmt.Errorf("failed to get the next free port: %w", err)
5645
}
5746

58-
if err := m.Create(ctx, in.ToSvcLB(lbIPToUse, nextFreePort)); err != nil {
47+
if err := m.Create(ctx, in.ToSvcLB(m.LBIP, nextFreePort)); err != nil {
5948
return fmt.Errorf("failed to create Service of type LoadBalancer: %w", err)
6049
}
6150
return nil
@@ -75,35 +64,27 @@ func (m *LBManager) DeleteSvcLB(ctx context.Context, in *api.Postgres) error {
7564
}
7665

7766
// nextFreeSocket finds any existing LoadBalancerIP and the next free port out of the configure port range.
78-
func (m *LBManager) nextFreeSocket(ctx context.Context) (string, int32, error) {
67+
func (m *LBManager) nextFreePort(ctx context.Context) (int32, error) {
7968
// TODO prevent concurrency issues when calculating port / ip.
8069

81-
anyExistingLBIP := ""
82-
8370
// Fetch all services managed by this postgreslet
8471
lbs := &corev1.ServiceList{}
8572
if err := m.List(ctx, lbs, client.MatchingLabels(api.SvcLoadBalancerLabel)); err != nil {
86-
return anyExistingLBIP, 0, fmt.Errorf("failed to fetch the list of services of type LoadBalancer: %w", err)
73+
return 0, fmt.Errorf("failed to fetch the list of services of type LoadBalancer: %w", err)
8774
}
8875

8976
// If there are none, this will be the first (managed) service we create, so start with PortRangeStart and return
9077
if len(lbs.Items) == 0 {
91-
return anyExistingLBIP, m.PortRangeStart, nil
78+
return m.PortRangeStart, nil
9279
}
9380

9481
// If there are already any managed services, store all the used ports in a slice.
95-
// Also store the LoadBalancerIP.
96-
portsInUse := make([]int32, len(lbs.Items))
82+
portsInUse := []int32{}
9783
for i := range lbs.Items {
9884
svc := lbs.Items[i]
9985
if len(svc.Spec.Ports) > 0 {
10086
portsInUse = append(portsInUse, svc.Spec.Ports[0].Port)
10187
}
102-
if svc.Spec.LoadBalancerIP != "" {
103-
// Technically, we only store the IP of the last Service in this list.
104-
// As there should only be one IP per postgreslet and one postgreslet per cluster, this is good enough.
105-
anyExistingLBIP = svc.Spec.LoadBalancerIP
106-
}
10788
}
10889

10990
// Now try all ports in the configured port range to find a free one.
@@ -115,11 +96,11 @@ func (m *LBManager) nextFreeSocket(ctx context.Context) (string, int32, error) {
11596
continue
11697
}
11798
// The postgreslet hasn't assigned this port yet, so use it.
118-
return anyExistingLBIP, port, nil
99+
return port, nil
119100
}
120101

121102
// If we made it this far, no free port could be found.
122-
return anyExistingLBIP, 0, errors.New("no free port in the configured port range found")
103+
return 0, errors.New("no free port in the configured port range found")
123104
}
124105

125106
func containsElem(s []int32, v int32) bool {

pkg/lbmanager/lbmanager_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package lbmanager
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
8+
api "github.com/fi-ts/postgreslet/api/v1"
9+
corev1 "k8s.io/api/core/v1"
10+
"k8s.io/apimachinery/pkg/runtime"
11+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
12+
)
13+
14+
func TestLBManager_nextFreePort(t *testing.T) {
15+
portRangeStart := int32(0)
16+
portRangeSize := int32(5)
17+
18+
tests := []struct {
19+
name string
20+
lbMgr *LBManager
21+
want int32
22+
wantErr bool
23+
}{
24+
{
25+
name: "no svc in the cluster",
26+
lbMgr: &LBManager{
27+
Client: fake.NewClientBuilder().WithScheme(scheme()).WithLists(svcListWithPorts()).Build(),
28+
LBIP: "0.0.0.0",
29+
PortRangeStart: portRangeStart,
30+
PortRangeSize: portRangeSize,
31+
},
32+
want: 0,
33+
wantErr: false,
34+
},
35+
{
36+
name: "one svc already in the cluster",
37+
lbMgr: &LBManager{
38+
Client: fake.NewClientBuilder().WithScheme(scheme()).WithLists(svcListWithPorts(0)).Build(),
39+
LBIP: "0.0.0.0",
40+
PortRangeStart: portRangeStart,
41+
PortRangeSize: portRangeSize,
42+
},
43+
want: 1,
44+
wantErr: false,
45+
},
46+
{
47+
name: "last free port left",
48+
lbMgr: &LBManager{
49+
Client: fake.NewClientBuilder().WithScheme(scheme()).WithLists(svcListWithPorts(0, 1, 2, 3)).Build(),
50+
LBIP: "0.0.0.0",
51+
PortRangeStart: portRangeStart,
52+
PortRangeSize: portRangeSize,
53+
},
54+
want: 4,
55+
wantErr: false,
56+
},
57+
{
58+
name: "no free port",
59+
lbMgr: &LBManager{
60+
Client: fake.NewClientBuilder().WithScheme(scheme()).WithLists(svcListWithPorts(0, 1, 2, 3, 4)).Build(),
61+
LBIP: "0.0.0.0",
62+
PortRangeStart: portRangeStart,
63+
PortRangeSize: portRangeSize,
64+
},
65+
want: 0,
66+
wantErr: true,
67+
},
68+
}
69+
70+
for _, tt := range tests {
71+
t.Run(tt.name, func(t *testing.T) {
72+
got, err := tt.lbMgr.nextFreePort(context.Background())
73+
if (err != nil) != tt.wantErr {
74+
t.Errorf("LBManager.nextFreePort() error = %v, wantErr %v", err, tt.wantErr)
75+
return
76+
}
77+
if got != tt.want {
78+
t.Errorf("LBManager.nextFreePort() = %v, want %v", got, tt.want)
79+
}
80+
})
81+
}
82+
}
83+
84+
func scheme() *runtime.Scheme {
85+
scheme := runtime.NewScheme()
86+
_ = corev1.AddToScheme(scheme)
87+
88+
return scheme
89+
}
90+
91+
// svcListWithPorts generates a `ServiceList` containing `Service`s with ports respectively
92+
func svcListWithPorts(ports ...int32) *corev1.ServiceList {
93+
svcList := &corev1.ServiceList{}
94+
for _, port := range ports {
95+
svcList.Items = append(svcList.Items, *svcWithPort(port))
96+
}
97+
return svcList
98+
}
99+
100+
func svcWithPort(port int32) *corev1.Service {
101+
svc := corev1.Service{}
102+
svc.Name = fmt.Sprintf("svc-with-port-%d", port)
103+
svc.Labels = api.SvcLoadBalancerLabel
104+
svc.Spec.Ports = []corev1.ServicePort{
105+
{
106+
Port: port,
107+
},
108+
}
109+
return &svc
110+
}

0 commit comments

Comments
 (0)