Make sandbox container optional
Infra container (sandbox) is not a must for native Docker driver (it is a must for Nova Docker driver). Introducing infra container to native Docker driver incurs an overhead to manage the additional resources. This patch makes infra container optional for native Docker driver. Cloud administrators could turn on/off infra container via a config. By default, infra container is turned off. Co-Authored-By: Hongbin Lu <hongbin.lu@huawei.com> Change-Id: Ie9e8dec947ed3a89f979e4f4df3abd8d133bd856 Implements: blueprint make-sandbox-optional
This commit is contained in:
parent
5a49fefb15
commit
a001c5a023
@ -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:
|
||||
|
@ -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.
|
||||
""")
|
||||
]
|
||||
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
||||
|
@ -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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user