unarchive - fall back to unzip -Z if zipinfo is not available (#76971)

Add a new handler class ZipZArchive to use unzip -Z as an alternative to zipinfo

Run 'unzip -Z' in can_handle_archive so we fall back to the next handler if it's not available (failing in is_unarchived is too late)

* Add a test for unzip -Z when zipinfo is not available

* Update test for missing binary altogether by removing /usr/bin from the PATH
pull/77993/head
Sloane Hertel 2 years ago committed by GitHub
parent a43112290a
commit 9d6cc7b576
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,2 @@
bugfixes:
- unarchive - if unzip is available but zipinfo is not, use unzip -Z instead of zipinfo (https://github.com/ansible/ansible/issues/76959).

@ -312,6 +312,11 @@ class ZipArchive(object):
self.zipinfo_cmd_path = None
self._files_in_archive = []
self._infodict = dict()
self.zipinfoflag = ''
self.binaries = (
('unzip', 'cmd_path'),
('zipinfo', 'zipinfo_cmd_path'),
)
def _permstr_to_octal(self, modestr, umask):
''' Convert a Unix permission string (rw-r--r--) into a mode (0644) '''
@ -399,7 +404,10 @@ class ZipArchive(object):
def is_unarchived(self):
# BSD unzip doesn't support zipinfo listings with timestamp.
cmd = [self.zipinfo_cmd_path, '-T', '-s', self.src]
if self.zipinfoflag:
cmd = [self.zipinfo_cmd_path, self.zipinfoflag, '-T', '-s', self.src]
else:
cmd = [self.zipinfo_cmd_path, '-T', '-s', self.src]
if self.excludes:
cmd.extend(['-x', ] + self.excludes)
@ -720,12 +728,8 @@ class ZipArchive(object):
return dict(cmd=cmd, rc=rc, out=out, err=err)
def can_handle_archive(self):
binaries = (
('unzip', 'cmd_path'),
('zipinfo', 'zipinfo_cmd_path'),
)
missing = []
for b in binaries:
for b in self.binaries:
try:
setattr(self, b[1], get_bin_path(b[0]))
except ValueError:
@ -948,9 +952,32 @@ class TarZstdArchive(TgzArchive):
self.zipflag = '--use-compress-program=zstd'
class ZipZArchive(ZipArchive):
def __init__(self, src, b_dest, file_args, module):
super(ZipZArchive, self).__init__(src, b_dest, file_args, module)
self.zipinfoflag = '-Z'
self.binaries = (
('unzip', 'cmd_path'),
('unzip', 'zipinfo_cmd_path'),
)
def can_handle_archive(self):
unzip_available, error_msg = super(ZipZArchive, self).can_handle_archive()
if not unzip_available:
return unzip_available, error_msg
# Ensure unzip -Z is available before we use it in is_unarchive
cmd = [self.zipinfo_cmd_path, self.zipinfoflag]
rc, out, err = self.module.run_command(cmd)
if 'zipinfo' in out.lower():
return True, None
return False, 'Command "unzip -Z" could not handle archive: %s' % err
# 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, TarZstdArchive]
handlers = [ZipArchive, ZipZArchive, TgzArchive, TarArchive, TarBzipArchive, TarXzArchive, TarZstdArchive]
reasons = set()
for handler in handlers:
obj = handler(src, dest, file_args, module)

@ -33,6 +33,14 @@
remote_src: yes
register: zip_fail
ignore_errors: yes
# FreeBSD does not have zipinfo, but does have a bootstrapped unzip in /usr/bin
# which alone is sufficient to run unarchive.
# Exclude /usr/bin from the PATH to test having no binary available.
environment:
PATH: "{{ ENV_PATH }}"
vars:
ENV_PATH: "{{ lookup('env', 'PATH') | regex_replace(re, '') }}"
re: "[^A-Za-z](\/usr\/bin:?)"
- name: Ensure tasks worked as expected
assert:
@ -41,6 +49,29 @@
- zip_fail is failed
- zip_fail.msg is search('Unable to find required')
- name: unarchive a zip file using unzip without zipinfo
unarchive:
src: '{{remote_tmp_dir}}/test-unarchive.zip'
dest: '{{remote_tmp_dir}}/test-unarchive-zip'
list_files: True
remote_src: yes
register: zip_success
# FreeBSD does not have zipinfo, but does have a bootstrapped unzip in /usr/bin
# which alone is sufficient to run unarchive.
when: ansible_pkg_mgr == 'pkgng'
- assert:
that:
- zip_success is success
- zip_success.changed
# Verify that file list is generated
- "'files' in zip_success"
- "{{zip_success['files']| length}} == 3"
- "'foo-unarchive.txt' in zip_success['files']"
- "'foo-unarchive-777.txt' in zip_success['files']"
- "'FOO-UNAR.TXT' in zip_success['files']"
when: ansible_pkg_mgr == 'pkgng'
- name: Remove unarchive destinations
file:
path: '{{ remote_tmp_dir }}/test-unarchive-{{ item }}'

Loading…
Cancel
Save