Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

unarchive: add support for .tar.zst (zstd compression) #73265

Merged
merged 2 commits into from Feb 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/unarchive-support-zst.yml
@@ -0,0 +1,2 @@
minor_changes:
- unarchive - Add support for .tar.zst (zstd compression) (https://github.com/ansible/ansible/pull/73265).
20 changes: 17 additions & 3 deletions lib/ansible/modules/unarchive.py
Expand Up @@ -110,8 +110,9 @@
- Re-implement zip support using native zipfile module.
notes:
- Requires C(zipinfo) and C(gtar)/C(unzip) command on target host.
- Can handle I(.zip) files using C(unzip) as well as I(.tar), I(.tar.gz), I(.tar.bz2) and I(.tar.xz) files using C(gtar).
- Does not handle I(.gz) files, I(.bz2) files or I(.xz) files that do not contain a I(.tar) archive.
- Requires C(zstd) command on target host to expand I(.tar.zst) files.
- Can handle I(.zip) files using C(unzip) as well as I(.tar), I(.tar.gz), I(.tar.bz2), I(.tar.xz), and I(.tar.zst) files using C(gtar).
- Does not handle I(.gz) files, I(.bz2) files, I(.xz), or I(.zst) files that do not contain a I(.tar) archive.
- Uses gtar's C(--diff) arg to calculate if changed or not. If this C(arg) is not
supported, it will always unpack the archive.
- Existing files/directories in the destination which are not in the archive
Expand Down Expand Up @@ -891,9 +892,22 @@ def __init__(self, src, b_dest, file_args, module):
self.zipflag = '-J'


# Class to handle zstd compressed tar files
class TarZstdArchive(TgzArchive):
def __init__(self, src, b_dest, file_args, module):
super(TarZstdArchive, self).__init__(src, b_dest, file_args, module)
# GNU Tar supports the --use-compress-program option to
# specify which executable to use for
# compression/decompression.
#
# Note: some flavors of BSD tar support --zstd (e.g., FreeBSD
# 12.2), but the TgzArchive class only supports GNU Tar.
self.zipflag = '--use-compress-program=zstd'


# try handlers in order and return the one that works or bail if none work
def pick_handler(src, dest, file_args, module):
handlers = [ZipArchive, TgzArchive, TarArchive, TarBzipArchive, TarXzArchive]
handlers = [ZipArchive, TgzArchive, TarArchive, TarBzipArchive, TarXzArchive, TarZstdArchive]
reasons = set()
for handler in handlers:
obj = handler(src, dest, file_args, module)
Expand Down
1 change: 1 addition & 0 deletions test/integration/targets/unarchive/tasks/main.yml
Expand Up @@ -4,6 +4,7 @@
- import_tasks: test_tar_gz_creates.yml
- import_tasks: test_tar_gz_owner_group.yml
- import_tasks: test_tar_gz_keep_newer.yml
- import_tasks: test_tar_zst.yml
- import_tasks: test_zip.yml
- import_tasks: test_exclude.yml
- import_tasks: test_include.yml
Expand Down
29 changes: 29 additions & 0 deletions test/integration/targets/unarchive/tasks/prepare_tests.yml
Expand Up @@ -6,6 +6,13 @@
- unzip
when: ansible_pkg_mgr in ('yum', 'dnf', 'apt', 'pkgng')

- name: Ensure zstd is present, if available
ignore_errors: true
package:
name:
- zstd
when: ansible_pkg_mgr in ('yum', 'dnf', 'apt', 'pkgng')

- name: prep our file
copy:
src: foo.txt
Expand All @@ -18,6 +25,28 @@
- name: prep a tar.gz file
shell: tar czvf test-unarchive.tar.gz foo-unarchive.txt chdir={{remote_tmp_dir}}

- name: see if we have the zstd executable
ignore_errors: true
shell: zstd --version
register: zstd_available

- when: zstd_available.rc == 0
block:
- name: find gnu tar
shell: |
#!/bin/sh
which gtar 2>/dev/null
if test $? -ne 0; then
if test -z "`tar --version | grep bsdtar`"; then
which tar
fi
fi
register: gnu_tar

- name: prep a tar.zst file
shell: "{{ gnu_tar.stdout }} --use-compress-program=zstd -cvf test-unarchive.tar.zst foo-unarchive.txt chdir={{remote_tmp_dir}}"
when: gnu_tar.stdout != ""

- name: prep a chmodded file for zip
copy:
src: foo.txt
Expand Down
40 changes: 40 additions & 0 deletions test/integration/targets/unarchive/tasks/test_tar_zst.yml
@@ -0,0 +1,40 @@
# Only do this whole file when the "zstd" executable is present
- when:
- zstd_available.rc == 0
- gnu_tar.stdout != ""
block:
- name: create our tar.zst unarchive destination
file:
path: '{{remote_tmp_dir}}/test-unarchive-tar-zst'
state: directory

- name: unarchive a tar.zst file
unarchive:
src: '{{remote_tmp_dir}}/test-unarchive.tar.zst'
dest: '{{remote_tmp_dir}}/test-unarchive-tar-zst'
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-zst/foo-unarchive.txt'
state: file

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

- name: test owner/group perms
include_tasks: test_owner_group.yml
vars:
ext: tar.zst
archive: test-unarchive.tar.zst
testfile: foo-unarchive.txt