diff --git a/Authors b/Authors index c56441ed7c..15fc8c184c 100644 --- a/Authors +++ b/Authors @@ -28,6 +28,7 @@ Kevin L. Mitchell Lorin Hochstein Major Hayden Mark McLoughlin +Mark Washenberger Matt Dietz Mike Lundy Monty Taylor diff --git a/glance/image_cache/__init__.py b/glance/image_cache/__init__.py index 0756b358b0..b6c91c21d8 100644 --- a/glance/image_cache/__init__.py +++ b/glance/image_cache/__init__.py @@ -220,11 +220,22 @@ def get_caching_iter(self, image_id, image_iter): logger.debug(_("Tee'ing image '%s' into cache"), image_id) def tee_iter(image_id): - with self.driver.open_for_write(image_id) as cache_file: - for chunk in image_iter: - cache_file.write(chunk) - yield chunk - cache_file.flush() + try: + with self.driver.open_for_write(image_id) as cache_file: + for chunk in image_iter: + try: + cache_file.write(chunk) + finally: + yield chunk + cache_file.flush() + except Exception: + logger.exception(_("Exception encountered while tee'ing " + "image '%s' into cache. Continuing " + "with response.") % image_id) + + # NOTE(markwash): continue responding even if caching failed + for chunk in image_iter: + yield chunk return tee_iter(image_id) diff --git a/glance/tests/unit/test_image_cache.py b/glance/tests/unit/test_image_cache.py index 77b260509c..a7aff69cea 100644 --- a/glance/tests/unit/test_image_cache.py +++ b/glance/tests/unit/test_image_cache.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. +from contextlib import contextmanager import os import random import shutil @@ -24,6 +25,7 @@ import stubout from glance import image_cache +from glance.common import cfg from glance.common import exception from glance.common import utils from glance.tests import utils as test_utils @@ -308,3 +310,62 @@ def setUp(self): def tearDown(self): if os.path.exists(self.cache_dir): shutil.rmtree(self.cache_dir) + + +class TestImageCacheNoDep(unittest.TestCase): + + def setUp(self): + self.driver = None + + def init_driver(self2): + self2.driver = self.driver + + self.stubs = stubout.StubOutForTesting() + self.stubs.Set(image_cache.ImageCache, 'init_driver', init_driver) + + def tearDown(self): + self.stubs.UnsetAll() + + def test_get_caching_iter_when_write_fails(self): + + class FailingFile(object): + + def write(self, data): + if data == "Fail": + raise IOError + + class FailingFileDriver(object): + + def is_cacheable(self, *args, **kwargs): + return True + + @contextmanager + def open_for_write(self, *args, **kwargs): + yield FailingFile() + + self.driver = FailingFileDriver() + conf = cfg.ConfigOpts() + cache = image_cache.ImageCache(conf) + data = ['a', 'b', 'c', 'Fail', 'd', 'e', 'f'] + + caching_iter = cache.get_caching_iter('dummy_id', iter(data)) + self.assertEqual(list(caching_iter), data) + + def test_get_caching_iter_when_open_fails(self): + + class OpenFailingDriver(object): + + def is_cacheable(self, *args, **kwargs): + return True + + @contextmanager + def open_for_write(self, *args, **kwargs): + raise IOError + + self.driver = OpenFailingDriver() + conf = cfg.ConfigOpts() + cache = image_cache.ImageCache(conf) + data = ['a', 'b', 'c', 'd', 'e', 'f'] + + caching_iter = cache.get_caching_iter('dummy_id', iter(data)) + self.assertEqual(list(caching_iter), data)