From 2057b668bf6e1c494df795c5520c201ae9c9fcf4 Mon Sep 17 00:00:00 2001 From: Kien Nguyen Date: Thu, 14 Sep 2017 10:40:15 +0700 Subject: [PATCH] Bring hostname option back Zun already had this option before, but had to remove due to the Conflict Exception (relate to sandbox). At this time, we made sandbox optional. Therefore, if use_sandbox option is False, we can use hostname option. If it is True, raise the Exception to let user know. This option will help us to implement the blueprint internal-dns-resolution. Change-Id: I2f8826fc66e94c0d4452ff32e3bc17700ef6b668 Closes-Bug: #1716849 --- api-ref/source/containers.inc | 1 + api-ref/source/parameters.yaml | 3 +- .../source/samples/container-create-req.json | 3 +- .../source/samples/container-create-resp.json | 3 +- zun/api/controllers/v1/containers.py | 35 ++++++++++++------ zun/api/controllers/v1/schemas/containers.py | 3 +- .../controllers/v1/views/containers_view.py | 3 +- zun/api/controllers/versions.py | 3 +- zun/api/rest_api_version_history.rst | 6 +++ zun/common/exception.py | 10 ++++- zun/common/validation/parameter_types.py | 6 +++ zun/container/docker/driver.py | 6 ++- zun/tests/unit/api/controllers/test_root.py | 6 +-- .../api/controllers/v1/test_containers.py | 37 ++++++++++++++++++- .../container/docker/test_docker_driver.py | 1 + 15 files changed, 99 insertions(+), 27 deletions(-) diff --git a/api-ref/source/containers.inc b/api-ref/source/containers.inc index fb9108ccd..759cf02a6 100644 --- a/api-ref/source/containers.inc +++ b/api-ref/source/containers.inc @@ -49,6 +49,7 @@ Request - security_groups: security_groups - nets: nets - runtime: runtime + - hostname: hostname Request Example ---------------- diff --git a/api-ref/source/parameters.yaml b/api-ref/source/parameters.yaml index baf43f762..26b0085b8 100644 --- a/api-ref/source/parameters.yaml +++ b/api-ref/source/parameters.yaml @@ -134,9 +134,8 @@ host_list: type: array hostname: description: | - The host where container is running. + The hostname of container. in: body - required: true type: string id_s: description: | diff --git a/api-ref/source/samples/container-create-req.json b/api-ref/source/samples/container-create-req.json index a75d9fb32..5e44ebe87 100644 --- a/api-ref/source/samples/container-create-req.json +++ b/api-ref/source/samples/container-create-req.json @@ -27,5 +27,6 @@ "port": "890699a9-4690-4bd6-8b70-3a9c1be77ecb" } ], - "runtime": "runc" + "runtime": "runc", + "hostname": "testhost" } diff --git a/api-ref/source/samples/container-create-resp.json b/api-ref/source/samples/container-create-resp.json index 06ef41538..552ad3659 100644 --- a/api-ref/source/samples/container-create-resp.json +++ b/api-ref/source/samples/container-create-resp.json @@ -34,5 +34,6 @@ "command": "/bin/sh -c 'echo hello'", "cpu": 2.0, "interactive": false, - "runtime": "runc" + "runtime": "runc", + "hostname": "testhost" } diff --git a/zun/api/controllers/v1/containers.py b/zun/api/controllers/v1/containers.py index fa126267c..46355fffd 100644 --- a/zun/api/controllers/v1/containers.py +++ b/zun/api/controllers/v1/containers.py @@ -252,12 +252,9 @@ class ContainersController(base.Controller): msg = _('Auto_remove value are true or false') raise exception.InvalidValue(msg) else: - msg = _('Invalid param auto_remove because current request ' - 'version is %(req_version)s. Auto_remove is only ' - 'supported from version %(min_version)s') % \ - {'req_version': req_version, - 'min_version': min_version} - raise exception.InvalidParam(msg) + raise exception.InvalidParamInVersion(param='auto_remove', + req_version=req_version, + min_version=min_version) runtime = container_dict.pop('runtime', None) if runtime is not None: @@ -266,12 +263,26 @@ class ContainersController(base.Controller): if req_version >= min_version: container_dict['runtime'] = runtime else: - msg = _('Invalid param runtime because current request ' - 'version is %(req_version)s. `runtime` is only ' - 'supported from version %(min_version)s') % \ - {'req_version': req_version, - 'min_version': min_version} - raise exception.InvalidParam(msg) + raise exception.InvalidParamInVersion(param='runtime', + req_version=req_version, + min_version=min_version) + + hostname = container_dict.pop('hostname', None) + if hostname is not None: + if CONF.use_sandbox: + raise exception.ConflictOptions( + 'Cannot set container\'s hostname when use sandbox. ' + 'Because with sandbox, network_mode will be set, it ' + 'is incompatible with legacy network (hostname).') + req_version = pecan.request.version + min_version = versions.Version('', '', '', '1.9') + container_dict['hostname'] = hostname + if req_version >= min_version: + container_dict['hostname'] = hostname + else: + raise exception.InvalidParamInVersion(param='hostname', + req_version=req_version, + min_version=min_version) nets = container_dict.get('nets', []) requested_networks = self._build_requested_networks(context, nets) diff --git a/zun/api/controllers/v1/schemas/containers.py b/zun/api/controllers/v1/schemas/containers.py index fccb013e6..c0487a323 100644 --- a/zun/api/controllers/v1/schemas/containers.py +++ b/zun/api/controllers/v1/schemas/containers.py @@ -31,7 +31,8 @@ _container_properties = { 'security_groups': parameter_types.security_groups, 'hints': parameter_types.hints, 'nets': parameter_types.nets, - 'runtime': parameter_types.runtime + 'runtime': parameter_types.runtime, + 'hostname': parameter_types.hostname, } container_create = { diff --git a/zun/api/controllers/v1/views/containers_view.py b/zun/api/controllers/v1/views/containers_view.py index 8e81ad22a..1aa6da150 100644 --- a/zun/api/controllers/v1/views/containers_view.py +++ b/zun/api/controllers/v1/views/containers_view.py @@ -40,7 +40,8 @@ _basic_keys = ( 'image_driver', 'security_groups', 'auto_remove', - 'runtime' + 'runtime', + 'hostname', ) diff --git a/zun/api/controllers/versions.py b/zun/api/controllers/versions.py index 7065d3c65..ec1278d51 100644 --- a/zun/api/controllers/versions.py +++ b/zun/api/controllers/versions.py @@ -41,10 +41,11 @@ REST_API_VERSION_HISTORY = """REST API Version History: * 1.6 - Support detach network from a container * 1.7 - Disallow non-admin users to force delete containers * 1.8 - Support attach a network to a container + * 1.9 - Add support set container's hostname """ BASE_VER = '1.1' -CURRENT_MAX_VER = '1.8' +CURRENT_MAX_VER = '1.9' class Version(object): diff --git a/zun/api/rest_api_version_history.rst b/zun/api/rest_api_version_history.rst index c6f7f491c..5b1359aa5 100644 --- a/zun/api/rest_api_version_history.rst +++ b/zun/api/rest_api_version_history.rst @@ -76,3 +76,9 @@ user documentation. Add attach a network to a container. Users can use this api to attach a neutron network to a container. + +1.9 +--- + Add a new attribute 'hostname' to the request to create a container. + Users can use this attribute to specify container's hostname. + diff --git a/zun/common/exception.py b/zun/common/exception.py index 2fe983a43..82b90cf00 100644 --- a/zun/common/exception.py +++ b/zun/common/exception.py @@ -313,6 +313,10 @@ class Conflict(ZunException): code = 409 +class ConflictOptions(Conflict): + message = _('Conflicting options.') + + class InvalidState(Conflict): message = _("Invalid resource state.") @@ -323,8 +327,10 @@ class InvalidParameterValue(Invalid): message = _("%(err)s") -class InvalidParam(Invalid): - message = _('Invalid param %(param)s') +class InvalidParamInVersion(Invalid): + message = _('Invalid param %(param)s because current request ' + 'version is %(req_version)s. %(param)s is only ' + 'supported from version %(min_version)s') class PatchError(Invalid): diff --git a/zun/common/validation/parameter_types.py b/zun/common/validation/parameter_types.py index 6ab9c4a50..53059cf1f 100644 --- a/zun/common/validation/parameter_types.py +++ b/zun/common/validation/parameter_types.py @@ -122,6 +122,12 @@ environment = { }, } +hostname = { + 'type': ['string', 'null'], + 'minLength': 2, + 'maxLength': 63 +} + runtime = { 'type': ['string', 'null'], } diff --git a/zun/container/docker/driver.py b/zun/container/docker/driver.py index 03c4f5823..4daad846b 100644 --- a/zun/container/docker/driver.py +++ b/zun/container/docker/driver.py @@ -132,6 +132,7 @@ class DockerDriver(driver.ContainerDriver): 'labels': container.labels, 'tty': container.interactive, 'stdin_open': container.interactive, + 'hostname': container.hostname, } runtime = container.runtime if container.runtime\ @@ -396,8 +397,9 @@ class DockerDriver(driver.ContainerDriver): container.command = command_str def _populate_hostname_and_ports(self, container, config): - # populate hostname - container.hostname = config.get('Hostname') + # populate hostname only when container.hostname wasn't set + if container.hostname is None: + container.hostname = config.get('Hostname') # populate ports ports = [] exposed_ports = config.get('ExposedPorts') diff --git a/zun/tests/unit/api/controllers/test_root.py b/zun/tests/unit/api/controllers/test_root.py index 2ef4ecbac..f1f0bef12 100644 --- a/zun/tests/unit/api/controllers/test_root.py +++ b/zun/tests/unit/api/controllers/test_root.py @@ -17,7 +17,7 @@ import webtest from zun.api import app from zun.tests.unit.api import base as api_base -CURRENT_VERSION = "container 1.8" +CURRENT_VERSION = "container 1.9" class TestRootController(api_base.FunctionalTest): @@ -27,7 +27,7 @@ class TestRootController(api_base.FunctionalTest): 'default_version': {'id': 'v1', 'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}], - 'max_version': '1.8', + 'max_version': '1.9', 'min_version': '1.1', 'status': 'CURRENT'}, 'description': 'Zun is an OpenStack project which ' @@ -35,7 +35,7 @@ class TestRootController(api_base.FunctionalTest): 'versions': [{'id': 'v1', 'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}], - 'max_version': '1.8', + 'max_version': '1.9', 'min_version': '1.1', 'status': 'CURRENT'}]} diff --git a/zun/tests/unit/api/controllers/v1/test_containers.py b/zun/tests/unit/api/controllers/v1/test_containers.py index 4f9692e01..88005ea41 100644 --- a/zun/tests/unit/api/controllers/v1/test_containers.py +++ b/zun/tests/unit/api/controllers/v1/test_containers.py @@ -23,7 +23,7 @@ from zun.tests.unit.api import base as api_base from zun.tests.unit.db import utils from zun.tests.unit.objects import utils as obj_utils -CURRENT_VERSION = "container 1.8" +CURRENT_VERSION = "container 1.9" class TestContainerController(api_base.FunctionalTest): @@ -108,6 +108,41 @@ class TestContainerController(api_base.FunctionalTest): params=params, content_type='application/json', headers=api_version) + def test_run_container_with_hostname_wrong_api_version(self): + params = ('{"name": "MyDocker", "image": "ubuntu",' + '"command": "env", "memory": "512",' + '"environment": {"key1": "val1", "key2": "val2"},' + '"hostname": "testhost"}') + headers = {"OpenStack-API-Version": "container 1.7", + "Accept": "application/json"} + with self.assertRaisesRegex(AppError, + "Invalid param hostname"): + self.app.post('/v1/containers?run=true', + params=params, content_type='application/json', + headers=headers) + + @patch('zun.network.neutron.NeutronAPI.get_available_network') + @patch('zun.compute.api.API.container_create') + @patch('zun.compute.api.API.image_search') + def test_run_container_with_hostname_successfully( + self, mock_search, + mock_container_create, + mock_neutron_get_network): + params = ('{"name": "MyDocker", "image": "ubuntu",' + '"command": "env", "memory": "512",' + '"environment": {"key1": "val1", "key2": "val2"},' + '"hostname": "testhost"}') + api_version = {"OpenStack-API-Version": CURRENT_VERSION} + response = self.app.post('/v1/containers?run=true', + params=params, + content_type='application/json', + headers=api_version) + + self.assertEqual(202, response.status_int) + self.assertTrue(mock_container_create.called) + self.assertTrue(mock_container_create.call_args[1]['run'] is True) + mock_neutron_get_network.assert_called_once() + @patch('zun.network.neutron.NeutronAPI.get_available_network') @patch('zun.compute.api.API.container_create') @patch('zun.compute.api.API.image_search') diff --git a/zun/tests/unit/container/docker/test_docker_driver.py b/zun/tests/unit/container/docker/test_docker_driver.py index 75df713ef..9db8160d9 100644 --- a/zun/tests/unit/container/docker/test_docker_driver.py +++ b/zun/tests/unit/container/docker/test_docker_driver.py @@ -111,6 +111,7 @@ class TestDockerDriver(base.DriverTestCase): 'host_config': {'Id1': 'val1', 'key2': 'val2'}, 'stdin_open': True, 'tty': True, + 'hostname': 'testhost', } self.mock_docker.create_container.assert_called_once_with( image['repo'] + ":" + image['tag'], **kwargs)