diff --git a/plum/cli/main.py b/plum/cli/main.py index 05b90c8ff..0fd68656f 100644 --- a/plum/cli/main.py +++ b/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() diff --git a/plum/container.py b/plum/container.py index 0bf6fccd2..616952161 100644 --- a/plum/container.py +++ b/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 '' % self.name + + def __eq__(self, other): + if type(self) != type(other): + return False + return self.id == other.id diff --git a/plum/service.py b/plum/service.py index de1d54409..486015e1d 100644 --- a/plum/service.py +++ b/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 parse_name(name): +def is_valid_name(name, one_off=False): match = NAME_RE.match(name) - (project, service_name, suffix) = match.groups() + 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, one_off=False): + match = NAME_RE.match(name) + (project, service_name, _, suffix) = match.groups() return (project, service_name, int(suffix)) diff --git a/tests/service_test.py b/tests/service_test.py index 7ad890213..c09e17e36 100644 --- a/tests/service_test.py +++ b/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'})