mirror of
				https://github.com/docker/compose.git
				synced 2025-10-31 11:14:02 +01:00 
			
		
		
		
	Merge pull request #5384 from ilinum/1498-docker-compose-services
Implement --filter flag for docker-compose config --services and use it in bash completion
This commit is contained in:
		
						commit
						c4fda0834d
					
				| @ -619,11 +619,24 @@ class TopLevelCommand(object): | ||||
|         """ | ||||
|         List containers. | ||||
| 
 | ||||
|         Usage: ps [options] [SERVICE...] | ||||
|         Usage: ps [options] [--filter KEY=VAL] [SERVICE...] | ||||
| 
 | ||||
|         Options: | ||||
|             -q                   Only display IDs | ||||
|             --services           Display services | ||||
|             --filter KEY=VAL     Filter services by a property | ||||
|         """ | ||||
|         if options['-q'] and options['--services']: | ||||
|             raise UserError('-q and --services cannot be combined') | ||||
| 
 | ||||
|         if options['--services']: | ||||
|             filt = build_filter(options.get('--filter')) | ||||
|             services = self.project.services | ||||
|             if filt: | ||||
|                 services = filter_services(filt, services, self.project) | ||||
|             print('\n'.join(service.name for service in services)) | ||||
|             return | ||||
| 
 | ||||
