From c03555b650250d46f075889dd2b3890ccb9e0586 Mon Sep 17 00:00:00 2001 From: Toshio Kuratomi Date: Thu, 26 May 2016 14:47:11 -0700 Subject: [PATCH] Fix unarchive failures when the destination is a symlink to a directory (#15999) Also add integration tests for this problem and unicode filenames inside a tarball. Fixes #3725 --- lib/ansible/plugins/action/__init__.py | 4 +- lib/ansible/plugins/action/unarchive.py | 2 +- .../test-unarchive-nonascii-àâæçéèïîôœ.tar.gz | Bin 0 -> 4947 bytes .../roles/test_unarchive/tasks/main.yml | 101 +++++++++++++++++- 4 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 test/integration/roles/test_unarchive/files/test-unarchive-nonascii-àâæçéèïîôœ.tar.gz diff --git a/lib/ansible/plugins/action/__init__.py b/lib/ansible/plugins/action/__init__.py index f35d586eb9..210607f39a 100644 --- a/lib/ansible/plugins/action/__init__.py +++ b/lib/ansible/plugins/action/__init__.py @@ -428,7 +428,7 @@ class ActionBase(with_metaclass(ABCMeta, object)): return mystat['stat'] - def _remote_checksum(self, path, all_vars): + def _remote_checksum(self, path, all_vars, follow=False): ''' Produces a remote checksum given a path, Returns a number 0-4 for specific errors instead of checksum, also ensures it is different @@ -440,7 +440,7 @@ class ActionBase(with_metaclass(ABCMeta, object)): ''' x = "0" # unknown error has occured try: - remote_stat = self._execute_remote_stat(path, all_vars, follow=False) + remote_stat = self._execute_remote_stat(path, all_vars, follow=follow) if remote_stat['exists'] and remote_stat['isdir']: x = "3" # its a directory not a file else: diff --git a/lib/ansible/plugins/action/unarchive.py b/lib/ansible/plugins/action/unarchive.py index 590881165e..5b8d049985 100644 --- a/lib/ansible/plugins/action/unarchive.py +++ b/lib/ansible/plugins/action/unarchive.py @@ -71,7 +71,7 @@ class ActionModule(ActionBase): else: source = self._loader.path_dwim_relative(self._loader.get_basedir(), 'files', source) - remote_checksum = self._remote_checksum(dest, all_vars=task_vars) + remote_checksum = self._remote_checksum(dest, all_vars=task_vars, follow=True) if remote_checksum == '4': result['failed'] = True result['msg'] = "python isn't present on the system. Unable to compute checksum" diff --git a/test/integration/roles/test_unarchive/files/test-unarchive-nonascii-àâæçéèïîôœ.tar.gz b/test/integration/roles/test_unarchive/files/test-unarchive-nonascii-àâæçéèïîôœ.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..4882b9207a28c0c1a5e1d5ef682e6240d3f403ac GIT binary patch literal 4947 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 RfCK(7@oyFf=)M3b008^>&*cCB literal 0 HcmV?d00001 diff --git a/test/integration/roles/test_unarchive/tasks/main.yml b/test/integration/roles/test_unarchive/tasks/main.yml index e4f438e525..6e284c3aea 100644 --- a/test/integration/roles/test_unarchive/tasks/main.yml +++ b/test/integration/roles/test_unarchive/tasks/main.yml @@ -21,9 +21,9 @@ yum: name=zip state=latest when: ansible_pkg_mgr == 'yum' -- name: Ensure zip is present to create test archive (dnf) - dnf: name=zip state=latest - when: ansible_pkg_mgr == 'dnf' + #- name: Ensure zip is present to create test archive (dnf) + # dnf: name=zip state=latest + # when: ansible_pkg_mgr == 'dnf' - name: Ensure zip is present to create test archive (apt) apt: name=zip state=latest @@ -287,6 +287,35 @@ - name: remove quotable chars test file: path="{{ output_dir }}/test-quotes~root" state=absent +- name: create our unarchive destination + file: + path: "{{ output_dir }}/test-unarchive-nonascii-àâæçéèïîôœ-tar-gz" + state: directory + +- name: test that unarchive works with an archive that contains non-ascii filenames + unarchive: + # Both the filename of the tarball and the filename inside the tarball have + # nonascii chars + src: "test-unarchive-nonascii-àâæçéèïîôœ.tar.gz" + dest: "{{ output_dir }}/test-unarchive-nonascii-àâæçéèïîôœ-tar-gz" + mode: "u+rwX,go+rX" + copy: yes + register: nonascii_result0 + +- name: Check that file is really there + stat: + path: "{{ output_dir | expanduser }}/test-unarchive-nonascii-àâæçéèïîôœ-tar-gz/storage/àâæçéèïîôœ(copy)!@#$%^&-().jpg" + register: nonascii_stat0 + +- name: Assert that nonascii tests succeeded + assert: + that: + - "nonascii_result0.changed == true" + - "nonascii_stat0.stat.exists == true" + +- name: remove nonascii test + file: path="{{ output_dir }}/test-unarchive-nonascii-àâæçéèïîôœ-tar-gz" state=absent + # Test that unarchiving is performed if files are missing # https://github.com/ansible/ansible-modules-core/issues/1064 - name: create our unarchive destination @@ -330,3 +359,69 @@ - name: remove our tar.gz unarchive destination file: path={{ output_dir }}/test-unarchive-tar-gz state=absent + +# +# Symlink tests +# + +- name: Create a destination dir + file: + path: "{{ output_dir }}/test-unarchive-tar-gz" + state: directory + +- name: Create a symlink to the detination dir + file: + path: "{{ output_dir }}/link-to-unarchive-dir" + src: "{{ output_dir }}/test-unarchive-tar-gz" + state: "link" + +- name: test that unarchive works when dest is a symlink to a dir + unarchive: + src: "{{ output_dir }}/test-unarchive.tar.gz" + dest: "{{ output_dir | expanduser }}/link-to-unarchive-dir" + mode: "u+rwX,go+rX" + copy: no + register: unarchive_11 + +- name: Check that file is really there + stat: + path: "{{ output_dir | expanduser }}/test-unarchive-tar-gz/foo-unarchive.txt" + register: unarchive11_stat0 + +- name: Assert that unarchive when dest is a symlink to a dir worked + assert: + that: + - "unarchive_11.changed == true" + - "unarchive11_stat0.stat.exists == true" + +- name: remove our tar.gz unarchive destination + file: path={{ output_dir }}/test-unarchive-tar-gz state=absent + + +- name: Create a file + file: + path: "{{ output_dir }}/test-unarchive-tar-gz" + state: touch + +- name: Create a symlink to the file + file: + path: "{{ output_dir }}/link-to-unarchive-file" + src: "{{ output_dir }}/test-unarchive-tar-gz" + state: "link" + +- name: test that unarchive fails when dest is a link to a file + unarchive: + src: "{{ output_dir }}/test-unarchive.tar.gz" + dest: "{{ output_dir | expanduser }}/link-to-unarchive-file" + mode: "u+rwX,go+rX" + copy: no + ignore_errors: True + register: unarchive_12 + +- name: Assert that unarchive when dest is a file failed + assert: + that: + - "unarchive_12.failed == true" + +- name: remove our tar.gz unarchive destination + file: path={{ output_dir }}/test-unarchive-tar-gz state=absent