Skip to content

Commit cf050d2

Browse files
committed
Implemented complete parse_host method to handle all accepted values of Client's base_url. Unit tests
1 parent 9170219 commit cf050d2

File tree

4 files changed

+103
-12
lines changed

4 files changed

+103
-12
lines changed

docker/client.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,9 @@ class Client(requests.Session):
3939
def __init__(self, base_url=None, version=DEFAULT_DOCKER_API_VERSION,
4040
timeout=DEFAULT_TIMEOUT_SECONDS):
4141
super(Client, self).__init__()
42-
if base_url is None:
43-
base_url = "http+unix://var/run/docker.sock"
44-
if 'unix:///' in base_url:
42+
base_url = utils.parse_host(base_url)
43+
if 'http+unix:///' in base_url:
4544
base_url = base_url.replace('unix:/', 'unix:')
46-
if base_url.startswith('unix:'):
47-
base_url = "http+" + base_url
48-
if base_url.startswith('tcp:'):
49-
base_url = base_url.replace('tcp:', 'http:')
50-
if base_url.endswith('/'):
51-
base_url = base_url[:-1]
5245
self.base_url = base_url
5346
self._version = version
5447
self._timeout = timeout

docker/utils/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from .utils import (
22
compare_version, convert_port_bindings, convert_volume_binds,
3-
mkbuildcontext, ping, tar, parse_repository_tag
3+
mkbuildcontext, ping, tar, parse_repository_tag, parse_host
44
) # flake8: noqa

docker/utils/utils.py

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
import requests
2121
import six
2222

23+
from .. import errors
24+
25+
DEFAULT_HTTP_HOST = "127.0.0.1"
26+
DEFAULT_UNIX_SOCKET = "http+unix://var/run/docker.sock"
27+
2328

2429
def mkbuildcontext(dockerfile):
2530
f = tempfile.NamedTemporaryFile()
@@ -139,9 +144,69 @@ def parse_repository_tag(repo):
139144
column_index = repo.rfind(':')
140145
if column_index < 0:
141146
return repo, None
142-
tag = repo[column_index+1:]
147+
tag = repo[column_index + 1:]
143148
slash_index = tag.find('/')
144149
if slash_index < 0:
145150
return repo[:column_index], tag
146151

147152
return repo, None
153+
154+
155+
# Based on utils.go:ParseHost http://tinyurl.com/nkahcfh
156+
# fd:// protocol unsupported (for obvious reasons)
157+
# Added support for http and https
158+
# Protocol translation: tcp -> http, unix -> http+unix
159+
def parse_host(addr):
160+
proto = "http+unix"
161+
host = DEFAULT_HTTP_HOST
162+
port = None
163+
if not addr or addr.strip() == 'unix://':
164+
return DEFAULT_UNIX_SOCKET
165+
166+
addr = addr.strip()
167+
if addr.startswith('http://'):
168+
addr = addr.replace('http://', 'tcp://')
169+
170+
if addr == 'tcp://':
171+
raise errors.DockerException("Invalid bind address format: %s" % addr)
172+
elif addr.startswith('unix://'):
173+
addr = addr[7:]
174+
elif addr.startswith('tcp://'):
175+
proto = "http"
176+
addr = addr[6:]
177+
elif addr.startswith('https://'):
178+
proto = "https"
179+
addr = addr[8:]
180+
elif addr.startswith('fd://'):
181+
raise errors.DockerException("fd protocol is not implemented")
182+
else:
183+
if "://" in addr:
184+
raise errors.DockerException(
185+
"Invalid bind address protocol: %s" % addr
186+
)
187+
proto = "http"
188+
189+
if proto != "http+unix" and ":" in addr:
190+
host_parts = addr.split(':')
191+
if len(host_parts) != 2:
192+
raise errors.DockerException(
193+
"Invalid bind address format: %s" % addr
194+
)
195+
if host_parts[0]:
196+
host = host_parts[0]
197+
198+
try:
199+
port = int(host_parts[1])
200+
except Exception:
201+
raise errors.DockerException(
202+
"Invalid port: %s", addr
203+
)
204+
205+
elif proto in ("http", "https") and ':' not in addr:
206+
raise errors.DockerException("Bind address needs a port: %s" % addr)
207+
else:
208+
host = addr
209+
210+
if proto == "http+unix":
211+
return "%s://%s" % (proto, host)
212+
return "%s://%s:%d" % (proto, host, port)

tests/utils_test.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import unittest
22

3-
from docker.utils import parse_repository_tag
3+
from docker.errors import DockerException
4+
from docker.utils import parse_repository_tag, parse_host
45

56

67
class UtilsTest(unittest.TestCase):
8+
longMessage = True
79

810
def test_parse_repository_tag(self):
911
self.assertEqual(parse_repository_tag("root"),
@@ -19,6 +21,37 @@ def test_parse_repository_tag(self):
1921
self.assertEqual(parse_repository_tag("url:5000/repo:tag"),
2022
("url:5000/repo", "tag"))
2123

24+
def test_parse_host(self):
25+
invalid_hosts = [
26+
'0.0.0.0',
27+
'tcp://',
28+
'udp://127.0.0.1',
29+
'udp://127.0.0.1:2375',
30+
]
31+
32+
valid_hosts = {
33+
'0.0.0.1:5555': 'http://0.0.0.1:5555',
34+
':6666': 'http://127.0.0.1:6666',
35+
'tcp://:7777': 'http://127.0.0.1:7777',
36+
'http://:7777': 'http://127.0.0.1:7777',
37+
'https://kokia.jp:2375': 'https://kokia.jp:2375',
38+
'': 'http+unix://var/run/docker.sock',
39+
None: 'http+unix://var/run/docker.sock',
40+
'unix:///var/run/docker.sock': 'http+unix:///var/run/docker.sock',
41+
'unix://': 'http+unix://var/run/docker.sock'
42+
}
43+
44+
for host in invalid_hosts:
45+
try:
46+
parsed = parse_host(host)
47+
self.fail('Expected to fail but success: %s -> %s' % (
48+
host, parsed
49+
))
50+
except DockerException:
51+
pass
52+
53+
for host, expected in valid_hosts.items():
54+
self.assertEqual(parse_host(host), expected, msg=host)
2255

2356
if __name__ == '__main__':
2457
unittest.main()

0 commit comments

Comments
 (0)