Change validate-modules for removed modules

Removed modules now don't have documentation.  Need to account for that
when checking them in validte-modules
pull/44661/merge
Toshio Kuratomi 6 years ago
parent 3ccdb35f59
commit 68c60ad307

@ -106,7 +106,9 @@ Errors
316 Invalid ``ANSIBLE_METADATA`` schema 316 Invalid ``ANSIBLE_METADATA`` schema
317 option is marked as required but specifies a default. 317 option is marked as required but specifies a default.
Arguments with a default should not be marked as required Arguments with a default should not be marked as required
318 Module deprecated, but DOCUMENTATION.deprecated is missing 318 Module marked as deprecated or removed in at least one of the filename, its metadata, or
in DOCUMENTATION (setting DOCUMENTATION.deprecated for deprecation or removing all
documentation for removed) but not in all three places.
319 ``RETURN`` fragments missing or invalid 319 ``RETURN`` fragments missing or invalid
320 ``DOCUMENTATION.options`` must be a dictionary/hash when used 320 ``DOCUMENTATION.options`` must be a dictionary/hash when used
321 ``Exception`` attempting to import module for ``argument_spec`` introspection 321 ``Exception`` attempting to import module for ``argument_spec`` introspection
@ -121,6 +123,8 @@ Errors
330 Choices value from the argument_spec is not compatible with type defined in the argument_spec 330 Choices value from the argument_spec is not compatible with type defined in the argument_spec
331 argument in argument_spec must be a dictionary/hash when used 331 argument in argument_spec must be a dictionary/hash when used
332 ``AnsibleModule`` schema validation error 332 ``AnsibleModule`` schema validation error
333 ``ANSIBLE_METADATA.status`` of deprecated or removed can't include other statuses
.. ..
--------- ------------------- --------- -------------------
**4xx** **Syntax** **4xx** **Syntax**

