mirror of
https://github.com/ansible/ansible.git
synced 2026-08-03 08:03:05 +02:00
Add ignore invalid options override for mod wrapper (#81899)
Adds an option that can have an action plugin tell the module to ignore options that do not fit its arg spec. This is to enable support for core running modules that exist outside of the collection that may not be new enough to support some of the options supplied to it.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
minor_changes:
|
||||
- >-
|
||||
modules - Add the ability for an action plugin to call ``self._execute_module(*, ignore_unknown_opts=True)`` to execute a module with options that may not be
|
||||
supported for the version being called. This tells the module basic wrapper to ignore validating the options provided match the arg spec.
|
||||
bugfixes:
|
||||
- fetch - Do not calculate the file size for Windows fetch targets to improve performance.
|
||||
@@ -480,6 +480,8 @@ class AnsibleModule(object):
|
||||
|
||||
try:
|
||||
error = self.validation_result.errors[0]
|
||||
if isinstance(error, UnsupportedError) and self._ignore_unknown_opts:
|
||||
error = None
|
||||
except IndexError:
|
||||
error = None
|
||||
|
||||
|
||||
@@ -87,6 +87,7 @@ PASS_VARS = {
|
||||
'debug': ('_debug', False),
|
||||
'diff': ('_diff', False),
|
||||
'keep_remote_files': ('_keep_remote_files', False),
|
||||
'ignore_unknown_opts': ('_ignore_unknown_opts', False),
|
||||
'module_name': ('_name', None),
|
||||
'no_log': ('no_log', False),
|
||||
'remote_tmp': ('_remote_tmp', None),
|
||||
@@ -100,7 +101,7 @@ PASS_VARS = {
|
||||
'version': ('ansible_version', '0.0'),
|
||||
}
|
||||
|
||||
PASS_BOOLS = ('check_mode', 'debug', 'diff', 'keep_remote_files', 'no_log')
|
||||
PASS_BOOLS = ('check_mode', 'debug', 'diff', 'keep_remote_files', 'ignore_unknown_opts', 'no_log')
|
||||
|
||||
DEFAULT_TYPE_VALIDATORS = {
|
||||
'str': check_type_str,
|
||||
|
||||
@@ -49,6 +49,7 @@ namespace Ansible.Basic
|
||||
private static List<string> BOOLEANS_TRUE = new List<string>() { "y", "yes", "on", "1", "true", "t", "1.0" };
|
||||
private static List<string> BOOLEANS_FALSE = new List<string>() { "n", "no", "off", "0", "false", "f", "0.0" };
|
||||
|
||||
private bool ignoreUnknownOpts = false;
|
||||
private string remoteTmp = Path.GetTempPath();
|
||||
private string tmpdir = null;
|
||||
private HashSet<string> noLogValues = new HashSet<string>();
|
||||
@@ -64,6 +65,7 @@ namespace Ansible.Basic
|
||||
{ "debug", "DebugMode" },
|
||||
{ "diff", "DiffMode" },
|
||||
{ "keep_remote_files", "KeepRemoteFiles" },
|
||||
{ "ignore_unknown_opts", "ignoreUnknownOpts" },
|
||||
{ "module_name", "ModuleName" },
|
||||
{ "no_log", "NoLog" },
|
||||
{ "remote_tmp", "remoteTmp" },
|
||||
@@ -76,7 +78,7 @@ namespace Ansible.Basic
|
||||
{ "verbosity", "Verbosity" },
|
||||
{ "version", "AnsibleVersion" },
|
||||
};
|
||||
private List<string> passBools = new List<string>() { "check_mode", "debug", "diff", "keep_remote_files", "no_log" };
|
||||
private List<string> passBools = new List<string>() { "check_mode", "debug", "diff", "keep_remote_files", "ignore_unknown_opts", "no_log" };
|
||||
private List<string> passInts = new List<string>() { "verbosity" };
|
||||
private Dictionary<string, List<object>> specDefaults = new Dictionary<string, List<object>>()
|
||||
{
|
||||
@@ -1043,7 +1045,7 @@ namespace Ansible.Basic
|
||||
foreach (string parameter in removedParameters)
|
||||
param.Remove(parameter);
|
||||
|
||||
if (unsupportedParameters.Count > 0)
|
||||
if (unsupportedParameters.Count > 0 && !ignoreUnknownOpts)
|
||||
{
|
||||
legalInputs.RemoveAll(x => passVars.Keys.Contains(x.Replace("_ansible_", "")));
|
||||
string msg = String.Format("Unsupported parameters for ({0}) module: {1}", ModuleName, String.Join(", ", unsupportedParameters));
|
||||
|
||||
@@ -849,10 +849,13 @@ class ActionBase(ABC):
|
||||
path=path,
|
||||
follow=follow,
|
||||
get_checksum=checksum,
|
||||
get_size=False, # ansible.windows.win_stat added this in 1.11.0
|
||||
checksum_algorithm='sha1',
|
||||
)
|
||||
# Unknown opts are ignored as module_args could be specific for the
|
||||
# module that is being executed.
|
||||
mystat = self._execute_module(module_name='ansible.legacy.stat', module_args=module_args, task_vars=all_vars,
|
||||
wrap_async=False)
|
||||
wrap_async=False, ignore_unknown_opts=True)
|
||||
|
||||
if mystat.get('failed'):
|
||||
msg = mystat.get('module_stderr')
|
||||
@@ -936,7 +939,7 @@ class ActionBase(ABC):
|
||||
data = re.sub(r'^((\r)?\n)?BECOME-SUCCESS.*(\r)?\n', '', data)
|
||||
return data
|
||||
|
||||
def _update_module_args(self, module_name, module_args, task_vars):
|
||||
def _update_module_args(self, module_name, module_args, task_vars, ignore_unknown_opts: bool = False):
|
||||
|
||||
# set check mode in the module arguments, if required
|
||||
if self._task.check_mode:
|
||||
@@ -994,7 +997,11 @@ class ActionBase(ABC):
|
||||
# make sure the remote_tmp value is sent through in case modules needs to create their own
|
||||
module_args['_ansible_remote_tmp'] = self.get_shell_option('remote_tmp', default='~/.ansible/tmp')
|
||||
|
||||
def _execute_module(self, module_name=None, module_args=None, tmp=None, task_vars=None, persist_files=False, delete_remote_tmp=None, wrap_async=False):
|
||||
# tells the module to ignore options that are not in its argspec.
|
||||
module_args['_ansible_ignore_unknown_opts'] = ignore_unknown_opts
|
||||
|
||||
def _execute_module(self, module_name=None, module_args=None, tmp=None, task_vars=None, persist_files=False, delete_remote_tmp=None, wrap_async=False,
|
||||
ignore_unknown_opts: bool = False):
|
||||
'''
|
||||
Transfer and run a module along with its arguments.
|
||||
'''
|
||||
@@ -1030,7 +1037,7 @@ class ActionBase(ABC):
|
||||
if module_args is None:
|
||||
module_args = self._task.args
|
||||
|
||||
self._update_module_args(module_name, module_args, task_vars)
|
||||
self._update_module_args(module_name, module_args, task_vars, ignore_unknown_opts=ignore_unknown_opts)
|
||||
|
||||
remove_async_dir = None
|
||||
if wrap_async or self._task.async_val:
|
||||
|
||||
@@ -2253,6 +2253,34 @@ test_no_log - Invoked with:
|
||||
$actual.invocation | Assert-DictionaryEqual -Expected @{module_args = $complex_args }
|
||||
}
|
||||
|
||||
"Unsupported options with ignore" = {
|
||||
$spec = @{
|
||||
options = @{
|
||||
option_key = @{
|
||||
type = "str"
|
||||
}
|
||||
}
|
||||
}
|
||||
Set-Variable -Name complex_args -Scope Global -Value @{
|
||||
option_key = "abc"
|
||||
invalid_key = "def"
|
||||
another_key = "ghi"
|
||||
_ansible_ignore_unknown_opts = $true
|
||||
}
|
||||
|
||||
$m = [Ansible.Basic.AnsibleModule]::Create(@(), $spec)
|
||||
$m.Params | Assert-DictionaryEqual -Expected @{ option_key = "abc"; invalid_key = "def"; another_key = "ghi" }
|
||||
try {
|
||||
$m.ExitJson()
|
||||
}
|
||||
catch [System.Management.Automation.RuntimeException] {
|
||||
$output = [Ansible.Basic.AnsibleModule]::FromJson($_.Exception.InnerException.Output)
|
||||
}
|
||||
$output.Keys.Count | Assert-Equal -Expected 2
|
||||
$output.changed | Assert-Equal -Expected $false
|
||||
$output.invocation | Assert-DictionaryEqual -Expected @{module_args = @{option_key = "abc"; invalid_key = "def"; another_key = "ghi" } }
|
||||
}
|
||||
|
||||
"Check mode and module doesn't support check mode" = {
|
||||
$spec = @{
|
||||
options = @{
|
||||
|
||||
@@ -66,6 +66,8 @@ VALID_SPECS = (
|
||||
({'arg': {'type': 'list', 'elements': 'str'}}, {'arg': [42, 32]}, ['42', '32']),
|
||||
# parameter is required
|
||||
({'arg': {'required': True}}, {'arg': 42}, '42'),
|
||||
# ignored unknown parameters
|
||||
({'arg': {'type': 'int'}}, {'arg': 1, 'invalid': True, '_ansible_ignore_unknown_opts': True}, 1),
|
||||
)
|
||||
|
||||
INVALID_SPECS = (
|
||||
|
||||
Reference in New Issue
Block a user