From 43ead54a4c8e9e9086ca1006445c5ab9149a5fed Mon Sep 17 00:00:00 2001 From: Lucas <12496191+lucashuy@users.noreply.github.com> Date: Wed, 6 Dec 2023 08:38:48 -0800 Subject: [PATCH] fix: Recursively call method on directory children (#6402) * Recursively call method on directory children * Add test and typing * Catch Windows error when trying to determine resolved file path * Test positive case and add file in console output --- samcli/lib/utils/tar.py | 19 ++++++++++++++++--- tests/unit/lib/utils/test_tar.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/samcli/lib/utils/tar.py b/samcli/lib/utils/tar.py index 3d1bdc31fc..0cfa0207ba 100644 --- a/samcli/lib/utils/tar.py +++ b/samcli/lib/utils/tar.py @@ -62,7 +62,7 @@ def create_tarball( tarballfile.close() -def _validate_destinations_exists(tar_paths: List[Union[str, Path]]) -> bool: +def _validate_destinations_exists(tar_paths: Union[List[Union[str, Path]], List[Path]]) -> bool: """ Validates whether the destination of a symlink exists by resolving the link and checking the resolved path. @@ -79,9 +79,22 @@ def _validate_destinations_exists(tar_paths: List[Union[str, Path]]) -> bool: """ for file in tar_paths: file_path_obj = Path(file) - resolved_path = file_path_obj.resolve() - if file_path_obj.is_symlink() and not resolved_path.exists(): + try: + resolved_path = file_path_obj.resolve() + except OSError: + # this exception will occur on Windows and will return + # a WinError 123 + LOG.warning(f"Failed to resolve file {file_path_obj} on the host machine") + return False + + if file_path_obj.is_dir(): + # recursively call this method to validate the children are not symlinks to empty locations + children = list(file_path_obj.iterdir()) + if not _validate_destinations_exists(children): + # exits early + return False + elif file_path_obj.is_symlink() and not resolved_path.exists(): LOG.warning(f"Symlinked file {file_path_obj} -> {resolved_path} does not exist!") return False diff --git a/tests/unit/lib/utils/test_tar.py b/tests/unit/lib/utils/test_tar.py index fd98c35914..f9b4bfda96 100644 --- a/tests/unit/lib/utils/test_tar.py +++ b/tests/unit/lib/utils/test_tar.py @@ -196,9 +196,38 @@ def test_validating_symlinked_tar_path(self, does_resolved_exist, path_mock): mock_path_object.resolve.return_value = mock_resolved_object mock_path_object.is_symlink = Mock() mock_path_object.is_symlink.return_value = True + mock_path_object.is_dir.return_value = False path_mock.return_value = mock_path_object result = _validate_destinations_exists(["mock_path"]) self.assertEqual(result, does_resolved_exist) + + @parameterized.expand( + [ + (True,), + (False,), + ] + ) + @patch("samcli.lib.utils.tar.Path") + def test_validating_symlinked_tar_path_directory(self, file_exists, path_mock): + mock_child_resolve = Mock() + mock_child_resolve.exists.return_value = file_exists + + mock_child = Mock() + mock_child.is_symlink.return_value = True + mock_child.is_dir.return_value = False + mock_child.resolve.return_value = mock_child_resolve + + mock_dir_object = Mock() + mock_dir_object.is_symlink.return_value = False + mock_dir_object.is_dir.return_value = True + mock_dir_object.iterdir.return_value = ["mock_child"] + mock_dir_object.resolve = Mock() + + path_mock.side_effect = [mock_dir_object, mock_child] + + result = _validate_destinations_exists(["mock_folder"]) + + self.assertEqual(result, file_exists)