@@ -115,6 +115,10 @@ const (
115115 // serviceAnnotationLoadBalancerZone is the zone to create the load balancer
116116 serviceAnnotationLoadBalancerZone = "service.beta.kubernetes.io/scw-loadbalancer-zone"
117117
118+ // serviceAnnotationLoadBalancerTimeoutClient is the maximum client connection inactivity time
119+ // The default value is "10m". The duration are go's time.Duration (ex: "1s", "2m", "4h", ...)
120+ serviceAnnotationLoadBalancerTimeoutClient = "service.beta.kubernetes.io/scw-loadbalancer-timeout-client"
121+
118122 // serviceAnnotationLoadBalancerTimeoutServer is the maximum server connection inactivity time
119123 // The default value is "10m". The duration are go's time.Duration (ex: "1s", "2m", "4h", ...)
120124 serviceAnnotationLoadBalancerTimeoutServer = "service.beta.kubernetes.io/scw-loadbalancer-timeout-server"
@@ -144,14 +148,18 @@ const (
144148 // (for instance "80,443")
145149 serviceAnnotationLoadBalancerProtocolHTTP = "service.beta.kubernetes.io/scw-loadbalancer-protocol-http"
146150
147- // serviceAnnotationLoadBalancerCertificateIDs is the annotation to choose the the certificate IDS to associate
151+ // serviceAnnotationLoadBalancerCertificateIDs is the annotation to choose the certificate IDS to associate
148152 // with this LoadBalancer.
149153 // The possible format are:
150154 // "<certificate-id>": will use this certificate for all frontends
151155 // "<certificate-id>,<certificate-id>" will use these certificates for all frontends
152156 // "<port1>:<certificate1-id>,<certificate2-id>;<port2>,<port3>:<certificate3-id>" will use certificate 1 and 2 for frontend with port port1
153157 // and certificate3 for frotend with port port2 and port3
154158 serviceAnnotationLoadBalancerCertificateIDs = "service.beta.kubernetes.io/scw-loadbalancer-certificate-ids"
159+
160+ // serviceAnnotationLoadBalancerTargetNodeLabels is the annotation to target nodes with specific label(s)
161+ // Expected format: "Key1=Val1,Key2=Val2"
162+ serviceAnnotationLoadBalancerTargetNodeLabels = "service.beta.kubernetes.io/scw-loadbalancer-target-node-labels"
155163)
156164
157165const MaxEntriesPerACL = 60
@@ -272,6 +280,8 @@ func (l *loadbalancers) EnsureLoadBalancer(ctx context.Context, clusterName stri
272280 return nil , LoadBalancerNotReady
273281 }
274282
283+ nodes = filterNodes (service , nodes )
284+
275285 err = l .updateLoadBalancer (ctx , lb , service , nodes )
276286 if err != nil {
277287 klog .Errorf ("error updating loadbalancer for service %s: %v" , service .Name , err )
@@ -338,7 +348,6 @@ func (l *loadbalancers) EnsureLoadBalancerDeleted(ctx context.Context, clusterNa
338348 return l .deleteLoadBalancer (ctx , lb , service )
339349}
340350
341- //
342351func (l * loadbalancers ) deleteLoadBalancer (ctx context.Context , lb * scwlb.LB , service * v1.Service ) error {
343352 // remove loadbalancer annotation
344353 if err := l .unannotateAndPatch (service ); err != nil {
@@ -710,6 +719,13 @@ func (l *loadbalancers) updateLoadBalancer(ctx context.Context, loadbalancer *sc
710719 if err != nil {
711720 return fmt .Errorf ("error getting certificate IDs for loadbalancer %s: %v" , loadbalancer .ID , err )
712721 }
722+
723+ timeoutClient , err := getTimeoutClient (service )
724+ if err != nil {
725+ return fmt .Errorf ("error getting %s annotation for loadbalancer %s: %v" ,
726+ serviceAnnotationLoadBalancerTimeoutClient , loadbalancer .ID , err )
727+ }
728+
713729 // if the frontend exists for the port, update it
714730 if frontend , ok := portFrontends [port .Port ]; ok {
715731 _ , err := l .api .UpdateFrontend (& scwlb.ZonedAPIUpdateFrontendRequest {
@@ -718,7 +734,7 @@ func (l *loadbalancers) updateLoadBalancer(ctx context.Context, loadbalancer *sc
718734 Name : frontend .Name ,
719735 InboundPort : frontend .InboundPort ,
720736 BackendID : portBackends [port .NodePort ].ID ,
721- TimeoutClient : frontend . TimeoutClient ,
737+ TimeoutClient : & timeoutClient ,
722738 CertificateIDs : scw .StringsPtr (certificateIDs ),
723739 })
724740
@@ -729,14 +745,13 @@ func (l *loadbalancers) updateLoadBalancer(ctx context.Context, loadbalancer *sc
729745
730746 frontendID = frontend .ID
731747 } else { // if the frontend for this port does not exist, create it
732- timeoutClient := time .Minute * 10
733748 resp , err := l .api .CreateFrontend (& scwlb.ZonedAPICreateFrontendRequest {
734749 Zone : loadbalancer .Zone ,
735750 LBID : loadbalancer .ID ,
736751 Name : fmt .Sprintf ("%s_tcp_%d" , string (service .UID ), port .Port ),
737752 InboundPort : port .Port ,
738753 BackendID : portBackends [port .NodePort ].ID ,
739- TimeoutClient : & timeoutClient , // TODO use annotation?
754+ TimeoutClient : & timeoutClient ,
740755 CertificateIDs : scw .StringsPtr (certificateIDs ),
741756 })
742757
@@ -1330,6 +1345,21 @@ func getProxyProtocol(service *v1.Service, nodePort int32) (scwlb.ProxyProtocol,
13301345 return getSendProxyV2 (service , nodePort )
13311346}
13321347
1348+ func getTimeoutClient (service * v1.Service ) (time.Duration , error ) {
1349+ timeoutClient , ok := service .Annotations [serviceAnnotationLoadBalancerTimeoutClient ]
1350+ if ! ok {
1351+ return time .ParseDuration ("10m" )
1352+ }
1353+
1354+ timeoutClientDuration , err := time .ParseDuration (timeoutClient )
1355+ if err != nil {
1356+ klog .Errorf ("invalid value for annotation %s" , serviceAnnotationLoadBalancerTimeoutClient )
1357+ return time .Duration (0 ), errLoadBalancerInvalidAnnotation
1358+ }
1359+
1360+ return timeoutClientDuration , nil
1361+ }
1362+
13331363func getTimeoutServer (service * v1.Service ) (time.Duration , error ) {
13341364 timeoutServer , ok := service .Annotations [serviceAnnotationLoadBalancerTimeoutServer ]
13351365 if ! ok {
@@ -1712,3 +1742,63 @@ func getHTTPSHealthCheck(service *v1.Service, nodePort int32) (*scwlb.HealthChec
17121742 URI : uri ,
17131743 }, nil
17141744}
1745+
1746+ // Original version: https://github.com/kubernetes/legacy-cloud-providers/blob/1aa918bf227e52af6f8feb3fa065dabff251a0a3/aws/aws_loadbalancer.go#L117
1747+ func getKeyValueFromAnnotation (annotation string ) map [string ]string {
1748+ additionalTags := make (map [string ]string )
1749+ additionalTagsList := strings .TrimSpace (annotation )
1750+
1751+ // Break up list of "Key1=Val,Key2=Val2"
1752+ tagList := strings .Split (additionalTagsList , "," )
1753+
1754+ // Break up "Key=Val"
1755+ for _ , tagSet := range tagList {
1756+ tag := strings .Split (strings .TrimSpace (tagSet ), "=" )
1757+
1758+ // Accept "Key=val" or "Key=" or just "Key"
1759+ if len (tag ) >= 2 && len (tag [0 ]) != 0 {
1760+ // There is a key and a value, so save it
1761+ additionalTags [tag [0 ]] = tag [1 ]
1762+ } else if len (tag ) == 1 && len (tag [0 ]) != 0 {
1763+ // Just "Key"
1764+ additionalTags [tag [0 ]] = ""
1765+ }
1766+ }
1767+
1768+ return additionalTags
1769+ }
1770+
1771+ // Original version: https://github.com/kubernetes/legacy-cloud-providers/blob/1aa918bf227e52af6f8feb3fa065dabff251a0a3/aws/aws_loadbalancer.go#L1631
1772+ func filterNodes (service * v1.Service , nodes []* v1.Node ) []* v1.Node {
1773+ nodeLabels , ok := service .Annotations [serviceAnnotationLoadBalancerTargetNodeLabels ]
1774+ if ! ok {
1775+ return nodes
1776+ }
1777+
1778+ targetNodeLabels := getKeyValueFromAnnotation (nodeLabels )
1779+
1780+ if len (targetNodeLabels ) == 0 {
1781+ return nodes
1782+ }
1783+
1784+ targetNodes := make ([]* v1.Node , 0 , len (nodes ))
1785+
1786+ for _ , node := range nodes {
1787+ if node .Labels != nil && len (node .Labels ) > 0 {
1788+ allFiltersMatch := true
1789+
1790+ for targetLabelKey , targetLabelValue := range targetNodeLabels {
1791+ if nodeLabelValue , ok := node .Labels [targetLabelKey ]; ! ok || (nodeLabelValue != targetLabelValue && targetLabelValue != "" ) {
1792+ allFiltersMatch = false
1793+ break
1794+ }
1795+ }
1796+
1797+ if allFiltersMatch {
1798+ targetNodes = append (targetNodes , node )
1799+ }
1800+ }
1801+ }
1802+
1803+ return targetNodes
1804+ }
0 commit comments