Version source tagging (automatic and manual) for version_added and deprecation versions (#69680)
* Track collection for version_added. Validate *all* version numbers in validate-modules. For tagged version numbers (i.e. version_added), consider source collection to chose validation. * Make tagging/untagging functions more flexible. * Tag all versions in doc fragments. * Tag all deprecation versions issued by code. * Make Display.deprecated() understand tagged versions. * Extend validation to enforce tagged version numbers. * Tag versions in tests. * Lint and fix test. * Mention collection name in collection loader's deprecation/removal messages. * Fix error IDs. * Handle tagged dates in Display.deprecated(). * Also require that removed_at_date and deprecated_aliases.date are tagged. * Also automatically tag/untag removed_at_date; fix sanity module removal version check. * Improve error message when invalid version number is used (like '2.14' in collections).
This commit is contained in:
parent
f5f3ba7ab5
commit
40f21dfd3c
60 changed files with 601 additions and 302 deletions
|
@ -0,0 +1,2 @@
|
|||
minor_changes:
|
||||
- "ansible-doc - now indicates if an option is added by a doc fragment from another collection by prepending the collection name, or ``ansible.builtin`` for ansible-base, to the version number."
|
2
changelogs/fragments/ansible-test-version-validation.yml
Normal file
2
changelogs/fragments/ansible-test-version-validation.yml
Normal file
|
@ -0,0 +1,2 @@
|
|||
minor_changes:
|
||||
- "ansible-test - ``validate-modules`` now validates all version numbers in documentation and argument spec. Version numbers for collections are checked for being valid semantic versioning version number strings."
|
|
@ -95,8 +95,10 @@ Codes
|
|||
invalid-extension Naming Error Official Ansible modules must have a ``.py`` extension for python modules or a ``.ps1`` for powershell modules
|
||||
invalid-metadata-status Documentation Error ``ANSIBLE_METADATA.status`` of deprecated or removed can't include other statuses
|
||||
invalid-metadata-type Documentation Error ``ANSIBLE_METADATA`` was not provided as a dict, YAML not supported, Invalid ``ANSIBLE_METADATA`` schema
|
||||
invalid-module-deprecation-source Documentation Error The deprecated version for the module must not be from a documentation fragment from another collection or Ansible-base
|
||||
invalid-module-schema Documentation Error ``AnsibleModule`` schema validation error
|
||||
invalid-requires-extension Naming Error Module ``#AnsibleRequires -CSharpUtil`` should not end in .cs, Module ``#Requires`` should not end in .psm1
|
||||
invalid-tagged-version Documentation Error All version numbers specified in code have to be explicitly tagged with the collection name, i.e. ``community.general:1.2.3`` or ``ansible.builtin:2.10``
|
||||
last-line-main-call Syntax Error Call to ``main()`` not the last line (or ``removed_module()`` in the case of deprecated & docs only modules)
|
||||
metadata-changed Documentation Error ``ANSIBLE_METADATA`` cannot be changed in a point release for a stable branch
|
||||
missing-doc-fragment Documentation Error ``DOCUMENTATION`` fragment missing
|
||||
|
|
|
@ -353,7 +353,7 @@ class CLI(with_metaclass(ABCMeta, object)):
|
|||
verbosity_arg = next(iter([arg for arg in self.args if arg.startswith('-v')]), None)
|
||||
if verbosity_arg:
|
||||
display.deprecated("Setting verbosity before the arg sub command is deprecated, set the verbosity "
|
||||
"after the sub command", "2.13")
|
||||
"after the sub command", "ansible.builtin:2.13")
|
||||
options.verbosity = verbosity_arg.count('v')
|
||||
|
||||
return options
|
||||
|
|
|
@ -397,7 +397,7 @@ class ConsoleCLI(CLI, cmd.Cmd):
|
|||
|
||||
def module_args(self, module_name):
|
||||
in_path = module_loader.find_plugin(module_name)
|
||||
oc, a, _, _ = plugin_docs.get_docstring(in_path, fragment_loader)
|
||||
oc, a, _, _ = plugin_docs.get_docstring(in_path, fragment_loader, is_module=True)
|
||||
return list(oc['options'].keys())
|
||||
|
||||
def run(self):
|
||||
|
|
|
@ -31,7 +31,7 @@ from ansible.plugins.loader import action_loader, fragment_loader
|
|||
from ansible.utils.collection_loader import AnsibleCollectionConfig
|
||||
from ansible.utils.collection_loader._collection_finder import _get_collection_name_from_path
|
||||
from ansible.utils.display import Display
|
||||
from ansible.utils.plugin_docs import BLACKLIST, get_docstring, get_versioned_doclink
|
||||
from ansible.utils.plugin_docs import BLACKLIST, untag_versions_and_dates, get_docstring, get_versioned_doclink
|
||||
|
||||
display = Display()
|
||||
|
||||
|
@ -218,7 +218,7 @@ class DocCLI(CLI):
|
|||
plugin_docs = {}
|
||||
for plugin in context.CLIARGS['args']:
|
||||
try:
|
||||
doc, plainexamples, returndocs, metadata = DocCLI._get_plugin_doc(plugin, loader, search_paths)
|
||||
doc, plainexamples, returndocs, metadata = DocCLI._get_plugin_doc(plugin, plugin_type, loader, search_paths)
|
||||
except PluginNotFound:
|
||||
display.warning("%s %s not found in:\n%s\n" % (plugin_type, plugin, search_paths))
|
||||
continue
|
||||
|
@ -281,8 +281,13 @@ class DocCLI(CLI):
|
|||
if filename is None:
|
||||
raise AnsibleError("unable to load {0} plugin named {1} ".format(plugin_type, plugin_name))
|
||||
|
||||
collection_name = 'ansible.builtin'
|
||||
if plugin_name.startswith('ansible_collections.'):
|
||||
collection_name = '.'.join(plugin_name.split('.')[1:3])
|
||||
|
||||
try:
|
||||
doc, __, __, metadata = get_docstring(filename, fragment_loader, verbose=(context.CLIARGS['verbosity'] > 0))
|
||||
doc, __, __, metadata = get_docstring(filename, fragment_loader, verbose=(context.CLIARGS['verbosity'] > 0),
|
||||
collection_name=collection_name, is_module=(plugin_type == 'module'))
|
||||
except Exception:
|
||||
display.vvv(traceback.format_exc())
|
||||
raise AnsibleError(
|
||||
|
@ -319,13 +324,20 @@ class DocCLI(CLI):
|
|||
return clean_ns
|
||||
|
||||
@staticmethod
|
||||
def _get_plugin_doc(plugin, loader, search_paths):
|
||||
def _get_plugin_doc(plugin, plugin_type, loader, search_paths):
|
||||
# if the plugin lives in a non-python file (eg, win_X.ps1), require the corresponding python file for docs
|
||||
filename = loader.find_plugin(plugin, mod_type='.py', ignore_deprecated=True, check_aliases=True)
|
||||
if filename is None:
|
||||
result = loader.find_plugin_with_context(plugin, mod_type='.py', ignore_deprecated=True, check_aliases=True)
|
||||
if result is None:
|
||||
raise PluginNotFound('%s was not found in %s' % (plugin, search_paths))
|
||||
plugin_name, filename = result.plugin_resolved_name, result.plugin_resolved_path
|
||||
|
||||
doc, plainexamples, returndocs, metadata = get_docstring(filename, fragment_loader, verbose=(context.CLIARGS['verbosity'] > 0))
|
||||
collection_name = 'ansible.builtin'
|
||||
if plugin_name.startswith('ansible_collections.'):
|
||||
collection_name = '.'.join(plugin_name.split('.')[1:3])
|
||||
|
||||
doc, plainexamples, returndocs, metadata = get_docstring(
|
||||
filename, fragment_loader, verbose=(context.CLIARGS['verbosity'] > 0),
|
||||
collection_name=collection_name, is_module=(plugin_type == 'module'))
|
||||
|
||||
# If the plugin existed but did not have a DOCUMENTATION element and was not removed, it's
|
||||
# an error
|
||||
|
@ -346,6 +358,7 @@ class DocCLI(CLI):
|
|||
raise ValueError('%s did not contain a DOCUMENTATION attribute' % plugin)
|
||||
|
||||
doc['filename'] = filename
|
||||
untag_versions_and_dates(doc, '%s:' % (collection_name, ), is_module=(plugin_type == 'module'))
|
||||
return doc, plainexamples, returndocs, metadata
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -487,7 +487,7 @@ class TaskExecutor:
|
|||
'Invoking "%s" only once while using a loop via squash_actions is deprecated. '
|
||||
'Instead of using a loop to supply multiple items and specifying `%s: "%s"`, '
|
||||
'please use `%s: %s` and remove the loop' % (self._task.action, found, name, found, value_text),
|
||||
version='2.11'
|
||||
version='ansible.builtin:2.11'
|
||||
)
|
||||
for item in items:
|
||||
variables[loop_var] = item
|
||||
|
|
|
@ -1528,7 +1528,8 @@ def url_argument_spec():
|
|||
'''
|
||||
return dict(
|
||||
url=dict(type='str'),
|
||||
force=dict(type='bool', default=False, aliases=['thirsty'], deprecated_aliases=[dict(name='thirsty', version='2.13')]),
|
||||
force=dict(type='bool', default=False, aliases=['thirsty'],
|
||||
deprecated_aliases=[dict(name='thirsty', version='ansible.builtin:2.13')]),
|
||||
http_agent=dict(type='str', default='ansible-httpget'),
|
||||
use_proxy=dict(type='bool', default=True),
|
||||
validate_certs=dict(type='bool', default=True),
|
||||
|
|
|
@ -517,7 +517,7 @@ def main():
|
|||
)
|
||||
|
||||
if module.params.get('thirsty'):
|
||||
module.deprecate('The alias "thirsty" has been deprecated and will be removed, use "force" instead', version='2.13')
|
||||
module.deprecate('The alias "thirsty" has been deprecated and will be removed, use "force" instead', version='ansible.builtin:2.13')
|
||||
|
||||
src = module.params['src']
|
||||
b_src = to_bytes(src, errors='surrogate_or_strict')
|
||||
|
|
|
@ -621,12 +621,12 @@ def main():
|
|||
if not name:
|
||||
module.deprecate(
|
||||
msg="The 'name' parameter will be required in future releases.",
|
||||
version='2.12'
|
||||
version='ansible.builtin:2.12'
|
||||
)
|
||||
if reboot:
|
||||
module.deprecate(
|
||||
msg="The 'reboot' parameter will be removed in future releases. Use 'special_time' option instead.",
|
||||
version='2.12'
|
||||
version='ansible.builtin:2.12'
|
||||
)
|
||||
|
||||
if module._diff:
|
||||
|
|
|
@ -449,10 +449,10 @@ def main():
|
|||
)
|
||||
|
||||
if module.params.get('thirsty'):
|
||||
module.deprecate('The alias "thirsty" has been deprecated and will be removed, use "force" instead', version='2.13')
|
||||
module.deprecate('The alias "thirsty" has been deprecated and will be removed, use "force" instead', version='ansible.builtin:2.13')
|
||||
|
||||
if module.params.get('sha256sum'):
|
||||
module.deprecate('The parameter "sha256sum" has been deprecated and will be removed, use "checksum" instead', version='2.14')
|
||||
module.deprecate('The parameter "sha256sum" has been deprecated and will be removed, use "checksum" instead', version='ansible.builtin:2.14')
|
||||
|
||||
url = module.params['url']
|
||||
dest = module.params['dest']
|
||||
|
|
|
@ -360,7 +360,7 @@ def main():
|
|||
''' Set CLI options depending on params '''
|
||||
if module.params['user'] is not None:
|
||||
# handle user deprecation, mutually exclusive with scope
|
||||
module.deprecate("The 'user' option is being replaced by 'scope'", version='2.11')
|
||||
module.deprecate("The 'user' option is being replaced by 'scope'", version='ansible.builtin:2.11')
|
||||
if module.params['user']:
|
||||
module.params['scope'] = 'user'
|
||||
else:
|
||||
|
|
|
@ -615,7 +615,7 @@ def main():
|
|||
)
|
||||
|
||||
if module.params.get('thirsty'):
|
||||
module.deprecate('The alias "thirsty" has been deprecated and will be removed, use "force" instead', version='2.13')
|
||||
module.deprecate('The alias "thirsty" has been deprecated and will be removed, use "force" instead', version='ansible.builtin:2.13')
|
||||
|
||||
url = module.params['url']
|
||||
body = module.params['body']
|
||||
|
|
|
@ -79,7 +79,7 @@ class Playbook:
|
|||
self._loader.set_basedir(cur_basedir)
|
||||
raise AnsibleParserError("A playbook must be a list of plays, got a %s instead" % type(ds), obj=ds)
|
||||
elif not ds:
|
||||
display.deprecated("Empty plays will currently be skipped, in the future they will cause a syntax error", version='2.12')
|
||||
display.deprecated("Empty plays will currently be skipped, in the future they will cause a syntax error", version='ansible.builtin:2.12')
|
||||
|
||||
# Parse the playbook entries. For plays, we simply parse them
|
||||
# using the Play() object, and includes are parsed using the
|
||||
|
@ -92,7 +92,7 @@ class Playbook:
|
|||
|
||||
if any(action in entry for action in ('import_playbook', 'include')):
|
||||
if 'include' in entry:
|
||||
display.deprecated("'include' for playbook includes. You should use 'import_playbook' instead", version="2.12")
|
||||
display.deprecated("'include' for playbook includes. You should use 'import_playbook' instead", version="ansible.builtin:2.12")
|
||||
pb = PlaybookInclude.load(entry, basedir=self._basedir, variable_manager=variable_manager, loader=self._loader)
|
||||
if pb is not None:
|
||||
self._entries.extend(pb._entries)
|
||||
|
|
|
@ -134,7 +134,7 @@ class Conditional:
|
|||
conditional = templar.template(conditional, disable_lookups=disable_lookups)
|
||||
if bare_vars_warning and not isinstance(conditional, bool):
|
||||
display.deprecated('evaluating %r as a bare variable, this behaviour will go away and you might need to add |bool'
|
||||
' to the expression in the future. Also see CONDITIONAL_BARE_VARS configuration toggle' % original, "2.12")
|
||||
' to the expression in the future. Also see CONDITIONAL_BARE_VARS configuration toggle' % original, "ansible.builtin:2.12")
|
||||
if not isinstance(conditional, text_type) or conditional == "":
|
||||
return conditional
|
||||
|
||||
|
|
|
@ -156,7 +156,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
|
|||
is_static = True
|
||||
elif t.static is not None:
|
||||
display.deprecated("The use of 'static' has been deprecated. "
|
||||
"Use 'import_tasks' for static inclusion, or 'include_tasks' for dynamic inclusion", version='2.12')
|
||||
"Use 'import_tasks' for static inclusion, or 'include_tasks' for dynamic inclusion", version='ansible.builtin:2.12')
|
||||
is_static = t.static
|
||||
else:
|
||||
is_static = C.DEFAULT_TASK_INCLUDES_STATIC or \
|
||||
|
@ -257,7 +257,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
|
|||
"later. In the future, this will be an error unless 'static: no' is used "
|
||||
"on the include task. If you do not want missing includes to be considered "
|
||||
"dynamic, use 'static: yes' on the include or set the global ansible.cfg "
|
||||
"options to make all includes static for tasks and/or handlers" % include_file, version="2.12"
|
||||
"options to make all includes static for tasks and/or handlers" % include_file, version="ansible.builtin:2.12"
|
||||
)
|
||||
task_list.append(t)
|
||||
continue
|
||||
|
@ -294,7 +294,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
|
|||
suppress_extended_error=True,
|
||||
)
|
||||
display.deprecated("You should not specify tags in the include parameters. All tags should be specified using the task-level option",
|
||||
version="2.12")
|
||||
version="ansible.builtin:2.12")
|
||||
else:
|
||||
tags = ti_copy.tags[:]
|
||||
|
||||
|
@ -332,7 +332,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
|
|||
|
||||
elif ir.static is not None:
|
||||
display.deprecated("The use of 'static' for 'include_role' has been deprecated. "
|
||||
"Use 'import_role' for static inclusion, or 'include_role' for dynamic inclusion", version='2.12')
|
||||
"Use 'import_role' for static inclusion, or 'include_role' for dynamic inclusion", version='ansible.builtin:2.12')
|
||||
is_static = ir.static
|
||||
|
||||
if is_static:
|
||||
|
|
|
@ -342,7 +342,7 @@ class PlayContext(Base):
|
|||
""" helper function to create privilege escalation commands """
|
||||
display.deprecated(
|
||||
"PlayContext.make_become_cmd should not be used, the calling code should be using become plugins instead",
|
||||
version="2.12"
|
||||
version="ansible.builtin:2.12"
|
||||
)
|
||||
|
||||
if not cmd or not self.become:
|
||||
|
|
|
@ -163,7 +163,7 @@ class Task(Base, Conditional, Taggable, CollectionSearch):
|
|||
raise AnsibleError("you must specify a value when using %s" % k, obj=ds)
|
||||
new_ds['loop_with'] = loop_name
|
||||
new_ds['loop'] = v
|
||||
# display.deprecated("with_ type loops are being phased out, use the 'loop' keyword instead", version="2.10")
|
||||
# display.deprecated("with_ type loops are being phased out, use the 'loop' keyword instead", version="ansible.builtin:2.10")
|
||||
|
||||
def preprocess_data(self, ds):
|
||||
'''
|
||||
|
@ -258,7 +258,7 @@ class Task(Base, Conditional, Taggable, CollectionSearch):
|
|||
if action in ('include',) and k not in self._valid_attrs and k not in self.DEPRECATED_ATTRIBUTES:
|
||||
display.deprecated("Specifying include variables at the top-level of the task is deprecated."
|
||||
" Please see:\nhttps://docs.ansible.com/ansible/playbooks_roles.html#task-include-files-and-encouraging-reuse\n\n"
|
||||
" for currently supported syntax regarding included files and variables", version="2.12")
|
||||
" for currently supported syntax regarding included files and variables", version="ansible.builtin:2.12")
|
||||
new_ds['vars'][k] = v
|
||||
elif C.INVALID_TASK_ATTRIBUTE_FAILED or k in self._valid_attrs:
|
||||
new_ds[k] = v
|
||||
|
|
|
@ -822,7 +822,7 @@ class ActionBase(with_metaclass(ABCMeta, object)):
|
|||
msg = "Setting the async dir from the environment keyword " \
|
||||
"ANSIBLE_ASYNC_DIR is deprecated. Set the async_dir " \
|
||||
"shell option instead"
|
||||
self._display.deprecated(msg, "2.12")
|
||||
self._display.deprecated(msg, "ansible.builtin:2.12")
|
||||
else:
|
||||
# ANSIBLE_ASYNC_DIR is not set on the task, we get the value
|
||||
# from the shell option and temporarily add to the environment
|
||||
|
|
|
@ -33,7 +33,7 @@ class ActionModule(ActionBase):
|
|||
msg = "Setting the async dir from the environment keyword " \
|
||||
"ANSIBLE_ASYNC_DIR is deprecated. Set the async_dir " \
|
||||
"shell option instead"
|
||||
self._display.deprecated(msg, "2.12")
|
||||
self._display.deprecated(msg, "ansible.builtin:2.12")
|
||||
else:
|
||||
# inject the async directory based on the shell option into the
|
||||
# module args
|
||||
|
|
4
lib/ansible/plugins/cache/__init__.py
vendored
4
lib/ansible/plugins/cache/__init__.py
vendored
|
@ -50,7 +50,7 @@ class FactCache(RealFactCache):
|
|||
' ansible.vars.fact_cache.FactCache. If you are looking for the class'
|
||||
' to subclass for a cache plugin, you want'
|
||||
' ansible.plugins.cache.BaseCacheModule or one of its subclasses.',
|
||||
version='2.12')
|
||||
version='ansible.builtin:2.12')
|
||||
super(FactCache, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
|
@ -62,7 +62,7 @@ class BaseCacheModule(AnsiblePlugin):
|
|||
def __init__(self, *args, **kwargs):
|
||||
# Third party code is not using cache_loader to load plugin - fall back to previous behavior
|
||||
if not hasattr(self, '_load_name'):
|
||||
display.deprecated('Rather than importing custom CacheModules directly, use ansible.plugins.loader.cache_loader', version='2.14')
|
||||
display.deprecated('Rather than importing custom CacheModules directly, use ansible.plugins.loader.cache_loader', version='ansible.builtin:2.14')
|
||||
self._load_name = self.__module__.split('.')[-1]
|
||||
self._load_name = resource_from_fqcr(self.__module__)
|
||||
super(BaseCacheModule, self).__init__()
|
||||
|
|
|
@ -242,7 +242,7 @@ class CallbackBase(AnsiblePlugin):
|
|||
def _get_item(self, result):
|
||||
''' here for backwards compat, really should have always been named: _get_item_label'''
|
||||
cback = getattr(self, 'NAME', os.path.basename(__file__))
|
||||
self._display.deprecated("The %s callback plugin should be updated to use the _get_item_label method instead" % cback, version="2.11")
|
||||
self._display.deprecated("The %s callback plugin should be updated to use the _get_item_label method instead" % cback, version="ansible.builtin:2.11")
|
||||
return self._get_item_label(result)
|
||||
|
||||
def _process_items(self, result):
|
||||
|
|
|
@ -237,28 +237,28 @@ class ConnectionBase(AnsiblePlugin):
|
|||
def check_become_success(self, b_output):
|
||||
display.deprecated(
|
||||
"Connection.check_become_success is deprecated, calling code should be using become plugins instead",
|
||||
version="2.12"
|
||||
version="ansible.builtin:2.12"
|
||||
)
|
||||
return self.become.check_success(b_output)
|
||||
|
||||
def check_password_prompt(self, b_output):
|
||||
display.deprecated(
|
||||
"Connection.check_password_prompt is deprecated, calling code should be using become plugins instead",
|
||||
version="2.12"
|
||||
version="ansible.builtin:2.12"
|
||||
)
|
||||
return self.become.check_password_prompt(b_output)
|
||||
|
||||
def check_incorrect_password(self, b_output):
|
||||
display.deprecated(
|
||||
"Connection.check_incorrect_password is deprecated, calling code should be using become plugins instead",
|
||||
version="2.12"
|
||||
version="ansible.builtin:2.12"
|
||||
)
|
||||
return self.become.check_incorrect_password(b_output)
|
||||
|
||||
def check_missing_password(self, b_output):
|
||||
display.deprecated(
|
||||
"Connection.check_missing_password is deprecated, calling code should be using become plugins instead",
|
||||
version="2.12"
|
||||
version="ansible.builtin:2.12"
|
||||
)
|
||||
return self.become.check_missing_password(b_output)
|
||||
|
||||
|
|
|
@ -291,19 +291,19 @@ class DeprecatedCache(object):
|
|||
display.deprecated('InventoryModule should utilize self._cache as a dict instead of self.cache. '
|
||||
'When expecting a KeyError, use self._cache[key] instead of using self.cache.get(key). '
|
||||
'self._cache is a dictionary and will return a default value instead of raising a KeyError '
|
||||
'when the key does not exist', version='2.12')
|
||||
'when the key does not exist', version='ansible.builtin:2.12')
|
||||
return self.real_cacheable._cache[key]
|
||||
|
||||
def set(self, key, value):
|
||||
display.deprecated('InventoryModule should utilize self._cache as a dict instead of self.cache. '
|
||||
'To set the self._cache dictionary, use self._cache[key] = value instead of self.cache.set(key, value). '
|
||||
'To force update the underlying cache plugin with the contents of self._cache before parse() is complete, '
|
||||
'call self.set_cache_plugin and it will use the self._cache dictionary to update the cache plugin', version='2.12')
|
||||
'call self.set_cache_plugin and it will use the self._cache dictionary to update the cache plugin', version='ansible.builtin:2.12')
|
||||
self.real_cacheable._cache[key] = value
|
||||
self.real_cacheable.set_cache_plugin()
|
||||
|
||||
def __getattr__(self, name):
|
||||
display.deprecated('InventoryModule should utilize self._cache instead of self.cache', version='2.12')
|
||||
display.deprecated('InventoryModule should utilize self._cache instead of self.cache', version='ansible.builtin:2.12')
|
||||
return self.real_cacheable._cache.__getattribute__(name)
|
||||
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
|
|||
display.deprecated(
|
||||
msg="The 'cache' option is deprecated for the script inventory plugin. "
|
||||
"External scripts implement their own caching and this option has never been used",
|
||||
version="2.12"
|
||||
version="ansible.builtin:2.12"
|
||||
)
|
||||
|
||||
# Support inventory scripts that are not prefixed with some
|
||||
|
|
|
@ -130,7 +130,7 @@ class PluginLoadContext(object):
|
|||
self.deprecation_warnings = []
|
||||
self.resolved = False
|
||||
|
||||
def record_deprecation(self, name, deprecation):
|
||||
def record_deprecation(self, name, deprecation, collection_name):
|
||||
if not deprecation:
|
||||
return self
|
||||
|
||||
|
@ -142,11 +142,14 @@ class PluginLoadContext(object):
|
|||
removal_version = None
|
||||
if not warning_text:
|
||||
if removal_date:
|
||||
warning_text = '{0} has been deprecated and will be removed in a release after {1}'.format(name, removal_date)
|
||||
warning_text = '{0} has been deprecated and will be removed in a release of {2} after {1}'.format(
|
||||
name, removal_date, collection_name)
|
||||
elif removal_version:
|
||||
warning_text = '{0} has been deprecated and will be removed in version {1}'.format(name, removal_version)
|
||||
warning_text = '{0} has been deprecated and will be removed in version {1} of {2}'.format(
|
||||
name, removal_version, collection_name)
|
||||
else:
|
||||
warning_text = '{0} has been deprecated and will be removed in a future release'.format(name)
|
||||
warning_text = '{0} has been deprecated and will be removed in a future release of {2}'.format(
|
||||
name, collection_name)
|
||||
|
||||
self.deprecated = True
|
||||
if removal_date:
|
||||
|
@ -374,7 +377,7 @@ class PluginLoader:
|
|||
if type_name in C.CONFIGURABLE_PLUGINS:
|
||||
dstring = AnsibleLoader(getattr(module, 'DOCUMENTATION', ''), file_name=path).get_single_data()
|
||||
if dstring:
|
||||
add_fragments(dstring, path, fragment_loader=fragment_loader)
|
||||
add_fragments(dstring, path, fragment_loader=fragment_loader, is_module=(type_name == 'module'))
|
||||
|
||||
if dstring and 'options' in dstring and isinstance(dstring['options'], dict):
|
||||
C.config.initialize_plugin_configuration_definitions(type_name, name, dstring['options'])
|
||||
|
@ -444,7 +447,7 @@ class PluginLoader:
|
|||
deprecation = routing_metadata.get('deprecation', None)
|
||||
|
||||
# this will no-op if there's no deprecation metadata for this plugin
|
||||
plugin_load_context.record_deprecation(fq_name, deprecation)
|
||||
plugin_load_context.record_deprecation(fq_name, deprecation, acr.collection)
|
||||
|
||||
tombstone = routing_metadata.get('tombstone', None)
|
||||
|
||||
|
@ -453,12 +456,12 @@ class PluginLoader:
|
|||
removal_date = tombstone.get('removal_date')
|
||||
removal_version = tombstone.get('removal_version')
|
||||
if removal_date:
|
||||
removed_msg = '{0} was removed on {1}'.format(fq_name, removal_date)
|
||||
removed_msg = '{0} was removed from {2} on {1}'.format(fq_name, removal_date, acr.collection)
|
||||
removal_version = None
|
||||
elif removal_version:
|
||||
removed_msg = '{0} was removed in version {1}'.format(fq_name, removal_version)
|
||||
removed_msg = '{0} was removed in version {1} of {2}'.format(fq_name, removal_version, acr.collection)
|
||||
else:
|
||||
removed_msg = '{0} was removed in a previous release'.format(fq_name)
|
||||
removed_msg = '{0} was removed in a previous release of {1}'.format(fq_name, acr.collection)
|
||||
plugin_load_context.removal_date = removal_date
|
||||
plugin_load_context.removal_version = removal_version
|
||||
plugin_load_context.resolved = True
|
||||
|
|
|
@ -71,7 +71,7 @@ def SharedPluginLoaderObj():
|
|||
'''This only exists for backwards compat, do not use.
|
||||
'''
|
||||
display.deprecated('SharedPluginLoaderObj is deprecated, please directly use ansible.plugins.loader',
|
||||
version='2.11')
|
||||
version='ansible.builtin:2.11')
|
||||
return plugin_loader
|
||||
|
||||
|
||||
|
@ -905,7 +905,7 @@ class StrategyBase:
|
|||
"Mixing tag specify styles is prohibited for whole import hierarchy, not only for single import statement",
|
||||
obj=included_file._task._ds)
|
||||
display.deprecated("You should not specify tags in the include parameters. All tags should be specified using the task-level option",
|
||||
version='2.12')
|
||||
version='ansible.builtin:2.12')
|
||||
included_file._task.tags = tags
|
||||
|
||||
block_list = load_list_of_blocks(
|
||||
|
|
|
@ -574,7 +574,7 @@ class Templar:
|
|||
def set_available_variables(self, variables):
|
||||
display.deprecated(
|
||||
'set_available_variables is being deprecated. Use "@available_variables.setter" instead.',
|
||||
version='2.13'
|
||||
version='ansible.builtin:2.13'
|
||||
)
|
||||
self.available_variables = variables
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import locale
|
|||
import logging
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
|
@ -36,8 +37,8 @@ from termios import TIOCGWINSZ
|
|||
|
||||
from ansible import constants as C
|
||||
from ansible.errors import AnsibleError, AnsibleAssertionError
|
||||
from ansible.module_utils._text import to_bytes, to_text
|
||||
from ansible.module_utils.six import with_metaclass
|
||||
from ansible.module_utils._text import to_bytes, to_text, to_native
|
||||
from ansible.module_utils.six import with_metaclass, string_types
|
||||
from ansible.utils.color import stringc
|
||||
from ansible.utils.singleton import Singleton
|
||||
from ansible.utils.unsafe_proxy import wrap_var
|
||||
|
@ -50,6 +51,9 @@ except NameError:
|
|||
pass
|
||||
|
||||
|
||||
TAGGED_VERSION_RE = re.compile('^([^.]+.[^.]+):(.*)$')
|
||||
|
||||
|
||||
class FilterBlackList(logging.Filter):
|
||||
def __init__(self, blacklist):
|
||||
self.blacklist = [logging.Filter(name) for name in blacklist]
|
||||
|
@ -258,9 +262,34 @@ class Display(with_metaclass(Singleton, object)):
|
|||
|
||||
if not removed:
|
||||
if date:
|
||||
new_msg = "[DEPRECATION WARNING]: %s. This feature will be removed in a release after %s." % (msg, date)
|
||||
m = None
|
||||
if isinstance(date, string_types):
|
||||
version = to_native(date)
|
||||
m = TAGGED_VERSION_RE.match(date)
|
||||
if m:
|
||||
collection = m.group(1)
|
||||
date = m.group(2)
|
||||
if collection == 'ansible.builtin':
|
||||
collection = 'Ansible-base'
|
||||
new_msg = "[DEPRECATION WARNING]: %s. This feature will be removed in a release of %s after %s." % (
|
||||
msg, collection, date)
|
||||
else:
|
||||
new_msg = "[DEPRECATION WARNING]: %s. This feature will be removed in a release after %s." % (
|
||||
msg, date)
|
||||
elif version:
|
||||
new_msg = "[DEPRECATION WARNING]: %s. This feature will be removed in version %s." % (msg, version)
|
||||
m = None
|
||||
if isinstance(version, string_types):
|
||||
version = to_native(version)
|
||||
m = TAGGED_VERSION_RE.match(version)
|
||||
if m:
|
||||
collection = m.group(1)
|
||||
version = m.group(2)
|
||||
if collection == 'ansible.builtin':
|
||||
collection = 'Ansible-base'
|
||||
new_msg = "[DEPRECATION WARNING]: %s. This feature will be removed in version %s of %s." % (msg, version,
|
||||
collection)
|
||||
else:
|
||||
new_msg = "[DEPRECATION WARNING]: %s. This feature will be removed in version %s." % (msg, version)
|
||||
else:
|
||||
new_msg = "[DEPRECATION WARNING]: %s. This feature will be removed in a future release." % (msg)
|
||||
new_msg = new_msg + " Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.\n\n"
|
||||
|
|
|
@ -40,7 +40,70 @@ def merge_fragment(target, source):
|
|||
target[key] = value
|
||||
|
||||
|
||||
def add_fragments(doc, filename, fragment_loader):
|
||||
def _process_versions_and_dates(fragment, is_module, callback):
|
||||
def process_deprecation(deprecation):
|
||||
if is_module and 'removed_in' in deprecation: # used in module deprecations
|
||||
callback(deprecation, 'removed_in')
|
||||
if 'removed_at_date' in deprecation:
|
||||
callback(deprecation, 'removed_at_date')
|
||||
if not is_module and 'version' in deprecation: # used in plugin option deprecations
|
||||
callback(deprecation, 'version')
|
||||
|
||||
def process_option_specifiers(specifiers):
|
||||
for specifier in specifiers:
|
||||
if 'version_added' in specifier:
|
||||
callback(specifier, 'version_added')
|
||||
if isinstance(specifier.get('deprecated'), dict):
|
||||
process_deprecation(specifier['deprecated'])
|
||||
|
||||
def process_options(options):
|
||||
for option in options.values():
|
||||
if 'version_added' in option:
|
||||
callback(option, 'version_added')
|
||||
if not is_module:
|
||||
if isinstance(option.get('env'), list):
|
||||
process_option_specifiers(option['env'])
|
||||
if isinstance(option.get('ini'), list):
|
||||
process_option_specifiers(option['ini'])
|
||||
if isinstance(option.get('vars'), list):
|
||||
process_option_specifiers(option['vars'])
|
||||
if isinstance(option.get('suboptions'), dict):
|
||||
process_options(option['suboptions'])
|
||||
|
||||
def process_return_values(return_values):
|
||||
for return_value in return_values.values():
|
||||
if 'version_added' in return_value:
|
||||
callback(return_value, 'version_added')
|
||||
if isinstance(return_value.get('contains'), dict):
|
||||
process_return_values(return_value['contains'])
|
||||
|
||||
if 'version_added' in fragment:
|
||||
callback(fragment, 'version_added')
|
||||
if isinstance(fragment.get('deprecated'), dict):
|
||||
process_deprecation(fragment['deprecated'])
|
||||
if isinstance(fragment.get('options'), dict):
|
||||
process_options(fragment['options'])
|
||||
|
||||
|
||||
def tag_versions_and_dates(fragment, prefix, is_module):
|
||||
def tag(options, option):
|
||||
options[option] = '%s%s' % (prefix, options[option])
|
||||
|
||||
_process_versions_and_dates(fragment, is_module, tag)
|
||||
|
||||
|
||||
def untag_versions_and_dates(fragment, prefix, is_module):
|
||||
def untag(options, option):
|
||||
v = options[option]
|
||||
if isinstance(v, string_types):
|
||||
v = to_native(v)
|
||||
if v.startswith(prefix):
|
||||
options[option] = v[len(prefix):]
|
||||
|
||||
_process_versions_and_dates(fragment, is_module, untag)
|
||||
|
||||
|
||||
def add_fragments(doc, filename, fragment_loader, is_module=False):
|
||||
|
||||
fragments = doc.pop('extends_documentation_fragment', [])
|
||||
|
||||
|
@ -80,6 +143,12 @@ def add_fragments(doc, filename, fragment_loader):
|
|||
|
||||
fragment = AnsibleLoader(fragment_yaml, file_name=filename).get_single_data()
|
||||
|
||||
real_collection_name = 'ansible.builtin'
|
||||
real_fragment_name = getattr(fragment_class, '_load_name')
|
||||
if real_fragment_name.startswith('ansible_collections.'):
|
||||
real_collection_name = '.'.join(real_fragment_name.split('.')[1:3])
|
||||
tag_versions_and_dates(fragment, '%s:' % (real_collection_name, ), is_module=is_module)
|
||||
|
||||
if 'notes' in fragment:
|
||||
notes = fragment.pop('notes')
|
||||
if notes:
|
||||
|
@ -116,16 +185,20 @@ def add_fragments(doc, filename, fragment_loader):
|
|||
raise AnsibleError('unknown doc_fragment(s) in file {0}: {1}'.format(filename, to_native(', '.join(unknown_fragments))))
|
||||
|
||||
|
||||
def get_docstring(filename, fragment_loader, verbose=False, ignore_errors=False):
|
||||
def get_docstring(filename, fragment_loader, verbose=False, ignore_errors=False, collection_name=None, is_module=False):
|
||||
"""
|
||||
DOCUMENTATION can be extended using documentation fragments loaded by the PluginLoader from the doc_fragments plugins.
|
||||
"""
|
||||
|
||||
data = read_docstring(filename, verbose=verbose, ignore_errors=ignore_errors)
|
||||
|
||||
# add fragments to documentation
|
||||
if data.get('doc', False):
|
||||
add_fragments(data['doc'], filename, fragment_loader=fragment_loader)
|
||||
# tag version_added
|
||||
if collection_name is not None:
|
||||
tag_versions_and_dates(data['doc'], '%s:' % (collection_name, ), is_module=is_module)
|
||||
|
||||
# add fragments to documentation
|
||||
add_fragments(data['doc'], filename, fragment_loader=fragment_loader, is_module=is_module)
|
||||
|
||||
return data['doc'], data['plainexamples'], data['returndocs'], data['metadata']
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ class UnsafeProxy(object):
|
|||
from ansible.utils.display import Display
|
||||
Display().deprecated(
|
||||
'UnsafeProxy is being deprecated. Use wrap_var or AnsibleUnsafeBytes/AnsibleUnsafeText directly instead',
|
||||
version='2.13'
|
||||
version='ansible.builtin:2.13'
|
||||
)
|
||||
# In our usage we should only receive unicode strings.
|
||||
# This conditional and conversion exists to sanity check the values
|
||||
|
|
|
@ -99,7 +99,7 @@ class FactCache(MutableMapping):
|
|||
display.deprecated('Calling FactCache().update(key, value) is deprecated. Use'
|
||||
' FactCache().first_order_merge(key, value) if you want the old'
|
||||
' behaviour or use FactCache().update({key: value}) if you want'
|
||||
' dict-like behaviour.', version='2.12')
|
||||
' dict-like behaviour.', version='ansible.builtin:2.12')
|
||||
return self.first_order_merge(*args)
|
||||
|
||||
elif len(args) == 1:
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
- get_element_attribute_wrong.matches[0]['rating']['subjective'] == 'true'
|
||||
- get_element_attribute_wrong.deprecations is defined
|
||||
- get_element_attribute_wrong.deprecations[0].msg == "Parameter 'attribute=subjective' is ignored when using 'content=attribute' only 'xpath' is used. Please remove entry."
|
||||
- get_element_attribute_wrong.deprecations[0].version == '2.12'
|
||||
- get_element_attribute_wrong.deprecations[0].version == 'ansible.builtin:2.12'
|
||||
|
||||
- name: Get element text
|
||||
xml:
|
||||
|
|
|
@ -35,7 +35,7 @@ MSGS = {
|
|||
"Display.deprecated or AnsibleModule.deprecate",
|
||||
"ansible-invalid-deprecated-version",
|
||||
"Used when a call to Display.deprecated specifies an invalid "
|
||||
"Ansible version number",
|
||||
"tagged Ansible version number",
|
||||
{'minversion': (2, 6)}),
|
||||
'E9504': ("Deprecated version (%r) found in call to Display.deprecated "
|
||||
"or AnsibleModule.deprecate",
|
||||
|
@ -48,30 +48,59 @@ MSGS = {
|
|||
"Display.deprecated or AnsibleModule.deprecate",
|
||||
"collection-invalid-deprecated-version",
|
||||
"Used when a call to Display.deprecated specifies an invalid "
|
||||
"collection version number",
|
||||
"tagged collection version number",
|
||||
{'minversion': (2, 6)}),
|
||||
'E9506': ("Expired date (%r) found in call to Display.deprecated "
|
||||
'E9506': ("Invalid tagged version (%r) found in call to "
|
||||
"Display.deprecated or AnsibleModule.deprecate",
|
||||
"invalid-tagged-version",
|
||||
"Used when a call to Display.deprecated specifies a version "
|
||||
"number which has no collection name tag, for example "
|
||||
"`community.general:1.2.3` or `ansible.builtin:2.10`",
|
||||
{'minversion': (2, 6)}),
|
||||
'E9507': ("Version tag for wrong collection (%r) found in call to "
|
||||
"Display.deprecated or AnsibleModule.deprecate",
|
||||
"wrong-collection-deprecated-version-tag",
|
||||
"Deprecation versions must be prefixed with the name of this "
|
||||
"collection (`ansible.builtin:` for Ansible-base)",
|
||||
{'minversion': (2, 6)}),
|
||||
'E9508': ("Expired date (%r) found in call to Display.deprecated "
|
||||
"or AnsibleModule.deprecate",
|
||||
"ansible-deprecated-date",
|
||||
"Used when a call to Display.deprecated specifies a date "
|
||||
"before today",
|
||||
{'minversion': (2, 6)}),
|
||||
'E9507': ("Invalid deprecated date (%r) found in call to "
|
||||
'E9509': ("Invalid deprecated date (%r) found in call to "
|
||||
"Display.deprecated or AnsibleModule.deprecate",
|
||||
"ansible-invalid-deprecated-date",
|
||||
"Used when a call to Display.deprecated specifies an invalid "
|
||||
"date. It must be a string in format YYYY-MM-DD (ISO 8601)",
|
||||
"date. It must be a string in format `namespace.name:YYYY-MM-DD` "
|
||||
"(collection identifier followed by ISO 8601)",
|
||||
{'minversion': (2, 6)}),
|
||||
'E9508': ("Both version and date found in call to "
|
||||
'E9510': ("Both version and date found in call to "
|
||||
"Display.deprecated or AnsibleModule.deprecate",
|
||||
"ansible-deprecated-both-version-and-date",
|
||||
"Only one of version and date must be specified",
|
||||
{'minversion': (2, 6)}),
|
||||
'E9511': ("Invalid tagged date (%r) found in call to "
|
||||
"Display.deprecated or AnsibleModule.deprecate",
|
||||
"invalid-tagged-date",
|
||||
"Used when a call to Display.deprecated specifies a date "
|
||||
"which has no collection name tag, for example "
|
||||
"`community.general:2020-01-01` or `ansible.builtin:2020-12-31`",
|
||||
{'minversion': (2, 6)}),
|
||||
'E9512': ("Date tag for wrong collection (%r) found in call to "
|
||||
"Display.deprecated or AnsibleModule.deprecate",
|
||||
"wrong-collection-deprecated-date-tag",
|
||||
"Deprecation dates must be prefixed with the name of this "
|
||||
"collection (`ansible.builtin:` for Ansible-base)",
|
||||
{'minversion': (2, 6)}),
|
||||
}
|
||||
|
||||
|
||||
ANSIBLE_VERSION = LooseVersion('.'.join(ansible_version_raw.split('.')[:3]))
|
||||
|
||||
TAGGED_VERSION_RE = re.compile('^([^.]+.[^.]+):(.*)$')
|
||||
|
||||
|
||||
def _get_expr_name(node):
|
||||
"""Funciton to get either ``attrname`` or ``name`` from ``node.func.expr``
|
||||
|
@ -109,11 +138,11 @@ class AnsibleDeprecatedChecker(BaseChecker):
|
|||
msgs = MSGS
|
||||
|
||||
options = (
|
||||
('is-collection', {
|
||||
'default': False,
|
||||
'type': 'yn',
|
||||
'metavar': '<y_or_n>',
|
||||
'help': 'Whether this is a collection or not.',
|
||||
('collection-name', {
|
||||
'default': None,
|
||||
'type': 'string',
|
||||
'metavar': '<name>',
|
||||
'help': 'The collection\'s name used to check tagged version numbers in deprecations.',
|
||||
}),
|
||||
('collection-version', {
|
||||
'default': None,
|
||||
|
@ -125,17 +154,71 @@ class AnsibleDeprecatedChecker(BaseChecker):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.collection_version = None
|
||||
self.is_collection = False
|
||||
self.version_constructor = LooseVersion
|
||||
self.collection_name = None
|
||||
super(AnsibleDeprecatedChecker, self).__init__(*args, **kwargs)
|
||||
|
||||
def set_option(self, optname, value, action=None, optdict=None):
|
||||
super(AnsibleDeprecatedChecker, self).set_option(optname, value, action, optdict)
|
||||
if optname == 'collection-version' and value is not None:
|
||||
self.version_constructor = SemanticVersion
|
||||
self.collection_version = self.version_constructor(self.config.collection_version)
|
||||
if optname == 'is-collection':
|
||||
self.is_collection = self.config.is_collection
|
||||
self.collection_version = SemanticVersion(self.config.collection_version)
|
||||
if optname == 'collection-name' and value is not None:
|
||||
self.collection_name = self.config.collection_name
|
||||
|
||||
def _check_date(self, node, date):
|
||||
if not isinstance(date, str):
|
||||
self.add_message('invalid-tagged-date', node=node, args=(date,))
|
||||
return
|
||||
|
||||
matcher = TAGGED_VERSION_RE.match(date)
|
||||
if not matcher:
|
||||
self.add_message('invalid-tagged-date', node=node, args=(date,))
|
||||
return
|
||||
|
||||
collection = matcher.group(1)
|
||||
date_str = matcher.group(2)
|
||||
|
||||
if collection != (self.collection_name or 'ansible.builtin'):
|
||||
self.add_message('wrong-collection-deprecated-date-tag', node=node, args=(date,))
|
||||
|
||||
try:
|
||||
if parse_isodate(date_str) < datetime.date.today():
|
||||
self.add_message('ansible-deprecated-date', node=node, args=(date,))
|
||||
except ValueError:
|
||||
self.add_message('ansible-invalid-deprecated-date', node=node, args=(date,))
|
||||
|
||||
def _check_version(self, node, version):
|
||||
if not isinstance(version, str):
|
||||
self.add_message('invalid-tagged-version', node=node, args=(version,))
|
||||
return
|
||||
|
||||
matcher = TAGGED_VERSION_RE.match(version)
|
||||
if not matcher:
|
||||
self.add_message('invalid-tagged-version', node=node, args=(version,))
|
||||
return
|
||||
|
||||
collection = matcher.group(1)
|
||||
version_no = matcher.group(2)
|
||||
|
||||
if collection != (self.collection_name or 'ansible.builtin'):
|
||||
self.add_message('wrong-collection-deprecated-version-tag', node=node, args=(version,))
|
||||
|
||||
if collection == 'ansible.builtin':
|
||||
# Ansible-base
|
||||
try:
|
||||
loose_version = LooseVersion(str(version_no))
|
||||
if ANSIBLE_VERSION >= loose_version:
|
||||
self.add_message('ansible-deprecated-version', node=node, args=(version,))
|
||||
except ValueError:
|
||||
self.add_message('ansible-invalid-deprecated-version', node=node, args=(version,))
|
||||
else:
|
||||
# Collections
|
||||
try:
|
||||
semantic_version = SemanticVersion(version_no)
|
||||
if collection == self.collection_name and self.collection_version is not None:
|
||||
if self.collection_version >= semantic_version:
|
||||
self.add_message('collection-deprecated-version', node=node, args=(version,))
|
||||
except ValueError:
|
||||
self.add_message('collection-invalid-deprecated-version', node=node, args=(version,))
|
||||
|
||||
@check_messages(*(MSGS.keys()))
|
||||
def visit_call(self, node):
|
||||
|
@ -169,26 +252,11 @@ class AnsibleDeprecatedChecker(BaseChecker):
|
|||
self.add_message('ansible-deprecated-both-version-and-date', node=node)
|
||||
return
|
||||
|
||||
if version:
|
||||
try:
|
||||
loose_version = self.version_constructor(str(version))
|
||||
if self.is_collection and self.collection_version is not None:
|
||||
if self.collection_version >= loose_version:
|
||||
self.add_message('collection-deprecated-version', node=node, args=(version,))
|
||||
if not self.is_collection and ANSIBLE_VERSION >= loose_version:
|
||||
self.add_message('ansible-deprecated-version', node=node, args=(version,))
|
||||
except ValueError:
|
||||
if self.is_collection:
|
||||
self.add_message('collection-invalid-deprecated-version', node=node, args=(version,))
|
||||
else:
|
||||
self.add_message('ansible-invalid-deprecated-version', node=node, args=(version,))
|
||||
|
||||
if date:
|
||||
try:
|
||||
if parse_isodate(date) < datetime.date.today():
|
||||
self.add_message('ansible-deprecated-date', node=node, args=(date,))
|
||||
except ValueError:
|
||||
self.add_message('ansible-invalid-deprecated-date', node=node, args=(date,))
|
||||
self._check_date(node, date)
|
||||
|
||||
if version:
|
||||
self._check_version(node, version)
|
||||
except AttributeError:
|
||||
# Not the type of node we are interested in
|
||||
pass
|
||||
|
|
|
@ -41,10 +41,10 @@ import yaml
|
|||
from ansible import __version__ as ansible_version
|
||||
from ansible.executor.module_common import REPLACER_WINDOWS
|
||||
from ansible.module_utils.common._collections_compat import Mapping
|
||||
from ansible.module_utils._text import to_bytes
|
||||
from ansible.module_utils._text import to_bytes, to_native
|
||||
from ansible.plugins.loader import fragment_loader
|
||||
from ansible.utils.collection_loader._collection_finder import _AnsibleCollectionFinder
|
||||
from ansible.utils.plugin_docs import BLACKLIST, add_fragments, get_docstring
|
||||
from ansible.utils.plugin_docs import BLACKLIST, tag_versions_and_dates, add_fragments, get_docstring
|
||||
from ansible.utils.version import SemanticVersion
|
||||
|
||||
from .module_args import AnsibleModuleImportError, AnsibleModuleNotInitialized, get_argument_spec
|
||||
|
@ -258,9 +258,12 @@ class ModuleValidator(Validator):
|
|||
self.StrictVersion = StrictVersion
|
||||
|
||||
self.collection = collection
|
||||
self.collection_name = 'ansible.builtin'
|
||||
if self.collection:
|
||||
self.Version = SemanticVersion
|
||||
self.StrictVersion = SemanticVersion
|
||||
collection_namespace_path, collection_name = os.path.split(self.collection)
|
||||
self.collection_name = '%s.%s' % (os.path.basename(collection_namespace_path), collection_name)
|
||||
self.routing = routing
|
||||
self.collection_version = None
|
||||
if collection_version is not None:
|
||||
|
@ -873,6 +876,8 @@ class ModuleValidator(Validator):
|
|||
for error in errors:
|
||||
path = [str(p) for p in error.path]
|
||||
|
||||
local_error_code = getattr(error, 'ansible_error_code', error_code)
|
||||
|
||||
if isinstance(error.data, dict):
|
||||
error_message = humanize_error(error.data, error)
|
||||
else:
|
||||
|
@ -885,10 +890,28 @@ class ModuleValidator(Validator):
|
|||
|
||||
self.reporter.error(
|
||||
path=self.object_path,
|
||||
code=error_code,
|
||||
code=local_error_code,
|
||||
msg='%s: %s' % (combined_path, error_message)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _split_tagged_version(version_str):
|
||||
if not isinstance(version_str, string_types):
|
||||
raise ValueError('Tagged version must be string')
|
||||
version_str = to_native(version_str)
|
||||
if ':' not in version_str:
|
||||
raise ValueError('Tagged version must have ":"')
|
||||
return version_str.split(':', 1)
|
||||
|
||||
@staticmethod
|
||||
def _extract_version_from_tag_for_msg(version_str):
|
||||
if not isinstance(version_str, string_types):
|
||||
return version_str
|
||||
version_str = to_native(version_str)
|
||||
if ':' not in version_str:
|
||||
return version_str
|
||||
return version_str.split(':', 1)[1]
|
||||
|
||||
def _validate_docs(self):
|
||||
doc_info = self._get_docs()
|
||||
doc = None
|
||||
|
@ -966,6 +989,8 @@ class ModuleValidator(Validator):
|
|||
doc_info['DOCUMENTATION']['lineno'],
|
||||
self.name, 'DOCUMENTATION'
|
||||
)
|
||||
if doc:
|
||||
tag_versions_and_dates(doc, '%s:' % (self.collection_name, ), is_module=True)
|
||||
for error in errors:
|
||||
self.reporter.error(
|
||||
path=self.object_path,
|
||||
|
@ -981,7 +1006,8 @@ class ModuleValidator(Validator):
|
|||
missing_fragment = False
|
||||
with CaptureStd():
|
||||
try:
|
||||
get_docstring(self.path, fragment_loader, verbose=True)
|
||||
get_docstring(self.path, fragment_loader, verbose=True,
|
||||
collection_name=self.collection_name, is_module=True)
|
||||
except AssertionError:
|
||||
fragment = doc['extends_documentation_fragment']
|
||||
self.reporter.error(
|
||||
|
@ -1002,7 +1028,7 @@ class ModuleValidator(Validator):
|
|||
)
|
||||
|
||||
if not missing_fragment:
|
||||
add_fragments(doc, self.object_path, fragment_loader=fragment_loader)
|
||||
add_fragments(doc, self.object_path, fragment_loader=fragment_loader, is_module=True)
|
||||
|
||||
if 'options' in doc and doc['options'] is None:
|
||||
self.reporter.error(
|
||||
|
@ -1024,9 +1050,8 @@ class ModuleValidator(Validator):
|
|||
doc,
|
||||
doc_schema(
|
||||
os.readlink(self.object_path).split('.')[0],
|
||||
version_added=not bool(self.collection),
|
||||
deprecated_module=deprecated,
|
||||
for_collection=bool(self.collection),
|
||||
deprecated_module=deprecated,
|
||||
),
|
||||
'DOCUMENTATION',
|
||||
'invalid-documentation',
|
||||
|
@ -1037,9 +1062,8 @@ class ModuleValidator(Validator):
|
|||
doc,
|
||||
doc_schema(
|
||||
self.object_name.split('.')[0],
|
||||
version_added=not bool(self.collection),
|
||||
deprecated_module=deprecated,
|
||||
for_collection=bool(self.collection),
|
||||
deprecated_module=deprecated,
|
||||
),
|
||||
'DOCUMENTATION',
|
||||
'invalid-documentation',
|
||||
|
@ -1088,7 +1112,8 @@ class ModuleValidator(Validator):
|
|||
data, errors, traces = parse_yaml(doc_info['RETURN']['value'],
|
||||
doc_info['RETURN']['lineno'],
|
||||
self.name, 'RETURN')
|
||||
self._validate_docs_schema(data, return_schema, 'RETURN', 'return-syntax-error')
|
||||
self._validate_docs_schema(data, return_schema(for_collection=bool(self.collection)),
|
||||
'RETURN', 'return-syntax-error')
|
||||
|
||||
for error in errors:
|
||||
self.reporter.error(
|
||||
|
@ -1142,8 +1167,10 @@ class ModuleValidator(Validator):
|
|||
# Make sure they give the same version or date.
|
||||
routing_date = routing_deprecation.get('removal_date')
|
||||
routing_version = routing_deprecation.get('removal_version')
|
||||
documentation_date = doc_deprecation.get('removed_at_date')
|
||||
documentation_version = doc_deprecation.get('removed_in')
|
||||
# The versions and dates in the module documentation are auto-tagged, so remove the tag
|
||||
# to make comparison possible and to avoid confusing the user.
|
||||
documentation_date = self._extract_version_from_tag_for_msg(doc_deprecation.get('removed_at_date'))
|
||||
documentation_version = self._extract_version_from_tag_for_msg(doc_deprecation.get('removed_in'))
|
||||
if routing_date != documentation_date:
|
||||
self.reporter.error(
|
||||
path=self.object_path,
|
||||
|
@ -1166,23 +1193,26 @@ class ModuleValidator(Validator):
|
|||
def _check_version_added(self, doc, existing_doc):
|
||||
version_added_raw = doc.get('version_added')
|
||||
try:
|
||||
version_added = self.StrictVersion(str(doc.get('version_added', '0.0') or '0.0'))
|
||||
version_added = self.StrictVersion(self._extract_version_from_tag_for_msg(str(doc.get('version_added', '0.0') or '0.0')))
|
||||
except ValueError:
|
||||
version_added = doc.get('version_added', '0.0')
|
||||
if self._is_new_module() or version_added != 'historical':
|
||||
self.reporter.error(
|
||||
path=self.object_path,
|
||||
code='module-invalid-version-added',
|
||||
msg='version_added is not a valid version number: %r' % version_added
|
||||
)
|
||||
if self._is_new_module() or version_added != 'ansible.builtin:historical':
|
||||
# already reported during schema validation, except:
|
||||
if version_added == 'ansible.builtin:historical':
|
||||
self.reporter.error(
|
||||
path=self.object_path,
|
||||
code='module-invalid-version-added',
|
||||
msg='version_added is not a valid version number: %r' % 'historical'
|
||||
)
|
||||
return
|
||||
|
||||
if existing_doc and str(version_added_raw) != str(existing_doc.get('version_added')):
|
||||
self.reporter.error(
|
||||
path=self.object_path,
|
||||
code='module-incorrect-version-added',
|
||||
msg='version_added should be %r. Currently %r' % (existing_doc.get('version_added'),
|
||||
version_added_raw)
|
||||
msg='version_added should be %r. Currently %r' % (
|
||||
self._extract_version_from_tag_for_msg(existing_doc.get('version_added')),
|
||||
self._extract_version_from_tag_for_msg(version_added_raw))
|
||||
)
|
||||
|
||||
if not self._is_new_module():
|
||||
|
@ -1196,10 +1226,11 @@ class ModuleValidator(Validator):
|
|||
self.reporter.error(
|
||||
path=self.object_path,
|
||||
code='module-incorrect-version-added',
|
||||
msg='version_added should be %r. Currently %r' % (should_be, version_added_raw)
|
||||
msg='version_added should be %r. Currently %r' % (
|
||||
should_be, self._extract_version_from_tag_for_msg(version_added_raw))
|
||||
)
|
||||
|
||||
def _validate_ansible_module_call(self, docs):
|
||||
def _validate_ansible_module_call(self, docs, dates_tagged=True):
|
||||
try:
|
||||
spec, args, kwargs = get_argument_spec(self.path, self.collection)
|
||||
except AnsibleModuleNotInitialized:
|
||||
|
@ -1221,7 +1252,9 @@ class ModuleValidator(Validator):
|
|||
)
|
||||
return
|
||||
|
||||
self._validate_docs_schema(kwargs, ansible_module_kwargs_schema(), 'AnsibleModule', 'invalid-ansiblemodule-schema')
|
||||
self._validate_docs_schema(kwargs, ansible_module_kwargs_schema(for_collection=bool(self.collection),
|
||||
dates_tagged=dates_tagged),
|
||||
'AnsibleModule', 'invalid-ansiblemodule-schema')
|
||||
|
||||
self._validate_argument_spec(docs, spec, kwargs)
|
||||
|
||||
|
@ -1433,7 +1466,7 @@ class ModuleValidator(Validator):
|
|||
|
||||
try:
|
||||
if not context:
|
||||
add_fragments(docs, self.object_path, fragment_loader=fragment_loader)
|
||||
add_fragments(docs, self.object_path, fragment_loader=fragment_loader, is_module=True)
|
||||
except Exception:
|
||||
# Cannot merge fragments
|
||||
return
|
||||
|
@ -1497,7 +1530,8 @@ class ModuleValidator(Validator):
|
|||
removed_at_date = data.get('removed_at_date', None)
|
||||
if removed_at_date is not None:
|
||||
try:
|
||||
if parse_isodate(removed_at_date) < datetime.date.today():
|
||||
date = self._extract_version_from_tag_for_msg(removed_at_date)
|
||||
if parse_isodate(date) < datetime.date.today():
|
||||
msg = "Argument '%s' in argument_spec" % arg
|
||||
if context:
|
||||
msg += " found in %s" % " -> ".join(context)
|
||||
|
@ -1517,7 +1551,8 @@ class ModuleValidator(Validator):
|
|||
for deprecated_alias in deprecated_aliases:
|
||||
if 'name' in deprecated_alias and 'date' in deprecated_alias:
|
||||
try:
|
||||
if parse_isodate(deprecated_alias['date']) < datetime.date.today():
|
||||
date = self._extract_version_from_tag_for_msg(deprecated_alias['date'])
|
||||
if parse_isodate(date) < datetime.date.today():
|
||||
msg = "Argument '%s' in argument_spec" % arg
|
||||
if context:
|
||||
msg += " found in %s" % " -> ".join(context)
|
||||
|
@ -1549,7 +1584,8 @@ class ModuleValidator(Validator):
|
|||
removed_in_version = data.get('removed_in_version', None)
|
||||
if removed_in_version is not None:
|
||||
try:
|
||||
if compare_version >= self.Version(str(removed_in_version)):
|
||||
collection_name, removed_in_version = self._split_tagged_version(removed_in_version)
|
||||
if collection_name == self.collection_name and compare_version >= self.Version(str(removed_in_version)):
|
||||
msg = "Argument '%s' in argument_spec" % arg
|
||||
if context:
|
||||
msg += " found in %s" % " -> ".join(context)
|
||||
|
@ -1561,22 +1597,15 @@ class ModuleValidator(Validator):
|
|||
msg=msg,
|
||||
)
|
||||
except ValueError:
|
||||
msg = "Argument '%s' in argument_spec" % arg
|
||||
if context:
|
||||
msg += " found in %s" % " -> ".join(context)
|
||||
msg += " has an invalid removed_in_version '%s'," % removed_in_version
|
||||
msg += " i.e. %s" % version_parser_error
|
||||
self.reporter.error(
|
||||
path=self.object_path,
|
||||
code=code_prefix + '-invalid-version',
|
||||
msg=msg,
|
||||
)
|
||||
# Has been caught in schema validation
|
||||
pass
|
||||
|
||||
if deprecated_aliases is not None:
|
||||
for deprecated_alias in deprecated_aliases:
|
||||
if 'name' in deprecated_alias and 'version' in deprecated_alias:
|
||||
try:
|
||||
if compare_version >= self.Version(str(deprecated_alias['version'])):
|
||||
collection_name, version = self._split_tagged_version(deprecated_alias['version'])
|
||||
if collection_name == self.collection_name and compare_version >= self.Version(str(version)):
|
||||
msg = "Argument '%s' in argument_spec" % arg
|
||||
if context:
|
||||
msg += " found in %s" % " -> ".join(context)
|
||||
|
@ -1589,17 +1618,8 @@ class ModuleValidator(Validator):
|
|||
msg=msg,
|
||||
)
|
||||
except ValueError:
|
||||
msg = "Argument '%s' in argument_spec" % arg
|
||||
if context:
|
||||
msg += " found in %s" % " -> ".join(context)
|
||||
msg += " has aliases '%s' with removal in invalid version '%s'," % (
|
||||
deprecated_alias['name'], deprecated_alias['version'])
|
||||
msg += " i.e. %s" % version_parser_error
|
||||
self.reporter.error(
|
||||
path=self.object_path,
|
||||
code=code_prefix + '-invalid-version',
|
||||
msg=msg,
|
||||
)
|
||||
# Has been caught in schema validation
|
||||
pass
|
||||
|
||||
aliases = data.get('aliases', [])
|
||||
if arg in aliases:
|
||||
|
@ -1978,7 +1998,8 @@ class ModuleValidator(Validator):
|
|||
|
||||
with CaptureStd():
|
||||
try:
|
||||
existing_doc, dummy_examples, dummy_return, existing_metadata = get_docstring(self.base_module, fragment_loader, verbose=True)
|
||||
existing_doc, dummy_examples, dummy_return, existing_metadata = get_docstring(
|
||||
self.base_module, fragment_loader, verbose=True, collection_name=self.collection_name, is_module=True)
|
||||
existing_options = existing_doc.get('options', {}) or {}
|
||||
except AssertionError:
|
||||
fragment = doc['extends_documentation_fragment']
|
||||
|
@ -2031,6 +2052,7 @@ class ModuleValidator(Validator):
|
|||
continue
|
||||
|
||||
if any(name in existing_options for name in names):
|
||||
# The option already existed. Make sure version_added didn't change.
|
||||
for name in names:
|
||||
existing_version = existing_options.get(name, {}).get('version_added')
|
||||
if existing_version:
|
||||
|
@ -2052,19 +2074,7 @@ class ModuleValidator(Validator):
|
|||
str(details.get('version_added', '0.0'))
|
||||
)
|
||||
except ValueError:
|
||||
version_added = details.get('version_added', '0.0')
|
||||
self.reporter.error(
|
||||
path=self.object_path,
|
||||
code='module-invalid-version-added-number',
|
||||
msg=('version_added for new option (%s) '
|
||||
'is not a valid version number: %r' %
|
||||
(option, version_added))
|
||||
)
|
||||
continue
|
||||
except Exception:
|
||||
# If there is any other exception it should have been caught
|
||||
# in schema validation, so we won't duplicate errors by
|
||||
# listing it again
|
||||
# already reported during schema validation
|
||||
continue
|
||||
|
||||
if (strict_ansible_version != mod_version_added and
|
||||
|
@ -2148,7 +2158,16 @@ class ModuleValidator(Validator):
|
|||
pass
|
||||
if 'removed_in' in docs['deprecated']:
|
||||
try:
|
||||
removed_in = self.StrictVersion(str(docs['deprecated']['removed_in']))
|
||||
collection_name, version = self._split_tagged_version(docs['deprecated']['removed_in'])
|
||||
if collection_name != self.collection_name:
|
||||
self.reporter.error(
|
||||
path=self.object_path,
|
||||
code='invalid-module-deprecation-source',
|
||||
msg=('The deprecation version for a module must be added in this collection')
|
||||
)
|
||||
# Treat the module as not to be removed:
|
||||
raise ValueError('')
|
||||
removed_in = self.StrictVersion(str(version))
|
||||
except ValueError:
|
||||
end_of_deprecation_should_be_removed_only = False
|
||||
else:
|
||||
|
@ -2182,7 +2201,8 @@ class ModuleValidator(Validator):
|
|||
if re.search(pattern, self.text) and self.object_name not in self.PS_ARG_VALIDATE_BLACKLIST:
|
||||
with ModuleValidator(docs_path, base_branch=self.base_branch, git_cache=self.git_cache) as docs_mv:
|
||||
docs = docs_mv._validate_docs()[1]
|
||||
self._validate_ansible_module_call(docs)
|
||||
# Don't expect tagged dates!
|
||||
self._validate_ansible_module_call(docs, dates_tagged=False)
|
||||
|
||||
self._check_gpl3_header()
|
||||
if not self._just_docs() and not end_of_deprecation_should_be_removed_only:
|
||||
|
|
|
@ -9,9 +9,13 @@ __metaclass__ = type
|
|||
import datetime
|
||||
import re
|
||||
|
||||
from functools import partial
|
||||
|
||||
from voluptuous import ALLOW_EXTRA, PREVENT_EXTRA, All, Any, Invalid, Length, Required, Schema, Self, ValueInvalid
|
||||
from ansible.module_utils.six import string_types
|
||||
from ansible.module_utils.common.collections import is_iterable
|
||||
from ansible.utils.version import SemanticVersion
|
||||
from distutils.version import StrictVersion
|
||||
|
||||
from .utils import parse_isodate
|
||||
|
||||
|
@ -28,6 +32,86 @@ any_string_types = Any(*string_types)
|
|||
author_line = re.compile(r'^\w.*(\(@([\w-]+)\)|!UNKNOWN)(?![\w.])|^Ansible Core Team$|^Michael DeHaan$')
|
||||
|
||||
|
||||
def _add_ansible_error_code(exception, error_code):
|
||||
setattr(exception, 'ansible_error_code', error_code)
|
||||
return exception
|
||||
|
||||
|
||||
def semantic_version(v, error_code=None):
|
||||
if not isinstance(v, string_types):
|
||||
raise _add_ansible_error_code(Invalid('Semantic version must be a string'), error_code or 'collection-invalid-version')
|
||||
try:
|
||||
SemanticVersion(v)
|
||||
except ValueError as e:
|
||||
raise _add_ansible_error_code(Invalid(str(e)), error_code or 'collection-invalid-version')
|
||||
return v
|
||||
|
||||
|
||||
def ansible_version(v, error_code=None):
|
||||
# Assumes argument is a string or float
|
||||
if 'historical' == v:
|
||||
return v
|
||||
try:
|
||||
StrictVersion(str(v))
|
||||
except ValueError as e:
|
||||
raise _add_ansible_error_code(Invalid(str(e)), error_code or 'ansible-invalid-version')
|
||||
return v
|
||||
|
||||
|
||||
def isodate(v, error_code=None):
|
||||
try:
|
||||
parse_isodate(v)
|
||||
except ValueError as e:
|
||||
raise _add_ansible_error_code(Invalid(str(e)), error_code or 'ansible-invalid-date')
|
||||
return v
|
||||
|
||||
|
||||
TAGGED_VERSION_RE = re.compile('^([^.]+.[^.]+):(.*)$')
|
||||
|
||||
|
||||
def tagged_version(v, error_code=None):
|
||||
if not isinstance(v, string_types):
|
||||
# Should never happen to versions tagged by code
|
||||
raise _add_ansible_error_code(Invalid('Tagged version must be a string'), 'invalid-tagged-version')
|
||||
m = TAGGED_VERSION_RE.match(v)
|
||||
if not m:
|
||||
# Should never happen to versions tagged by code
|
||||
raise _add_ansible_error_code(Invalid('Tagged version does not match format'), 'invalid-tagged-version')
|
||||
collection = m.group(1)
|
||||
version = m.group(2)
|
||||
if collection != 'ansible.builtin':
|
||||
semantic_version(version, error_code=error_code)
|
||||
else:
|
||||
ansible_version(version, error_code=error_code)
|
||||
return v
|
||||
|
||||
|
||||
def tagged_isodate(v, error_code=None):
|
||||
if not isinstance(v, string_types):
|
||||
# Should never happen to dates tagged by code
|
||||
raise _add_ansible_error_code(Invalid('Tagged date must be a string'), 'invalid-tagged-date')
|
||||
m = TAGGED_VERSION_RE.match(v)
|
||||
if not m:
|
||||
# Should never happen to dates tagged by code
|
||||
raise _add_ansible_error_code(Invalid('Tagged date does not match format'), 'invalid-tagged-date')
|
||||
isodate(m.group(2), error_code=error_code)
|
||||
return v
|
||||
|
||||
|
||||
def version(for_collection=False, tagged='never', error_code=None):
|
||||
if tagged == 'always':
|
||||
return Any(partial(tagged_version, error_code=error_code))
|
||||
if for_collection:
|
||||
return Any(partial(semantic_version, error_code=error_code))
|
||||
return All(Any(float, *string_types), partial(ansible_version, error_code=error_code))
|
||||
|
||||
|
||||
def date(tagged='never', error_code=None):
|
||||
if tagged == 'always':
|
||||
return Any(partial(tagged_isodate, error_code=error_code))
|
||||
return Any(isodate)
|
||||
|
||||
|
||||
def is_callable(v):
|
||||
if not callable(v):
|
||||
raise ValueInvalid('not a valid value')
|
||||
|
@ -101,15 +185,8 @@ def options_with_apply_defaults(v):
|
|||
return v
|
||||
|
||||
|
||||
def isodate(v):
|
||||
try:
|
||||
parse_isodate(v)
|
||||
except ValueError as e:
|
||||
raise Invalid(str(e))
|
||||
return v
|
||||
|
||||
|
||||
def argument_spec_schema():
|
||||
def argument_spec_schema(for_collection, dates_tagged=True):
|
||||
dates_tagged = 'always' if dates_tagged else 'never'
|
||||
any_string_types = Any(*string_types)
|
||||
schema = {
|
||||
any_string_types: {
|
||||
|
@ -125,17 +202,17 @@ def argument_spec_schema():
|
|||
'no_log': bool,
|
||||
'aliases': Any(list_string_types, tuple(list_string_types)),
|
||||
'apply_defaults': bool,
|
||||
'removed_in_version': Any(float, *string_types),
|
||||
'removed_at_date': Any(isodate),
|
||||
'removed_in_version': version(for_collection, tagged='always'),
|
||||
'removed_at_date': date(tagged=dates_tagged),
|
||||
'options': Self,
|
||||
'deprecated_aliases': Any([Any(
|
||||
{
|
||||
Required('name'): Any(*string_types),
|
||||
Required('date'): Any(isodate),
|
||||
Required('date'): date(tagged=dates_tagged),
|
||||
},
|
||||
{
|
||||
Required('name'): Any(*string_types),
|
||||
Required('version'): Any(float, *string_types),
|
||||
Required('version'): version(for_collection, tagged='always'),
|
||||
},
|
||||
)]),
|
||||
}
|
||||
|
@ -150,9 +227,9 @@ def argument_spec_schema():
|
|||
return Schema(schemas)
|
||||
|
||||
|
||||
def ansible_module_kwargs_schema():
|
||||
def ansible_module_kwargs_schema(for_collection, dates_tagged=True):
|
||||
schema = {
|
||||
'argument_spec': argument_spec_schema(),
|
||||
'argument_spec': argument_spec_schema(for_collection, dates_tagged=dates_tagged),
|
||||
'bypass_checks': bool,
|
||||
'no_log': bool,
|
||||
'check_invalid_arguments': Any(None, bool),
|
||||
|
@ -172,48 +249,49 @@ json_value = Schema(Any(
|
|||
))
|
||||
|
||||
|
||||
suboption_schema = Schema(
|
||||
{
|
||||
Required('description'): Any(list_string_types, *string_types),
|
||||
'required': bool,
|
||||
'choices': list,
|
||||
'aliases': Any(list_string_types),
|
||||
'version_added': Any(float, *string_types),
|
||||
'default': json_value,
|
||||
# Note: Types are strings, not literal bools, such as True or False
|
||||
'type': Any(None, 'bits', 'bool', 'bytes', 'dict', 'float', 'int', 'json', 'jsonarg', 'list', 'path', 'raw', 'sid', 'str'),
|
||||
# in case of type='list' elements define type of individual item in list
|
||||
'elements': Any(None, 'bits', 'bool', 'bytes', 'dict', 'float', 'int', 'json', 'jsonarg', 'list', 'path', 'raw', 'sid', 'str'),
|
||||
# Recursive suboptions
|
||||
'suboptions': Any(None, *list({str_type: Self} for str_type in string_types)),
|
||||
},
|
||||
extra=PREVENT_EXTRA
|
||||
)
|
||||
def list_dict_option_schema(for_collection):
|
||||
suboption_schema = Schema(
|
||||
{
|
||||
Required('description'): Any(list_string_types, *string_types),
|
||||
'required': bool,
|
||||
'choices': list,
|
||||
'aliases': Any(list_string_types),
|
||||
'version_added': version(for_collection, tagged='always', error_code='option-invalid-version-added'),
|
||||
'default': json_value,
|
||||
# Note: Types are strings, not literal bools, such as True or False
|
||||
'type': Any(None, 'bits', 'bool', 'bytes', 'dict', 'float', 'int', 'json', 'jsonarg', 'list', 'path', 'raw', 'sid', 'str'),
|
||||
# in case of type='list' elements define type of individual item in list
|
||||
'elements': Any(None, 'bits', 'bool', 'bytes', 'dict', 'float', 'int', 'json', 'jsonarg', 'list', 'path', 'raw', 'sid', 'str'),
|
||||
# Recursive suboptions
|
||||
'suboptions': Any(None, *list({str_type: Self} for str_type in string_types)),
|
||||
},
|
||||
extra=PREVENT_EXTRA
|
||||
)
|
||||
|
||||
# This generates list of dicts with keys from string_types and suboption_schema value
|
||||
# for example in Python 3: {str: suboption_schema}
|
||||
list_dict_suboption_schema = [{str_type: suboption_schema} for str_type in string_types]
|
||||
# This generates list of dicts with keys from string_types and suboption_schema value
|
||||
# for example in Python 3: {str: suboption_schema}
|
||||
list_dict_suboption_schema = [{str_type: suboption_schema} for str_type in string_types]
|
||||
|
||||
option_schema = Schema(
|
||||
{
|
||||
Required('description'): Any(list_string_types, *string_types),
|
||||
'required': bool,
|
||||
'choices': list,
|
||||
'aliases': Any(list_string_types),
|
||||
'version_added': Any(float, *string_types),
|
||||
'default': json_value,
|
||||
'suboptions': Any(None, *list_dict_suboption_schema),
|
||||
# Note: Types are strings, not literal bools, such as True or False
|
||||
'type': Any(None, 'bits', 'bool', 'bytes', 'dict', 'float', 'int', 'json', 'jsonarg', 'list', 'path', 'raw', 'sid', 'str'),
|
||||
# in case of type='list' elements define type of individual item in list
|
||||
'elements': Any(None, 'bits', 'bool', 'bytes', 'dict', 'float', 'int', 'json', 'jsonarg', 'list', 'path', 'raw', 'sid', 'str'),
|
||||
},
|
||||
extra=PREVENT_EXTRA
|
||||
)
|
||||
option_schema = Schema(
|
||||
{
|
||||
Required('description'): Any(list_string_types, *string_types),
|
||||
'required': bool,
|
||||
'choices': list,
|
||||
'aliases': Any(list_string_types),
|
||||
'version_added': version(for_collection, tagged='always', error_code='option-invalid-version-added'),
|
||||
'default': json_value,
|
||||
'suboptions': Any(None, *list_dict_suboption_schema),
|
||||
# Note: Types are strings, not literal bools, such as True or False
|
||||
'type': Any(None, 'bits', 'bool', 'bytes', 'dict', 'float', 'int', 'json', 'jsonarg', 'list', 'path', 'raw', 'sid', 'str'),
|
||||
# in case of type='list' elements define type of individual item in list
|
||||
'elements': Any(None, 'bits', 'bool', 'bytes', 'dict', 'float', 'int', 'json', 'jsonarg', 'list', 'path', 'raw', 'sid', 'str'),
|
||||
},
|
||||
extra=PREVENT_EXTRA
|
||||
)
|
||||
|
||||
# This generates list of dicts with keys from string_types and option_schema value
|
||||
# for example in Python 3: {str: option_schema}
|
||||
list_dict_option_schema = [{str_type: option_schema} for str_type in string_types]
|
||||
# This generates list of dicts with keys from string_types and option_schema value
|
||||
# for example in Python 3: {str: option_schema}
|
||||
return [{str_type: option_schema} for str_type in string_types]
|
||||
|
||||
|
||||
def return_contains(v):
|
||||
|
@ -228,51 +306,52 @@ def return_contains(v):
|
|||
return v
|
||||
|
||||
|
||||
return_contains_schema = Any(
|
||||
All(
|
||||
Schema(
|
||||
{
|
||||
Required('description'): Any(list_string_types, *string_types),
|
||||
'returned': Any(*string_types), # only returned on top level
|
||||
Required('type'): Any('bool', 'complex', 'dict', 'float', 'int', 'list', 'str'),
|
||||
'version_added': Any(float, *string_types),
|
||||
'sample': json_value,
|
||||
'example': json_value,
|
||||
'contains': Any(None, *list({str_type: Self} for str_type in string_types)),
|
||||
# in case of type='list' elements define type of individual item in list
|
||||
'elements': Any(None, 'bits', 'bool', 'bytes', 'dict', 'float', 'int', 'json', 'jsonarg', 'list', 'path', 'raw', 'sid', 'str'),
|
||||
}
|
||||
),
|
||||
Schema(return_contains)
|
||||
),
|
||||
Schema(type(None)),
|
||||
)
|
||||
|
||||
# This generates list of dicts with keys from string_types and return_contains_schema value
|
||||
# for example in Python 3: {str: return_contains_schema}
|
||||
list_dict_return_contains_schema = [{str_type: return_contains_schema} for str_type in string_types]
|
||||
|
||||
return_schema = Any(
|
||||
All(
|
||||
Schema(
|
||||
{
|
||||
any_string_types: {
|
||||
def return_schema(for_collection):
|
||||
return_contains_schema = Any(
|
||||
All(
|
||||
Schema(
|
||||
{
|
||||
Required('description'): Any(list_string_types, *string_types),
|
||||
Required('returned'): Any(*string_types),
|
||||
'returned': Any(*string_types), # only returned on top level
|
||||
Required('type'): Any('bool', 'complex', 'dict', 'float', 'int', 'list', 'str'),
|
||||
'version_added': Any(float, *string_types),
|
||||
'version_added': version(for_collection, error_code='return-invalid-version-added'),
|
||||
'sample': json_value,
|
||||
'example': json_value,
|
||||
'contains': Any(None, *list_dict_return_contains_schema),
|
||||
'contains': Any(None, *list({str_type: Self} for str_type in string_types)),
|
||||
# in case of type='list' elements define type of individual item in list
|
||||
'elements': Any(None, 'bits', 'bool', 'bytes', 'dict', 'float', 'int', 'json', 'jsonarg', 'list', 'path', 'raw', 'sid', 'str'),
|
||||
}
|
||||
}
|
||||
),
|
||||
Schema(return_contains)
|
||||
),
|
||||
Schema({any_string_types: return_contains})
|
||||
),
|
||||
Schema(type(None)),
|
||||
)
|
||||
Schema(type(None)),
|
||||
)
|
||||
|
||||
# This generates list of dicts with keys from string_types and return_contains_schema value
|
||||
# for example in Python 3: {str: return_contains_schema}
|
||||
list_dict_return_contains_schema = [{str_type: return_contains_schema} for str_type in string_types]
|
||||
|
||||
return Any(
|
||||
All(
|
||||
Schema(
|
||||
{
|
||||
any_string_types: {
|
||||
Required('description'): Any(list_string_types, *string_types),
|
||||
Required('returned'): Any(*string_types),
|
||||
Required('type'): Any('bool', 'complex', 'dict', 'float', 'int', 'list', 'str'),
|
||||
'version_added': version(for_collection, error_code='return-invalid-version-added'),
|
||||
'sample': json_value,
|
||||
'example': json_value,
|
||||
'contains': Any(None, *list_dict_return_contains_schema),
|
||||
# in case of type='list' elements define type of individual item in list
|
||||
'elements': Any(None, 'bits', 'bool', 'bytes', 'dict', 'float', 'int', 'json', 'jsonarg', 'list', 'path', 'raw', 'sid', 'str'),
|
||||
}
|
||||
}
|
||||
),
|
||||
Schema({any_string_types: return_contains})
|
||||
),
|
||||
Schema(type(None)),
|
||||
)
|
||||
|
||||
|
||||
def deprecation_schema(for_collection):
|
||||
|
@ -283,13 +362,13 @@ def deprecation_schema(for_collection):
|
|||
}
|
||||
|
||||
date_schema = {
|
||||
Required('removed_at_date'): Any(isodate),
|
||||
Required('removed_at_date'): date(tagged='always'),
|
||||
}
|
||||
date_schema.update(main_fields)
|
||||
|
||||
if for_collection:
|
||||
version_schema = {
|
||||
Required('removed_in'): Any(float, *string_types),
|
||||
Required('removed_in'): version(for_collection, tagged='always'),
|
||||
}
|
||||
else:
|
||||
version_schema = {
|
||||
|
@ -297,13 +376,16 @@ def deprecation_schema(for_collection):
|
|||
# Deprecation cycle changed at 2.4 (though not retroactively)
|
||||
# 2.3 -> removed_in: "2.5" + n for docs stub
|
||||
# 2.4 -> removed_in: "2.8" + n for docs stub
|
||||
Required('removed_in'): Any("2.2", "2.3", "2.4", "2.5", "2.6", "2.8", "2.9", "2.10", "2.11", "2.12", "2.13", "2.14"),
|
||||
Required('removed_in'): Any(
|
||||
"ansible.builtin:2.2", "ansible.builtin:2.3", "ansible.builtin:2.4", "ansible.builtin:2.5",
|
||||
"ansible.builtin:2.6", "ansible.builtin:2.8", "ansible.builtin:2.9", "ansible.builtin:2.10",
|
||||
"ansible.builtin:2.11", "ansible.builtin:2.12", "ansible.builtin:2.13", "ansible.builtin:2.14"),
|
||||
}
|
||||
version_schema.update(main_fields)
|
||||
|
||||
return Any(
|
||||
Schema(date_schema, extra=PREVENT_EXTRA),
|
||||
Schema(version_schema, extra=PREVENT_EXTRA),
|
||||
Schema(date_schema, extra=PREVENT_EXTRA),
|
||||
)
|
||||
|
||||
|
||||
|
@ -318,7 +400,7 @@ def author(value):
|
|||
raise Invalid("Invalid author")
|
||||
|
||||
|
||||
def doc_schema(module_name, version_added=True, deprecated_module=False, for_collection=False):
|
||||
def doc_schema(module_name, for_collection=False, deprecated_module=False):
|
||||
|
||||
if module_name.startswith('_'):
|
||||
module_name = module_name[1:]
|
||||
|
@ -332,15 +414,17 @@ def doc_schema(module_name, version_added=True, deprecated_module=False, for_col
|
|||
'seealso': Any(None, seealso_schema),
|
||||
'requirements': list_string_types,
|
||||
'todo': Any(None, list_string_types, *string_types),
|
||||
'options': Any(None, *list_dict_option_schema),
|
||||
'options': Any(None, *list_dict_option_schema(for_collection)),
|
||||
'extends_documentation_fragment': Any(list_string_types, *string_types)
|
||||
}
|
||||
|
||||
if version_added:
|
||||
doc_schema_dict[Required('version_added')] = Any(float, *string_types)
|
||||
else:
|
||||
if for_collection:
|
||||
# Optional
|
||||
doc_schema_dict['version_added'] = Any(float, *string_types)
|
||||
doc_schema_dict['version_added'] = version(
|
||||
for_collection=True, tagged='always', error_code='module-invalid-version-added')
|
||||
else:
|
||||
doc_schema_dict[Required('version_added')] = version(
|
||||
for_collection=False, tagged='always', error_code='module-invalid-version-added')
|
||||
|
||||
if deprecated_module:
|
||||
deprecation_required_scheme = {
|
||||
|
|
|
@ -239,12 +239,10 @@ class PylintTest(SanitySingleVersion):
|
|||
] + paths
|
||||
|
||||
if data_context().content.collection:
|
||||
cmd.extend(['--is-collection', 'yes'])
|
||||
cmd.extend(['--collection-name', data_context().content.collection.full_name])
|
||||
|
||||
if collection_detail and collection_detail.version:
|
||||
cmd.extend(['--collection-version', collection_detail.version])
|
||||
else:
|
||||
cmd.extend(['--enable', 'ansible-deprecated-version'])
|
||||
|
||||
append_python_path = [plugin_dir]
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ def main():
|
|||
|
||||
module = AnsibleAWSModule(argument_spec=argument_spec)
|
||||
if module._name == 'aws_az_facts':
|
||||
module.deprecate("The 'aws_az_facts' module has been renamed to 'aws_az_info'", version='2.14')
|
||||
module.deprecate("The 'aws_az_facts' module has been renamed to 'aws_az_info'", version='ansible.builtin:2.14')
|
||||
|
||||
connection = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff())
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ class AzureRMFunctionAppInfo(AzureRMModuleBase):
|
|||
|
||||
is_old_facts = self.module._name == 'azure_rm_functionapp_facts'
|
||||
if is_old_facts:
|
||||
self.module.deprecate("The 'azure_rm_functionapp_facts' module has been renamed to 'azure_rm_functionapp_info'", version='2.13')
|
||||
self.module.deprecate("The 'azure_rm_functionapp_facts' module has been renamed to 'azure_rm_functionapp_info'", version='ansible.builtin:2.13')
|
||||
|
||||
for key in self.module_arg_spec:
|
||||
setattr(self, key, kwargs[key])
|
||||
|
|
|
@ -139,7 +139,8 @@ class AzureRMMariaDbConfigurationInfo(AzureRMModuleBase):
|
|||
def exec_module(self, **kwargs):
|
||||
is_old_facts = self.module._name == 'azure_rm_mariadbconfiguration_facts'
|
||||
if is_old_facts:
|
||||
self.module.deprecate("The 'azure_rm_mariadbconfiguration_facts' module has been renamed to 'azure_rm_mariadbconfiguration_info'", version='2.13')
|
||||
self.module.deprecate("The 'azure_rm_mariadbconfiguration_facts' module has been renamed to 'azure_rm_mariadbconfiguration_info'",
|
||||
version='ansible.builtin:2.13')
|
||||
|
||||
for key in self.module_arg_spec:
|
||||
setattr(self, key, kwargs[key])
|
||||
|
|
|
@ -145,7 +145,8 @@ class AzureRMMariaDbDatabaseInfo(AzureRMModuleBase):
|
|||
def exec_module(self, **kwargs):
|
||||
is_old_facts = self.module._name == 'azure_rm_mariadbdatabase_facts'
|
||||
if is_old_facts:
|
||||
self.module.deprecate("The 'azure_rm_mariadbdatabase_facts' module has been renamed to 'azure_rm_mariadbdatabase_info'", version='2.13')
|
||||
self.module.deprecate("The 'azure_rm_mariadbdatabase_facts' module has been renamed to 'azure_rm_mariadbdatabase_info'",
|
||||
version='ansible.builtin:2.13')
|
||||
|
||||
for key in self.module_arg_spec:
|
||||
setattr(self, key, kwargs[key])
|
||||
|
|
|
@ -141,7 +141,8 @@ class AzureRMMariaDbFirewallRuleInfo(AzureRMModuleBase):
|
|||
def exec_module(self, **kwargs):
|
||||
is_old_facts = self.module._name == 'azure_rm_mariadbfirewallrule_facts'
|
||||
if is_old_facts:
|
||||
self.module.deprecate("The 'azure_rm_mariadbfirewallrule_facts' module has been renamed to 'azure_rm_mariadbfirewallrule_info'", version='2.13')
|
||||
self.module.deprecate("The 'azure_rm_mariadbfirewallrule_facts' module has been renamed to 'azure_rm_mariadbfirewallrule_info'",
|
||||
version='ansible.builtin:2.13')
|
||||
|
||||
for key in self.module_arg_spec:
|
||||
setattr(self, key, kwargs[key])
|
||||
|
|
|
@ -193,7 +193,7 @@ class AzureRMMariaDbServerInfo(AzureRMModuleBase):
|
|||
def exec_module(self, **kwargs):
|
||||
is_old_facts = self.module._name == 'azure_rm_mariadbserver_facts'
|
||||
if is_old_facts:
|
||||
self.module.deprecate("The 'azure_rm_mariadbserver_facts' module has been renamed to 'azure_rm_mariadbserver_info'", version='2.13')
|
||||
self.module.deprecate("The 'azure_rm_mariadbserver_facts' module has been renamed to 'azure_rm_mariadbserver_info'", version='ansible.builtin:2.13')
|
||||
|
||||
for key in self.module_arg_spec:
|
||||
setattr(self, key, kwargs[key])
|
||||
|
|
|
@ -336,7 +336,7 @@ class AzureRMResourceInfo(AzureRMModuleBase):
|
|||
def exec_module(self, **kwargs):
|
||||
is_old_facts = self.module._name == 'azure_rm_resource_facts'
|
||||
if is_old_facts:
|
||||
self.module.deprecate("The 'azure_rm_resource_facts' module has been renamed to 'azure_rm_resource_info'", version='2.13')
|
||||
self.module.deprecate("The 'azure_rm_resource_facts' module has been renamed to 'azure_rm_resource_info'", version='ansible.builtin:2.13')
|
||||
|
||||
for key in self.module_arg_spec:
|
||||
setattr(self, key, kwargs[key])
|
||||
|
|
|
@ -265,7 +265,7 @@ class AzureRMWebAppInfo(AzureRMModuleBase):
|
|||
def exec_module(self, **kwargs):
|
||||
is_old_facts = self.module._name == 'azure_rm_webapp_facts'
|
||||
if is_old_facts:
|
||||
self.module.deprecate("The 'azure_rm_webapp_facts' module has been renamed to 'azure_rm_webapp_info'", version='2.13')
|
||||
self.module.deprecate("The 'azure_rm_webapp_facts' module has been renamed to 'azure_rm_webapp_info'", version='ansible.builtin:2.13')
|
||||
|
||||
for key in self.module_arg_spec:
|
||||
setattr(self, key, kwargs[key])
|
||||
|
|
|
@ -302,7 +302,7 @@ def main():
|
|||
is_old_facts = module._name == 'cloudformation_facts'
|
||||
if is_old_facts:
|
||||
module.deprecate("The 'cloudformation_facts' module has been renamed to 'cloudformation_info', "
|
||||
"and the renamed one no longer returns ansible_facts", version='2.13')
|
||||
"and the renamed one no longer returns ansible_facts", version='ansible.builtin:2.13')
|
||||
|
||||
service_mgr = CloudFormationServiceManager(module)
|
||||
|
||||
|
|
|
@ -450,7 +450,7 @@ class SwarmManager(DockerBaseClass):
|
|||
if self.state == 'inspect':
|
||||
self.client.module.deprecate(
|
||||
"The 'inspect' state is deprecated, please use 'docker_swarm_info' to inspect swarm cluster",
|
||||
version='2.12')
|
||||
version='ansible.builtin:2.12')
|
||||
|
||||
choice_map.get(self.state)()
|
||||
|
||||
|
|
|
@ -1695,7 +1695,7 @@ def main():
|
|||
module.deprecate(
|
||||
msg='Support for passing both group and group_id has been deprecated. '
|
||||
'Currently group_id is ignored, in future passing both will result in an error',
|
||||
version='2.14')
|
||||
version='ansible.builtin:2.14')
|
||||
|
||||
if not HAS_BOTO:
|
||||
module.fail_json(msg='boto required for this module')
|
||||
|
|
|
@ -270,7 +270,7 @@ def main():
|
|||
|
||||
module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True)
|
||||
if module._module._name == 'ec2_ami_facts':
|
||||
module._module.deprecate("The 'ec2_ami_facts' module has been renamed to 'ec2_ami_info'", version='2.13')
|
||||
module._module.deprecate("The 'ec2_ami_facts' module has been renamed to 'ec2_ami_info'", version='ansible.builtin:2.13')
|
||||
|
||||
ec2_client = module.client('ec2')
|
||||
|
||||
|
|
|
@ -259,7 +259,7 @@ def main():
|
|||
|
||||
module = AnsibleModule(argument_spec=argument_spec)
|
||||
if module._name == 'ec2_eni_facts':
|
||||
module.deprecate("The 'ec2_eni_facts' module has been renamed to 'ec2_eni_info'", version='2.13')
|
||||
module.deprecate("The 'ec2_eni_facts' module has been renamed to 'ec2_eni_info'", version='ansible.builtin:2.13')
|
||||
|
||||
if not HAS_BOTO3:
|
||||
module.fail_json(msg='boto3 required for this module')
|
||||
|
|
|
@ -552,7 +552,7 @@ def main():
|
|||
supports_check_mode=True
|
||||
)
|
||||
if module._name == 'ec2_instance_facts':
|
||||
module.deprecate("The 'ec2_instance_facts' module has been renamed to 'ec2_instance_info'", version='2.13')
|
||||
module.deprecate("The 'ec2_instance_facts' module has been renamed to 'ec2_instance_info'", version='ansible.builtin:2.13')
|
||||
|
||||
if not HAS_BOTO3:
|
||||
module.fail_json(msg='boto3 required for this module')
|
||||
|
|
|
@ -637,7 +637,7 @@ def main():
|
|||
|
||||
if module.params.get('purge_policies') is None:
|
||||
module.deprecate('In Ansible 2.14 the default value of purge_policies will change from true to false.'
|
||||
' To maintain the existing behaviour explicity set purge_policies=true', version='2.14')
|
||||
' To maintain the existing behaviour explicity set purge_policies=true', version='ansible.builtin:2.14')
|
||||
|
||||
if module.params.get('boundary'):
|
||||
if module.params.get('create_instance_profile'):
|
||||
|
|
|
@ -142,7 +142,7 @@ class KubernetesInfoModule(KubernetesAnsibleModule):
|
|||
supports_check_mode=True,
|
||||
**kwargs)
|
||||
if self._name == 'k8s_facts':
|
||||
self.deprecate("The 'k8s_facts' module has been renamed to 'k8s_info'", version='2.13')
|
||||
self.deprecate("The 'k8s_facts' module has been renamed to 'k8s_info'", version='ansible.builtin:2.13')
|
||||
|
||||
def execute_module(self):
|
||||
self.client = self.get_api_client()
|
||||
|
|
|
@ -2654,7 +2654,7 @@ def main():
|
|||
if provider == 'assertonly':
|
||||
module.deprecate("The 'assertonly' provider is deprecated; please see the examples of "
|
||||
"the 'openssl_certificate' module on how to replace it with other modules",
|
||||
version='2.13')
|
||||
version='ansible.builtin:2.13')
|
||||
elif provider == 'selfsigned':
|
||||
if module.params['privatekey_path'] is None and module.params['privatekey_content'] is None:
|
||||
module.fail_json(msg='One of privatekey_path and privatekey_content must be specified for the selfsigned provider.')
|
||||
|
@ -2702,7 +2702,7 @@ def main():
|
|||
except AttributeError:
|
||||
module.fail_json(msg='You need to have PyOpenSSL>=0.15')
|
||||
|
||||
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated', version='2.13')
|
||||
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated', version='ansible.builtin:2.13')
|
||||
if provider == 'selfsigned':
|
||||
certificate = SelfSignedCertificate(module)
|
||||
elif provider == 'acme':
|
||||
|
|
|
@ -845,7 +845,7 @@ def main():
|
|||
except AttributeError:
|
||||
module.fail_json(msg='You need to have PyOpenSSL>=0.15')
|
||||
|
||||
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated', version='2.13')
|
||||
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated', version='ansible.builtin:2.13')
|
||||
certificate = CertificateInfoPyOpenSSL(module)
|
||||
elif backend == 'cryptography':
|
||||
if not CRYPTOGRAPHY_FOUND:
|
||||
|
|
|
@ -1091,7 +1091,7 @@ def main():
|
|||
|
||||
if module.params['version'] != 1:
|
||||
module.deprecate('The version option will only support allowed values from Ansible 2.14 on. '
|
||||
'Currently, only the value 1 is allowed by RFC 2986', version='2.14')
|
||||
'Currently, only the value 1 is allowed by RFC 2986', version='ansible.builtin:2.14')
|
||||
|
||||
base_dir = os.path.dirname(module.params['path']) or '.'
|
||||
if not os.path.isdir(base_dir):
|
||||
|
@ -1125,7 +1125,7 @@ def main():
|
|||
except AttributeError:
|
||||
module.fail_json(msg='You need to have PyOpenSSL>=0.15 to generate CSRs')
|
||||
|
||||
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated', version='2.13')
|
||||
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated', version='ansible.builtin:2.13')
|
||||
csr = CertificateSigningRequestPyOpenSSL(module)
|
||||
elif backend == 'cryptography':
|
||||
if not CRYPTOGRAPHY_FOUND:
|
||||
|
|
|
@ -908,7 +908,7 @@ def main():
|
|||
if not PYOPENSSL_FOUND:
|
||||
module.fail_json(msg=missing_required_lib('pyOpenSSL >= {0}'.format(MINIMAL_PYOPENSSL_VERSION)),
|
||||
exception=PYOPENSSL_IMP_ERR)
|
||||
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated', version='2.13')
|
||||
module.deprecate('The module is using the PyOpenSSL backend. This backend has been deprecated', version='ansible.builtin:2.13')
|
||||
private_key = PrivateKeyPyOpenSSL(module)
|
||||
elif backend == 'cryptography':
|
||||
if not CRYPTOGRAPHY_FOUND:
|
||||
|
|
|
@ -118,7 +118,7 @@ def main():
|
|||
supports_check_mode=True,
|
||||
)
|
||||
if module._name == 'python_requirements_facts':
|
||||
module.deprecate("The 'python_requirements_facts' module has been renamed to 'python_requirements_info'", version='2.13')
|
||||
module.deprecate("The 'python_requirements_facts' module has been renamed to 'python_requirements_info'", version='ansible.builtin:2.13')
|
||||
if not HAS_DISTUTILS:
|
||||
module.fail_json(
|
||||
msg='Could not import "distutils" and "pkg_resources" libraries to introspect python environment.',
|
||||
|
|
|
@ -885,7 +885,8 @@ def main():
|
|||
# Report wrongly used attribute parameter when using content=attribute
|
||||
# TODO: Remove this in Ansible v2.12 (and reinstate strict parameter test above) and remove the integration test example
|
||||
if content == 'attribute' and attribute is not None:
|
||||
module.deprecate("Parameter 'attribute=%s' is ignored when using 'content=attribute' only 'xpath' is used. Please remove entry." % attribute, '2.12')
|
||||
module.deprecate("Parameter 'attribute=%s' is ignored when using 'content=attribute' only 'xpath' is used. Please remove entry." % attribute,
|
||||
'ansible.builtin:2.12')
|
||||
|
||||
# Check if the file exists
|
||||
if xml_string:
|
||||
|
|
|
@ -22,23 +22,23 @@ def test_warn(am, capfd):
|
|||
@pytest.mark.parametrize('stdin', [{}], indirect=['stdin'])
|
||||
def test_deprecate(am, capfd):
|
||||
am.deprecate('deprecation1')
|
||||
am.deprecate('deprecation2', '2.3')
|
||||
am.deprecate('deprecation3', version='2.4')
|
||||
am.deprecate('deprecation4', date='2020-03-10')
|
||||
am.deprecate('deprecation2', 'ansible.builtin:2.3')
|
||||
am.deprecate('deprecation3', version='ansible.builtin:2.4')
|
||||
am.deprecate('deprecation4', date='ansible.builtin:2020-03-10')
|
||||
|
||||
with pytest.raises(SystemExit):
|
||||
am.exit_json(deprecations=['deprecation5', ('deprecation6', '2.4')])
|
||||
am.exit_json(deprecations=['deprecation5', ('deprecation6', 'ansible.builtin:2.4')])
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
output = json.loads(out)
|
||||
assert ('warnings' not in output or output['warnings'] == [])
|
||||
assert output['deprecations'] == [
|
||||
{u'msg': u'deprecation1', u'version': None},
|
||||
{u'msg': u'deprecation2', u'version': '2.3'},
|
||||
{u'msg': u'deprecation3', u'version': '2.4'},
|
||||
{u'msg': u'deprecation4', u'date': '2020-03-10'},
|
||||
{u'msg': u'deprecation2', u'version': 'ansible.builtin:2.3'},
|
||||
{u'msg': u'deprecation3', u'version': 'ansible.builtin:2.4'},
|
||||
{u'msg': u'deprecation4', u'date': 'ansible.builtin:2020-03-10'},
|
||||
{u'msg': u'deprecation5', u'version': None},
|
||||
{u'msg': u'deprecation6', u'version': '2.4'},
|
||||
{u'msg': u'deprecation6', u'version': 'ansible.builtin:2.4'},
|
||||
]
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue