Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ToHostPort needs more flexibility #107

Closed
mariotoffia opened this issue Sep 5, 2019 · 3 comments
Closed

ToHostPort needs more flexibility #107

mariotoffia opened this issue Sep 5, 2019 · 3 comments
Assignees
Milestone

Comments

@mariotoffia
Copy link
Owner

mariotoffia commented Sep 5, 2019

The current implementation how to get a host and port, the host part is not sufficient in some cases to acommodate users. It needs to have a bit of a configurable flexibility. This is derived from Issue #105.

The current implementation is merely a bulldozer as follows:

/// <summary>
    ///   Translates a docker exposed port and protocol (on format 'port/proto' e.g. '534/tcp') to a
    ///   host endpoint that can be contacted outside the container.
    /// </summary>
    /// <param name="ports">The ports from the <see cref="ContainerNetworkSettings.Ports" /> property.</param>
    /// <param name="portAndProto">The port and protocol string.</param>
    /// <param name="dockerUri">Optional docker uri to use when the address is 0.0.0.0 in the endpoint.</param>
    /// <returns>A endpoint of the host exposed ip and port into the container port. If none is found, null is returned.</returns>
    public static IPEndPoint ToHostPort(this Dictionary<string, HostIpEndpoint[]> ports, string portAndProto,
      Uri dockerUri = null)
    {
      if (null == ports || string.IsNullOrEmpty(portAndProto)) return null;

      if (!ports.TryGetValue(portAndProto, out var endpoints)) return null;

      if (null == endpoints || endpoints.Length == 0) return null;

      if (CommandExtensions.IsNative()) return endpoints[0];

      if (CommandExtensions.IsEmulatedNative())
        return CommandExtensions.IsDockerDnsAvailable()
          ? new IPEndPoint(CommandExtensions.EmulatedNativeAdress(), endpoints[0].Port)
          : new IPEndPoint(IPAddress.Loopback, endpoints[0].Port);

      if (Equals(endpoints[0].Address, IPAddress.Any) && null != dockerUri)
        return new IPEndPoint(IPAddress.Parse(dockerUri.Host), endpoints[0].Port);

      return endpoints[0];
    }
@ta32
Copy link

ta32 commented Oct 1, 2019

Hi
I didn't create a new issue, but the 'testcontainers' library has the option of selecting a random unused host port. This off course should be optional, because some people still might prefer to use pick host port ranges themselves

https://www.testcontainers.org/features/networking/

ok nvm. I can just let docker select a host port binding... and get what port it used to avoid port collisions

"This is to always resolve to a working ip and port to communicate with the docker container. It is also possible to map a random port, i.e. let Docker choose a available port. For example:"

@mariotoffia
Copy link
Owner Author

mariotoffia commented Oct 10, 2019

Hi @ta32, the current implementation will use the docker ability to select a random port on the host (where daemon hosting the container). In e.g. unittest

 public void ImplicitPortMappingShouldWork()
    {
      using (
        var container =
          Fd.UseContainer()
            .UseImage("postgres:9.6-alpine")
            .ExposePort(5432)
            .WithEnvironment("POSTGRES_PASSWORD=mysecretpassword")
            .Build()
            .Start())
      {
        var endpoint = container.ToHostExposedEndpoint("5432/tcp");
        AreNotEqual(0, endpoint.Port);
      }
    }

will render the following

C:\Users\martoffi>docker ps -a                                                                                                                                                                                                 
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                     NAMES
93289c5d2b57        postgres:9.6-alpine   "docker-entrypoint.s…"   36 seconds ago      Up 29 seconds       0.0.0.0:32768->5432/tcp   amazing_keller

where the ExposePort(5432) will bind container port 5432 to random host port. This is later on resolveable by code using e.g. an extension such as var endpoint = container.ToHostExposedEndpoint("5432/tcp") where the endpoint will e.g contain 127.0.0.1:32768. The port 32768 is selected by the docker damon randomly and non colliding.

If you want to have explicit portmapping, look at the unittest

 public void ExplicitPortMappingShouldWork()
    {
      using (
        var container =
          Fd.UseContainer()
            .UseImage("postgres:9.6-alpine")
            .ExposePort(40001, 5432)
            .WithEnvironment("POSTGRES_PASSWORD=mysecretpassword")
            .Build()
            .Start())
      {
        var endpoint = container.ToHostExposedEndpoint("5432/tcp");
        AreEqual(40001, endpoint.Port);
      }
    }

Where the container port 5432 is exposed on host 40001 and thus may experience a conflict.

This issue is in regards when custom networking or other remote daemon. Therefore the ToHostPort needs be much more flexible to acommodate different needs of resolving the IP address.

Cheers,
Mario

@mariotoffia
Copy link
Owner Author

mariotoffia commented Mar 25, 2021

Finally support for a custom resolver on single containers.
This is working in version 2.9.1 and updated README.md with an example how to use it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants