From dbe629302232dd70a7858fd71fe053c1227cbb6e Mon Sep 17 00:00:00 2001 From: Pradeep Kumar Singh Date: Sat, 21 Jan 2017 07:45:54 +0000 Subject: [PATCH] Support to provide 'image_driver' during container create This patch adds the functionality to support image_driver during container create/run. If user provides a valid image_driver, that is defined in configuration he/she will be allowed to create the container otherwise the request will fail. This patch also add image_driver field in db, so that user can inspect which image driver he/she used during the container create. Partially-Implements: BP allow-specify-image-driver Change-Id: Id2299a613ed414b2df67c103fe6cda0397ab791d --- setup.cfg | 4 +++ zun/api/controllers/v1/containers.py | 8 ++++- zun/api/controllers/v1/images.py | 8 ++++- zun/api/controllers/v1/schemas/containers.py | 1 + zun/api/controllers/v1/schemas/images.py | 9 +++++ .../controllers/v1/views/containers_view.py | 3 +- zun/api/utils.py | 7 ++++ zun/common/validation/parameter_types.py | 11 ++++++ zun/compute/api.py | 4 +-- zun/compute/manager.py | 23 ++++++++---- zun/compute/rpcapi.py | 3 +- zun/conf/image_driver.py | 24 ++++++++++--- .../5458f8394206_add_image_driver_field.py | 35 +++++++++++++++++++ zun/db/sqlalchemy/models.py | 1 + zun/image/driver.py | 28 ++++++++++----- zun/image/glance/driver.py | 1 - zun/objects/container.py | 4 ++- zun/tests/tempest/api/common/datagen.py | 3 +- .../api/controllers/v1/test_containers.py | 17 +++++++++ .../unit/api/controllers/v1/test_images.py | 29 +++++++++------ zun/tests/unit/common/test_validations.py | 16 +++++++-- .../unit/compute/test_compute_manager.py | 24 +++++++------ zun/tests/unit/db/utils.py | 1 + 23 files changed, 213 insertions(+), 51 deletions(-) create mode 100644 zun/db/sqlalchemy/alembic/versions/5458f8394206_add_image_driver_field.py diff --git a/setup.cfg b/setup.cfg index d2cd2bc3c..fce026f63 100644 --- a/setup.cfg +++ b/setup.cfg @@ -64,5 +64,9 @@ zun.scheduler.driver = chance_scheduler = zun.scheduler.chance_scheduler:ChanceScheduler fake_scheduler = zun.tests.unit.scheduler.fake_scheduler:FakeScheduler +zun.image.driver = + glance = zun.image.glance.driver:GlanceDriver + docker = zun.image.docker.driver:DockerDriver + tempest.test_plugins = zun_tests = zun.tests.tempest.plugin:ZunTempestPlugin diff --git a/zun/api/controllers/v1/containers.py b/zun/api/controllers/v1/containers.py index 3d4e72acb..b2ddb0a3e 100644 --- a/zun/api/controllers/v1/containers.py +++ b/zun/api/controllers/v1/containers.py @@ -202,13 +202,19 @@ class ContainersController(rest.RestController): '"false", True, False, "True" and "False"') raise exception.InvalidValue(msg) + # Valiadtion accepts 'None' so need to convert it to None + if container_dict.get('image_driver'): + container_dict['image_driver'] = api_utils.string_or_none( + container_dict.get('image_driver')) + # NOTE(mkrai): Intent here is to check the existence of image # before proceeding to create container. If image is not found, # container create will fail with 400 status. images = compute_api.image_search(context, container_dict['image'], + container_dict.get('image_driver'), True) if not images: - raise exception.ImageNotFound(container_dict['image']) + raise exception.ImageNotFound(image=container_dict['image']) container_dict['project_id'] = context.project_id container_dict['user_id'] = context.user_id name = container_dict.get('name') or \ diff --git a/zun/api/controllers/v1/images.py b/zun/api/controllers/v1/images.py index 583d5e880..381b5d44c 100644 --- a/zun/api/controllers/v1/images.py +++ b/zun/api/controllers/v1/images.py @@ -117,7 +117,8 @@ class ImagesController(rest.RestController): @pecan.expose('json') @exception.wrap_pecan_controller_exception - def search(self, image, exact_match=False): + @validation.validate_query_param(pecan.request, schema.query_param_search) + def search(self, image, image_driver=None, exact_match=False): context = pecan.request.context policy.enforce(context, "image:search", action="image:search") @@ -129,5 +130,10 @@ class ImagesController(rest.RestController): msg = _("Valid exact_match values are true," " false, 0, 1, yes and no") raise exception.InvalidValue(msg) + # Valiadtion accepts 'None' so need to convert it to None + if image_driver: + image_driver = api_utils.string_or_none(image_driver) + return pecan.request.compute_api.image_search(context, image, + image_driver, exact_match) diff --git a/zun/api/controllers/v1/schemas/containers.py b/zun/api/controllers/v1/schemas/containers.py index f3b7fb3b3..501d6e954 100644 --- a/zun/api/controllers/v1/schemas/containers.py +++ b/zun/api/controllers/v1/schemas/containers.py @@ -27,6 +27,7 @@ _container_properties = { 'restart_policy': parameter_types.restart_policy, 'tty': parameter_types.boolean, 'stdin_open': parameter_types.boolean, + 'image_driver': parameter_types.image_driver } container_create = { diff --git a/zun/api/controllers/v1/schemas/images.py b/zun/api/controllers/v1/schemas/images.py index 2c4b5eeea..d233b6077 100644 --- a/zun/api/controllers/v1/schemas/images.py +++ b/zun/api/controllers/v1/schemas/images.py @@ -25,3 +25,12 @@ image_create = { 'required': ['repo'], 'additionalProperties': False } + +query_param_search = { + 'type': 'object', + 'properties': { + 'image_driver': parameter_types.image_driver, + 'exact_match': parameter_types.boolean + }, + 'additionalProperties': False +} diff --git a/zun/api/controllers/v1/views/containers_view.py b/zun/api/controllers/v1/views/containers_view.py index 9a7377f91..347cb88f7 100644 --- a/zun/api/controllers/v1/views/containers_view.py +++ b/zun/api/controllers/v1/views/containers_view.py @@ -37,7 +37,8 @@ _basic_keys = ( 'restart_policy', 'status_detail', 'tty', - 'stdin_open' + 'stdin_open', + 'image_driver' ) diff --git a/zun/api/utils.py b/zun/api/utils.py index ba7bae28a..e083ca2d7 100644 --- a/zun/api/utils.py +++ b/zun/api/utils.py @@ -33,6 +33,13 @@ JSONPATCH_EXCEPTIONS = (jsonpatch.JsonPatchException, DOCKER_MINIMUM_MEMORY = 4 * 1024 * 1024 +def string_or_none(value): + if value in [None, 'None']: + return None + else: + return value + + def validate_limit(limit): try: if limit is not None and int(limit) <= 0: diff --git a/zun/common/validation/parameter_types.py b/zun/common/validation/parameter_types.py index 990927a60..cf48fd15a 100644 --- a/zun/common/validation/parameter_types.py +++ b/zun/common/validation/parameter_types.py @@ -11,7 +11,13 @@ # under the License. import copy +import zun.conf +CONF = zun.conf.CONF + +image_driver_list = [driver for driver in CONF.image_driver_list] + +image_driver_list_with_none = image_driver_list + [None, 'None'] non_negative_integer = { 'type': ['integer', 'string'], @@ -31,6 +37,11 @@ boolean = { 'enum': [True, 'True', 'true', False, 'False', 'false'], } +image_driver = { + 'type': ['string', 'null'], + 'enum': image_driver_list_with_none +} + container_name = { 'type': ['string', 'null'], 'minLength': 2, diff --git a/zun/compute/api.py b/zun/compute/api.py index 7f7febce4..54d3e6d40 100644 --- a/zun/compute/api.py +++ b/zun/compute/api.py @@ -90,5 +90,5 @@ class API(object): def image_pull(self, context, image, *args): return self.rpcapi.image_pull(context, image, *args) - def image_search(self, context, image, *args): - return self.rpcapi.image_search(context, image, *args) + def image_search(self, context, image, image_driver, *args): + return self.rpcapi.image_search(context, image, image_driver, *args) diff --git a/zun/compute/manager.py b/zun/compute/manager.py index ed06c97f4..77ed080d4 100644 --- a/zun/compute/manager.py +++ b/zun/compute/manager.py @@ -21,11 +21,12 @@ from zun.common import exception from zun.common.i18n import _LE from zun.common import utils from zun.common.utils import translate_exception +import zun.conf from zun.container import driver from zun.image import driver as image_driver from zun.objects import fields - +CONF = zun.conf.CONF LOG = logging.getLogger(__name__) @@ -67,10 +68,14 @@ class Manager(object): container.task_state = fields.TaskState.SANDBOX_CREATING container.save(context) sandbox_id = None - sandbox_image = 'kubernetes/pause' + sandbox_image = CONF.sandbox_image + sandbox_image_driver = CONF.sandbox_image_driver + sandbox_image_pull_policy = CONF.sandbox_image_pull_policy repo, tag = utils.parse_image_name(sandbox_image) try: - image = image_driver.pull_image(context, repo, tag, 'ifnotpresent') + image = image_driver.pull_image(context, repo, tag, + sandbox_image_pull_policy, + sandbox_image_driver) sandbox_id = self.driver.create_sandbox(context, container, image=sandbox_image) except Exception as e: @@ -86,9 +91,11 @@ class Manager(object): repo, tag = utils.parse_image_name(container.image) image_pull_policy = utils.get_image_pull_policy( container.image_pull_policy, tag) + image_driver_name = container.image_driver try: - image = image_driver.pull_image(context, repo, - tag, image_pull_policy) + image = image_driver.pull_image(context, repo, tag, + image_pull_policy, + image_driver_name) except exception.ImageNotFound as e: with excutils.save_and_reraise_exception(reraise=reraise): LOG.error(six.text_type(e)) @@ -112,6 +119,7 @@ class Manager(object): return container.task_state = fields.TaskState.CONTAINER_CREATING + container.image_driver = image.get('driver') container.save(context) try: container = self.driver.create(context, container, @@ -372,10 +380,11 @@ class Manager(object): raise @translate_exception - def image_search(self, context, image, exact_match): + def image_search(self, context, image, image_driver_name, exact_match): LOG.debug('Searching image...', image=image) try: - return image_driver.search_image(context, image, exact_match) + return image_driver.search_image(context, image, + image_driver_name, exact_match) except Exception as e: LOG.exception(_LE("Unexpected exception while searching " "image: %s"), six.text_type(e)) diff --git a/zun/compute/rpcapi.py b/zun/compute/rpcapi.py index 74b8f89de..348294878 100644 --- a/zun/compute/rpcapi.py +++ b/zun/compute/rpcapi.py @@ -89,10 +89,11 @@ class API(rpc_service.API): host = None self._cast(host, 'image_pull', image=image) - def image_search(self, context, image, exact_match): + def image_search(self, context, image, image_driver, exact_match): # NOTE(hongbin): Image API doesn't support multiple compute nodes # scenario yet, so we temporarily set host to None and rpc will # choose an arbitrary host. host = None return self._call(host, 'image_search', image=image, + image_driver_name=image_driver, exact_match=exact_match) diff --git a/zun/conf/image_driver.py b/zun/conf/image_driver.py index 8e9591e60..725b0e85a 100644 --- a/zun/conf/image_driver.py +++ b/zun/conf/image_driver.py @@ -18,11 +18,11 @@ from zun.conf import path image_driver_opts = [ cfg.ListOpt( 'image_driver_list', - default=['glance.driver.GlanceDriver', 'docker.driver.DockerDriver'], + default=['glance', 'docker'], help="""Defines the list of image driver to use for downloading image. Possible values: -* ``docker.driver.DockerDriver`` -* ``glance.driver.GlanceDriver`` +* ``docker`` +* ``glance`` Services which consume this: * ``zun-compute`` Interdependencies to other options: @@ -30,6 +30,21 @@ Interdependencies to other options: """) ] +sandbox_opts = [ + cfg.StrOpt( + 'sandbox_image', + default='kubernetes/pause', + help='Container image for sandbox container.'), + cfg.StrOpt( + 'sandbox_image_driver', + default='docker', + help='Image driver for sandbox container.'), + cfg.StrOpt( + 'sandbox_image_pull_policy', + default='ifnotpresent', + help='Image pull policy for sandbox image.'), +] + glance_driver_opts = [ cfg.StrOpt( 'images_directory', @@ -42,13 +57,14 @@ glance_driver_opts = [ glance_opt_group = cfg.OptGroup(name='glance', title='Glance options for image management') -ALL_OPTS = (glance_driver_opts + image_driver_opts) +ALL_OPTS = (glance_driver_opts + image_driver_opts + sandbox_opts) def register_opts(conf): conf.register_group(glance_opt_group) conf.register_opts(glance_driver_opts, group=glance_opt_group) conf.register_opts(image_driver_opts) + conf.register_opts(sandbox_opts) def list_opts(): diff --git a/zun/db/sqlalchemy/alembic/versions/5458f8394206_add_image_driver_field.py b/zun/db/sqlalchemy/alembic/versions/5458f8394206_add_image_driver_field.py new file mode 100644 index 000000000..2c7a1a84a --- /dev/null +++ b/zun/db/sqlalchemy/alembic/versions/5458f8394206_add_image_driver_field.py @@ -0,0 +1,35 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +"""add image driver field + +Revision ID: 5458f8394206 +Revises: d1ef05fd92c8 +Create Date: 2017-01-25 19:01:46.033461 + +""" + +# revision identifiers, used by Alembic. +revision = '5458f8394206' +down_revision = 'd1ef05fd92c8' +branch_labels = None +depends_on = None + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + op.add_column('container', + sa.Column('image_driver', sa.Text(), + nullable=True)) diff --git a/zun/db/sqlalchemy/models.py b/zun/db/sqlalchemy/models.py index d339c5230..1b921c8db 100644 --- a/zun/db/sqlalchemy/models.py +++ b/zun/db/sqlalchemy/models.py @@ -150,6 +150,7 @@ class Container(Base): status_detail = Column(String(50)) tty = Column(Boolean, default=False) stdin_open = Column(Boolean, default=False) + image_driver = Column(String(255)) class Image(Base): diff --git a/zun/image/driver.py b/zun/image/driver.py index 34336984f..df365d389 100644 --- a/zun/image/driver.py +++ b/zun/image/driver.py @@ -16,7 +16,7 @@ import six import sys from oslo_log import log as logging -from oslo_utils import importutils +import stevedore from zun.common import exception from zun.common.i18n import _ @@ -45,26 +45,34 @@ def load_image_driver(image_driver=None): LOG.info(_LI("Loading container image driver '%s'"), image_driver) try: - driver = importutils.import_object( - 'zun.image.%s' % image_driver) + driver = stevedore.driver.DriverManager( + "zun.image.driver", + image_driver, + invoke_on_load=True).driver + if not isinstance(driver, ContainerImageDriver): raise Exception(_('Expected driver of type: %s') % str(ContainerImageDriver)) return driver - except ImportError: + except Exception: LOG.exception(_LE("Unable to load the container image driver")) sys.exit(1) -def pull_image(context, repo, tag, image_pull_policy): - image_driver_list = CONF.image_driver_list +def pull_image(context, repo, tag, image_pull_policy, image_driver): + if image_driver: + image_driver_list = [image_driver.lower()] + else: + image_driver_list = CONF.image_driver_list + for driver in image_driver_list: try: image_driver = load_image_driver(driver) image = image_driver.pull_image(context, repo, tag, image_pull_policy) if image: + image['driver'] = driver.split('.')[0] break except exception.ImageNotFound: image = None @@ -77,10 +85,14 @@ def pull_image(context, repo, tag, image_pull_policy): return image -def search_image(context, image_name, exact_match): +def search_image(context, image_name, image_driver, exact_match): images = [] repo, tag = parse_image_name(image_name) - for driver in CONF.image_driver_list: + if image_driver: + image_driver_list = [image_driver.lower()] + else: + image_driver_list = CONF.image_driver_list + for driver in image_driver_list: try: image_driver = load_image_driver(driver) imgs = image_driver.search_image(context, repo, tag, diff --git a/zun/image/glance/driver.py b/zun/image/glance/driver.py index f10ae708f..103c5424b 100644 --- a/zun/image/glance/driver.py +++ b/zun/image/glance/driver.py @@ -98,7 +98,6 @@ class GlanceDriver(driver.ContainerImageDriver): try: # TODO(hongbin): find image by both repo and tag images = utils.find_images(context, repo, exact_match) - LOG.debug('Image %s was found in glance' % repo) return images except Exception as e: raise exception.ZunException(six.text_type(e)) diff --git a/zun/objects/container.py b/zun/objects/container.py index d5b368bae..51181308f 100644 --- a/zun/objects/container.py +++ b/zun/objects/container.py @@ -30,7 +30,8 @@ class Container(base.ZunPersistentObject, base.ZunObject): # Version 1.8: Add restart_policy # Version 1.9: Add status_detail column # Version 1.10: Add tty, stdin_open - VERSION = '1.10' + # Version 1.11: Add image_driver + VERSION = '1.11' fields = { 'id': fields.IntegerField(), @@ -59,6 +60,7 @@ class Container(base.ZunPersistentObject, base.ZunObject): 'status_detail': fields.StringField(nullable=True), 'tty': fields.BooleanField(nullable=True), 'stdin_open': fields.BooleanField(nullable=True), + 'image_driver': fields.StringField(nullable=True) } @staticmethod diff --git a/zun/tests/tempest/api/common/datagen.py b/zun/tests/tempest/api/common/datagen.py index 8982fc3d9..7e2f648eb 100644 --- a/zun/tests/tempest/api/common/datagen.py +++ b/zun/tests/tempest/api/common/datagen.py @@ -53,7 +53,8 @@ def container_data(**kwargs): 'image': 'cirros:latest', 'command': 'sleep 10000', 'memory': '100', - 'environment': {} + 'environment': {}, + 'image_driver': 'docker' } data.update(kwargs) diff --git a/zun/tests/unit/api/controllers/v1/test_containers.py b/zun/tests/unit/api/controllers/v1/test_containers.py index fe7df1bec..8e9218aad 100644 --- a/zun/tests/unit/api/controllers/v1/test_containers.py +++ b/zun/tests/unit/api/controllers/v1/test_containers.py @@ -1101,6 +1101,23 @@ class TestContainerController(api_base.FunctionalTest): '/v1/containers/%s/%s/' % (container_uuid, 'kill')) self.assertTrue(mock_container_kill.called) + @patch('zun.compute.api.API.container_create') + @patch('zun.compute.api.API.image_search') + def test_create_container_resp_has_image_driver(self, mock_search, + mock_container_create): + mock_container_create.side_effect = lambda x, y: y + # Create a container with a command + params = ('{"name": "MyDocker", "image": "ubuntu",' + '"command": "env", "memory": "512",' + '"environment": {"key1": "val1", "key2": "val2"},' + '"image_driver": "glance"}') + response = self.app.post('/v1/containers/', + params=params, + content_type='application/json') + self.assertEqual(202, response.status_int) + self.assertIn('image_driver', response.json.keys()) + self.assertEqual('glance', response.json.get('image_driver')) + class TestContainerEnforcement(api_base.FunctionalTest): diff --git a/zun/tests/unit/api/controllers/v1/test_images.py b/zun/tests/unit/api/controllers/v1/test_images.py index 735db7eeb..744105fec 100644 --- a/zun/tests/unit/api/controllers/v1/test_images.py +++ b/zun/tests/unit/api/controllers/v1/test_images.py @@ -138,7 +138,7 @@ class TestImageController(api_base.FunctionalTest): response = self.app.get('/v1/images/redis/search/') self.assertEqual(200, response.status_int) mock_image_search.assert_called_once_with( - mock.ANY, 'redis', False) + mock.ANY, 'redis', None, False) @patch('zun.compute.api.API.image_search') def test_search_image_with_tag(self, mock_image_search): @@ -146,37 +146,46 @@ class TestImageController(api_base.FunctionalTest): response = self.app.get('/v1/images/redis:test/search/') self.assertEqual(200, response.status_int) mock_image_search.assert_called_once_with( - mock.ANY, 'redis:test', False) + mock.ANY, 'redis:test', None, False) @patch('zun.compute.api.API.image_search') def test_search_image_not_found(self, mock_image_search): mock_image_search.side_effect = exception.ImageNotFound self.assertRaises(AppError, self.app.get, '/v1/images/redis/search/') mock_image_search.assert_called_once_with( - mock.ANY, 'redis', False) + mock.ANY, 'redis', None, False) @patch('zun.compute.rpcapi.API.image_search') def test_search_image_with_exact_match_true(self, mock_image_search): mock_image_search.return_value = {'name': 'redis', 'stars': 2000} - response = self.app.get('/v1/images/redis/search?exact_match=true') + response = self.app.get( + '/v1/images/redis/search?exact_match=true&image_driver=docker') self.assertEqual(200, response.status_int) mock_image_search.assert_called_once_with( - mock.ANY, 'redis', True) + mock.ANY, 'redis', 'docker', True) @patch('zun.compute.rpcapi.API.image_search') def test_search_image_with_exact_match_false(self, mock_image_search): mock_image_search.return_value = {'name': 'redis', 'stars': 2000} - response = self.app.get('/v1/images/redis/search?exact_match=false') + response = self.app.get( + '/v1/images/redis/search?exact_match=false&image_driver=glance') self.assertEqual(200, response.status_int) mock_image_search.assert_called_once_with( - mock.ANY, 'redis', False) + mock.ANY, 'redis', 'glance', False) @patch('zun.compute.api.API.image_search') def test_search_image_with_exact_match_wrong(self, mock_image_search): mock_image_search.side_effect = exception.InvalidValue - self.assertRaises(AppError, self.app.get, - '/v1/images/redis/search?exact_match=wrong') - self.assertTrue(mock_image_search.not_called) + with self.assertRaisesRegexp(AppError, + "Invalid input for query parameters"): + self.app.get('/v1/images/redis/search?exact_match=wrong') + + @patch('zun.compute.api.API.image_search') + def test_search_image_with_image_driver_wrong(self, mock_image_search): + mock_image_search.side_effect = exception.InvalidValue + with self.assertRaisesRegexp(AppError, + "Invalid input for query parameters"): + self.app.get('/v1/images/redis/search?image_driver=wrong') class TestImageEnforcement(api_base.FunctionalTest): diff --git a/zun/tests/unit/common/test_validations.py b/zun/tests/unit/common/test_validations.py index 146959ff6..41971d766 100644 --- a/zun/tests/unit/common/test_validations.py +++ b/zun/tests/unit/common/test_validations.py @@ -28,7 +28,8 @@ CONTAINER_CREATE = { 'image_pull_policy': parameter_types.image_pull_policy, 'labels': parameter_types.labels, 'environment': parameter_types.environment, - 'restart_policy': parameter_types.restart_policy + 'restart_policy': parameter_types.restart_policy, + 'image_driver': parameter_types.image_driver }, 'required': ['image'], 'additionalProperties': False, @@ -48,7 +49,8 @@ class TestSchemaValidations(base.BaseTestCase): 'labels': {'abc': 12, 'bcd': 'xyz'}, 'environment': {'xyz': 'pqr', 'pqr': 2}, 'restart_policy': {'Name': 'no', - 'MaximumRetryCount': '0'}} + 'MaximumRetryCount': '0'}, + 'image_driver': 'docker'} self.schema_validator.validate(request_to_validate) def test_create_schema_with_all_parameters_none(self): @@ -58,7 +60,8 @@ class TestSchemaValidations(base.BaseTestCase): 'image_pull_policy': None, 'labels': None, 'environment': None, - 'restart_policy': None + 'restart_policy': None, + 'image_driver': None } self.schema_validator.validate(request_to_validate) @@ -154,3 +157,10 @@ class TestSchemaValidations(base.BaseTestCase): with self.assertRaisesRegexp(exception.SchemaValidationError, "'Name' is a required property"): self.schema_validator.validate(request_to_validate) + + def test_create_schema_wrong_image_driver(self): + request_to_validate = {'image_driver': 'xyz', 'image': 'nginx'} + with self.assertRaisesRegexp(exception.SchemaValidationError, + "Invalid input for field" + " 'image_driver'"): + self.schema_validator.validate(request_to_validate) diff --git a/zun/tests/unit/compute/test_compute_manager.py b/zun/tests/unit/compute/test_compute_manager.py index c53bd8031..9552aedc3 100644 --- a/zun/tests/unit/compute/test_compute_manager.py +++ b/zun/tests/unit/compute/test_compute_manager.py @@ -50,14 +50,15 @@ class TestManager(base.TestCase): def test_container_create(self, mock_create_sandbox, mock_create, mock_pull, mock_save): container = Container(self.context, **utils.get_test_container()) - mock_pull.return_value = 'fake_path' + image = {'image': 'repo', 'path': 'out_path', 'driver': 'glance'} + mock_pull.return_value = image mock_create_sandbox.return_value = 'fake_id' self.compute_manager._do_container_create(self.context, container) mock_save.assert_called_with(self.context) mock_pull.assert_any_call(self.context, container.image, 'latest', - 'always') + 'always', 'glance') mock_create.assert_called_once_with(self.context, container, - 'fake_id', 'fake_path') + 'fake_id', image) @mock.patch.object(Container, 'save') @mock.patch.object(fake_driver, 'create_sandbox') @@ -109,6 +110,8 @@ class TestManager(base.TestCase): 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 mock_create.side_effect = exception.DockerError("Creation Failed") mock_create_sandbox.return_value = mock.MagicMock() self.compute_manager._do_container_create(self.context, container) @@ -122,15 +125,16 @@ class TestManager(base.TestCase): def test_container_run(self, mock_start, mock_create, mock_pull, mock_save): container = Container(self.context, **utils.get_test_container()) - mock_pull.return_value = 'fake_path' + image = {'image': 'repo', 'path': 'out_path', 'driver': 'glance'} mock_create.return_value = container + mock_pull.return_value = image container.status = 'Stopped' self.compute_manager._do_container_run(self.context, container) mock_save.assert_called_with(self.context) mock_pull.assert_any_call(self.context, container.image, 'latest', - 'always') + 'always', 'glance') mock_create.assert_called_once_with(self.context, container, - None, 'fake_path') + None, image) mock_start.assert_called_once_with(container) @mock.patch.object(Container, 'save') @@ -147,7 +151,7 @@ class TestManager(base.TestCase): mock_fail.assert_called_with(self.context, container, 'Image Not Found') mock_pull.assert_called_once_with(self.context, 'kubernetes/pause', - 'latest', 'ifnotpresent') + 'latest', 'ifnotpresent', 'docker') @mock.patch.object(Container, 'save') @mock.patch('zun.image.driver.pull_image') @@ -163,7 +167,7 @@ class TestManager(base.TestCase): mock_fail.assert_called_with(self.context, container, 'Image Not Found') mock_pull.assert_called_once_with(self.context, 'kubernetes/pause', - 'latest', 'ifnotpresent') + 'latest', 'ifnotpresent', 'docker') @mock.patch.object(Container, 'save') @mock.patch('zun.image.driver.pull_image') @@ -179,7 +183,7 @@ class TestManager(base.TestCase): mock_fail.assert_called_with(self.context, container, 'Docker Error occurred') mock_pull.assert_called_once_with(self.context, 'kubernetes/pause', - 'latest', 'ifnotpresent') + 'latest', 'ifnotpresent', 'docker') @mock.patch.object(Container, 'save') @mock.patch('zun.image.driver.pull_image') @@ -198,7 +202,7 @@ class TestManager(base.TestCase): mock_fail.assert_called_with(self.context, container, 'Docker Error occurred') mock_pull.assert_any_call(self.context, container.image, 'latest', - 'always') + 'always', 'glance') mock_create.assert_called_once_with(self.context, container, None, {'name': 'nginx', 'path': None}) diff --git a/zun/tests/unit/db/utils.py b/zun/tests/unit/db/utils.py index 28d5aa882..d6f47bc62 100644 --- a/zun/tests/unit/db/utils.py +++ b/zun/tests/unit/db/utils.py @@ -62,6 +62,7 @@ def get_test_container(**kw): 'status_detail': kw.get('status_detail', 'up from 5 hours'), 'tty': kw.get('tty', True), 'stdin_open': kw.get('stdin_open', True), + 'image_driver': 'glance' }