Skip to content

Commit b9d7293

Browse files
author
linusliu
committed
feat: add support for load balancer, listener, and pool tags annotations
1 parent ce4af77 commit b9d7293

1 file changed

Lines changed: 114 additions & 3 deletions

File tree

pkg/openstack/loadbalancer.go

Lines changed: 114 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ const (
9696
defaultProxyHostnameSuffix = "nip.io"
9797
ServiceAnnotationLoadBalancerID = "loadbalancer.openstack.org/load-balancer-id"
9898

99+
// ServiceAnnotationLoadBalancerTags The lb tags annotation is used to set tags on the loadbalancer resource itself(support json list).
100+
ServiceAnnotationLoadBalancerTags = "loadbalancer.openstack.org/load-balancer-tags"
101+
// ServiceAnnotationListenerTags The listener tags annotation is used to set tags on the loadbalancer listener resources(support json list).
102+
ServiceAnnotationListenerTags = "loadbalancer.openstack.org/listener-tags"
103+
// ServiceAnnotationPoolTags The pool tags annotation is used to set tags on the loadbalancer pool resources(support json list).
104+
ServiceAnnotationPoolTags = "loadbalancer.openstack.org/pool-tags"
105+
99106
// Octavia resources name formats
100107
servicePrefix = "kube_service_"
101108
lbFormat = "%s%s_%s_%s"
@@ -146,6 +153,9 @@ type serviceConfig struct {
146153
healthMonitorMaxRetries int
147154
healthMonitorMaxRetriesDown int
148155
preferredIPFamily corev1.IPFamily // preferred (the first) IP family indicated in service's `spec.ipFamilies`
156+
lbTags string
157+
listenerTags string
158+
poolTags string
149159
}
150160

151161
type listenerKey struct {
@@ -197,6 +207,20 @@ func getLoadbalancerByName(ctx context.Context, client *gophercloud.ServiceClien
197207
return &validLBs[0], nil
198208
}
199209

210+
// mergeTags merges existedTags and desiredTags, returns true if all desiredTags are in existedTags.
211+
func mergeTags(existedTags []string, desiredTags []string) (bool, []string) {
212+
if len(existedTags) == 0 || existedTags == nil {
213+
return false, desiredTags
214+
}
215+
desiredTagsSet := sets.NewString(desiredTags...)
216+
tagSet := sets.NewString(existedTags...)
217+
if tagSet.HasAll(desiredTags...) {
218+
return true, nil
219+
} else {
220+
return false, tagSet.Union(desiredTagsSet).List()
221+
}
222+
}
223+
200224
func popListener(existingListeners []listeners.Listener, id string) []listeners.Listener {
201225
newListeners := []listeners.Listener{}
202226
for _, existingListener := range existingListeners {
@@ -235,7 +259,13 @@ func (lbaas *LbaasV2) createOctaviaLoadBalancer(ctx context.Context, name, clust
235259
}
236260

237261
if svcConf.supportLBTags {
238-
createOpts.Tags = []string{svcConf.lbName}
262+
var desiredTags []string
263+
if len(svcConf.lbTags) == 0 {
264+
klog.V(4).Infof("No load balancer tags found from service annotation key: %s", ServiceAnnotationLoadBalancerTags)
265+
} else if err := json.Unmarshal([]byte(svcConf.lbTags), &desiredTags); err != nil {
266+
klog.Warningf("unmarshal service annotation load balancer tags from key: %s, error: %s", ServiceAnnotationLoadBalancerTags, err)
267+
}
268+
createOpts.Tags = append([]string{svcConf.lbName}, desiredTags...)
239269
}
240270

241271
if svcConf.flavorID != "" {
@@ -923,16 +953,43 @@ func (lbaas *LbaasV2) ensureOctaviaPool(ctx context.Context, lbID string, name s
923953
}
924954
}
925955

956+
var desiredTags []string
957+
if len(svcConf.poolTags) == 0 {
958+
klog.V(4).Infof("No pools tags found from service annotation key: %s", ServiceAnnotationPoolTags)
959+
} else if err := json.Unmarshal([]byte(svcConf.poolTags), &desiredTags); err != nil {
960+
klog.Warningf("unmarshal service annotation pools tags from key: %s, error: %s", ServiceAnnotationPoolTags, err)
961+
}
962+
926963
if pool == nil {
927964
createOpt := lbaas.buildPoolCreateOpt(listener.Protocol, service, svcConf, name)
928965
createOpt.ListenerID = listener.ID
929-
966+
if svcConf.supportLBTags {
967+
createOpt.Tags = desiredTags
968+
}
930969
klog.InfoS("Creating pool", "listenerID", listener.ID, "protocol", createOpt.Protocol)
970+
klog.V(4).Infof("Pool create options: %+v", createOpt)
931971
pool, err = openstackutil.CreatePool(ctx, lbaas.lb, createOpt, lbID)
932972
if err != nil {
933973
return nil, err
934974
}
935975
klog.V(2).Infof("Pool %s created for listener %s", pool.ID, listener.ID)
976+
} else {
977+
if svcConf.supportLBTags {
978+
// Update tags if needed
979+
if len(desiredTags) > 0 {
980+
klog.V(4).Infof("Desired pools tags: %+v from service annotation key: %s", desiredTags, ServiceAnnotationPoolTags)
981+
if ok, tags := mergeTags(pool.Tags, desiredTags); !ok {
982+
klog.V(4).Infof("Will update pools' tags, current pools tags: %+v, desired tags: %+v", pool.Tags, tags)
983+
updateOpts := v2pools.UpdateOpts{
984+
Tags: &tags,
985+
}
986+
klog.InfoS("Updating pool tags", "poolID", pool.ID, "listenerID", listener.ID, "lbID", lbID, "tags", desiredTags)
987+
if err := openstackutil.UpdatePool(ctx, lbaas.lb, lbID, pool.ID, updateOpts); err != nil {
988+
klog.Warningf("Error updating LB pool tags: %v", err)
989+
}
990+
}
991+
}
992+
}
936993
}
937994

938995
if lbaas.opts.ProviderRequiresSerialAPICalls {
@@ -1105,6 +1162,23 @@ func (lbaas *LbaasV2) ensureOctaviaListener(ctx context.Context, lbID string, na
11051162
newTags = append(newTags, svcConf.lbName)
11061163
updateOpts.Tags = &newTags
11071164
listenerChanged = true
1165+
} else {
1166+
// Get desired tags from Service annotations
1167+
var desiredTags []string
1168+
if len(svcConf.listenerTags) == 0 {
1169+
klog.V(4).Infof("No listeners tags found from service annotation key: %s", ServiceAnnotationListenerTags)
1170+
} else if err := json.Unmarshal([]byte(svcConf.listenerTags), &desiredTags); err != nil {
1171+
klog.Warningf("unmarshal service annotation listeners tags from key: %s, error: %s", ServiceAnnotationListenerTags, err)
1172+
}
1173+
// Ensure listeners tags match the desired tags from Service annotations
1174+
if len(desiredTags) > 0 {
1175+
klog.Infof("Desired listener tags: %+v from service annotation key: %s", desiredTags, ServiceAnnotationListenerTags)
1176+
if ok, tags := mergeTags(listener.Tags, desiredTags); !ok {
1177+
klog.V(4).Infof("Will update listeners' tags, current listeners tags: %+v, desired tags: %+v", listener.Tags, tags)
1178+
updateOpts.Tags = &tags
1179+
listenerChanged = true
1180+
}
1181+
}
11081182
}
11091183
}
11101184

@@ -1177,7 +1251,20 @@ func (lbaas *LbaasV2) buildListenerCreateOpt(ctx context.Context, port corev1.Se
11771251
}
11781252

11791253
if svcConf.supportLBTags {
1180-
listenerCreateOpt.Tags = []string{svcConf.lbName}
1254+
// Get desired tags from Service annotations
1255+
var desiredTags []string
1256+
if len(svcConf.listenerTags) == 0 {
1257+
klog.V(4).Infof("No listeners tags found from service annotation key: %s", ServiceAnnotationListenerTags)
1258+
} else if err := json.Unmarshal([]byte(svcConf.listenerTags), &desiredTags); err != nil {
1259+
klog.Warningf("unmarshal service annotation listeners tags from key: %s, error: %s", ServiceAnnotationListenerTags, err)
1260+
}
1261+
if len(desiredTags) > 0 {
1262+
klog.V(4).Infof("Desired listener tags: %+v from service annotation key: %s", desiredTags, ServiceAnnotationListenerTags)
1263+
if ok, tags := mergeTags([]string{svcConf.lbName}, desiredTags); !ok {
1264+
klog.V(4).Infof("Will update listeners' tags, current listeners tags: %s, desired tags: %+v", svcConf.lbName, tags)
1265+
listenerCreateOpt.Tags = tags
1266+
}
1267+
}
11811268
}
11821269

11831270
if openstackutil.IsOctaviaFeatureSupported(ctx, lbaas.lb, openstackutil.OctaviaFeatureTimeout, lbaas.opts.LBProvider) {
@@ -1365,6 +1452,11 @@ func (lbaas *LbaasV2) checkService(ctx context.Context, service *corev1.Service,
13651452
return fmt.Errorf("no service ports provided")
13661453
}
13671454

1455+
annotations := service.GetAnnotations()
1456+
svcConf.lbTags = annotations[ServiceAnnotationLoadBalancerTags]
1457+
svcConf.listenerTags = annotations[ServiceAnnotationListenerTags]
1458+
svcConf.poolTags = annotations[ServiceAnnotationPoolTags]
1459+
13681460
if len(service.Spec.IPFamilies) > 0 {
13691461
// Since OCCM does not support multiple load-balancers per service yet,
13701462
// the first IP family will determine the IP family of the load-balancer
@@ -1756,6 +1848,25 @@ func (lbaas *LbaasV2) ensureOctaviaLoadBalancer(ctx context.Context, clusterName
17561848
// Make sure LB ID will be saved at this point.
17571849
lbaas.updateServiceAnnotation(service, ServiceAnnotationLoadBalancerID, loadbalancer.ID)
17581850

1851+
if svcConf.supportLBTags {
1852+
var desiredTags []string
1853+
if len(svcConf.lbTags) == 0 {
1854+
klog.V(4).Infof("No load balancer tags found from service annotation key: %s", ServiceAnnotationLoadBalancerTags)
1855+
} else if err := json.Unmarshal([]byte(svcConf.lbTags), &desiredTags); err != nil {
1856+
klog.Warningf("unmarshal service annotation load balancer tags from key: %s, error: %s", ServiceAnnotationLoadBalancerTags, err)
1857+
}
1858+
// add the service annotation tags to load balancer tags if the tags don't match
1859+
if len(desiredTags) > 0 {
1860+
klog.V(4).Infof("Desired load balancer tags: %v from service annotation: %v", desiredTags, ServiceAnnotationLoadBalancerTags)
1861+
if ok, tags := mergeTags(loadbalancer.Tags, desiredTags); !ok {
1862+
klog.Infof("Will update load balancer's tags, current load balancer tags: %+v, desired tags: %+v", loadbalancer.Tags, tags)
1863+
if err := openstackutil.UpdateLoadBalancerTags(ctx, lbaas.lb, loadbalancer.ID, tags); err != nil {
1864+
klog.Warningf("failed to update load balancer %s tags: %v", loadbalancer.ID, err)
1865+
}
1866+
}
1867+
}
1868+
}
1869+
17591870
if loadbalancer.ProvisioningStatus != activeStatus {
17601871
return nil, fmt.Errorf("load balancer %s is not ACTIVE, current provisioning status: %s", loadbalancer.ID, loadbalancer.ProvisioningStatus)
17611872
}

0 commit comments

Comments
 (0)