Skip to content

Commit 45ff9c8

Browse files
Harvey Lowndesprydie
Harvey Lowndes
authored andcommitted
Support load balancer listeners with http protocol via annotation (#239)
1 parent f3201c2 commit 45ff9c8

File tree

4 files changed

+195
-0
lines changed

4 files changed

+195
-0
lines changed

docs/load-balancer-annotations.md

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ spec:
2828
| `oci-load-balancer-subnet2` | The OCID of the second [subnet][2] of the two required subnets to attach the load balancer to. Must be in separate Availability Domains. | Value provided in config file |
2929
| `oci-load-balancer-connection-idle-timeout` | The maximum idle time, in seconds, allowed between two successive receive or two successive send operations between the client and backend servers. | `300` for TCP listeners, `60` for HTTP listeners |
3030
| `oci-load-balancer-security-list-management-mode` | Specifies the [security list mode](##security-list-management-modes) (`"All"`, `"Frontend"`,`"None"`) to configure how security lists are managed by the CCM. | `"All"`
31+
| `oci-load-balancer-backend-protocol` | Specify protocol on which the listener accepts connection requests. To get a list of valid protocols, use the [`ListProtocols`][5] operation. | `"TCP"`
3132

3233
## TLS-related
3334

@@ -51,3 +52,4 @@ Note:
5152
[2]: https://docs.us-phoenix-1.oraclecloud.com/Content/Network/Tasks/managingVCNs.htm
5253
[3]: https://kubernetes.io/docs/concepts/services-networking/ingress/#tls
5354
[4]: https://kubernetes.io/docs/concepts/services-networking/service/
55+
[5]: https://docs.cloud.oracle.com/iaas/api/#/en/loadbalancer/20170115/LoadBalancerProtocol/ListProtocols

pkg/oci/load_balancer.go

+9
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,21 @@ const (
7070
// ServiceAnnotaionLoadBalancerSecurityListManagementMode is a Service annotation for
7171
// specifying the security list managment mode ("All", "Frontend", "None") that configures how security lists are managed by the CCM
7272
ServiceAnnotaionLoadBalancerSecurityListManagementMode = "service.beta.kubernetes.io/oci-load-balancer-security-list-management-mode"
73+
74+
// ServiceAnnotationLoadBalancerBEProtocol is a Service annotation for specifying the
75+
// load balancer listener backend protocol ("TCP", "HTTP").
76+
// See: https://docs.cloud.oracle.com/iaas/Content/Balance/Concepts/balanceoverview.htm#concepts
77+
ServiceAnnotationLoadBalancerBEProtocol = "service.beta.kubernetes.io/oci-load-balancer-backend-protocol"
7378
)
7479

7580
// DefaultLoadBalancerPolicy defines the default traffic policy for load
7681
// balancers created by the CCM.
7782
const DefaultLoadBalancerPolicy = "ROUND_ROBIN"
7883

84+
// DefaultLoadBalancerBEProtocol defines the default protocol for load
85+
// balancer listeners created by the CCM.
86+
const DefaultLoadBalancerBEProtocol = "TCP"
87+
7988
const (
8089
// Fallback value if annotation on service is not set
8190
lbDefaultShape = "100Mbps"

pkg/oci/load_balancer_spec.go

+13
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package oci
1717
import (
1818
"fmt"
1919
"strconv"
20+
"strings"
2021

2122
"github.com/oracle/oci-go-sdk/common"
2223
"github.com/oracle/oci-go-sdk/loadbalancer"
@@ -304,6 +305,18 @@ func getListeners(svc *v1.Service, sslCfg *SSLConfig) (map[string]loadbalancer.L
304305
listeners := make(map[string]loadbalancer.ListenerDetails)
305306
for _, servicePort := range svc.Spec.Ports {
306307
protocol := string(servicePort.Protocol)
308+
// Annotation overrides the protocol.
309+
if p, ok := svc.Annotations[ServiceAnnotationLoadBalancerBEProtocol]; ok {
310+
// Default
311+
if p == "" {
312+
p = DefaultLoadBalancerBEProtocol
313+
}
314+
if strings.EqualFold(p, "HTTP") || strings.EqualFold(p, "TCP") {
315+
protocol = p
316+
} else {
317+
return nil, fmt.Errorf("invalid backend protocol %q requested for load balancer listener. Only 'HTTP' and 'TCP' protocols supported", p)
318+
}
319+
}
307320
port := int(servicePort.Port)
308321
sslConfiguration := getSSLConfiguration(sslCfg, port)
309322
name := getListenerName(protocol, port, sslConfiguration)

pkg/oci/load_balancer_spec_test.go

+171
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,177 @@ func TestNewLBSpecSuccess(t *testing.T) {
311311
securityListManager: newSecurityListManagerNOOP(),
312312
},
313313
},
314+
"protocol annotation set to http": {
315+
defaultSubnetOne: "one",
316+
defaultSubnetTwo: "two",
317+
service: &v1.Service{
318+
ObjectMeta: metav1.ObjectMeta{
319+
Namespace: "kube-system",
320+
Name: "testservice",
321+
UID: "test-uid",
322+
Annotations: map[string]string{
323+
ServiceAnnotationLoadBalancerBEProtocol: "HTTP",
324+
ServiceAnnotationLoadBalancerSubnet1: "annotation-one",
325+
ServiceAnnotationLoadBalancerSubnet2: "annotation-two",
326+
},
327+
},
328+
Spec: v1.ServiceSpec{
329+
SessionAffinity: v1.ServiceAffinityNone,
330+
Ports: []v1.ServicePort{
331+
v1.ServicePort{
332+
Protocol: v1.ProtocolTCP,
333+
Port: int32(80),
334+
},
335+
},
336+
},
337+
},
338+
expected: &LBSpec{
339+
Name: "test-uid",
340+
Shape: "100Mbps",
341+
Internal: false,
342+
Subnets: []string{"annotation-one", "annotation-two"},
343+
Listeners: map[string]loadbalancer.ListenerDetails{
344+
"HTTP-80": loadbalancer.ListenerDetails{
345+
DefaultBackendSetName: common.String("TCP-80"),
346+
Port: common.Int(80),
347+
Protocol: common.String("HTTP"),
348+
},
349+
},
350+
BackendSets: map[string]loadbalancer.BackendSetDetails{
351+
"TCP-80": loadbalancer.BackendSetDetails{
352+
Backends: []loadbalancer.BackendDetails{},
353+
HealthChecker: &loadbalancer.HealthCheckerDetails{
354+
Protocol: common.String("HTTP"),
355+
Port: common.Int(10256),
356+
UrlPath: common.String("/healthz"),
357+
},
358+
Policy: common.String("ROUND_ROBIN"),
359+
},
360+
},
361+
SourceCIDRs: []string{"0.0.0.0/0"},
362+
Ports: map[string]portSpec{
363+
"TCP-80": portSpec{
364+
ListenerPort: 80,
365+
HealthCheckerPort: 10256,
366+
},
367+
},
368+
securityListManager: newSecurityListManagerNOOP(),
369+
},
370+
},
371+
"protocol annotation set to tcp": {
372+
defaultSubnetOne: "one",
373+
defaultSubnetTwo: "two",
374+
service: &v1.Service{
375+
ObjectMeta: metav1.ObjectMeta{
376+
Namespace: "kube-system",
377+
Name: "testservice",
378+
UID: "test-uid",
379+
Annotations: map[string]string{
380+
ServiceAnnotationLoadBalancerBEProtocol: "TCP",
381+
ServiceAnnotationLoadBalancerSubnet1: "annotation-one",
382+
ServiceAnnotationLoadBalancerSubnet2: "annotation-two",
383+
},
384+
},
385+
Spec: v1.ServiceSpec{
386+
SessionAffinity: v1.ServiceAffinityNone,
387+
Ports: []v1.ServicePort{
388+
v1.ServicePort{
389+
Protocol: v1.ProtocolTCP,
390+
Port: int32(80),
391+
},
392+
},
393+
},
394+
},
395+
expected: &LBSpec{
396+
Name: "test-uid",
397+
Shape: "100Mbps",
398+
Internal: false,
399+
Subnets: []string{"annotation-one", "annotation-two"},
400+
Listeners: map[string]loadbalancer.ListenerDetails{
401+
"TCP-80": loadbalancer.ListenerDetails{
402+
DefaultBackendSetName: common.String("TCP-80"),
403+
Port: common.Int(80),
404+
Protocol: common.String("TCP"),
405+
},
406+
},
407+
BackendSets: map[string]loadbalancer.BackendSetDetails{
408+
"TCP-80": loadbalancer.BackendSetDetails{
409+
Backends: []loadbalancer.BackendDetails{},
410+
HealthChecker: &loadbalancer.HealthCheckerDetails{
411+
Protocol: common.String("HTTP"),
412+
Port: common.Int(10256),
413+
UrlPath: common.String("/healthz"),
414+
},
415+
Policy: common.String("ROUND_ROBIN"),
416+
},
417+
},
418+
SourceCIDRs: []string{"0.0.0.0/0"},
419+
Ports: map[string]portSpec{
420+
"TCP-80": portSpec{
421+
ListenerPort: 80,
422+
HealthCheckerPort: 10256,
423+
},
424+
},
425+
securityListManager: newSecurityListManagerNOOP(),
426+
},
427+
},
428+
"protocol annotation empty": {
429+
defaultSubnetOne: "one",
430+
defaultSubnetTwo: "two",
431+
service: &v1.Service{
432+
ObjectMeta: metav1.ObjectMeta{
433+
Namespace: "kube-system",
434+
Name: "testservice",
435+
UID: "test-uid",
436+
Annotations: map[string]string{
437+
ServiceAnnotationLoadBalancerBEProtocol: "",
438+
ServiceAnnotationLoadBalancerSubnet1: "annotation-one",
439+
ServiceAnnotationLoadBalancerSubnet2: "annotation-two",
440+
},
441+
},
442+
Spec: v1.ServiceSpec{
443+
SessionAffinity: v1.ServiceAffinityNone,
444+
Ports: []v1.ServicePort{
445+
v1.ServicePort{
446+
Protocol: v1.ProtocolTCP,
447+
Port: int32(80),
448+
},
449+
},
450+
},
451+
},
452+
expected: &LBSpec{
453+
Name: "test-uid",
454+
Shape: "100Mbps",
455+
Internal: false,
456+
Subnets: []string{"annotation-one", "annotation-two"},
457+
Listeners: map[string]loadbalancer.ListenerDetails{
458+
"TCP-80": loadbalancer.ListenerDetails{
459+
DefaultBackendSetName: common.String("TCP-80"),
460+
Port: common.Int(80),
461+
Protocol: common.String("TCP"),
462+
},
463+
},
464+
BackendSets: map[string]loadbalancer.BackendSetDetails{
465+
"TCP-80": loadbalancer.BackendSetDetails{
466+
Backends: []loadbalancer.BackendDetails{},
467+
HealthChecker: &loadbalancer.HealthCheckerDetails{
468+
Protocol: common.String("HTTP"),
469+
Port: common.Int(10256),
470+
UrlPath: common.String("/healthz"),
471+
},
472+
Policy: common.String("ROUND_ROBIN"),
473+
},
474+
},
475+
SourceCIDRs: []string{"0.0.0.0/0"},
476+
Ports: map[string]portSpec{
477+
"TCP-80": portSpec{
478+
ListenerPort: 80,
479+
HealthCheckerPort: 10256,
480+
},
481+
},
482+
securityListManager: newSecurityListManagerNOOP(),
483+
},
484+
},
314485
}
315486

316487
for name, tc := range testCases {

0 commit comments

Comments
 (0)