|         containers = sorted( | ||||
|             self.project.containers(service_names=options['SERVICE'], stopped=True) + | ||||
|             self.project.containers(service_names=options['SERVICE'], one_off=OneOffFilter.only), | ||||
| @ -1352,3 +1365,49 @@ def build_exec_command(options, container_id, command): | ||||
|     args += [container_id] | ||||
|     args += command | ||||
|     return args | ||||
| 
 | ||||
| 
 | ||||
| def has_container_with_state(containers, state): | ||||
|     states = { | ||||
|         'running': lambda c: c.is_running, | ||||
|         'stopped': lambda c: not c.is_running, | ||||
|         'paused': lambda c: c.is_paused, | ||||
|         'restarting': lambda c: c.is_restarting, | ||||
|     } | ||||
|     for container in containers: | ||||
|         if state not in states: | ||||
|             raise UserError("Invalid state: %s" % state) | ||||
|         if states[state](container): | ||||
|             return True | ||||
| 
 | ||||
| 
 | ||||
| def filter_services(filt, services, project): | ||||
|     def should_include(service): | ||||
|         for f in filt: | ||||
|             if f == 'status': | ||||
|                 state = filt[f] | ||||
|                 containers = project.containers([service.name], stopped=True) | ||||
|                 if not has_container_with_state(containers, state): | ||||
|                     return False | ||||
|             elif f == 'source': | ||||
|                 source = filt[f] | ||||
|                 if source == 'image' or source == 'build': | ||||
|                     if source not in service.options: | ||||
|                         return False | ||||
|                 else: | ||||
|                     raise UserError("Invalid value for source filter: %s" % source) | ||||
|             else: | ||||
|                 raise UserError("Invalid filter: %s" % f) | ||||
|         return True | ||||
| 
 | ||||
|     return filter(should_include, services) | ||||
| 
 | ||||
| 
 | ||||
| def build_filter(arg): | ||||
|     filt = {} | ||||
|     if arg is not None: | ||||
|         if '=' not in arg: | ||||
|             raise UserError("Arguments to --filter should be in form KEY=VAL") | ||||
|         key, val = arg.split('=', 1) | ||||
|         filt[key] = val | ||||
|     return filt | ||||
|  | ||||
| @ -64,48 +64,32 @@ __docker_compose_services_all() { | ||||
| 	COMPREPLY=( $(compgen -W "$(___docker_compose_all_services_in_compose_file)" -- "$cur") ) | ||||
| } | ||||
| 
 | ||||
| # All services that have an entry with the given key in their compose_file section | ||||
| ___docker_compose_services_with_key() { | ||||
| 	# flatten sections under "services" to one line, then filter lines containing the key and return section name | ||||
| 	__docker_compose_q config \ | ||||
| 		| sed -n -e '/^services:/,/^[^ ]/p' \ | ||||
| 		| sed -n 's/^  //p' \ | ||||
| 		| awk '/^[a-zA-Z0-9]/{printf "\n"};{printf $0;next;}' \ | ||||
| 		| awk -F: -v key=": +$1:" '$0 ~ key {print $1}' | ||||
| } | ||||
| 
 | ||||
| # All services that are defined by a Dockerfile reference | ||||
| __docker_compose_services_from_build() { | ||||
| 	COMPREPLY=( $(compgen -W "$(___docker_compose_services_with_key build)" -- "$cur") ) | ||||
| 	COMPREPLY=( $(compgen -W "$(__docker_compose_q ps --services --filter "source=build")" -- "$cur") ) | ||||
| } | ||||
| 
 | ||||
| # All services that are defined by an image | ||||
| __docker_compose_services_from_image() { | ||||
| 	COMPREPLY=( $(compgen -W "$(___docker_compose_services_with_key image)" -- "$cur") ) | ||||
| } | ||||
| 
 | ||||
| # The services for which containers have been created, optionally filtered | ||||
| # by a boolean expression passed in as argument. | ||||
| __docker_compose_services_with() { | ||||
| 	local containers names | ||||
| 	containers="$(__docker_compose_q ps -q)" | ||||
| 	names=$(docker 2>/dev/null inspect -f "{{if ${1:-true}}}{{range \$k, \$v := .Config.Labels}}{{if eq \$k \"com.docker.compose.service\"}}{{\$v}}{{end}}{{end}}{{end}}" $containers) | ||||
| 	COMPREPLY=( $(compgen -W "$names" -- "$cur") ) | ||||
| 	COMPREPLY=( $(compgen -W "$(__docker_compose_q ps --services --filter "source=image")" -- "$cur") ) | ||||
| } | ||||
| 
 | ||||
| # The services for which at least one paused container exists | ||||
| __docker_compose_services_paused() { | ||||
| 	__docker_compose_services_with '.State.Paused' | ||||
| 	names=$(__docker_compose_q ps --services --filter "status=paused") | ||||
| 	COMPREPLY=( $(compgen -W "$names" -- "$cur") ) | ||||
| } | ||||
| 
 | ||||
| # The services for which at least one running container exists | ||||
| __docker_compose_services_running() { | ||||
| 	__docker_compose_services_with '.State.Running' | ||||
| 	names=$(__docker_compose_q ps --services --filter "status=running") | ||||
| 	COMPREPLY=( $(compgen -W "$names" -- "$cur") ) | ||||
| } | ||||
| 
 | ||||
| # The services for which at least one stopped container exists | ||||
| __docker_compose_services_stopped() { | ||||
| 	__docker_compose_services_with 'not .State.Running' | ||||
| 	names=$(__docker_compose_q ps --services --filter "status=stopped") | ||||
| 	COMPREPLY=( $(compgen -W "$names" -- "$cur") ) | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -491,6 +491,34 @@ class CLITestCase(DockerClientTestCase): | ||||
|         assert 'multiplecomposefiles_another_1' not in result.stdout | ||||
|         assert 'multiplecomposefiles_yetanother_1' in result.stdout | ||||
| 
 | ||||
|     def test_ps_services_filter_option(self): | ||||
|         self.base_dir = 'tests/fixtures/ps-services-filter' | ||||
|         image = self.dispatch(['ps', '--services', '--filter', 'source=image']) | ||||
|         build = self.dispatch(['ps', '--services', '--filter', 'source=build']) | ||||
|         all_services = self.dispatch(['ps', '--services']) | ||||
| 
 | ||||
|         assert 'with_build' in all_services.stdout | ||||
|         assert 'with_image' in all_services.stdout | ||||
|         assert 'with_build' in build.stdout | ||||
|         assert 'with_build' not in image.stdout | ||||
|         assert 'with_image' in image.stdout | ||||
|         assert 'with_image' not in build.stdout | ||||
| 
 | ||||
|     def test_ps_services_filter_status(self): | ||||
|         self.base_dir = 'tests/fixtures/ps-services-filter' | ||||
|         self.dispatch(['up', '-d']) | ||||
|         self.dispatch(['pause', 'with_image']) | ||||
|         paused = self.dispatch(['ps', '--services', '--filter', 'status=paused']) | ||||
|         stopped = self.dispatch(['ps', '--services', '--filter', 'status=stopped']) | ||||
|         running = self.dispatch(['ps', '--services', '--filter', 'status=running']) | ||||
| 
 | ||||
|         assert 'with_build' not in stopped.stdout | ||||
|         assert 'with_image' not in stopped.stdout | ||||
|         assert 'with_build' not in paused.stdout | ||||
|         assert 'with_image' in paused.stdout | ||||
|         assert 'with_build' in running.stdout | ||||
|         assert 'with_image' in running.stdout | ||||
| 
 | ||||
|     def test_pull(self): | ||||
|         result = self.dispatch(['pull']) | ||||
|         assert sorted(result.stderr.split('\n'))[1:] == [ | ||||
|  | ||||
							
								
								
									
										6
									
								
								tests/fixtures/ps-services-filter/docker-compose.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								tests/fixtures/ps-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