# Test code for the unarchive module.
# (c) 2014, Richard Isaacson <richard.c.isaacson@gmail.com>

# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
# Make sure we start fresh

# Need unzip for unarchive module, and zip for archive creation.
- name: Ensure zip and unzip is present to create test archive (yum)
  yum: name=zip,unzip 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 & unzip is present to create test archive (apt)
  apt: name=zip,unzip state=latest
  when: ansible_pkg_mgr  ==  'apt'

- name: Ensure zip & unzip is present to create test archive (pkg)
  pkgng: name=zip,unzip state=present
  when: ansible_pkg_mgr  ==  'pkgng'

- name: prep our file
  copy: src=foo.txt dest={{remote_tmp_dir}}/foo-unarchive.txt

- name: prep a tar file
  shell: tar cvf test-unarchive.tar foo-unarchive.txt chdir={{remote_tmp_dir}}

- name: prep a tar.gz file
  shell: tar czvf test-unarchive.tar.gz foo-unarchive.txt chdir={{remote_tmp_dir}}

- name: prep a chmodded file for zip
  copy: src=foo.txt dest={{remote_tmp_dir}}/foo-unarchive-777.txt mode=0777

- name: prep a windows permission file for our zip
  copy: src=foo.txt dest={{remote_tmp_dir}}/FOO-UNAR.TXT

# This gets around an unzip timestamp bug in some distributions
# Recent unzip on Ubuntu and BSD will randomly round some timestamps up.
# But that doesn't seem to happen when the timestamp has an even second.
- name: Bug work around
  command: touch -t "201705111530.00" {{remote_tmp_dir}}/foo-unarchive.txt {{remote_tmp_dir}}/foo-unarchive-777.txt {{remote_tmp_dir}}/FOO-UNAR.TXT
# See Ubuntu bug 1691636: https://bugs.launchpad.net/ubuntu/+source/unzip/+bug/1691636
# When these are fixed, this code should be removed.

- name: prep a zip file
  shell: zip test-unarchive.zip foo-unarchive.txt foo-unarchive-777.txt chdir={{remote_tmp_dir}}

- name: Prepare - Create test dirs
  file:
    path: "{{remote_tmp_dir}}/{{item}}"
    state: directory
  with_items:
    - created/include
    - created/exclude
    - created/other

- name: Prepare - Create test files
  file:
    path: "{{remote_tmp_dir}}/created/{{item}}"
    state: touch
  with_items:
    - include/include-1.txt
    - include/include-2.txt
    - include/include-3.txt
    - exclude/exclude-1.txt
    - exclude/exclude-2.txt
    - exclude/exclude-3.txt
    - other/include-1.ext
    - other/include-2.ext
    - other/exclude-1.ext
    - other/exclude-2.ext
    - other/other-1.ext
    - other/other-2.ext

- name: Prepare - zip file
  shell: zip -r {{remote_tmp_dir}}/unarchive-00.zip * chdir={{remote_tmp_dir}}/created/

- name: Prepare - tar file
  shell: tar czvf {{remote_tmp_dir}}/unarchive-00.tar * chdir={{remote_tmp_dir}}/created/

- name: add a file with Windows permissions to zip file
  shell: zip -k test-unarchive.zip FOO-UNAR.TXT chdir={{remote_tmp_dir}}

- name: prep a subdirectory
  file: path={{remote_tmp_dir}}/unarchive-dir state=directory

- name: prep our file
  copy: src=foo.txt dest={{remote_tmp_dir}}/unarchive-dir/foo-unarchive.txt

- name: prep a tar.gz file with directory
  shell: tar czvf test-unarchive-dir.tar.gz unarchive-dir  chdir={{remote_tmp_dir}}

- name: create our tar unarchive destination
  file: path={{remote_tmp_dir}}/test-unarchive-tar state=directory

- name: unarchive a tar file
  unarchive: src={{remote_tmp_dir}}/test-unarchive.tar dest="{{remote_tmp_dir}}/test-unarchive-tar" remote_src=yes
  register: unarchive01

- name: verify that the file was marked as changed
  assert:
    that:
      - "unarchive01.changed == true"

- name: verify that the file was unarchived
  file: path={{remote_tmp_dir}}/test-unarchive-tar/foo-unarchive.txt state=file

