From a4710fa9e149dc570fc9588d0e18e85e9a388d94 Mon Sep 17 00:00:00 2001 From: Aanand Prasad Date: Fri, 20 Dec 2013 16:22:54 +0000 Subject: [PATCH] 'plum up' is now the special magic 'start' and 'stop' are now analogous to their Docker namesakes. --- plum/cli/main.py | 44 ++++++++++++++++++++---------------- plum/container.py | 9 ++++++-- plum/project.py | 24 ++++++++++++++++---- plum/service.py | 24 ++++++-------------- tests/project_test.py | 52 +++++++++++++++++++++++++++++++++++-------- tests/service_test.py | 35 ++++++++++++++--------------- 6 files changed, 119 insertions(+), 69 deletions(-) diff --git a/plum/cli/main.py b/plum/cli/main.py index b3e4f11e6..7819df61d 100644 --- a/plum/cli/main.py +++ b/plum/cli/main.py @@ -140,37 +140,43 @@ class TopLevelCommand(Command): service.start_container(container, ports=None) c.run() - def start(self, options): + def up(self, options): """ - Start all services + Create and start containers - Usage: start [options] + Usage: up [options] Options: -d Detached mode: Run containers in the background, print new container names """ - if options['-d']: - self.project.start() - return + detached = options['-d'] - running = [] - unstarted = [] + unstarted = self.project.create_containers() - for s in self.project.services: - if len(s.containers()) == 0: - unstarted.append((s, s.create_container())) - else: - running += s.containers(stopped=False) - - log_printer = LogPrinter(running + [c for (s, c) in unstarted]) + if not detached: + to_attach = self.project.containers() + [c for (s, c) in unstarted] + print "Attaching to", list_containers(to_attach) + log_printer = LogPrinter(to_attach, attach_params={'logs': True}) for (s, c) in unstarted: s.start_container(c) - try: - log_printer.run() - finally: - self.project.stop() + if detached: + for (s, c) in unstarted: + print c.name + else: + try: + log_printer.run() + finally: + self.project.kill_and_remove(unstarted) + + def start(self, options): + """ + Start all services + + Usage: start + """ + self.project.start() def stop(self, options): """ diff --git a/plum/container.py b/plum/container.py index 616952161..5e0254400 100644 --- a/plum/container.py +++ b/plum/container.py @@ -83,13 +83,18 @@ class Container(object): out[k] = v return out + @property + def is_running(self): + self.inspect_if_not_inspected() + return self.dictionary['State']['Running'] + def start(self, **options): log.info("Starting %s..." % self.name) return self.client.start(self.id, **options) - def stop(self): + def stop(self, **options): log.info("Stopping %s..." % self.name) - return self.client.stop(self.id) + return self.client.stop(self.id, **options) def kill(self): log.info("Killing %s..." % self.name) diff --git a/plum/project.py b/plum/project.py index 7a72e4a65..847616441 100644 --- a/plum/project.py +++ b/plum/project.py @@ -50,13 +50,29 @@ class Project(object): if service.name == name: return service - def start(self): + def create_containers(self): + """ + Returns a list of (service, container) tuples, + one for each service with no running containers. + """ + containers = [] for service in self.services: - service.start() + if len(service.containers()) == 0: + containers.append((service, service.create_container())) + return containers - def stop(self): + def kill_and_remove(self, tuples): + for (service, container) in tuples: + container.kill() + container.remove() + + def start(self, **options): for service in self.services: - service.stop() + service.start(**options) + + def stop(self, **options): + for service in self.services: + service.stop(**options) def containers(self, *args, **kwargs): l = [] diff --git a/plum/service.py b/plum/service.py index 0550a7c03..9caccc6ff 100644 --- a/plum/service.py +++ b/plum/service.py @@ -38,19 +38,14 @@ class Service(object): l.append(Container.from_ps(self.client, container)) return l - def start(self): - if len(self.containers()) == 0: - return self.start_container() + def start(self, **options): + for c in self.containers(stopped=True): + if not c.is_running: + self.start_container(c, **options) - def stop(self): - self.scale(0) - - def scale(self, num): - while len(self.containers()) < num: - self.start_container() - - while len(self.containers()) > num: - self.stop_container() + def stop(self, **options): + for c in self.containers(): + c.stop(**options) def create_container(self, one_off=False, **override_options): """ @@ -99,11 +94,6 @@ class Service(object): ) return container - def stop_container(self): - container = self.containers()[-1] - container.kill() - container.remove() - def next_container_name(self, one_off=False): bits = [self.project, self.name] if one_off: diff --git a/tests/project_test.py b/tests/project_test.py index aa56b4075..c982990ae 100644 --- a/tests/project_test.py +++ b/tests/project_test.py @@ -42,18 +42,52 @@ class ProjectTest(DockerClientTestCase): project = Project('test', [web], self.client) self.assertEqual(project.get_service('web'), web) + def test_up(self): + web = self.create_service('web') + db = self.create_service('db') + project = Project('test', [web, db], self.client) + + web.create_container() + + self.assertEqual(len(web.containers()), 0) + self.assertEqual(len(db.containers()), 0) + 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()), 0) + self.assertEqual(len(db.containers()), 0) + self.assertEqual(len(web.containers(stopped=True)), 2) + self.assertEqual(len(db.containers(stopped=True)), 1) + + project.kill_and_remove(unstarted) + + self.assertEqual(len(web.containers()), 0) + self.assertEqual(len(db.containers()), 0) + self.assertEqual(len(web.containers(stopped=True)), 1) + self.assertEqual(len(db.containers(stopped=True)), 0) + def test_start_stop(self): - project = Project('test', [ - self.create_service('web'), - self.create_service('db'), - ], self.client) + web = self.create_service('web') + db = self.create_service('db') + project = Project('test', [web, db], self.client) project.start() - self.assertEqual(len(project.get_service('web').containers()), 1) - self.assertEqual(len(project.get_service('db').containers()), 1) + self.assertEqual(len(web.containers()), 0) + self.assertEqual(len(db.containers()), 0) - project.stop() + web.create_container() + project.start() - self.assertEqual(len(project.get_service('web').containers()), 0) - self.assertEqual(len(project.get_service('db').containers()), 0) + self.assertEqual(len(web.containers()), 1) + self.assertEqual(len(db.containers()), 0) + + project.stop(timeout=1) + + self.assertEqual(len(web.containers()), 0) + self.assertEqual(len(db.containers()), 0) diff --git a/tests/service_test.py b/tests/service_test.py index 3f84664dc..774196302 100644 --- a/tests/service_test.py +++ b/tests/service_test.py @@ -26,13 +26,14 @@ class ServiceTest(DockerClientTestCase): foo = self.create_service('foo') bar = self.create_service('bar') - foo.start() + foo.start_container() self.assertEqual(len(foo.containers()), 1) self.assertEqual(foo.containers()[0].name, '/default_foo_1') self.assertEqual(len(bar.containers()), 0) - bar.scale(2) + bar.start_container() + bar.start_container() self.assertEqual(len(foo.containers()), 1) self.assertEqual(len(bar.containers()), 2) @@ -49,30 +50,28 @@ class ServiceTest(DockerClientTestCase): def test_project_is_added_to_container_name(self): service = self.create_service('web', project='myproject') - service.start() + service.start_container() self.assertEqual(service.containers()[0].name, '/myproject_web_1') - def test_up_scale_down(self): + def test_start_stop(self): service = self.create_service('scalingtest') + self.assertEqual(len(service.containers(stopped=True)), 0) + + service.create_container() self.assertEqual(len(service.containers()), 0) + self.assertEqual(len(service.containers(stopped=True)), 1) service.start() self.assertEqual(len(service.containers()), 1) + self.assertEqual(len(service.containers(stopped=True)), 1) - service.start() - self.assertEqual(len(service.containers()), 1) - - service.scale(2) - self.assertEqual(len(service.containers()), 2) - - service.scale(1) - self.assertEqual(len(service.containers()), 1) - - service.stop() + service.stop(timeout=1) self.assertEqual(len(service.containers()), 0) + self.assertEqual(len(service.containers(stopped=True)), 1) - service.stop() + service.stop(timeout=1) self.assertEqual(len(service.containers()), 0) + self.assertEqual(len(service.containers(stopped=True)), 1) def test_create_container_with_one_off(self): db = self.create_service('db') @@ -101,8 +100,8 @@ class ServiceTest(DockerClientTestCase): db.start_container() web.start_container() self.assertIn('default_db_1', web.containers()[0].links()) - db.stop() - web.stop() + db.stop(timeout=1) + web.stop(timeout=1) def test_start_container_builds_images(self): service = Service( @@ -110,7 +109,7 @@ class ServiceTest(DockerClientTestCase): client=self.client, build='tests/fixtures/simple-dockerfile', ) - container = service.start() + container = service.start_container() container.wait() self.assertIn('success', container.logs()) self.assertEqual(len(self.client.images(name='default_test')), 1)