Просмотр исходного кода

Add support for differentiating one-off containers

This is a basic start, the API is pretty shonky.
Ben Firshman 12 лет назад
Родитель
Сommit
2f28265d10
4 измененных файлов с 50 добавлено и 20 удалено
  1. 7 5
      plum/cli/main.py
  2. 8 0
      plum/container.py
  3. 24 15
      plum/service.py
  4. 11 0
      tests/service_test.py

+ 7 - 5
plum/cli/main.py

@@ -85,8 +85,10 @@ class TopLevelCommand(Command):
         Options:
             -q    Only display IDs
         """
+        containers = self.project.containers(stopped=True) + self.project.containers(one_off=True)
+
         if options['-q']:
-            for container in self.project.containers(all=True):
+            for container in containers:
                 print container.id
         else:
             headers = [
@@ -96,7 +98,7 @@ class TopLevelCommand(Command):
                 'Ports',
             ]
             rows = []
-            for container in self.project.containers(all=True):
+            for container in containers:
                 rows.append([
                     container.name,
                     container.human_readable_command,
@@ -117,7 +119,7 @@ class TopLevelCommand(Command):
         container_options = {
             'command': [options['COMMAND']] + options['ARGS'],
         }
-        container = service.create_container(**container_options)
+        container = service.create_container(one_off=True, **container_options)
         stream = container.logs(stream=True)
         service.start_container(container, ports=None)
         for data in stream:
@@ -142,7 +144,7 @@ class TopLevelCommand(Command):
             if len(s.containers()) == 0:
                 unstarted.append((s, s.create_container()))
             else:
-                running += s.containers(all=False)
+                running += s.containers(stopped=False)
 
         log_printer = LogPrinter(running + [c for (s, c) in unstarted])
 
@@ -168,7 +170,7 @@ class TopLevelCommand(Command):
 
         Usage: logs
         """
-        containers = self.project.containers(all=False)
+        containers = self.project.containers(stopped=False)
         print "Attaching to", list_containers(containers)
         LogPrinter(containers, attach_params={'logs': True}).run()
 

+ 8 - 0
plum/container.py

@@ -124,3 +124,11 @@ class Container(object):
 
     def attach_socket(self, **kwargs):
         return self.client.attach_socket(self.id, **kwargs)
+
+    def __repr__(self):
+        return '<Container: %s>' % self.name
+
+    def __eq__(self, other):
+        if type(self) != type(other):
+            return False
+        return self.id == other.id

+ 24 - 15
plum/service.py

@@ -27,11 +27,11 @@ class Service(object):
         self.links = links or []
         self.options = options
 
-    def containers(self, all=False):
+    def containers(self, stopped=False, one_off=False):
         l = []
-        for container in self.client.containers(all=all):
+        for container in self.client.containers(all=stopped):
             name = get_container_name(container)
-            if not is_valid_name(name):
+            if not is_valid_name(name, one_off):
                 continue
             project, name, number = parse_name(name)
             if project == self.project and name == self.name:
@@ -52,12 +52,12 @@ class Service(object):
         while len(self.containers()) > num:
             self.stop_container()
 
-    def create_container(self, **override_options):
+    def create_container(self, one_off=False, **override_options):
         """
         Create a container for this service. If the image doesn't exist, attempt to pull
         it.
         """
-        container_options = self._get_container_options(override_options)
+        container_options = self._get_container_options(override_options, one_off=one_off)
         try:
             return Container.create(self.client, **container_options)
         except APIError, e:
@@ -104,11 +104,14 @@ class Service(object):
         container.kill()
         container.remove()
 
-    def next_container_name(self):
-        return '%s_%s_%s' % (self.project, self.name, self.next_container_number())
+    def next_container_name(self, one_off=False):
+        bits = [self.project, self.name]
+        if one_off:
+            bits.append('run')
+        return '_'.join(bits + [unicode(self.next_container_number())])
 
     def next_container_number(self):
-        numbers = [parse_name(c.name)[2] for c in self.containers(all=True)]
+        numbers = [parse_name(c.name)[2] for c in self.containers(stopped=True)]
 
         if len(numbers) == 0:
             return 1
@@ -122,12 +125,12 @@ class Service(object):
                 links[container.name[1:]] = container.name[1:]
         return links
 
-    def _get_container_options(self, override_options):
+    def _get_container_options(self, override_options, one_off=False):
         keys = ['image', 'command', 'hostname', 'user', 'detach', 'stdin_open', 'tty', 'mem_limit', 'ports', 'environment', 'dns', 'volumes', 'volumes_from']
         container_options = dict((k, self.options[k]) for k in keys if k in self.options)
         container_options.update(override_options)
 
-        container_options['name'] = self.next_container_name()
+        container_options['name'] = self.next_container_name(one_off)
 
         if 'ports' in container_options:
             container_options['ports'] = [unicode(p).split(':')[0] for p in container_options['ports']]
@@ -160,16 +163,22 @@ class Service(object):
         return image_id
 
 
-NAME_RE = re.compile(r'^([^_]+)_([^_]+)_(\d+)$')
+NAME_RE = re.compile(r'^([^_]+)_([^_]+)_(run_)?(\d+)$')
 
 
-def is_valid_name(name):
-    return (NAME_RE.match(name) is not None)
+def is_valid_name(name, one_off=False):
+    match = NAME_RE.match(name)
+    if match is None:
+        return False
+    if one_off:
+        return match.group(3) == 'run_'
+    else:
+        return match.group(3) is None
 
 
-def parse_name(name):
+def parse_name(name, one_off=False):
     match = NAME_RE.match(name)
-    (project, service_name, suffix) = match.groups()
+    (project, service_name, _, suffix) = match.groups()
     return (project, service_name, int(suffix))
 
 

+ 11 - 0
tests/service_test.py

@@ -41,6 +41,12 @@ class ServiceTest(DockerClientTestCase):
         self.assertIn('/default_bar_1', names)
         self.assertIn('/default_bar_2', names)
 
+    def test_containers_one_off(self):
+        db = self.create_service('db')
+        container = db.create_container(one_off=True)
+        self.assertEqual(db.containers(stopped=True), [])
+        self.assertEqual(db.containers(one_off=True, stopped=True), [container])
+
     def test_project_is_added_to_container_name(self):
         service = self.create_service('web', project='myproject')
         service.start()
@@ -68,6 +74,11 @@ class ServiceTest(DockerClientTestCase):
         service.stop()
         self.assertEqual(len(service.containers()), 0)
 
+    def test_create_container_with_one_off(self):
+        db = self.create_service('db')
+        container = db.create_container(one_off=True)
+        self.assertEqual(container.name, '/default_db_run_1')
+
     def test_start_container_passes_through_options(self):
         db = self.create_service('db')
         db.start_container(environment={'FOO': 'BAR'})