- name: remove our tar unarchive destination
  file: path={{remote_tmp_dir}}/test-unarchive-tar state=absent

- name: create our tar.gz unarchive destination
  file: path={{remote_tmp_dir}}/test-unarchive-tar-gz state=directory

- name: unarchive a tar.gz file
  unarchive: src={{remote_tmp_dir}}/test-unarchive.tar.gz dest={{remote_tmp_dir}}/test-unarchive-tar-gz remote_src=yes
  register: unarchive02

- name: verify that the file was marked as changed
  assert:
    that:
      - "unarchive02.changed == true"
      # Verify that no file list is generated
      - "'files' not in unarchive02"

- name: verify that the file was unarchived
  file: path={{remote_tmp_dir}}/test-unarchive-tar-gz/foo-unarchive.txt state=file

- name: remove our tar.gz unarchive destination
  file: path={{remote_tmp_dir}}/test-unarchive-tar-gz state=absent

- name: create our tar.gz unarchive destination for creates
  file: path={{remote_tmp_dir}}/test-unarchive-tar-gz state=directory

- name: unarchive a tar.gz file with creates set
  unarchive: src={{remote_tmp_dir}}/test-unarchive.tar.gz dest={{remote_tmp_dir}}/test-unarchive-tar-gz remote_src=yes creates={{remote_tmp_dir}}/test-unarchive-tar-gz/foo-unarchive.txt
  register: unarchive02b

- name: verify that the file was marked as changed
  assert:
    that:
      - "unarchive02b.changed == true"

- name: verify that the file was unarchived
  file: path={{remote_tmp_dir}}/test-unarchive-tar-gz/foo-unarchive.txt state=file

- name: unarchive a tar.gz file with creates over an existing file
  unarchive: src={{remote_tmp_dir}}/test-unarchive.tar.gz dest={{remote_tmp_dir}}/test-unarchive-tar-gz remote_src=yes creates={{remote_tmp_dir}}/test-unarchive-tar-gz/foo-unarchive.txt
  register: unarchive02c

- name: verify that the file was not marked as changed
  assert:
    that:
      - "unarchive02c.changed == false"

- name: unarchive a tar.gz file with creates over an existing file using complex_args
  unarchive:
    src: "{{remote_tmp_dir}}/test-unarchive.tar.gz"
    dest: "{{remote_tmp_dir}}/test-unarchive-tar-gz"
    remote_src: yes
    creates: "{{remote_tmp_dir}}/test-unarchive-tar-gz/foo-unarchive.txt"
  register: unarchive02d

- name: verify that the file was not marked as changed
  assert:
    that:
      - "unarchive02d.changed == false"

- name: remove our tar.gz unarchive destination
  file: path={{remote_tmp_dir}}/test-unarchive-tar-gz state=absent

- name: create our zip unarchive destination
  file: path={{remote_tmp_dir}}/test-unarchive-zip state=directory

- name: unarchive a zip file
  unarchive: src={{remote_tmp_dir}}/test-unarchive.zip dest={{remote_tmp_dir}}/test-unarchive-zip remote_src=yes list_files=True
  register: unarchive03

- name: verify that the file was marked as changed
  assert:
    that:
      - "unarchive03.changed == true"
      # Verify that file list is generated
      - "'files' in unarchive03"
      - "{{unarchive03['files']| length}} == 3"
      - "'foo-unarchive.txt' in unarchive03['files']"
      - "'foo-unarchive-777.txt' in unarchive03['files']"
      - "'FOO-UNAR.TXT' in unarchive03['files']"

- name: verify that the file was unarchived
  file: path={{remote_tmp_dir}}/test-unarchive-zip/{{item}} state=file
  with_items:
    - foo-unarchive.txt
    - foo-unarchive-777.txt
    - FOO-UNAR.TXT

- name: repeat the last request to verify no changes
  unarchive: src={{remote_tmp_dir}}/test-unarchive.zip dest={{remote_tmp_dir}}/test-unarchive-zip remote_src=yes list_files=True
  register: unarchive03b

- name: verify that the task was not marked as changed
  assert:
    that:
      - "unarchive03b.changed == false"

