Skip to content

Support load balancer listeners with http protocol via annotation #239

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/load-balancer-annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ spec:
| `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 |
| `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 |
| `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"`
| `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"`

## TLS-related

Expand All @@ -51,3 +52,4 @@ Note:
[2]: https://docs.us-phoenix-1.oraclecloud.com/Content/Network/Tasks/managingVCNs.htm
[3]: https://kubernetes.io/docs/concepts/services-networking/ingress/#tls
[4]: https://kubernetes.io/docs/concepts/services-networking/service/
[5]: https://docs.cloud.oracle.com/iaas/api/#/en/loadbalancer/20170115/LoadBalancerProtocol/ListProtocols
9 changes: 9 additions & 0 deletions pkg/oci/load_balancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,21 @@ const (
// ServiceAnnotaionLoadBalancerSecurityListManagementMode is a Service annotation for
// specifying the security list managment mode ("All", "Frontend", "None") that configures how security lists are managed by the CCM
ServiceAnnotaionLoadBalancerSecurityListManagementMode = "service.beta.kubernetes.io/oci-load-balancer-security-list-management-mode"

// ServiceAnnotationLoadBalancerBEProtocol is a Service annotation for specifying the
// load balancer listener backend protocol ("TCP", "HTTP").
// See: https://docs.cloud.oracle.com/iaas/Content/Balance/Concepts/balanceoverview.htm#concepts
ServiceAnnotationLoadBalancerBEProtocol = "service.beta.kubernetes.io/oci-load-balancer-backend-protocol"
)

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

// DefaultLoadBalancerBEProtocol defines the default protocol for load
// balancer listeners created by the CCM.
const DefaultLoadBalancerBEProtocol = "TCP"

