Bladeren bron

'plum up' is now the special magic

'start' and 'stop' are now analogous to their Docker namesakes.
Aanand Prasad 12 jaren geleden
bovenliggende
commit
a4710fa9e1
6 gewijzigde bestanden met toevoegingen van 119 en 69 verwijderingen
  1. 25 19
      plum/cli/main.py
  2. 7 2
      plum/container.py
  3. 20 4
      plum/project.py
  4. 7 17
      plum/service.py
  5. 43 9
      tests/project_test.py
  6. 17 18
      tests/service_test.py

+ 25 - 19
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):
         """

+ 7 - 2
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)

+ 20 - 4
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:
+            if len(service.containers()) == 0:
+                containers.append((service, service.create_container()))
+        return containers
+
+    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.start()
+            service.start(**options)
 
-    def stop(self):
+    def stop(self, **options):
         for service in self.services:
-            service.stop()
+            service.stop(**options)
 
     def containers(self, *args, **kwargs):
         l = []

+ 7 - 17
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:

+ 43 - 9
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(web.containers()), 0)
+        self.assertEqual(len(db.containers()), 0)
 
+        web.create_container()
         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()), 1)
+        self.assertEqual(len(db.containers()), 0)
 
-        project.stop()
+        project.stop(timeout=1)
 
-        self.assertEqual(len(project.get_service('web').containers()), 0)
-        self.assertEqual(len(project.get_service('db').containers()), 0)
+        self.assertEqual(len(web.containers()), 0)
+        self.assertEqual(len(db.containers()), 0)

+ 17 - 18
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()), 0)
+        self.assertEqual(len(service.containers(stopped=True)), 0)
 
-        service.start()
-        self.assertEqual(len(service.containers()), 1)
+        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.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)