From a1a7b7d6243858f23be1a7a24fed3e94b9b27914 Mon Sep 17 00:00:00 2001 From: ShunliZhou Date: Tue, 15 Aug 2017 16:37:43 +0800 Subject: [PATCH] Add detach network server side. Add support for detach a network from a container. Change-Id: I36d8829ae2c36751b4101d55ee621cb280eae4b7 Partially-Implements: blueprint network-rest-api --- api-ref/source/containers.inc | 34 ++ etc/zun/policy.json | 1 + zun/api/controllers/v1/containers.py | 18 +- zun/api/controllers/v1/schemas/containers.py | 11 + zun/api/controllers/versions.py | 3 +- zun/api/rest_api_version_history.rst | 6 + zun/compute/api.py | 3 + zun/compute/manager.py | 9 + zun/compute/rpcapi.py | 4 + zun/container/docker/driver.py | 17 + zun/container/driver.py | 3 + zun/tests/unit/api/controllers/test_root.py | 13 +- .../api/controllers/v1/test_containers.py | 292 +++++++++++++----- .../unit/compute/test_compute_manager.py | 6 + .../container/docker/test_docker_driver.py | 9 + 15 files changed, 342 insertions(+), 87 deletions(-) diff --git a/api-ref/source/containers.inc b/api-ref/source/containers.inc index d221717fb..740172df0 100644 --- a/api-ref/source/containers.inc +++ b/api-ref/source/containers.inc @@ -692,3 +692,37 @@ Response .. rest_parameters:: parameters.yaml - image: image + + +Detach a network from a container +================================ + +.. rest_method:: POST /v1/containers/{container_ident}/network_detach?network={network} + +Detach a network from a container. + +Response Codes +------------- + +.. rest_status_code:: success status.yaml + + - 202 + +.. rest_status_code:: error status.yaml + + - 401 + - 403 + - 404 + +.. rest_parameters:: parameters.yaml + + - container_ident: container_ident + - network: network + +Response +-------- + +This request does not return anything in the response body. + +.. rest_parameters:; parameters.yaml + - X-Openstack-Request-Id: request_id diff --git a/etc/zun/policy.json b/etc/zun/policy.json index 613840c71..f7cd7d900 100644 --- a/etc/zun/policy.json +++ b/etc/zun/policy.json @@ -30,6 +30,7 @@ "container:stats": "rule:default", "container:commit": "rule:default", "container:add_security_group": "rule:default", + "container:network_detach": "rule:default", "image:pull": "rule:default", "image:get_all": "rule:default", diff --git a/zun/api/controllers/v1/containers.py b/zun/api/controllers/v1/containers.py index 48ff264cd..7e6b22404 100644 --- a/zun/api/controllers/v1/containers.py +++ b/zun/api/controllers/v1/containers.py @@ -112,7 +112,8 @@ class ContainersController(base.Controller): 'put_archive': ['POST'], 'stats': ['GET'], 'commit': ['POST'], - 'add_security_group': ['POST'] + 'add_security_group': ['POST'], + 'network_detach': ['POST'] } @pecan.expose('json') @@ -700,3 +701,18 @@ class ContainersController(base.Controller): return compute_api.container_commit(context, container, kwargs.get('repository', None), kwargs.get('tag', None)) + + @base.Controller.api_version("1.6") + @pecan.expose('json') + @exception.wrap_pecan_controller_exception + @validation.validate_query_param(pecan.request, schema.network_detach) + def network_detach(self, container_id, **kwargs): + container = _get_container(container_id) + check_policy_on_container(container.as_dict(), + "container:network_detach") + context = pecan.request.context + compute_api = pecan.request.compute_api + neutron_api = neutron.NeutronAPI(context) + neutron_net = neutron_api.get_neutron_network(kwargs.get('network')) + compute_api.network_detach(context, container, neutron_net['id']) + pecan.response.status = 202 diff --git a/zun/api/controllers/v1/schemas/containers.py b/zun/api/controllers/v1/schemas/containers.py index 463110bf7..2f8692d5d 100644 --- a/zun/api/controllers/v1/schemas/containers.py +++ b/zun/api/controllers/v1/schemas/containers.py @@ -169,3 +169,14 @@ add_security_group = { }, 'additionalProperties': False } + +network_detach = { + 'type': 'object', + 'properties': { + 'network': { + 'type': 'string' + } + }, + 'required': ['network'], + 'additionalProperties': False +} diff --git a/zun/api/controllers/versions.py b/zun/api/controllers/versions.py index fefa59d9b..6ca52a09b 100644 --- a/zun/api/controllers/versions.py +++ b/zun/api/controllers/versions.py @@ -38,10 +38,11 @@ REST_API_VERSION_HISTORY = """REST API Version History: * 1.3 - Add auto_remove to container * 1.4 - Support list all container host and show a container host * 1.5 - Add runtime to container + * 1.6 - Support detach network from a container """ BASE_VER = '1.1' -CURRENT_MAX_VER = '1.5' +CURRENT_MAX_VER = '1.6' class Version(object): diff --git a/zun/api/rest_api_version_history.rst b/zun/api/rest_api_version_history.rst index e81db89eb..79f5a8a89 100644 --- a/zun/api/rest_api_version_history.rst +++ b/zun/api/rest_api_version_history.rst @@ -58,3 +58,9 @@ user documentation. Users can use this attribute to choose runtime for their containers. The specified runtime should be configured by admin to run with Zun. The default runtime for Zun is runc. + +1.6 +--- + + Add detach a network from a container api. + Users can use this api to detach a neutron network from a container. diff --git a/zun/compute/api.py b/zun/compute/api.py index fd0d6add7..1796929ab 100644 --- a/zun/compute/api.py +++ b/zun/compute/api.py @@ -146,3 +146,6 @@ class API(object): return self.rpcapi.capsule_create(context, host_state['host'], new_capsule, requested_networks, host_state['limits']) + + def network_detach(self, context, container, *args): + return self.rpcapi.network_detach(context, container, *args) diff --git a/zun/compute/manager.py b/zun/compute/manager.py index fe446da46..08ccf8fad 100644 --- a/zun/compute/manager.py +++ b/zun/compute/manager.py @@ -721,3 +721,12 @@ class Manager(periodic_task.PeriodicTasks): limits) if created_container: self._do_container_start(context, created_container) + + def network_detach(self, context, container, network): + LOG.debug('Detach network: %(network)s from container: %(container)s.', + {'container': container, 'network': network}) + try: + self.driver.network_detach(context, container, network) + except Exception as e: + with excutils.save_and_reraise_exception(reraise=False): + LOG.exception("Unexpected exception: %s", six.text_type(e)) diff --git a/zun/compute/rpcapi.py b/zun/compute/rpcapi.py index a4b272b83..dabce24f6 100644 --- a/zun/compute/rpcapi.py +++ b/zun/compute/rpcapi.py @@ -181,3 +181,7 @@ class API(rpc_service.API): capsule=capsule, requested_networks=requested_networks, limits=limits) + + def network_detach(self, context, container, network): + return self._call(container.host, 'network_detach', + container=container, network=network) diff --git a/zun/container/docker/driver.py b/zun/container/docker/driver.py index 629acd798..3f42a316d 100644 --- a/zun/container/docker/driver.py +++ b/zun/container/docker/driver.py @@ -756,6 +756,23 @@ class DockerDriver(driver.ContainerDriver): def get_available_nodes(self): return [self._host.get_hostname()] + def network_detach(self, context, container, network): + with docker_utils.docker_client() as docker: + network_api = zun_network.api(context, + docker_api=docker) + docker_net = self._get_docker_network_name(context, network) + network_api.disconnect_container_from_network(container, + docker_net, network) + + # Only clear network info related to this network + # Cannot del container.address directly which will not update + # changed fields of the container objects as the del operate on + # the addresses object, only base.getter will called. + update = container.addresses + del update[network] + container.addresses = update + container.save(context) + class NovaDockerDriver(DockerDriver): capabilities = { diff --git a/zun/container/driver.py b/zun/container/driver.py index 9d77fbb8c..b70a95993 100644 --- a/zun/container/driver.py +++ b/zun/container/driver.py @@ -228,3 +228,6 @@ class ContainerDriver(object): if nodename in self.get_available_nodes(): return True return False + + def network_detach(self, context, container, network): + raise NotImplementedError() diff --git a/zun/tests/unit/api/controllers/test_root.py b/zun/tests/unit/api/controllers/test_root.py index a043aff00..06e965df9 100644 --- a/zun/tests/unit/api/controllers/test_root.py +++ b/zun/tests/unit/api/controllers/test_root.py @@ -17,6 +17,8 @@ import webtest from zun.api import app from zun.tests.unit.api import base as api_base +CURRENT_VERSION = "container 1.6" + class TestRootController(api_base.FunctionalTest): def setUp(self): @@ -25,7 +27,7 @@ class TestRootController(api_base.FunctionalTest): 'default_version': {'id': 'v1', 'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}], - 'max_version': '1.5', + 'max_version': '1.6', 'min_version': '1.1', 'status': 'CURRENT'}, 'description': 'Zun is an OpenStack project which ' @@ -33,7 +35,7 @@ class TestRootController(api_base.FunctionalTest): 'versions': [{'id': 'v1', 'links': [{'href': 'http://localhost/v1/', 'rel': 'self'}], - 'max_version': '1.5', + 'max_version': '1.6', 'min_version': '1.1', 'status': 'CURRENT'}]} @@ -105,15 +107,16 @@ class TestRootController(api_base.FunctionalTest): def test_noauth(self): # Don't need to auth paste_file = "zun/tests/unit/api/controllers/noauth-paste.ini" + headers = {'OpenStack-API-Version': CURRENT_VERSION} app = self.make_app(paste_file) - response = app.get('/') + response = app.get('/', headers=headers) self.assertEqual(self.root_expected, response.json) - response = app.get('/v1/') + response = app.get('/v1/', headers=headers) self.assertEqual(self.v1_expected, response.json) - response = app.get('/v1/containers/') + response = app.get('/v1/containers/', headers=headers) self.assertEqual(200, response.status_int) def test_auth_with_no_public_routes(self): diff --git a/zun/tests/unit/api/controllers/v1/test_containers.py b/zun/tests/unit/api/controllers/v1/test_containers.py index 3dbdbce27..897cbc698 100644 --- a/zun/tests/unit/api/controllers/v1/test_containers.py +++ b/zun/tests/unit/api/controllers/v1/test_containers.py @@ -23,6 +23,8 @@ 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.6" + class TestContainerController(api_base.FunctionalTest): @patch('zun.network.neutron.NeutronAPI.get_available_network') @@ -35,8 +37,11 @@ class TestContainerController(api_base.FunctionalTest): params = ('{"name": "MyDocker", "image": "ubuntu",' '"command": "env", "memory": "512",' '"environment": {"key1": "val1", "key2": "val2"}}') + + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.post('/v1/containers?run=true', params=params, + headers=headers, content_type='application/json') self.assertEqual(202, response.status_int) @@ -52,8 +57,9 @@ class TestContainerController(api_base.FunctionalTest): '"environment": {"key1": "val1", "key2": "val2"}}') with self.assertRaisesRegex(AppError, "Invalid input for query parameters"): + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.app.post('/v1/containers?run=xyz', params=params, - content_type='application/json') + content_type='application/json', headers=headers) @patch('zun.network.neutron.NeutronAPI.get_available_network') @patch('zun.compute.api.API.container_run') @@ -65,7 +71,7 @@ class TestContainerController(api_base.FunctionalTest): '"command": "env", "memory": "512",' '"environment": {"key1": "val1", "key2": "val2"},' '"runtime": "runc"}') - api_version = {"OpenStack-API-Version": "container 1.5"} + api_version = {"OpenStack-API-Version": CURRENT_VERSION} response = self.app.post('/v1/containers?run=true', params=params, content_type='application/json', @@ -80,19 +86,20 @@ class TestContainerController(api_base.FunctionalTest): '"command": "env", "memory": "512",' '"environment": {"key1": "val1", "key2": "val2"},' '"runtime": "runc"}') - api_version = {"OpenStack-API-Version": "container 1.4"} + headers = {"OpenStack-API-Version": "container 1.4", + "Accept": "application/json"} with self.assertRaisesRegex(AppError, "Invalid param runtime"): self.app.post('/v1/containers?run=true', params=params, content_type='application/json', - headers=api_version) + headers=headers) def test_run_container_runtime_wrong_value(self): params = ('{"name": "MyDocker", "image": "ubuntu",' '"command": "env", "memory": "512",' '"environment": {"key1": "val1", "key2": "val2"},' '"runtime": "wrong_value"}') - api_version = {"OpenStack-API-Version": "container 1.4"} + api_version = {"OpenStack-API-Version": CURRENT_VERSION} with self.assertRaisesRegex(AppError, "Invalid input for field"): self.app.post('/v1/containers?run=true', @@ -110,9 +117,11 @@ class TestContainerController(api_base.FunctionalTest): params = ('{"name": "MyDocker", "image": "ubuntu",' '"command": "env", "memory": "512",' '"environment": {"key1": "val1", "key2": "val2"}}') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.post('/v1/containers?run=false', params=params, - content_type='application/json') + content_type='application/json', + headers=headers) self.assertEqual(202, response.status_int) self.assertFalse(mock_container_run.called) mock_neutron_get_network.assert_called_once() @@ -125,8 +134,10 @@ class TestContainerController(api_base.FunctionalTest): params = ('{"name": "MyDocker", "image": "ubuntu",' '"command": "env", "memory": "512",' '"environment": {"key1": "val1", "key2": "val2"}}') + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.assertRaises(AppError, self.app.post, '/v1/containers?run=wrong', - params=params, content_type='application/json') + params=params, content_type='application/json', + headers=headers) self.assertTrue(mock_container_run.not_called) @patch('zun.network.neutron.NeutronAPI.get_available_network') @@ -139,8 +150,9 @@ class TestContainerController(api_base.FunctionalTest): params = ('{"name": "MyDocker", "image": "ubuntu",' '"command": "env", "memory": "512",' '"environment": {"key1": "val1", "key2": "val2"}}') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.post('/v1/containers/', - params=params, + params=params, headers=headers, content_type='application/json') self.assertEqual(202, response.status_int) @@ -155,8 +167,9 @@ class TestContainerController(api_base.FunctionalTest): '"environment": {"key1": "val1", "key2": "val2"}}') with self.assertRaisesRegex(AppError, "is a required property"): + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.app.post('/v1/containers/', - params=params, + params=params, headers=headers, content_type='application/json') self.assertTrue(mock_container_create.not_called) @@ -170,7 +183,9 @@ class TestContainerController(api_base.FunctionalTest): mock_search.side_effect = exception.ImageNotFound() params = {"name": "MyDocker", "image": "not-found"} - response = self.post_json('/containers/', params, expect_errors=True) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.post_json('/containers/', params, headers=headers, + expect_errors=True) self.assertEqual('application/json', response.content_type) self.assertEqual(404, response.status_int) self.assertFalse(mock_container_create.called) @@ -191,8 +206,9 @@ class TestContainerController(api_base.FunctionalTest): params = ('{"name": "MyDocker", "image": "ubuntu",' '"command": "env", "memory": "512",' '"environment": {"key1": "val1", "key2": "val2"}}') + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.app.post('/v1/containers/', - params=params, + params=params, headers=headers, content_type='application/json') mock_neutron_get_network.assert_called_once() @@ -207,8 +223,9 @@ class TestContainerController(api_base.FunctionalTest): params = ('{"name": "MyDocker", "image": "ubuntu",' '"command": "env", "memory": "512",' '"environment": {"key1": "val1", "key2": "val2"}}') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.post('/v1/containers/', - params=params, + params=params, headers=headers, content_type='application/json') self.assertEqual(202, response.status_int) self.assertIn('status_reason', response.json.keys()) @@ -231,8 +248,9 @@ class TestContainerController(api_base.FunctionalTest): params = ('{"name": "MyDocker", "image": "ubuntu",' '"command": "env", "memory": "512",' '"environment": {"key1": "val1", "key2": "val2"}}') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.post('/v1/containers/', - params=params, + params=params, headers=headers, content_type='application/json') self.assertEqual(202, response.status_int) # get all containers @@ -286,8 +304,9 @@ class TestContainerController(api_base.FunctionalTest): params = ('{"name": "MyDocker", "image": "ubuntu",' '"command": "env",' '"environment": {"key1": "val1", "key2": "val2"}}') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.post('/v1/containers/', - params=params, + params=params, headers=headers, content_type='application/json') self.assertEqual(202, response.status_int) # get all containers @@ -324,8 +343,9 @@ class TestContainerController(api_base.FunctionalTest): # Create a container with a command params = ('{"name": "MyDocker", "image": "ubuntu",' '"command": "env", "memory": "512"}') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.post('/v1/containers/', - params=params, + params=params, headers=headers, content_type='application/json') self.assertEqual(202, response.status_int) # get all containers @@ -361,8 +381,9 @@ class TestContainerController(api_base.FunctionalTest): mock_neutron_get_network.return_value = fake_network params = ('{"image": "ubuntu", "command": "env", "memory": "512",' '"environment": {"key1": "val1", "key2": "val2"}}') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.post('/v1/containers/', - params=params, + params=params, headers=headers, content_type='application/json') self.assertEqual(202, response.status_int) # get all containers @@ -403,8 +424,9 @@ class TestContainerController(api_base.FunctionalTest): '"command": "env", "memory": "512",' '"restart_policy": {"Name": "no",' '"MaximumRetryCount": "0"}}') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.post('/v1/containers/', - params=params, + params=params, headers=headers, content_type='application/json') self.assertEqual(202, response.status_int) # get all containers @@ -445,8 +467,9 @@ class TestContainerController(api_base.FunctionalTest): '"command": "env", "memory": "512",' '"restart_policy": {"Name": "no",' '"MaximumRetryCount": "6"}}') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.post('/v1/containers/', - params=params, + params=params, headers=headers, content_type='application/json') self.assertEqual(202, response.status_int) # get all containers @@ -486,8 +509,9 @@ class TestContainerController(api_base.FunctionalTest): params = ('{"name": "MyDocker", "image": "ubuntu",' '"command": "env", "memory": "512",' '"restart_policy": {"Name": "no"}}') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.post('/v1/containers/', - params=params, + params=params, headers=headers, content_type='application/json') self.assertEqual(202, response.status_int) # get all containers @@ -528,8 +552,9 @@ class TestContainerController(api_base.FunctionalTest): '"command": "env", "memory": "512",' '"restart_policy": {"Name": "unless-stopped",' '"MaximumRetryCount": "0"}}') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.post('/v1/containers/', - params=params, + params=params, headers=headers, content_type='application/json') self.assertEqual(202, response.status_int) # get all containers @@ -569,8 +594,9 @@ class TestContainerController(api_base.FunctionalTest): '"command": "env", "memory": "512",' '"environment": {"key1": "val1", "key2": "val2"},' '"nets": [{"port": "testport"}]}') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.post('/v1/containers/', - params=params, + params=params, headers=headers, content_type='application/json') self.assertEqual(202, response.status_int) # get all containers @@ -628,8 +654,9 @@ class TestContainerController(api_base.FunctionalTest): '"MaximumRetryCount": "1"}}') with self.assertRaisesRegex( AppError, "maximum retry count not valid with"): + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.app.post('/v1/containers/', - params=params, + params=params, headers=headers, content_type='application/json') self.assertTrue(mock_container_create.not_called) mock_neutron_get_network.assert_called_once() @@ -641,8 +668,10 @@ class TestContainerController(api_base.FunctionalTest): # Long name params = ('{"name": "' + 'i' * 256 + '", "image": "ubuntu",' '"command": "env", "memory": "512"}') + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.assertRaises(AppError, self.app.post, '/v1/containers/', - params=params, content_type='application/json') + params=params, content_type='application/json', + headers=headers) self.assertTrue(mock_container_create.not_called) @patch('zun.compute.api.API.container_show') @@ -654,7 +683,8 @@ class TestContainerController(api_base.FunctionalTest): mock_container_list.return_value = containers mock_container_show.return_value = containers[0] - response = self.app.get('/v1/containers/') + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.get('/v1/containers/', headers=headers) mock_container_list.assert_called_once_with(mock.ANY, 1000, None, 'id', 'asc', @@ -676,7 +706,9 @@ class TestContainerController(api_base.FunctionalTest): mock_container_list.return_value = containers mock_container_show.return_value = containers[0] - response = self.app.get('/v1/containers/?all_tenants=1') + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.get('/v1/containers/?all_tenants=1', + headers=headers) mock_container_list.assert_called_once_with(mock.ANY, 1000, None, 'id', 'asc', @@ -698,7 +730,8 @@ class TestContainerController(api_base.FunctionalTest): mock_container_list.return_value = containers mock_container_show.return_value = containers[0] - response = self.app.get('/v1/containers/') + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.get('/v1/containers/', headers=headers) self.assertEqual(200, response.status_int) actual_containers = response.json['containers'] self.assertEqual(1, len(actual_containers)) @@ -721,8 +754,9 @@ class TestContainerController(api_base.FunctionalTest): **test_container)) mock_container_list.return_value = container_list[-1:] mock_container_show.return_value = container_list[-1] + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.get('/v1/containers/?limit=3&marker=%s' - % container_list[2].uuid) + % container_list[2].uuid, headers=headers) self.assertEqual(200, response.status_int) actual_containers = response.json['containers'] @@ -739,7 +773,8 @@ class TestContainerController(api_base.FunctionalTest): mock_container_list.return_value = containers mock_container_show.side_effect = Exception - response = self.app.get('/v1/containers/') + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.get('/v1/containers/', headers=headers) mock_container_list.assert_called_once_with(mock.ANY, 1000, None, 'id', 'asc', @@ -762,7 +797,9 @@ class TestContainerController(api_base.FunctionalTest): mock_container_get_by_uuid.return_value = test_container_obj mock_container_show.return_value = test_container_obj - response = self.app.get('/v1/containers/%s/' % test_container['uuid']) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.get('/v1/containers/%s/' % test_container['uuid'], + headers=headers) mock_container_get_by_uuid.assert_called_once_with( mock.ANY, @@ -782,8 +819,9 @@ class TestContainerController(api_base.FunctionalTest): mock_container_get_by_uuid.return_value = test_container_obj mock_container_show.return_value = test_container_obj + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.get('/v1/containers/%s/?all_tenants=1' % - test_container['uuid']) + test_container['uuid'], headers=headers) mock_container_get_by_uuid.assert_called_once_with( mock.ANY, @@ -804,9 +842,10 @@ class TestContainerController(api_base.FunctionalTest): params = {'cpu': 1} container_uuid = test_container.get('uuid') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.patch_json( '/v1/containers/%s/' % container_uuid, - params=params) + params=params, headers=headers) self.assertEqual(200, response.status_int) self.assertTrue(mock_update.called) @@ -818,14 +857,16 @@ class TestContainerController(api_base.FunctionalTest): get_by_ident_loc = 'zun.objects.Container.get_by_%s' % ident_field with patch(get_by_ident_loc) as mock_get_by_indent: mock_get_by_indent.return_value = test_container_obj + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.post('/v1/containers/%s/%s/?%s' % - (ident, action, query_param)) + (ident, action, query_param), + headers=headers) self.assertEqual(status_code, response.status_int) # Only PUT should work, others like GET should fail self.assertRaises(AppError, self.app.get, ('/v1/containers/%s/%s/' % - (ident, action))) + (ident, action)), headers=headers) if query_param: value = query_param.split('=')[1] mock_container_action.assert_called_once_with( @@ -843,8 +884,10 @@ class TestContainerController(api_base.FunctionalTest): with patch.object(test_container_obj, 'save') as mock_save: params = {'name': 'new_name'} container_uuid = test_container.get('uuid') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.post('/v1/containers/%s/rename' % - container_uuid, params=params) + container_uuid, params=params, + headers=headers) mock_save.assert_called_once() self.assertEqual(200, response.status_int) @@ -859,9 +902,10 @@ class TestContainerController(api_base.FunctionalTest): container_name = test_container.get('name') params = {'name': container_name} + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.assertRaises(AppError, self.app.post, '/v1/containers/%s/rename' % - container_uuid, params=params) + container_uuid, params=params, headers=headers) @patch('zun.objects.Container.get_by_name') def test_rename_with_invalid_name_by_uuid(self, @@ -877,8 +921,9 @@ class TestContainerController(api_base.FunctionalTest): params = {'name': value} with self.assertRaisesRegex(AppError, "Invalid input for query parameters"): + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.app.post('/v1/containers/%s/rename' % - container_uuid, params=params) + container_uuid, params=params, headers=headers) @patch('zun.common.utils.validate_container_state') @patch('zun.compute.api.API.container_start') @@ -896,8 +941,9 @@ class TestContainerController(api_base.FunctionalTest): uuid=uuid, status='Running') with self.assertRaisesRegex( AppError, "Cannot start container %s in Running state" % uuid): + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.app.post('/v1/containers/%s/%s/' % (test_object.uuid, - 'start')) + 'start'), headers=headers) @patch('zun.common.utils.validate_container_state') @patch('zun.compute.api.API.container_stop') @@ -928,8 +974,9 @@ class TestContainerController(api_base.FunctionalTest): uuid=uuid, status='Stopped') with self.assertRaisesRegex( AppError, "Cannot stop container %s in Stopped state" % uuid): + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.app.post('/v1/containers/%s/%s/' % (test_object.uuid, - 'stop')) + 'stop'), headers=headers) @patch('zun.common.utils.validate_container_state') @patch('zun.compute.api.API.container_pause') @@ -947,8 +994,9 @@ class TestContainerController(api_base.FunctionalTest): uuid=uuid, status='Stopped') with self.assertRaisesRegex( AppError, "Cannot pause container %s in Stopped state" % uuid): + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.app.post('/v1/containers/%s/%s/' % (test_object.uuid, - 'pause')) + 'pause'), headers=headers) @patch('zun.common.utils.validate_container_state') @patch('zun.compute.api.API.container_unpause') @@ -967,8 +1015,10 @@ class TestContainerController(api_base.FunctionalTest): with self.assertRaisesRegex( AppError, "Cannot unpause container %s in Running state" % uuid): + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.app.post('/v1/containers/%s/%s/' % (test_object.uuid, - 'unpause')) + 'unpause'), + headers=headers) @patch('zun.common.utils.validate_container_state') @patch('zun.compute.api.API.container_reboot') @@ -987,8 +1037,10 @@ class TestContainerController(api_base.FunctionalTest): uuid=uuid, status='Paused') with self.assertRaisesRegex( AppError, "Cannot reboot container %s in Paused state" % uuid): + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.app.post('/v1/containers/%s/%s/' % (test_object.uuid, - 'reboot')) + 'reboot'), + headers=headers) @patch('zun.common.utils.validate_container_state') @patch('zun.compute.api.API.container_reboot') @@ -1010,7 +1062,9 @@ class TestContainerController(api_base.FunctionalTest): mock_get_by_uuid.return_value = test_container_obj container_uuid = test_container.get('uuid') - response = self.app.get('/v1/containers/%s/logs/' % container_uuid) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.get('/v1/containers/%s/logs/' % container_uuid, + headers=headers) self.assertEqual(200, response.status_int) mock_container_logs.assert_called_once_with( @@ -1026,9 +1080,11 @@ class TestContainerController(api_base.FunctionalTest): mock_get_by_uuid.return_value = test_container_obj container_uuid = test_container.get('uuid') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.get( '/v1/containers/%s/logs?stderr=True&stdout=True' - '×tamps=False&tail=1&since=100000000' % container_uuid) + '×tamps=False&tail=1&since=100000000' % container_uuid, + headers=headers) self.assertEqual(200, response.status_int) mock_container_logs.assert_called_once_with( mock.ANY, test_container_obj, True, True, False, '1', '100000000') @@ -1041,8 +1097,10 @@ class TestContainerController(api_base.FunctionalTest): mock_get_by_uuid.return_value = test_container_obj container_uuid = test_container.get('uuid') + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.assertRaises(AppError, self.app.post, - '/v1/containers/%s/logs/' % container_uuid) + '/v1/containers/%s/logs/' % container_uuid, + headers=headers) self.assertFalse(mock_container_logs.called) @patch('zun.compute.api.API.container_logs') @@ -1059,9 +1117,10 @@ class TestContainerController(api_base.FunctionalTest): container_uuid = test_container.get('uuid') params = {'since': value} + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.assertRaises(AppError, self.app.post, '/v1/containers/%s/logs' % - container_uuid, params) + container_uuid, params, headers=headers) self.assertFalse(mock_container_logs.called) def test_get_logs_with_invalid_state(self): @@ -1071,7 +1130,9 @@ class TestContainerController(api_base.FunctionalTest): with self.assertRaisesRegex( AppError, "Cannot logs container %s in Creating state" % uuid): - self.app.get('/v1/containers/%s/logs/' % test_object.uuid) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + self.app.get('/v1/containers/%s/logs/' % test_object.uuid, + headers=headers) @patch('zun.common.utils.validate_container_state') @patch('zun.compute.api.API.container_exec') @@ -1086,7 +1147,8 @@ class TestContainerController(api_base.FunctionalTest): container_uuid = test_container.get('uuid') url = '/v1/containers/%s/%s/' % (container_uuid, 'execute') cmd = {'command': 'ls'} - response = self.app.post(url, cmd) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.post(url, cmd, headers=headers) self.assertEqual(200, response.status_int) mock_container_exec.assert_called_once_with( mock.ANY, test_container_obj, cmd['command'], True, False) @@ -1099,8 +1161,10 @@ class TestContainerController(api_base.FunctionalTest): with self.assertRaisesRegex( AppError, "Cannot execute container %s in Stopped state" % uuid): + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.app.post('/v1/containers/%s/%s/' % (test_object.uuid, - 'execute'), cmd) + 'execute'), cmd, + headers=headers) @patch('zun.common.utils.validate_container_state') @patch('zun.compute.api.API.container_exec') @@ -1114,9 +1178,10 @@ class TestContainerController(api_base.FunctionalTest): container_uuid = test_container.get('uuid') cmd = {'command': ''} + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.assertRaises(AppError, self.app.post, '/v1/containers/%s/execute' % - container_uuid, cmd) + container_uuid, cmd, headers=headers) self.assertFalse(mock_container_exec.called) @patch('zun.common.utils.validate_container_state') @@ -1129,7 +1194,9 @@ class TestContainerController(api_base.FunctionalTest): mock_get_by_uuid.return_value = test_container_obj container_uuid = test_container.get('uuid') - response = self.app.delete('/v1/containers/%s/' % container_uuid) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.delete('/v1/containers/%s/' % container_uuid, + headers=headers) self.assertEqual(204, response.status_int) mock_container_delete.assert_called_once_with( @@ -1148,8 +1215,9 @@ class TestContainerController(api_base.FunctionalTest): mock_get_by_uuid.return_value = test_container_obj container_uuid = test_container.get('uuid') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.delete('/v1/containers/%s/?all_tenants=1' % - container_uuid) + container_uuid, headers=headers) self.assertEqual(204, response.status_int) mock_container_delete.assert_called_once_with( @@ -1164,7 +1232,9 @@ class TestContainerController(api_base.FunctionalTest): with self.assertRaisesRegex( AppError, "Cannot delete container %s in Running state" % uuid): - self.app.delete('/v1/containers/%s' % (test_object.uuid)) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + self.app.delete('/v1/containers/%s' % (test_object.uuid), + headers=headers) def test_delete_force_by_uuid_invalid_state(self): uuid = uuidutils.generate_uuid() @@ -1173,15 +1243,18 @@ class TestContainerController(api_base.FunctionalTest): with self.assertRaisesRegex( AppError, "Cannot delete_force container %s in Paused state" % uuid): - self.app.delete('/v1/containers/%s?force=True' % test_object.uuid) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + self.app.delete('/v1/containers/%s?force=True' % test_object.uuid, + headers=headers) @patch('zun.compute.api.API.container_delete') def test_delete_by_uuid_invalid_state_force_true(self, mock_delete): uuid = uuidutils.generate_uuid() test_object = utils.create_test_container(context=self.context, uuid=uuid, status='Running') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.delete('/v1/containers/%s?force=True' % ( - test_object.uuid)) + test_object.uuid), headers=headers) self.assertEqual(204, response.status_int) @patch('zun.compute.api.API.container_delete') @@ -1190,14 +1263,17 @@ class TestContainerController(api_base.FunctionalTest): test_object = utils.create_test_container(context=self.context, uuid=uuid) mock_delete.side_effect = exception.InvalidValue + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.assertRaises(AppError, self.app.delete, - '/v1/containers/%s?force=wrong' % test_object.uuid) + '/v1/containers/%s?force=wrong' % test_object.uuid, + headers=headers) self.assertTrue(mock_delete.not_called) def test_delete_container_with_uuid_not_found(self): uuid = uuidutils.generate_uuid() + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.assertRaises(AppError, self.app.delete, - '/v1/containers/%s' % uuid) + '/v1/containers/%s' % uuid, headers=headers) @patch('zun.common.utils.validate_container_state') @patch('zun.compute.api.API.container_kill') @@ -1215,7 +1291,8 @@ class TestContainerController(api_base.FunctionalTest): container_uuid = test_container.get('uuid') url = '/v1/containers/%s/%s/' % (container_uuid, 'kill') cmd = {'signal': '9'} - response = self.app.post(url, cmd) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.post(url, cmd, headers=headers) self.assertEqual(202, response.status_int) mock_container_kill.assert_called_once_with( mock.ANY, test_container_obj, cmd['signal']) @@ -1227,8 +1304,10 @@ class TestContainerController(api_base.FunctionalTest): body = {'signal': 9} with self.assertRaisesRegex( AppError, "Cannot kill container %s in Stopped state" % uuid): + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.app.post('/v1/containers/%s/%s/' % (test_object.uuid, - 'kill'), body) + 'kill'), body, + headers=headers) @patch('zun.common.utils.validate_container_state') @patch('zun.compute.api.API.container_kill') @@ -1244,8 +1323,10 @@ class TestContainerController(api_base.FunctionalTest): mock_container_kill.side_effect = Exception container_uuid = "edfe2a25-2901-438d-8157-fffffd68d051" + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.assertRaises(AppError, self.app.post, - '/v1/containers/%s/%s/' % (container_uuid, 'kill')) + '/v1/containers/%s/%s/' % (container_uuid, 'kill'), + headers=headers) self.assertTrue(mock_container_kill.called) @patch('zun.common.utils.validate_container_state') @@ -1262,8 +1343,10 @@ class TestContainerController(api_base.FunctionalTest): mock_container_kill.side_effect = Exception container_uuid = test_container.get('uuid') + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.assertRaises(AppError, self.app.post, - '/v1/containers/%s/%s/' % (container_uuid, 'kill')) + '/v1/containers/%s/%s/' % (container_uuid, 'kill'), + headers=headers) self.assertTrue(mock_container_kill.called) @patch('zun.compute.api.API.container_kill') @@ -1282,8 +1365,9 @@ class TestContainerController(api_base.FunctionalTest): params = {'signal': value} with self.assertRaisesRegex( AppError, "Bad response: 400 Bad Request"): + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.app.post('/v1/containers/%s/kill/' % - container_uuid, params) + container_uuid, params, headers=headers) self.assertFalse(mock_container_kill.called) @patch('zun.network.neutron.NeutronAPI.get_available_network') @@ -1298,8 +1382,9 @@ class TestContainerController(api_base.FunctionalTest): '"command": "env", "memory": "512",' '"environment": {"key1": "val1", "key2": "val2"},' '"image_driver": "glance"}') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.post('/v1/containers/', - params=params, + params=params, headers=headers, content_type='application/json') self.assertEqual(202, response.status_int) self.assertIn('image_driver', response.json.keys()) @@ -1316,7 +1401,9 @@ class TestContainerController(api_base.FunctionalTest): mock_get_by_uuid.return_value = test_container_obj container_uuid = test_container.get('uuid') - response = self.app.get('/v1/containers/%s/attach/' % container_uuid) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.get('/v1/containers/%s/attach/' % container_uuid, + headers=headers) self.assertEqual(200, response.status_int) mock_container_attach.assert_called_once_with( @@ -1336,8 +1423,10 @@ class TestContainerController(api_base.FunctionalTest): mock_container_attach.side_effect = Exception container_uuid = test_container.get('uuid') + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.assertRaises(AppError, self.app.get, - '/v1/containers/%s/attach/' % container_uuid) + '/v1/containers/%s/attach/' % container_uuid, + headers=headers) self.assertTrue(mock_container_attach.called) @patch('zun.common.utils.validate_container_state') @@ -1357,7 +1446,8 @@ class TestContainerController(api_base.FunctionalTest): container_name = test_container.get('name') url = '/v1/containers/%s/%s/' % (container_name, 'resize') cmd = {'h': '100', 'w': '100'} - response = self.app.post(url, cmd) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.post(url, cmd, headers=headers) self.assertEqual(200, response.status_int) mock_container_resize.assert_called_once_with( mock.ANY, test_container_obj, cmd['h'], cmd['w']) @@ -1377,9 +1467,10 @@ class TestContainerController(api_base.FunctionalTest): container_uuid = test_container.get('uuid') body = {'h': '100', 'w': '100'} + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.assertRaises(AppError, self.app.post, '/v1/containers/%s/%s/' % - (container_uuid, 'resize'), body) + (container_uuid, 'resize'), body, headers=headers) self.assertTrue(mock_container_resize.called) @patch('zun.common.utils.validate_container_state') @@ -1393,8 +1484,9 @@ class TestContainerController(api_base.FunctionalTest): mock_get_by_uuid.return_value = test_container_obj container_uuid = test_container.get('uuid') + headers = {'OpenStack-API-Version': CURRENT_VERSION} response = self.app.get('/v1/containers/%s/top?ps_args=aux' % - container_uuid) + container_uuid, headers=headers) self.assertEqual(200, response.status_int) self.assertTrue(mock_container_top.called) @@ -1410,9 +1502,10 @@ class TestContainerController(api_base.FunctionalTest): mock_container_top.side_effect = Exception container_uuid = test_container.get('uuid') + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.assertRaises(AppError, self.app.get, '/v1/containers/%s/top?ps_args=kkkk' % - container_uuid) + container_uuid, headers=headers) self.assertTrue(mock_container_top.called) @patch('zun.common.utils.validate_container_state') @@ -1430,7 +1523,8 @@ class TestContainerController(api_base.FunctionalTest): container_uuid = test_container.get('uuid') url = '/v1/containers/%s/%s/' % (container_uuid, 'get_archive') cmd = {'path': '/home/1.txt'} - response = self.app.get(url, cmd) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.get(url, cmd, headers=headers) self.assertEqual(200, response.status_int) container_get_archive.assert_called_once_with( mock.ANY, test_container_obj, cmd['path']) @@ -1442,8 +1536,10 @@ class TestContainerController(api_base.FunctionalTest): with self.assertRaisesRegex( AppError, "Cannot get_archive container %s in Error state" % uuid): + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.app.get('/v1/containers/%s/%s/' % (test_object.uuid, - 'get_archive')) + 'get_archive'), + headers=headers) @patch('zun.common.utils.validate_container_state') @patch('zun.compute.api.API.container_put_archive') @@ -1461,7 +1557,8 @@ class TestContainerController(api_base.FunctionalTest): url = '/v1/containers/%s/%s/' % (container_uuid, 'put_archive') cmd = {'path': '/home/', 'data': '/home/1.tar'} - response = self.app.post(url, cmd) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.post(url, cmd, headers=headers) self.assertEqual(200, response.status_int) container_put_archive.assert_called_once_with( mock.ANY, test_container_obj, cmd['path'], cmd['data']) @@ -1473,8 +1570,10 @@ class TestContainerController(api_base.FunctionalTest): with self.assertRaisesRegex( AppError, "Cannot put_archive container %s in Error state" % uuid): + headers = {'OpenStack-API-Version': CURRENT_VERSION} self.app.post('/v1/containers/%s/%s/' % (test_object.uuid, - 'put_archive')) + 'put_archive'), + headers=headers) @patch('zun.common.utils.validate_container_state') @patch('zun.compute.api.API.container_stats') @@ -1489,7 +1588,8 @@ class TestContainerController(api_base.FunctionalTest): container_uuid = test_container.get('uuid') url = '/v1/containers/%s/stats'\ % container_uuid - response = self.app.get(url) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.get(url, headers=headers) self.assertEqual(200, response.status_int) self.assertTrue(mock_container_stats.called) @@ -1508,7 +1608,8 @@ class TestContainerController(api_base.FunctionalTest): container_name = test_container.get('name') url = '/v1/containers/%s/%s/' % (container_name, 'commit') cmd = {'repository': 'repo', 'tag': 'tag'} - response = self.app.post(url, cmd) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.post(url, cmd, headers=headers) self.assertEqual(202, response.status_int) mock_container_commit.assert_called_once_with( mock.ANY, test_container_obj, cmd['repository'], cmd['tag']) @@ -1528,7 +1629,8 @@ class TestContainerController(api_base.FunctionalTest): container_uuid = test_container.get('uuid') url = '/v1/containers/%s/%s/' % (container_uuid, 'commit') cmd = {'repository': 'repo', 'tag': 'tag'} - response = self.app.post(url, cmd) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.post(url, cmd, headers=headers) self.assertEqual(202, response.status_int) mock_container_commit.assert_called_once_with( mock.ANY, test_container_obj, cmd['repository'], cmd['tag']) @@ -1540,7 +1642,9 @@ class TestContainerController(api_base.FunctionalTest): uuid=uuid, status='Error') with self.assertRaisesRegex( AppError, "Cannot commit container %s in Error state" % uuid): - self.app.post('/v1/containers/%s/commit/' % uuid, cmd) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + self.app.post('/v1/containers/%s/commit/' % uuid, cmd, + headers=headers) @patch('zun.common.utils.validate_container_state') @patch('zun.compute.api.API.container_exec_resize') @@ -1555,7 +1659,8 @@ class TestContainerController(api_base.FunctionalTest): fake_exec_id = ('7df36611fa1fc855618c2c643835d41d' 'ac3fe568e7688f0bae66f7bcb3cccc6c') kwargs = {'exec_id': fake_exec_id, 'h': '100', 'w': '100'} - response = self.app.post(url, kwargs) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.post(url, kwargs, headers=headers) self.assertEqual(200, response.status_int) mock_exec_resize.assert_called_once_with( mock.ANY, test_container_obj, fake_exec_id, kwargs['h'], @@ -1580,7 +1685,8 @@ class TestContainerController(api_base.FunctionalTest): url = '/v1/containers/%s/%s?name=%s' % (container_name, 'add_security_group', default_security_group) - response = self.app.post(url, expect_errors=True) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.post(url, headers=headers, expect_errors=True) self.assertEqual(400, response.status_int) self.assertEqual('application/json', response.content_type) self.assertEqual( @@ -1603,7 +1709,8 @@ class TestContainerController(api_base.FunctionalTest): url = '/v1/containers/%s/%s?uuid=%s' % (container_name, 'add_security_group', security_group_id_to_add) - response = self.app.post(url) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.post(url, headers=headers) self.assertEqual(202, response.status_int) self.assertEqual('application/json', response.content_type) mock_add_security_group.assert_called_once_with( @@ -1625,18 +1732,41 @@ class TestContainerController(api_base.FunctionalTest): url = '/v1/containers/%s/%s?uuid=%s' % (container_name, 'add_security_group', invalid_uuid) - response = self.app.post(url, expect_errors=True) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.post(url, headers=headers, expect_errors=True) self.assertEqual(400, response.status_int) self.assertEqual('application/json', response.content_type) self.assertEqual( "Expected a uuid but received %(uuid)s." % {'uuid': invalid_uuid}, response.json['errors'][0]['detail']) + @patch('zun.network.neutron.NeutronAPI.get_neutron_network') + @patch('zun.compute.api.API.network_detach') + @patch('zun.objects.Container.get_by_uuid') + def test_network_detach(self, mock_by_uuid, mock_detach, mock_get_network): + test_container = utils.get_test_container() + test_container_obj = objects.Container(self.context, **test_container) + mock_by_uuid.return_value = test_container_obj + container_uuid = test_container.get('uuid') + mock_get_network.return_value = {'id': 'private'} + mock_detach.return_value = None + url = '/v1/containers/%s/%s?network=%s' % (container_uuid, + 'network_detach', + 'private') + headers = {'OpenStack-API-Version': CURRENT_VERSION} + response = self.app.post(url, + headers=headers) + self.assertEqual(202, response.status_int) + mock_detach.assert_called_once_with(mock.ANY, test_container_obj, + mock.ANY) + class TestContainerEnforcement(api_base.FunctionalTest): def _common_policy_check(self, rule, func, *arg, **kwarg): self.policy.set_rules({rule: 'project_id:non_fake'}) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + kwarg['headers'] = headers response = func(*arg, **kwarg) self.assertEqual(403, response.status_int) self.assertEqual('application/json', response.content_type) @@ -1703,6 +1833,8 @@ class TestContainerEnforcement(api_base.FunctionalTest): def _owner_check(self, rule, func, *args, **kwargs): self.policy.set_rules({rule: "user_id:%(user_id)s"}) + headers = {'OpenStack-API-Version': CURRENT_VERSION} + kwargs['headers'] = headers response = func(*args, **kwargs) self.assertEqual(403, response.status_int) self.assertEqual('application/json', response.content_type) diff --git a/zun/tests/unit/compute/test_compute_manager.py b/zun/tests/unit/compute/test_compute_manager.py index f9124da9a..a5b1e5bee 100644 --- a/zun/tests/unit/compute/test_compute_manager.py +++ b/zun/tests/unit/compute/test_compute_manager.py @@ -593,3 +593,9 @@ class TestManager(base.TestCase): self.assertRaises(exception.DockerError, self.compute_manager._do_container_commit, self.context, container, 'repo', 'tag') + + @mock.patch.object(fake_driver, 'network_detach') + def test_container_network_detach(self, mock_detach): + container = Container(self.context, **utils.get_test_container()) + self.compute_manager.network_detach(self.context, container, 'network') + mock_detach.assert_called_once_with(self.context, container, mock.ANY) diff --git a/zun/tests/unit/container/docker/test_docker_driver.py b/zun/tests/unit/container/docker/test_docker_driver.py index 5e68b0b58..31693b75c 100644 --- a/zun/tests/unit/container/docker/test_docker_driver.py +++ b/zun/tests/unit/container/docker/test_docker_driver.py @@ -465,6 +465,15 @@ class TestDockerDriver(base.DriverTestCase): self.assertEqual('10000000/0', stats_info['BLOCK I/O(B)']) self.assertEqual('200/200', stats_info['NET I/O(B)']) + @mock.patch('zun.network.kuryr_network.KuryrNetwork' + '.disconnect_container_from_network') + def test_network_detach(self, mock_detach): + mock_container = mock.MagicMock() + self.driver.network_detach(self.context, mock_container, 'network') + mock_detach.assert_called_once_with(mock_container, + 'network-fake_project', + mock.ANY) + class TestNovaDockerDriver(base.DriverTestCase): def setUp(self):