Skip to content

Commit bec79c9

Browse files
committed
feat: add http protocol and certificate IDs for LB
Signed-off-by: Patrik Cyvoct <patrik@ptrk.io>
1 parent 0ed4a1d commit bec79c9

File tree

1 file changed

+95
-12
lines changed

1 file changed

+95
-12
lines changed

scaleway/loadbalancers.go

Lines changed: 95 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,20 @@ const (
127127
// serviceAnnotationLoadBalancerUseHostname is the annotation that force the use of the LB hostname instead of the public IP.
128128
// This is useful when it it needed to not bypass the LoadBalacer for traffic coming from the cluster
129129
serviceAnnotationLoadBalancerUseHostname = "service.beta.kubernetes.io/scw-loadbalancer-use-hostname"
130+
131+
// serviceAnnotationLoadBalancerProtocolHTTP is the annotation to set the forward protocol of the LB to HTTP
132+
// The possible values are "false", "true" or "*" for all ports or a comma delimited list of the service port
133+
// (for instance "80,443")
134+
serviceAnnotationLoadBalancerProtocolHTTP = "service.beta.kubernetes.io/scw-loadbalancer-protocol-http"
135+
136+
// serviceAnnotationLoadBalancerCertificateIDs is the annotation to choose the the certificate IDS to associate
137+
// with this LoadBalancer.
138+
// The possible format are:
139+
// "<certificate-id>": will use this certificate for all frontends
140+
// "<certificate-id>,<certificate-id>" will use these certificates for all frontends
141+
// "<port1>:<certificate1-id>,<certificate2-id>;<port2>,<port3>:<certificate3-id>" will use certificate 1 and 2 for frontend with port port1
142+
// and certificate3 for frotend with port port2 and port3
143+
serviceAnnotationLoadBalancerCertificateIDs = "service.beta.kubernetes.io/scw-loadbalancer-certificate-ids"
130144
)
131145

