From c3e362cb9a97629781a9b13ef19271ccde84356b Mon Sep 17 00:00:00 2001 From: Afonne-CID Date: Tue, 1 Sep 2026 20:41:33 +0100 Subject: [PATCH] Don't fail publishing an already published image If the fallback copy raises SameFileError, treat the publish as successful. Closes-Bug: #2150735 Change-Id: Ia3d9aa4e71d691dbd67e1e00b09986dda5b68335 Signed-off-by: Afonne-CID (cherry picked from commit Ia3d9aa4e71d691dbd67e1e00b09986dda5b68335) --- ironic/common/image_publisher.py | 8 +++++++- .../tests/unit/common/test_image_publisher.py | 18 ++++++++++++++++++ ...file-error-on-publish-7e585c90c9abc1c7.yaml | 9 +++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/fix-samefile-error-on-publish-7e585c90c9abc1c7.yaml diff --git a/ironic/common/image_publisher.py b/ironic/common/image_publisher.py index f0c70fe631..3e4d777c45 100644 --- a/ironic/common/image_publisher.py +++ b/ironic/common/image_publisher.py @@ -102,7 +102,13 @@ def publish(self, source_path, file_name=None): 'public': published_file, 'error': exc}) - shutil.copyfile(source_path, published_file) + try: + shutil.copyfile(source_path, published_file) + except shutil.SameFileError: + LOG.debug('Image %(image)s is already published at ' + '%(public)s', + {'image': source_path, + 'public': published_file}) os.chmod(published_file, self.file_permission) if self.image_subdir: diff --git a/ironic/tests/unit/common/test_image_publisher.py b/ironic/tests/unit/common/test_image_publisher.py index 4a7166fb8b..226d567895 100644 --- a/ironic/tests/unit/common/test_image_publisher.py +++ b/ironic/tests/unit/common/test_image_publisher.py @@ -172,6 +172,24 @@ def test_publish_local_copy(self, mock_mkdir, mock_link, mock_chmod.assert_called_once_with('/httpboot/redfish/boot.iso', 0o644) + @mock.patch.object(os, 'chmod', autospec=True) + @mock.patch.object(shutil, 'copyfile', autospec=True) + @mock.patch.object(os, 'link', autospec=True) + @mock.patch.object(os, 'mkdir', autospec=True) + def test_publish_local_already_published(self, mock_mkdir, mock_link, + mock_copyfile, mock_chmod): + mock_link.side_effect = FileExistsError() + mock_copyfile.side_effect = shutil.SameFileError() + + url = self.publisher.publish('file.iso', 'boot.iso') + + self.assertEqual( + 'http://localhost/redfish/boot.iso', url) + mock_copyfile.assert_called_once_with( + 'file.iso', '/httpboot/redfish/boot.iso') + mock_chmod.assert_called_once_with('/httpboot/redfish/boot.iso', + 0o644) + @mock.patch.object(utils, 'unlink_without_raise', autospec=True) def test_unpublish_local(self, mock_unlink): object_name = 'boot.iso' diff --git a/releasenotes/notes/fix-samefile-error-on-publish-7e585c90c9abc1c7.yaml b/releasenotes/notes/fix-samefile-error-on-publish-7e585c90c9abc1c7.yaml new file mode 100644 index 0000000000..5bf137914e --- /dev/null +++ b/releasenotes/notes/fix-samefile-error-on-publish-7e585c90c9abc1c7.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + Fixes a ``SameFileError`` when publishing an image to the local HTTP + server whose destination is already a hardlink to the source, which + happens when a previous deployment published the same cached image and + did not clean it up. The image is already published in that case, so it + is no longer treated as an error. See + `bug 2150735 `_.