@@ -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
132146type 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
770790func (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
908933func (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