Refactor add_file_common_args and decrypt

Unify the system for handling common arguments
by adding an extends_common_args argument to the
AnsibleModule __init__. This stops the proliferation
of add_whatever_common_args bools to the __init__
pull/86110/head
Patrick Kingston 1 month ago
parent 12acd5c18f
commit c30f96a2b4

@ -204,6 +204,13 @@ FILE_COMMON_ARGUMENTS = dict(
unsafe_writes=dict(type='bool', default=False, fallback=(env_fallback, ['ANSIBLE_UNSAFE_WRITES'])), # should be available to any module using atomic_move unsafe_writes=dict(type='bool', default=False, fallback=(env_fallback, ['ANSIBLE_UNSAFE_WRITES'])), # should be available to any module using atomic_move
) )
DECRYPT_COMMON_ARGUMENT = dict(
decrypt=dict(
type='bool',
default=True,
),
)
PASSWD_ARG_RE = re.compile(r'^[-]{0,2}pass[-]?(word|wd)?') PASSWD_ARG_RE = re.compile(r'^[-]{0,2}pass[-]?(word|wd)?')
# Used for parsing symbolic file perms # Used for parsing symbolic file perms
@ -368,7 +375,7 @@ def missing_required_lib(library, reason=None, url=None):
class AnsibleModule(object): class AnsibleModule(object):
def __init__(self, argument_spec, bypass_checks=False, no_log=False, def __init__(self, argument_spec, bypass_checks=False, no_log=False,
mutually_exclusive=None, required_together=None, mutually_exclusive=None, required_together=None,
required_one_of=None, add_file_common_args=False, required_one_of=None, extends_common_args=(),
supports_check_mode=False, required_if=None, required_by=None): supports_check_mode=False, required_if=None, required_by=None):
""" """
@ -408,8 +415,8 @@ class AnsibleModule(object):
self._options_context = list() self._options_context = list()
self._tmpdir = None self._tmpdir = None
if add_file_common_args: for shared_arguments in extends_common_args:
for k, v in FILE_COMMON_ARGUMENTS.items(): for k, v in shared_arguments.items():
if k not in self.argument_spec: if k not in self.argument_spec:
self.argument_spec[k] = v self.argument_spec[k] = v

@ -130,7 +130,7 @@ import os
import re import re
import tempfile import tempfile
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule, FILE_COMMON_ARGUMENTS, DECRYPT_COMMON_ARGUMENT
from ansible.module_utils.common.text.converters import to_native from ansible.module_utils.common.text.converters import to_native
@ -201,13 +201,14 @@ def main():
regexp=dict(type='str'), regexp=dict(type='str'),
ignore_hidden=dict(type='bool', default=False), ignore_hidden=dict(type='bool', default=False),
validate=dict(type='str'), validate=dict(type='str'),
),
extends_common_args=(
FILE_COMMON_ARGUMENTS,
# Options that are for the action plugin, but ignored by the module itself. # Options that are for the action plugin, but ignored by the module itself.
# We have them here so that the tests pass without ignores, which # We have them here so that the tests pass without ignores, which
# reduces the likelihood of further bugs added. # reduces the likelihood of further bugs added.
decrypt=dict(type='bool', default=True), DECRYPT_COMMON_ARGUMENT,
), ),
add_file_common_args=True,
supports_check_mode=True, supports_check_mode=True,
) )

@ -200,7 +200,7 @@ import re
import os import os
import tempfile import tempfile
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule, FILE_COMMON_ARGUMENTS
from ansible.module_utils.common.text.converters import to_native from ansible.module_utils.common.text.converters import to_native
@ -257,7 +257,7 @@ def main():
encoding=dict(type='str', default='utf-8'), encoding=dict(type='str', default='utf-8'),
), ),
mutually_exclusive=[['insertbefore', 'insertafter']], mutually_exclusive=[['insertbefore', 'insertafter']],
add_file_common_args=True, extends_common_args=(FILE_COMMON_ARGUMENTS,),
supports_check_mode=True supports_check_mode=True
) )
params = module.params params = module.params

