A short-form port keeps its protocol and still gets its machine port

"3478/udp" read as one token was not a port, so it passed through and the
runtime published it wherever it liked: on ace, unifi's STUN and discovery
landed on random machine ports while every TCP pin beside them held. The
protocol is split off, the number is assigned as for any short form, and
the suffix rides along on the outside.
This commit is contained in:
2026-09-30 16:58:23 +02:00
parent 542ce76c0a
commit f0634e11f4
2 changed files with 35 additions and 2 deletions
+11 -2
View File
@@ -1576,14 +1576,23 @@ func publishedOn(resource map[string]any, module string, with Rendering) {
out = append(out, givenOuter(written, with.Given[module]))
continue
}
wanted, err := strconv.Atoi(strings.TrimSpace(written))
// A short form may carry the protocol — `"3478/udp"` — and the number is what the mesh
// assigns for; the protocol rides along. Read as one token, the `/udp` made the whole
// entry "not a port", and passing it through let the runtime publish it wherever it
// liked: unifi's STUN and discovery landed on random machine ports while every TCP pin
// beside them held.
mapping, protocol := written, ""
if cut := strings.LastIndex(written, "/"); cut >= 0 {
mapping, protocol = written[:cut], written[cut:]
}
wanted, err := strconv.Atoi(strings.TrimSpace(mapping))
if err != nil {
// Not a port at all. Passed through, so the host refuses it with its own words rather
// than this quietly dropping something somebody meant.
out = append(out, written)
continue
}
out = append(out, fmt.Sprintf("%d:%d", with.machinePort(module, wanted), wanted))
out = append(out, fmt.Sprintf("%d:%d%s", with.machinePort(module, wanted), wanted, protocol))
}
resource["ports"] = out
}
@@ -0,0 +1,24 @@
package catalogue
import (
"fmt"
"testing"
)
// A short-form port may name its protocol — "3478/udp" — and the mesh assigns the number exactly
// as it does for "3478": the protocol rides along on the outside. Read as one token, the suffix made
// the entry "not a port" and the runtime published it on a random machine port (found on ace: unifi's
// STUN and discovery, while every TCP pin beside them held).
func TestAShortFormPortKeepsItsProtocolAndGetsItsMachinePort(t *testing.T) {
container := map[string]any{"type": "container", "ports": []any{"3478/udp", "8443", "10001/udp"}}
with := Rendering{
Given: map[string]map[int]int{"unifi": {3478: 3478}},
Ports: map[string]map[int]int{"unifi": {8443: 20010, 10001: 20011}},
}
publishedOn(container, "unifi", with)
got := fmt.Sprint(container["ports"])
want := "[3478:3478/udp 20010:8443 20011:10001/udp]"
if got != want {
t.Fatalf("published %s, want %s", got, want)
}
}