From d7b7ed6ba6dd2919c05e7b2c0dcab913ff028e02 Mon Sep 17 00:00:00 2001 From: Matt Clay Date: Fri, 6 Mar 2020 12:18:35 -0800 Subject: [PATCH] Remove assemble from incidental tests. The module and action plugin are now remaining in base. --- .../targets/incidental_assemble/aliases | 1 - .../incidental_assemble/files/fragment1 | 1 - .../incidental_assemble/files/fragment2 | 1 - .../incidental_assemble/files/fragment3 | 1 - .../incidental_assemble/files/fragment4 | 1 - .../incidental_assemble/files/fragment5 | 1 - .../incidental_assemble/tasks/main.yml | 101 ------- .../integration/plugins/action/assemble.py | 165 ----------- .../integration/plugins/modules/assemble.py | 261 ------------------ 9 files changed, 533 deletions(-) delete mode 100644 test/integration/targets/incidental_assemble/aliases delete mode 100644 test/integration/targets/incidental_assemble/files/fragment1 delete mode 100644 test/integration/targets/incidental_assemble/files/fragment2 delete mode 100644 test/integration/targets/incidental_assemble/files/fragment3 delete mode 100644 test/integration/targets/incidental_assemble/files/fragment4 delete mode 100644 test/integration/targets/incidental_assemble/files/fragment5 delete mode 100644 test/integration/targets/incidental_assemble/tasks/main.yml delete mode 100644 test/support/integration/plugins/action/assemble.py delete mode 100644 test/support/integration/plugins/modules/assemble.py diff --git a/test/integration/targets/incidental_assemble/aliases b/test/integration/targets/incidental_assemble/aliases deleted file mode 100644 index 31c6a8b454f..00000000000 --- a/test/integration/targets/incidental_assemble/aliases +++ /dev/null @@ -1 +0,0 @@ -shippable/posix/incidental diff --git a/test/integration/targets/incidental_assemble/files/fragment1 b/test/integration/targets/incidental_assemble/files/fragment1 deleted file mode 100644 index a00d3ea04ab..00000000000 --- a/test/integration/targets/incidental_assemble/files/fragment1 +++ /dev/null @@ -1 +0,0 @@ -this is fragment 1 diff --git a/test/integration/targets/incidental_assemble/files/fragment2 b/test/integration/targets/incidental_assemble/files/fragment2 deleted file mode 100644 index 860f760388e..00000000000 --- a/test/integration/targets/incidental_assemble/files/fragment2 +++ /dev/null @@ -1 +0,0 @@ -this is fragment 2 diff --git a/test/integration/targets/incidental_assemble/files/fragment3 b/test/integration/targets/incidental_assemble/files/fragment3 deleted file mode 100644 index df95b24bb6a..00000000000 --- a/test/integration/targets/incidental_assemble/files/fragment3 +++ /dev/null @@ -1 +0,0 @@ -this is fragment 3 diff --git a/test/integration/targets/incidental_assemble/files/fragment4 b/test/integration/targets/incidental_assemble/files/fragment4 deleted file mode 100644 index c83252bb8ee..00000000000 --- a/test/integration/targets/incidental_assemble/files/fragment4 +++ /dev/null @@ -1 +0,0 @@ -this is fragment 4 diff --git a/test/integration/targets/incidental_assemble/files/fragment5 b/test/integration/targets/incidental_assemble/files/fragment5 deleted file mode 100644 index 8a527d15f75..00000000000 --- a/test/integration/targets/incidental_assemble/files/fragment5 +++ /dev/null @@ -1 +0,0 @@ -this is fragment 5 diff --git a/test/integration/targets/incidental_assemble/tasks/main.yml b/test/integration/targets/incidental_assemble/tasks/main.yml deleted file mode 100644 index 72f0c111d78..00000000000 --- a/test/integration/targets/incidental_assemble/tasks/main.yml +++ /dev/null @@ -1,101 +0,0 @@ -# test code for the assemble module -# (c) 2014, James Cammarata - -# 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 . - -- name: create a new directory for file source - file: dest="{{output_dir}}/src" state=directory - register: result - -- name: assert the directory was created - assert: - that: - - "result.state == 'directory'" - -- name: copy the files to a new directory - copy: src="./" dest="{{output_dir}}/src" - register: result - -- name: create unicode file for test - shell: echo "π" > {{ output_dir }}/src/ßΩ.txt - register: result - -- name: assert that the new file was created - assert: - that: - - "result.changed == true" - -- name: test assemble with all fragments - assemble: src="{{output_dir}}/src" dest="{{output_dir}}/assembled1" - register: result - -- name: assert the fragments were assembled - assert: - that: - - "result.state == 'file'" - - "result.changed == True" - - "result.checksum == '74152e9224f774191bc0bedf460d35de86ad90e6'" - -- name: test assemble with all fragments - assemble: src="{{output_dir}}/src" dest="{{output_dir}}/assembled1" - register: result - -- name: assert that the same assemble made no changes - assert: - that: - - "result.state == 'file'" - - "result.changed == False" - - "result.checksum == '74152e9224f774191bc0bedf460d35de86ad90e6'" - -- name: test assemble with fragments matching a regex - assemble: src="{{output_dir}}/src" dest="{{output_dir}}/assembled2" regexp="^fragment[1-3]$" - register: result - -- name: assert the fragments were assembled with a regex - assert: - that: - - "result.state == 'file'" - - "result.checksum == 'edfe2d7487ef8f5ebc0f1c4dc57ba7b70a7b8e2b'" - -- name: test assemble with a delimiter - assemble: src="{{output_dir}}/src" dest="{{output_dir}}/assembled3" delimiter="#--- delimiter ---#" - register: result - -- name: assert the fragments were assembled with a delimiter - assert: - that: - - "result.state == 'file'" - - "result.checksum == 'd986cefb82e34e4cf14d33a3cda132ff45aa2980'" - -- name: test assemble with remote_src=False - assemble: src="./" dest="{{output_dir}}/assembled4" remote_src=no - register: result - -- name: assert the fragments were assembled without remote - assert: - that: - - "result.state == 'file'" - - "result.checksum == '048a1bd1951aa5ccc427eeb4ca19aee45e9c68b3'" - -- name: test assemble with remote_src=False and a delimiter - assemble: src="./" dest="{{output_dir}}/assembled5" remote_src=no delimiter="#--- delimiter ---#" - register: result - -- name: assert the fragments were assembled without remote - assert: - that: - - "result.state == 'file'" - - "result.checksum == '505359f48c65b3904127cf62b912991d4da7ed6d'" diff --git a/test/support/integration/plugins/action/assemble.py b/test/support/integration/plugins/action/assemble.py deleted file mode 100644 index d874c7090b8..00000000000 --- a/test/support/integration/plugins/action/assemble.py +++ /dev/null @@ -1,165 +0,0 @@ -# (c) 2013-2016, Michael DeHaan -# Stephen Fromm -# Brian Coca -# Toshio Kuratomi -# -# 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 -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - -import codecs -import os -import os.path -import re -import tempfile - -from ansible import constants as C -from ansible.errors import AnsibleError, AnsibleAction, _AnsibleActionDone, AnsibleActionFail -from ansible.module_utils._text import to_native, to_text -from ansible.module_utils.parsing.convert_bool import boolean -from ansible.plugins.action import ActionBase -from ansible.utils.hashing import checksum_s - - -class ActionModule(ActionBase): - - TRANSFERS_FILES = True - - def _assemble_from_fragments(self, src_path, delimiter=None, compiled_regexp=None, ignore_hidden=False, decrypt=True): - ''' assemble a file from a directory of fragments ''' - - tmpfd, temp_path = tempfile.mkstemp(dir=C.DEFAULT_LOCAL_TMP) - tmp = os.fdopen(tmpfd, 'wb') - delimit_me = False - add_newline = False - - for f in (to_text(p, errors='surrogate_or_strict') for p in sorted(os.listdir(src_path))): - if compiled_regexp and not compiled_regexp.search(f): - continue - fragment = u"%s/%s" % (src_path, f) - if not os.path.isfile(fragment) or (ignore_hidden and os.path.basename(fragment).startswith('.')): - continue - - with open(self._loader.get_real_file(fragment, decrypt=decrypt), 'rb') as fragment_fh: - fragment_content = fragment_fh.read() - - # always put a newline between fragments if the previous fragment didn't end with a newline. - if add_newline: - tmp.write(b'\n') - - # delimiters should only appear between fragments - if delimit_me: - if delimiter: - # un-escape anything like newlines - delimiter = codecs.escape_decode(delimiter)[0] - tmp.write(delimiter) - # always make sure there's a newline after the - # delimiter, so lines don't run together - if delimiter[-1] != b'\n': - tmp.write(b'\n') - - tmp.write(fragment_content) - delimit_me = True - if fragment_content.endswith(b'\n'): - add_newline = False - else: - add_newline = True - - tmp.close() - return temp_path - - def run(self, tmp=None, task_vars=None): - - self._supports_check_mode = False - - result = super(ActionModule, self).run(tmp, task_vars) - del tmp # tmp no longer has any effect - - if task_vars is None: - task_vars = dict() - - src = self._task.args.get('src', None) - dest = self._task.args.get('dest', None) - delimiter = self._task.args.get('delimiter', None) - remote_src = self._task.args.get('remote_src', 'yes') - regexp = self._task.args.get('regexp', None) - follow = self._task.args.get('follow', False) - ignore_hidden = self._task.args.get('ignore_hidden', False) - decrypt = self._task.args.get('decrypt', True) - - try: - if src is None or dest is None: - raise AnsibleActionFail("src and dest are required") - - if boolean(remote_src, strict=False): - result.update(self._execute_module(module_name='assemble', task_vars=task_vars)) - raise _AnsibleActionDone() - else: - try: - src = self._find_needle('files', src) - except AnsibleError as e: - raise AnsibleActionFail(to_native(e)) - - if not os.path.isdir(src): - raise AnsibleActionFail(u"Source (%s) is not a directory" % src) - - _re = None - if regexp is not None: - _re = re.compile(regexp) - - # Does all work assembling the file - path = self._assemble_from_fragments(src, delimiter, _re, ignore_hidden, decrypt) - - path_checksum = checksum_s(path) - dest = self._remote_expand_user(dest) - dest_stat = self._execute_remote_stat(dest, all_vars=task_vars, follow=follow) - - diff = {} - - # setup args for running modules - new_module_args = self._task.args.copy() - - # clean assemble specific options - for opt in ['remote_src', 'regexp', 'delimiter', 'ignore_hidden', 'decrypt']: - if opt in new_module_args: - del new_module_args[opt] - new_module_args['dest'] = dest - - if path_checksum != dest_stat['checksum']: - - if self._play_context.diff: - diff = self._get_diff_data(dest, path, task_vars) - - remote_path = self._connection._shell.join_path(self._connection._shell.tmpdir, 'src') - xfered = self._transfer_file(path, remote_path) - - # fix file permissions when the copy is done as a different user - self._fixup_perms2((self._connection._shell.tmpdir, remote_path)) - - new_module_args.update(dict(src=xfered,)) - - res = self._execute_module(module_name='copy', module_args=new_module_args, task_vars=task_vars) - if diff: - res['diff'] = diff - result.update(res) - else: - result.update(self._execute_module(module_name='file', module_args=new_module_args, task_vars=task_vars)) - - except AnsibleAction as e: - result.update(e.result) - finally: - self._remove_tmp_path(self._connection._shell.tmpdir) - - return result diff --git a/test/support/integration/plugins/modules/assemble.py b/test/support/integration/plugins/modules/assemble.py deleted file mode 100644 index 57ece419f9b..00000000000 --- a/test/support/integration/plugins/modules/assemble.py +++ /dev/null @@ -1,261 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2012, Stephen Fromm -# Copyright: (c) 2016, Toshio Kuratomi -# Copyright: (c) 2017, Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['stableinterface'], - 'supported_by': 'core'} - -DOCUMENTATION = r''' ---- -module: assemble -short_description: Assemble configuration files from fragments -description: -- Assembles a configuration file from fragments. -- Often a particular program will take a single configuration file and does not support a - C(conf.d) style structure where it is easy to build up the configuration - from multiple sources. C(assemble) will take a directory of files that can be - local or have already been transferred to the system, and concatenate them - together to produce a destination file. -- Files are assembled in string sorting order. -- Puppet calls this idea I(fragments). -version_added: '0.5' -options: - src: - description: - - An already existing directory full of source files. - type: path - required: true - dest: - description: - - A file to create using the concatenation of all of the source files. - type: path - required: true - backup: - description: - - Create a backup file (if C(yes)), including the timestamp information so - you can get the original file back if you somehow clobbered it - incorrectly. - type: bool - default: no - delimiter: - description: - - A delimiter to separate the file contents. - type: str - version_added: '1.4' - remote_src: - description: - - If C(no), it will search for src at originating/master machine. - - If C(yes), it will go to the remote/target machine for the src. - type: bool - default: no - version_added: '1.4' - regexp: - description: - - Assemble files only if C(regex) matches the filename. - - If not set, all files are assembled. - - Every "\" (backslash) must be escaped as "\\" to comply to YAML syntax. - - Uses L(Python regular expressions,http://docs.python.org/2/library/re.html). - type: str - ignore_hidden: - description: - - A boolean that controls if files that start with a '.' will be included or not. - type: bool - default: no - version_added: '2.0' - validate: - description: - - The validation command to run before copying into place. - - The path to the file to validate is passed in via '%s' which must be present as in the sshd example below. - - The command is passed securely so shell features like expansion and pipes won't work. - type: str - version_added: '2.0' -seealso: -- module: copy -- module: template -- module: win_copy -author: -- Stephen Fromm (@sfromm) -extends_documentation_fragment: -- decrypt -- files -''' - -EXAMPLES = r''' -- name: Assemble from fragments from a directory - assemble: - src: /etc/someapp/fragments - dest: /etc/someapp/someapp.conf - -- name: Inserted provided delimiter in between each fragment - assemble: - src: /etc/someapp/fragments - dest: /etc/someapp/someapp.conf - delimiter: '### START FRAGMENT ###' - -- name: Assemble a new "sshd_config" file into place, after passing validation with sshd - assemble: - src: /etc/ssh/conf.d/ - dest: /etc/ssh/sshd_config - validate: /usr/sbin/sshd -t -f %s -''' - -import codecs -import os -import re -import tempfile - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import b, indexbytes -from ansible.module_utils._text import to_native - - -def assemble_from_fragments(src_path, delimiter=None, compiled_regexp=None, ignore_hidden=False, tmpdir=None): - ''' assemble a file from a directory of fragments ''' - tmpfd, temp_path = tempfile.mkstemp(dir=tmpdir) - tmp = os.fdopen(tmpfd, 'wb') - delimit_me = False - add_newline = False - - for f in sorted(os.listdir(src_path)): - if compiled_regexp and not compiled_regexp.search(f): - continue - fragment = os.path.join(src_path, f) - if not os.path.isfile(fragment) or (ignore_hidden and os.path.basename(fragment).startswith('.')): - continue - with open(fragment, 'rb') as fragment_fh: - fragment_content = fragment_fh.read() - - # always put a newline between fragments if the previous fragment didn't end with a newline. - if add_newline: - tmp.write(b('\n')) - - # delimiters should only appear between fragments - if delimit_me: - if delimiter: - # un-escape anything like newlines - delimiter = codecs.escape_decode(delimiter)[0] - tmp.write(delimiter) - # always make sure there's a newline after the - # delimiter, so lines don't run together - - # byte indexing differs on Python 2 and 3, - # use indexbytes for compat - # chr(10) == '\n' - if indexbytes(delimiter, -1) != 10: - tmp.write(b('\n')) - - tmp.write(fragment_content) - delimit_me = True - if fragment_content.endswith(b('\n')): - add_newline = False - else: - add_newline = True - - tmp.close() - return temp_path - - -def cleanup(path, result=None): - # cleanup just in case - if os.path.exists(path): - try: - os.remove(path) - except (IOError, OSError) as e: - # don't error on possible race conditions, but keep warning - if result is not None: - result['warnings'] = ['Unable to remove temp file (%s): %s' % (path, to_native(e))] - - -def main(): - - module = AnsibleModule( - # not checking because of daisy chain to file module - argument_spec=dict( - src=dict(type='path', required=True), - delimiter=dict(type='str'), - dest=dict(type='path', required=True), - backup=dict(type='bool', default=False), - remote_src=dict(type='bool', default=False), - regexp=dict(type='str'), - ignore_hidden=dict(type='bool', default=False), - validate=dict(type='str'), - ), - add_file_common_args=True, - ) - - changed = False - path_hash = None - dest_hash = None - src = module.params['src'] - dest = module.params['dest'] - backup = module.params['backup'] - delimiter = module.params['delimiter'] - regexp = module.params['regexp'] - compiled_regexp = None - ignore_hidden = module.params['ignore_hidden'] - validate = module.params.get('validate', None) - - result = dict(src=src, dest=dest) - if not os.path.exists(src): - module.fail_json(msg="Source (%s) does not exist" % src) - - if not os.path.isdir(src): - module.fail_json(msg="Source (%s) is not a directory" % src) - - if regexp is not None: - try: - compiled_regexp = re.compile(regexp) - except re.error as e: - module.fail_json(msg="Invalid Regexp (%s) in \"%s\"" % (to_native(e), regexp)) - - if validate and "%s" not in validate: - module.fail_json(msg="validate must contain %%s: %s" % validate) - - path = assemble_from_fragments(src, delimiter, compiled_regexp, ignore_hidden, module.tmpdir) - path_hash = module.sha1(path) - result['checksum'] = path_hash - - # Backwards compat. This won't return data if FIPS mode is active - try: - pathmd5 = module.md5(path) - except ValueError: - pathmd5 = None - result['md5sum'] = pathmd5 - - if os.path.exists(dest): - dest_hash = module.sha1(dest) - - if path_hash != dest_hash: - if validate: - (rc, out, err) = module.run_command(validate % path) - result['validation'] = dict(rc=rc, stdout=out, stderr=err) - if rc != 0: - cleanup(path) - module.fail_json(msg="failed to validate: rc:%s error:%s" % (rc, err)) - if backup and dest_hash is not None: - result['backup_file'] = module.backup_local(dest) - - module.atomic_move(path, dest, unsafe_writes=module.params['unsafe_writes']) - changed = True - - cleanup(path, result) - - # handle file permissions - file_args = module.load_file_common_arguments(module.params) - result['changed'] = module.set_fs_attributes_if_different(file_args, changed) - - # Mission complete - result['msg'] = "OK" - module.exit_json(**result) - - -if __name__ == '__main__': - main()