diff --git a/zun/compute/manager.py b/zun/compute/manager.py index a2f5615d0..9b1577181 100644 --- a/zun/compute/manager.py +++ b/zun/compute/manager.py @@ -40,6 +40,10 @@ class Manager(object): self.driver = driver.load_container_driver(container_driver) self.host = CONF.host self._resource_tracker = None + if self._use_sandbox(): + self.use_sandbox = True + else: + self.use_sandbox = False def _fail_container(self, context, container, error, unset_host=False): container.status = consts.ERROR @@ -66,9 +70,13 @@ class Manager(object): if created_container: self._do_container_start(context, created_container) - def _do_sandbox_cleanup(self, context, container, sandbox_id): + def _do_sandbox_cleanup(self, context, container): + sandbox_id = container.get_sandbox_id() + if sandbox_id is None: + return + try: - self.driver.delete_sandbox(context, container, sandbox_id) + self.driver.delete_sandbox(context, container) except Exception as e: LOG.error("Error occurred while deleting sandbox: %s", six.text_type(e)) @@ -90,10 +98,12 @@ class Manager(object): self._fail_container(self, context, container, msg) return - sandbox_id = self._create_sandbox(context, container, - requested_networks, reraise) - if sandbox_id is None: - return + sandbox_id = None + if self.use_sandbox: + sandbox_id = self._create_sandbox(context, container, + requested_networks, reraise) + if sandbox_id is None: + return self._update_task_state(context, container, consts.IMAGE_PULLING) repo, tag = utils.parse_image_name(container.image) @@ -108,21 +118,21 @@ class Manager(object): except exception.ImageNotFound as e: with excutils.save_and_reraise_exception(reraise=reraise): LOG.error(six.text_type(e)) - self._do_sandbox_cleanup(context, container, sandbox_id) + self._do_sandbox_cleanup(context, container) self._fail_container(context, container, six.text_type(e)) return except exception.DockerError as e: with excutils.save_and_reraise_exception(reraise=reraise): LOG.error("Error occurred while calling Docker image API: %s", six.text_type(e)) - self._do_sandbox_cleanup(context, container, sandbox_id) + self._do_sandbox_cleanup(context, container) self._fail_container(context, container, six.text_type(e)) return except Exception as e: with excutils.save_and_reraise_exception(reraise=reraise): LOG.exception("Unexpected exception: %s", six.text_type(e)) - self._do_sandbox_cleanup(context, container, sandbox_id) + self._do_sandbox_cleanup(context, container) self._fail_container(context, container, six.text_type(e)) return @@ -134,15 +144,14 @@ class Manager(object): rt = self._get_resource_tracker() with rt.container_claim(context, container, container.host, limits): - container = self.driver.create(context, container, - sandbox_id, image) + container = self.driver.create(context, container, image) self._update_task_state(context, container, None) return container except exception.DockerError as e: with excutils.save_and_reraise_exception(reraise=reraise): LOG.error("Error occurred while calling Docker create API: %s", six.text_type(e)) - self._do_sandbox_cleanup(context, container, sandbox_id) + self._do_sandbox_cleanup(context, container) self._fail_container(context, container, six.text_type(e), unset_host=True) return @@ -150,11 +159,24 @@ class Manager(object): with excutils.save_and_reraise_exception(reraise=reraise): LOG.exception("Unexpected exception: %s", six.text_type(e)) - self._do_sandbox_cleanup(context, container, sandbox_id) + self._do_sandbox_cleanup(context, container) self._fail_container(context, container, six.text_type(e), unset_host=True) return + def _use_sandbox(self): + if CONF.use_sandbox and self.driver.capabilities["support_sandbox"]: + return True + elif (not CONF.use_sandbox and + self.driver.capabilities["support_standalone"]): + return False + else: + raise exception.ZunException(_( + "The configuration of use_sandbox '%(use_sandbox)s' is not " + "supported by driver '%(driver)s'.") % + {'use_sandbox': CONF.use_sandbox, + 'driver': self.driver}) + def _create_sandbox(self, context, container, requested_networks, reraise=False): self._update_task_state(context, container, consts.SANDBOX_CREATING) @@ -202,7 +224,7 @@ class Manager(object): self._update_task_state(context, container, consts.CONTAINER_DELETING) reraise = not force try: - self.driver.delete(container, force) + self.driver.delete(context, container, force) except exception.DockerError as e: with excutils.save_and_reraise_exception(reraise=reraise): LOG.error(("Error occurred while calling Docker " @@ -213,7 +235,9 @@ class Manager(object): LOG.exception("Unexpected exception: %s", six.text_type(e)) self._fail_container(context, container, six.text_type(e)) - self._delete_sandbox(context, container, reraise) + if self.use_sandbox: + self._delete_sandbox(context, container, reraise) + self._update_task_state(context, container, None) container.destroy(context) self._get_resource_tracker() @@ -224,12 +248,12 @@ class Manager(object): return container def _delete_sandbox(self, context, container, reraise=False): - sandbox_id = self.driver.get_sandbox_id(container) + sandbox_id = container.get_sandbox_id() if sandbox_id: self._update_task_state(context, container, consts.SANDBOX_DELETING) try: - self.driver.delete_sandbox(context, container, sandbox_id) + self.driver.delete_sandbox(context, container) except Exception as e: with excutils.save_and_reraise_exception(reraise=reraise): LOG.exception("Unexpected exception: %s", six.text_type(e)) @@ -242,9 +266,7 @@ class Manager(object): def _add_security_group(self, context, container, security_group): LOG.debug('Adding security_group to container: %s', container.uuid) try: - sandbox_id = self.driver.get_sandbox_id(container) - self.driver.add_security_group(context, container, security_group, - sandbox_id=sandbox_id) + self.driver.add_security_group(context, container, security_group) container.security_groups += [security_group] container.save(context) except Exception as e: diff --git a/zun/conf/container_driver.py b/zun/conf/container_driver.py index 5b13c0997..60de8e223 100644 --- a/zun/conf/container_driver.py +++ b/zun/conf/container_driver.py @@ -37,6 +37,16 @@ Interdependencies to other options: cfg.StrOpt('floating_cpu_set', default="", help='Define the cpusets to be excluded from pinning'), + cfg.BoolOpt('use_sandbox', + default=False, + help="""Whether to use infra container. If set to True, +Zun will create an infra container that serves as a placeholder of a few +Linux namespaces (i.e. network namespace). Then, one or multiple containers +could join the namespaces of the infra container thus sharing resources inside +the sandbox (i.e. the network interface). This is typically used to group +a set of high-coupled containers into a unit. If set to False, infra container +won't be created. +""") ] diff --git a/zun/container/docker/driver.py b/zun/container/docker/driver.py index 7aa62e050..e10dc5b7e 100644 --- a/zun/container/docker/driver.py +++ b/zun/container/docker/driver.py @@ -76,6 +76,10 @@ def wrap_docker_error(function): class DockerDriver(driver.ContainerDriver): """Implementation of container drivers for Docker.""" + capabilities = { + "support_sandbox": True, + "support_standalone": True, + } def __init__(self): super(DockerDriver, self).__init__() @@ -102,12 +106,24 @@ class DockerDriver(driver.ContainerDriver): with docker_utils.docker_client() as docker: return docker.images(repo, quiet) - def create(self, context, container, sandbox_id, image): + def create(self, context, container, image, requested_networks=None): + sandbox_id = container.get_sandbox_id() + network_standalone = False if sandbox_id else True + with docker_utils.docker_client() as docker: + network_api = zun_network.api(context=context, docker_api=docker) name = container.name image = container.image LOG.debug('Creating container with image %(image)s name %(name)s', {'image': image, 'name': name}) + if requested_networks is None: + if network_standalone: + network = self._provision_network(context, container, + network_api) + requested_networks = [{'network': network['Name'], + 'port': '', + 'v4-fixed-ip': '', + 'v6-fixed-ip': ''}] kwargs = { 'name': self.get_container_name(container), @@ -120,12 +136,13 @@ class DockerDriver(driver.ContainerDriver): } host_config = {} - host_config['network_mode'] = 'container:%s' % sandbox_id - # TODO(hongbin): Uncomment this after docker-py add support for - # container mode for pid namespace. - # host_config['pid_mode'] = 'container:%s' % sandbox_id - host_config['ipc_mode'] = 'container:%s' % sandbox_id - host_config['volumes_from'] = sandbox_id + if sandbox_id: + host_config['network_mode'] = 'container:%s' % sandbox_id + # TODO(hongbin): Uncomment this after docker-py add support for + # container mode for pid namespace. + # host_config['pid_mode'] = 'container:%s' % sandbox_id + host_config['ipc_mode'] = 'container:%s' % sandbox_id + host_config['volumes_from'] = sandbox_id if container.auto_remove: host_config['auto_remove'] = container.auto_remove if container.memory is not None: @@ -142,6 +159,12 @@ class DockerDriver(driver.ContainerDriver): response = docker.create_container(image, **kwargs) container.container_id = response['Id'] + + if network_standalone: + addresses = self._setup_network_for_container( + context, container, requested_networks, network_api) + container.addresses = addresses + container.status = consts.CREATED container.status_reason = None container.save(context) @@ -160,26 +183,32 @@ class DockerDriver(driver.ContainerDriver): def _setup_network_for_container(self, context, container, requested_networks, network_api): - sandbox_id = self.get_sandbox_id(container) security_group_ids = self._get_security_group_ids( context, container.security_groups) # Container connects to the bridge network by default so disconnect # the container from it before connecting it to neutron network. # This avoids potential conflict between these two networks. - network_api.disconnect_container_from_network(container, 'bridge', - sandbox_id) + network_api.disconnect_container_from_network(container, 'bridge') addresses = {} for network in requested_networks: network_name = network['network'] addrs = network_api.connect_container_to_network( - container, network_name, sandbox_id=sandbox_id, - security_groups=security_group_ids) + container, network_name, security_groups=security_group_ids) addresses[network_name] = addrs return addresses - def delete(self, container, force): + def delete(self, context, container, force): + teardown_network = True + if container.get_sandbox_id(): + teardown_network = False + with docker_utils.docker_client() as docker: + if teardown_network: + network_api = zun_network.api(context=context, + docker_api=docker) + self._cleanup_network_for_container(container, network_api) + if container.container_id: try: docker.remove_container(container.container_id, @@ -189,11 +218,9 @@ class DockerDriver(driver.ContainerDriver): return raise - def _cleanup_network_for_container(self, container, network_api, - sandbox_id): + def _cleanup_network_for_container(self, container, network_api): for name in container.addresses: - network_api.disconnect_container_from_network(container, name, - sandbox_id) + network_api.disconnect_container_from_network(container, name) def list(self, context): id_to_container_map = {} @@ -619,9 +646,13 @@ class DockerDriver(driver.ContainerDriver): name = self.get_sandbox_name(container) sandbox = docker.create_container(image, name=name, hostname=name[:63]) - self.set_sandbox_id(container, sandbox['Id']) + container.set_sandbox_id(sandbox['Id']) addresses = self._setup_network_for_container( context, container, requested_networks, network_api) + if addresses is None: + raise exception.ZunException(_( + "Unexpected missing of addresses")) + container.addresses = addresses container.save(context) @@ -669,11 +700,11 @@ class DockerDriver(driver.ContainerDriver): return docker_networks[0] - def delete_sandbox(self, context, container, sandbox_id): + def delete_sandbox(self, context, container): + sandbox_id = container.get_sandbox_id() with docker_utils.docker_client() as docker: network_api = zun_network.api(context=context, docker_api=docker) - self._cleanup_network_for_container(container, network_api, - sandbox_id) + self._cleanup_network_for_container(container, network_api) try: docker.remove_container(sandbox_id, force=True) except errors.APIError as api_error: @@ -685,19 +716,6 @@ class DockerDriver(driver.ContainerDriver): with docker_utils.docker_client() as docker: docker.stop(sandbox_id) - def get_sandbox_id(self, container): - if container.meta: - return container.meta.get('sandbox_id', None) - else: - LOG.warning("Unexpected missing of sandbox_id") - return None - - def set_sandbox_id(self, container, sandbox_id): - if container.meta is None: - container.meta = {'sandbox_id': sandbox_id} - else: - container.meta['sandbox_id'] = sandbox_id - def get_sandbox_name(self, container): return 'zun-sandbox-' + container.uuid @@ -754,13 +772,18 @@ class DockerDriver(driver.ContainerDriver): sandbox = docker.inspect_container(sandbox_id) for network in sandbox["NetworkSettings"]["Networks"]: network_api.add_security_groups_to_ports( - container, security_group_ids, sandbox_id) + container, security_group_ids) def get_available_nodes(self): return [self._host.get_hostname()] class NovaDockerDriver(DockerDriver): + capabilities = { + "support_sandbox": True, + "support_standalone": False, + } + def add_security_group(self, context, container, security_group, **kwargs): msg = "NovaDockerDriver does not support security_groups" raise exception.ZunException(msg) @@ -839,7 +862,7 @@ class NovaDockerDriver(DockerDriver): def get_addresses(self, context, container): elevated = context.elevated() novaclient = nova.NovaClient(elevated) - sandbox_id = self.get_sandbox_id(container) + sandbox_id = container.get_sandbox_id() if sandbox_id: server_name = self._find_server_by_container_id(sandbox_id) if server_name: diff --git a/zun/container/driver.py b/zun/container/driver.py index 963118db9..75b4fab85 100644 --- a/zun/container/driver.py +++ b/zun/container/driver.py @@ -68,7 +68,7 @@ class ContainerDriver(object): """Commit a container.""" raise NotImplementedError() - def delete(self, container, force): + def delete(self, context, container, force): """Delete a container.""" raise NotImplementedError() @@ -163,14 +163,6 @@ class ContainerDriver(object): """Stop a sandbox.""" raise NotImplementedError() - def get_sandbox_id(self, container): - """Retrieve sandbox ID.""" - raise NotImplementedError() - - def set_sandbox_id(self, container, sandbox_id): - """Set sandbox ID.""" - raise NotImplementedError() - def get_sandbox_name(self, container): """Retrieve sandbox name.""" raise NotImplementedError() diff --git a/zun/network/kuryr_network.py b/zun/network/kuryr_network.py index 1ddae308a..447b53d9d 100644 --- a/zun/network/kuryr_network.py +++ b/zun/network/kuryr_network.py @@ -116,15 +116,15 @@ class KuryrNetwork(network.Network): return self.docker.networks(**kwargs) def connect_container_to_network(self, container, network_name, - sandbox_id=None, security_groups=None): + security_groups=None): """Connect container to the network This method will create a neutron port, retrieve the ip address(es) of the port, and pass them to docker.connect_container_to_network. """ - container_id = container.container_id - if sandbox_id: - container_id = sandbox_id + container_id = container.get_sandbox_id() + if not container_id: + container_id = container.container_id network = self.inspect_network(network_name) neutron_net_id = network['Options']['neutron.net.uuid'] @@ -166,11 +166,10 @@ class KuryrNetwork(network.Network): container_id, network_name, **kwargs) return addresses - def disconnect_container_from_network(self, container, network_name, - sandbox_id=None): - container_id = container.container_id - if sandbox_id: - container_id = sandbox_id + def disconnect_container_from_network(self, container, network_name): + container_id = container.get_sandbox_id() + if not container_id: + container_id = container.container_id neutron_ports = set() if container.addresses: @@ -190,8 +189,11 @@ class KuryrNetwork(network.Network): 'or neutron tag extension does not supported or' ' not enabled.') - def add_security_groups_to_ports(self, container, security_group_ids, - sandbox_id=None): + def add_security_groups_to_ports(self, container, security_group_ids): + container_id = container.get_sandbox_id() + if not container_id: + container_id = container.container_id + port_ids = set() for addrs_list in container.addresses.values(): for addr in addrs_list: diff --git a/zun/network/network.py b/zun/network/network.py index 85098e515..7efd580a9 100644 --- a/zun/network/network.py +++ b/zun/network/network.py @@ -58,6 +58,6 @@ class Network(object): **kwargs): raise NotImplementedError() - def add_security_groups_to_ports(self, container, network_name, - security_group_ids, **kwargs): + def add_security_groups_to_ports(self, container, security_group_ids, + **kwargs): raise NotImplementedError() diff --git a/zun/objects/container.py b/zun/objects/container.py index cc05eb06c..eaa9c7fa5 100644 --- a/zun/objects/container.py +++ b/zun/objects/container.py @@ -214,3 +214,15 @@ class Container(base.ZunPersistentObject, base.ZunObject): if self.obj_attr_is_set(field) and \ getattr(self, field) != getattr(current, field): setattr(self, field, getattr(current, field)) + + def get_sandbox_id(self): + if self.meta: + return self.meta.get('sandbox_id', None) + else: + return None + + def set_sandbox_id(self, sandbox_id): + if self.meta is None: + self.meta = {'sandbox_id': sandbox_id} + else: + self.meta['sandbox_id'] = sandbox_id diff --git a/zun/tests/unit/compute/test_compute_manager.py b/zun/tests/unit/compute/test_compute_manager.py index d78bb0185..4128c5a09 100644 --- a/zun/tests/unit/compute/test_compute_manager.py +++ b/zun/tests/unit/compute/test_compute_manager.py @@ -55,13 +55,10 @@ class TestManager(base.TestCase): @mock.patch.object(Container, 'save') @mock.patch('zun.image.driver.pull_image') @mock.patch.object(fake_driver, 'create') - @mock.patch.object(fake_driver, 'create_sandbox') - def test_container_create(self, mock_create_sandbox, mock_create, - mock_pull, mock_save): + def test_container_create(self, mock_create, mock_pull, mock_save): container = Container(self.context, **utils.get_test_container()) image = {'image': 'repo', 'path': 'out_path', 'driver': 'glance'} mock_pull.return_value = image, False - mock_create_sandbox.return_value = 'fake_id' self.compute_manager._resource_tracker = FakeResourceTracker() networks = [] self.compute_manager._do_container_create(self.context, container, @@ -69,18 +66,15 @@ class TestManager(base.TestCase): mock_save.assert_called_with(self.context) mock_pull.assert_any_call(self.context, container.image, 'latest', 'always', 'glance') - mock_create.assert_called_once_with(self.context, container, - 'fake_id', image) + mock_create.assert_called_once_with(self.context, container, image) @mock.patch.object(Container, 'save') - @mock.patch.object(fake_driver, 'create_sandbox') @mock.patch('zun.image.driver.pull_image') @mock.patch.object(manager.Manager, '_fail_container') def test_container_create_pull_image_failed_docker_error( - self, mock_fail, mock_pull, mock_create_sandbox, mock_save): + self, mock_fail, mock_pull, mock_save): container = Container(self.context, **utils.get_test_container()) mock_pull.side_effect = exception.DockerError("Pull Failed") - mock_create_sandbox.return_value = mock.MagicMock() networks = [] self.compute_manager._do_container_create(self.context, container, networks) @@ -88,14 +82,12 @@ class TestManager(base.TestCase): container, "Pull Failed") @mock.patch.object(Container, 'save') - @mock.patch.object(fake_driver, 'create_sandbox') @mock.patch('zun.image.driver.pull_image') @mock.patch.object(manager.Manager, '_fail_container') def test_container_create_pull_image_failed_image_not_found( - self, mock_fail, mock_pull, mock_create_sandbox, mock_save): + self, mock_fail, mock_pull, mock_save): container = Container(self.context, **utils.get_test_container()) mock_pull.side_effect = exception.ImageNotFound("Image Not Found") - mock_create_sandbox.return_value = mock.MagicMock() networks = [] self.compute_manager._do_container_create(self.context, container, networks) @@ -103,15 +95,13 @@ class TestManager(base.TestCase): container, "Image Not Found") @mock.patch.object(Container, 'save') - @mock.patch.object(fake_driver, 'create_sandbox') @mock.patch('zun.image.driver.pull_image') @mock.patch.object(manager.Manager, '_fail_container') def test_container_create_pull_image_failed_zun_exception( - self, mock_fail, mock_pull, mock_create_sandbox, mock_save): + self, mock_fail, mock_pull, mock_save): container = Container(self.context, **utils.get_test_container()) mock_pull.side_effect = exception.ZunException( message="Image Not Found") - mock_create_sandbox.return_value = mock.MagicMock() networks = [] self.compute_manager._do_container_create(self.context, container, networks) @@ -121,17 +111,14 @@ class TestManager(base.TestCase): @mock.patch.object(Container, 'save') @mock.patch('zun.image.driver.pull_image') @mock.patch.object(fake_driver, 'create') - @mock.patch.object(fake_driver, 'create_sandbox') @mock.patch.object(manager.Manager, '_fail_container') def test_container_create_docker_create_failed(self, mock_fail, - mock_create_sandbox, mock_create, mock_pull, mock_save): container = Container(self.context, **utils.get_test_container()) image = {'image': 'repo', 'path': 'out_path', 'driver': 'glance'} mock_pull.return_value = image, False mock_create.side_effect = exception.DockerError("Creation Failed") - mock_create_sandbox.return_value = mock.MagicMock() self.compute_manager._resource_tracker = FakeResourceTracker() networks = [] self.compute_manager._do_container_create(self.context, container, @@ -157,8 +144,7 @@ class TestManager(base.TestCase): mock_save.assert_called_with(self.context) mock_pull.assert_any_call(self.context, container.image, 'latest', 'always', 'glance') - mock_create.assert_called_once_with(self.context, container, - 'fake_sandbox', image) + mock_create.assert_called_once_with(self.context, container, image) mock_start.assert_called_once_with(self.context, container) @mock.patch.object(Container, 'save') @@ -166,7 +152,10 @@ class TestManager(base.TestCase): @mock.patch.object(manager.Manager, '_fail_container') def test_container_run_image_not_found(self, mock_fail, mock_pull, mock_save): - container = Container(self.context, **utils.get_test_container()) + container_dict = utils.get_test_container( + image='test:latest', image_driver='docker', + image_pull_policy='ifnotpresent') + container = Container(self.context, **container_dict) mock_pull.side_effect = exception.ImageNotFound( message="Image Not Found") networks = [] @@ -176,15 +165,18 @@ class TestManager(base.TestCase): mock_save.assert_called_with(self.context) mock_fail.assert_called_with(self.context, container, 'Image Not Found') - mock_pull.assert_called_once_with(self.context, 'kubernetes/pause', - 'latest', 'ifnotpresent', 'docker') + mock_pull.assert_called_once_with(self.context, 'test', 'latest', + 'ifnotpresent', 'docker') @mock.patch.object(Container, 'save') @mock.patch('zun.image.driver.pull_image') @mock.patch.object(manager.Manager, '_fail_container') def test_container_run_image_pull_exception_raised(self, mock_fail, mock_pull, mock_save): - container = Container(self.context, **utils.get_test_container()) + container_dict = utils.get_test_container( + image='test:latest', image_driver='docker', + image_pull_policy='ifnotpresent') + container = Container(self.context, **container_dict) mock_pull.side_effect = exception.ZunException( message="Image Not Found") networks = [] @@ -194,15 +186,18 @@ class TestManager(base.TestCase): mock_save.assert_called_with(self.context) mock_fail.assert_called_with(self.context, container, 'Image Not Found') - mock_pull.assert_called_once_with(self.context, 'kubernetes/pause', - 'latest', 'ifnotpresent', 'docker') + mock_pull.assert_called_once_with(self.context, 'test', 'latest', + 'ifnotpresent', 'docker') @mock.patch.object(Container, 'save') @mock.patch('zun.image.driver.pull_image') @mock.patch.object(manager.Manager, '_fail_container') def test_container_run_image_pull_docker_error(self, mock_fail, mock_pull, mock_save): - container = Container(self.context, **utils.get_test_container()) + container_dict = utils.get_test_container( + image='test:latest', image_driver='docker', + image_pull_policy='ifnotpresent') + container = Container(self.context, **container_dict) mock_pull.side_effect = exception.DockerError( message="Docker Error occurred") networks = [] @@ -212,8 +207,8 @@ class TestManager(base.TestCase): mock_save.assert_called_with(self.context) mock_fail.assert_called_with(self.context, container, 'Docker Error occurred') - mock_pull.assert_called_once_with(self.context, 'kubernetes/pause', - 'latest', 'ifnotpresent', 'docker') + mock_pull.assert_called_once_with(self.context, 'test', 'latest', + 'ifnotpresent', 'docker') @mock.patch.object(Container, 'save') @mock.patch('zun.image.driver.pull_image') @@ -237,8 +232,7 @@ class TestManager(base.TestCase): mock_pull.assert_any_call(self.context, container.image, 'latest', 'always', 'glance') mock_create.assert_called_once_with( - self.context, container, 'fake_sandbox', - {'name': 'nginx', 'path': None}) + self.context, container, {'name': 'nginx', 'path': None}) @mock.patch.object(compute_node_tracker.ComputeNodeTracker, 'remove_usage_from_container') @@ -250,7 +244,7 @@ class TestManager(base.TestCase): container = Container(self.context, **utils.get_test_container()) self.compute_manager.container_delete(self. context, container, False) mock_save.assert_called_with(self.context) - mock_delete.assert_called_once_with(container, False) + mock_delete.assert_called_once_with(self.context, container, False) mock_cnt_destroy.assert_called_once_with(self.context) mock_remove_usage.assert_called_once_with(self.context, container, True) @@ -272,14 +266,14 @@ class TestManager(base.TestCase): @mock.patch.object(manager.Manager, '_fail_container') @mock.patch.object(fake_driver, 'delete_sandbox') - @mock.patch.object(fake_driver, 'get_sandbox_id') @mock.patch.object(Container, 'save') @mock.patch.object(fake_driver, 'delete') def test_container_delete_sandbox_failed(self, mock_delete, mock_save, - mock_sandbox, mock_delete_sandbox, + mock_delete_sandbox, mock_fail): + self.compute_manager.use_sandbox = True container = Container(self.context, **utils.get_test_container()) - mock_sandbox.return_value = "sandbox_id" + container.set_sandbox_id("sandbox_id") mock_delete_sandbox.side_effect = exception.ZunException( message="Unexpected exception") self.assertRaises(exception.ZunException, diff --git a/zun/tests/unit/container/docker/test_docker_driver.py b/zun/tests/unit/container/docker/test_docker_driver.py index b82011018..ceb15c8c2 100644 --- a/zun/tests/unit/container/docker/test_docker_driver.py +++ b/zun/tests/unit/container/docker/test_docker_driver.py @@ -79,20 +79,24 @@ class TestDockerDriver(base.DriverTestCase): self.driver.images(repo='test') self.mock_docker.images.assert_called_once_with('test', False) + @mock.patch('zun.network.kuryr_network.KuryrNetwork' + '.connect_container_to_network') + @mock.patch( + 'zun.container.docker.driver.DockerDriver._get_security_group_ids') @mock.patch('zun.objects.container.Container.save') - def test_create_image_path_is_none(self, mock_save): + def test_create_image_path_is_none(self, mock_save, + mock_get_security_group_ids, + mock_connect): self.mock_docker.create_host_config = mock.Mock( return_value={'Id1': 'val1', 'key2': 'val2'}) self.mock_docker.create_container = mock.Mock( return_value={'Id': 'val1', 'key1': 'val2'}) image = {'path': ''} mock_container = self.mock_default_container - result_container = self.driver.create(self.context, mock_container, - 'test_sandbox', image) + with mock.patch.object(self.driver, '_get_available_network'): + result_container = self.driver.create(self.context, mock_container, + image, None) host_config = {} - host_config['network_mode'] = 'container:test_sandbox' - host_config['ipc_mode'] = 'container:test_sandbox' - host_config['volumes_from'] = 'test_sandbox' host_config['mem_limit'] = '512m' host_config['cpu_quota'] = 100000 host_config['cpu_period'] = 100000 @@ -119,7 +123,7 @@ class TestDockerDriver(base.DriverTestCase): def test_delete_success(self): self.mock_docker.remove_container = mock.Mock() mock_container = mock.MagicMock() - self.driver.delete(mock_container, True) + self.driver.delete(self.context, mock_container, True) self.mock_docker.remove_container.assert_called_once_with( mock_container.container_id, force=True) @@ -129,7 +133,7 @@ class TestDockerDriver(base.DriverTestCase): self.mock_docker.remove_container = mock.Mock( side_effect=errors.APIError('Error', '', '')) mock_container = mock.MagicMock() - self.driver.delete(mock_container, True) + self.driver.delete(self.context, mock_container, True) self.mock_docker.remove_container.assert_called_once_with( mock_container.container_id, force=True) self.assertEqual(1, mock_init.call_count) @@ -141,7 +145,7 @@ class TestDockerDriver(base.DriverTestCase): side_effect=errors.APIError('Error', '', '')) mock_container = mock.MagicMock() self.assertRaises(errors.APIError, self.driver.delete, - mock_container, + self.context, mock_container, True) self.mock_docker.remove_container.assert_called_once_with( mock_container.container_id, force=True) @@ -355,8 +359,8 @@ class TestDockerDriver(base.DriverTestCase): def test_delete_sandbox(self): self.mock_docker.remove_container = mock.Mock() mock_container = mock.MagicMock() - self.driver.delete_sandbox(self.context, mock_container, - sandbox_id='test_sandbox_id') + mock_container.get_sandbox_id.return_value = 'test_sandbox_id' + self.driver.delete_sandbox(self.context, mock_container) self.mock_docker.remove_container.assert_called_once_with( 'test_sandbox_id', force=True) @@ -366,24 +370,6 @@ class TestDockerDriver(base.DriverTestCase): sandbox_id='test_sandbox_id') self.mock_docker.stop.assert_called_once_with('test_sandbox_id') - def test_get_sandbox_none_id(self): - mock_container = mock.MagicMock() - mock_container.meta = None - result_sandbox_id = self.driver.get_sandbox_id(mock_container) - self.assertIsNone(result_sandbox_id) - - def test_get_sandbox_not_none_id(self): - mock_container = mock.MagicMock() - result_sandbox_id = self.driver.get_sandbox_id(mock_container) - self.assertEqual(result_sandbox_id, - mock_container.meta.get('sandbox_id', None)) - - def test_set_sandbox_id(self): - mock_container = mock.MagicMock(meta={'sandbox_id': 'test_sandbox_id'}) - self.driver.set_sandbox_id(mock_container, 'test_sandbox_id') - self.assertEqual(mock_container.meta['sandbox_id'], - 'test_sandbox_id') - def test_get_sandbox_name(self): mock_container = mock.MagicMock( uuid='ea8e2a25-2901-438d-8157-de7ffd68d051') @@ -548,19 +534,17 @@ class TestNovaDockerDriver(base.DriverTestCase): @mock.patch('zun.container.docker.driver.' 'NovaDockerDriver._find_server_by_container_id') - @mock.patch('zun.container.docker.driver.NovaDockerDriver.get_sandbox_id') @mock.patch('zun.common.nova.NovaClient') - def test_get_addresses(self, mock_nova_client, mock_get_sandbox_id, + def test_get_addresses(self, mock_nova_client, mock_find_server_by_container_id): nova_client_instance = mock.MagicMock() nova_client_instance.get_addresses.return_value = 'test_address' mock_nova_client.return_value = nova_client_instance - mock_get_sandbox_id.return_value = 'test_sanbox_id' mock_find_server_by_container_id.return_value = 'test_test_server_name' mock_container = mock.MagicMock() + mock_container.get_sandbox_id.return_value = 'test_sanbox_id' result_address = self.driver.get_addresses(self.context, mock_container) - mock_get_sandbox_id.assert_called_once_with(mock_container) mock_find_server_by_container_id.assert_called_once_with( 'test_sanbox_id') nova_client_instance.get_addresses.assert_called_once_with( diff --git a/zun/tests/unit/container/fake_driver.py b/zun/tests/unit/container/fake_driver.py index b225f652f..54c9f615d 100644 --- a/zun/tests/unit/container/fake_driver.py +++ b/zun/tests/unit/container/fake_driver.py @@ -18,6 +18,10 @@ from zun.container import driver class FakeDriver(driver.ContainerDriver): """Fake driver for testing.""" + capabilities = { + "support_sandbox": True, + "support_standalone": True, + } def __init__(self): super(FakeDriver, self).__init__() @@ -87,17 +91,11 @@ class FakeDriver(driver.ContainerDriver): pass def create_sandbox(self, context, name, **kwargs): - return "fake_sandbox" + pass def delete_sandbox(self, context, id): pass - def get_sandbox_id(self, container): - pass - - def set_sandbox_id(self, container, id): - pass - def get_addresses(self, context, container): pass diff --git a/zun/tests/unit/db/utils.py b/zun/tests/unit/db/utils.py index 8161306aa..ce1305c1e 100644 --- a/zun/tests/unit/db/utils.py +++ b/zun/tests/unit/db/utils.py @@ -62,7 +62,7 @@ def get_test_container(**kwargs): {'Name': 'no', 'MaximumRetryCount': '0'}), 'status_detail': kwargs.get('status_detail', 'up from 5 hours'), 'interactive': kwargs.get('interactive', True), - 'image_driver': 'glance', + 'image_driver': kwargs.get('image_driver', 'glance'), 'websocket_url': 'ws://127.0.0.1:6784/4c03164962fa/attach/' 'ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1', 'websocket_token': '7878038e-957c-4d52-ae19-1e9561784e7b',