const (
// Fallback value if annotation on service is not set
lbDefaultShape = "100Mbps"
Expand Down
13 changes: 13 additions & 0 deletions pkg/oci/load_balancer_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package oci
import (
"fmt"
"strconv"
"strings"

"github.com/oracle/oci-go-sdk/common"
"github.com/oracle/oci-go-sdk/loadbalancer"
Expand Down Expand Up @@ -304,6 +305,18 @@ func getListeners(svc *v1.Service, sslCfg *SSLConfig) (map[string]loadbalancer.L
listeners := make(map[string]loadbalancer.ListenerDetails)
for _, servicePort := range svc.Spec.Ports {
protocol := string(servicePort.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") {
protocol = p
} else {
return nil, fmt.Errorf("invalid backend protocol %q requested for load balancer listener. Only 'HTTP' and 'TCP' protocols supported", p)
}
}
port := int(servicePort.Port)
sslConfiguration := getSSLConfiguration(sslCfg, port)
name := getListenerName(protocol, port, sslConfiguration)
Expand Down
171 changes: 171 additions & 0 deletions pkg/oci/load_balancer_spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,177 @@ func TestNewLBSpecSuccess(t *testing.T) {
securityListManager: newSecurityListManagerNOOP(),
},
},
"protocol annotation set to http": {
defaultSubnetOne: "one",
defaultSubnetTwo: "two",
service: &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Namespace: "kube-system",
Name: "testservice",
UID: "test-uid",
Annotations: map[string]string{
ServiceAnnotationLoadBalancerBEProtocol: "HTTP",
ServiceAnnotationLoadBalancerSubnet1: "annotation-one",
ServiceAnnotationLoadBalancerSubnet2: "annotation-two",
},
},
Spec: v1.ServiceSpec{
SessionAffinity: v1.ServiceAffinityNone,
Ports: []v1.ServicePort{
v1.ServicePort{
Protocol: v1.ProtocolTCP,
Port: int32(80),
},
},
},
},
expected: &LBSpec{
Name: "test-uid",
Shape: "100Mbps",
Internal: false,
Subnets: []string{"annotation-one", "annotation-two"},
Listeners: map[string]loadbalancer.ListenerDetails{
"HTTP-80": loadbalancer.ListenerDetails{
DefaultBackendSetName: common.String("TCP-80"),
Port: common.Int(80),
Protocol: common.String("HTTP"),
},
},
BackendSets: map[string]loadbalancer.BackendSetDetails{
"TCP-80": loadbalancer.BackendSetDetails{
Backends: []loadbalancer.BackendDetails{},
HealthChecker: &loadbalancer.HealthCheckerDetails{
Protocol: common.String("HTTP"),
Port: common.Int(10256),
UrlPath: common.String("/healthz"),
},
Policy: common.String("ROUND_ROBIN"),
},
},
SourceCIDRs: []string{"0.0.0.0/0"},
Ports: map[string]portSpec{
"TCP-80": portSpec{
ListenerPort: 80,
HealthCheckerPort: 10256,
},
},
securityListManager: newSecurityListManagerNOOP(),
},
},
"protocol annotation set to tcp": {
defaultSubnetOne: "one",
defaultSubnetTwo: "two",
service: &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Namespace: "kube-system",
Name: "testservice",
UID: "test-uid",
Annotations: map[string]string{
ServiceAnnotationLoadBalancerBEProtocol: "TCP",
ServiceAnnotationLoadBalancerSubnet1: "annotation-one",
ServiceAnnotationLoadBalancerSubnet2: "annotation-two",
},
},
Spec: v1.ServiceSpec{
SessionAffinity: v1.ServiceAffinityNone,
Ports: []v1.ServicePort{
v1.ServicePort{
Protocol: v1.ProtocolTCP,
Port: int32(80),
},
},
},
},
expected: &LBSpec{
Name: "test-uid",
Shape: "100Mbps",
Internal: false,
Subnets: []string{"annotation-one", "annotation-two"},
Listeners: map[string]loadbalancer.ListenerDetails{
"TCP-80": loadbalancer.ListenerDetails{
DefaultBackendSetName: common.String("TCP-80"),
Port: common.Int(80),
Protocol: common.String("TCP"),
},
},
BackendSets: map[string]loadbalancer.BackendSetDetails{
"TCP-80": loadbalancer.BackendSetDetails{
Backends: []loadbalancer.BackendDetails{},
HealthChecker: &loadbalancer.HealthCheckerDetails{
Protocol: common.String("HTTP"),
Port: common.Int(10256),
UrlPath: common.String("/healthz"),
},
Policy: common.String("ROUND_ROBIN"),
},
},
SourceCIDRs: []string{"0.0.0.0/0"},
Ports: map[string]portSpec{
"TCP-80": portSpec{
ListenerPort: 80,
HealthCheckerPort: 10256,
},
},
securityListManager: newSecurityListManagerNOOP(),
},
},
"protocol annotation empty": {
defaultSubnetOne: "one",
defaultSubnetTwo: "two",
service: &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Namespace: "kube-system",
Name: "testservice",
UID: "test-uid",
Annotations: map[string]string{
ServiceAnnotationLoadBalancerBEProtocol: "",
ServiceAnnotationLoadBalancerSubnet1: "annotation-one",
ServiceAnnotationLoadBalancerSubnet2: "annotation-two",
},
},
Spec: v1.ServiceSpec{
SessionAffinity: v1.ServiceAffinityNone,
Ports: []v1.ServicePort{
v1.ServicePort{
Protocol: v1.ProtocolTCP,
Port: int32(80),
},
},
},
},
expected: &LBSpec{
Name: "test-uid",
Shape: "100Mbps",
Internal: false,
Subnets: []string{"annotation-one", "annotation-two"},
Listeners: map[string]loadbalancer.ListenerDetails{
"TCP-80": loadbalancer.ListenerDetails{
DefaultBackendSetName: common.String("TCP-80"),
Port: common.Int(80),
Protocol: common.String("TCP"),
},
},
BackendSets: map[string]loadbalancer.BackendSetDetails{
"TCP-80": loadbalancer.BackendSetDetails{
Backends: []loadbalancer.BackendDetails{},
HealthChecker: &loadbalancer.HealthCheckerDetails{
Protocol: common.String("HTTP"),
Port: common.Int(10256),
UrlPath: common.String("/healthz"),
},
Policy: common.String("ROUND_ROBIN"),
},
},
SourceCIDRs: []string{"0.0.0.0/0"},
Ports: map[string]portSpec{
"TCP-80": portSpec{
ListenerPort: 80,
HealthCheckerPort: 10256,
},
},
securityListManager: newSecurityListManagerNOOP(),
},
},
}

for name, tc := range testCases {
Expand Down