Уточнена диагностика UDP port mapping для Docker и обновлены checklist/docs
This commit is contained in:
+124
-16
@@ -187,6 +187,34 @@ fn parse_udp_port_path_from_inspect(
|
||||
.and_then(|x| x.as_object())
|
||||
.ok_or_else(|| anyhow::anyhow!("docker inspect has no NetworkSettings.Ports"))?;
|
||||
|
||||
let mut udp_bindings: Vec<(u16, u16)> = Vec::new();
|
||||
for (k, v) in ports {
|
||||
if !k.ends_with("/udp") {
|
||||
continue;
|
||||
}
|
||||
let Some(host_port) = extract_host_port_from_binding(v) else {
|
||||
continue;
|
||||
};
|
||||
let container_port = k
|
||||
.split('/')
|
||||
.next()
|
||||
.and_then(|x| x.parse::<u16>().ok())
|
||||
.ok_or_else(|| anyhow::anyhow!("invalid udp port key in docker inspect: {k}"))?;
|
||||
udp_bindings.push((host_port, container_port));
|
||||
}
|
||||
|
||||
if let Some((host_port, container_port)) = udp_bindings
|
||||
.iter()
|
||||
.copied()
|
||||
.find(|(host_port, _)| *host_port == expected_udp_port)
|
||||
{
|
||||
return Ok(PublishedUdpPortPath {
|
||||
host_port,
|
||||
container_ip,
|
||||
container_port,
|
||||
});
|
||||
}
|
||||
|
||||
let expected_key = format!("{expected_udp_port}/udp");
|
||||
if let Some(host_port) = extract_host_port_for_udp_key(ports, &expected_key) {
|
||||
return Ok(PublishedUdpPortPath {
|
||||
@@ -196,22 +224,19 @@ fn parse_udp_port_path_from_inspect(
|
||||
});
|
||||
}
|
||||
|
||||
for (k, v) in ports {
|
||||
if !k.ends_with("/udp") {
|
||||
continue;
|
||||
}
|
||||
if let Some(host_port) = extract_host_port_from_binding(v) {
|
||||
let container_port = k
|
||||
.split('/')
|
||||
.next()
|
||||
.and_then(|x| x.parse::<u16>().ok())
|
||||
.unwrap_or(expected_udp_port);
|
||||
return Ok(PublishedUdpPortPath {
|
||||
host_port,
|
||||
container_ip,
|
||||
container_port,
|
||||
});
|
||||
}
|
||||
if udp_bindings.len() == 1 {
|
||||
let (host_port, container_port) = udp_bindings[0];
|
||||
return Ok(PublishedUdpPortPath {
|
||||
host_port,
|
||||
container_ip,
|
||||
container_port,
|
||||
});
|
||||
}
|
||||
|
||||
if udp_bindings.len() > 1 {
|
||||
return Err(anyhow::anyhow!(
|
||||
"ambiguous udp published ports in docker inspect"
|
||||
));
|
||||
}
|
||||
|
||||
Err(anyhow::anyhow!(
|
||||
@@ -286,6 +311,20 @@ mod tests {
|
||||
"443/udp": [{ "HostIp": "0.0.0.0", "HostPort": "443" }]
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_udp_port_prefers_host_port_match() {
|
||||
let raw = r#"[
|
||||
{
|
||||
"NetworkSettings": {
|
||||
"Networks": {
|
||||
"bridge": { "IPAddress": "172.29.172.2" }
|
||||
},
|
||||
"Ports": {
|
||||
"44344/udp": [{ "HostIp": "0.0.0.0", "HostPort": "443" }],
|
||||
"443/udp": [{ "HostIp": "0.0.0.0", "HostPort": "5443" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
]"#;
|
||||
let got = parse_udp_port_path_from_inspect(raw, 443).expect("parse inspect");
|
||||
@@ -294,11 +333,80 @@ mod tests {
|
||||
PublishedUdpPortPath {
|
||||
host_port: 443,
|
||||
container_ip: "172.29.172.2".to_string(),
|
||||
container_port: 44344,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_udp_port_fallbacks_to_expected_key() {
|
||||
let raw = r#"[
|
||||
{
|
||||
"NetworkSettings": {
|
||||
"Networks": {
|
||||
"bridge": { "IPAddress": "172.29.172.2" }
|
||||
},
|
||||
"Ports": {
|
||||
"443/udp": [{ "HostIp": "0.0.0.0", "HostPort": "5443" }],
|
||||
"44344/udp": [{ "HostIp": "0.0.0.0", "HostPort": "15443" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
]"#;
|
||||
let got = parse_udp_port_path_from_inspect(raw, 443).expect("parse inspect");
|
||||
assert_eq!(
|
||||
got,
|
||||
PublishedUdpPortPath {
|
||||
host_port: 5443,
|
||||
container_ip: "172.29.172.2".to_string(),
|
||||
container_port: 443,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_udp_port_uses_single_udp_binding() {
|
||||
let raw = r#"[
|
||||
{
|
||||
"NetworkSettings": {
|
||||
"Networks": {
|
||||
"bridge": { "IPAddress": "172.29.172.2" }
|
||||
},
|
||||
"Ports": {
|
||||
"44344/udp": [{ "HostIp": "0.0.0.0", "HostPort": "44344" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
]"#;
|
||||
let got = parse_udp_port_path_from_inspect(raw, 443).expect("parse inspect");
|
||||
assert_eq!(
|
||||
got,
|
||||
PublishedUdpPortPath {
|
||||
host_port: 44344,
|
||||
container_ip: "172.29.172.2".to_string(),
|
||||
container_port: 44344,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_udp_port_returns_ambiguous_on_multiple_without_match() {
|
||||
let raw = r#"[
|
||||
{
|
||||
"NetworkSettings": {
|
||||
"Networks": {
|
||||
"bridge": { "IPAddress": "172.29.172.2" }
|
||||
},
|
||||
"Ports": {
|
||||
"10000/udp": [{ "HostIp": "0.0.0.0", "HostPort": "10000" }],
|
||||
"20000/udp": [{ "HostIp": "0.0.0.0", "HostPort": "20000" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
]"#;
|
||||
let err = parse_udp_port_path_from_inspect(raw, 443).expect_err("must be ambiguous");
|
||||
assert!(err.to_string().contains("ambiguous udp published ports"));
|
||||
}
|
||||
#[test]
|
||||
fn parse_forward_policy_and_docker_rules_works() {
|
||||
let raw = r#"
|
||||
|
||||
Reference in New Issue
Block a user