mirror of https://github.com/docker/docs.git
Merge pull request #2547 from seguins/1125-docker-compose-create
Add docker-compose create command.
This commit is contained in:
commit
a2d2915a64
|
@ -130,6 +130,7 @@ class TopLevelCommand(DocoptCommand):
|
||||||
Commands:
|
Commands:
|
||||||
build Build or rebuild services
|
build Build or rebuild services
|
||||||
config Validate and view the compose file
|
config Validate and view the compose file
|
||||||
|
create Create services
|
||||||
help Get help on a command
|
help Get help on a command
|
||||||
kill Kill containers
|
kill Kill containers
|
||||||
logs View output from containers
|
logs View output from containers
|
||||||
|
@ -221,6 +222,27 @@ class TopLevelCommand(DocoptCommand):
|
||||||
indent=2,
|
indent=2,
|
||||||
width=80))
|
width=80))
|
||||||
|
|
||||||
|
def create(self, project, options):
|
||||||
|
"""
|
||||||
|
Creates containers for a service.
|
||||||
|
|
||||||
|
Usage: create [options] [SERVICE...]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--force-recreate Recreate containers even if their configuration and
|
||||||
|
image haven't changed. Incompatible with --no-recreate.
|
||||||
|
--no-recreate If containers already exist, don't recreate them.
|
||||||
|
Incompatible with --force-recreate.
|
||||||
|
--no-build Don't build an image, even if it's missing
|
||||||
|
"""
|
||||||
|
service_names = options['SERVICE']
|
||||||
|
|
||||||
|
project.create(
|
||||||
|
service_names=service_names,
|
||||||
|
strategy=convergence_strategy_from_opts(options),
|
||||||
|
do_build=not options['--no-build']
|
||||||
|
)
|
||||||
|
|
||||||
def help(self, project, options):
|
def help(self, project, options):
|
||||||
"""
|
"""
|
||||||
Get help on a command.
|
Get help on a command.
|
||||||
|
|
|
@ -123,6 +123,12 @@ class Project(object):
|
||||||
[uniques.append(s) for s in services if s not in uniques]
|
[uniques.append(s) for s in services if s not in uniques]
|
||||||
return uniques
|
return uniques
|
||||||
|
|
||||||
|
def get_services_without_duplicate(self, service_names=None, include_deps=False):
|
||||||
|
services = self.get_services(service_names, include_deps)
|
||||||
|
for service in services:
|
||||||
|
service.remove_duplicate_containers()
|
||||||
|
return services
|
||||||
|
|
||||||
def get_links(self, service_dict):
|
def get_links(self, service_dict):
|
||||||
links = []
|
links = []
|
||||||
if 'links' in service_dict:
|
if 'links' in service_dict:
|
||||||
|
@ -224,6 +230,14 @@ class Project(object):
|
||||||
else:
|
else:
|
||||||
log.info('%s uses an image, skipping' % service.name)
|
log.info('%s uses an image, skipping' % service.name)
|
||||||
|
|
||||||
|
def create(self, service_names=None, strategy=ConvergenceStrategy.changed, do_build=True):
|
||||||
|
services = self.get_services_without_duplicate(service_names, include_deps=True)
|
||||||
|
|
||||||
|
plans = self._get_convergence_plans(services, strategy)
|
||||||
|
|
||||||
|
for service in services:
|
||||||
|
service.execute_convergence_plan(plans[service.name], do_build, detached=True, start=False)
|
||||||
|
|
||||||
def up(self,
|
def up(self,
|
||||||
service_names=None,
|
service_names=None,
|
||||||
start_deps=True,
|
start_deps=True,
|
||||||
|
@ -232,10 +246,7 @@ class Project(object):
|
||||||
timeout=DEFAULT_TIMEOUT,
|
timeout=DEFAULT_TIMEOUT,
|
||||||
detached=False):
|
detached=False):
|
||||||
|
|
||||||
services = self.get_services(service_names, include_deps=start_deps)
|
services = self.get_services_without_duplicate(service_names, include_deps=start_deps)
|
||||||
|
|
||||||
for service in services:
|
|
||||||
service.remove_duplicate_containers()
|
|
||||||
|
|
||||||
plans = self._get_convergence_plans(services, strategy)
|
plans = self._get_convergence_plans(services, strategy)
|
||||||
|
|
||||||
|
|
|
@ -331,7 +331,8 @@ class Service(object):
|
||||||
plan,
|
plan,
|
||||||
do_build=True,
|
do_build=True,
|
||||||
timeout=DEFAULT_TIMEOUT,
|
timeout=DEFAULT_TIMEOUT,
|
||||||
detached=False):
|
detached=False,
|
||||||
|
start=True):
|
||||||
(action, containers) = plan
|
(action, containers) = plan
|
||||||
should_attach_logs = not detached
|
should_attach_logs = not detached
|
||||||
|
|
||||||
|
@ -341,6 +342,7 @@ class Service(object):
|
||||||
if should_attach_logs:
|
if should_attach_logs:
|
||||||
container.attach_log_stream()
|
container.attach_log_stream()
|
||||||
|
|
||||||
|
if start:
|
||||||
container.start()
|
container.start()
|
||||||
|
|
||||||
return [container]
|
return [container]
|
||||||
|
@ -351,12 +353,14 @@ class Service(object):
|
||||||
container,
|
container,
|
||||||
do_build=do_build,
|
do_build=do_build,
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
attach_logs=should_attach_logs
|
attach_logs=should_attach_logs,
|
||||||
|
start_new_container=start
|
||||||
)
|
)
|
||||||
for container in containers
|
for container in containers
|
||||||
]
|
]
|
||||||
|
|
||||||
elif action == 'start':
|
elif action == 'start':
|
||||||
|
if start:
|
||||||
for container in containers:
|
for container in containers:
|
||||||
self.start_container_if_stopped(container, attach_logs=should_attach_logs)
|
self.start_container_if_stopped(container, attach_logs=should_attach_logs)
|
||||||
|
|
||||||
|
@ -376,7 +380,8 @@ class Service(object):
|
||||||
container,
|
container,
|
||||||
do_build=False,
|
do_build=False,
|
||||||
timeout=DEFAULT_TIMEOUT,
|
timeout=DEFAULT_TIMEOUT,
|
||||||
attach_logs=False):
|
attach_logs=False,
|
||||||
|
start_new_container=True):
|
||||||
"""Recreate a container.
|
"""Recreate a container.
|
||||||
|
|
||||||
The original container is renamed to a temporary name so that data
|
The original container is renamed to a temporary name so that data
|
||||||
|
@ -395,6 +400,7 @@ class Service(object):
|
||||||
)
|
)
|
||||||
if attach_logs:
|
if attach_logs:
|
||||||
new_container.attach_log_stream()
|
new_container.attach_log_stream()
|
||||||
|
if start_new_container:
|
||||||
new_container.start()
|
new_container.start()
|
||||||
container.remove()
|
container.remove()
|
||||||
return new_container
|
return new_container
|
||||||
|
|
|
@ -264,6 +264,52 @@ class CLITestCase(DockerClientTestCase):
|
||||||
]
|
]
|
||||||
assert not containers
|
assert not containers
|
||||||
|
|
||||||
|
def test_create(self):
|
||||||
|
self.dispatch(['create'])
|
||||||
|
service = self.project.get_service('simple')
|
||||||
|
another = self.project.get_service('another')
|
||||||
|
self.assertEqual(len(service.containers()), 0)
|
||||||
|
self.assertEqual(len(another.containers()), 0)
|
||||||
|
self.assertEqual(len(service.containers(stopped=True)), 1)
|
||||||
|
self.assertEqual(len(another.containers(stopped=True)), 1)
|
||||||
|
|
||||||
|
def test_create_with_force_recreate(self):
|
||||||
|
self.dispatch(['create'], None)
|
||||||
|
service = self.project.get_service('simple')
|
||||||
|
self.assertEqual(len(service.containers()), 0)
|
||||||
|
self.assertEqual(len(service.containers(stopped=True)), 1)
|
||||||
|
|
||||||
|
old_ids = [c.id for c in service.containers(stopped=True)]
|
||||||
|
|
||||||
|
self.dispatch(['create', '--force-recreate'], None)
|
||||||
|
self.assertEqual(len(service.containers()), 0)
|
||||||
|
self.assertEqual(len(service.containers(stopped=True)), 1)
|
||||||
|
|
||||||
|
new_ids = [c.id for c in service.containers(stopped=True)]
|
||||||
|
|
||||||
|
self.assertNotEqual(old_ids, new_ids)
|
||||||
|
|
||||||
|
def test_create_with_no_recreate(self):
|
||||||
|
self.dispatch(['create'], None)
|
||||||
|
service = self.project.get_service('simple')
|
||||||
|
self.assertEqual(len(service.containers()), 0)
|
||||||
|
self.assertEqual(len(service.containers(stopped=True)), 1)
|
||||||
|
|
||||||
|
old_ids = [c.id for c in service.containers(stopped=True)]
|
||||||
|
|
||||||
|
self.dispatch(['create', '--no-recreate'], None)
|
||||||
|
self.assertEqual(len(service.containers()), 0)
|
||||||
|
self.assertEqual(len(service.containers(stopped=True)), 1)
|
||||||
|
|
||||||
|
new_ids = [c.id for c in service.containers(stopped=True)]
|
||||||
|
|
||||||
|
self.assertEqual(old_ids, new_ids)
|
||||||
|
|
||||||
|
def test_create_with_force_recreate_and_no_recreate(self):
|
||||||
|
self.dispatch(
|
||||||
|
['create', '--force-recreate', '--no-recreate'],
|
||||||
|
returncode=1)
|
||||||
|
|
||||||
def test_up_detached(self):
|
def test_up_detached(self):
|
||||||
self.dispatch(['up', '-d'])
|
self.dispatch(['up', '-d'])
|
||||||
service = self.project.get_service('simple')
|
service = self.project.get_service('simple')
|
||||||
|
|
|
@ -213,6 +213,71 @@ class ProjectTest(DockerClientTestCase):
|
||||||
project.remove_stopped()
|
project.remove_stopped()
|
||||||
self.assertEqual(len(project.containers(stopped=True)), 0)
|
self.assertEqual(len(project.containers(stopped=True)), 0)
|
||||||
|
|
||||||
|
def test_create(self):
|
||||||
|
web = self.create_service('web')
|
||||||
|
db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
|
||||||
|
project = Project('composetest', [web, db], self.client)
|
||||||
|
|
||||||
|
project.create(['db'])
|
||||||
|
self.assertEqual(len(project.containers()), 0)
|
||||||
|
self.assertEqual(len(project.containers(stopped=True)), 1)
|
||||||
|
self.assertEqual(len(db.containers()), 0)
|
||||||
|
self.assertEqual(len(db.containers(stopped=True)), 1)
|
||||||
|
self.assertEqual(len(web.containers(stopped=True)), 0)
|
||||||
|
|
||||||
|
def test_create_twice(self):
|
||||||
|
web = self.create_service('web')
|
||||||
|
db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
|
||||||
|
project = Project('composetest', [web, db], self.client)
|
||||||
|
|
||||||
|
project.create(['db', 'web'])
|
||||||
|
project.create(['db', 'web'])
|
||||||
|
self.assertEqual(len(project.containers()), 0)
|
||||||
|
self.assertEqual(len(project.containers(stopped=True)), 2)
|
||||||
|
self.assertEqual(len(db.containers()), 0)
|
||||||
|
self.assertEqual(len(db.containers(stopped=True)), 1)
|
||||||
|
self.assertEqual(len(web.containers()), 0)
|
||||||
|
self.assertEqual(len(web.containers(stopped=True)), 1)
|
||||||
|
|
||||||
|
def test_create_with_links(self):
|
||||||
|
db = self.create_service('db')
|
||||||
|
web = self.create_service('web', links=[(db, 'db')])
|
||||||
|
project = Project('composetest', [db, web], self.client)
|
||||||
|
|
||||||
|
project.create(['web'])
|
||||||
|
self.assertEqual(len(project.containers()), 0)
|
||||||
|
self.assertEqual(len(project.containers(stopped=True)), 2)
|
||||||
|
self.assertEqual(len(db.containers()), 0)
|
||||||
|
self.assertEqual(len(db.containers(stopped=True)), 1)
|
||||||
|
self.assertEqual(len(web.containers()), 0)
|
||||||
|
self.assertEqual(len(web.containers(stopped=True)), 1)
|
||||||
|
|
||||||
|
def test_create_strategy_always(self):
|
||||||
|
db = self.create_service('db')
|
||||||
|
project = Project('composetest', [db], self.client)
|
||||||
|
project.create(['db'])
|
||||||
|
old_id = project.containers(stopped=True)[0].id
|
||||||
|
|
||||||
|
project.create(['db'], strategy=ConvergenceStrategy.always)
|
||||||
|
self.assertEqual(len(project.containers()), 0)
|
||||||
|
self.assertEqual(len(project.containers(stopped=True)), 1)
|
||||||
|
|
||||||
|
db_container = project.containers(stopped=True)[0]
|
||||||
|
self.assertNotEqual(db_container.id, old_id)
|
||||||
|
|
||||||
|
def test_create_strategy_never(self):
|
||||||
|
db = self.create_service('db')
|
||||||
|
project = Project('composetest', [db], self.client)
|
||||||
|
project.create(['db'])
|
||||||
|
old_id = project.containers(stopped=True)[0].id
|
||||||
|
|
||||||
|
project.create(['db'], strategy=ConvergenceStrategy.never)
|
||||||
|
self.assertEqual(len(project.containers()), 0)
|
||||||
|
self.assertEqual(len(project.containers(stopped=True)), 1)
|
||||||
|
|
||||||
|
db_container = project.containers(stopped=True)[0]
|
||||||
|
self.assertEqual(db_container.id, old_id)
|
||||||
|
|
||||||
def test_project_up(self):
|
def test_project_up(self):
|
||||||
web = self.create_service('web')
|
web = self.create_service('web')
|
||||||
db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
|
db = self.create_service('db', volumes=[VolumeSpec.parse('/var/db')])
|
||||||
|
|
|
@ -339,6 +339,24 @@ class ServiceTest(DockerClientTestCase):
|
||||||
self.assertEqual(list(new_container.get('Volumes')), ['/data'])
|
self.assertEqual(list(new_container.get('Volumes')), ['/data'])
|
||||||
self.assertEqual(new_container.get('Volumes')['/data'], volume_path)
|
self.assertEqual(new_container.get('Volumes')['/data'], volume_path)
|
||||||
|
|
||||||
|
def test_execute_convergence_plan_without_start(self):
|
||||||
|
service = self.create_service(
|
||||||
|
'db',
|
||||||
|
build='tests/fixtures/dockerfile-with-volume'
|
||||||
|
)
|
||||||
|
|
||||||
|
containers = service.execute_convergence_plan(ConvergencePlan('create', []), start=False)
|
||||||
|
self.assertEqual(len(service.containers()), 0)
|
||||||
|
self.assertEqual(len(service.containers(stopped=True)), 1)
|
||||||
|
|
||||||
|
containers = service.execute_convergence_plan(ConvergencePlan('recreate', containers), start=False)
|
||||||
|
self.assertEqual(len(service.containers()), 0)
|
||||||
|
self.assertEqual(len(service.containers(stopped=True)), 1)
|
||||||
|
|
||||||
|
service.execute_convergence_plan(ConvergencePlan('start', containers), start=False)
|
||||||
|
self.assertEqual(len(service.containers()), 0)
|
||||||
|
self.assertEqual(len(service.containers(stopped=True)), 1)
|
||||||
|
|
||||||
def test_start_container_passes_through_options(self):
|
def test_start_container_passes_through_options(self):
|
||||||
db = self.create_service('db')
|
db = self.create_service('db')
|
||||||
create_and_start_container(db, environment={'FOO': 'BAR'})
|
create_and_start_container(db, environment={'FOO': 'BAR'})
|
||||||
|
|
Loading…
Reference in New Issue