diff --git a/docs/yml.md b/docs/yml.md index 81668b5610b..a189a54e386 100644 --- a/docs/yml.md +++ b/docs/yml.md @@ -70,6 +70,23 @@ external_links: - project_db_1:postgresql ``` +### extra_hosts + +Add hostname mappings. Use the same values as the docker client `--add-hosts` parameter. + +``` +extra_hosts: + - docker: 162.242.195.82 + - fig: 50.31.209.229 +``` + +An entry with the ip address and hostname will be created in `/etc/hosts` inside containers for this service, e.g: + +``` +162.242.195.82 docker +50.31.209.229 fig +``` + ### ports Expose ports. Either specify both ports (`HOST:CONTAINER`), or just the container port (a random host port will be chosen). diff --git a/fig/service.py b/fig/service.py index 18ba3f618fb..307209555ad 100644 --- a/fig/service.py +++ b/fig/service.py @@ -27,6 +27,7 @@ 'entrypoint', 'env_file', 'environment', + 'extra_hosts', 'hostname', 'image', 'mem_limit', @@ -43,6 +44,7 @@ ] DOCKER_CONFIG_HINTS = { 'cpu_share' : 'cpu_shares', + 'add_host' : 'extra_hosts', 'link' : 'links', 'port' : 'ports', 'privilege' : 'privileged', @@ -58,6 +60,7 @@ 'dns', 'dns_search', 'env_file', + 'extra_hosts', 'net', 'privileged', 'restart', @@ -97,7 +100,6 @@ def __init__(self, name, client=None, project='default', links=None, external_li supported_options = DOCKER_CONFIG_KEYS + ['build', 'expose', 'external_links'] - for k in options: if k not in supported_options: msg = "Unsupported config option for %s service: '%s'" % (name, k) @@ -322,6 +324,8 @@ def start_container(self, container, intermediate_container=None, **override_opt restart = parse_restart_spec(options.get('restart', None)) + extra_hosts = build_extra_hosts(options.get('extra_hosts', None)) + container.start( links=self._get_links(link_to_self=options.get('one_off', False)), port_bindings=port_bindings, @@ -334,6 +338,7 @@ def start_container(self, container, intermediate_container=None, **override_opt restart_policy=restart, cap_add=cap_add, cap_drop=cap_drop, + extra_hosts=extra_hosts, ) return container @@ -629,6 +634,26 @@ def split_port(port): return internal_port, (external_ip, external_port or None) +def build_extra_hosts(extra_hosts_config): + if extra_hosts_config is None: + return None + + if isinstance(extra_hosts_config, basestring): + extra_hosts_config = [extra_hosts_config] + + extra_hosts_dict = {} + for extra_hosts_line in extra_hosts_config: + if isinstance(extra_hosts_line, dict): + # already interpreted as a dict (depends on pyyaml version) + extra_hosts_dict.update(extra_hosts_line) + else: + # not already interpreted as a dict + host, ip = extra_hosts_line.split(':') + extra_hosts_dict.update({host.strip(): ip.strip()}) + + return extra_hosts_dict + + def merge_environment(options): env = {} diff --git a/requirements.txt b/requirements.txt index 2ccdf59a258..a31a19ae9c6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ PyYAML==3.10 -docker-py==0.6.0 +docker-py==0.7.1 dockerpty==0.3.2 docopt==0.6.1 requests==2.2.1 diff --git a/setup.py b/setup.py index 4cf8e589d98..c378f3559e3 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ def find_version(*file_paths): 'requests >= 2.2.1, < 3', 'texttable >= 0.8.1, < 0.9', 'websocket-client >= 0.11.0, < 0.12', - 'docker-py >= 0.6.0, < 0.7', + 'docker-py >= 0.7.1, < 0.8', 'dockerpty >= 0.3.2, < 0.4', 'six >= 1.3.0, < 2', ] diff --git a/tests/integration/service_test.py b/tests/integration/service_test.py index d01d118ff25..fb9e5eb254d 100644 --- a/tests/integration/service_test.py +++ b/tests/integration/service_test.py @@ -5,6 +5,7 @@ from fig import Service from fig.service import CannotBeScaledError +from fig.service import build_extra_hosts from fig.container import Container from docker.errors import APIError from .testcases import DockerClientTestCase @@ -106,6 +107,42 @@ def test_create_container_with_cpu_shares(self): service.start_container(container) self.assertEqual(container.inspect()['Config']['CpuShares'], 73) + def test_build_extra_hosts(self): + # string + self.assertEqual(build_extra_hosts("www.example.com: 192.168.0.17"), + {'www.example.com': '192.168.0.17'}) + + # list of strings + self.assertEqual(build_extra_hosts( + ["www.example.com: 192.168.0.17"]), + {'www.example.com': '192.168.0.17'}) + self.assertEqual(build_extra_hosts( + ["www.example.com: 192.168.0.17", + "api.example.com: 192.168.0.18"]), + {'www.example.com': '192.168.0.17', + 'api.example.com': '192.168.0.18'}) + # list of dictionaries + self.assertEqual(build_extra_hosts( + [{'www.example.com': '192.168.0.17'}, + {'api.example.com': '192.168.0.18'} + ]), + {'www.example.com': '192.168.0.17', + 'api.example.com': '192.168.0.18'}) + + def test_create_container_with_extra_hosts_list(self): + extra_hosts = ['docker:162.242.195.82', 'fig:50.31.209.229'] + service = self.create_service('db', extra_hosts=extra_hosts) + container = service.create_container() + service.start_container(container) + self.assertEqual(container.get('HostConfig.ExtraHosts'), extra_hosts) + + def test_create_container_with_extra_hosts_string(self): + extra_hosts = 'docker:162.242.195.82' + service = self.create_service('db', extra_hosts=extra_hosts) + container = service.create_container() + service.start_container(container) + self.assertEqual(container.get('HostConfig.ExtraHosts'), [extra_hosts]) + def test_create_container_with_specified_volume(self): host_path = '/tmp/host-path' container_path = '/container-path'