From e7d91e4cf12b0e0f044e9c812bee194066081a48 Mon Sep 17 00:00:00 2001 From: Ranielly Ferreira Date: Mon, 14 Jul 2025 17:01:08 -0300 Subject: [PATCH 1/2] Add support for load balancer HTTP2 listener protocol --- .../providers/oci/load_balancer_spec.go | 38 ++++++++---- .../providers/oci/load_balancer_spec_test.go | 58 +++++++++++++++++++ 2 files changed, 86 insertions(+), 10 deletions(-) diff --git a/pkg/cloudprovider/providers/oci/load_balancer_spec.go b/pkg/cloudprovider/providers/oci/load_balancer_spec.go index 89134355b..100379cdd 100644 --- a/pkg/cloudprovider/providers/oci/load_balancer_spec.go +++ b/pkg/cloudprovider/providers/oci/load_balancer_spec.go @@ -146,6 +146,10 @@ const ( // loadbalancer traffic policy("ROUND_ROBIN", "LEAST_CONNECTION", "IP_HASH") ServiceAnnotationLoadBalancerPolicy = "oci.oraclecloud.com/loadbalancer-policy" + // ServiceAnnotationLoadBalancerProtocol is a service annotation for specifying + // the load balancer listener protocol ("HTTP", "HTTP2", "TCP", "GRPC"). + ServiceAnnotationLoadBalancerProtocol = "oci.oraclecloud.com/oci-load-balancer-protocol" + // ServiceAnnotationLoadBalancerInitialDefinedTagsOverride is a service annotation for specifying // defined tags on the LB ServiceAnnotationLoadBalancerInitialDefinedTagsOverride = "oci.oraclecloud.com/initial-defined-tags-override" @@ -1092,19 +1096,32 @@ func getListenersOciLoadBalancer(svc *v1.Service, sslCfg *SSLConfig) (map[string listeners := make(map[string]client.GenericListener) for _, servicePort := range svc.Spec.Ports { - protocol := string(servicePort.Protocol) - // Annotation overrides the protocol. + backendProtocol := string(servicePort.Protocol) + // Backend protocol annotation overrides the protocol. if p, ok := svc.Annotations[ServiceAnnotationLoadBalancerBEProtocol]; ok { // Default if p == "" { p = DefaultLoadBalancerBEProtocol } if strings.EqualFold(p, "HTTP") || strings.EqualFold(p, "TCP") || strings.EqualFold(p, "GRPC") { - protocol = p + backendProtocol = p } else { return nil, fmt.Errorf("invalid backend protocol %q requested for load balancer listener. Only 'HTTP', 'TCP' and 'GRPC' protocols supported", p) } } + + // Listener protocol - starts with backend protocol but can be overridden + listenerProtocol := backendProtocol + if p, ok := svc.Annotations[ServiceAnnotationLoadBalancerProtocol]; ok { + if p != "" { + if strings.EqualFold(p, "HTTP") || strings.EqualFold(p, "HTTP2") || strings.EqualFold(p, "TCP") || strings.EqualFold(p, "GRPC") { + listenerProtocol = p + } else { + return nil, fmt.Errorf("invalid listener protocol %q requested for load balancer listener. Only 'HTTP', 'HTTP2', 'TCP' and 'GRPC' protocols supported", p) + } + } + } + port := int(servicePort.Port) var secretName string @@ -1118,8 +1135,8 @@ func getListenersOciLoadBalancer(svc *v1.Service, sslCfg *SSLConfig) (map[string return nil, err } } - if strings.EqualFold(protocol, "GRPC") { - protocol = ProtocolGrpc + if strings.EqualFold(listenerProtocol, "GRPC") { + listenerProtocol = ProtocolGrpc if sslConfiguration == nil { return nil, fmt.Errorf("SSL configuration cannot be empty for GRPC protocol") } @@ -1127,12 +1144,12 @@ func getListenersOciLoadBalancer(svc *v1.Service, sslCfg *SSLConfig) (map[string sslConfiguration.CipherSuiteName = common.String(DefaultCipherSuiteForGRPC) } } - name := getListenerName(protocol, port) + name := getListenerName(listenerProtocol, port) listener := client.GenericListener{ Name: &name, DefaultBackendSetName: common.String(getBackendSetName(string(servicePort.Protocol), int(servicePort.Port))), - Protocol: &protocol, + Protocol: &listenerProtocol, Port: &port, RuleSetNames: rs, SslConfiguration: sslConfiguration, @@ -1145,10 +1162,11 @@ func getListenersOciLoadBalancer(svc *v1.Service, sslCfg *SSLConfig) (map[string if proxyProtocolVersion != nil && connectionIdleTimeout == nil { // At that point LB only supports HTTP and TCP defaultIdleTimeoutPerProtocol := map[string]int64{ - "HTTP": lbConnectionIdleTimeoutHTTP, - "TCP": lbConnectionIdleTimeoutTCP, + "HTTP": lbConnectionIdleTimeoutHTTP, + "HTTP2": lbConnectionIdleTimeoutHTTP, // HTTP2 uses same timeout as HTTP + "TCP": lbConnectionIdleTimeoutTCP, } - actualConnectionIdleTimeout = common.Int64(defaultIdleTimeoutPerProtocol[strings.ToUpper(protocol)]) + actualConnectionIdleTimeout = common.Int64(defaultIdleTimeoutPerProtocol[strings.ToUpper(listenerProtocol)]) } if actualConnectionIdleTimeout != nil { diff --git a/pkg/cloudprovider/providers/oci/load_balancer_spec_test.go b/pkg/cloudprovider/providers/oci/load_balancer_spec_test.go index e093df26d..2d8fbddc2 100644 --- a/pkg/cloudprovider/providers/oci/load_balancer_spec_test.go +++ b/pkg/cloudprovider/providers/oci/load_balancer_spec_test.go @@ -8943,6 +8943,64 @@ func Test_getListeners(t *testing.T) { }, }, }, + { + name: "HTTP2 listener protocol with HTTP backend protocol", + service: &v1.Service{ + Spec: v1.ServiceSpec{ + Ports: []v1.ServicePort{ + { + Protocol: v1.ProtocolTCP, + Port: int32(443), + }, + }, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + ServiceAnnotationLoadBalancerBEProtocol: "HTTP", + ServiceAnnotationLoadBalancerProtocol: "HTTP2", + }, + }, + }, + listenerBackendIpVersion: []string{IPv4}, + sslConfig: nil, + want: map[string]client.GenericListener{ + "HTTP2-443": { + Name: common.String("HTTP2-443"), + Port: common.Int(443), + Protocol: common.String("HTTP2"), + DefaultBackendSetName: common.String("TCP-443"), + }, + }, + }, + { + name: "HTTP listener protocol with HTTP backend protocol", + service: &v1.Service{ + Spec: v1.ServiceSpec{ + Ports: []v1.ServicePort{ + { + Protocol: v1.ProtocolTCP, + Port: int32(80), + }, + }, + }, + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + ServiceAnnotationLoadBalancerBEProtocol: "HTTP", + ServiceAnnotationLoadBalancerProtocol: "HTTP", + }, + }, + }, + listenerBackendIpVersion: []string{IPv4}, + sslConfig: nil, + want: map[string]client.GenericListener{ + "HTTP-80": { + Name: common.String("HTTP-80"), + Port: common.Int(80), + Protocol: common.String("HTTP"), + DefaultBackendSetName: common.String("TCP-80"), + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 609b2236322e30a9a4af37ad5ad1fe5d9d37e827 Mon Sep 17 00:00:00 2001 From: Ranielly Ferreira Date: Wed, 16 Jul 2025 12:47:18 -0300 Subject: [PATCH 2/2] Refactor listener naming to standardize HTTP and HTTP2 prefixes --- pkg/cloudprovider/providers/oci/load_balancer_spec.go | 2 +- pkg/cloudprovider/providers/oci/load_balancer_spec_test.go | 4 ++-- pkg/cloudprovider/providers/oci/load_balancer_util.go | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/cloudprovider/providers/oci/load_balancer_spec.go b/pkg/cloudprovider/providers/oci/load_balancer_spec.go index 100379cdd..9cf7b7d2a 100644 --- a/pkg/cloudprovider/providers/oci/load_balancer_spec.go +++ b/pkg/cloudprovider/providers/oci/load_balancer_spec.go @@ -1163,7 +1163,7 @@ func getListenersOciLoadBalancer(svc *v1.Service, sslCfg *SSLConfig) (map[string // At that point LB only supports HTTP and TCP defaultIdleTimeoutPerProtocol := map[string]int64{ "HTTP": lbConnectionIdleTimeoutHTTP, - "HTTP2": lbConnectionIdleTimeoutHTTP, // HTTP2 uses same timeout as HTTP + "HTTP2": lbConnectionIdleTimeoutHTTP, // HTTP/2 uses same timeout as HTTP "TCP": lbConnectionIdleTimeoutTCP, } actualConnectionIdleTimeout = common.Int64(defaultIdleTimeoutPerProtocol[strings.ToUpper(listenerProtocol)]) diff --git a/pkg/cloudprovider/providers/oci/load_balancer_spec_test.go b/pkg/cloudprovider/providers/oci/load_balancer_spec_test.go index 2d8fbddc2..6c4e99b8f 100644 --- a/pkg/cloudprovider/providers/oci/load_balancer_spec_test.go +++ b/pkg/cloudprovider/providers/oci/load_balancer_spec_test.go @@ -8964,8 +8964,8 @@ func Test_getListeners(t *testing.T) { listenerBackendIpVersion: []string{IPv4}, sslConfig: nil, want: map[string]client.GenericListener{ - "HTTP2-443": { - Name: common.String("HTTP2-443"), + "HTTP-443": { + Name: common.String("HTTP-443"), Port: common.Int(443), Protocol: common.String("HTTP2"), DefaultBackendSetName: common.String("TCP-443"), diff --git a/pkg/cloudprovider/providers/oci/load_balancer_util.go b/pkg/cloudprovider/providers/oci/load_balancer_util.go index c78d92665..2d8b03966 100644 --- a/pkg/cloudprovider/providers/oci/load_balancer_util.go +++ b/pkg/cloudprovider/providers/oci/load_balancer_util.go @@ -679,6 +679,10 @@ func getSanitizedName(name string) string { } func getListenerName(protocol string, port int) string { + // For HTTP and HTTP/2 protocols, always use "HTTP" prefix + if strings.EqualFold(protocol, "HTTP") || strings.EqualFold(protocol, "HTTP2") { + return fmt.Sprintf("HTTP-%d", port) + } return fmt.Sprintf("%s-%d", protocol, port) }