Merge pull request #3102 from shin-/1477-a-modest-proposal
Add flag to up/down to remove orphaned containers
This commit is contained in:
commit
dd40331b44
6 changed files with 105 additions and 22 deletions
|
|
@ -252,13 +252,15 @@ class TopLevelCommand(object):
|
|||
Usage: down [options]
|
||||
|
||||
Options:
|
||||
--rmi type Remove images, type may be one of: 'all' to remove
|
||||
all images, or 'local' to remove only images that
|
||||
don't have an custom name set by the `image` field
|
||||
-v, --volumes Remove data volumes
|
||||
--rmi type Remove images, type may be one of: 'all' to remove
|
||||
all images, or 'local' to remove only images that
|
||||
don't have an custom name set by the `image` field
|
||||
-v, --volumes Remove data volumes
|
||||
--remove-orphans Remove containers for services not defined in
|
||||
the Compose file
|
||||
"""
|
||||
image_type = image_type_from_opt('--rmi', options['--rmi'])
|
||||
self.project.down(image_type, options['--volumes'])
|
||||
self.project.down(image_type, options['--volumes'], options['--remove-orphans'])
|
||||
|
||||
def events(self, options):
|
||||
"""
|
||||
|
|
@ -324,9 +326,9 @@ class TopLevelCommand(object):
|
|||
signals.set_signal_handler_to_shutdown()
|
||||
try:
|
||||
operation = ExecOperation(
|
||||
self.project.client,
|
||||
exec_id,
|
||||
interactive=tty,
|
||||
self.project.client,
|
||||
exec_id,
|
||||
interactive=tty,
|
||||
)
|
||||
pty = PseudoTerminal(self.project.client, operation)
|
||||
pty.start()
|
||||
|
|
@ -666,12 +668,15 @@ class TopLevelCommand(object):
|
|||
-t, --timeout TIMEOUT Use this timeout in seconds for container shutdown
|
||||
when attached or when containers are already
|
||||
running. (default: 10)
|
||||
--remove-orphans Remove containers for services not
|
||||
defined in the Compose file
|
||||
"""
|
||||
monochrome = options['--no-color']
|
||||
start_deps = not options['--no-deps']
|
||||
cascade_stop = options['--abort-on-container-exit']
|
||||
service_names = options['SERVICE']
|
||||
timeout = int(options.get('--timeout') or DEFAULT_TIMEOUT)
|
||||
remove_orphans = options['--remove-orphans']
|
||||
detached = options.get('-d')
|
||||
|
||||
if detached and cascade_stop:
|
||||
|
|
@ -684,7 +689,8 @@ class TopLevelCommand(object):
|
|||
strategy=convergence_strategy_from_opts(options),
|
||||
do_build=build_action_from_opts(options),
|
||||
timeout=timeout,
|
||||
detached=detached)
|
||||
detached=detached,
|
||||
remove_orphans=remove_orphans)
|
||||
|
||||
if detached:
|
||||
return
|
||||
|
|
|
|||
|
|
@ -252,9 +252,11 @@ class Project(object):
|
|||
def remove_stopped(self, service_names=None, **options):
|
||||
parallel.parallel_remove(self.containers(service_names, stopped=True), options)
|
||||
|
||||
def down(self, remove_image_type, include_volumes):
|
||||
def down(self, remove_image_type, include_volumes, remove_orphans=False):
|
||||
self.stop()
|
||||
self.find_orphan_containers(remove_orphans)
|
||||
self.remove_stopped(v=include_volumes)
|
||||
|
||||
self.networks.remove()
|
||||
|
||||
if include_volumes:
|
||||
|
|
@ -334,7 +336,8 @@ class Project(object):
|
|||
strategy=ConvergenceStrategy.changed,
|
||||
do_build=BuildAction.none,
|
||||
timeout=DEFAULT_TIMEOUT,
|
||||
detached=False):
|
||||
detached=False,
|
||||
remove_orphans=False):
|
||||
|
||||
self.initialize()
|
||||
services = self.get_services_without_duplicate(
|
||||
|
|
@ -346,6 +349,8 @@ class Project(object):
|
|||
for svc in services:
|
||||
svc.ensure_image_exists(do_build=do_build)
|
||||
|
||||
self.find_orphan_containers(remove_orphans)
|
||||
|
||||
def do(service):
|
||||
return service.execute_convergence_plan(
|
||||
plans[service.name],
|
||||
|
|
@ -402,23 +407,52 @@ class Project(object):
|
|||
for service in self.get_services(service_names, include_deps=False):
|
||||
service.pull(ignore_pull_failures)
|
||||
|
||||
def _labeled_containers(self, stopped=False, one_off=False):
|
||||
return list(filter(None, [
|
||||
Container.from_ps(self.client, container)
|
||||
for container in self.client.containers(
|
||||
all=stopped,
|
||||
filters={'label': self.labels(one_off=one_off)})])
|
||||
)
|
||||
|
||||
def containers(self, service_names=None, stopped=False, one_off=False):
|
||||
if service_names:
|
||||
self.validate_service_names(service_names)
|
||||
else:
|
||||
service_names = self.service_names
|
||||
|
||||
containers = list(filter(None, [
|
||||
Container.from_ps(self.client, container)
|
||||
for container in self.client.containers(
|
||||
all=stopped,
|
||||
filters={'label': self.labels(one_off=one_off)})]))
|
||||
containers = self._labeled_containers(stopped, one_off)
|
||||
|
||||
def matches_service_names(container):
|
||||
return container.labels.get(LABEL_SERVICE) in service_names
|
||||
|
||||
return [c for c in containers if matches_service_names(c)]
|
||||
|
||||
def find_orphan_containers(self, remove_orphans):
|
||||
def _find():
|
||||
containers = self._labeled_containers()
|
||||
for ctnr in containers:
|
||||
service_name = ctnr.labels.get(LABEL_SERVICE)
|
||||
if service_name not in self.service_names:
|
||||
yield ctnr
|
||||
orphans = list(_find())
|
||||
if not orphans:
|
||||
return
|
||||
if remove_orphans:
|
||||
for ctnr in orphans:
|
||||
log.info('Removing orphan container "{0}"'.format(ctnr.name))
|
||||
ctnr.kill()
|
||||
ctnr.remove(force=True)
|
||||
else:
|
||||
log.warning(
|
||||
'Found orphan containers ({0}) for this project. If '
|
||||
'you removed or renamed this service in your compose '
|
||||
'file, you can run this command with the '
|
||||
'--remove-orphans flag to clean it up.'.format(
|
||||
', '.join(["{}".format(ctnr.name) for ctnr in orphans])
|
||||
)
|
||||
)
|
||||
|
||||
def _inject_deps(self, acc, service):
|
||||
dep_names = service.get_dependency_names()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue