@@ -34,6 +34,9 @@ const (
34
34
35
35
// annotationCivoLoadBalancerAlgorithm is the annotation specifying the CivoLoadbalancer algorith.
36
36
annotationCivoLoadBalancerAlgorithm = "kubernetes.civo.com/loadbalancer-algorithm"
37
+
38
+ // annotationCivoIPv4 is the annotation specifying the reserved IP.
39
+ annotationCivoIPv4 = "kubernetes.civo.com/ipv4-address"
37
40
)
38
41
39
42
type loadbalancer struct {
@@ -88,30 +91,15 @@ func (l *loadbalancer) EnsureLoadBalancer(ctx context.Context, clusterName strin
88
91
return nil , err
89
92
}
90
93
91
- // CivLB has been found
94
+ // CivoLB has been found
92
95
if err == nil {
93
- lbuc := civogo.LoadBalancerUpdateConfig {
94
- ExternalTrafficPolicy : string (service .Spec .ExternalTrafficPolicy ),
95
- Region : Region ,
96
- }
97
-
98
- if enableProxyProtocol := getEnableProxyProtocol (service ); enableProxyProtocol != "" {
99
- lbuc .EnableProxyProtocol = enableProxyProtocol
100
- }
101
- if algorithm := getAlgorithm (service ); algorithm != "" {
102
- lbuc .Algorithm = algorithm
103
- }
104
- if firewallID := getFirewallID (service ); firewallID != "" {
105
- lbuc .FirewallID = firewallID
106
- }
107
-
108
- updatedlb , err := l .client .civoClient .UpdateLoadBalancer (civolb .ID , & lbuc )
96
+ ul , err := l .updateLBConfig (civolb , service , nodes )
109
97
if err != nil {
110
98
klog .Errorf ("Unable to update loadbalancer, error: %v" , err )
111
99
return nil , err
112
100
}
113
101
114
- return lbStatusFor (updatedlb ), nil
102
+ return lbStatusFor (ul )
115
103
}
116
104
117
105
err = createLoadBalancer (ctx , clusterName , service , nodes , l .client .civoClient , l .client .kclient )
@@ -125,41 +113,10 @@ func (l *loadbalancer) EnsureLoadBalancer(ctx context.Context, clusterName strin
125
113
return nil , err
126
114
}
127
115
128
- if civolb .State != statusAvailable {
129
- klog .Errorf ("Loadbalancer is not available, state: %s" , civolb .State )
130
- return nil , fmt .Errorf ("loadbalancer is not yet available, current state: %s" , civolb .State )
131
- }
132
-
133
- return lbStatusFor (civolb ), nil
116
+ return lbStatusFor (civolb )
134
117
}
135
118
136
- func lbStatusFor (civolb * civogo.LoadBalancer ) * v1.LoadBalancerStatus {
137
- status := & v1.LoadBalancerStatus {
138
- Ingress : make ([]v1.LoadBalancerIngress , 1 ),
139
- }
140
-
141
- if civolb .EnableProxyProtocol == "" {
142
- status .Ingress [0 ].IP = civolb .PublicIP
143
- }
144
- status .Ingress [0 ].Hostname = fmt .Sprintf ("%s.lb.civo.com" , civolb .ID )
145
-
146
- return status
147
- }
148
-
149
- // UpdateLoadBalancer updates hosts under the specified load balancer.
150
- // Implementations must treat the *v1.Service and *v1.Node
151
- // parameters as read-only and not modify them.
152
- // Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
153
- func (l * loadbalancer ) UpdateLoadBalancer (ctx context.Context , clusterName string , service * v1.Service , nodes []* v1.Node ) error {
154
- civolb , err := getLoadBalancer (ctx , l .client .civoClient , l .client .kclient , clusterName , service )
155
- if err != nil {
156
- if strings .Contains (err .Error (), string (civogo .ZeroMatchesError )) || strings .Contains (err .Error (), string (civogo .DatabaseLoadBalancerNotFoundError )) {
157
- return nil
158
- }
159
- klog .Errorf ("Unable to get loadbalancer, error: %v" , err )
160
- return err
161
- }
162
-
119
+ func (l * loadbalancer ) updateLBConfig (civolb * civogo.LoadBalancer , service * v1.Service , nodes []* v1.Node ) (* civogo.LoadBalancer , error ) {
163
120
lbuc := civogo.LoadBalancerUpdateConfig {
164
121
ExternalTrafficPolicy : string (service .Spec .ExternalTrafficPolicy ),
165
122
Region : Region ,
@@ -189,7 +146,97 @@ func (l *loadbalancer) UpdateLoadBalancer(ctx context.Context, clusterName strin
189
146
}
190
147
lbuc .Backends = backends
191
148
192
- ulb , err := l .client .civoClient .UpdateLoadBalancer (civolb .ID , & lbuc )
149
+ if ip := getReservedIPFromAnnotation (service ); ip != "" {
150
+ rip , err := l .client .civoClient .FindIP (ip )
151
+ if err != nil {
152
+ klog .Errorf ("Unable to find reserved IP, error: %v" , err )
153
+ return nil , err
154
+ }
155
+
156
+ // this is so that we don't try to reassign the reserved IP to the loadbalancer
157
+ if rip .AssignedTo .ID != civolb .ID {
158
+ _ , err = l .client .civoClient .AssignIP (rip .ID , civolb .ID , "loadbalancer" )
159
+ if err != nil {
160
+ klog .Errorf ("Unable to assign reserved IP, error: %v" , err )
161
+ return nil , err
162
+ }
163
+ }
164
+ } else {
165
+ ip , err := findIPWithLBID (l .client .civoClient , civolb .ID )
166
+ if err != nil {
167
+ klog .Errorf ("Unable to find IP with loadbalancer ID, error: %v" , err )
168
+ return nil , err
169
+ }
170
+
171
+ if ip != nil {
172
+ _ , err = l .client .civoClient .UnassignIP (ip .ID )
173
+ if err != nil {
174
+ klog .Errorf ("Unable to unassign IP, error: %v" , err )
175
+ return nil , err
176
+ }
177
+ }
178
+ }
179
+
180
+ updatedlb , err := l .client .civoClient .UpdateLoadBalancer (civolb .ID , & lbuc )
181
+ if err != nil {
182
+ klog .Errorf ("Unable to update loadbalancer, error: %v" , err )
183
+ return nil , err
184
+ }
185
+
186
+ return updatedlb , nil
187
+
188
+ }
189
+
190
+ // there's no direct way to find if the LB is using a reserved IP. This method lists all the reserved IPs in the account
191
+ // and checks if the loadbalancer is using one of them.
192
+ func findIPWithLBID (civo civogo.Clienter , lbID string ) (* civogo.IP , error ) {
193
+ ips , err := civo .ListIPs ()
194
+ if err != nil {
195
+ klog .Errorf ("Unable to list IPs, error: %v" , err )
196
+ return nil , err
197
+ }
198
+
199
+ for _ , ip := range ips .Items {
200
+ if ip .AssignedTo .ID == lbID {
201
+ return & ip , nil
202
+ }
203
+ }
204
+ return nil , nil
205
+ }
206
+
207
+ func lbStatusFor (civolb * civogo.LoadBalancer ) (* v1.LoadBalancerStatus , error ) {
208
+ status := & v1.LoadBalancerStatus {
209
+ Ingress : make ([]v1.LoadBalancerIngress , 1 ),
210
+ }
211
+
212
+ if civolb .State != statusAvailable {
213
+ klog .Errorf ("Loadbalancer is not available, state: %s" , civolb .State )
214
+ return nil , fmt .Errorf ("loadbalancer is not yet available, current state: %s" , civolb .State )
215
+ }
216
+
217
+ if civolb .EnableProxyProtocol == "" {
218
+ status .Ingress [0 ].IP = civolb .PublicIP
219
+ }
220
+ status .Ingress [0 ].Hostname = fmt .Sprintf ("%s.lb.civo.com" , civolb .ID )
221
+
222
+ return status , nil
223
+ }
224
+
225
+ // UpdateLoadBalancer updates hosts under the specified load balancer.
226
+ // Implementations must treat the *v1.Service and *v1.Node
227
+ // parameters as read-only and not modify them.
228
+ // Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager
229
+ func (l * loadbalancer ) UpdateLoadBalancer (ctx context.Context , clusterName string , service * v1.Service , nodes []* v1.Node ) error {
230
+ civolb , err := getLoadBalancer (ctx , l .client .civoClient , l .client .kclient , clusterName , service )
231
+ if err != nil {
232
+ if strings .Contains (err .Error (), string (civogo .ZeroMatchesError )) || strings .Contains (err .Error (), string (civogo .DatabaseLoadBalancerNotFoundError )) {
233
+ return nil
234
+ }
235
+ klog .Errorf ("Unable to get loadbalancer, error: %v" , err )
236
+ return err
237
+ }
238
+
239
+ ulb , err := l .updateLBConfig (civolb , service , nodes )
193
240
if err != nil {
194
241
klog .Errorf ("Unable to update loadbalancer, error: %v" , err )
195
242
return err
@@ -350,20 +397,19 @@ func getEnableProxyProtocol(service *v1.Service) string {
350
397
351
398
// getAlgorithm returns the algorithm value from the service annotation.
352
399
func getAlgorithm (service * v1.Service ) string {
353
- algorithm , ok := service .Annotations [annotationCivoLoadBalancerAlgorithm ]
354
- if ! ok {
355
- return ""
356
- }
400
+ algorithm , _ := service .Annotations [annotationCivoLoadBalancerAlgorithm ]
357
401
358
402
return algorithm
359
403
}
360
404
405
+ // getReservedIPFromAnnotation returns the reservedIP value from the service annotation.
406
+ func getReservedIPFromAnnotation (service * v1.Service ) string {
407
+ ip , _ := service .Annotations [annotationCivoIPv4 ]
408
+ return ip
409
+ }
410
+
361
411
// getFirewallID returns the firewallID value from the service annotation.
362
412
func getFirewallID (service * v1.Service ) string {
363
- firewallID , ok := service .Annotations [annotationCivoFirewallID ]
364
- if ! ok {
365
- return ""
366
- }
367
-
413
+ firewallID , _ := service .Annotations [annotationCivoFirewallID ]
368
414
return firewallID
369
415
}
0 commit comments