@@ -11,13 +11,15 @@ import (
11
11
"sigs.k8s.io/controller-runtime/pkg/client"
12
12
)
13
13
14
+ // LBManager Responsible for the creation and deletion of externally accessible Services to access the Postgresql clusters managed by the Postgreslet.
14
15
type LBManager struct {
15
- client.Client // todo: service cluster
16
- LBIP string // todo: via configmap
17
- PortRangeStart int32 // todo: via configmap
16
+ client.Client
17
+ LBIP string
18
+ PortRangeStart int32
18
19
PortRangeSize int32
19
20
}
20
21
22
+ // New Creates a new LBManager with the given configuration
21
23
func New (client client.Client , lbIP string , portRangeStart , portRangeSize int32 ) * LBManager {
22
24
return & LBManager {
23
25
Client : client ,
@@ -27,6 +29,7 @@ func New(client client.Client, lbIP string, portRangeStart, portRangeSize int32)
27
29
}
28
30
}
29
31
32
+ // CreateSvcLBIfNone Creates a new Service of type LoadBalancer for the given Postgres resource if neccessary
30
33
func (m * LBManager ) CreateSvcLBIfNone (ctx context.Context , in * api.Postgres ) error {
31
34
if err := m .Get (ctx , client.ObjectKey {
32
35
Namespace : in .ToPeripheralResourceNamespace (),
@@ -60,6 +63,7 @@ func (m *LBManager) CreateSvcLBIfNone(ctx context.Context, in *api.Postgres) err
60
63
return nil
61
64
}
62
65
66
+ // DeleteSvcLB Deletes the corresponding Service of type LoadBalancer of the given Postgres resource.
63
67
func (m * LBManager ) DeleteSvcLB (ctx context.Context , in * api.Postgres ) error {
64
68
lb := & corev1.Service {}
65
69
lb .Namespace = in .ToPeripheralResourceNamespace ()
@@ -70,37 +74,59 @@ func (m *LBManager) DeleteSvcLB(ctx context.Context, in *api.Postgres) error {
70
74
return nil
71
75
}
72
76
77
+ // nextFreeSocket finds any existing LoadBalancerIP and the next free port out of the configure port range.
73
78
func (m * LBManager ) nextFreeSocket (ctx context.Context ) (string , int32 , error ) {
74
79
// TODO prevent concurrency issues when calculating port / ip.
75
80
76
81
existingLBIP := ""
77
82
83
+ // Fetch all services managed by this postgreslet
78
84
lbs := & corev1.ServiceList {}
79
85
if err := m .List (ctx , lbs , client .MatchingLabels (api .SvcLoadBalancerLabel )); err != nil {
80
86
return existingLBIP , 0 , fmt .Errorf ("failed to fetch the list of services of type LoadBalancer: %w" , err )
81
87
}
82
88
89
+ // If there are none, this will be the first (managed) service we create, so start with PortRangeStart and return
83
90
if len (lbs .Items ) == 0 {
84
91
return existingLBIP , m .PortRangeStart , nil
85
92
}
86
93
87
- // Record weather any port is occupied
88
- isOccupied := make ([]bool , int (m .PortRangeSize ))
94
+ // 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 ))
89
97
for i := range lbs .Items {
90
98
svc := lbs .Items [i ]
91
99
if len (svc .Spec .Ports ) > 0 {
92
- isOccupied [ svc .Spec .Ports [0 ].Port - m . PortRangeStart ] = true
100
+ portsInUse = append ( portsInUse , svc .Spec .Ports [0 ].Port )
93
101
}
94
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.
95
105
existingLBIP = svc .Spec .LoadBalancerIP
96
106
}
97
107
}
98
108
99
- for i := range isOccupied {
100
- if ! isOccupied [i ] {
101
- return existingLBIP , m .PortRangeStart + int32 (i ), nil
109
+ // Now try all ports in the configured port range to find a free one.
110
+ // While not as effective as other implementations, this allows us to freely change PortRangeStart and PortRangeSize
111
+ // retroactively without breaking the implementation.
112
+ for port := m .PortRangeStart ; port < m .PortRangeStart + m .PortRangeSize ; port ++ {
113
+ if containsElem (portsInUse , port ) {
114
+ // Port already in use, try the next one
115
+ continue
102
116
}
117
+ // The postgreslet hasn't assigned this port yet, so use it.
118
+ return existingLBIP , port , nil
103
119
}
104
120
105
- return existingLBIP , 0 , errors .New ("no free port" )
121
+ // If we made it this far, no free port could be found.
122
+ return existingLBIP , 0 , errors .New ("no free port in the configured port range found" )
123
+ }
124
+
125
+ func containsElem (s []int32 , v int32 ) bool {
126
+ for _ , elem := range s {
127
+ if elem == v {
128
+ return true
129
+ }
130
+ }
131
+ return false
106
132
}
0 commit comments