@ -831,8 +831,79 @@ class ModuleValidator(Validator):
def _validate_docs(self): def _validate_docs(self):
doc_info = self._get_docs() doc_info = self._get_docs()
deprecated = False
doc = None doc = None
documentation_exists = False
examples_exist = False
returns_exist = False
# We have three ways of marking deprecated/removed files. Have to check each one
# individually and then make sure they all agree
filename_deprecated_or_removed = False
deprecated = False
removed = False
doc_deprecated = None # doc legally might not exist
if self.object_name.startswith('_') and not os.path.islink(self.object_path):
filename_deprecated_or_removed = True
# Have to check the metadata first so that we know if the module is removed or deprecated
if not bool(doc_info['ANSIBLE_METADATA']['value']):
self.reporter.error(
path=self.object_path,
code=314,
msg='No ANSIBLE_METADATA provided'
)
else:
metadata = None
if isinstance(doc_info['ANSIBLE_METADATA']['value'], ast.Dict):
metadata = ast.literal_eval(
doc_info['ANSIBLE_METADATA']['value']
)
else:
# ANSIBLE_METADATA doesn't properly support YAML
# we should consider removing it from the spec
# Below code kept, incase we change our minds
# metadata, errors, traces = parse_yaml(
# doc_info['ANSIBLE_METADATA']['value'].s,
# doc_info['ANSIBLE_METADATA']['lineno'],
# self.name, 'ANSIBLE_METADATA'
# )
# for error in errors:
# self.reporter.error(
# path=self.object_path,
# code=315,
# **error
# )
# for trace in traces:
# self.reporter.trace(
# path=self.object_path,
# tracebk=trace
# )
self.reporter.error(
path=self.object_path,
code=315,
msg='ANSIBLE_METADATA was not provided as a dict, YAML not supported'
)
if metadata:
self._validate_docs_schema(metadata, metadata_1_1_schema(),
'ANSIBLE_METADATA', 316)
# We could validate these via the schema if we knew what the values are ahead of
# time. We can figure that out for deprecated but we can't for removed. Only the
# metadata has that information.
if 'removed' in metadata['status']:
removed = True
if 'deprecated' in metadata['status']:
deprecated = True
if (deprecated or removed) and len(metadata['status']) > 1:
self.reporter.error(
path=self.object_path,
code=333,
msg='ANSIBLE_METADATA.status must be exactly one of "deprecated" or "removed"'
)
if not removed:
if not bool(doc_info['DOCUMENTATION']['value']): if not bool(doc_info['DOCUMENTATION']['value']):
self.reporter.error( self.reporter.error(
path=self.object_path, path=self.object_path,
@ -840,6 +911,7 @@ class ModuleValidator(Validator):
msg='No DOCUMENTATION provided' msg='No DOCUMENTATION provided'
) )
else: else:
documentation_exists = True
doc, errors, traces = parse_yaml( doc, errors, traces = parse_yaml(
doc_info['DOCUMENTATION']['value'], doc_info['DOCUMENTATION']['value'],
doc_info['DOCUMENTATION']['lineno'], doc_info['DOCUMENTATION']['lineno'],
@ -885,14 +957,10 @@ class ModuleValidator(Validator):
msg='DOCUMENTATION.options must be a dictionary/hash when used', msg='DOCUMENTATION.options must be a dictionary/hash when used',
) )
if self.object_name.startswith('_') and not os.path.islink(self.object_path): if 'deprecated' in doc and doc.get('deprecated'):
deprecated = True doc_deprecated = True
if 'deprecated' not in doc or not doc.get('deprecated'): else:
self.reporter.error( doc_deprecated = False
path=self.object_path,
code=318,
msg='Module deprecated, but DOCUMENTATION.deprecated is missing'
)
if os.path.islink(self.object_path): if os.path.islink(self.object_path):
# This module has an alias, which we can tell as it's a symlink # This module has an alias, which we can tell as it's a symlink
@ -912,6 +980,7 @@ class ModuleValidator(Validator):
msg='No EXAMPLES provided' msg='No EXAMPLES provided'
) )
else: else:
examples_exists = True
_, errors, traces = parse_yaml(doc_info['EXAMPLES']['value'], _, errors, traces = parse_yaml(doc_info['EXAMPLES']['value'],
doc_info['EXAMPLES']['lineno'], doc_info['EXAMPLES']['lineno'],
self.name, 'EXAMPLES', load_all=True) self.name, 'EXAMPLES', load_all=True)
@ -928,6 +997,7 @@ class ModuleValidator(Validator):
) )
if not bool(doc_info['RETURN']['value']): if not bool(doc_info['RETURN']['value']):
returns_exists = True
if self._is_new_module(): if self._is_new_module():
self.reporter.error( self.reporter.error(
path=self.object_path, path=self.object_path,
@ -960,50 +1030,25 @@ class ModuleValidator(Validator):
tracebk=trace tracebk=trace
) )
if not bool(doc_info['ANSIBLE_METADATA']['value']): # Check for mismatched deprecation
self.reporter.error( mismatched_deprecation = True
path=self.object_path, if not (filename_deprecated_or_removed or removed or deprecated or doc_deprecated):
code=314, mismatched_deprecation = False
msg='No ANSIBLE_METADATA provided'
)
else:
metadata = None
if isinstance(doc_info['ANSIBLE_METADATA']['value'], ast.Dict):
metadata = ast.literal_eval(
doc_info['ANSIBLE_METADATA']['value']
)
else: else:
# ANSIBLE_METADATA doesn't properly support YAML if (filename_deprecated_or_removed and deprecated and doc_deprecated):
# we should consider removing it from the spec mismatched_deprecation = False
# Below code kept, incase we change our minds if (filename_deprecated_or_removed and removed and not (documentation_exists or examples_exist or returns_exist)):
mismatched_deprecation = False
# metadata, errors, traces = parse_yaml(
# doc_info['ANSIBLE_METADATA']['value'].s,
# doc_info['ANSIBLE_METADATA']['lineno'],
# self.name, 'ANSIBLE_METADATA'
# )
# for error in errors:
# self.reporter.error(
# path=self.object_path,
# code=315,
# **error
# )
# for trace in traces:
# self.reporter.trace(
# path=self.object_path,
# tracebk=trace
# )
if mismatched_deprecation:
self.reporter.error( self.reporter.error(
path=self.object_path, path=self.object_path,
code=315, code=318,
msg='ANSIBLE_METADATA was not provided as a dict, YAML not supported' msg='Module deprecation/removed must agree in Metadata, by prepending filename with'
' "_", and setting DOCUMENTATION.deprecated for deprecation or by removing all'
' documentation for removed'
) )
if metadata:
self._validate_docs_schema(metadata, metadata_1_1_schema(deprecated),
'ANSIBLE_METADATA', 316)
return doc_info, doc return doc_info, doc
def _check_version_added(self, doc): def _check_version_added(self, doc):
@ -1368,22 +1413,23 @@ class ModuleValidator(Validator):
) )
return return
end_of_deprecation_should_be_docs_only = False end_of_deprecation_should_be_removed_only = False
if self._python_module(): if self._python_module():
doc_info, docs = self._validate_docs() doc_info, docs = self._validate_docs()
# See if current version => deprecated.removed_in, ie, should be docs only # See if current version => deprecated.removed_in, ie, should be docs only
if docs and 'deprecated' in docs and docs['deprecated'] is not None: if 'removed' in ast.literal_eval(doc_info['ANSIBLE_METADATA']['value'])['status']:
end_of_deprecation_should_be_removed_only = True
elif docs and 'deprecated' in docs and docs['deprecated'] is not None:
try: try:
removed_in = StrictVersion(str(docs.get('deprecated')['removed_in'])) removed_in = StrictVersion(str(docs.get('deprecated')['removed_in']))
except ValueError: except ValueError:
end_of_deprecation_should_be_docs_only = False end_of_deprecation_should_be_removed_only = False
else: else:
strict_ansible_version = StrictVersion('.'.join(ansible_version.split('.')[:2])) strict_ansible_version = StrictVersion('.'.join(ansible_version.split('.')[:2]))
end_of_deprecation_should_be_docs_only = strict_ansible_version >= removed_in end_of_deprecation_should_be_removed_only = strict_ansible_version >= removed_in
# FIXME if +2 then file should be empty? - maybe add this only in the future
if self._python_module() and not self._just_docs() and not end_of_deprecation_should_be_docs_only: if self._python_module() and not self._just_docs() and not end_of_deprecation_should_be_removed_only:
self._validate_ansible_module_call(docs) self._validate_ansible_module_call(docs)
self._check_for_sys_exit() self._check_for_sys_exit()
self._find_blacklist_imports() self._find_blacklist_imports()
@ -1400,14 +1446,17 @@ class ModuleValidator(Validator):
self._find_ps_docs_py_file() self._find_ps_docs_py_file()
self._check_gpl3_header() self._check_gpl3_header()
if not self._just_docs() and not end_of_deprecation_should_be_docs_only: if not self._just_docs() and not end_of_deprecation_should_be_removed_only:
self._check_interpreter(powershell=self._powershell_module()) self._check_interpreter(powershell=self._powershell_module())
self._check_type_instead_of_isinstance( self._check_type_instead_of_isinstance(
powershell=self._powershell_module() powershell=self._powershell_module()
) )
if end_of_deprecation_should_be_docs_only: if end_of_deprecation_should_be_removed_only:
# Ensure that `if __name__ == '__main__':` calls `removed_module()` which ensure that the module has no code in # Ensure that `if __name__ == '__main__':` calls `removed_module()` which ensure that the module has no code in
main = self._find_main_call('removed_module') main = self._find_main_call('removed_module')
# FIXME: Ensure that the version in the call to removed_module is less than +2.
# Otherwise it's time to remove the file (This may need to be done in another test to
# avoid breaking whenever the Ansible version bumps)
class PythonPackageValidator(Validator): class PythonPackageValidator(Validator):

@ -179,10 +179,8 @@ def metadata_1_0_schema(deprecated):
) )
def metadata_1_1_schema(deprecated): def metadata_1_1_schema():
valid_status = Any('stableinterface', 'preview', 'deprecated', 'removed') valid_status = Any('stableinterface', 'preview', 'deprecated', 'removed')
if deprecated:
valid_status = Any('deprecated')
return Schema( return Schema(
{ {

Loading…
Cancel
Save