Skip to content

Commit

Permalink
Specify all HostConfig at create time
Browse files Browse the repository at this point in the history
- This is required for Swarm integration: the cluster needs to know
  about config like `links` and `volumes_from` at create time so that it
  can co-schedule containers.

- Because the ability to specify HostConfig at create time was
  introduced in Docker 1.4, that's our new minimum version requirement.
  The install docs and CHANGELOG have been updated accordingly.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
  • Loading branch information
aanand committed Mar 20, 2015
1 parent 9931cd2 commit d45980a
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 45 deletions.
4 changes: 3 additions & 1 deletion compose/cli/docker_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import ssl
import os

API_VERSION = '1.15'


def docker_client():
"""
Expand Down Expand Up @@ -32,4 +34,4 @@ def docker_client():
)

timeout = int(os.environ.get('DOCKER_CLIENT_TIMEOUT', 60))
return Client(base_url=base_url, tls=tls_config, version='1.14', timeout=timeout)
return Client(base_url=base_url, tls=tls_config, version=API_VERSION, timeout=timeout)
10 changes: 5 additions & 5 deletions compose/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,20 +328,20 @@ def run(self, project, options):
if options['--user']:
container_options['user'] = options.get('--user')

if not options['--service-ports']:
container_options['ports'] = []

container = service.create_container(
one_off=True,
insecure_registry=insecure_registry,
**container_options
)

service_ports = None
if options['--service-ports']:
service_ports = service.options['ports']
if options['-d']:
service.start_container(container, ports=service_ports, one_off=True)
service.start_container(container)
print(container.name)
else:
service.start_container(container, ports=service_ports, one_off=True)
service.start_container(container)
dockerpty.start(project.client, container.id, interactive=not options['-T'])
exit_code = container.wait()
if options['--rm']:
Expand Down
94 changes: 57 additions & 37 deletions compose/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import sys

from docker.errors import APIError
from docker.utils import create_host_config

from .config import DOCKER_CONFIG_KEYS
from .container import Container, get_container_name
Expand Down Expand Up @@ -168,14 +169,17 @@ def create_container(self,
one_off=False,
insecure_registry=False,
do_build=True,
intermediate_container=None,
**override_options):
"""
Create a container for this service. If the image doesn't exist, attempt to pull
it.
"""
container_options = self._get_container_create_options(
override_options,
one_off=one_off)
one_off=one_off,
intermediate_container=intermediate_container,
)

if (do_build and
self.can_be_built() and
Expand Down Expand Up @@ -240,56 +244,33 @@ def recreate_container(self, container, **override_options):
entrypoint=['/bin/echo'],
command=[],
detach=True,
host_config=create_host_config(volumes_from=[container.id]),
)
intermediate_container.start(volumes_from=container.id)
intermediate_container.start()
intermediate_container.wait()
container.remove()

options = dict(override_options)
new_container = self.create_container(do_build=False, **options)
self.start_container(new_container, intermediate_container=intermediate_container)
new_container = self.create_container(
do_build=False,
intermediate_container=intermediate_container,
**options
)
self.start_container(new_container)

intermediate_container.remove()

return (intermediate_container, new_container)

def start_container_if_stopped(self, container, **options):
def start_container_if_stopped(self, container):
if container.is_running:
return container
else:
log.info("Starting %s..." % container.name)
return self.start_container(container, **options)

def start_container(self, container, intermediate_container=None, **override_options):
options = dict(self.options, **override_options)
port_bindings = build_port_bindings(options.get('ports') or [])

volume_bindings = dict(
build_volume_binding(parse_volume_spec(volume))
for volume in options.get('volumes') or []
if ':' in volume)

privileged = options.get('privileged', False)
dns = options.get('dns', None)
dns_search = options.get('dns_search', None)
cap_add = options.get('cap_add', None)
cap_drop = options.get('cap_drop', None)

restart = parse_restart_spec(options.get('restart', None))
return self.start_container(container)

container.start(
links=self._get_links(link_to_self=options.get('one_off', False)),
port_bindings=port_bindings,
binds=volume_bindings,
volumes_from=self._get_volumes_from(intermediate_container),
privileged=privileged,
network_mode=self._get_net(),
dns=dns,
dns_search=dns_search,
restart_policy=restart,
cap_add=cap_add,
cap_drop=cap_drop,
)
def start_container(self, container):
container.start()
return container

def start_or_create_containers(
Expand Down Expand Up @@ -389,7 +370,7 @@ def _get_net(self):

return net

def _get_container_create_options(self, override_options, one_off=False):
def _get_container_create_options(self, override_options, one_off=False, intermediate_container=None):
container_options = dict(
(k, self.options[k])
for k in DOCKER_CONFIG_KEYS if k in self.options)
Expand Down Expand Up @@ -436,8 +417,47 @@ def _get_container_create_options(self, override_options, one_off=False):
for key in DOCKER_START_KEYS:
container_options.pop(key, None)

container_options['host_config'] = self._get_container_host_config(override_options, one_off=one_off, intermediate_container=intermediate_container)

return container_options

def _get_container_host_config(self, override_options, one_off=False, intermediate_container=None):
options = dict(self.options, **override_options)
port_bindings = build_port_bindings(options.get('ports') or [])

volume_bindings = dict(
build_volume_binding(parse_volume_spec(volume))
for volume in options.get('volumes') or []
if ':' in volume)

privileged = options.get('privileged', False)
cap_add = options.get('cap_add', None)
cap_drop = options.get('cap_drop', None)

dns = options.get('dns', None)
if not isinstance(dns, list):
dns = [dns]

dns_search = options.get('dns_search', None)
if not isinstance(dns_search, list):
dns_search = [dns_search]

restart = parse_restart_spec(options.get('restart', None))

return create_host_config(
links=self._get_links(link_to_self=one_off),
port_bindings=port_bindings,
binds=volume_bindings,
volumes_from=self._get_volumes_from(intermediate_container),
privileged=privileged,
network_mode=self._get_net(),
dns=dns,
dns_search=dns_search,
restart_policy=restart,
cap_add=cap_add,
cap_drop=cap_drop,
)

def _get_image_name(self, image):
repo, tag = parse_repository_tag(image)
if tag == "":
Expand Down
2 changes: 1 addition & 1 deletion docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Compose with a `curl` command.

### Install Docker

First, install Docker version 1.3 or greater:
First, install Docker version 1.4.1 or greater:

- [Instructions for Mac OS X](http://docs.docker.com/installation/mac/)
- [Instructions for Ubuntu](http://docs.docker.com/installation/ubuntulinux/)
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/service_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

def create_and_start_container(service, **override_options):
container = service.create_container(**override_options)
return service.start_container(container, **override_options)
return service.start_container(container)


class ServiceTest(DockerClientTestCase):
Expand Down

0 comments on commit d45980a

Please sign in to comment.