Skip to content

Commit e6cef2e

Browse files
authored
Merge pull request #108 from jtherin/backport0.21
chore: backport recent changes to release-0.21
2 parents 193065b + 9a42eaf commit e6cef2e

File tree

2 files changed

+100
-6
lines changed

2 files changed

+100
-6
lines changed

docs/loadbalancer-annotations.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ spec:
1818
1919
## Load balancer properties
2020
21-
You can get a list of working annotation on in the Scaleway loadBalancer [documentation](https://developers.scaleway.com/en/products/lb/api/#post-db0bfe) annotations are:
21+
You can get a list of working annotation on in the Scaleway loadBalancer [documentation](https://developers.scaleway.com/en/products/lb/zoned_api/) annotations are:
2222
2323
Note:
2424
- If an invalid mode is passed in the annotation, the service will throw an error.
@@ -95,6 +95,10 @@ The possible values are `false`, `true` or `*` for all ports or a comma delimite
9595
### `service.beta.kubernetes.io/scw-loadbalancer-type`
9696
This is the annotation to set the load balancer offer type.
9797

98+
### `service.beta.kubernetes.io/scw-loadbalancer-timeout-client`
99+
This is the annotation to set the maximum client connection inactivity time.
100+
The default value is `10m`. The duration are go's time.Duration (ex: `1s`, `2m`, `4h`, ...).
101+
98102
### `service.beta.kubernetes.io/scw-loadbalancer-timeout-server`
99103
This is the annotation to set the maximum server connection inactivity time.
100104
The default value is `10m`. The duration are go's time.Duration (ex: `1s`, `2m`, `4h`, ...).

scaleway/loadbalancers.go

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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

157165
const 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-
//
342351
func (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+
13331363
func 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

Comments
 (0)