From d3346fa174897498b5f86c9c4a8fadd4e961d345 Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Fri, 20 Dec 2013 18:30:23 +0000 Subject: [PATCH] up, start, stop, kill and rm all accept a list of services --- plum/cli/main.py | 28 +++++++++++----------- plum/project.py | 54 +++++++++++++++++++++++++++++++++---------- tests/project_test.py | 49 +++++++++++++++++++++++++++++++-------- 3 files changed, 97 insertions(+), 34 deletions(-) diff --git a/plum/cli/main.py b/plum/cli/main.py index fe03dbd3cd..0c844040f0 100644 --- a/plum/cli/main.py +++ b/plum/cli/main.py @@ -5,6 +5,7 @@ import re from inspect import getdoc from .. import __version__ +from ..project import NoSuchService from .command import Command from .formatter import Formatter from .log_printer import LogPrinter @@ -37,6 +38,9 @@ def main(): except UserError, e: log.error(e.msg) exit(1) + except NoSuchService, e: + log.error(e.msg) + exit(1) except NoSuchCommand, e: log.error("No such command: %s", e.command) log.error("") @@ -121,8 +125,6 @@ class TopLevelCommand(Command): -d Detached mode: Run container in the background, print new container name """ service = self.project.get_service(options['SERVICE']) - if service is None: - raise UserError("No such service: %s" % options['SERVICE']) container_options = { 'command': [options['COMMAND']] + options['ARGS'], 'tty': not options['-d'], @@ -146,17 +148,17 @@ class TopLevelCommand(Command): """ Create and start containers - Usage: up [options] + Usage: up [options] [SERVICE...] Options: -d Detached mode: Run containers in the background, print new container names """ detached = options['-d'] - unstarted = self.project.create_containers() + unstarted = self.project.create_containers(service_names=options['SERVICE']) 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) log_printer = LogPrinter(to_attach, attach_params={'logs': True}) @@ -176,33 +178,33 @@ class TopLevelCommand(Command): """ Start all services - Usage: start + Usage: start [SERVICE...] """ - self.project.start() + self.project.start(service_names=options['SERVICE']) def stop(self, options): """ Stop all services - Usage: stop + Usage: stop [SERVICE...] """ - self.project.stop() + self.project.stop(service_names=options['SERVICE']) def kill(self, options): """ Kill all containers - Usage: kill + Usage: kill [SERVICE...] """ - self.project.kill() + self.project.kill(service_names=options['SERVICE']) def rm(self, options): """ 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): """ diff --git a/plum/project.py b/plum/project.py index b9fbaabd4b..52a050d232 100644 --- a/plum/project.py +++ b/plum/project.py @@ -46,17 +46,40 @@ class Project(object): return cls.from_dicts(name, dicts, client) def get_service(self, name): + """ + Retrieve a service by name. Raises NoSuchService + if the named service does not exist. + """ for service in self.services: if service.name == name: 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, one for each service with no running containers. """ containers = [] - for service in self.services: + for service in self.get_services(service_names): if len(service.containers()) == 0: containers.append((service, service.create_container())) return containers @@ -66,27 +89,34 @@ class Project(object): container.kill() container.remove() - def start(self, **options): - for service in self.services: + def start(self, service_names=None, **options): + for service in self.get_services(service_names): service.start(**options) - def stop(self, **options): - for service in self.services: + def stop(self, service_names=None, **options): + for service in self.get_services(service_names): service.stop(**options) - def kill(self, **options): - for service in self.services: + def kill(self, service_names=None, **options): + for service in self.get_services(service_names): service.kill(**options) - def remove_stopped(self, **options): - for service in self.services: + def remove_stopped(self, service_names=None, **options): + for service in self.get_services(service_names): service.remove_stopped(**options) - def containers(self, *args, **kwargs): + def containers(self, service_names=None, *args, **kwargs): l = [] - for service in self.services: + for service in self.get_services(service_names): for container in service.containers(*args, **kwargs): l.append(container) 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 diff --git a/tests/project_test.py b/tests/project_test.py index c982990aeb..e96923dd5f 100644 --- a/tests/project_test.py +++ b/tests/project_test.py @@ -42,11 +42,30 @@ class ProjectTest(DockerClientTestCase): project = Project('test', [web], self.client) self.assertEqual(project.get_service('web'), web) - def test_up(self): + def test_create_containers(self): web = self.create_service('web') db = self.create_service('db') 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() self.assertEqual(len(web.containers()), 0) @@ -54,7 +73,7 @@ class ProjectTest(DockerClientTestCase): self.assertEqual(len(web.containers(stopped=True)), 1) 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(unstarted[0][0], web) self.assertEqual(unstarted[1][0], db) @@ -71,7 +90,7 @@ class ProjectTest(DockerClientTestCase): self.assertEqual(len(web.containers(stopped=True)), 1) 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') db = self.create_service('db') project = Project('test', [web, db], self.client) @@ -81,13 +100,25 @@ class ProjectTest(DockerClientTestCase): self.assertEqual(len(web.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() + 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) - self.assertEqual(len(db.containers()), 0) + project.stop(service_names=['web'], timeout=1) + 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) - self.assertEqual(len(db.containers()), 0) + project.remove_stopped(service_names=['web']) + self.assertEqual(len(project.containers(stopped=True)), 1) + + project.remove_stopped() + self.assertEqual(len(project.containers(stopped=True)), 0)