Skip to content

Commit a8b6bae

Browse files
amoreylyda
authored andcommitted
Add support for TCP_UDP to NLB TargetGroups and Listeners
Previously, aws-load-balancer-controller ignored extra overlapping ServicePorts defined in the Kubernetes Service spec if the external port numbers were the same even if the protocols were different (e.g. TCP:53, UDP:53). This behavior prevented users from exposing services that support TCP and UDP on the same external load balancer port number. This patch solves the problem by detecting when a user defines multiple ServicePorts for the same external load balancer port number but using TCP and UDP protocols separately. In such situations, a TCP_UDP TargetGroup and Listener are created and SecurityGroup rules are updated accordingly. If more than two ServicePorts are defined, only the first two mergeable ServicePorts are used. Otherwise, the first ServicePort is used. Note: rebasing errors would be my fault -- Kevin Lyda Signed-off-by: Kevin Lyda <[email protected]>
1 parent a070b16 commit a8b6bae

7 files changed

+736
-145
lines changed

apis/elbv2/v1beta1/targetgroupbinding_types.go

+3
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ const (
8787

8888
// NetworkingProtocolUDP is the UDP protocol.
8989
NetworkingProtocolUDP NetworkingProtocol = "UDP"
90+
91+
// NetworkingProtocolTCP_UDP is the TCP_UDP protocol.
92+
NetworkingProtocolTCP_UDP NetworkingProtocol = "TCP_UDP"
9093
)
9194

9295
// NetworkingPort defines the port and protocol for networking rules.

config/webhook/manifests.yaml

