Skip to content

Commit be7fc8a

Browse files
authored
Merge pull request #918 from minrk/allow-one-port
allow user to specify a single port with default command
2 parents 0f5ee70 + b3234b5 commit be7fc8a

File tree

5 files changed

+45
-22
lines changed

5 files changed

+45
-22
lines changed

repo2docker/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ def make_r2d(argv=None):
354354
)
355355
sys.exit(1)
356356

357-
if args.ports and not r2d.run_cmd:
357+
if args.ports and len(args.ports) > 1 and not r2d.run_cmd:
358358
print(
359359
"To publish user defined port mapping, user must specify "
360360
"the command to run in the container"

repo2docker/app.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -595,8 +595,24 @@ def start_container(self):
595595
self.hostname = host_name
596596

597597
if not self.run_cmd:
598-
port = str(self._get_free_port())
599-
self.port = port
598+
if len(self.ports) == 1:
599+
# single port mapping specified
600+
# retrieve container and host port from dict
601+
# {'8888/tcp': ('hostname', 'port')}
602+
# or
603+
# {'8888/tcp': 'port'}
604+
container_port_proto, host_port = next(iter(self.ports.items()))
605+
if isinstance(host_port, tuple):
606+
# (hostname, port) tuple or string port
607+
host_name, host_port = host_port
608+
self.hostname = host_name
609+
host_port = int(host_port)
610+
container_port = int(container_port_proto.split("/", 1)[0])
611+
else:
612+
# no port specified, pick a random one
613+
container_port = host_port = str(self._get_free_port())
614+
self.ports = {f"{container_port}/tcp": host_port}
615+
self.port = host_port
600616
# To use the option --NotebookApp.custom_display_url
601617
# make sure the base-notebook image is updated:
602618
# docker pull jupyter/base-notebook
@@ -606,20 +622,13 @@ def start_container(self):
606622
"--ip",
607623
"0.0.0.0",
608624
"--port",
609-
port,
610-
"--NotebookApp.custom_display_url=http://{}:{}".format(host_name, port),
625+
container_port,
626+
f"--NotebookApp.custom_display_url=http://{host_name}:{host_port}"
611627
"--NotebookApp.default_url=/lab",
612628
]
613-
ports = {"%s/tcp" % port: port}
614629
else:
615630
# run_cmd given by user, if port is also given then pass it on
616631
run_cmd = self.run_cmd
617-
if self.ports:
618-
ports = self.ports
619-
else:
620-
ports = {}
621-
# store ports on self so they can be retrieved in tests
622-
self.ports = ports
623632

624633
container_volumes = {}
625634
if self.volumes:
@@ -634,7 +643,7 @@ def start_container(self):
634643

635644
run_kwargs = dict(
636645
publish_all_ports=self.all_ports,
637-
ports=ports,
646+
ports=self.ports,
638647
command=run_cmd,
639648
volumes=container_volumes,
640649
environment=self.environment,

repo2docker/utils.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,10 @@ def check_port(port):
139139
raise ValueError(
140140
'Port specification "{}" has ' "an invalid port.".format(mapping)
141141
)
142-
if p > 65535:
142+
if not 0 < p <= 65535:
143143
raise ValueError(
144144
'Port specification "{}" specifies '
145-
"a port above 65535.".format(mapping)
145+
"a port outside 1-65535.".format(mapping)
146146
)
147147
return port
148148

@@ -168,7 +168,12 @@ def check_port_string(p):
168168
return ports
169169

170170
for mapping in port_mappings:
171-
parts = mapping.split(":")
171+
if ":" in mapping:
172+
parts = mapping.split(":")
173+
else:
174+
# single port '8888' specified,
175+
# treat as '8888:8888'
176+
parts = [mapping, mapping]
172177

173178
*host, container_port = parts
174179
# just a port

tests/unit/test_args.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
"""
44
import os
55
import pytest
6-
import logging
76
from repo2docker.__main__ import make_r2d
87
from repo2docker import __version__
98

@@ -71,10 +70,6 @@ def test_run_required():
7170
with pytest.raises(SystemExit):
7271
make_r2d(["--no-run", "-p", "8000:8000", "."])
7372

74-
# Can't publish any ports while running if we don't specify a command explicitly
75-
with pytest.raises(SystemExit):
76-
make_r2d(["-p", "8000:8000", "."])
77-
7873

7974
def test_clean():
8075
"""

tests/unit/test_ports.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import pytest
1414

1515
from repo2docker.app import Repo2Docker
16+
from repo2docker.__main__ import make_r2d
1617

1718

1819
def read_port_mapping_response(
@@ -95,7 +96,7 @@ def _cleanup():
9596
else:
9697
break
9798
else:
98-
pytest.fail("Never succeded in talking to %s" % url)
99+
pytest.fail(f"Never succeeded in talking to {url}")
99100
assert "Directory listing" in r.text
100101

101102

@@ -113,3 +114,16 @@ def test_port_mapping(request, tmpdir, host, protocol):
113114
"""Test a port mapping"""
114115
port = str(random.randint(50000, 51000))
115116
read_port_mapping_response(request, tmpdir, host=host, port=port, protocol=protocol)
117+
118+
119+
@pytest.mark.parametrize(
120+
"port_str, port_dict",
121+
[
122+
("8000", {"8000/tcp": "8000"}),
123+
("8000:9000", {"9000/tcp": "8000"}),
124+
("127.0.0.1:8000:9000", {"9000/tcp": ("127.0.0.1", "8000")}),
125+
],
126+
)
127+
def test_port_args(port_str, port_dict):
128+
app = make_r2d(["-p", port_str, "."])
129+
assert app.ports == port_dict

0 commit comments

Comments
 (0)