diff --git a/ironic/common/image_service.py b/ironic/common/image_service.py index 615e8f63e0..e878f8e63d 100644 --- a/ironic/common/image_service.py +++ b/ironic/common/image_service.py @@ -183,10 +183,13 @@ class HttpImageService(BaseImageService): except ValueError: continue + no_cache = 'no-store' in response.headers.get('Cache-Control', '') + return { 'size': int(image_size), 'updated_at': date, - 'properties': {} + 'properties': {}, + 'no_cache': no_cache, } @@ -259,7 +262,9 @@ class FileImageService(BaseImageService): 'size': os.path.getsize(source_image_path), 'updated_at': utils.unix_file_modification_datetime( source_image_path), - 'properties': {} + 'properties': {}, + # No point in caching local file images + 'no_cache': True, } diff --git a/ironic/drivers/modules/image_cache.py b/ironic/drivers/modules/image_cache.py index 8a5a4284d4..17dfba9cf2 100644 --- a/ironic/drivers/modules/image_cache.py +++ b/ironic/drivers/modules/image_cache.py @@ -112,11 +112,13 @@ class ImageCache(object): # TODO(dtantsur): lock expiration time with lockutils.lock(img_download_lock_name): + img_service = image_service.get_image_service(href, context=ctx) + img_info = img_service.show(href) # NOTE(vdrok): After rebuild requested image can change, so we # should ensure that dest_path and master_path (if exists) are # pointing to the same file and their content is up to date cache_up_to_date = _delete_master_path_if_stale(master_path, href, - ctx) + img_info) dest_up_to_date = _delete_dest_path_if_stale(master_path, dest_path) @@ -137,13 +139,14 @@ class ImageCache(object): LOG.info("Master cache miss for image %(href)s, will download", {'href': href}) self._download_image( - href, master_path, dest_path, ctx=ctx, force_raw=force_raw) + href, master_path, dest_path, img_info, + ctx=ctx, force_raw=force_raw) # NOTE(dtantsur): we increased cache size - time to clean up self.clean_up() - def _download_image(self, href, master_path, dest_path, ctx=None, - force_raw=True): + def _download_image(self, href, master_path, dest_path, img_info, + ctx=None, force_raw=True): """Download image by href and store at a given path. This method should be called with uuid-specific lock taken. @@ -151,6 +154,7 @@ class ImageCache(object): :param href: image UUID or href to fetch :param master_path: destination master path :param dest_path: destination file path + :param img_info: image information from the image service :param ctx: context :param force_raw: boolean value, whether to convert the image to raw format @@ -166,10 +170,16 @@ class ImageCache(object): try: with _concurrency_semaphore: _fetch(ctx, href, tmp_path, force_raw) - # NOTE(dtantsur): no need for global lock here - master_path - # will have link count >1 at any moment, so won't be cleaned up - os.link(tmp_path, master_path) - os.link(master_path, dest_path) + + if img_info.get('no_cache'): + LOG.debug("Caching is disabled for image %s", href) + # Cache disabled, link directly to destination + os.link(tmp_path, dest_path) + else: + # NOTE(dtantsur): no need for global lock here - master_path + # will have link count >1 at any moment, so won't be cleaned up + os.link(tmp_path, master_path) + os.link(master_path, dest_path) except OSError as exc: msg = (_("Could not link image %(img_href)s from %(src_path)s " "to %(dst_path)s, error: %(exc)s") % @@ -410,12 +420,12 @@ def cleanup(priority): return _add_property_to_class_func -def _delete_master_path_if_stale(master_path, href, ctx): +def _delete_master_path_if_stale(master_path, href, img_info): """Delete image from cache if it is not up to date with href contents. :param master_path: path to an image in master cache :param href: image href - :param ctx: context to use + :param img_info: image information from the service :returns: True if master_path is up to date with href contents, False if master_path was stale and was deleted or it didn't exist """ @@ -423,8 +433,7 @@ def _delete_master_path_if_stale(master_path, href, ctx): # Glance image contents cannot be updated without changing image's UUID return os.path.exists(master_path) if os.path.exists(master_path): - img_service = image_service.get_image_service(href, context=ctx) - img_mtime = img_service.show(href).get('updated_at') + img_mtime = img_info.get('updated_at') if not img_mtime: # This means that href is not a glance image and doesn't have an # updated_at attribute. To play on the safe side, redownload the diff --git a/ironic/tests/unit/common/test_image_service.py b/ironic/tests/unit/common/test_image_service.py index 6aa5da5462..30b6f4f37f 100644 --- a/ironic/tests/unit/common/test_image_service.py +++ b/ironic/tests/unit/common/test_image_service.py @@ -205,7 +205,7 @@ class HttpImageServiceTestCase(base.TestCase): head_mock.assert_called_once_with(self.href, verify=True, timeout=60) self.assertEqual({'size': 100, 'updated_at': mtime_date, - 'properties': {}}, result) + 'properties': {}, 'no_cache': False}, result) def test_show_rfc_822(self): self._test_show(mtime='Tue, 15 Nov 2014 08:12:31 GMT', @@ -219,6 +219,33 @@ class HttpImageServiceTestCase(base.TestCase): self._test_show(mtime='Tue Nov 15 08:12:31 2014', mtime_date=datetime.datetime(2014, 11, 15, 8, 12, 31)) + @mock.patch.object(requests, 'head', autospec=True) + def _test_show_with_cache(self, head_mock, cache_control, no_cache): + head_mock.return_value.status_code = http_client.OK + head_mock.return_value.headers = { + 'Content-Length': 100, + 'Last-Modified': 'Tue, 15 Nov 2014 08:12:31 GMT', + 'Cache-Control': cache_control, + } + result = self.service.show(self.href) + head_mock.assert_called_once_with(self.href, verify=True, + timeout=60) + self.assertEqual({ + 'size': 100, + 'updated_at': datetime.datetime(2014, 11, 15, 8, 12, 31), + 'properties': {}, + 'no_cache': no_cache}, result) + + def test_show_cache_allowed(self): + self._test_show_with_cache( + # Just because we cannot have nice things, "no-cache" actually + # means "cache, but always re-validate". + cache_control='no-cache, private', no_cache=False) + + def test_show_cache_disabled(self): + self._test_show_with_cache( + cache_control='no-store', no_cache=True) + @mock.patch.object(requests, 'head', autospec=True) def test_show_no_content_length(self, head_mock): head_mock.return_value.status_code = http_client.OK @@ -425,7 +452,8 @@ class FileImageServiceTestCase(base.TestCase): self.assertEqual({'size': 42, 'updated_at': datetime.datetime(2015, 5, 8, 12, 25, 9, 164191), - 'properties': {}}, result) + 'properties': {}, + 'no_cache': True}, result) @mock.patch.object(os, 'link', autospec=True) @mock.patch.object(os, 'remove', autospec=True) diff --git a/ironic/tests/unit/drivers/modules/test_image_cache.py b/ironic/tests/unit/drivers/modules/test_image_cache.py index 2e077997b1..cc6e36a505 100644 --- a/ironic/tests/unit/drivers/modules/test_image_cache.py +++ b/ironic/tests/unit/drivers/modules/test_image_cache.py @@ -37,10 +37,10 @@ def touch(filename): open(filename, 'w').close() -class TestImageCacheFetch(base.TestCase): +class BaseTest(base.TestCase): def setUp(self): - super(TestImageCacheFetch, self).setUp() + super().setUp() self.master_dir = tempfile.mkdtemp() self.cache = image_cache.ImageCache(self.master_dir, None, None) self.dest_dir = tempfile.mkdtemp() @@ -48,28 +48,31 @@ class TestImageCacheFetch(base.TestCase): self.uuid = uuidutils.generate_uuid() self.master_path = ''.join([os.path.join(self.master_dir, self.uuid), '.converted']) + self.img_info = {} + + +@mock.patch.object(image_service, 'get_image_service', autospec=True) +@mock.patch.object(image_cache.ImageCache, 'clean_up', autospec=True) +@mock.patch.object(image_cache.ImageCache, '_download_image', autospec=True) +class TestImageCacheFetch(BaseTest): @mock.patch.object(image_cache, '_fetch', autospec=True) - @mock.patch.object(image_cache.ImageCache, 'clean_up', autospec=True) - @mock.patch.object(image_cache.ImageCache, '_download_image', - autospec=True) - def test_fetch_image_no_master_dir(self, mock_download, mock_clean_up, - mock_fetch): + def test_fetch_image_no_master_dir(self, mock_fetch, mock_download, + mock_clean_up, mock_image_service): self.cache.master_dir = None self.cache.fetch_image(self.uuid, self.dest_path) self.assertFalse(mock_download.called) mock_fetch.assert_called_once_with( None, self.uuid, self.dest_path, True) self.assertFalse(mock_clean_up.called) + mock_image_service.assert_not_called() @mock.patch.object(image_cache, '_fetch', autospec=True) - @mock.patch.object(image_cache.ImageCache, 'clean_up', autospec=True) - @mock.patch.object(image_cache.ImageCache, '_download_image', - autospec=True) def test_fetch_image_no_master_dir_memory_low(self, + mock_fetch, mock_download, mock_clean_up, - mock_fetch): + mock_image_service): mock_fetch.side_effect = exception.InsufficentMemory self.cache.master_dir = None self.assertRaises(exception.InsufficentMemory, @@ -79,10 +82,8 @@ class TestImageCacheFetch(base.TestCase): mock_fetch.assert_called_once_with( None, self.uuid, self.dest_path, True) self.assertFalse(mock_clean_up.called) + mock_image_service.assert_not_called() - @mock.patch.object(image_cache.ImageCache, 'clean_up', autospec=True) - @mock.patch.object(image_cache.ImageCache, '_download_image', - autospec=True) @mock.patch.object(os, 'link', autospec=True) @mock.patch.object(image_cache, '_delete_dest_path_if_stale', return_value=True, autospec=True) @@ -90,18 +91,18 @@ class TestImageCacheFetch(base.TestCase): return_value=True, autospec=True) def test_fetch_image_dest_and_master_uptodate( self, mock_cache_upd, mock_dest_upd, mock_link, mock_download, - mock_clean_up): + mock_clean_up, mock_image_service): self.cache.fetch_image(self.uuid, self.dest_path) - mock_cache_upd.assert_called_once_with(self.master_path, self.uuid, - None) + mock_cache_upd.assert_called_once_with( + self.master_path, self.uuid, + mock_image_service.return_value.show.return_value) mock_dest_upd.assert_called_once_with(self.master_path, self.dest_path) self.assertFalse(mock_link.called) self.assertFalse(mock_download.called) self.assertFalse(mock_clean_up.called) + mock_image_service.assert_called_once_with(self.uuid, context=None) + mock_image_service.return_value.show.assert_called_once_with(self.uuid) - @mock.patch.object(image_cache.ImageCache, 'clean_up', autospec=True) - @mock.patch.object(image_cache.ImageCache, '_download_image', - autospec=True) @mock.patch.object(os, 'link', autospec=True) @mock.patch.object(image_cache, '_delete_dest_path_if_stale', return_value=True, autospec=True) @@ -109,19 +110,17 @@ class TestImageCacheFetch(base.TestCase): return_value=True, autospec=True) def test_fetch_image_dest_and_master_uptodate_no_force_raw( self, mock_cache_upd, mock_dest_upd, mock_link, mock_download, - mock_clean_up): + mock_clean_up, mock_image_service): master_path = os.path.join(self.master_dir, self.uuid) self.cache.fetch_image(self.uuid, self.dest_path, force_raw=False) - mock_cache_upd.assert_called_once_with(master_path, self.uuid, - None) + mock_cache_upd.assert_called_once_with( + master_path, self.uuid, + mock_image_service.return_value.show.return_value) mock_dest_upd.assert_called_once_with(master_path, self.dest_path) self.assertFalse(mock_link.called) self.assertFalse(mock_download.called) self.assertFalse(mock_clean_up.called) - @mock.patch.object(image_cache.ImageCache, 'clean_up', autospec=True) - @mock.patch.object(image_cache.ImageCache, '_download_image', - autospec=True) @mock.patch.object(os, 'link', autospec=True) @mock.patch.object(image_cache, '_delete_dest_path_if_stale', return_value=False, autospec=True) @@ -129,18 +128,16 @@ class TestImageCacheFetch(base.TestCase): return_value=True, autospec=True) def test_fetch_image_dest_out_of_date( self, mock_cache_upd, mock_dest_upd, mock_link, mock_download, - mock_clean_up): + mock_clean_up, mock_image_service): self.cache.fetch_image(self.uuid, self.dest_path) - mock_cache_upd.assert_called_once_with(self.master_path, self.uuid, - None) + mock_cache_upd.assert_called_once_with( + self.master_path, self.uuid, + mock_image_service.return_value.show.return_value) mock_dest_upd.assert_called_once_with(self.master_path, self.dest_path) mock_link.assert_called_once_with(self.master_path, self.dest_path) self.assertFalse(mock_download.called) self.assertFalse(mock_clean_up.called) - @mock.patch.object(image_cache.ImageCache, 'clean_up', autospec=True) - @mock.patch.object(image_cache.ImageCache, '_download_image', - autospec=True) @mock.patch.object(os, 'link', autospec=True) @mock.patch.object(image_cache, '_delete_dest_path_if_stale', return_value=True, autospec=True) @@ -148,20 +145,21 @@ class TestImageCacheFetch(base.TestCase): return_value=False, autospec=True) def test_fetch_image_master_out_of_date( self, mock_cache_upd, mock_dest_upd, mock_link, mock_download, - mock_clean_up): + mock_clean_up, mock_image_service): self.cache.fetch_image(self.uuid, self.dest_path) - mock_cache_upd.assert_called_once_with(self.master_path, self.uuid, - None) + mock_cache_upd.assert_called_once_with( + self.master_path, self.uuid, + mock_image_service.return_value.show.return_value) mock_dest_upd.assert_called_once_with(self.master_path, self.dest_path) self.assertFalse(mock_link.called) mock_download.assert_called_once_with( self.cache, self.uuid, self.master_path, self.dest_path, + mock_image_service.return_value.show.return_value, ctx=None, force_raw=True) mock_clean_up.assert_called_once_with(self.cache) + mock_image_service.assert_called_once_with(self.uuid, context=None) + mock_image_service.return_value.show.assert_called_once_with(self.uuid) - @mock.patch.object(image_cache.ImageCache, 'clean_up', autospec=True) - @mock.patch.object(image_cache.ImageCache, '_download_image', - autospec=True) @mock.patch.object(os, 'link', autospec=True) @mock.patch.object(image_cache, '_delete_dest_path_if_stale', return_value=True, autospec=True) @@ -169,21 +167,21 @@ class TestImageCacheFetch(base.TestCase): return_value=False, autospec=True) def test_fetch_image_both_master_and_dest_out_of_date( self, mock_cache_upd, mock_dest_upd, mock_link, mock_download, - mock_clean_up): + mock_clean_up, mock_image_service): self.cache.fetch_image(self.uuid, self.dest_path) - mock_cache_upd.assert_called_once_with(self.master_path, self.uuid, - None) + mock_cache_upd.assert_called_once_with( + self.master_path, self.uuid, + mock_image_service.return_value.show.return_value) mock_dest_upd.assert_called_once_with(self.master_path, self.dest_path) self.assertFalse(mock_link.called) mock_download.assert_called_once_with( self.cache, self.uuid, self.master_path, self.dest_path, + mock_image_service.return_value.show.return_value, ctx=None, force_raw=True) mock_clean_up.assert_called_once_with(self.cache) - @mock.patch.object(image_cache.ImageCache, 'clean_up', autospec=True) - @mock.patch.object(image_cache.ImageCache, '_download_image', - autospec=True) - def test_fetch_image_not_uuid(self, mock_download, mock_clean_up): + def test_fetch_image_not_uuid(self, mock_download, mock_clean_up, + mock_image_service): href = u'http://abc.com/ubuntu.qcow2' href_converted = str(uuid.uuid5(uuid.NAMESPACE_URL, href)) master_path = ''.join([os.path.join(self.master_dir, href_converted), @@ -191,24 +189,27 @@ class TestImageCacheFetch(base.TestCase): self.cache.fetch_image(href, self.dest_path) mock_download.assert_called_once_with( self.cache, href, master_path, self.dest_path, + mock_image_service.return_value.show.return_value, ctx=None, force_raw=True) self.assertTrue(mock_clean_up.called) - @mock.patch.object(image_cache.ImageCache, 'clean_up', autospec=True) - @mock.patch.object(image_cache.ImageCache, '_download_image', - autospec=True) def test_fetch_image_not_uuid_no_force_raw(self, mock_download, - mock_clean_up): + mock_clean_up, + mock_image_service): href = u'http://abc.com/ubuntu.qcow2' href_converted = str(uuid.uuid5(uuid.NAMESPACE_URL, href)) master_path = os.path.join(self.master_dir, href_converted) self.cache.fetch_image(href, self.dest_path, force_raw=False) mock_download.assert_called_once_with( self.cache, href, master_path, self.dest_path, + mock_image_service.return_value.show.return_value, ctx=None, force_raw=False) self.assertTrue(mock_clean_up.called) - @mock.patch.object(image_cache, '_fetch', autospec=True) + +@mock.patch.object(image_cache, '_fetch', autospec=True) +class TestImageCacheDownload(BaseTest): + def test__download_image(self, mock_fetch): def _fake_fetch(ctx, uuid, tmp_path, *args): self.assertEqual(self.uuid, uuid) @@ -218,7 +219,8 @@ class TestImageCacheFetch(base.TestCase): fp.write("TEST") mock_fetch.side_effect = _fake_fetch - self.cache._download_image(self.uuid, self.master_path, self.dest_path) + self.cache._download_image(self.uuid, self.master_path, self.dest_path, + self.img_info) self.assertTrue(os.path.isfile(self.dest_path)) self.assertTrue(os.path.isfile(self.master_path)) self.assertEqual(os.stat(self.dest_path).st_ino, @@ -226,7 +228,6 @@ class TestImageCacheFetch(base.TestCase): with open(self.dest_path) as fp: self.assertEqual("TEST", fp.read()) - @mock.patch.object(image_cache, '_fetch', autospec=True) def test__download_image_large_url(self, mock_fetch): # A long enough URL may exceed the file name limits of the file system. # Make sure we don't use any parts of the URL anywhere. @@ -240,7 +241,8 @@ class TestImageCacheFetch(base.TestCase): fp.write("TEST") mock_fetch.side_effect = _fake_fetch - self.cache._download_image(url, self.master_path, self.dest_path) + self.cache._download_image(url, self.master_path, self.dest_path, + self.img_info) self.assertTrue(os.path.isfile(self.dest_path)) self.assertTrue(os.path.isfile(self.master_path)) self.assertEqual(os.stat(self.dest_path).st_ino, @@ -248,123 +250,97 @@ class TestImageCacheFetch(base.TestCase): with open(self.dest_path) as fp: self.assertEqual("TEST", fp.read()) - @mock.patch.object(image_cache, '_fetch', autospec=True) @mock.patch.object(image_cache, 'LOG', autospec=True) @mock.patch.object(os, 'link', autospec=True) def test__download_image_linkfail(self, mock_link, mock_log, mock_fetch): mock_link.side_effect = [None, OSError] self.assertRaises(exception.ImageDownloadFailed, self.cache._download_image, - self.uuid, self.master_path, self.dest_path) + self.uuid, self.master_path, self.dest_path, + self.img_info) self.assertTrue(mock_fetch.called) self.assertEqual(2, mock_link.call_count) self.assertTrue(mock_log.error.called) - @mock.patch.object(image_cache, '_fetch', autospec=True) def test__download_image_raises_memory_guard(self, mock_fetch): mock_fetch.side_effect = exception.InsufficentMemory self.assertRaises(exception.InsufficentMemory, self.cache._download_image, self.uuid, self.master_path, - self.dest_path) + self.dest_path, self.img_info) @mock.patch.object(os, 'unlink', autospec=True) -class TestUpdateImages(base.TestCase): - - def setUp(self): - super(TestUpdateImages, self).setUp() - self.master_dir = tempfile.mkdtemp() - self.dest_dir = tempfile.mkdtemp() - self.dest_path = os.path.join(self.dest_dir, 'dest') - self.uuid = uuidutils.generate_uuid() - self.master_path = os.path.join(self.master_dir, self.uuid) +class TestUpdateImages(BaseTest): @mock.patch.object(os.path, 'exists', return_value=False, autospec=True) - @mock.patch.object(image_service, 'get_image_service', autospec=True) def test__delete_master_path_if_stale_glance_img_not_cached( - self, mock_gis, mock_path_exists, mock_unlink): + self, mock_path_exists, mock_unlink): res = image_cache._delete_master_path_if_stale(self.master_path, - self.uuid, None) - self.assertFalse(mock_gis.called) + self.uuid, + self.img_info) self.assertFalse(mock_unlink.called) mock_path_exists.assert_called_once_with(self.master_path) self.assertFalse(res) @mock.patch.object(os.path, 'exists', return_value=True, autospec=True) - @mock.patch.object(image_service, 'get_image_service', autospec=True) def test__delete_master_path_if_stale_glance_img( - self, mock_gis, mock_path_exists, mock_unlink): + self, mock_path_exists, mock_unlink): res = image_cache._delete_master_path_if_stale(self.master_path, - self.uuid, None) - self.assertFalse(mock_gis.called) + self.uuid, + self.img_info) self.assertFalse(mock_unlink.called) mock_path_exists.assert_called_once_with(self.master_path) self.assertTrue(res) - @mock.patch.object(image_service, 'get_image_service', autospec=True) - def test__delete_master_path_if_stale_no_master(self, mock_gis, - mock_unlink): + def test__delete_master_path_if_stale_no_master(self, mock_unlink): res = image_cache._delete_master_path_if_stale(self.master_path, - 'http://11', None) - self.assertFalse(mock_gis.called) + 'http://11', + self.img_info) self.assertFalse(mock_unlink.called) self.assertFalse(res) - @mock.patch.object(image_service, 'get_image_service', autospec=True) - def test__delete_master_path_if_stale_no_updated_at(self, mock_gis, - mock_unlink): + def test__delete_master_path_if_stale_no_updated_at(self, mock_unlink): touch(self.master_path) href = 'http://awesomefreeimages.al/img111' - mock_gis.return_value.show.return_value = {} res = image_cache._delete_master_path_if_stale(self.master_path, href, - None) - mock_gis.assert_called_once_with(href, context=None) + self.img_info) mock_unlink.assert_called_once_with(self.master_path) self.assertFalse(res) - @mock.patch.object(image_service, 'get_image_service', autospec=True) - def test__delete_master_path_if_stale_master_up_to_date(self, mock_gis, - mock_unlink): + def test__delete_master_path_if_stale_master_up_to_date(self, mock_unlink): touch(self.master_path) href = 'http://awesomefreeimages.al/img999' - mock_gis.return_value.show.return_value = { + self.img_info = { 'updated_at': datetime.datetime(1999, 11, 15, 8, 12, 31) } res = image_cache._delete_master_path_if_stale(self.master_path, href, - None) - mock_gis.assert_called_once_with(href, context=None) + self.img_info) self.assertFalse(mock_unlink.called) self.assertTrue(res) - @mock.patch.object(image_service, 'get_image_service', autospec=True) - def test__delete_master_path_if_stale_master_same_time(self, mock_gis, - mock_unlink): + def test__delete_master_path_if_stale_master_same_time(self, mock_unlink): # When times identical should not delete cached file touch(self.master_path) mtime = utils.unix_file_modification_datetime(self.master_path) href = 'http://awesomefreeimages.al/img999' - mock_gis.return_value.show.return_value = { + self.img_info = { 'updated_at': mtime } res = image_cache._delete_master_path_if_stale(self.master_path, href, - None) - mock_gis.assert_called_once_with(href, context=None) + self.img_info) self.assertFalse(mock_unlink.called) self.assertTrue(res) - @mock.patch.object(image_service, 'get_image_service', autospec=True) - def test__delete_master_path_if_stale_out_of_date(self, mock_gis, - mock_unlink): + def test__delete_master_path_if_stale_out_of_date(self, mock_unlink): touch(self.master_path) href = 'http://awesomefreeimages.al/img999' - mock_gis.return_value.show.return_value = { + self.img_info = { 'updated_at': datetime.datetime((datetime.datetime.utcnow().year + 1), 11, 15, 8, 12, 31) } res = image_cache._delete_master_path_if_stale(self.master_path, href, - None) - mock_gis.assert_called_once_with(href, context=None) + self.img_info) mock_unlink.assert_called_once_with(self.master_path) self.assertFalse(res) @@ -552,7 +528,7 @@ class TestImageCacheCleanUp(base.TestCase): mock_fetch.side_effect = _fake_fetch master_path = os.path.join(self.master_dir, 'uuid') dest_path = os.path.join(tempfile.mkdtemp(), 'dest') - self.cache._download_image('uuid', master_path, dest_path) + self.cache._download_image('uuid', master_path, dest_path, {}) self.assertTrue(mock_rmtree.called) @mock.patch.object(utils, 'rmtree_without_raise', autospec=True) @@ -561,7 +537,7 @@ class TestImageCacheCleanUp(base.TestCase): mock_fetch.side_effect = exception.IronicException self.assertRaises(exception.IronicException, self.cache._download_image, - 'uuid', 'fake', 'fake') + 'uuid', 'fake', 'fake', {}) self.assertTrue(mock_rmtree.called) @mock.patch.object(image_cache.LOG, 'warning', autospec=True) diff --git a/releasenotes/notes/no-cache-df7caa45f3d8b6d7.yaml b/releasenotes/notes/no-cache-df7caa45f3d8b6d7.yaml new file mode 100644 index 0000000000..87d0113ac9 --- /dev/null +++ b/releasenotes/notes/no-cache-df7caa45f3d8b6d7.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + The image cache now respects the ``Cache-Control: no-store`` header + for HTTP(s) images. + - | + File images are no longer cached in the image cache to avoid unnecessary + consumption of the disk space.