Browse Source

Merge branch 'cuckoohello-5360-add-runtime-support'

Joffrey F 7 years ago
parent
commit
ddbc9e30b0

+ 1 - 0
compose/config/config.py

@@ -98,6 +98,7 @@ DOCKER_CONFIG_KEYS = [
     'privileged',
     'read_only',
     'restart',
+    'runtime',
     'secrets',
     'security_opt',
     'shm_size',

+ 1 - 0
compose/config/config_schema_v2.3.json

@@ -261,6 +261,7 @@
         "privileged": {"type": "boolean"},
         "read_only": {"type": "boolean"},
         "restart": {"type": "string"},
+        "runtime": {"type": "string"},
         "scale": {"type": "integer"},
         "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true},
         "shm_size": {"type": ["number", "string"]},

+ 2 - 0
compose/service.py

@@ -87,6 +87,7 @@ HOST_CONFIG_KEYS = [
     'pids_limit',
     'privileged',
     'restart',
+    'runtime',
     'security_opt',
     'shm_size',
     'storage_opt',
@@ -878,6 +879,7 @@ class Service(object):
             dns_opt=options.get('dns_opt'),
             dns_search=options.get('dns_search'),
             restart_policy=options.get('restart'),
+            runtime=options.get('runtime'),
             cap_add=options.get('cap_add'),
             cap_drop=options.get('cap_drop'),
             mem_limit=options.get('mem_limit'),

+ 68 - 5
tests/integration/project_test.py

@@ -22,6 +22,7 @@ from compose.config.types import VolumeSpec
 from compose.const import COMPOSEFILE_V2_0 as V2_0
 from compose.const import COMPOSEFILE_V2_1 as V2_1
 from compose.const import COMPOSEFILE_V2_2 as V2_2
+from compose.const import COMPOSEFILE_V2_3 as V2_3
 from compose.const import COMPOSEFILE_V3_1 as V3_1
 from compose.const import LABEL_PROJECT
 from compose.const import LABEL_SERVICE
@@ -31,6 +32,7 @@ from compose.errors import NoHealthCheckConfigured
 from compose.project import Project
 from compose.project import ProjectError
 from compose.service import ConvergenceStrategy
+from tests.integration.testcases import if_runtime_available
 from tests.integration.testcases import is_cluster
 from tests.integration.testcases import no_cluster
 from tests.integration.testcases import v2_1_only
@@ -832,11 +834,11 @@ class ProjectTest(DockerClientTestCase):
 
         service_container = project.get_service('web').containers()[0]
 
-        IPAMConfig = (service_container.inspect().get('NetworkSettings', {}).
-                      get('Networks', {}).get('composetest_static_test', {}).
-                      get('IPAMConfig', {}))
-        assert IPAMConfig.get('IPv4Address') == '172.16.100.100'
-        assert IPAMConfig.get('IPv6Address') == 'fe80::1001:102'
+        ipam_config = (service_container.inspect().get('NetworkSettings', {}).
+                       get('Networks', {}).get('composetest_static_test', {}).
+                       get('IPAMConfig', {}))
+        assert ipam_config.get('IPv4Address') == '172.16.100.100'
+        assert ipam_config.get('IPv6Address') == 'fe80::1001:102'
 
     @v2_1_only()
     def test_up_with_enable_ipv6(self):
@@ -1029,6 +1031,67 @@ class ProjectTest(DockerClientTestCase):
         with self.assertRaises(ProjectError):
             project.up()
 
+    @v2_3_only()
+    @if_runtime_available('runc')
+    def test_up_with_runtime(self):
+        self.require_api_version('1.30')
+        config_data = build_config(
+            version=V2_3,
+            services=[{
+                'name': 'web',
+                'image': 'busybox:latest',
+                'runtime': 'runc'
+            }],
+        )
+        project = Project.from_config(
+            client=self.client,
+            name='composetest',
+            config_data=config_data
+        )
+        project.up(detached=True)
+        service_container = project.get_service('web').containers(stopped=True)[0]
+        assert service_container.inspect()['HostConfig']['Runtime'] == 'runc'
+
+    @v2_3_only()
+    def test_up_with_invalid_runtime(self):
+        self.require_api_version('1.30')
+        config_data = build_config(
+            version=V2_3,
+            services=[{
+                'name': 'web',
+                'image': 'busybox:latest',
+                'runtime': 'foobar'
+            }],
+        )
+        project = Project.from_config(
+            client=self.client,
+            name='composetest',
+            config_data=config_data
+        )
+        with self.assertRaises(ProjectError):
+            project.up()
+
+    @v2_3_only()
+    @if_runtime_available('nvidia')
+    def test_up_with_nvidia_runtime(self):
+        self.require_api_version('1.30')
+        config_data = build_config(
+            version=V2_3,
+            services=[{
+                'name': 'web',
+                'image': 'busybox:latest',
+                'runtime': 'nvidia'
+            }],
+        )
+        project = Project.from_config(
+            client=self.client,
+            name='composetest',
+            config_data=config_data
+        )
+        project.up(detached=True)
+        service_container = project.get_service('web').containers(stopped=True)[0]
+        assert service_container.inspect()['HostConfig']['Runtime'] == 'nvidia'
+
     @v2_only()
     def test_project_up_with_network_internal(self):
         self.require_api_version('1.23')

+ 12 - 0
tests/integration/testcases.py

@@ -155,6 +155,18 @@ class DockerClientTestCase(unittest.TestCase):
         return self.client.inspect_volume(volumes[0]['Name'])
 
 
+def if_runtime_available(runtime):
+    def decorator(f):
+        @functools.wraps(f)
+        def wrapper(self, *args, **kwargs):
+            if runtime not in self.client.info().get('Runtimes', {}):
+                return pytest.skip("This daemon does not support the '{}'' runtime".format(runtime))
+            return f(self, *args, **kwargs)
+        return wrapper
+
+    return decorator
+
+
 def is_cluster(client):
     if SWARM_ASSUME_MULTINODE:
         return True

+ 19 - 0
tests/unit/config/config_test.py

@@ -1784,6 +1784,25 @@ class ConfigTest(unittest.TestCase):
             }
         ]
 
+    def test_runtime_option(self):
+        actual = config.load(build_config_details({
+            'version': str(V2_3),
+            'services': {
+                'web': {
+                    'image': 'nvidia/cuda',
+                    'runtime': 'nvidia'
+                }
+            }
+        }))
+
+        assert actual.services == [
+            {
+                'name': 'web',
+                'image': 'nvidia/cuda',
+                'runtime': 'nvidia',
+            }
+        ]
+
     def test_merge_service_dicts_from_files_with_extends_in_base(self):
         base = {
             'volumes': ['.:/app'],