mirror of
https://github.com/docker/compose.git
synced 2025-07-06 13:24:25 +02:00
Implement --filter flag for docker-compose config --services.
Fix #1498 Signed-off-by: Svyatoslav Ilinskiy <ilinskiy.sv@gmail.com>
This commit is contained in:
parent
d1633d8e9d
commit
21e312e402
@ -287,7 +287,7 @@ class TopLevelCommand(object):
|
|||||||
"""
|
"""
|
||||||
Validate and view the Compose file.
|
Validate and view the Compose file.
|
||||||
|
|
||||||
Usage: config [options]
|
Usage: config [options] [-f KEY=VAL...]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--resolve-image-digests Pin image tags to digests.
|
--resolve-image-digests Pin image tags to digests.
|
||||||
@ -295,6 +295,7 @@ class TopLevelCommand(object):
|
|||||||
anything.
|
anything.
|
||||||
--services Print the service names, one per line.
|
--services Print the service names, one per line.
|
||||||
--volumes Print the volume names, one per line.
|
--volumes Print the volume names, one per line.
|
||||||
|
-f, --filter KEY=VAL Filter containers by a property (can be used multiple times)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -309,7 +310,15 @@ class TopLevelCommand(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if options['--services']:
|
if options['--services']:
|
||||||
print('\n'.join(service['name'] for service in compose_config.services))
|
filters = build_filters(options.get('--filter'))
|
||||||
|
if filters:
|
||||||
|
if not self.project:
|
||||||
|
self.project = project_from_options('.', config_options)
|
||||||
|
services = filter_services(filters, self.project.services, self.project)
|
||||||
|
else:
|
||||||
|
services = [service['name'] for service in compose_config.services]
|
||||||
|
|
||||||
|
print('\n'.join(services))
|
||||||
return
|
return
|
||||||
|
|
||||||
if options['--volumes']:
|
if options['--volumes']:
|
||||||
@ -1312,3 +1321,51 @@ def build_exec_command(options, container_id, command):
|
|||||||
args += [container_id]
|
args += [container_id]
|
||||||
args += command
|
args += command
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def has_container_with_state(containers, state):
|
||||||
|
for container in containers:
|
||||||
|
states = {
|
||||||
|
'running': container.is_running,
|
||||||
|
'stopped': not container.is_running,
|
||||||
|
'paused': container.is_paused,
|
||||||
|
}
|
||||||
|
if state not in states:
|
||||||
|
raise UserError("Invalid state: %s" % state)
|
||||||
|
if states[state]:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def filter_services(filters, services, project):
|
||||||
|
def should_include(service):
|
||||||
|
for f in filters:
|
||||||
|
if f == 'status':
|
||||||
|
containers = project.containers([service.name], stopped=True)
|
||||||
|
for status in filters[f]:
|
||||||
|
if not has_container_with_state(containers, status):
|
||||||
|
return False
|
||||||
|
elif f == 'option':
|
||||||
|
for option in filters[f]:
|
||||||
|
if option == 'image' or option == 'build':
|
||||||
|
if option not in service.options:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
raise UserError("Invalid option: %s" % option)
|
||||||
|
else:
|
||||||
|
raise UserError("Invalid filter: %s" % f)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return [s.name for s in services if should_include(s)]
|
||||||
|
|
||||||
|
|
||||||
|
def build_filters(args):
|
||||||
|
filters = {}
|
||||||
|
for arg in args:
|
||||||
|
if '=' not in arg:
|
||||||
|
raise UserError("Arguments to --filter should be in form KEY=VAL")
|
||||||
|
key, val = arg.split('=', 1)
|
||||||
|
if key not in filters:
|
||||||
|
filters[key] = []
|
||||||
|
filters[key].append(val)
|
||||||
|
return filters
|
||||||
|
@ -440,6 +440,32 @@ class CLITestCase(DockerClientTestCase):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def test_config_services_filter_option(self):
|
||||||
|
self.base_dir = 'tests/fixtures/config-services-filter'
|
||||||
|
image = self.dispatch(['config', '--services', '--filter', 'option=image'])
|
||||||
|
build = self.dispatch(['config', '--services', '--filter', 'option=build'])
|
||||||
|
|
||||||
|
self.assertIn('with_build', build.stdout)
|
||||||
|
self.assertNotIn('with_build', image.stdout)
|
||||||
|
self.assertIn('with_image', image.stdout)
|
||||||
|
self.assertNotIn('with_image', build.stdout)
|
||||||
|
|
||||||
|
def test_config_services_filter_status(self):
|
||||||
|
self.base_dir = 'tests/fixtures/config-services-filter'
|
||||||
|
self.dispatch(['up', '-d'])
|
||||||
|
self.dispatch(['pause', 'with_image'])
|
||||||
|
paused = self.dispatch(['config', '--services', '--filter', 'status=paused'])
|
||||||
|
stopped = self.dispatch(['config', '--services', '--filter', 'status=stopped'])
|
||||||
|
running = self.dispatch(['config', '--services', '--filter', 'status=running',
|
||||||
|
'--filter', 'option=build'])
|
||||||
|
|
||||||
|
self.assertNotIn('with_build', stopped.stdout)
|
||||||
|
self.assertNotIn('with_image', stopped.stdout)
|
||||||
|
self.assertNotIn('with_build', paused.stdout)
|
||||||
|
self.assertIn('with_image', paused.stdout)
|
||||||
|
self.assertIn('with_build', running.stdout)
|
||||||
|
self.assertNotIn('with_image', running.stdout)
|
||||||
|
|
||||||
def test_ps(self):
|
def test_ps(self):
|
||||||
self.project.get_service('simple').create_container()
|
self.project.get_service('simple').create_container()
|
||||||
result = self.dispatch(['ps'])
|
result = self.dispatch(['ps'])
|
||||||
|
6
tests/fixtures/config-services-filter/docker-compose.yml
vendored
Normal file
6
tests/fixtures/config-services-filter/docker-compose.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
with_image:
|
||||||
|
image: busybox:latest
|
||||||
|
command: top
|
||||||
|
with_build:
|
||||||
|
build: ../build-ctx/
|
||||||
|
command: top
|
Loading…
x
Reference in New Issue
Block a user