Skip to content

Commit e9c8095

Browse files
committed
✨ elbv2/tg: Allow TG health check customization
Some deployments requires customizations in the health check configurations, such as protocol, probe periods and checks. This change introduce health check override for all Load Balancers and listeners (API and additional). The override for the API target group are limited to the probe configuration, and customizing the Path, Port and Protocol for the Target Group for the Kube API Server is not allowed.
1 parent 5570d7c commit e9c8095

File tree

1 file changed

+124
-34
lines changed

1 file changed

+124
-34
lines changed

pkg/cloud/services/elb/loadbalancer.go

Lines changed: 124 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,98 @@ func (s *Service) reconcileV2LB(lbSpec *infrav1.AWSLoadBalancerSpec) error {
159159
return nil
160160
}
161161

162+
// getAPITargetGroupHealthCheck creates the health check for the Kube apiserver target group,
163+
// limiting the customization for the health check probe counters (skipping standarized/reserved
164+
// fields: Protocol, Port or Path). To customize the health check protocol, use HealthCheckProtocol instead.
165+
func (s *Service) getAPITargetGroupHealthCheck(lbSpec *infrav1.AWSLoadBalancerSpec) *infrav1.TargetGroupHealthCheck {
166+
apiHealthCheckProtocol := infrav1.ELBProtocolTCP.String()
167+
if lbSpec != nil && lbSpec.HealthCheckProtocol != nil {
168+
s.scope.Trace("Found API health check protocol override in the Load Balancer spec, applying it to the API Target Group", "api-server-elb", lbSpec.HealthCheckProtocol.String())
169+
apiHealthCheckProtocol = lbSpec.HealthCheckProtocol.String()
170+
}
171+
apiHealthCheck := &infrav1.TargetGroupHealthCheck{
172+
Protocol: aws.String(apiHealthCheckProtocol),
173+
Port: aws.String(infrav1.DefaultAPIServerPortString),
174+
Path: nil,
175+
IntervalSeconds: aws.Int64(infrav1.DefaultAPIServerHealthCheckIntervalSec),
176+
TimeoutSeconds: aws.Int64(infrav1.DefaultAPIServerHealthCheckTimeoutSec),
177+
ThresholdCount: aws.Int64(infrav1.DefaultAPIServerHealthThresholdCount),
178+
UnhealthyThresholdCount: aws.Int64(infrav1.DefaultAPIServerUnhealthThresholdCount),
179+
}
180+
if apiHealthCheckProtocol == infrav1.ELBProtocolHTTP.String() || apiHealthCheckProtocol == infrav1.ELBProtocolHTTPS.String() {
181+
apiHealthCheck.Path = aws.String(infrav1.DefaultAPIServerHealthCheckPath)
182+
}
183+
184+
if lbSpec != nil && lbSpec.HealthCheck != nil {
185+
s.scope.Trace("Found API health check override in the Load Balancer spec, applying it to the API Target Group", "api-server-elb", lbSpec.HealthCheck)
186+
if lbSpec.HealthCheck.IntervalSeconds != nil {
187+
apiHealthCheck.IntervalSeconds = lbSpec.HealthCheck.IntervalSeconds
188+
}
189+
if lbSpec.HealthCheck.TimeoutSeconds != nil {
190+
apiHealthCheck.TimeoutSeconds = lbSpec.HealthCheck.TimeoutSeconds
191+
}
192+
if lbSpec.HealthCheck.ThresholdCount != nil {
193+
apiHealthCheck.ThresholdCount = lbSpec.HealthCheck.ThresholdCount
194+
}
195+
if lbSpec.HealthCheck.UnhealthyThresholdCount != nil {
196+
apiHealthCheck.UnhealthyThresholdCount = lbSpec.HealthCheck.UnhealthyThresholdCount
197+
}
198+
}
199+
return apiHealthCheck
200+
}
201+
202+
// getAdditionalTargetGroupHealthCheck creates the target group health check for additional listener.
203+
// Additional listeners allows to set customized attributes for health check.
204+
func (s *Service) getAdditionalTargetGroupHealthCheck(ln infrav1.AdditionalListenerSpec) *infrav1.TargetGroupHealthCheck {
205+
healthCheck := &infrav1.TargetGroupHealthCheck{
206+
Port: aws.String(fmt.Sprintf("%d", ln.Port)),
207+
Protocol: aws.String(ln.Protocol.String()),
208+
Path: nil,
209+
IntervalSeconds: aws.Int64(infrav1.DefaultAPIServerHealthCheckIntervalSec),
210+
TimeoutSeconds: aws.Int64(infrav1.DefaultAPIServerHealthCheckTimeoutSec),
211+
ThresholdCount: aws.Int64(infrav1.DefaultAPIServerHealthThresholdCount),
212+
UnhealthyThresholdCount: aws.Int64(infrav1.DefaultAPIServerUnhealthThresholdCount),
213+
}
214+
if ln.HealthCheck == nil {
215+
return healthCheck
216+
}
217+
if ln.HealthCheck.Protocol != nil {
218+
healthCheck.Protocol = aws.String(*ln.HealthCheck.Protocol)
219+
}
220+
if ln.HealthCheck.Port != nil {
221+
healthCheck.Port = aws.String(*ln.HealthCheck.Port)
222+
}
223+
if ln.HealthCheck.Path != nil {
224+
healthCheck.Path = aws.String(*ln.HealthCheck.Path)
225+
}
226+
if ln.HealthCheck.IntervalSeconds != nil {
227+
healthCheck.IntervalSeconds = aws.Int64(*ln.HealthCheck.IntervalSeconds)
228+
}
229+
if ln.HealthCheck.TimeoutSeconds != nil {
230+
healthCheck.TimeoutSeconds = aws.Int64(*ln.HealthCheck.TimeoutSeconds)
231+
}
232+
if ln.HealthCheck.ThresholdCount != nil {
233+
healthCheck.ThresholdCount = aws.Int64(*ln.HealthCheck.ThresholdCount)
234+
}
235+
if ln.HealthCheck.UnhealthyThresholdCount != nil {
236+
healthCheck.UnhealthyThresholdCount = aws.Int64(*ln.HealthCheck.UnhealthyThresholdCount)
237+
}
238+
239+
return healthCheck
240+
}
241+
242+
// getTargetGroupName creates the target group name based on LB Name, when defined, otherwise return
243+
// the standard name created from the timestamp.
244+
func (s *Service) getTargetGroupName(lbSpec *infrav1.AWSLoadBalancerSpec, defaultPrefix string, port int64) string {
245+
targetName := fmt.Sprintf("%s-%d", defaultPrefix, time.Now().Unix())
246+
247+
if lbSpec != nil && lbSpec.Name != nil {
248+
targetName = fmt.Sprintf("%s-%d", *lbSpec.Name, port)
249+
}
250+
251+
return targetName
252+
}
253+
162254
func (s *Service) getAPIServerLBSpec(elbName string, lbSpec *infrav1.AWSLoadBalancerSpec) (*infrav1.LoadBalancer, error) {
163255
var securityGroupIDs []string
164256
if lbSpec != nil {
@@ -173,22 +265,8 @@ func (s *Service) getAPIServerLBSpec(elbName string, lbSpec *infrav1.AWSLoadBala
173265
}
174266

175267
// The default API health check is TCP, allowing customization to HTTP or HTTPS when HealthCheckProtocol is set.
176-
apiHealthCheckProtocol := infrav1.ELBProtocolTCP
177-
if lbSpec != nil && lbSpec.HealthCheckProtocol != nil {
178-
s.scope.Trace("Found API health check protocol override in the Load Balancer spec, applying it to the API Target Group", "api-server-elb", lbSpec.HealthCheckProtocol)
179-
apiHealthCheckProtocol = *lbSpec.HealthCheckProtocol
180-
}
181-
apiHealthCheck := &infrav1.TargetGroupHealthCheck{
182-
Protocol: aws.String(apiHealthCheckProtocol.String()),
183-
Port: aws.String(infrav1.DefaultAPIServerPortString),
184-
Path: nil,
185-
IntervalSeconds: aws.Int64(infrav1.DefaultAPIServerHealthCheckIntervalSec),
186-
TimeoutSeconds: aws.Int64(infrav1.DefaultAPIServerHealthCheckTimeoutSec),
187-
ThresholdCount: aws.Int64(infrav1.DefaultAPIServerHealthThresholdCount),
188-
}
189-
if apiHealthCheckProtocol == infrav1.ELBProtocolHTTP || apiHealthCheckProtocol == infrav1.ELBProtocolHTTPS {
190-
apiHealthCheck.Path = aws.String(infrav1.DefaultAPIServerHealthCheckPath)
191-
}
268+
apiHealthCheck := s.getAPITargetGroupHealthCheck(lbSpec)
269+
apiTargetGroupName := s.getTargetGroupName(lbSpec, "apiserver-target", infrav1.DefaultAPIServerPort)
192270
res := &infrav1.LoadBalancer{
193271
Name: elbName,
194272
Scheme: scheme,
@@ -198,7 +276,7 @@ func (s *Service) getAPIServerLBSpec(elbName string, lbSpec *infrav1.AWSLoadBala
198276
Protocol: infrav1.ELBProtocolTCP,
199277
Port: infrav1.DefaultAPIServerPort,
200278
TargetGroup: infrav1.TargetGroupSpec{
201-
Name: fmt.Sprintf("apiserver-target-%d", time.Now().Unix()),
279+
Name: apiTargetGroupName,
202280
Port: infrav1.DefaultAPIServerPort,
203281
Protocol: infrav1.ELBProtocolTCP,
204282
VpcID: s.scope.VPC().ID,
@@ -210,19 +288,25 @@ func (s *Service) getAPIServerLBSpec(elbName string, lbSpec *infrav1.AWSLoadBala
210288
}
211289

212290
if lbSpec != nil {
213-
for _, additionalListeners := range lbSpec.AdditionalListeners {
291+
for _, listener := range lbSpec.AdditionalListeners {
292+
targetGroupName := s.getTargetGroupName(lbSpec, "additional-listener", listener.Port)
293+
lnHealthCheck := &infrav1.TargetGroupHealthCheck{
294+
Protocol: aws.String(string(listener.Protocol)),
295+
Port: aws.String(strconv.FormatInt(listener.Port, 10)),
296+
}
297+
if listener.HealthCheck != nil {
298+
s.scope.Trace("Found health check override in the additional listener spec, applying it to the Target Group", listener.HealthCheck)
299+
lnHealthCheck = s.getAdditionalTargetGroupHealthCheck(listener)
300+
}
214301
res.ELBListeners = append(res.ELBListeners, infrav1.Listener{
215-
Protocol: additionalListeners.Protocol,
216-
Port: additionalListeners.Port,
302+
Protocol: listener.Protocol,
303+
Port: listener.Port,
217304
TargetGroup: infrav1.TargetGroupSpec{
218-
Name: fmt.Sprintf("additional-listener-%d", time.Now().Unix()),
219-
Port: additionalListeners.Port,
220-
Protocol: additionalListeners.Protocol,
221-
VpcID: s.scope.VPC().ID,
222-
HealthCheck: &infrav1.TargetGroupHealthCheck{
223-
Protocol: aws.String(string(additionalListeners.Protocol)),
224-
Port: aws.String(strconv.FormatInt(additionalListeners.Port, 10)),
225-
},
305+
Name: targetGroupName,
306+
Port: listener.Port,
307+
Protocol: listener.Protocol,
308+
VpcID: s.scope.VPC().ID,
309+
HealthCheck: lnHealthCheck,
226310
},
227311
})
228312
}
@@ -322,11 +406,15 @@ func (s *Service) createLB(spec *infrav1.LoadBalancer, lbSpec *infrav1.AWSLoadBa
322406
for _, ln := range spec.ELBListeners {
323407
// create the target group first
324408
targetGroupInput := &elbv2.CreateTargetGroupInput{
325-
Name: aws.String(ln.TargetGroup.Name),
326-
Port: aws.Int64(ln.TargetGroup.Port),
327-
Protocol: aws.String(ln.TargetGroup.Protocol.String()),
328-
VpcId: aws.String(ln.TargetGroup.VpcID),
329-
Tags: input.Tags,
409+
Name: aws.String(ln.TargetGroup.Name),
410+
Port: aws.Int64(ln.TargetGroup.Port),
411+
Protocol: aws.String(ln.TargetGroup.Protocol.String()),
412+
VpcId: aws.String(ln.TargetGroup.VpcID),
413+
Tags: input.Tags,
414+
HealthCheckIntervalSeconds: aws.Int64(infrav1.DefaultAPIServerHealthCheckIntervalSec),
415+
HealthCheckTimeoutSeconds: aws.Int64(infrav1.DefaultAPIServerHealthCheckTimeoutSec),
416+
HealthyThresholdCount: aws.Int64(infrav1.DefaultAPIServerHealthThresholdCount),
417+
UnhealthyThresholdCount: aws.Int64(infrav1.DefaultAPIServerUnhealthThresholdCount),
330418
}
331419
if s.scope.VPC().IsIPv6Enabled() {
332420
targetGroupInput.IpAddressType = aws.String("ipv6")
@@ -335,7 +423,6 @@ func (s *Service) createLB(spec *infrav1.LoadBalancer, lbSpec *infrav1.AWSLoadBa
335423
targetGroupInput.HealthCheckEnabled = aws.Bool(true)
336424
targetGroupInput.HealthCheckProtocol = ln.TargetGroup.HealthCheck.Protocol
337425
targetGroupInput.HealthCheckPort = ln.TargetGroup.HealthCheck.Port
338-
targetGroupInput.UnhealthyThresholdCount = aws.Int64(infrav1.DefaultAPIServerUnhealthThresholdCount)
339426
if ln.TargetGroup.HealthCheck.Path != nil {
340427
targetGroupInput.HealthCheckPath = ln.TargetGroup.HealthCheck.Path
341428
}
@@ -348,6 +435,9 @@ func (s *Service) createLB(spec *infrav1.LoadBalancer, lbSpec *infrav1.AWSLoadBa
348435
if ln.TargetGroup.HealthCheck.ThresholdCount != nil {
349436
targetGroupInput.HealthyThresholdCount = ln.TargetGroup.HealthCheck.ThresholdCount
350437
}
438+
if ln.TargetGroup.HealthCheck.UnhealthyThresholdCount != nil {
439+
targetGroupInput.UnhealthyThresholdCount = ln.TargetGroup.HealthCheck.UnhealthyThresholdCount
440+
}
351441
}
352442
s.scope.Debug("creating target group", "group", targetGroupInput, "listener", ln)
353443
group, err := s.ELBV2Client.CreateTargetGroup(targetGroupInput)

0 commit comments

Comments
 (0)