- name: "Create {{ remote_tmp_dir }}/exclude directory"
  file:
    state: directory
    path: "{{ remote_tmp_dir }}/exclude-{{item}}"
  with_items:
    - zip
    - tar

- name: Unpack archive file excluding regular and glob files.
  unarchive:
    src: "{{ remote_tmp_dir }}/unarchive-00.{{item}}"
    dest: "{{ remote_tmp_dir }}/exclude-{{item}}"
    remote_src: yes
    exclude: 
      - "exclude/exclude-*.txt"
      - "other/exclude-1.ext"
  with_items:
    - zip
    - tar

- name: verify that the file was unarchived
  shell: find {{ remote_tmp_dir }}/exclude-{{item}} chdir={{ remote_tmp_dir }}
  register: unarchive00
  with_items:
    - zip
    - tar

- name: verify that archive extraction excluded the files
  assert:
    that:
      - "'exclude/exclude-1.txt' not in item.stdout"
      - "'other/exclude-1.ext' not in item.stdout"
  with_items:
    - "{{ unarchive00.results }}"

- name: remove our zip unarchive destination
  file: path={{remote_tmp_dir}}/test-unarchive-zip state=absent

- name: remove our test files for the archive
  file: path={{remote_tmp_dir}}/{{item}} state=absent
  with_items:
    - foo-unarchive.txt
    - foo-unarchive-777.txt
    - FOO-UNAR.TXT

- name: check if /tmp/foo-unarchive.text exists
  stat: path=/tmp/foo-unarchive.txt
  ignore_errors: True
  register: unarchive04

- name: fail if the proposed destination file exists for safey
  fail: msg="/tmp/foo-unarchive.txt already exists, aborting"
  when: unarchive04.stat.exists

- name: try unarchiving to /tmp
  unarchive: src={{remote_tmp_dir}}/test-unarchive.tar.gz dest=/tmp remote_src=yes
  register: unarchive05

- name: verify that the file was marked as changed
  assert:
    that:
      - "unarchive05.changed == true"

- name: verify that the file was unarchived
  file: path=/tmp/foo-unarchive.txt state=file

- name: remove our unarchive destination
  file: path=/tmp/foo-unarchive.txt state=absent

- name: create our unarchive destination
  file: path={{remote_tmp_dir}}/test-unarchive-tar-gz state=directory

- name: unarchive and set mode to 0600, directories 0700
  unarchive:
    src: "{{ remote_tmp_dir }}/test-unarchive.tar.gz"
    dest: "{{ remote_tmp_dir }}/test-unarchive-tar-gz"
    remote_src: yes
    mode: "u+rwX,g-rwx,o-rwx"
    list_files: True
  register: unarchive06

- name: Test that the file modes were changed
  stat:
    path: "{{ remote_tmp_dir }}/test-unarchive-tar-gz/foo-unarchive.txt"
  register: unarchive06_stat

- name: Test that the file modes were changed
  assert:
    that:
      - "unarchive06.changed == true"
      - "unarchive06_stat.stat.mode == '0600'"
      # Verify that file list is generated
      - "'files' in unarchive06"
      - "{{unarchive06['files']| length}} == 1"
      - "'foo-unarchive.txt' in unarchive06['files']"

- name: remove our tar.gz unarchive destination
  file: path={{ remote_tmp_dir }}/test-unarchive-tar-gz state=absent

- name: create our unarchive destination
  file: path={{remote_tmp_dir}}/test-unarchive-tar-gz state=directory

- name: unarchive over existing extraction and set mode to 0644
  unarchive:
    src: "{{ remote_tmp_dir }}/test-unarchive.tar.gz"
    dest: "{{ remote_tmp_dir }}/test-unarchive-tar-gz"
    remote_src: yes
    mode: "u+rwX,g-wx,o-wx,g+r,o+r"
  register: unarchive06_2

- name: Test that the file modes were changed
  stat:
    path: "{{ remote_tmp_dir }}/test-unarchive-tar-gz/foo-unarchive.txt"
  register: unarchive06_2_stat

- debug: var=unarchive06_2_stat.stat.mode
- name: Test that the files were changed
  assert:
    that:
      - "unarchive06_2.changed == true"
      - "unarchive06_2_stat.stat.mode == '0644'"

