mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
archive - Adding exclusion_patterns option (#2616)
* Adding exclusion_patterns option * Adding changelog fragment and Python 2.6 compatability * Minor refactoring for readability * Removing unneccessary conditional * Applying initial review suggestions * Adding missed review suggestion
This commit is contained in:
parent
bef3c04d1c
commit
b6c0cc0b61
3 changed files with 100 additions and 20 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- archive - added ``exclusion_patterns`` option to exclude files or subdirectories from archives (https://github.com/ansible-collections/community.general/pull/2616).
|
|
@ -41,8 +41,16 @@ options:
|
||||||
exclude_path:
|
exclude_path:
|
||||||
description:
|
description:
|
||||||
- Remote absolute path, glob, or list of paths or globs for the file or files to exclude from I(path) list and glob expansion.
|
- Remote absolute path, glob, or list of paths or globs for the file or files to exclude from I(path) list and glob expansion.
|
||||||
|
- Use I(exclusion_patterns) to instead exclude files or subdirectories below any of the paths from the I(path) list.
|
||||||
type: list
|
type: list
|
||||||
elements: path
|
elements: path
|
||||||
|
exclusion_patterns:
|
||||||
|
description:
|
||||||
|
- Glob style patterns to exclude files or directories from the resulting archive.
|
||||||
|
- This differs from I(exclude_path) which applies only to the source paths from I(path).
|
||||||
|
type: list
|
||||||
|
elements: path
|
||||||
|
version_added: 3.2.0
|
||||||
force_archive:
|
force_archive:
|
||||||
description:
|
description:
|
||||||
- Allows you to force the module to treat this as an archive even if only a single file is specified.
|
- Allows you to force the module to treat this as an archive even if only a single file is specified.
|
||||||
|
@ -163,6 +171,8 @@ import re
|
||||||
import shutil
|
import shutil
|
||||||
import tarfile
|
import tarfile
|
||||||
import zipfile
|
import zipfile
|
||||||
|
from fnmatch import fnmatch
|
||||||
|
from sys import version_info
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
||||||
|
@ -186,6 +196,8 @@ else:
|
||||||
LZMA_IMP_ERR = format_exc()
|
LZMA_IMP_ERR = format_exc()
|
||||||
HAS_LZMA = False
|
HAS_LZMA = False
|
||||||
|
|
||||||
|
PY27 = version_info[0:2] >= (2, 7)
|
||||||
|
|
||||||
|
|
||||||
def to_b(s):
|
def to_b(s):
|
||||||
return to_bytes(s, errors='surrogate_or_strict')
|
return to_bytes(s, errors='surrogate_or_strict')
|
||||||
|
@ -214,6 +226,59 @@ def expand_paths(paths):
|
||||||
return expanded_path, is_globby
|
return expanded_path, is_globby
|
||||||
|
|
||||||
|
|
||||||
|
def matches_exclusion_patterns(path, exclusion_patterns):
|
||||||
|
return any(fnmatch(path, p) for p in exclusion_patterns)
|
||||||
|
|
||||||
|
|
||||||
|
def get_filter(exclusion_patterns, format):
|
||||||
|
def zip_filter(path):
|
||||||
|
return matches_exclusion_patterns(path, exclusion_patterns)
|
||||||
|
|
||||||
|
def tar_filter(tarinfo):
|
||||||
|
return None if matches_exclusion_patterns(tarinfo.name, exclusion_patterns) else tarinfo
|
||||||
|
|
||||||
|
return zip_filter if format == 'zip' or not PY27 else tar_filter
|
||||||
|
|
||||||
|
|
||||||
|
def get_archive_contains(format):
|
||||||
|
def archive_contains(archive, name):
|
||||||
|
try:
|
||||||
|
if format == 'zip':
|
||||||
|
archive.getinfo(name)
|
||||||
|
else:
|
||||||
|
archive.getmember(name)
|
||||||
|
except KeyError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
return archive_contains
|
||||||
|
|
||||||
|
|
||||||
|
def get_add_to_archive(format, filter):
|
||||||
|
def add_to_zip_archive(archive_file, path, archive_name):
|
||||||
|
try:
|
||||||
|
if not filter(path):
|
||||||
|
archive_file.write(path, archive_name)
|
||||||
|
except Exception as e:
|
||||||
|
return e
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def add_to_tar_archive(archive_file, path, archive_name):
|
||||||
|
try:
|
||||||
|
if PY27:
|
||||||
|
archive_file.add(path, archive_name, recursive=False, filter=filter)
|
||||||
|
else:
|
||||||
|
archive_file.add(path, archive_name, recursive=False, exclude=filter)
|
||||||
|
except Exception as e:
|
||||||
|
return e
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
return add_to_zip_archive if format == 'zip' else add_to_tar_archive
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
argument_spec=dict(
|
argument_spec=dict(
|
||||||
|
@ -221,6 +286,7 @@ def main():
|
||||||
format=dict(type='str', default='gz', choices=['bz2', 'gz', 'tar', 'xz', 'zip']),
|
format=dict(type='str', default='gz', choices=['bz2', 'gz', 'tar', 'xz', 'zip']),
|
||||||
dest=dict(type='path'),
|
dest=dict(type='path'),
|
||||||
exclude_path=dict(type='list', elements='path'),
|
exclude_path=dict(type='list', elements='path'),
|
||||||
|
exclusion_patterns=dict(type='list', elements='path'),
|
||||||
force_archive=dict(type='bool', default=False),
|
force_archive=dict(type='bool', default=False),
|
||||||
remove=dict(type='bool', default=False),
|
remove=dict(type='bool', default=False),
|
||||||
),
|
),
|
||||||
|
@ -242,6 +308,8 @@ def main():
|
||||||
changed = False
|
changed = False
|
||||||
state = 'absent'
|
state = 'absent'
|
||||||
|
|
||||||
|
exclusion_patterns = params['exclusion_patterns'] or []
|
||||||
|
|
||||||
# Simple or archive file compression (inapplicable with 'zip' since it's always an archive)
|
# Simple or archive file compression (inapplicable with 'zip' since it's always an archive)
|
||||||
b_successes = []
|
b_successes = []
|
||||||
|
|
||||||
|
@ -262,6 +330,10 @@ def main():
|
||||||
# Only attempt to expand the exclude paths if it exists
|
# Only attempt to expand the exclude paths if it exists
|
||||||
b_expanded_exclude_paths = expand_paths(exclude_paths)[0] if exclude_paths else []
|
b_expanded_exclude_paths = expand_paths(exclude_paths)[0] if exclude_paths else []
|
||||||
|
|
||||||
|
filter = get_filter(exclusion_patterns, fmt)
|
||||||
|
archive_contains = get_archive_contains(fmt)
|
||||||
|
add_to_archive = get_add_to_archive(fmt, filter)
|
||||||
|
|
||||||
# Only try to determine if we are working with an archive or not if we haven't set archive to true
|
# Only try to determine if we are working with an archive or not if we haven't set archive to true
|
||||||
if not force_archive:
|
if not force_archive:
|
||||||
# If we actually matched multiple files or TRIED to, then
|
# If we actually matched multiple files or TRIED to, then
|
||||||
|
@ -384,38 +456,31 @@ def main():
|
||||||
n_fullpath = to_na(b_fullpath)
|
n_fullpath = to_na(b_fullpath)
|
||||||
n_arcname = to_native(b_match_root.sub(b'', b_fullpath), errors='surrogate_or_strict')
|
n_arcname = to_native(b_match_root.sub(b'', b_fullpath), errors='surrogate_or_strict')
|
||||||
|
|
||||||
try:
|
err = add_to_archive(arcfile, n_fullpath, n_arcname)
|
||||||
if fmt == 'zip':
|
if err:
|
||||||
arcfile.write(n_fullpath, n_arcname)
|
errors.append('%s: %s' % (n_fullpath, to_native(err)))
|
||||||
else:
|
|
||||||
arcfile.add(n_fullpath, n_arcname, recursive=False)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
errors.append('%s: %s' % (n_fullpath, to_native(e)))
|
|
||||||
|
|
||||||
for b_filename in b_filenames:
|
for b_filename in b_filenames:
|
||||||
b_fullpath = b_dirpath + b_filename
|
b_fullpath = b_dirpath + b_filename
|
||||||
n_fullpath = to_na(b_fullpath)
|
n_fullpath = to_na(b_fullpath)
|
||||||
n_arcname = to_n(b_match_root.sub(b'', b_fullpath))
|
n_arcname = to_n(b_match_root.sub(b'', b_fullpath))
|
||||||
|
|
||||||
try:
|
err = add_to_archive(arcfile, n_fullpath, n_arcname)
|
||||||
if fmt == 'zip':
|
if err:
|
||||||
arcfile.write(n_fullpath, n_arcname)
|
errors.append('Adding %s: %s' % (to_native(b_path), to_native(err)))
|
||||||
else:
|
|
||||||
arcfile.add(n_fullpath, n_arcname, recursive=False)
|
|
||||||
|
|
||||||
|
if archive_contains(arcfile, n_arcname):
|
||||||
b_successes.append(b_fullpath)
|
b_successes.append(b_fullpath)
|
||||||
except Exception as e:
|
|
||||||
errors.append('Adding %s: %s' % (to_native(b_path), to_native(e)))
|
|
||||||
else:
|
else:
|
||||||
path = to_na(b_path)
|
path = to_na(b_path)
|
||||||
arcname = to_n(b_match_root.sub(b'', b_path))
|
arcname = to_n(b_match_root.sub(b'', b_path))
|
||||||
if fmt == 'zip':
|
|
||||||
arcfile.write(path, arcname)
|
|
||||||
else:
|
|
||||||
arcfile.add(path, arcname, recursive=False)
|
|
||||||
|
|
||||||
b_successes.append(b_path)
|
err = add_to_archive(arcfile, path, arcname)
|
||||||
|
if err:
|
||||||
|
errors.append('Adding %s: %s' % (to_native(b_path), to_native(err)))
|
||||||
|
|
||||||
|
if archive_contains(arcfile, arcname):
|
||||||
|
b_successes.append(b_path)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
expanded_fmt = 'zip' if fmt == 'zip' else ('tar.' + fmt)
|
expanded_fmt = 'zip' if fmt == 'zip' else ('tar.' + fmt)
|
||||||
|
|
|
@ -363,6 +363,19 @@
|
||||||
- name: remove nonascii test
|
- name: remove nonascii test
|
||||||
file: path="{{ output_dir }}/test-archive-nonascii-くらとみ.zip" state=absent
|
file: path="{{ output_dir }}/test-archive-nonascii-くらとみ.zip" state=absent
|
||||||
|
|
||||||
|
- name: Test exclusion_patterns option
|
||||||
|
archive:
|
||||||
|
path: "{{ output_dir }}/*.txt"
|
||||||
|
dest: "{{ output_dir }}/test-archive-exclustion-patterns.tgz"
|
||||||
|
exclusion_patterns: b?r.*
|
||||||
|
register: exclusion_patterns_result
|
||||||
|
|
||||||
|
- name: Assert that exclusion_patterns only archives included files
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- exclusion_patterns_result is changed
|
||||||
|
- "'bar.txt' not in exclusion_patterns_result.archived"
|
||||||
|
|
||||||
- name: Remove backports.lzma if previously installed (pip)
|
- name: Remove backports.lzma if previously installed (pip)
|
||||||
pip: name=backports.lzma state=absent
|
pip: name=backports.lzma state=absent
|
||||||
when: backports_lzma_pip is changed
|
when: backports_lzma_pip is changed
|
||||||
|
|
Loading…
Reference in a new issue