Merge pull request #6126 from docker/wfender-2013-expose-config-hash

Add --hash opt for config command
This commit is contained in:
Joffrey F 2018-08-07 21:12:48 -07:00 committed by GitHub
commit f207d94b3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 46 additions and 10 deletions

1
.gitignore vendored
View File

@ -13,3 +13,4 @@ compose/GITSHA
*.swp *.swp
.DS_Store .DS_Store
.cache .cache
.idea

View File

@ -328,7 +328,9 @@ 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.
--hash="*" Print the service config hash, one per line.
Set "service1,service2" for a list of specified services
or use the wildcard symbol to display all services
""" """
compose_config = get_config_from_options(self.project_dir, self.toplevel_options) compose_config = get_config_from_options(self.project_dir, self.toplevel_options)
@ -350,6 +352,15 @@ class TopLevelCommand(object):
print('\n'.join(volume for volume in compose_config.volumes)) print('\n'.join(volume for volume in compose_config.volumes))
return return
if options['--hash'] is not None:
h = options['--hash']
self.project = project_from_options('.', self.toplevel_options)
services = [svc for svc in options['--hash'].split(',')] if h != '*' else None
with errors.handle_connection_errors(self.project.client):
for service in self.project.get_services(services):
print('{} {}'.format(service.name, service.config_hash))
return
print(serialize_config(compose_config, image_digests)) print(serialize_config(compose_config, image_digests))
def create(self, options): def create(self, options):

View File

@ -323,7 +323,12 @@ def get_networks(service_dict, network_definitions):
'Service "{}" uses an undefined network "{}"' 'Service "{}" uses an undefined network "{}"'
.format(service_dict['name'], name)) .format(service_dict['name'], name))
return OrderedDict(sorted( if any([v.get('priority') for v in networks.values()]):
networks.items(), return OrderedDict(sorted(
key=lambda t: t[1].get('priority') or 0, reverse=True networks.items(),
)) key=lambda t: t[1].get('priority') or 0, reverse=True
))
else:
# Ensure Compose will pick a consistent primary network if no
# priority is set
return OrderedDict(sorted(networks.items(), key=lambda t: t[0]))

View File

@ -656,9 +656,15 @@ class Service(object):
return json_hash(self.config_dict()) return json_hash(self.config_dict())
def config_dict(self): def config_dict(self):
def image_id():
try:
return self.image()['Id']
except NoSuchImageError:
return None
return { return {
'options': self.options, 'options': self.options,
'image_id': self.image()['Id'], 'image_id': image_id(),
'links': self.get_link_names(), 'links': self.get_link_names(),
'net': self.network_mode.id, 'net': self.network_mode.id,
'networks': self.networks, 'networks': self.networks,

View File

@ -136,7 +136,7 @@ _docker_compose_bundle() {
_docker_compose_config() { _docker_compose_config() {
COMPREPLY=( $( compgen -W "--help --quiet -q --resolve-image-digests --services --volumes" -- "$cur" ) ) COMPREPLY=( $( compgen -W "--help --quiet -q --resolve-image-digests --services --volumes --hash" -- "$cur" ) )
} }

View File

@ -213,7 +213,8 @@ __docker-compose_subcommand() {
'(--quiet -q)'{--quiet,-q}"[Only validate the configuration, don't print anything.]" \ '(--quiet -q)'{--quiet,-q}"[Only validate the configuration, don't print anything.]" \
'--resolve-image-digests[Pin image tags to digests.]' \ '--resolve-image-digests[Pin image tags to digests.]' \
'--services[Print the service names, one per line.]' \ '--services[Print the service names, one per line.]' \
'--volumes[Print the volume names, one per line.]' && ret=0 '--volumes[Print the volume names, one per line.]' \
'--hash[Print the service config hash, one per line. Set "service1,service2" for a list of specified services.]' \ && ret=0
;; ;;
(create) (create)
_arguments \ _arguments \

View File

@ -222,6 +222,16 @@ class CLITestCase(DockerClientTestCase):
self.base_dir = 'tests/fixtures/v2-full' self.base_dir = 'tests/fixtures/v2-full'
assert self.dispatch(['config', '--quiet']).stdout == '' assert self.dispatch(['config', '--quiet']).stdout == ''
def test_config_with_hash_option(self):
self.base_dir = 'tests/fixtures/v2-full'
result = self.dispatch(['config', '--hash=*'])
for service in self.project.get_services():
assert '{} {}\n'.format(service.name, service.config_hash) in result.stdout
svc = self.project.get_service('other')
result = self.dispatch(['config', '--hash=other'])
assert result.stdout == '{} {}\n'.format(svc.name, svc.config_hash)
def test_config_default(self): def test_config_default(self):
self.base_dir = 'tests/fixtures/v2-full' self.base_dir = 'tests/fixtures/v2-full'
result = self.dispatch(['config']) result = self.dispatch(['config'])

View File

@ -701,9 +701,11 @@ class ServiceTest(unittest.TestCase):
image='example.com/foo', image='example.com/foo',
client=self.mock_client, client=self.mock_client,
network_mode=NetworkMode('bridge'), network_mode=NetworkMode('bridge'),
networks={'bridge': {}}, networks={'bridge': {}, 'net2': {}},
links=[(Service('one', client=self.mock_client), 'one')], links=[(Service('one', client=self.mock_client), 'one')],
volumes_from=[VolumeFromSpec(Service('two', client=self.mock_client), 'rw', 'service')] volumes_from=[VolumeFromSpec(Service('two', client=self.mock_client), 'rw', 'service')],
volumes=[VolumeSpec('/ext', '/int', 'ro')],
build={'context': 'some/random/path'},
) )
config_hash = service.config_hash config_hash = service.config_hash