浏览代码

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

Add --hash opt for config command
Joffrey F 7 年之前
父节点
当前提交
f207d94b3c

+ 1 - 0
.gitignore

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

+ 12 - 1
compose/cli/main.py

@@ -328,7 +328,9 @@ class TopLevelCommand(object):
                                      anything.
             --services               Print the service 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)
@@ -350,6 +352,15 @@ class TopLevelCommand(object):
             print('\n'.join(volume for volume in compose_config.volumes))
             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))
 
     def create(self, options):

+ 9 - 4
compose/network.py

@@ -323,7 +323,12 @@ def get_networks(service_dict, network_definitions):
                 'Service "{}" uses an undefined network "{}"'
                 .format(service_dict['name'], name))
 
-    return OrderedDict(sorted(
-        networks.items(),
-        key=lambda t: t[1].get('priority') or 0, reverse=True
-    ))
+    if any([v.get('priority') for v in networks.values()]):
+        return OrderedDict(sorted(
+            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]))

+ 7 - 1
compose/service.py

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

+ 1 - 1
contrib/completion/bash/docker-compose

@@ -136,7 +136,7 @@ _docker_compose_bundle() {
 
 
 _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" ) )
 }
 
 

+ 2 - 1
contrib/completion/zsh/_docker-compose

@@ -213,7 +213,8 @@ __docker-compose_subcommand() {
                 '(--quiet -q)'{--quiet,-q}"[Only validate the configuration, don't print anything.]" \
                 '--resolve-image-digests[Pin image tags to digests.]' \
                 '--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)
             _arguments \

+ 10 - 0
tests/acceptance/cli_test.py

@@ -222,6 +222,16 @@ class CLITestCase(DockerClientTestCase):
         self.base_dir = 'tests/fixtures/v2-full'
         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):
         self.base_dir = 'tests/fixtures/v2-full'
         result = self.dispatch(['config'])

+ 4 - 2
tests/unit/service_test.py

@@ -701,9 +701,11 @@ class ServiceTest(unittest.TestCase):
             image='example.com/foo',
             client=self.mock_client,
             network_mode=NetworkMode('bridge'),
-            networks={'bridge': {}},
+            networks={'bridge': {}, 'net2': {}},
             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