From 6826d72aa2e5474e625e989df7264512d600061a Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 26 Jun 2020 12:38:25 +0200 Subject: [PATCH] allow user to specify a single port with default command Allows specifying `-p9999` or `-p5555:9999` without also needing to override the default command app retrieves port arguments from length-1 port mapping for default command, instead of requiring random port OR overriding the whole command --- repo2docker/__main__.py | 2 +- repo2docker/app.py | 33 +++++++++++++++++++++------------ repo2docker/utils.py | 11 ++++++++--- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/repo2docker/__main__.py b/repo2docker/__main__.py index 2f9887ff..1b52b1ae 100644 --- a/repo2docker/__main__.py +++ b/repo2docker/__main__.py @@ -354,7 +354,7 @@ def make_r2d(argv=None): ) sys.exit(1) - if args.ports and not r2d.run_cmd: + if args.ports and len(args.ports) > 1 and not r2d.run_cmd: print( "To publish user defined port mapping, user must specify " "the command to run in the container" diff --git a/repo2docker/app.py b/repo2docker/app.py index d9a114a4..4625ac3f 100755 --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -595,8 +595,24 @@ class Repo2Docker(Application): self.hostname = host_name if not self.run_cmd: - port = str(self._get_free_port()) - self.port = port + if len(self.ports) == 1: + # single port mapping specified + # retrieve container and host port from dict + # {'8888/tcp': ('hostname', 'port')} + # or + # {'8888/tcp': 'port'} + container_port_proto, host_port = next(iter(self.ports.items())) + if isinstance(host_port, tuple): + # (hostname, port) tuple or string port + host_name, host_port = host_port + self.hostname = host_name + host_port = int(host_port) + container_port = int(container_port_proto.split("/", 1)[0]) + else: + # no port specified, pick a random one + container_port = host_port = str(self._get_free_port()) + self.ports = {"%s/tcp" % container_port: host_port} + self.port = host_port # To use the option --NotebookApp.custom_display_url # make sure the base-notebook image is updated: # docker pull jupyter/base-notebook @@ -606,20 +622,13 @@ class Repo2Docker(Application): "--ip", "0.0.0.0", "--port", - port, - "--NotebookApp.custom_display_url=http://{}:{}".format(host_name, port), + container_port, + f"--NotebookApp.custom_display_url=http://{host_name}:{host_port}" "--NotebookApp.default_url=/lab", ] - ports = {"%s/tcp" % port: port} else: # run_cmd given by user, if port is also given then pass it on run_cmd = self.run_cmd - if self.ports: - ports = self.ports - else: - ports = {} - # store ports on self so they can be retrieved in tests - self.ports = ports container_volumes = {} if self.volumes: @@ -634,7 +643,7 @@ class Repo2Docker(Application): run_kwargs = dict( publish_all_ports=self.all_ports, - ports=ports, + ports=self.ports, command=run_cmd, volumes=container_volumes, environment=self.environment, diff --git a/repo2docker/utils.py b/repo2docker/utils.py index ef61814b..e8833bd8 100644 --- a/repo2docker/utils.py +++ b/repo2docker/utils.py @@ -139,10 +139,10 @@ def validate_and_generate_port_mapping(port_mappings): raise ValueError( 'Port specification "{}" has ' "an invalid port.".format(mapping) ) - if p > 65535: + if not 0 < p <= 65535: raise ValueError( 'Port specification "{}" specifies ' - "a port above 65535.".format(mapping) + "a port outside 1-65535.".format(mapping) ) return port @@ -168,7 +168,12 @@ def validate_and_generate_port_mapping(port_mappings): return ports for mapping in port_mappings: - parts = mapping.split(":") + if ":" in mapping: + parts = mapping.split(":") + else: + # single port '8888' specified, + # treat as '8888:8888' + parts = [mapping, mapping] *host, container_port = parts # just a port