`ListContainersAsync` response contains inconsistent public host ports
- Dominant language
- C#
- Stars
- 2.4k
- Forks
- 416
- PR merge metrics
- No merged PRs in 30d
Description
**Output of `dotnet --info`:**
```
.NET SDK (reflecting any global.json):
Version: 6.0.300
Commit: 8473146e7d
Runtime Environment:
OS Name: Windows
OS Version: 10.0.19044
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\6.0.300\
Host (useful for support):
Version: 6.0.5
Commit: 70ae3df4a6
.NET SDKs installed:
6.0.300 [C:\Program Files\dotnet\sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
To install additional .NET runtimes or SDKs:
https://aka.ms/dotnet-download
```
**What version of Docker.DotNet?:**
```
3.125.5
```
**Steps to reproduce the issue:**
```cs
public sealed class ExampleContainer
{
private static readonly IDockerClient DockerClient = new DockerClientConfiguration(new Uri("npipe://./pipe/docker_engine")).CreateClient();
private static readonly CreateContainerParameters CreateContainerParameters = new CreateContainerParameters();
private readonly ISet hostPorts = new HashSet();
private int tryCount;
private string ID;
static ExampleContainer()
{
var hostConfig = new HostConfig();
hostConfig.PortBindings = new Dictionary> { { "80/tcp", GetFailureConfig() } };
CreateContainerParameters.Image = "nginx";
CreateContainerParameters.HostConfig = hostConfig;
}
[Fact]
public Task ShouldReturnOnePublicHostPort()
{
return Task.WhenAll(Enumerable.Range(0, 25)
.Select(_ => new ExampleContainer())
.Select(example => Task.Run(async () =>
{
try
{
await example.InitializeAsync()
.ConfigureAwait(false);
await example.GetPublicHostPort()
.ConfigureAwait(false);
}
finally
{
await example.DisposeAsync()
.ConfigureAwait(false);
}
})));
}
private static IList GetSuccessConfig()
{
return new[] { new PortBinding { HostPort = "0" } };
}
private static IList GetFailureConfig()
{
return new[] { new PortBinding { HostPort = null } };
}
private async Task InitializeAsync()
{
var createContainerResponse = await DockerClient.Containers.CreateContainerAsync(CreateContainerParameters)
.ConfigureAwait(false);
this.ID = createContainerResponse.ID;
_ = await DockerClient.Containers.StartContainerAsync(this.ID, new ContainerStartParameters())
.ConfigureAwait(false);
}
private Task DisposeAsync()
{
var containerRemoveParameters = new ContainerRemoveParameters();
containerRemoveParameters.Force = true;
return DockerClient.Containers.RemoveContainerAsync(this.ID, containerRemoveParameters);
}
private async Task GetPublicHostPort()
{
do
{
var containersListParameters = new ContainersListParameters();
containersListParameters.All = true;
containersListParameters.Filters = new Dictionary>();
containersListParameters.Filters.Add("id", new Dictionary { { this.ID, true } });
var listContainersResponse = await DockerClient.Containers.ListContainersAsync(containersListParameters)
.ConfigureAwait(false);
foreach (var publicHostPort in listContainersResponse.Single().Ports.Select(portBindings => portBindings.PublicPort))
{
this.hostPorts.Add(publicHostPort.ToString(CultureInfo.InvariantCulture));
}
if (this.hostPorts.Count > 1)
{
throw new InvalidOperationException(string.Join(',', this.hostPorts));
}
}
while (++this.tryCount < 10);
}
}
```
**What actually happened?:**
The response of `ListContainersAsync` sometimes contains for a single port binding multiple public host ports. In this case, a service behind the mapped port is not available. It looks like setting the port binding host port to `null` (assign random port) breaks it. I attached a reproducible example above. E.g. `L:89` throws: `System.InvalidOperationException: 49196,52481`.
- `... new[] { new PortBinding { HostPort = null } }`: fails
- `... new[] { new PortBinding { HostPort = "0" } }`: successful
This also breaks `InspectContainerAsync`. In this case the response (list of port bindings) is empty for at least the first call.
**What did you expect to happen?:**
A consistent response.
**Additional information:**
\-
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.