+121-121
Original file line numberDiff line numberDiff line change
@@ -2,130 +2,130 @@
22
apiVersion: admissionregistration.k8s.io/v1
33
kind: MutatingWebhookConfiguration
44
metadata:
5-
name: webhook
5+
name: mutating-webhook-configuration
66
webhooks:
7-
- admissionReviewVersions:
8-
- v1beta1
9-
clientConfig:
10-
service:
11-
name: webhook-service
12-
namespace: system
13-
path: /mutate-v1-pod
14-
failurePolicy: Fail
15-
name: mpod.elbv2.k8s.aws
16-
rules:
17-
- apiGroups:
18-
- ""
19-
apiVersions:
20-
- v1
21-
operations:
22-
- CREATE
23-
resources:
24-
- pods
25-
sideEffects: None
26-
- admissionReviewVersions:
27-
- v1beta1
28-
clientConfig:
29-
service:
30-
name: webhook-service
31-
namespace: system
32-
path: /mutate-v1-service
33-
failurePolicy: Fail
34-
name: mservice.elbv2.k8s.aws
35-
rules:
36-
- apiGroups:
37-
- ""
38-
apiVersions:
39-
- v1
40-
operations:
41-
- CREATE
42-
resources:
43-
- services
44-
sideEffects: None
45-
- admissionReviewVersions:
46-
- v1beta1
47-
clientConfig:
48-
service:
49-
name: webhook-service
50-
namespace: system
51-
path: /mutate-elbv2-k8s-aws-v1beta1-targetgroupbinding
52-
failurePolicy: Fail
53-
name: mtargetgroupbinding.elbv2.k8s.aws
54-
rules:
55-
- apiGroups:
56-
- elbv2.k8s.aws
57-
apiVersions:
58-
- v1beta1
59-
operations:
60-
- CREATE
61-
- UPDATE
62-
resources:
63-
- targetgroupbindings
64-
sideEffects: None
7+
- admissionReviewVersions:
8+
- v1beta1
9+
clientConfig:
10+
service:
11+
name: webhook-service
12+
namespace: system
13+
path: /mutate-v1-pod
14+
failurePolicy: Fail
15+
name: mpod.elbv2.k8s.aws
16+
rules:
17+
- apiGroups:
18+
- ""
19+
apiVersions:
20+
- v1
21+
operations:
22+
- CREATE
23+
resources:
24+
- pods
25+
sideEffects: None
26+
- admissionReviewVersions:
27+
- v1beta1
28+
clientConfig:
29+
service:
30+
name: webhook-service
31+
namespace: system
32+
path: /mutate-v1-service
33+
failurePolicy: Fail
34+
name: mservice.elbv2.k8s.aws
35+
rules:
36+
- apiGroups:
37+
- ""
38+
apiVersions:
39+
- v1
40+
operations:
41+
- CREATE
42+
resources:
43+
- services
44+
sideEffects: None
45+
- admissionReviewVersions:
46+
- v1beta1
47+
clientConfig:
48+
service:
49+
name: webhook-service
50+
namespace: system
51+
path: /mutate-elbv2-k8s-aws-v1beta1-targetgroupbinding
52+
failurePolicy: Fail
53+
name: mtargetgroupbinding.elbv2.k8s.aws
54+
rules:
55+
- apiGroups:
56+
- elbv2.k8s.aws
57+
apiVersions:
58+
- v1beta1
59+
operations:
60+
- CREATE
61+
- UPDATE
62+
resources:
63+
- targetgroupbindings
64+
sideEffects: None
6565
---
6666
apiVersion: admissionregistration.k8s.io/v1
6767
kind: ValidatingWebhookConfiguration
6868
metadata:
69-
name: webhook
69+
name: validating-webhook-configuration
7070
webhooks:
71-
- admissionReviewVersions:
72-
- v1beta1
73-
clientConfig:
74-
service:
75-
name: webhook-service
76-
namespace: system
77-
path: /validate-elbv2-k8s-aws-v1beta1-ingressclassparams
78-
failurePolicy: Fail
79-
name: vingressclassparams.elbv2.k8s.aws
80-
rules:
81-
- apiGroups:
82-
- elbv2.k8s.aws
83-
apiVersions:
84-
- v1beta1
85-
operations:
86-
- CREATE
87-
- UPDATE
88-
resources:
89-
- ingressclassparams
90-
sideEffects: None
91-
- admissionReviewVersions:
92-
- v1beta1
93-
clientConfig:
94-
service:
95-
name: webhook-service
96-
namespace: system
97-
path: /validate-elbv2-k8s-aws-v1beta1-targetgroupbinding
98-
failurePolicy: Fail
99-
name: vtargetgroupbinding.elbv2.k8s.aws
100-
rules:
101-
- apiGroups:
102-
- elbv2.k8s.aws
103-
apiVersions:
104-
- v1beta1
105-
operations:
106-
- CREATE
107-
- UPDATE
108-
resources:
109-
- targetgroupbindings
110-
sideEffects: None
111-
- admissionReviewVersions:
112-
- v1beta1
113-
clientConfig:
114-
service:
115-
name: webhook-service
116-
namespace: system
117-
path: /validate-networking-v1-ingress
118-
failurePolicy: Fail
119-
matchPolicy: Equivalent
120-
name: vingress.elbv2.k8s.aws
121-
rules:
122-
- apiGroups:
123-
- networking.k8s.io
124-
apiVersions:
125-
- v1
126-
operations:
127-
- CREATE
128-
- UPDATE
129-
resources:
130-
- ingresses
131-
sideEffects: None
71+
- admissionReviewVersions:
72+
- v1beta1
73+
clientConfig:
74+
service:
75+
name: webhook-service
76+
namespace: system
77+
path: /validate-elbv2-k8s-aws-v1beta1-ingressclassparams
78+
failurePolicy: Fail
79+
name: vingressclassparams.elbv2.k8s.aws
80+
rules:
81+
- apiGroups:
82+
- elbv2.k8s.aws
83+
apiVersions:
84+
- v1beta1
85+
operations:
86+
- CREATE
87+
- UPDATE
88+
resources:
89+
- ingressclassparams
90+
sideEffects: None
91+
- admissionReviewVersions:
92+
- v1beta1
93+
clientConfig:
94+
service:
95+
name: webhook-service
96+
namespace: system
97+
path: /validate-elbv2-k8s-aws-v1beta1-targetgroupbinding
98+
failurePolicy: Fail
99+
name: vtargetgroupbinding.elbv2.k8s.aws
100+
rules:
101+
- apiGroups:
102+
- elbv2.k8s.aws
103+
apiVersions:
104+
- v1beta1
105+
operations:
106+
- CREATE
107+
- UPDATE
108+
resources:
109+
- targetgroupbindings
110+
sideEffects: None
111+
- admissionReviewVersions:
112+
- v1beta1
113+
clientConfig:
114+
service:
115+
name: webhook-service
116+
namespace: system
117+
path: /validate-networking-v1-ingress
118+
failurePolicy: Fail
119+
matchPolicy: Equivalent
120+
name: vingress.elbv2.k8s.aws
121+
rules:
122+
- apiGroups:
123+
- networking.k8s.io
124+
apiVersions:
125+
- v1
126+
operations:
127+
- CREATE
128+
- UPDATE
129+
resources:
130+
- ingresses
131+
sideEffects: None

