diff --git a/zun/common/exception.py b/zun/common/exception.py index ce3e8b178..d5c815268 100644 --- a/zun/common/exception.py +++ b/zun/common/exception.py @@ -411,6 +411,10 @@ class ImageNotFound(Invalid): message = _("Image %(image)s could not be found.") +class OperationNotSupported(ZunException): + message = _("The operation is not supported.") + + class ZunServiceNotFound(HTTPNotFound): message = _("Zun service %(binary)s on host %(host)s could not be found.") diff --git a/zun/compute/api.py b/zun/compute/api.py index 7fc93f4fe..1da08a60d 100644 --- a/zun/compute/api.py +++ b/zun/compute/api.py @@ -75,6 +75,11 @@ class API(object): raise exception.Conflict('Multiple images exist with same ' 'name. Please use the container ' 'uuid instead.') + except exception.OperationNotSupported: + LOG.info("Skip validation since search is not supported for " + "image '%(image)s' and image driver '%(driver)s'.", + {'image': new_container.image, + 'driver': new_container.image_driver}) except Exception as e: new_container.status = consts.ERROR new_container.status_reason = str(e) diff --git a/zun/container/docker/driver.py b/zun/container/docker/driver.py index a1d46897a..86f8035e1 100644 --- a/zun/container/docker/driver.py +++ b/zun/container/docker/driver.py @@ -206,6 +206,8 @@ class DockerDriver(driver.ContainerDriver): image_driver = self.image_drivers[driver_name] return image_driver.search_image(context, repo, tag, exact_match) + except exception.ZunException: + raise except Exception as e: LOG.exception('Unknown exception occurred while searching ' 'for image: %s', six.text_type(e)) diff --git a/zun/image/docker/driver.py b/zun/image/docker/driver.py index 01e9dd98a..efa90d5bd 100644 --- a/zun/image/docker/driver.py +++ b/zun/image/docker/driver.py @@ -19,6 +19,7 @@ import six from oslo_log import log as logging from oslo_utils import excutils +from zun.common.docker_image import reference as docker_image from zun.common import exception from zun.common.i18n import _ from zun.common import utils @@ -98,10 +99,17 @@ class DockerDriver(driver.ContainerImageDriver): raise exception.ZunException(msg.format(e)) def search_image(self, context, repo, tag, exact_match): + image_ref = docker_image.Reference.parse(repo) + registry, image_name = image_ref.split_hostname() + if registry and registry != 'docker.io': + # Images searching is only supported in DockerHub + msg = _('Image searching is not supported in registry: {0}') + raise exception.OperationNotSupported(msg.format(registry)) + with docker_utils.docker_client() as docker: try: - # TODO(hongbin): search image by both repo and tag - images = docker.search(repo) + # TODO(hongbin): search image by both name and tag + images = docker.search(image_name) except errors.APIError as api_error: raise exception.ZunException(six.text_type(api_error)) except Exception as e: @@ -109,7 +117,7 @@ class DockerDriver(driver.ContainerImageDriver): raise exception.ZunException(msg.format(e)) if exact_match: - images = [i for i in images if i['name'] == repo] + images = [i for i in images if i['name'] == image_name] for image in images: image['metadata'] = {} diff --git a/zun/tests/unit/compute/test_compute_api.py b/zun/tests/unit/compute/test_compute_api.py index 56e498793..634ddb194 100644 --- a/zun/tests/unit/compute/test_compute_api.py +++ b/zun/tests/unit/compute/test_compute_api.py @@ -59,6 +59,27 @@ class TestAPI(base.TestCase): self.assertTrue(mock_image_search.called) self.assertTrue(mock_container_create.called) + @mock.patch('zun.compute.api.API._record_action_start') + @mock.patch('zun.compute.rpcapi.API.container_create') + @mock.patch('zun.compute.rpcapi.API.image_search') + @mock.patch('zun.compute.api.API._schedule_container') + def test_container_create_with_private_registry_image( + self, mock_schedule_container, mock_image_search, + mock_container_create, mock_record_action_start): + container = self.container + container.image = 'myregistry.io/test-image' + container.image_driver = 'docker' + mock_schedule_container.return_value = {'host': u'Centos', + 'nodename': None, + 'limits': {}} + mock_image_search.side_effect = exception.OperationNotSupported + + self.compute_api.container_create(self.context, container, + None, None, None, False) + self.assertTrue(mock_schedule_container.called) + self.assertTrue(mock_image_search.called) + self.assertTrue(mock_container_create.called) + @mock.patch('zun.compute.api.API._schedule_container') @mock.patch.object(objects.Container, 'save') def test_schedule_container_exception(self, mock_save, diff --git a/zun/tests/unit/image/docker/test_driver.py b/zun/tests/unit/image/docker/test_driver.py index d782760d5..00d05b3ad 100644 --- a/zun/tests/unit/image/docker/test_driver.py +++ b/zun/tests/unit/image/docker/test_driver.py @@ -212,3 +212,9 @@ class TestDriver(base.BaseTestCase): None, 'test_image', None, False) self.mock_docker.search.assert_called_once_with('test_image') self.assertEqual(1, mock_search.call_count) + + def test_search_image_not_supported(self): + self.assertRaises(exception.OperationNotSupported, + self.driver.search_image, + None, 'myregistry.io/test-image', None, False) + self.mock_docker.search.assert_not_called()