Skip to content

Commit 276c525

Browse files
committed
Fix Matrix RTC's SFU constructing an invalid Service if given too wide a nodePort range.
1 parent 7623172 commit 276c525

4 files changed

Lines changed: 97 additions & 12 deletions

File tree

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{{- /*
2-
Copyright 2024 New Vector Ltd
2+
Copyright 2024-2025 New Vector Ltd
33

44
SPDX-License-Identifier: AGPL-3.0-only
55
*/ -}}
@@ -8,29 +8,36 @@ SPDX-License-Identifier: AGPL-3.0-only
88
{{- if .enabled -}}
99
{{- with .sfu -}}
1010
{{- if and .enabled .exposedServices.rtcUdp.enabled (eq .exposedServices.rtcUdp.portType "NodePort") -}}
11+
{{- $range_start := (.exposedServices.rtcUdp.portRange.startPort | int) -}}
12+
{{- $range_end := (.exposedServices.rtcUdp.portRange.endPort | int) -}}
13+
{{- $range_size := sub $range_end $range_start -}}
14+
{{- $number_of_ranges := ((add ((div $range_size 250) | floor | int) 1) | int) -}}
15+
{{- range $port_segment := until $number_of_ranges }}
16+
---
1117
apiVersion: v1
1218
kind: Service
1319
metadata:
1420
labels:
15-
{{- include "element-io.matrix-rtc-sfu-rtc.labels" (dict "root" $ "context" .) | nindent 4 }}
16-
name: {{ $.Release.Name }}-matrix-rtc-sfu-udp-range
21+
{{- include "element-io.matrix-rtc-sfu-rtc.labels" (dict "root" $ "context" $.Values.matrixRTC.sfu) | nindent 4 }}
22+
name: {{ $.Release.Name }}-matrix-rtc-sfu-udp-range-{{ $port_segment }}
1723
namespace: {{ $.Release.Namespace }}
1824
spec:
1925
type: NodePort
2026
externalTrafficPolicy: Local
2127
ports:
22-
{{- with .exposedServices.rtcUdp.portRange }}
23-
{{- range $port := untilStep (.startPort | int) (.endPort | int) 1 }}
28+
{{- $segment_start := ((add $range_start (mul 250 $port_segment)) | int) }}
29+
{{- $segment_end := ((min (add $range_end 1) (add $segment_start 250)) | int) }}
30+
{{- range $port := untilStep $segment_start $segment_end 1 }}
2431
- name: rtc-udp-{{ $port }}
2532
port: {{ $port }}
2633
targetPort: {{ $port }}
2734
nodePort: {{ $port }}
2835
protocol: UDP
29-
{{- end }}
3036
{{- end }}
3137
selector:
3238
app.kubernetes.io/instance: "{{ $.Release.Name }}-matrix-rtc-sfu"
3339
{{- end }}
3440
{{- end }}
3541
{{- end }}
3642
{{- end }}
43+
{{- end }}

newsfragments/549.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix Matrix RTC's SFU constructing an invalid Service if given too wide a nodePort range.

tests/manifests/test_matrix_rtc.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import pytest
66
import yaml
77

8+
from .utils import template_id
9+
810

