forked from ansible/ansible
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Role arg spec validation implementation (ansible#73152)
* Initial import of modified version of alikins' code * Add unit testing for new Role methods * Fix validate_arg_spec module for sanity test. Add test_include_role_fails.yml integration test from orig PR. * Add testing of suboptions * Use new ArgumentSpecValidator class instead of AnsibleModule * fix for roles with no tasks, use FQ name of new plugin * Add role dep warning
- Loading branch information
Showing
26 changed files
with
985 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
major_changes: | ||
- Support for role argument specification validation at role execution time. | ||
When a role contains an argument spec, an implicit validation task is inserted | ||
at the start of role execution. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
#!/usr/bin/python | ||
# -*- coding: utf-8 -*- | ||
|
||
# Copyright 2021 Red Hat | ||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) | ||
|
||
from __future__ import absolute_import, division, print_function | ||
__metaclass__ = type | ||
|
||
|
||
DOCUMENTATION = r''' | ||
--- | ||
module: validate_argument_spec | ||
short_description: Validate role argument specs. | ||
description: | ||
- This module validates role arguments with a defined argument specification. | ||
version_added: "2.11" | ||
options: | ||
argument_spec: | ||
description: | ||
- A dictionary like AnsibleModule argument_spec | ||
required: true | ||
provided_arguments: | ||
description: | ||
- A dictionary of the arguments that will be validated according to argument_spec | ||
author: | ||
- Ansible Core Team | ||
''' | ||
|
||
EXAMPLES = r''' | ||
''' | ||
|
||
RETURN = r''' | ||
argument_errors: | ||
description: A list of arg validation errors. | ||
returned: failure | ||
type: list | ||
elements: str | ||
sample: | ||
- "error message 1" | ||
- "error message 2" | ||
argument_spec_data: | ||
description: A dict of the data from the 'argument_spec' arg. | ||
returned: failure | ||
type: dict | ||
sample: | ||
some_arg: | ||
type: "str" | ||
some_other_arg: | ||
type: "int" | ||
required: true | ||
validate_args_context: | ||
description: A dict of info about where validate_args_spec was used | ||
type: dict | ||
returned: always | ||
sample: | ||
name: my_role | ||
type: role | ||
path: /home/user/roles/my_role/ | ||
argument_spec_name: main | ||
''' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
# Copyright 2021 Red Hat | ||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) | ||
|
||
from __future__ import (absolute_import, division, print_function) | ||
__metaclass__ = type | ||
|
||
from ansible.errors import AnsibleError | ||
from ansible.plugins.action import ActionBase | ||
from ansible.module_utils.six import iteritems, string_types | ||
from ansible.module_utils.common.arg_spec import ArgumentSpecValidator | ||
|
||
|
||
class ActionModule(ActionBase): | ||
''' Validate an arg spec''' | ||
|
||
TRANSFERS_FILES = False | ||
|
||
def get_args_from_task_vars(self, argument_spec, task_vars): | ||
''' | ||
Get any arguments that may come from `task_vars`. | ||
Expand templated variables so we can validate the actual values. | ||
:param argument_spec: A dict of the argument spec. | ||
:param task_vars: A dict of task variables. | ||
:returns: A dict of values that can be validated against the arg spec. | ||
''' | ||
args = {} | ||
|
||
for argument_name, argument_attrs in iteritems(argument_spec): | ||
if argument_name in task_vars: | ||
if isinstance(task_vars[argument_name], string_types): | ||
value = self._templar.do_template(task_vars[argument_name]) | ||
if value: | ||
args[argument_name] = value | ||
else: | ||
args[argument_name] = task_vars[argument_name] | ||
return args | ||
|
||
def run(self, tmp=None, task_vars=None): | ||
''' | ||
Validate an argument specification against a provided set of data. | ||
The `validate_argument_spec` module expects to receive the arguments: | ||
- argument_spec: A dict whose keys are the valid argument names, and | ||
whose values are dicts of the argument attributes (type, etc). | ||
- provided_arguments: A dict whose keys are the argument names, and | ||
whose values are the argument value. | ||
:param tmp: Deprecated. Do not use. | ||
:param task_vars: A dict of task variables. | ||
:return: An action result dict, including a 'argument_errors' key with a | ||
list of validation errors found. | ||
''' | ||
if task_vars is None: | ||
task_vars = dict() | ||
|
||
result = super(ActionModule, self).run(tmp, task_vars) | ||
del tmp # tmp no longer has any effect | ||
|
||
# This action can be called from anywhere, so pass in some info about what it is | ||
# validating args for so the error results make some sense | ||
result['validate_args_context'] = self._task.args.get('validate_args_context', {}) | ||
|
||
if 'argument_spec' not in self._task.args: | ||
raise AnsibleError('"argument_spec" arg is required in args: %s' % self._task.args) | ||
|
||
# Get the task var called argument_spec. This will contain the arg spec | ||
# data dict (for the proper entry point for a role). | ||
argument_spec_data = self._task.args.get('argument_spec') | ||
|
||
# the values that were passed in and will be checked against argument_spec | ||
provided_arguments = self._task.args.get('provided_arguments', {}) | ||
|
||
if not isinstance(argument_spec_data, dict): | ||
raise AnsibleError('Incorrect type for argument_spec, expected dict and got %s' % type(argument_spec_data)) | ||
|
||
if not isinstance(provided_arguments, dict): | ||
raise AnsibleError('Incorrect type for provided_arguments, expected dict and got %s' % type(provided_arguments)) | ||
|
||
args_from_vars = self.get_args_from_task_vars(argument_spec_data, task_vars) | ||
provided_arguments.update(args_from_vars) | ||
|
||
validator = ArgumentSpecValidator(argument_spec_data, provided_arguments) | ||
|
||
if not validator.validate(): | ||
result['failed'] = True | ||
result['msg'] = 'Validation of arguments failed:\n%s' % '\n'.join(validator.error_messages) | ||
result['argument_spec_data'] = argument_spec_data | ||
result['argument_errors'] = validator.error_messages | ||
return result | ||
|
||
result['changed'] = False | ||
result['msg'] = 'The arg spec validation passed' | ||
|
||
return result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
shippable/posix/group5 |
17 changes: 17 additions & 0 deletions
17
test/integration/targets/roles_arg_spec/roles/a/meta/main.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
argument_specs: | ||
main: | ||
short_description: Main entry point for role A. | ||
options: | ||
a_str: | ||
type: "str" | ||
required: true | ||
|
||
alternate: | ||
short_description: Alternate entry point for role A. | ||
options: | ||
a_int: | ||
type: "int" | ||
required: true | ||
|
||
no_spec_entrypoint: | ||
short_description: An entry point with no spec |
3 changes: 3 additions & 0 deletions
3
test/integration/targets/roles_arg_spec/roles/a/tasks/alternate.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
--- | ||
- debug: | ||
msg: "Role A (alternate) with {{ a_int }}" |
3 changes: 3 additions & 0 deletions
3
test/integration/targets/roles_arg_spec/roles/a/tasks/main.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
--- | ||
- debug: | ||
msg: "Role A with {{ a_str }}" |
3 changes: 3 additions & 0 deletions
3
test/integration/targets/roles_arg_spec/roles/a/tasks/no_spec_entrypoint.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
--- | ||
- debug: | ||
msg: "Role A no_spec_entrypoint" |
Oops, something went wrong.