'plum up' is now the special magic

'start' and 'stop' are now analogous to their Docker namesakes.
This commit is contained in:
Aanand Prasad 2013-12-20 16:22:54 +00:00
parent 3bebd18de7
commit a4710fa9e1
6 changed files with 119 additions and 69 deletions

View File

@ -140,37 +140,43 @@ class TopLevelCommand(Command):
service.start_container(container, ports=None) service.start_container(container, ports=None)
c.run() c.run()
def start(self, options): def up(self, options):
""" """
Start all services Create and start containers
Usage: start [options] Usage: up [options]
Options: Options:
-d Detached mode: Run containers in the background, print new container names -d Detached mode: Run containers in the background, print new container names
""" """
if options['-d']: detached = options['-d']
self.project.start()
return
running = [] unstarted = self.project.create_containers()
unstarted = []
for s in self.project.services: if not detached:
if len(s.containers()) == 0: to_attach = self.project.containers() + [c for (s, c) in unstarted]
unstarted.append((s, s.create_container())) print "Attaching to", list_containers(to_attach)
else: log_printer = LogPrinter(to_attach, attach_params={'logs': True})
running += s.containers(stopped=False)
log_printer = LogPrinter(running + [c for (s, c) in unstarted])
for (s, c) in unstarted: for (s, c) in unstarted:
s.start_container(c) s.start_container(c)
try: if detached:
log_printer.run() for (s, c) in unstarted:
finally: print c.name
self.project.stop() 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): def stop(self, options):
""" """

View File

@ -83,13 +83,18 @@ class Container(object):
out[k] = v out[k] = v
return out return out
@property
def is_running(self):
self.inspect_if_not_inspected()
return self.dictionary['State']['Running']
def start(self, **options): def start(self, **options):
log.info("Starting %s..." % self.name) log.info("Starting %s..." % self.name)
return self.client.start(self.id, **options) return self.client.start(self.id, **options)
def stop(self): def stop(self, **options):
log.info("Stopping %s..." % self.name) log.info("Stopping %s..." % self.name)
return self.client.stop(self.id) return self.client.stop(self.id, **options)
def kill(self): def kill(self):
log.info("Killing %s..." % self.name) log.info("Killing %s..." % self.name)

View File

@ -50,13 +50,29 @@ class Project(object):
if service.name == name: if service.name == name:
return service 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: 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: 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): def containers(self, *args, **kwargs):
l = [] l = []

View File

@ -38,19 +38,14 @@ class Service(object):
l.append(Container.from_ps(self.client, container)) l.append(Container.from_ps(self.client, container))
return l return l
def start(self): def start(self, **options):
if len(self.containers()) == 0: for c in self.containers(stopped=True):
return self.start_container() if not c.is_running:
self.start_container(c, **options)
def stop(self): def stop(self, **options):
self.scale(0) for c in self.containers():
c.stop(**options)
def scale(self, num):
while len(self.containers()) < num:
self.start_container()
while len(self.containers()) > num:
self.stop_container()
def create_container(self, one_off=False, **override_options): def create_container(self, one_off=False, **override_options):
""" """
@ -99,11 +94,6 @@ class Service(object):
) )
return container return container
def stop_container(self):
container = self.containers()[-1]
container.kill()
container.remove()
def next_container_name(self, one_off=False): def next_container_name(self, one_off=False):
bits = [self.project, self.name] bits = [self.project, self.name]
if one_off: if one_off:

View File

@ -42,18 +42,52 @@ class ProjectTest(DockerClientTestCase):
project = Project('test', [web], self.client) project = Project('test', [web], self.client)
self.assertEqual(project.get_service('web'), web) 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): def test_start_stop(self):
project = Project('test', [ web = self.create_service('web')
self.create_service('web'), db = self.create_service('db')
self.create_service('db'), project = Project('test', [web, db], self.client)
], self.client)
project.start() project.start()
self.assertEqual(len(project.get_service('web').containers()), 1) self.assertEqual(len(web.containers()), 0)
self.assertEqual(len(project.get_service('db').containers()), 1) 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(web.containers()), 1)
self.assertEqual(len(project.get_service('db').containers()), 0) self.assertEqual(len(db.containers()), 0)
project.stop(timeout=1)
self.assertEqual(len(web.containers()), 0)
self.assertEqual(len(db.containers()), 0)

View File

@ -26,13 +26,14 @@ class ServiceTest(DockerClientTestCase):
foo = self.create_service('foo') foo = self.create_service('foo')
bar = self.create_service('bar') bar = self.create_service('bar')
foo.start() foo.start_container()
self.assertEqual(len(foo.containers()), 1) self.assertEqual(len(foo.containers()), 1)
self.assertEqual(foo.containers()[0].name, '/default_foo_1') self.assertEqual(foo.containers()[0].name, '/default_foo_1')
self.assertEqual(len(bar.containers()), 0) self.assertEqual(len(bar.containers()), 0)
bar.scale(2) bar.start_container()
bar.start_container()
self.assertEqual(len(foo.containers()), 1) self.assertEqual(len(foo.containers()), 1)
self.assertEqual(len(bar.containers()), 2) self.assertEqual(len(bar.containers()), 2)
@ -49,30 +50,28 @@ class ServiceTest(DockerClientTestCase):
def test_project_is_added_to_container_name(self): def test_project_is_added_to_container_name(self):
service = self.create_service('web', project='myproject') service = self.create_service('web', project='myproject')
service.start() service.start_container()
self.assertEqual(service.containers()[0].name, '/myproject_web_1') 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') 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()), 0)
self.assertEqual(len(service.containers(stopped=True)), 1)
service.start() service.start()
self.assertEqual(len(service.containers()), 1) self.assertEqual(len(service.containers()), 1)
self.assertEqual(len(service.containers(stopped=True)), 1)
service.start() service.stop(timeout=1)
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()
self.assertEqual(len(service.containers()), 0) 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()), 0)
self.assertEqual(len(service.containers(stopped=True)), 1)
def test_create_container_with_one_off(self): def test_create_container_with_one_off(self):
db = self.create_service('db') db = self.create_service('db')
@ -101,8 +100,8 @@ class ServiceTest(DockerClientTestCase):
db.start_container() db.start_container()
web.start_container() web.start_container()
self.assertIn('default_db_1', web.containers()[0].links()) self.assertIn('default_db_1', web.containers()[0].links())
db.stop() db.stop(timeout=1)
web.stop() web.stop(timeout=1)
def test_start_container_builds_images(self): def test_start_container_builds_images(self):
service = Service( service = Service(
@ -110,7 +109,7 @@ class ServiceTest(DockerClientTestCase):
client=self.client, client=self.client,
build='tests/fixtures/simple-dockerfile', build='tests/fixtures/simple-dockerfile',
) )
container = service.start() container = service.start_container()
container.wait() container.wait()
self.assertIn('success', container.logs()) self.assertIn('success', container.logs())
self.assertEqual(len(self.client.images(name='default_test')), 1) self.assertEqual(len(self.client.images(name='default_test')), 1)