911
@pytest.mark.parametrize("values_file", ["matrix-rtc-minimal-values.yaml"])
1012
@pytest.mark.asyncio_cooperative
@@ -39,3 +41,68 @@ async def test_external_ip_underrides(values, make_templates):
3941
break
4042
else:
4143
raise RuntimeError("Could not find config.yaml")
44+
45+
46+
async def get_sfu_udp_port_range_services(start_port, end_point, values, make_templates):
47+
values["matrixRTC"]["sfu"]["exposedServices"]["rtcUdp"]["portRange"]["startPort"] = start_port
48+
values["matrixRTC"]["sfu"]["exposedServices"]["rtcUdp"]["portRange"]["endPort"] = end_point
49+
50+
services = []
51+
for template in await make_templates(values):
52+
if template["kind"] == "Service" and "matrix-rtc-sfu-udp-range" in template["metadata"]["name"]:
53+
services.append(template)
54+
return services
55+
56+
57+
def assert_sharded_udp_range_ports(start_port, end_port, service):
58+
id = template_id(service)
59+
service_ports = service["spec"]["ports"]
60+
assert len(service_ports) == (end_port - start_port + 1), f"{id} doesn't have the correct number of ports in it"
61+
62+
assert service_ports[0]["port"] == start_port, f"{id} doesn't start with port {start_port}"
63+
expected_port = start_port
64+
for index, port in enumerate(service_ports):
65+
assert port["port"] == expected_port, (
66+
f"{id}.port[{index}]['port'] isn't {expected_port} ({start_port} to {end_port})"
67+
)
68+
assert port["targetPort"] == expected_port, (
69+
f"{id}.port[{index}]['targetPort'] isn't {expected_port} ({start_port} to {end_port})"
70+
)
71+
assert port["nodePort"] == expected_port, (
72+
f"{id}.port[{index}]['nodePort'] isn't {expected_port} ({start_port} to {end_port})"
73+
)
74+
assert port["name"] == f"rtc-udp-{expected_port}", (
75+
f"{id}.port[{index}]['name'] isn't rtc-udp-{expected_port} ({start_port} to {end_port})"
76+
)
77+
expected_port += 1
78+
assert port["port"] == end_port, f"{id} doesn't end with port {end_port}"
79+
80+
81+
@pytest.mark.parametrize("values_file", ["matrix-rtc-exposed-services-values.yaml"])
82+
@pytest.mark.asyncio_cooperative
83+
async def test_udp_range_services_are_sharded(values, make_templates):
84+
start_port = 32000
85+
86+
services = await get_sfu_udp_port_range_services(start_port, start_port, values, make_templates)
87+
assert len(services) == 1, "SFU UDP Range service is incorrectly sharded for 1 port"
88+
assert_sharded_udp_range_ports(start_port, start_port, services[0])
89+
90+
services = await get_sfu_udp_port_range_services(start_port, start_port + 1, values, make_templates)
91+
assert len(services) == 1, "SFU UDP Range service is incorrectly sharded for 2 ports"
92+
assert_sharded_udp_range_ports(start_port, start_port + 1, services[0])
93+
94+
services = await get_sfu_udp_port_range_services(start_port, start_port + 249, values, make_templates)
95+
assert len(services) == 1, "SFU UDP Range service is incorrectly sharded for 250 ports"
96+
assert_sharded_udp_range_ports(start_port, start_port + 249, services[0])
97+
98+
services = await get_sfu_udp_port_range_services(start_port, start_port + 250, values, make_templates)
99+
assert len(services) == 2, "SFU UDP Range service is incorrectly sharded for 251 ports"
100+
assert_sharded_udp_range_ports(start_port, start_port + 249, services[0])
101+
assert_sharded_udp_range_ports(start_port + 250, start_port + 250, services[1])
102+
103+
services = await get_sfu_udp_port_range_services(start_port, start_port + 999, values, make_templates)
104+
assert len(services) == 4, "SFU UDP Range service is incorrectly sharded for 1000 ports"
105+
assert_sharded_udp_range_ports(start_port, start_port + 249, services[0])
106+
assert_sharded_udp_range_ports(start_port + 250, start_port + 499, services[1])
107+
assert_sharded_udp_range_ports(start_port + 500, start_port + 749, services[2])
108+
assert_sharded_udp_range_ports(start_port + 750, start_port + 999, services[3])

tests/manifests/test_services.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,22 @@
1313
async def test_ports_in_services_are_named(templates):
1414
for template in templates:
1515
if template["kind"] == "Service":
16-
id = template_id(template)
17-
assert "ports" in template["spec"], f"{id} does not specify a ports list"
18-
assert len(template["spec"]["ports"]) > 0, f"{id} does not include any ports"
19-
2016
port_names = []
2117
for port in template["spec"]["ports"]:
22-
assert "name" in port, f"{id} has a port without a name: {port}"
18+
assert "name" in port, f"{template_id(template)} has a port without a name: {port}"
2319
port_names.append(port["name"])
24-
assert len(port_names) == len(set(port_names)), f"Port names are not unique: {id}, {port_names}"
20+
assert len(port_names) == len(set(port_names)), (
21+
f"Port names are not unique: {template_id(template)}, {port_names}"
22+
)
23+
24+
25+
@pytest.mark.parametrize("values_file", services_values_files_to_test)
26+
@pytest.mark.asyncio_cooperative
27+
async def test_not_too_many_ports_in_services(templates):
28+
for template in templates:
29+
if template["kind"] == "Service":
30+
assert "ports" in template["spec"], f"{template_id(template)} does not specify a ports list"
31+
32+
number_of_ports = len(template["spec"]["ports"])
33+
assert number_of_ports > 0, f"{template_id(template)} does not include any ports"
34+
assert number_of_ports <= 250, f"{template_id(template)} has more than 250 ports"

0 commit comments

Comments
 (0)