diff options
author | Jim Rollenhagen <jim@jimrollenhagen.com> | 2015-01-06 07:49:26 -0800 |
---|---|---|
committer | Matthew Gilliard <matthew.gilliard@hp.com> | 2015-01-19 11:11:11 +0000 |
commit | c2b89f93a2ae25faeb18e1fcd93249bcf29ebc2e (patch) | |
tree | ef1308aed342d94ebd2453cc0fceafa1908c2232 | |
parent | 96177e71dcb21777471a2f6bf1b08da80ecc4962 (diff) | |
download | ironic-c2b89f93a2ae25faeb18e1fcd93249bcf29ebc2e.tar.gz |
Ensure that image link points to the correct image
If an image is cached for a node, and the image changes, Ironic won't
notice because we only check if the link exists. In this change it's
proposed to check in the image_cache if the image is up to date.
Closes-bug: #1405448
Co-Authored-By: Vladyslav Drok <vdrok@mirantis.com>
(cherry picked from commit 2f0c8cd8d8d8b2846b641b518b742ca051425028)
Conflicts:
ironic/drivers/modules/image_cache.py
ironic/tests/drivers/test_image_cache.py
Change-Id: If67c7e60eb6d048fa3aa03469d7904c6c233ff89
-rw-r--r-- | ironic/drivers/modules/image_cache.py | 17 | ||||
-rw-r--r-- | ironic/tests/drivers/test_image_cache.py | 39 |
2 files changed, 48 insertions, 8 deletions
diff --git a/ironic/drivers/modules/image_cache.py b/ironic/drivers/modules/image_cache.py index 2765c18c0..e50c602d1 100644 --- a/ironic/drivers/modules/image_cache.py +++ b/ironic/drivers/modules/image_cache.py @@ -104,11 +104,18 @@ class ImageCache(object): # TODO(dtantsur): lock expiration time with lockutils.lock(img_download_lock_name, 'ironic-'): if os.path.exists(dest_path): - LOG.debug("Destination %(dest)s already exists for " - "image %(uuid)s" % - {'uuid': uuid, - 'dest': dest_path}) - return + # 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 + if (os.path.exists(master_path) and + (os.stat(dest_path).st_ino == + os.stat(master_path).st_ino)): + LOG.debug("Destination %(dest)s already exists for " + "image %(uuid)s" % + {'uuid': uuid, + 'dest': dest_path}) + return + os.unlink(dest_path) try: # NOTE(dtantsur): ensure we're not in the middle of clean up diff --git a/ironic/tests/drivers/test_image_cache.py b/ironic/tests/drivers/test_image_cache.py index 0849b8285..0991b48fc 100644 --- a/ironic/tests/drivers/test_image_cache.py +++ b/ironic/tests/drivers/test_image_cache.py @@ -56,16 +56,49 @@ class TestImageCacheFetch(base.TestCase): None, 'uuid', self.dest_path, None) self.assertFalse(mock_clean_up.called) + @mock.patch.object(os, 'unlink') @mock.patch.object(image_cache.ImageCache, 'clean_up') @mock.patch.object(image_cache.ImageCache, '_download_image') - def test_fetch_image_dest_exists(self, mock_download, mock_clean_up, - mock_fetch_to_raw): + def test_fetch_image_dest_and_master_exist_uptodate(self, mock_download, + mock_clean_up, mock_unlink, mock_fetch): + touch(self.master_path) + os.link(self.master_path, self.dest_path) + self.cache.fetch_image(self.uuid, self.dest_path) + self.assertFalse(mock_unlink.called) + self.assertFalse(mock_download.called) + self.assertFalse(mock_fetch.called) + self.assertFalse(mock_clean_up.called) + + @mock.patch.object(image_cache.ImageCache, 'clean_up') + @mock.patch.object(image_cache.ImageCache, '_download_image') + def test_fetch_image_dest_and_master_exist_outdated(self, mock_download, + mock_clean_up, mock_fetch): + touch(self.master_path) touch(self.dest_path) + self.assertNotEqual(os.stat(self.dest_path).st_ino, + os.stat(self.master_path).st_ino) self.cache.fetch_image(self.uuid, self.dest_path) self.assertFalse(mock_download.called) - self.assertFalse(mock_fetch_to_raw.called) + self.assertFalse(mock_fetch.called) + self.assertTrue(os.path.isfile(self.dest_path)) + self.assertEqual(os.stat(self.dest_path).st_ino, + os.stat(self.master_path).st_ino) self.assertFalse(mock_clean_up.called) + @mock.patch.object(os, 'unlink') + @mock.patch.object(image_cache.ImageCache, 'clean_up') + @mock.patch.object(image_cache.ImageCache, '_download_image') + def test_fetch_image_only_dest_exists(self, mock_download, + mock_clean_up, mock_unlink, mock_fetch): + touch(self.dest_path) + self.cache.fetch_image(self.uuid, self.dest_path) + mock_unlink.assert_called_once_with(self.dest_path) + self.assertFalse(mock_fetch.called) + mock_download.assert_called_once_with( + self.uuid, self.master_path, self.dest_path, + ctx=None) + self.assertTrue(mock_clean_up.called) + @mock.patch.object(image_cache.ImageCache, 'clean_up') @mock.patch.object(image_cache.ImageCache, '_download_image') def test_fetch_image_master_exists(self, mock_download, mock_clean_up, |