pkg/service/model_build_listener.go

+39-5
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,29 @@ func (t *defaultModelBuildTask) buildListeners(ctx context.Context, scheme elbv2
1919
return err
2020
}
2121

22+
// execute build listener
23+
// group by listener port number
24+
portMap := make(map[int32][]corev1.ServicePort)
2225
for _, port := range t.service.Spec.Ports {
23-
_, err := t.buildListener(ctx, port, *cfg, scheme)
24-
if err != nil {
25-
return err
26+
key := port.Port
27+
if vals, exists := portMap[key]; exists {
28+
if len(vals) > 1 {
29+
port = mergeServicePortsForListener(vals)
30+
} else {
31+
port = vals[0]
32+
}
33+
_, err := t.buildListener(ctx, port, cfg, scheme)
34+
if err != nil {
35+
return err
36+
}
37+
delete(portMap, key)
2638
}
2739
}
40+
2841
return nil
2942
}
3043

31-
func (t *defaultModelBuildTask) buildListener(ctx context.Context, port corev1.ServicePort, cfg listenerConfig,
44+
func (t *defaultModelBuildTask) buildListener(ctx context.Context, port corev1.ServicePort, cfg *listenerConfig,
3245
scheme elbv2model.LoadBalancerScheme) (*elbv2model.Listener, error) {
3346
lsSpec, err := t.buildListenerSpec(ctx, port, cfg, scheme)
3447
if err != nil {
@@ -39,7 +52,7 @@ func (t *defaultModelBuildTask) buildListener(ctx context.Context, port corev1.S
3952
return ls, nil
4053
}
4154

42-
func (t *defaultModelBuildTask) buildListenerSpec(ctx context.Context, port corev1.ServicePort, cfg listenerConfig,
55+
func (t *defaultModelBuildTask) buildListenerSpec(ctx context.Context, port corev1.ServicePort, cfg *listenerConfig,
4356
scheme elbv2model.LoadBalancerScheme) (elbv2model.ListenerSpec, error) {
4457
tgProtocol := elbv2model.Protocol(port.Protocol)
4558
listenerProtocol := elbv2model.Protocol(port.Protocol)
@@ -212,3 +225,24 @@ func (t *defaultModelBuildTask) buildListenerConfig(ctx context.Context) (*liste
212225
func (t *defaultModelBuildTask) buildListenerTags(ctx context.Context) (map[string]string, error) {
213226
return t.buildAdditionalResourceTags(ctx)
214227
}
228+
229+
func mergeServicePortsForListener(ports []corev1.ServicePort) corev1.ServicePort {
230+
port0 := ports[0]
231+
mergeableProtocols := map[corev1.Protocol]bool{
232+
corev1.ProtocolTCP: true,
233+
corev1.ProtocolUDP: true,
234+
}
235+
if _, ok := mergeableProtocols[port0.Protocol]; !ok {
236+
return port0
237+
}
238+
for _, port := range ports[1:] {
239+
if _, ok := mergeableProtocols[port.Protocol]; !ok {
240+
continue
241+
}
242+
if port.NodePort == port0.NodePort && port.Protocol != port0.Protocol {
243+
port0.Protocol = corev1.Protocol("TCP_UDP")
244+
break
245+
}
246+
}
247+
return port0
248+
}

0 commit comments

Comments
 (0)