132146
type loadbalancers struct {
@@ -650,14 +664,19 @@ func (l *loadbalancers) updateLoadBalancer(ctx context.Context, loadbalancer *sc
650664

651665
for _, port := range service.Spec.Ports {
652666
var frontendID string
667+
certificateIDs, err := getCertificateIDs(service, port.Port)
668+
if err != nil {
669+
return fmt.Errorf("error getting certificate IDs for loadbalancer %s: %v", loadbalancer.ID, err)
670+
}
653671
// if the frontend exists for the port, update it
654672
if frontend, ok := portFrontends[port.Port]; ok {
655673
_, err := l.api.UpdateFrontend(&scwlb.UpdateFrontendRequest{
656-
FrontendID: frontend.ID,
657-
Name: frontend.Name,
658-
InboundPort: frontend.InboundPort,
659-
BackendID: portBackends[port.NodePort].ID,
660-
TimeoutClient: frontend.TimeoutClient,
674+
FrontendID: frontend.ID,
675+
Name: frontend.Name,
676+
InboundPort: frontend.InboundPort,
677+
BackendID: portBackends[port.NodePort].ID,
678+
TimeoutClient: frontend.TimeoutClient,
679+
CertificateIDs: scw.StringsPtr(certificateIDs),
661680
})
662681

663682
if err != nil {
@@ -669,11 +688,12 @@ func (l *loadbalancers) updateLoadBalancer(ctx context.Context, loadbalancer *sc
669688
} else { // if the frontend for this port does not exist, create it
670689
timeoutClient := time.Minute * 10
671690
resp, err := l.api.CreateFrontend(&scwlb.CreateFrontendRequest{
672-
LBID: loadbalancer.ID,
673-
Name: fmt.Sprintf("%s_tcp_%d", string(service.UID), port.Port),
674-
InboundPort: port.Port,
675-
BackendID: portBackends[port.NodePort].ID,
676-
TimeoutClient: &timeoutClient, // TODO use annotation?
691+
LBID: loadbalancer.ID,
692+
Name: fmt.Sprintf("%s_tcp_%d", string(service.UID), port.Port),
693+
InboundPort: port.Port,
694+
BackendID: portBackends[port.NodePort].ID,
695+
TimeoutClient: &timeoutClient, // TODO use annotation?
696+
CertificateIDs: scw.StringsPtr(certificateIDs),
677697
})
678698

679699
if err != nil {
@@ -768,10 +788,15 @@ func (l *loadbalancers) updateLoadBalancer(ctx context.Context, loadbalancer *sc
768788
}
769789

770790
func (l *loadbalancers) makeUpdateBackendRequest(backend *scwlb.Backend, service *v1.Service, nodes []*v1.Node) (*scwlb.UpdateBackendRequest, error) {
791+
protocol, err := getForwardProtocol(service, backend.ForwardPort)
792+
if err != nil {
793+
return nil, err
794+
}
795+
771796
request := &scwlb.UpdateBackendRequest{
772797
BackendID: backend.ID,
773798
Name: backend.Name,
774-
ForwardProtocol: scwlb.ProtocolTCP,
799+
ForwardProtocol: protocol,
775800
}
776801

777802
forwardPortAlgorithm, err := getForwardPortAlgorithm(service)
@@ -906,6 +931,10 @@ func (l *loadbalancers) makeUpdateHealthCheckRequest(backend *scwlb.Backend, nod
906931
}
907932

908933
func (l *loadbalancers) makeCreateBackendRequest(loadbalancer *scwlb.LB, nodePort int32, service *v1.Service, nodes []*v1.Node) (*scwlb.CreateBackendRequest, error) {
934+
protocol, err := getForwardProtocol(service, nodePort)
935+
if err != nil {
936+
return nil, err
937+
}
909938
var serverIPs []string
910939
if getForceInternalIP(service) {
911940
serverIPs = extractNodesInternalIps(nodes)
@@ -916,7 +945,7 @@ func (l *loadbalancers) makeCreateBackendRequest(loadbalancer *scwlb.LB, nodePor
916945
LBID: loadbalancer.ID,
917946
Name: fmt.Sprintf("%s_tcp_%d", string(service.UID), nodePort),
918947
ServerIP: serverIPs,
919-
ForwardProtocol: scwlb.ProtocolTCP,
948+
ForwardProtocol: protocol,
920949
ForwardPort: nodePort,
921950
}
922951

@@ -1402,3 +1431,57 @@ func getUseHostname(service *v1.Service) bool {
14021431
}
14031432
return value
14041433
}
1434+
1435+
func getForwardProtocol(service *v1.Service, nodePort int32) (scwlb.Protocol, error) {
1436+
httpProtocol := service.Annotations[serviceAnnotationLoadBalancerProtocolHTTP]
1437+
1438+
var svcPort int32 = -1
1439+
for _, p := range service.Spec.Ports {
1440+
if p.NodePort == nodePort {
1441+
svcPort = p.Port
1442+
}
1443+
}
1444+
if svcPort == -1 {
1445+
klog.Errorf("no valid port found")
1446+
return "", errLoadBalancerInvalidAnnotation
1447+
}
1448+
1449+
isHTTP, err := isPortInRange(httpProtocol, svcPort)
1450+
if err != nil {
1451+
klog.Errorf("unable to check if port %d is in range %s", svcPort, httpProtocol)
1452+
return "", err
1453+
}
1454+
1455+
if isHTTP {
1456+
return scwlb.ProtocolHTTP, nil
1457+
}
1458+
1459+
return scwlb.ProtocolTCP, nil
1460+
}
1461+
1462+
func getCertificateIDs(service *v1.Service, port int32) ([]string, error) {
1463+
certificates := service.Annotations[serviceAnnotationLoadBalancerCertificateIDs]
1464+
if certificates == "" {
1465+
return nil, nil
1466+
}
1467+
1468+
ids := []string{}
1469+
1470+
for _, perPortCertificate := range strings.Split(certificates, ";") {
1471+
split := strings.Split(perPortCertificate, ":")
1472+
if len(split) == 1 {
1473+
ids = append(ids, strings.Split(split[0], ",")...)
1474+
continue
1475+
}
1476+
inRange, err := isPortInRange(split[0], port)
1477+
if err != nil {
1478+
klog.Errorf("unable to check if port %d is in range %s", port, split[0])
1479+
return nil, err
1480+
}
1481+
if inRange {
1482+
ids = append(ids, strings.Split(split[1], ",")...)
1483+
}
1484+
}
1485+
1486+
return ids, nil
1487+
}

0 commit comments

Comments
 (0)