@@ -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
151161type 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+
200224func 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