up, start, stop, kill and rm all accept a list of services

This commit is contained in:
Aanand Prasad 2013-12-20 18:30:23 +00:00
parent 81093627fe
commit d3346fa174
3 changed files with 97 additions and 34 deletions

View File

@ -5,6 +5,7 @@ import re
from inspect import getdoc from inspect import getdoc
from .. import __version__ from .. import __version__
from ..project import NoSuchService
from .command import Command from .command import Command
from .formatter import Formatter from .formatter import Formatter
from .log_printer import LogPrinter from .log_printer import LogPrinter
@ -37,6 +38,9 @@ def main():
except UserError, e: except UserError, e:
log.error(e.msg) log.error(e.msg)
exit(1) exit(1)
except NoSuchService, e:
log.error(e.msg)
exit(1)
except NoSuchCommand, e: except NoSuchCommand, e:
log.error("No such command: %s", e.command) log.error("No such command: %s", e.command)
log.error("") log.error("")
@ -121,8 +125,6 @@ class TopLevelCommand(Command):
-d Detached mode: Run container in the background, print new container name -d Detached mode: Run container in the background, print new container name
""" """
service = self.project.get_service(options['SERVICE']) service = self.project.get_service(options['SERVICE'])
if service is None:
raise UserError("No such service: %s" % options['SERVICE'])
container_options = { container_options = {
'command': [options['COMMAND']] + options['ARGS'], 'command': [options['COMMAND']] + options['ARGS'],
'tty': not options['-d'], 'tty': not options['-d'],
@ -146,17 +148,17 @@ class TopLevelCommand(Command):
""" """
Create and start containers Create and start containers
Usage: up [options] Usage: up [options] [SERVICE...]
Options: Options:
-d Detached mode: Run containers in the background, print new container names -d Detached mode: Run containers in the background, print new container names
""" """
detached = options['-d'] detached = options['-d']
unstarted = self.project.create_containers() unstarted = self.project.create_containers(service_names=options['SERVICE'])
if not detached: if not detached:
to_attach = self.project.containers() + [c for (s, c) in unstarted] to_attach = self.project.containers(service_names=options['SERVICE']) + [c for (s, c) in unstarted]
print "Attaching to", list_containers(to_attach) print "Attaching to", list_containers(to_attach)
log_printer = LogPrinter(to_attach, attach_params={'logs': True}) log_printer = LogPrinter(to_attach, attach_params={'logs': True})
@ -176,33 +178,33 @@ class TopLevelCommand(Command):
""" """
Start all services Start all services
Usage: start Usage: start [SERVICE...]
""" """
self.project.start() self.project.start(service_names=options['SERVICE'])
def stop(self, options): def stop(self, options):
""" """
Stop all services Stop all services
Usage: stop Usage: stop [SERVICE...]
""" """
self.project.stop() self.project.stop(service_names=options['SERVICE'])
def kill(self, options): def kill(self, options):
""" """
Kill all containers Kill all containers
Usage: kill Usage: kill [SERVICE...]
""" """
self.project.kill() self.project.kill(service_names=options['SERVICE'])
def rm(self, options): def rm(self, options):
""" """
Remove all stopped containers Remove all stopped containers
Usage: rm Usage: rm [SERVICE...]
""" """
self.project.remove_stopped() self.project.remove_stopped(service_names=options['SERVICE'])
def logs(self, options): def logs(self, options):
""" """

View File

@ -46,17 +46,40 @@ class Project(object):
return cls.from_dicts(name, dicts, client) return cls.from_dicts(name, dicts, client)
def get_service(self, name): def get_service(self, name):
"""
Retrieve a service by name. Raises NoSuchService
if the named service does not exist.
"""
for service in self.services: for service in self.services:
if service.name == name: if service.name == name:
return service return service
def create_containers(self): raise NoSuchService(name)
def get_services(self, service_names=None):
"""
Returns a list of this project's services filtered
by the provided list of names, or all services if
service_names is None or [].
Preserves the original order of self.services.
Raises NoSuchService if any of the named services
do not exist.
"""
if service_names is None or len(service_names) == 0:
return self.services
else:
unsorted = [self.get_service(name) for name in service_names]
return [s for s in self.services if s in unsorted]
def create_containers(self, service_names=None):
""" """
Returns a list of (service, container) tuples, Returns a list of (service, container) tuples,
one for each service with no running containers. one for each service with no running containers.
""" """
containers = [] containers = []
for service in self.services: for service in self.get_services(service_names):
if len(service.containers()) == 0: if len(service.containers()) == 0:
containers.append((service, service.create_container())) containers.append((service, service.create_container()))
return containers return containers
@ -66,27 +89,34 @@ class Project(object):
container.kill() container.kill()
container.remove() container.remove()
def start(self, **options): def start(self, service_names=None, **options):
for service in self.services: for service in self.get_services(service_names):
service.start(**options) service.start(**options)
def stop(self, **options): def stop(self, service_names=None, **options):
for service in self.services: for service in self.get_services(service_names):
service.stop(**options) service.stop(**options)
def kill(self, **options): def kill(self, service_names=None, **options):
for service in self.services: for service in self.get_services(service_names):
service.kill(**options) service.kill(**options)
def remove_stopped(self, **options): def remove_stopped(self, service_names=None, **options):
for service in self.services: for service in self.get_services(service_names):
service.remove_stopped(**options) service.remove_stopped(**options)
def containers(self, *args, **kwargs): def containers(self, service_names=None, *args, **kwargs):
l = [] l = []
for service in self.services: for service in self.get_services(service_names):
for container in service.containers(*args, **kwargs): for container in service.containers(*args, **kwargs):
l.append(container) l.append(container)
return l return l
class NoSuchService(Exception):
def __init__(self, name):
self.name = name
self.msg = "No such service: %s" % self.name
def __str__(self):
return self.msg

View File

@ -42,11 +42,30 @@ class ProjectTest(DockerClientTestCase):
project = Project('test', [web], self.client) project = Project('test', [web], self.client)
self.assertEqual(project.get_service('web'), web) self.assertEqual(project.get_service('web'), web)
def test_up(self): def test_create_containers(self):
web = self.create_service('web') web = self.create_service('web')
db = self.create_service('db') db = self.create_service('db')
project = Project('test', [web, db], self.client) project = Project('test', [web, db], self.client)
unstarted = project.create_containers(service_names=['web'])
self.assertEqual(len(unstarted), 1)
self.assertEqual(unstarted[0][0], web)
self.assertEqual(len(web.containers(stopped=True)), 1)
self.assertEqual(len(db.containers(stopped=True)), 0)
unstarted = project.create_containers()
self.assertEqual(len(unstarted), 2)
self.assertEqual(unstarted[0][0], web)
self.assertEqual(unstarted[1][0], db)
self.assertEqual(len(web.containers(stopped=True)), 2)
self.assertEqual(len(db.containers(stopped=True)), 1)
def test_up(self):
web = self.create_service('web')
db = self.create_service('db')
other = self.create_service('other')
project = Project('test', [web, db, other], self.client)
web.create_container() web.create_container()
self.assertEqual(len(web.containers()), 0) self.assertEqual(len(web.containers()), 0)
@ -54,7 +73,7 @@ class ProjectTest(DockerClientTestCase):
self.assertEqual(len(web.containers(stopped=True)), 1) self.assertEqual(len(web.containers(stopped=True)), 1)
self.assertEqual(len(db.containers(stopped=True)), 0) self.assertEqual(len(db.containers(stopped=True)), 0)
unstarted = project.create_containers() unstarted = project.create_containers(service_names=['web', 'db'])
self.assertEqual(len(unstarted), 2) self.assertEqual(len(unstarted), 2)
self.assertEqual(unstarted[0][0], web) self.assertEqual(unstarted[0][0], web)
self.assertEqual(unstarted[1][0], db) self.assertEqual(unstarted[1][0], db)
@ -71,7 +90,7 @@ class ProjectTest(DockerClientTestCase):
self.assertEqual(len(web.containers(stopped=True)), 1) self.assertEqual(len(web.containers(stopped=True)), 1)
self.assertEqual(len(db.containers(stopped=True)), 0) self.assertEqual(len(db.containers(stopped=True)), 0)
def test_start_stop(self): def test_start_stop_kill_remove(self):
web = self.create_service('web') web = self.create_service('web')
db = self.create_service('db') db = self.create_service('db')
project = Project('test', [web, db], self.client) project = Project('test', [web, db], self.client)
@ -81,13 +100,25 @@ class ProjectTest(DockerClientTestCase):
self.assertEqual(len(web.containers()), 0) self.assertEqual(len(web.containers()), 0)
self.assertEqual(len(db.containers()), 0) self.assertEqual(len(db.containers()), 0)
web.create_container() web_container_1 = web.create_container()
web_container_2 = web.create_container()
db_container = db.create_container()
project.start(service_names=['web'])
self.assertEqual(set(c.name for c in project.containers()), set([web_container_1.name, web_container_2.name]))
project.start() project.start()
self.assertEqual(set(c.name for c in project.containers()), set([web_container_1.name, web_container_2.name, db_container.name]))
self.assertEqual(len(web.containers()), 1) project.stop(service_names=['web'], timeout=1)
self.assertEqual(len(db.containers()), 0) self.assertEqual(set(c.name for c in project.containers()), set([db_container.name]))
project.stop(timeout=1) project.kill(service_names=['db'])
self.assertEqual(len(project.containers()), 0)
self.assertEqual(len(project.containers(stopped=True)), 3)
self.assertEqual(len(web.containers()), 0) project.remove_stopped(service_names=['web'])
self.assertEqual(len(db.containers()), 0) self.assertEqual(len(project.containers(stopped=True)), 1)
project.remove_stopped()
self.assertEqual(len(project.containers(stopped=True)), 0)