Skip to content

Commit 74e30ac

Browse files
authored
[occm] ensure octavia monitor is always updated (kubernetes#2422)
1 parent 6c1f7e7 commit 74e30ac

File tree

2 files changed

+99
-73
lines changed

2 files changed

+99
-73
lines changed

pkg/openstack/loadbalancer.go

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

106112
// LbaasV2 is a LoadBalancer implementation based on Octavia
@@ -514,20 +520,17 @@ func (lbaas *LbaasV2) createFullyPopulatedOctaviaLoadBalancer(name, clusterName
514520
}
515521

516522
for portIndex, port := range service.Spec.Ports {
517-
listenerCreateOpt := lbaas.buildListenerCreateOpt(port, svcConf)
518-
listenerCreateOpt.Name = cutString(fmt.Sprintf("listener_%d_%s", portIndex, name))
523+
listenerCreateOpt := lbaas.buildListenerCreateOpt(port, svcConf, cpoutil.Sprintf255(listenerFormat, portIndex, name))
519524
members, newMembers, err := lbaas.buildBatchUpdateMemberOpts(port, nodes, svcConf)
520525
if err != nil {
521526
return nil, err
522527
}
523-
poolCreateOpt := lbaas.buildPoolCreateOpt(string(listenerCreateOpt.Protocol), service, svcConf)
528+
poolCreateOpt := lbaas.buildPoolCreateOpt(string(listenerCreateOpt.Protocol), service, svcConf, cpoutil.Sprintf255(poolFormat, portIndex, name))
524529
poolCreateOpt.Members = members
525530
// Pool name must be provided to create fully populated loadbalancer
526-
poolCreateOpt.Name = cutString(fmt.Sprintf("pool_%d_%s", portIndex, name))
527531
var withHealthMonitor string
528532
if svcConf.enableMonitor {
529-
opts := lbaas.buildMonitorCreateOpts(svcConf, port)
530-
opts.Name = cutString(fmt.Sprintf("monitor_%d_%s", port.Port, name))
533+
opts := lbaas.buildMonitorCreateOpts(svcConf, port, cpoutil.Sprintf255(monitorFormat, portIndex, name))
531534
poolCreateOpt.Monitor = &opts
532535
withHealthMonitor = " with healthmonitor"
533536
}
@@ -598,24 +601,14 @@ func (lbaas *LbaasV2) GetLoadBalancer(ctx context.Context, clusterName string, s
598601

599602
// GetLoadBalancerName returns the constructed load balancer name.
600603
func (lbaas *LbaasV2) GetLoadBalancerName(_ context.Context, clusterName string, service *corev1.Service) string {
601-
name := fmt.Sprintf("%s%s_%s_%s", servicePrefix, clusterName, service.Namespace, service.Name)
602-
return cutString(name)
604+
return cpoutil.Sprintf255(lbFormat, servicePrefix, clusterName, service.Namespace, service.Name)
603605
}
604606

605607
// getLoadBalancerLegacyName returns the legacy load balancer name for backward compatibility.
606608
func (lbaas *LbaasV2) getLoadBalancerLegacyName(_ context.Context, _ string, service *corev1.Service) string {
607609
return cloudprovider.DefaultLoadBalancerName(service)
608610
}
609611

610-
// cutString makes sure the string length doesn't exceed 255, which is usually the maximum string length in OpenStack.
611-
func cutString(original string) string {
612-
ret := original
613-
if len(original) > 255 {
614-
ret = original[:255]
615-
}
616-
return ret
617-
}
618-
619612
// The LB needs to be configured with instance addresses on the same
620613
// subnet as the LB (aka opts.SubnetID). Currently, we're just
621614
// guessing that the node's InternalIP is the right address.
@@ -1079,55 +1072,56 @@ func (lbaas *LbaasV2) ensureFloatingIP(clusterName string, service *corev1.Servi
10791072
func (lbaas *LbaasV2) ensureOctaviaHealthMonitor(lbID string, name string, pool *v2pools.Pool, port corev1.ServicePort, svcConf *serviceConfig) error {
10801073
monitorID := pool.MonitorID
10811074

1082-
if monitorID != "" {
1083-
monitor, err := openstackutil.GetHealthMonitor(lbaas.lb, monitorID)
1084-
if err != nil {
1085-
return err
1086-
}
1087-
//Recreate health monitor with correct protocol if externalTrafficPolicy was changed
1088-
createOpts := lbaas.buildMonitorCreateOpts(svcConf, port)
1089-
if createOpts.Type != monitor.Type {
1090-
klog.InfoS("Recreating health monitor for the pool", "pool", pool.ID, "oldMonitor", monitorID)
1091-
if err := openstackutil.DeleteHealthMonitor(lbaas.lb, monitorID, lbID); err != nil {
1092-
return err
1093-
}
1094-
monitorID = ""
1095-
}
1096-
if svcConf.healthMonitorDelay != monitor.Delay ||
1097-
svcConf.healthMonitorTimeout != monitor.Timeout ||
1098-
svcConf.healthMonitorMaxRetries != monitor.MaxRetries ||
1099-
svcConf.healthMonitorMaxRetriesDown != monitor.MaxRetriesDown {
1100-
updateOpts := v2monitors.UpdateOpts{
1101-
Delay: svcConf.healthMonitorDelay,
1102-
Timeout: svcConf.healthMonitorTimeout,
1103-
MaxRetries: svcConf.healthMonitorMaxRetries,
1104-
MaxRetriesDown: svcConf.healthMonitorMaxRetriesDown,
1105-
}
1106-
klog.Infof("Updating health monitor %s updateOpts %+v", monitorID, updateOpts)
1107-
if err := openstackutil.UpdateHealthMonitor(lbaas.lb, monitorID, updateOpts); err != nil {
1108-
return err
1109-
}
1075+
if monitorID == "" {
1076+
// do nothing
1077+
if !svcConf.enableMonitor {
1078+
return nil
11101079
}
1111-
}
1112-
if monitorID == "" && svcConf.enableMonitor {
1080+
1081+
// a new monitor must be created
11131082
klog.V(2).Infof("Creating monitor for pool %s", pool.ID)
1083+
createOpts := lbaas.buildMonitorCreateOpts(svcConf, port, name)
1084+
return lbaas.createOctaviaHealthMonitor(createOpts, pool.ID, lbID)
1085+
}
11141086

1115-
createOpts := lbaas.buildMonitorCreateOpts(svcConf, port)
1116-
// Populate PoolID, attribute is omitted for consumption of the createOpts for fully populated Loadbalancer
1117-
createOpts.PoolID = pool.ID
1118-
createOpts.Name = name
1119-
monitor, err := openstackutil.CreateHealthMonitor(lbaas.lb, createOpts, lbID)
1120-
if err != nil {
1121-
return err
1122-
}
1123-
monitorID = monitor.ID
1124-
klog.Infof("Health monitor %s for pool %s created.", monitorID, pool.ID)
1125-
} else if monitorID != "" && !svcConf.enableMonitor {
1087+
// an existing monitor must be deleted
1088+
if !svcConf.enableMonitor {
11261089
klog.Infof("Deleting health monitor %s for pool %s", monitorID, pool.ID)
1090+
return openstackutil.DeleteHealthMonitor(lbaas.lb, monitorID, lbID)
1091+
}
1092+
1093+
// get an existing monitor status
1094+
monitor, err := openstackutil.GetHealthMonitor(lbaas.lb, monitorID)
1095+
if err != nil {
1096+
// return err on 404 is ok, since we get monitorID dynamically from the pool
1097+
return err
1098+
}
11271099

1100+
// recreate health monitor with a new type
1101+
createOpts := lbaas.buildMonitorCreateOpts(svcConf, port, name)
1102+
if createOpts.Type != monitor.Type {
1103+
klog.InfoS("Recreating health monitor for the pool", "pool", pool.ID, "oldMonitor", monitorID)
11281104
if err := openstackutil.DeleteHealthMonitor(lbaas.lb, monitorID, lbID); err != nil {
11291105
return err
11301106
}
1107+
return lbaas.createOctaviaHealthMonitor(createOpts, pool.ID, lbID)
1108+
}
1109+
1110+
// update new monitor parameters
1111+
if name != monitor.Name ||
1112+
svcConf.healthMonitorDelay != monitor.Delay ||
1113+
svcConf.healthMonitorTimeout != monitor.Timeout ||
1114+
svcConf.healthMonitorMaxRetries != monitor.MaxRetries ||
1115+
svcConf.healthMonitorMaxRetriesDown != monitor.MaxRetriesDown {
1116+
updateOpts := v2monitors.UpdateOpts{
1117+
Name: &name,
1118+
Delay: svcConf.healthMonitorDelay,
1119+
Timeout: svcConf.healthMonitorTimeout,
1120+
MaxRetries: svcConf.healthMonitorMaxRetries,
1121+
MaxRetriesDown: svcConf.healthMonitorMaxRetriesDown,
1122+
}
1123+
klog.Infof("Updating health monitor %s updateOpts %+v", monitorID, updateOpts)
1124+
return openstackutil.UpdateHealthMonitor(lbaas.lb, monitorID, updateOpts)
11311125
}
11321126

11331127
return nil
@@ -1137,7 +1131,9 @@ func (lbaas *LbaasV2) canUseHTTPMonitor(port corev1.ServicePort) bool {
11371131
if lbaas.opts.LBProvider == "ovn" {
11381132
// ovn-octavia-provider doesn't support HTTP monitors at all. We got to avoid creating it with ovn.
11391133
return false
1140-
} else if port.Protocol == corev1.ProtocolUDP {
1134+
}
1135+
1136+
if port.Protocol == corev1.ProtocolUDP {
11411137
// Older Octavia versions or OVN provider doesn't support HTTP monitors on UDP pools. We got to check if that's the case.
11421138
return openstackutil.IsOctaviaFeatureSupported(lbaas.lb, openstackutil.OctaviaFeatureHTTPMonitorsOnUDP, lbaas.opts.LBProvider)
11431139
}
@@ -1146,8 +1142,9 @@ func (lbaas *LbaasV2) canUseHTTPMonitor(port corev1.ServicePort) bool {
11461142
}
11471143

11481144
// buildMonitorCreateOpts returns a v2monitors.CreateOpts without PoolID for consumption of both, fully popuplated Loadbalancers and Monitors.
1149-
func (lbaas *LbaasV2) buildMonitorCreateOpts(svcConf *serviceConfig, port corev1.ServicePort) v2monitors.CreateOpts {
1145+
func (lbaas *LbaasV2) buildMonitorCreateOpts(svcConf *serviceConfig, port corev1.ServicePort, name string) v2monitors.CreateOpts {
11501146
opts := v2monitors.CreateOpts{
1147+
Name: name,
11511148
Type: string(port.Protocol),
11521149
Delay: svcConf.healthMonitorDelay,
11531150
Timeout: svcConf.healthMonitorTimeout,
@@ -1166,6 +1163,18 @@ func (lbaas *LbaasV2) buildMonitorCreateOpts(svcConf *serviceConfig, port corev1
11661163
return opts
11671164
}
11681165

1166+
func (lbaas *LbaasV2) createOctaviaHealthMonitor(createOpts v2monitors.CreateOpts, poolID, lbID string) error {
1167+
// populate PoolID, attribute is omitted for consumption of the createOpts for fully populated Loadbalancer
1168+
createOpts.PoolID = poolID
1169+
monitor, err := openstackutil.CreateHealthMonitor(lbaas.lb, createOpts, lbID)
1170+
if err != nil {
1171+
return err
1172+
}
1173+
klog.Infof("Health monitor %s for pool %s created.", monitor.ID, poolID)
1174+
1175+
return nil
1176+
}
1177+
11691178
// Make sure the pool is created for the Service, nodes are added as pool members.
11701179
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) {
11711180
pool, err := openstackutil.GetPoolByListener(lbaas.lb, lbID, listener.ID)
@@ -1193,9 +1202,8 @@ func (lbaas *LbaasV2) ensureOctaviaPool(lbID string, name string, listener *list
11931202
}
11941203

11951204
if pool == nil {
1196-
createOpt := lbaas.buildPoolCreateOpt(listener.Protocol, service, svcConf)
1205+
createOpt := lbaas.buildPoolCreateOpt(listener.Protocol, service, svcConf, name)
11971206
createOpt.ListenerID = listener.ID
1198-
createOpt.Name = name
11991207

12001208
klog.InfoS("Creating pool", "listenerID", listener.ID, "protocol", createOpt.Protocol)
12011209
pool, err = openstackutil.CreatePool(lbaas.lb, createOpt, lbID)
@@ -1230,7 +1238,7 @@ func (lbaas *LbaasV2) ensureOctaviaPool(lbID string, name string, listener *list
12301238
return pool, nil
12311239
}
12321240

1233-
func (lbaas *LbaasV2) buildPoolCreateOpt(listenerProtocol string, service *corev1.Service, svcConf *serviceConfig) v2pools.CreateOpts {
1241+
func (lbaas *LbaasV2) buildPoolCreateOpt(listenerProtocol string, service *corev1.Service, svcConf *serviceConfig, name string) v2pools.CreateOpts {
12341242
// By default, use the protocol of the listener
12351243
poolProto := v2pools.Protocol(listenerProtocol)
12361244
if svcConf.enableProxyProtocol {
@@ -1257,6 +1265,7 @@ func (lbaas *LbaasV2) buildPoolCreateOpt(listenerProtocol string, service *corev
12571265

12581266
lbmethod := v2pools.LBMethod(lbaas.opts.LBMethod)
12591267
return v2pools.CreateOpts{
1268+
Name: name,
12601269
Protocol: poolProto,
12611270
LBMethod: lbmethod,
12621271
Persistence: persistence,
@@ -1309,9 +1318,8 @@ func (lbaas *LbaasV2) ensureOctaviaListener(lbID string, name string, curListene
13091318
Port: int(port.Port),
13101319
}]
13111320
if !isPresent {
1312-
listenerCreateOpt := lbaas.buildListenerCreateOpt(port, svcConf)
1321+
listenerCreateOpt := lbaas.buildListenerCreateOpt(port, svcConf, name)
13131322
listenerCreateOpt.LoadbalancerID = lbID
1314-
listenerCreateOpt.Name = name
13151323

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

@@ -1396,11 +1404,10 @@ func (lbaas *LbaasV2) ensureOctaviaListener(lbID string, name string, curListene
13961404
}
13971405

13981406
// buildListenerCreateOpt returns listeners.CreateOpts for a specific Service port and configuration
1399-
func (lbaas *LbaasV2) buildListenerCreateOpt(port corev1.ServicePort, svcConf *serviceConfig) listeners.CreateOpts {
1400-
listenerProtocol := listeners.Protocol(port.Protocol)
1401-
1407+
func (lbaas *LbaasV2) buildListenerCreateOpt(port corev1.ServicePort, svcConf *serviceConfig, name string) listeners.CreateOpts {
14021408
listenerCreateOpt := listeners.CreateOpts{
1403-
Protocol: listenerProtocol,
1409+
Name: name,
1410+
Protocol: listeners.Protocol(port.Protocol),
14041411
ProtocolPort: int(port.Port),
14051412
ConnLimit: &svcConf.connLimit,
14061413
}
@@ -1999,17 +2006,17 @@ func (lbaas *LbaasV2) ensureOctaviaLoadBalancer(ctx context.Context, clusterName
19992006
}
20002007

20012008
for portIndex, port := range service.Spec.Ports {
2002-
listener, err := lbaas.ensureOctaviaListener(loadbalancer.ID, cutString(fmt.Sprintf("listener_%d_%s", portIndex, lbName)), curListenerMapping, port, svcConf, service)
2009+
listener, err := lbaas.ensureOctaviaListener(loadbalancer.ID, cpoutil.Sprintf255(listenerFormat, portIndex, lbName), curListenerMapping, port, svcConf, service)
20032010
if err != nil {
20042011
return nil, err
20052012
}
20062013

2007-
pool, err := lbaas.ensureOctaviaPool(loadbalancer.ID, cutString(fmt.Sprintf("pool_%d_%s", portIndex, lbName)), listener, service, port, nodes, svcConf)
2014+
pool, err := lbaas.ensureOctaviaPool(loadbalancer.ID, cpoutil.Sprintf255(poolFormat, portIndex, lbName), listener, service, port, nodes, svcConf)
20082015
if err != nil {
20092016
return nil, err
20102017
}
20112018

2012-
if err := lbaas.ensureOctaviaHealthMonitor(loadbalancer.ID, cutString(fmt.Sprintf("monitor_%d_%s", portIndex, lbName)), pool, port, svcConf); err != nil {
2019+
if err := lbaas.ensureOctaviaHealthMonitor(loadbalancer.ID, cpoutil.Sprintf255(monitorFormat, portIndex, lbName), pool, port, svcConf); err != nil {
20132020
return nil, err
20142021
}
20152022

@@ -2199,7 +2206,12 @@ func (lbaas *LbaasV2) updateOctaviaLoadBalancer(ctx context.Context, clusterName
21992206
return fmt.Errorf("loadbalancer %s does not contain required listener for port %d and protocol %s", loadbalancer.ID, port.Port, port.Protocol)
22002207
}
22012208

2202-
_, err := lbaas.ensureOctaviaPool(loadbalancer.ID, cutString(fmt.Sprintf("pool_%d_%s", portIndex, loadbalancer.Name)), &listener, service, port, nodes, svcConf)
2209+
pool, err := lbaas.ensureOctaviaPool(loadbalancer.ID, cpoutil.Sprintf255(poolFormat, portIndex, loadbalancer.Name), &listener, service, port, nodes, svcConf)
2210+
if err != nil {
2211+
return err
2212+
}
2213+
2214+
err = lbaas.ensureOctaviaHealthMonitor(loadbalancer.ID, cpoutil.Sprintf255(monitorFormat, portIndex, loadbalancer.Name), pool, port, svcConf)
22032215
if err != nil {
22042216
return err
22052217
}

pkg/util/util.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,20 @@ import (
1616
"k8s.io/klog/v2"
1717
)
1818

19+
// CutString255 makes sure the string length doesn't exceed 255, which is usually the maximum string length in OpenStack.
20+
func CutString255(original string) string {
21+
ret := original
22+
if len(original) > 255 {
23+
ret = original[:255]
24+
}
25+
return ret
26+
}
27+
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+
1933
// MyDuration is the encoding.TextUnmarshaler interface for time.Duration
2034
type MyDuration struct {
2135
time.Duration

0 commit comments

Comments
 (0)