@ -293,7 +293,7 @@ import stat
import tempfile import tempfile
from ansible.module_utils.common.text.converters import to_bytes, to_native from ansible.module_utils.common.text.converters import to_bytes, to_native
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule, FILE_COMMON_ARGUMENTS
class AnsibleModuleError(Exception): class AnsibleModuleError(Exception):
@ -479,7 +479,7 @@ def main():
checksum=dict(type='str'), checksum=dict(type='str'),
follow=dict(type='bool', default=False), follow=dict(type='bool', default=False),
), ),
add_file_common_args=True, extends_common_args=(FILE_COMMON_ARGUMENTS,),
supports_check_mode=True, supports_check_mode=True,
) )

@ -236,7 +236,7 @@ import time
from pwd import getpwnam, getpwuid from pwd import getpwnam, getpwuid
from grp import getgrnam, getgrgid from grp import getgrnam, getgrgid
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule, FILE_COMMON_ARGUMENTS
from ansible.module_utils.common.text.converters import to_bytes, to_native from ansible.module_utils.common.text.converters import to_bytes, to_native
from ansible.module_utils.common.sentinel import Sentinel from ansible.module_utils.common.sentinel import Sentinel
@ -963,16 +963,16 @@ def main():
path=dict(type='path', required=True, aliases=['dest', 'name']), path=dict(type='path', required=True, aliases=['dest', 'name']),
_original_basename=dict(type='str'), # Internal use only, for recursive ops _original_basename=dict(type='str'), # Internal use only, for recursive ops
recurse=dict(type='bool', default=False), recurse=dict(type='bool', default=False),
force=dict(type='bool', default=False), # Note: Should not be in file_common_args in future force=dict(type='bool', default=False),
follow=dict(type='bool', default=True), # Note: Different default than file_common_args follow=dict(type='bool', default=True),
_diff_peek=dict(type='bool'), # Internal use only, for internal checks in the action plugins _diff_peek=dict(type='bool'), # Internal use only, for internal checks in the action plugins
src=dict(type='path'), # Note: Should not be in file_common_args in future src=dict(type='path'),
modification_time=dict(type='str'), modification_time=dict(type='str'),
modification_time_format=dict(type='str', default='%Y%m%d%H%M.%S'), modification_time_format=dict(type='str', default='%Y%m%d%H%M.%S'),
access_time=dict(type='str'), access_time=dict(type='str'),
access_time_format=dict(type='str', default='%Y%m%d%H%M.%S'), access_time_format=dict(type='str', default='%Y%m%d%H%M.%S'),
), ),
add_file_common_args=True, extends_common_args=(FILE_COMMON_ARGUMENTS,),
supports_check_mode=True, supports_check_mode=True,
) )

@ -376,7 +376,7 @@ import tempfile
from datetime import datetime, timezone from datetime import datetime, timezone
from urllib.parse import urlsplit from urllib.parse import urlsplit
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule, FILE_COMMON_ARGUMENTS
from ansible.module_utils.common.text.converters import to_native from ansible.module_utils.common.text.converters import to_native
from ansible.module_utils.urls import fetch_url, url_argument_spec from ansible.module_utils.urls import fetch_url, url_argument_spec
@ -535,7 +535,7 @@ def main():
module = AnsibleModule( module = AnsibleModule(
# not checking because of daisy chain to file module # not checking because of daisy chain to file module
argument_spec=argument_spec, argument_spec=argument_spec,
add_file_common_args=True, extends_common_args=(FILE_COMMON_ARGUMENTS,),
supports_check_mode=True, supports_check_mode=True,
) )

@ -253,7 +253,7 @@ import re
import tempfile import tempfile
# import module snippets # import module snippets
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule, FILE_COMMON_ARGUMENTS
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
@ -585,7 +585,7 @@ def main():
), ),
mutually_exclusive=[ mutually_exclusive=[
['insertbefore', 'insertafter'], ['regexp', 'search_string'], ['backrefs', 'search_string']], ['insertbefore', 'insertafter'], ['regexp', 'search_string'], ['backrefs', 'search_string']],
add_file_common_args=True, extends_common_args=(FILE_COMMON_ARGUMENTS,),
supports_check_mode=True, supports_check_mode=True,
) )

