Skip to content

Commit 09e3e5a

Browse files
authored
[occm] ensure octavia monitor is always updated (#2382)
1 parent 3fd4310 commit 09e3e5a

File tree

2 files changed

+90
-64
lines changed

2 files changed

+90
-64
lines changed

pkg/openstack/loadbalancer.go

Lines changed: 85 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ const (
100100
// See https://nip.io
101101
defaultProxyHostnameSuffix = "nip.io"
102102
ServiceAnnotationLoadBalancerID = "loadbalancer.openstack.org/load-balancer-id"
103+
104+
// Octavia resources name formats
105+
lbFormat = "%s%s_%s_%s"
106+
listenerFormat = "listener_%d_%s"
107+
poolFormat = "pool_%d_%s"
108+
monitorFormat = "monitor_%d_%s"
103109
)
104110

105111
// LbaasV2 is a LoadBalancer implementation based on Octavia
@@ -501,20 +507,17 @@ func (lbaas *LbaasV2) createOctaviaLoadBalancer(name, clusterName string, servic
501507

502508
if !lbaas.opts.ProviderRequiresSerialAPICalls {
503509
for portIndex, port := range service.Spec.Ports {
504-
listenerCreateOpt := lbaas.buildListenerCreateOpt(port, svcConf)
505-
listenerCreateOpt.Name = cpoutil.CutString255(fmt.Sprintf("listener_%d_%s", portIndex, name))
510+
listenerCreateOpt := lbaas.buildListenerCreateOpt(port, svcConf, cpoutil.Sprintf255(listenerFormat, portIndex, name))
506511
members, newMembers, err := lbaas.buildBatchUpdateMemberOpts(port, nodes, svcConf)
507512
if err != nil {
508513
return nil, err
509514
}
510-
poolCreateOpt := lbaas.buildPoolCreateOpt(string(listenerCreateOpt.Protocol), service, svcConf)
515+
poolCreateOpt := lbaas.buildPoolCreateOpt(string(listenerCreateOpt.Protocol), service, svcConf, cpoutil.Sprintf255(poolFormat, portIndex, name))
511516
poolCreateOpt.Members = members
512517
// Pool name must be provided to create fully populated loadbalancer
513-
poolCreateOpt.Name = cpoutil.CutString255(fmt.Sprintf("pool_%d_%s", portIndex, name))
514518
var withHealthMonitor string
515519
if svcConf.enableMonitor {
516-
opts := lbaas.buildMonitorCreateOpts(svcConf, port)
517-
opts.Name = cpoutil.CutString255(fmt.Sprintf("monitor_%d_%s", port.Port, name))
520+
opts := lbaas.buildMonitorCreateOpts(svcConf, port, cpoutil.Sprintf255(monitorFormat, portIndex, name))
518521
poolCreateOpt.Monitor = &opts
519522
withHealthMonitor = " with healthmonitor"
520523
}
@@ -586,8 +589,7 @@ func (lbaas *LbaasV2) GetLoadBalancer(ctx context.Context, clusterName string, s
586589

587590
// GetLoadBalancerName returns the constructed load balancer name.
588591
func (lbaas *LbaasV2) GetLoadBalancerName(_ context.Context, clusterName string, service *corev1.Service) string {
589-
name := fmt.Sprintf("%s%s_%s_%s", servicePrefix, clusterName, service.Namespace, service.Name)
590-
return cpoutil.CutString255(name)
592+
return cpoutil.Sprintf255(lbFormat, servicePrefix, clusterName, service.Namespace, service.Name)
591593
}
592594

593595
// getLoadBalancerLegacyName returns the legacy load balancer name for backward compatibility.
@@ -1039,55 +1041,56 @@ func (lbaas *LbaasV2) ensureFloatingIP(clusterName string, service *corev1.Servi
10391041
func (lbaas *LbaasV2) ensureOctaviaHealthMonitor(lbID string, name string, pool *v2pools.Pool, port corev1.ServicePort, svcConf *serviceConfig) error {
10401042
monitorID := pool.MonitorID
10411043

1042-
if monitorID != "" {
1043-
monitor, err := openstackutil.GetHealthMonitor(lbaas.lb, monitorID)
1044-
if err != nil {
1045-
return err
1046-
}
1047-
//Recreate health monitor with correct protocol if externalTrafficPolicy was changed
1048-
createOpts := lbaas.buildMonitorCreateOpts(svcConf, port)
1049-
if createOpts.Type != monitor.Type {
1050-
klog.InfoS("Recreating health monitor for the pool", "pool", pool.ID, "oldMonitor", monitorID)
1051-
if err := openstackutil.DeleteHealthMonitor(lbaas.lb, monitorID, lbID); err != nil {
1052-
return err
1053-
}
1054-
monitorID = ""
1055-
}
1056-
if svcConf.healthMonitorDelay != monitor.Delay ||
1057-
svcConf.healthMonitorTimeout != monitor.Timeout ||
1058-
svcConf.healthMonitorMaxRetries != monitor.MaxRetries ||
1059-
svcConf.healthMonitorMaxRetriesDown != monitor.MaxRetriesDown {
1060-
updateOpts := v2monitors.UpdateOpts{
1061-
Delay: svcConf.healthMonitorDelay,
1062-
Timeout: svcConf.healthMonitorTimeout,
1063-
MaxRetries: svcConf.healthMonitorMaxRetries,
1064-
MaxRetriesDown: svcConf.healthMonitorMaxRetriesDown,
1065-
}
1066-
klog.Infof("Updating health monitor %s updateOpts %+v", monitorID, updateOpts)
1067-
if err := openstackutil.UpdateHealthMonitor(lbaas.lb, monitorID, updateOpts, lbID); err != nil {
1068-
return err
1069-
}
1044+
if monitorID == "" {
1045+
// do nothing
1046+
if !svcConf.enableMonitor {
1047+
return nil
10701048
}
1071-
}
1072-
if monitorID == "" && svcConf.enableMonitor {
1049+
1050+
// a new monitor must be created
10731051
klog.V(2).Infof("Creating monitor for pool %s", pool.ID)
1052+
createOpts := lbaas.buildMonitorCreateOpts(svcConf, port, name)
1053+
return lbaas.createOctaviaHealthMonitor(createOpts, pool.ID, lbID)
1054+
}
10741055

1075-
createOpts := lbaas.buildMonitorCreateOpts(svcConf, port)
1076-
// Populate PoolID, attribute is omitted for consumption of the createOpts for fully populated Loadbalancer
1077-
createOpts.PoolID = pool.ID
1078-
createOpts.Name = name
1079-
monitor, err := openstackutil.CreateHealthMonitor(lbaas.lb, createOpts, lbID)
1080-
if err != nil {
1081-
return err
1082-
}
1083-
monitorID = monitor.ID
1084-
klog.Infof("Health monitor %s for pool %s created.", monitorID, pool.ID)
1085-
} else if monitorID != "" && !svcConf.enableMonitor {
1056+
// an existing monitor must be deleted
1057+
if !svcConf.enableMonitor {
10861058
klog.Infof("Deleting health monitor %s for pool %s", monitorID, pool.ID)
1059+
return openstackutil.DeleteHealthMonitor(lbaas.lb, monitorID, lbID)
1060+
}
10871061

1062+
// get an existing monitor status
1063+
monitor, err := openstackutil.GetHealthMonitor(lbaas.lb, monitorID)
1064+
if err != nil {
1065+
// return err on 404 is ok, since we get monitorID dynamically from the pool
1066+
return err
1067+
}
1068+
1069+
// recreate health monitor with a new type
1070+
createOpts := lbaas.buildMonitorCreateOpts(svcConf, port, name)
1071+
if createOpts.Type != monitor.Type {
1072+
klog.InfoS("Recreating health monitor for the pool", "pool", pool.ID, "oldMonitor", monitorID)
10881073
if err := openstackutil.DeleteHealthMonitor(lbaas.lb, monitorID, lbID); err != nil {
10891074
return err
10901075
}
1076+
return lbaas.createOctaviaHealthMonitor(createOpts, pool.ID, lbID)
1077+
}
1078+
1079+
// update new monitor parameters
1080+
if name != monitor.Name ||
1081+
svcConf.healthMonitorDelay != monitor.Delay ||
1082+
svcConf.healthMonitorTimeout != monitor.Timeout ||
1083+
svcConf.healthMonitorMaxRetries != monitor.MaxRetries ||
1084+
svcConf.healthMonitorMaxRetriesDown != monitor.MaxRetriesDown {
1085+
updateOpts := v2monitors.UpdateOpts{
1086+
Name: &name,
1087+
Delay: svcConf.healthMonitorDelay,
1088+
Timeout: svcConf.healthMonitorTimeout,
1089+
MaxRetries: svcConf.healthMonitorMaxRetries,
1090+
MaxRetriesDown: svcConf.healthMonitorMaxRetriesDown,
1091+
}
1092+
klog.Infof("Updating health monitor %s updateOpts %+v", monitorID, updateOpts)
1093+
return openstackutil.UpdateHealthMonitor(lbaas.lb, monitorID, updateOpts, lbID)
10911094
}
10921095

10931096
return nil
@@ -1097,7 +1100,9 @@ func (lbaas *LbaasV2) canUseHTTPMonitor(port corev1.ServicePort) bool {
10971100
if lbaas.opts.LBProvider == "ovn" {
10981101
// ovn-octavia-provider doesn't support HTTP monitors at all. We got to avoid creating it with ovn.
10991102
return false
1100-
} else if port.Protocol == corev1.ProtocolUDP {
1103+
}
1104+
1105+
if port.Protocol == corev1.ProtocolUDP {
11011106
// Older Octavia versions or OVN provider doesn't support HTTP monitors on UDP pools. We got to check if that's the case.
11021107
return openstackutil.IsOctaviaFeatureSupported(lbaas.lb, openstackutil.OctaviaFeatureHTTPMonitorsOnUDP, lbaas.opts.LBProvider)
11031108
}
@@ -1106,8 +1111,9 @@ func (lbaas *LbaasV2) canUseHTTPMonitor(port corev1.ServicePort) bool {
11061111
}
11071112

11081113
// buildMonitorCreateOpts returns a v2monitors.CreateOpts without PoolID for consumption of both, fully popuplated Loadbalancers and Monitors.
1109-
func (lbaas *LbaasV2) buildMonitorCreateOpts(svcConf *serviceConfig, port corev1.ServicePort) v2monitors.CreateOpts {
1114+
func (lbaas *LbaasV2) buildMonitorCreateOpts(svcConf *serviceConfig, port corev1.ServicePort, name string) v2monitors.CreateOpts {
11101115
opts := v2monitors.CreateOpts{
1116+
Name: name,
11111117
Type: string(port.Protocol),
11121118
Delay: svcConf.healthMonitorDelay,
11131119
Timeout: svcConf.healthMonitorTimeout,
@@ -1126,6 +1132,18 @@ func (lbaas *LbaasV2) buildMonitorCreateOpts(svcConf *serviceConfig, port corev1
11261132
return opts
11271133
}
11281134

1135+
func (lbaas *LbaasV2) createOctaviaHealthMonitor(createOpts v2monitors.CreateOpts, poolID, lbID string) error {
1136+
// populate PoolID, attribute is omitted for consumption of the createOpts for fully populated Loadbalancer
1137+
createOpts.PoolID = poolID
1138+
monitor, err := openstackutil.CreateHealthMonitor(lbaas.lb, createOpts, lbID)
1139+
if err != nil {
1140+
return err
1141+
}
1142+
klog.Infof("Health monitor %s for pool %s created.", monitor.ID, poolID)
1143+
1144+
return nil
1145+
}
1146+
11291147
// Make sure the pool is created for the Service, nodes are added as pool members.
11301148
func (lbaas *LbaasV2) ensureOctaviaPool(lbID string, name string, listener *listeners.Listener, service *corev1.Service, port corev1.ServicePort, nodes []*corev1.Node, svcConf *serviceConfig) (*v2pools.Pool, error) {
11311149
pool, err := openstackutil.GetPoolByListener(lbaas.lb, lbID, listener.ID)
@@ -1153,9 +1171,8 @@ func (lbaas *LbaasV2) ensureOctaviaPool(lbID string, name string, listener *list
11531171
}
11541172

11551173
if pool == nil {
1156-
createOpt := lbaas.buildPoolCreateOpt(listener.Protocol, service, svcConf)
1174+
createOpt := lbaas.buildPoolCreateOpt(listener.Protocol, service, svcConf, name)
11571175
createOpt.ListenerID = listener.ID
1158-
createOpt.Name = name
11591176

11601177
klog.InfoS("Creating pool", "listenerID", listener.ID, "protocol", createOpt.Protocol)
11611178
pool, err = openstackutil.CreatePool(lbaas.lb, createOpt, lbID)
@@ -1200,7 +1217,7 @@ func (lbaas *LbaasV2) ensureOctaviaPool(lbID string, name string, listener *list
12001217
return pool, nil
12011218
}
12021219

1203-
func (lbaas *LbaasV2) buildPoolCreateOpt(listenerProtocol string, service *corev1.Service, svcConf *serviceConfig) v2pools.CreateOpts {
1220+
func (lbaas *LbaasV2) buildPoolCreateOpt(listenerProtocol string, service *corev1.Service, svcConf *serviceConfig, name string) v2pools.CreateOpts {
12041221
// By default, use the protocol of the listener
12051222
poolProto := v2pools.Protocol(listenerProtocol)
12061223
if svcConf.enableProxyProtocol {
@@ -1227,6 +1244,7 @@ func (lbaas *LbaasV2) buildPoolCreateOpt(listenerProtocol string, service *corev
12271244

12281245
lbmethod := v2pools.LBMethod(lbaas.opts.LBMethod)
12291246
return v2pools.CreateOpts{
1247+
Name: name,
12301248
Protocol: poolProto,
12311249
LBMethod: lbmethod,
12321250
Persistence: persistence,
@@ -1279,9 +1297,8 @@ func (lbaas *LbaasV2) ensureOctaviaListener(lbID string, name string, curListene
12791297
Port: int(port.Port),
12801298
}]
12811299
if !isPresent {
1282-
listenerCreateOpt := lbaas.buildListenerCreateOpt(port, svcConf)
1300+
listenerCreateOpt := lbaas.buildListenerCreateOpt(port, svcConf, name)
12831301
listenerCreateOpt.LoadbalancerID = lbID
1284-
listenerCreateOpt.Name = name
12851302

12861303
klog.V(2).Infof("Creating listener for port %d using protocol %s", int(port.Port), listenerCreateOpt.Protocol)
12871304

@@ -1366,11 +1383,10 @@ func (lbaas *LbaasV2) ensureOctaviaListener(lbID string, name string, curListene
13661383
}
13671384

13681385
// buildListenerCreateOpt returns listeners.CreateOpts for a specific Service port and configuration
1369-
func (lbaas *LbaasV2) buildListenerCreateOpt(port corev1.ServicePort, svcConf *serviceConfig) listeners.CreateOpts {
1370-
listenerProtocol := listeners.Protocol(port.Protocol)
1371-
1386+
func (lbaas *LbaasV2) buildListenerCreateOpt(port corev1.ServicePort, svcConf *serviceConfig, name string) listeners.CreateOpts {
13721387
listenerCreateOpt := listeners.CreateOpts{
1373-
Protocol: listenerProtocol,
1388+
Name: name,
1389+
Protocol: listeners.Protocol(port.Protocol),
13741390
ProtocolPort: int(port.Port),
13751391
ConnLimit: &svcConf.connLimit,
13761392
}
@@ -1970,17 +1986,17 @@ func (lbaas *LbaasV2) ensureOctaviaLoadBalancer(ctx context.Context, clusterName
19701986
}
19711987

19721988
for portIndex, port := range service.Spec.Ports {
1973-
listener, err := lbaas.ensureOctaviaListener(loadbalancer.ID, cpoutil.CutString255(fmt.Sprintf("listener_%d_%s", portIndex, lbName)), curListenerMapping, port, svcConf, service)
1989+
listener, err := lbaas.ensureOctaviaListener(loadbalancer.ID, cpoutil.Sprintf255(listenerFormat, portIndex, lbName), curListenerMapping, port, svcConf, service)
19741990
if err != nil {
19751991
return nil, err
19761992
}
19771993

1978-
pool, err := lbaas.ensureOctaviaPool(loadbalancer.ID, cpoutil.CutString255(fmt.Sprintf("pool_%d_%s", portIndex, lbName)), listener, service, port, nodes, svcConf)
1994+
pool, err := lbaas.ensureOctaviaPool(loadbalancer.ID, cpoutil.Sprintf255(poolFormat, portIndex, lbName), listener, service, port, nodes, svcConf)
19791995
if err != nil {
19801996
return nil, err
19811997
}
19821998

1983-
if err := lbaas.ensureOctaviaHealthMonitor(loadbalancer.ID, cpoutil.CutString255(fmt.Sprintf("monitor_%d_%s", portIndex, lbName)), pool, port, svcConf); err != nil {
1999+
if err := lbaas.ensureOctaviaHealthMonitor(loadbalancer.ID, cpoutil.Sprintf255(monitorFormat, portIndex, lbName), pool, port, svcConf); err != nil {
19842000
return nil, err
19852001
}
19862002

@@ -2138,7 +2154,12 @@ func (lbaas *LbaasV2) updateOctaviaLoadBalancer(ctx context.Context, clusterName
21382154
return fmt.Errorf("loadbalancer %s does not contain required listener for port %d and protocol %s", loadbalancer.ID, port.Port, port.Protocol)
21392155
}
21402156

2141-
_, err := lbaas.ensureOctaviaPool(loadbalancer.ID, cpoutil.CutString255(fmt.Sprintf("pool_%d_%s", portIndex, loadbalancer.Name)), &listener, service, port, nodes, svcConf)
2157+
pool, err := lbaas.ensureOctaviaPool(loadbalancer.ID, cpoutil.Sprintf255(poolFormat, portIndex, loadbalancer.Name), &listener, service, port, nodes, svcConf)
2158+
if err != nil {
2159+
return err
2160+
}
2161+
2162+
err = lbaas.ensureOctaviaHealthMonitor(loadbalancer.ID, cpoutil.Sprintf255(monitorFormat, portIndex, loadbalancer.Name), pool, port, svcConf)
21422163
if err != nil {
21432164
return err
21442165
}

pkg/util/util.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ func CutString255(original string) string {
2525
return ret
2626
}
2727

28+
// Sprintf255 formats according to a format specifier and returns the resulting string with a maximum length of 255 characters.
29+
func Sprintf255(format string, args ...interface{}) string {
30+
return CutString255(fmt.Sprintf(format, args...))
31+
}
32+
2833
// MyDuration is the encoding.TextUnmarshaler interface for time.Duration
2934
type MyDuration struct {
3035
time.Duration

0 commit comments

Comments
 (0)