- name: Repeat the last request to verify no changes
  unarchive:
    src: "{{ remote_tmp_dir }}/test-unarchive.tar.gz"
    dest: "{{ remote_tmp_dir }}/test-unarchive-tar-gz"
    remote_src: yes
    mode: "u+rwX-x,g-wx,o-wx,g+r,o+r"
    list_files: True
  register: unarchive07

- name: Test that the files were not changed
  assert:
    that:
      - "unarchive07.changed == false"
      # Verify that file list is generated
      - "'files' in unarchive07"
      - "{{unarchive07['files']| length}} == 1"
      - "'foo-unarchive.txt' in unarchive07['files']"

- name: remove our tar.gz unarchive destination
  file: path={{ remote_tmp_dir }}/test-unarchive-tar-gz state=absent

- name: create our unarchive destination
  file: path={{remote_tmp_dir}}/test-unarchive-zip state=directory

- name: unarchive and set mode to 0601, directories 0700
  unarchive:
    src: "{{ remote_tmp_dir }}/test-unarchive.zip"
    dest: "{{ remote_tmp_dir }}/test-unarchive-zip"
    remote_src: yes
    mode: "u+rwX-x,g-rwx,o=x"
    list_files: True
  register: unarchive08

- name: Test that the file modes were changed
  stat:
    path: "{{ remote_tmp_dir }}/test-unarchive-zip/foo-unarchive.txt"
  register: unarchive08_stat

- name: Test that the file modes were changed
  assert:
    that:
      - "unarchive08.changed == true"
      - "unarchive08_stat.stat.mode == '0601'"
      # Verify that file list is generated
      - "'files' in unarchive08"
      - "{{unarchive08['files']| length}} == 3"
      - "'foo-unarchive.txt' in unarchive08['files']"
      - "'foo-unarchive-777.txt' in unarchive08['files']"
      - "'FOO-UNAR.TXT' in unarchive08['files']"

- name: unarchive zipfile a second time and set mode to 0601, directories 0700
  unarchive:
    src: "{{ remote_tmp_dir }}/test-unarchive.zip"
    dest: "{{ remote_tmp_dir }}/test-unarchive-zip"
    remote_src: yes
    mode: "u+rwX-x,g-rwx,o=x"
    list_files: True
  register: unarchive08

- name: Test that the file modes were not changed
  stat:
    path: "{{ remote_tmp_dir }}/test-unarchive-zip/foo-unarchive.txt"
  register: unarchive08_stat

- debug:
    var: unarchive08

- debug:
    var: unarchive08_stat

- name: Test that the files did not change
  assert:
    that:
      - "unarchive08.changed == false"
      - "unarchive08_stat.stat.mode == '0601'"
      # Verify that file list is generated
      - "'files' in unarchive08"
      - "{{unarchive08['files']| length}} == 3"
      - "'foo-unarchive.txt' in unarchive08['files']"
      - "'foo-unarchive-777.txt' in unarchive08['files']"
      - "'FOO-UNAR.TXT' in unarchive08['files']"

- name: remove our zip unarchive destination
  file: path={{ remote_tmp_dir }}/test-unarchive-zip state=absent

- name: create our unarchive destination
  file: path={{remote_tmp_dir}}/test-unarchive-tar-gz state=directory

- name: create a directory with quotable chars
  file: path="{{ remote_tmp_dir }}/test-quotes~root" state=directory

- name: unarchive into directory with quotable chars
  unarchive:
    src: "{{ remote_tmp_dir }}/test-unarchive.tar.gz"
    dest: "{{ remote_tmp_dir }}/test-quotes~root"
    remote_src: yes
  register: unarchive08

- name: Test that unarchive succeeded
  assert:
    that:
      - "unarchive08.changed == true"

- name: unarchive into directory with quotable chars a second time
  unarchive:
    src: "{{ remote_tmp_dir }}/test-unarchive.tar.gz"
    dest: "{{ remote_tmp_dir }}/test-quotes~root"
    remote_src: yes
  register: unarchive09

- name: Test that unarchive did nothing
  assert:
    that:
      - "unarchive09.changed == false"