@ -184,7 +184,7 @@ import re
import tempfile import tempfile
from ansible.module_utils.common.text.converters import to_text from ansible.module_utils.common.text.converters import to_text
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule, FILE_COMMON_ARGUMENTS
def write_changes(module, contents, path, encoding='utf-8'): def write_changes(module, contents, path, encoding='utf-8'):
@ -232,7 +232,7 @@ def main():
validate=dict(type='str'), validate=dict(type='str'),
encoding=dict(type='str', default='utf-8'), encoding=dict(type='str', default='utf-8'),
), ),
add_file_common_args=True, extends_common_args=(FILE_COMMON_ARGUMENTS,),
supports_check_mode=True, supports_check_mode=True,
) )

@ -254,7 +254,7 @@ from functools import partial
from zipfile import ZipFile from zipfile import ZipFile
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule, FILE_COMMON_ARGUMENTS, DECRYPT_COMMON_ARGUMENT
from ansible.module_utils.common.process import get_bin_path from ansible.module_utils.common.process import get_bin_path
from ansible.module_utils.common.locale import get_best_parsable_locale from ansible.module_utils.common.locale import get_best_parsable_locale
from ansible.module_utils.urls import fetch_file from ansible.module_utils.urls import fetch_file
@ -1067,9 +1067,11 @@ def main():
# We have them here so that the sanity tests pass without ignores, which # We have them here so that the sanity tests pass without ignores, which
# reduces the likelihood of further bugs added. # reduces the likelihood of further bugs added.
copy=dict(type='bool', default=True), copy=dict(type='bool', default=True),
decrypt=dict(type='bool', default=True),
), ),
add_file_common_args=True, extends_common_args=(
FILE_COMMON_ARGUMENTS,
DECRYPT_COMMON_ARGUMENT, # Also here for the action plugin
),
# check-mode only works for zip files, we cover that later # check-mode only works for zip files, we cover that later
supports_check_mode=True, supports_check_mode=True,
mutually_exclusive=[('include', 'exclude')], mutually_exclusive=[('include', 'exclude')],

@ -442,7 +442,7 @@ from collections.abc import Mapping, Sequence
from datetime import datetime, timezone from datetime import datetime, timezone
from urllib.parse import urlencode, urljoin from urllib.parse import urlencode, urljoin
from ansible.module_utils.basic import AnsibleModule, sanitize_keys from ansible.module_utils.basic import AnsibleModule, sanitize_keys, FILE_COMMON_ARGUMENTS
from ansible.module_utils.common.text.converters import to_native, to_text from ansible.module_utils.common.text.converters import to_native, to_text
from ansible.module_utils.urls import ( from ansible.module_utils.urls import (
fetch_url, fetch_url,
@ -614,7 +614,7 @@ def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec=argument_spec, argument_spec=argument_spec,
add_file_common_args=True, extends_common_args=(FILE_COMMON_ARGUMENTS,),
mutually_exclusive=[['body', 'src']], mutually_exclusive=[['body', 'src']],
) )

@ -586,7 +586,7 @@ def main():
["state", "present", ["description"]], ["state", "present", ["description"]],
], ],
argument_spec=argument_spec, argument_spec=argument_spec,
add_file_common_args=True, extends_common_args=FILE_COMMON_ARGUMENTS,
supports_check_mode=True, supports_check_mode=True,
) )

