Adds pause and unpause-commands

Signed-off-by: Frank Sachsenheim <funkyfuture@riseup.net>
This commit is contained in:
Frank Sachsenheim 2015-07-25 22:20:58 +02:00
parent 59d024f4f2
commit 227584b864
10 changed files with 147 additions and 4 deletions

View File

@ -172,6 +172,14 @@ class TopLevelCommand(Command):
print("Attaching to", list_containers(containers))
LogPrinter(containers, attach_params={'logs': True}, monochrome=monochrome).run()
def pause(self, project, options):
"""
Pause services.
Usage: pause [SERVICE...]
"""
project.pause(service_names=options['SERVICE'])
def port(self, project, options):
"""
Print the public port for a port binding.
@ -444,6 +452,14 @@ class TopLevelCommand(Command):
timeout = int(options.get('--timeout') or DEFAULT_TIMEOUT)
project.restart(service_names=options['SERVICE'], timeout=timeout)
def unpause(self, project, options):
"""
Unpause services.
Usage: unpause [SERVICE...]
"""
project.unpause(service_names=options['SERVICE'])
def up(self, project, options):
"""
Builds, (re)creates, starts, and attaches to containers for a service.

View File

@ -100,6 +100,8 @@ class Container(object):
@property
def human_readable_state(self):
if self.is_paused:
return 'Paused'
if self.is_running:
return 'Ghost' if self.get('State.Ghost') else 'Up'
else:
@ -119,6 +121,10 @@ class Container(object):
def is_running(self):
return self.get('State.Running')
@property
def is_paused(self):
return self.get('State.Paused')
def get(self, key):
"""Return a value from the container or None if the value is not set.
@ -142,6 +148,12 @@ class Container(object):
def stop(self, **options):
return self.client.stop(self.id, **options)
def pause(self, **options):
return self.client.pause(self.id, **options)
def unpause(self, **options):
return self.client.unpause(self.id, **options)
def kill(self, **options):
return self.client.kill(self.id, **options)

View File

@ -205,6 +205,14 @@ class Project(object):
msg="Stopping"
)
def pause(self, service_names=None, **options):
for service in reversed(self.get_services(service_names)):
service.pause(**options)
def unpause(self, service_names=None, **options):
for service in self.get_services(service_names):
service.unpause(**options)
def kill(self, service_names=None, **options):
parallel_execute(
objects=self.containers(service_names),

View File

@ -96,12 +96,14 @@ class Service(object):
self.net = net or None
self.options = options
def containers(self, stopped=False, one_off=False):
def containers(self, stopped=False, one_off=False, filters={}):
filters.update({'label': self.labels(one_off=one_off)})
containers = filter(None, [
Container.from_ps(self.client, container)
for container in self.client.containers(
all=stopped,
filters={'label': self.labels(one_off=one_off)})])
filters=filters)])
if not containers:
check_for_legacy_containers(
@ -132,6 +134,16 @@ class Service(object):
log.info("Stopping %s..." % c.name)
c.stop(**options)
def pause(self, **options):
for c in self.containers(filters={'status': 'running'}):
log.info("Pausing %s..." % c.name)
c.pause(**options)
def unpause(self, **options):
for c in self.containers(filters={'status': 'paused'}):
log.info("Unpausing %s..." % c.name)
c.unpause()
def kill(self, **options):
for c in self.containers():
log.info("Killing %s..." % c.name)

View File

@ -68,6 +68,11 @@ __docker_compose_services_with() {
COMPREPLY=( $(compgen -W "${names[*]}" -- "$cur") )
}
# The services for which at least one paused container exists
__docker_compose_services_paused() {
__docker_compose_services_with '.State.Paused'
}
# The services for which at least one running container exists
__docker_compose_services_running() {
__docker_compose_services_with '.State.Running'
@ -158,6 +163,18 @@ _docker_compose_migrate_to_labels() {
}
_docker_compose_pause() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
;;
*)
__docker_compose_services_running
;;
esac
}
_docker_compose_port() {
case "$prev" in
--protocol)
@ -306,6 +323,18 @@ _docker_compose_stop() {
}
_docker_compose_unpause() {
case "$cur" in
-*)
COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
;;
*)
__docker_compose_services_paused
;;
esac
}
_docker_compose_up() {
case "$prev" in
-t | --timeout)
@ -343,6 +372,7 @@ _docker_compose() {
kill
logs
migrate-to-labels
pause
port
ps
pull
@ -352,6 +382,7 @@ _docker_compose() {
scale
start
stop
unpause
up
version
)

View File

@ -28,6 +28,7 @@ Commands:
help Get help on a command
kill Kill containers
logs View output from containers
pause Pause services
port Print the public port for a port binding
ps List containers
pull Pulls service images
@ -37,6 +38,7 @@ Commands:
scale Set number of containers for a service
start Start services
stop Stop services
unpause Unpause services
up Create and start containers
migrate-to-labels Recreate containers to add labels
```

18
docs/reference/pause.md Normal file
View File

@ -0,0 +1,18 @@
<!--[metadata]>
+++
title = "pause"
description = "Pauses running containers for a service."
keywords = ["fig, composition, compose, docker, orchestration, cli, pause"]
[menu.main]
identifier="pause.compose"
parent = "smn_compose_cli"
+++
<![end-metadata]-->
# pause
```
Usage: pause [SERVICE...]
```
Pauses running containers of a service. They can be unpaused with `docker-compose unpause`.

18
docs/reference/unpause.md Normal file
View File

@ -0,0 +1,18 @@
<!--[metadata]>
+++
title = "unpause"
description = "Unpauses paused containers for a service."
keywords = ["fig, composition, compose, docker, orchestration, cli, unpause"]
[menu.main]
identifier="unpause.compose"
parent = "smn_compose_cli"
+++
<![end-metadata]-->
# pause
```
Usage: unpause [SERVICE...]
```
Unpauses paused containers of a service.

View File

@ -415,6 +415,17 @@ class CLITestCase(DockerClientTestCase):
self.assertEqual(len(service.containers(stopped=True)), 1)
self.assertFalse(service.containers(stopped=True)[0].is_running)
def test_pause_unpause(self):
self.command.dispatch(['up', '-d'], None)
service = self.project.get_service('simple')
self.assertFalse(service.containers()[0].is_paused)
self.command.dispatch(['pause'], None)
self.assertTrue(service.containers()[0].is_paused)
self.command.dispatch(['unpause'], None)
self.assertFalse(service.containers()[0].is_paused)
def test_logs_invalid_service_name(self):
with self.assertRaises(NoSuchService):
self.command.dispatch(['logs', 'madeupname'], None)

View File

@ -140,7 +140,7 @@ class ProjectTest(DockerClientTestCase):
web = project.get_service('web')
self.assertEqual(web._get_net(), 'container:' + net_container.id)
def test_start_stop_kill_remove(self):
def test_start_pause_unpause_stop_kill_remove(self):
web = self.create_service('web')
db = self.create_service('db')
project = Project('composetest', [web, db], self.client)
@ -158,7 +158,22 @@ class ProjectTest(DockerClientTestCase):
self.assertEqual(set(c.name for c in project.containers()), set([web_container_1.name, web_container_2.name]))
project.start()
self.assertEqual(set(c.name for c in project.containers()), set([web_container_1.name, web_container_2.name, db_container.name]))
self.assertEqual(set(c.name for c in project.containers()),
set([web_container_1.name, web_container_2.name, db_container.name]))
project.pause(service_names=['web'])
self.assertEqual(set([c.name for c in project.containers() if c.is_paused]),
set([web_container_1.name, web_container_2.name]))
project.pause()
self.assertEqual(set([c.name for c in project.containers() if c.is_paused]),
set([web_container_1.name, web_container_2.name, db_container.name]))
project.unpause(service_names=['db'])
self.assertEqual(len([c.name for c in project.containers() if c.is_paused]), 2)
project.unpause()
self.assertEqual(len([c.name for c in project.containers() if c.is_paused]), 0)
project.stop(service_names=['web'], timeout=1)
self.assertEqual(set(c.name for c in project.containers()), set([db_container.name]))