fix(localdb): re-bind ASPNETCORE_HTTP_PORTS surface, not just ASPNETCORE_URLS

The Kestrel re-bind read only urls/ASPNETCORE_URLS. The aspnet:8.0+ base images
set ASPNETCORE_HTTP_PORTS=8080 (all interfaces) as the CONTAINER default in place
of ASPNETCORE_URLS, so a driver node that never sets URLS fell straight through to
Kestrel's localhost:5000 default — silently moving its health/metrics surface to
loopback when the sync listener took over Kestrel. Now falls through URLS →
HTTP_PORTS/HTTPS_PORTS (wildcard, all-interfaces) → localhost:5000, mirroring
Kestrel's own source precedence. Found on the docker-dev live gate (central nodes
were fine — they set ASPNETCORE_URLS=9000 explicitly).
This commit is contained in:
Joseph Doherty
2026-07-20 22:25:31 -04:00
parent 4b2f0e6e15
commit ce9fa07ff8
3 changed files with 86 additions and 1 deletions
@@ -88,6 +88,43 @@ public sealed class LocalDbSyncListenerTests
KestrelHttpBinding.Parse("not a url;http://+:9000").ShouldHaveSingleItem().Port.ShouldBe(9000);
}
// ---- ParsePorts (ASPNETCORE_HTTP_PORTS / HTTPS_PORTS) ----------------------------------
[Fact]
public void ParsePorts_NullOrEmpty_YieldsNoBindings()
{
KestrelHttpBinding.ParsePorts(null, isSecure: false).ShouldBeEmpty();
KestrelHttpBinding.ParsePorts("", isSecure: false).ShouldBeEmpty();
KestrelHttpBinding.ParsePorts(" ", isSecure: false).ShouldBeEmpty();
}
[Fact]
public void ParsePorts_BarePort_BindsAllInterfaces()
{
// The aspnet:8.0+ container default is ASPNETCORE_HTTP_PORTS=8080, on all interfaces — not
// loopback. Re-binding it as a wildcard is what keeps a driver node's health/metrics surface
// reachable from other containers once the sync listener takes over Kestrel.
var binding = KestrelHttpBinding.ParsePorts("8080", isSecure: false).ShouldHaveSingleItem();
binding.Host.ShouldBe("+");
binding.Port.ShouldBe(8080);
binding.IsSecure.ShouldBeFalse();
}
[Fact]
public void ParsePorts_SeparatedList_AndSecureFlag()
{
KestrelHttpBinding.ParsePorts("8080;8081", isSecure: false).Select(b => b.Port).ShouldBe([8080, 8081]);
KestrelHttpBinding.ParsePorts("8080,8081", isSecure: false).Select(b => b.Port).ShouldBe([8080, 8081]);
KestrelHttpBinding.ParsePorts("443", isSecure: true).ShouldHaveSingleItem().IsSecure.ShouldBeTrue();
}
[Fact]
public void ParsePorts_MalformedPort_IsSkipped_NotThrown()
{
KestrelHttpBinding.ParsePorts("notaport;8080;0;70000", isSecure: false)
.ShouldHaveSingleItem().Port.ShouldBe(8080);
}
// ---------------------------------------------------------------------------------------
// The real Kestrel contract
// ---------------------------------------------------------------------------------------