Add several new doc<->arg_spec checks (#36247)

* Add several new doc<->arg_spec checks. See #18183

* Update ignore.txt for validate-modules
This commit is contained in:
Matt Martz 2018-02-15 14:34:40 -06:00 committed by John R Barker
parent 77fa41795e
commit 50adc5409b
4 changed files with 2958 additions and 7 deletions

View file

@ -110,6 +110,9 @@ Errors
321 ``Exception`` attempting to import module for ``argument_spec`` introspection 321 ``Exception`` attempting to import module for ``argument_spec`` introspection
322 argument is listed in the argument_spec, but not documented in the module 322 argument is listed in the argument_spec, but not documented in the module
323 argument is listed in DOCUMENTATION.options, but not accepted by the module 323 argument is listed in DOCUMENTATION.options, but not accepted by the module
324 Value for "default" from the argument_spec does not match the documentation
325 argument_spec defines type="bool" but documentation does not
326 Value for "choices" from the argument_spec does not match the documentation
.. ..
--------- ------------------- --------- -------------------
**4xx** **Syntax** **4xx** **Syntax**

File diff suppressed because it is too large Load diff

View file

@ -45,7 +45,7 @@ from module_args import AnsibleModuleImportError, get_argument_spec
from schema import doc_schema, metadata_1_1_schema, return_schema from schema import doc_schema, metadata_1_1_schema, return_schema
from utils import CaptureStd, parse_yaml from utils import CaptureStd, compare_unordered_lists, maybe_convert_bool, parse_yaml
from voluptuous.humanize import humanize_error from voluptuous.humanize import humanize_error
from ansible.module_utils.six import PY3, with_metaclass from ansible.module_utils.six import PY3, with_metaclass
@ -1058,6 +1058,34 @@ class ModuleValidator(Validator):
'should not be marked as required' % arg) 'should not be marked as required' % arg)
) )
doc_default = docs.get('options', {}).get(arg, {}).get('default', None)
if data.get('type') == 'bool':
doc_default = maybe_convert_bool(doc_default)
if 'default' in data and data['default'] != doc_default:
self.reporter.error(
path=self.object_path,
code=324,
msg=('Value for "default" from the argument_spec (%r) for "%s" does not match the '
'documentation (%r)' % (data['default'], arg, doc_default))
)
doc_type = docs.get('options', {}).get(arg, {}).get('type', 'str')
if 'type' in data and data['type'] == 'bool' and doc_type != 'bool':
self.reporter.error(
path=self.object_path,
code=325,
msg='argument_spec for "%s" defines type="bool" but documentation does not' % (arg,)
)
doc_choices = docs.get('options', {}).get(arg, {}).get('choices', [])
if not compare_unordered_lists(data.get('choices', []), doc_choices):
self.reporter.error(
path=self.object_path,
code=326,
msg=('Value for "choices" from the argument_spec (%r) for "%s" does not match the '
'documentation (%r)' % (data.get('choices', []), arg, doc_choices))
)
if docs: if docs:
try: try:
add_fragments(docs, self.object_path, fragment_loader=fragment_loader) add_fragments(docs, self.object_path, fragment_loader=fragment_loader)

View file

@ -25,6 +25,7 @@ import yaml
import yaml.reader import yaml.reader
from ansible.module_utils._text import to_text from ansible.module_utils._text import to_text
from ansible.module_utils.parsing.convert_bool import boolean
class AnsibleTextIOWrapper(TextIOWrapper): class AnsibleTextIOWrapper(TextIOWrapper):
@ -114,3 +115,24 @@ def parse_yaml(value, lineno, module, name, load_all=False):
}) })
return data, errors, traces return data, errors, traces
def maybe_convert_bool(value):
"""Safe conversion to boolean, catching TypeError and returning the original result
Only used in doc<->arg_spec comparisons
"""
try:
return boolean(value)
except TypeError:
return value
def compare_unordered_lists(a, b):
"""Safe list comparisons
Supports:
- unordered lists
- unhashable elements
"""
return len(a) == len(b) and all(x in b for x in a)