- name: remove quotable chars test
  file: path="{{ remote_tmp_dir }}/test-quotes~root" state=absent

- name: create our unarchive destination
  file:
    path: "{{ remote_tmp_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: "{{ remote_tmp_dir }}/test-unarchive-nonascii-くらとみ-tar-gz"
    mode: "u+rwX,go+rX"
    remote_src: no
  register: nonascii_result0

- name: Check that file is really there
  stat:
    path: "{{ remote_tmp_dir }}/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="{{ remote_tmp_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
  file: path={{remote_tmp_dir}}/test-unarchive-tar-gz state=directory

- name: unarchive a tar that has directories
  unarchive:
    src: "{{ remote_tmp_dir }}/test-unarchive-dir.tar.gz"
    dest: "{{ remote_tmp_dir }}/test-unarchive-tar-gz"
    mode: "0700"
    remote_src: yes
  register: unarchive10

- name: Test that unarchive succeeded
  assert:
    that:
      - "unarchive10.changed == true"

- name: Change the mode of the toplevel dir
  file:
    path: "{{ remote_tmp_dir }}/test-unarchive-tar-gz/unarchive-dir"
    mode: 0701

- name: Remove a file from the extraction point
  file:
    path: "{{ remote_tmp_dir }}/test-unarchive-tar-gz/unarchive-dir/foo-unarchive.txt"
    state: absent

- name: unarchive a tar that has directories
  unarchive:
    src: "{{ remote_tmp_dir }}/test-unarchive-dir.tar.gz"
    dest: "{{ remote_tmp_dir }}/test-unarchive-tar-gz"
    mode: "0700"
    remote_src: yes
  register: unarchive10_1

- name: Test that unarchive succeeded
  assert:
    that:
      - "unarchive10_1.changed == true"

- name: remove our tar.gz unarchive destination
  file: path={{ remote_tmp_dir }}/test-unarchive-tar-gz state=absent

#
# Symlink tests
#

- name: Create a destination dir
  file:
    path: "{{ remote_tmp_dir }}/test-unarchive-tar-gz"
    state: directory

- name: Create a symlink to the detination dir
  file:
    path: "{{ remote_tmp_dir }}/link-to-unarchive-dir"
    src: "{{ remote_tmp_dir }}/test-unarchive-tar-gz"
    state: "link"

- name: test that unarchive works when dest is a symlink to a dir
  unarchive:
    src: "{{ remote_tmp_dir }}/test-unarchive.tar.gz"
    dest: "{{ remote_tmp_dir }}/link-to-unarchive-dir"
    mode: "u+rwX,go+rX"
    remote_src: yes
  register: unarchive_11

- name: Check that file is really there
  stat:
    path: "{{ remote_tmp_dir }}/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={{ remote_tmp_dir }}/test-unarchive-tar-gz state=absent

- name: Create a file
  file:
    path: "{{ remote_tmp_dir }}/test-unarchive-tar-gz"
    state: touch

- name: Create a symlink to the file
  file:
    path: "{{ remote_tmp_dir }}/link-to-unarchive-file"
    src: "{{ remote_tmp_dir }}/test-unarchive-tar-gz"
    state: "link"

- name: test that unarchive fails when dest is a link to a file
  unarchive:
    src: "{{ remote_tmp_dir }}/test-unarchive.tar.gz"
    dest: "{{ remote_tmp_dir }}/link-to-unarchive-file"
    mode: "u+rwX,go+rX"
    remote_src: yes
  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={{ remote_tmp_dir }}/test-unarchive-tar-gz state=absent

# Test downloading a file before unarchiving it
- name: create our unarchive destination
  file: path={{remote_tmp_dir}}/test-unarchive-tar-gz state=directory

- name: unarchive a tar from an URL
  unarchive:
    src: "https://releases.ansible.com/ansible/ansible-latest.tar.gz"
    dest: "{{ remote_tmp_dir }}/test-unarchive-tar-gz"
    mode: "0700"
    remote_src: yes
  register: unarchive13

- name: Test that unarchive succeeded
  assert:
    that:
      - "unarchive13.changed == true"

- name: remove our tar.gz unarchive destination
  file: path={{ remote_tmp_dir }}/test-unarchive-tar-gz state=absent