daemon/links: fix port-sorting with mixed protocols

The intent of this sorting was twofold;

- the "default" port of the container to be the first TCP port (if present)
- consecutive port-mappings with the same protocol to be together so
  that port-ranges would produce an env-var describing the range.

The current sorting would sort TCP ports before UDP (or SCTP) port, but
only if they had the same port-number. This could result in range-detection
incorrectly combining TCP and UDP (or SCTP) ports in the same range.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2025-01-09 11:10:00 +01:00
parent 3d37d54b8c
commit 251c68c647
2 changed files with 47 additions and 5 deletions

View File

@@ -54,11 +54,7 @@ func (l *Link) ToEnv() []string {
alias := strings.ReplaceAll(strings.ToUpper(n), "-", "_")
// sort the ports so that we can bulk the continuous ports together
nat.Sort(l.Ports, func(ip, jp nat.Port) bool {
// If the two ports have the same number, tcp takes priority
// Sort in desc order
return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp")
})
nat.Sort(l.Ports, withTCPPriority)
for i := 0; i < len(l.Ports); {
p := l.Ports[i]
@@ -108,6 +104,23 @@ func (l *Link) ToEnv() []string {
return env
}
// withTCPPriority prioritizes ports using TCP over other protocols before
// comparing port-number and protocol.
func withTCPPriority(ip, jp nat.Port) bool {
if strings.EqualFold(ip.Proto(), jp.Proto()) {
return ip.Int() < jp.Int()
}
if strings.EqualFold(ip.Proto(), "tcp") {
return true
}
if strings.EqualFold(jp.Proto(), "tcp") {
return false
}
return strings.ToLower(ip.Proto()) < strings.ToLower(jp.Proto())
}
func nextContiguous(ports []nat.Port, value int, index int) int {
if index+1 == len(ports) {
return index

View File

@@ -60,6 +60,35 @@ func TestLinkEnv(t *testing.T) {
assert.DeepEqual(t, expectedEnv, actual)
}
// TestSortPorts verifies that ports are sorted with TCP taking priority,
// and ports with the same protocol to be sorted by port.
func TestSortPorts(t *testing.T) {
ports := []nat.Port{
"6379/tcp",
"6376/udp",
"6380/tcp",
"6376/sctp",
"6381/tcp",
"6381/udp",
"6375/udp",
"6375/sctp",
}
expected := []nat.Port{
"6379/tcp",
"6380/tcp",
"6381/tcp",
"6375/sctp",
"6376/sctp",
"6375/udp",
"6376/udp",
"6381/udp",
}
nat.Sort(ports, withTCPPriority)
assert.DeepEqual(t, expected, ports)
}
func TestLinkMultipleEnv(t *testing.T) {
actual := EnvVars("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, nat.PortSet{
"6379/tcp": struct{}{},