@ -25,6 +25,7 @@ import typing as _t
from ansible.errors import AnsibleError, AnsibleActionFail, AnsibleActionSkip from ansible.errors import AnsibleError, AnsibleActionFail, AnsibleActionSkip
from ansible.executor.powershell import module_manifest as ps_manifest from ansible.executor.powershell import module_manifest as ps_manifest
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
from ansible.module_utils.basic import DECRYPT_COMMON_ARGUMENT
from ansible.plugins.action import ActionBase from ansible.plugins.action import ActionBase
@ -49,7 +50,7 @@ class ActionModule(ActionBase):
'removes': {'type': 'str'}, 'removes': {'type': 'str'},
'chdir': {'type': 'str'}, 'chdir': {'type': 'str'},
'executable': {'type': 'str'}, 'executable': {'type': 'str'},
}, } | DECRYPT_COMMON_ARGUMENT,
required_one_of=[['_raw_params', 'cmd']], required_one_of=[['_raw_params', 'cmd']],
mutually_exclusive=[['_raw_params', 'cmd']], mutually_exclusive=[['_raw_params', 'cmd']],
) )

@ -163,11 +163,12 @@ def get_py_argument_spec(filename, collection):
fake.kwargs[arg_name] = arg fake.kwargs[arg_name] = arg
# for ping kwargs == {'argument_spec':{'data':{'type':'str','default':'pong'}}, 'supports_check_mode':True} # for ping kwargs == {'argument_spec':{'data':{'type':'str','default':'pong'}}, 'supports_check_mode':True}
argument_spec = fake.kwargs.get('argument_spec') or {} argument_spec = fake.kwargs.get('argument_spec') or {}
# If add_file_common_args is truish, add options from FILE_COMMON_ARGUMENTS when not present. # Add the common arguments from the extends_common_args
# This is the only modification to argument_spec done by AnsibleModule itself, and which is # This is the only modification to argument_spec done by AnsibleModule itself, and which is
# not caught by setup_env's AnsibleModule replacement # not caught by setup_env's AnsibleModule replacement
if fake.kwargs.get('add_file_common_args'): if args := fake.kwargs.get('extends_common_arguments'):
for k, v in FILE_COMMON_ARGUMENTS.items(): for common in args:
for k, v in common.items():
if k not in argument_spec: if k not in argument_spec:
argument_spec[k] = v argument_spec[k] = v
return argument_spec, fake.kwargs return argument_spec, fake.kwargs

@ -367,7 +367,7 @@ def ansible_module_kwargs_schema(module_name, for_collection):
'bypass_checks': bool, 'bypass_checks': bool,
'no_log': bool, 'no_log': bool,
'check_invalid_arguments': Any(None, bool), 'check_invalid_arguments': Any(None, bool),
'add_file_common_args': bool, 'extends_common_args': tuple[dict],
'supports_check_mode': bool, 'supports_check_mode': bool,
} }
if module_name.endswith(('_info', '_facts')): if module_name.endswith(('_info', '_facts')):

@ -15,6 +15,7 @@ import pytest
from unittest.mock import MagicMock from unittest.mock import MagicMock
from ansible.module_utils import basic from ansible.module_utils import basic
from ansible.module_utils.api import basic_auth_argument_spec, rate_limit_argument_spec, retry_argument_spec from ansible.module_utils.api import basic_auth_argument_spec, rate_limit_argument_spec, retry_argument_spec
from ansible.module_utils.basic import FILE_COMMON_ARGUMENTS
from ansible.module_utils.common.warnings import get_deprecation_messages, get_warning_messages from ansible.module_utils.common.warnings import get_deprecation_messages, get_warning_messages
import builtins import builtins
@ -138,7 +139,7 @@ def complex_argspec():
mutually_exclusive=mut_ex, mutually_exclusive=mut_ex,
required_together=req_to, required_together=req_to,
no_log=True, no_log=True,
add_file_common_args=True, extends_common_args=FILE_COMMON_ARGUMENTS,
supports_check_mode=True, supports_check_mode=True,
) )
return kwargs return kwargs
@ -188,7 +189,7 @@ def options_argspec_list():
kwargs = dict( kwargs = dict(
argument_spec=arg_spec, argument_spec=arg_spec,
no_log=True, no_log=True,
add_file_common_args=True, extends_common_args=FILE_COMMON_ARGUMENTS,
supports_check_mode=True supports_check_mode=True
) )
return kwargs return kwargs

Loading…
Cancel
Save