feat: allow to bind to multiple host IP addresses specified as CIDR prefix in x-ports (#358)

Signed-off-by: Miek Gieben <miek@miek.nl>
Co-authored-by: Pasha Sviderski <me@psviderski.name>
This commit is contained in:
Miek Gieben
2026-06-04 20:07:58 +10:00
committed by GitHub
co-authored by Pasha Sviderski
parent 3586a32987
commit 1247f961c2
11 changed files with 312 additions and 76 deletions
+118 -1
View File
@@ -191,6 +191,16 @@ func TestPortSpec_Validate(t *testing.T) {
},
wantErr: "host IP cannot be specified in ingress mode",
},
{
name: "host prefix in ingress mode",
spec: PortSpec{
HostPrefix: netip.MustParsePrefix("127.0.0.1/8"),
ContainerPort: 8080,
Protocol: ProtocolTCP,
Mode: PortModeIngress,
},
wantErr: "host prefix cannot be specified in ingress mode",
},
{
name: "zero published port in host mode",
spec: PortSpec{
@@ -231,6 +241,18 @@ func TestPortSpec_Validate(t *testing.T) {
},
wantErr: "unsupported protocol 'https' in host mode",
},
{
name: "both host ip and prefix",
spec: PortSpec{
HostIP: netip.MustParseAddr("127.0.0.1"),
HostPrefix: netip.MustParsePrefix("127.0.0.0/8"),
PublishedPort: 80,
ContainerPort: 8080,
Protocol: ProtocolTCP,
Mode: PortModeHost,
},
wantErr: "host IP and prefix cannot both be specified in host mode",
},
}
for _, tt := range tests {
@@ -369,6 +391,17 @@ func TestPortSpec_String(t *testing.T) {
},
expected: "127.0.0.1:80:8080/udp@host",
},
{
name: "host mode with IPv4 prefix",
spec: PortSpec{
HostPrefix: netip.MustParsePrefix("127.0.0.1/8"),
PublishedPort: 80,
ContainerPort: 8080,
Protocol: ProtocolUDP,
Mode: PortModeHost,
},
expected: "127.0.0.1/8:80:8080/udp@host",
},
{
name: "host mode with IPv6",
spec: PortSpec{
@@ -380,6 +413,17 @@ func TestPortSpec_String(t *testing.T) {
},
expected: "[2001:db8::1234:5678]:80:8080/tcp@host",
},
{
name: "host mode with IPv6 prefix",
spec: PortSpec{
HostPrefix: netip.MustParsePrefix("2001:db8::/64"),
PublishedPort: 80,
ContainerPort: 8080,
Protocol: ProtocolUDP,
Mode: PortModeHost,
},
expected: "[2001:db8::]/64:80:8080/udp@host",
},
}
for _, tt := range tests {
@@ -553,6 +597,39 @@ func TestParsePortSpec(t *testing.T) {
Mode: PortModeHost,
},
},
{
name: "host mode with prefix and protocol",
port: "192.168.76.0/24:80:8080/udp@host",
expected: PortSpec{
HostPrefix: netip.MustParsePrefix("192.168.76.0/24"),
PublishedPort: 80,
ContainerPort: 8080,
Protocol: ProtocolUDP,
Mode: PortModeHost,
},
},
{
name: "host mode with prefix without protocol",
port: "192.168.76.0/24:80:8080@host",
expected: PortSpec{
HostPrefix: netip.MustParsePrefix("192.168.76.0/24"),
PublishedPort: 80,
ContainerPort: 8080,
Protocol: ProtocolTCP,
Mode: PortModeHost,
},
},
{
name: "host mode with IPv6 prefix and protocol",
port: "[2001:db8::]/64:80:8080/udp@host",
expected: PortSpec{
HostPrefix: netip.MustParsePrefix("2001:db8::/64"),
PublishedPort: 80,
ContainerPort: 8080,
Protocol: ProtocolUDP,
Mode: PortModeHost,
},
},
// Error cases.
{
@@ -560,11 +637,31 @@ func TestParsePortSpec(t *testing.T) {
port: "",
wantErr: "invalid container port",
},
{
name: "slash",
port: "/",
wantErr: "unsupported protocol",
},
{
name: "at",
port: "@",
wantErr: "invalid mode",
},
{
name: "invalid container port",
port: "invalid",
wantErr: "invalid container port",
},
{
name: "no protocol",
port: "53/",
wantErr: "unsupported protocol",
},
{
name: "no host modifier",
port: "53@",
wantErr: "invalid mode",
},
{
name: "container port zero",
port: "0",
@@ -598,7 +695,7 @@ func TestParsePortSpec(t *testing.T) {
{
name: "multiple protocols",
port: "8080/tcp/udp",
wantErr: "too many '/' symbols",
wantErr: "unsupported protocol: 'tcp/udp'",
},
{
name: "invalid protocol",
@@ -671,6 +768,26 @@ func TestParsePortSpec(t *testing.T) {
port: "app.example.com:invalid:8080@host",
wantErr: "invalid published port",
},
{
name: "invalid prefix",
port: "192.168.76.0/45:53:5353/udp@host",
wantErr: "invalid host prefix",
},
{
name: "valid prefix invalid protocol",
port: "192.168.76.0/24:53:5353/http@host",
wantErr: "unsupported protocol 'http' in host mode, only 'tcp' and 'udp' are supported",
},
{
name: "valid prefix invalid protocol",
port: "192.168.76.0/24:53:5353/invalid@host",
wantErr: "unsupported protocol 'invalid' in host mode, only 'tcp' and 'udp' are supported",
},
{
name: "valid prefix no protocol",
port: "192.168.76.0/24:53:5353/@host",
wantErr: "unsupported protocol '' in host mode, only 'tcp' and 'udp' are supported",
},
}
for _, tt := range tests {