From afc6e3748ff91127abda5573f62318df2beeba82 Mon Sep 17 00:00:00 2001 From: James Saryerwinnie Date: Mon, 25 Nov 2013 15:05:13 -0800 Subject: [PATCH] Notify user when broken symlinks are encountered You will now see an error message shown as well as a non zero RC: $ aws s3 sync anotherdir/ s3://jamesls-test-sync/ [Errno 2] No such file or directory: '/private/tmp/symlnk/anotherdir/z-badsylmink' $ echo $? 1 There is potential to add something like a ``--skip-bad-symlinks`` option, but the default behavior is to let the user know that we've hit a bad symlink. Fies #425 and #487. --- CHANGELOG.rst | 3 +++ awscli/customizations/s3/executer.py | 4 ++++ awscli/customizations/s3/s3handler.py | 7 +++---- .../customizations/s3/test_plugin.py | 17 +++++++++++++++++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a204325ee044..de1d6d01ff5c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -12,6 +12,9 @@ Next Release (TBD) (issue 494 `__) * Fix issue with ``--associate-public-ip-address`` when a ``--subnet-id`` is provided `(issue 501 `__) +* Fix issue with symlinks silently failing during ``s3 sync/cp`` + (`issue 425 `__ + and `issue 487 `__) 1.2.5 diff --git a/awscli/customizations/s3/executer.py b/awscli/customizations/s3/executer.py index 2886c0e9360b..360b64c033e4 100644 --- a/awscli/customizations/s3/executer.py +++ b/awscli/customizations/s3/executer.py @@ -144,6 +144,7 @@ def __init__(self, result_queue, done, quiet, interrupt, timeout): self._num_parts = 0 self._file_count = 0 self._lock = threading.Lock() + self._needs_newline = False self._total_parts = 0 self._total_files = '...' @@ -179,6 +180,8 @@ def run(self): except Queue.Empty: pass if self._done.isSet(): + if self._needs_newline: + sys.stdout.write('\n') break def _process_print_task(self, print_task): @@ -226,4 +229,5 @@ def _process_print_task(self, print_task): final_str += prog_str if not self._quiet: uni_print(final_str) + self._needs_newline = not final_str.endswith('\n') sys.stdout.flush() diff --git a/awscli/customizations/s3/s3handler.py b/awscli/customizations/s3/s3handler.py index 4406fe502fa4..8ee635262e54 100644 --- a/awscli/customizations/s3/s3handler.py +++ b/awscli/customizations/s3/s3handler.py @@ -80,10 +80,9 @@ def call(self, files): self.result_queue.join() except Exception as e: - LOGGER.error('Exception caught during task execution: %s', - str(e)) - # Log the traceback at DEBUG level. - LOGGER.debug(str(e), exc_info=True) + LOGGER.debug('Exception caught during task execution: %s', + str(e), exc_info=True) + self.result_queue.put({'message': str(e), 'error': True}) except KeyboardInterrupt: self.interrupt.set() self.result_queue.put({'message': "Cleaning up. Please wait...", diff --git a/tests/integration/customizations/s3/test_plugin.py b/tests/integration/customizations/s3/test_plugin.py index 3e5734cab77a..7fa3b96cdb89 100644 --- a/tests/integration/customizations/s3/test_plugin.py +++ b/tests/integration/customizations/s3/test_plugin.py @@ -362,6 +362,23 @@ def test_sync_with_delete_option_with_same_prefix(self): self.assertEqual('', p.stdout) +class TestBadSymlinks(BaseS3CLICommand): + def test_bad_symlink_stops_sync_process(self): + bucket_name = self.create_bucket() + nested_dir = os.path.join(self.files.rootdir, 'realfiles') + os.mkdir(nested_dir) + full_path = self.files.create_file(os.path.join(nested_dir, 'foo.txt'), + contents='foo.txt contents') + symlink_dir = os.path.join(self.files.rootdir, 'symlinkdir') + os.mkdir(symlink_dir) + os.symlink(full_path, os.path.join(symlink_dir, 'a-goodsymlink')) + os.symlink('non-existent-file', os.path.join(symlink_dir, 'b-badsymlink')) + os.symlink(full_path, os.path.join(symlink_dir, 'c-goodsymlink')) + p = aws('s3 sync %s s3://%s/' % (symlink_dir, bucket_name)) + self.assertEqual(p.rc, 1, p.stdout) + self.assertIn('[Errno 2] No such file or directory', p.stdout) + + class TestUnicode(BaseS3CLICommand): """ The purpose of these tests are to ensure that the commands can handle