diff --git a/changelogs/fragments/ps_wrapper-deprecated_aliases.yaml b/changelogs/fragments/ps_wrapper-deprecated_aliases.yaml new file mode 100644 index 00000000000..dd7881dbd59 --- /dev/null +++ b/changelogs/fragments/ps_wrapper-deprecated_aliases.yaml @@ -0,0 +1,2 @@ +minor_changes: +- Ansible.Basic.cs - Added support for ``deprecated_aliases`` to deprecated aliases in a standard way diff --git a/docs/docsite/rst/dev_guide/developing_modules_general_windows.rst b/docs/docsite/rst/dev_guide/developing_modules_general_windows.rst index 39235d32994..8ac00d3bf4a 100644 --- a/docs/docsite/rst/dev_guide/developing_modules_general_windows.rst +++ b/docs/docsite/rst/dev_guide/developing_modules_general_windows.rst @@ -215,6 +215,7 @@ options set: - ``aliases``: A list of aliases for the module option - ``choices``: A list of valid values for the module option, if ``type=list`` then each list value is validated against the choices and not the list itself - ``default``: The default value for the module option if not set +- ``deprecated_aliases``: A list of hashtables that define aliases that are deprecated and the versions they will be removed in. Each entry must contain the keys ``name`` and ``version`` - ``elements``: When ``type=list``, this sets the type of each list value, the values are the same as ``type`` - ``no_log``: Will sanitise the input value before being returned in the ``module_invocation`` return value - ``removed_in_version``: States when a deprecated module option is to be removed, a warning is displayed to the end user if set diff --git a/lib/ansible/module_utils/csharp/Ansible.Basic.cs b/lib/ansible/module_utils/csharp/Ansible.Basic.cs index f0d10207669..38578ab8836 100644 --- a/lib/ansible/module_utils/csharp/Ansible.Basic.cs +++ b/lib/ansible/module_utils/csharp/Ansible.Basic.cs @@ -77,6 +77,7 @@ namespace Ansible.Basic { "aliases", new List() { typeof(List), typeof(List) } }, { "choices", new List() { typeof(List), typeof(List) } }, { "default", new List() { null, null } }, + { "deprecated_aliases", new List() { typeof(List), typeof(List) } }, { "elements", new List() { null, null } }, { "mutually_exclusive", new List() { typeof(List>), null } }, { "no_log", new List() { false, typeof(bool) } }, @@ -686,6 +687,27 @@ namespace Ansible.Basic } } + List deprecatedAliases = (List)argumentSpec["deprecated_aliases"]; + foreach (Hashtable depInfo in deprecatedAliases) + { + foreach (string keyName in new List { "name", "version" }) + { + if (!depInfo.ContainsKey(keyName)) + { + string msg = String.Format("{0} is required in a deprecated_aliases entry", keyName); + throw new ArgumentException(FormatOptionsContext(msg, " - ")); + } + } + string aliasName = (string)depInfo["name"]; + string depVersion = (string)depInfo["version"]; + + if (parameters.Contains(aliasName)) + { + string msg = String.Format("Alias '{0}' is deprecated. See the module docs for more information", aliasName); + Deprecate(FormatOptionsContext(msg, " - "), depVersion); + } + } + return aliasResults; } @@ -1045,7 +1067,7 @@ namespace Ansible.Basic if (missing.Count > 0) { - string msg = String.Format("missing parameter(s) required by '{0}': {1}", key, String.Join(", ", missing)); + string msg = String.Format("missing parameter(s) required by '{0}': {1}", key, String.Join(", ", missing)); FailJson(FormatOptionsContext(msg)); } } diff --git a/test/integration/targets/win_csharp_utils/library/ansible_basic_tests.ps1 b/test/integration/targets/win_csharp_utils/library/ansible_basic_tests.ps1 index bdbe6e4b976..74e8a4b9f96 100644 --- a/test/integration/targets/win_csharp_utils/library/ansible_basic_tests.ps1 +++ b/test/integration/targets/win_csharp_utils/library/ansible_basic_tests.ps1 @@ -793,6 +793,77 @@ test_no_log - Invoked with: $actual | Assert-DictionaryEquals -Expected $expected } + "Deprecated aliases" = { + $spec = @{ + options = @{ + option1 = @{ type = "str"; aliases = "alias1" } + option2 = @{ type = "str"; aliases = "alias2" } + option3 = @{ + type = "dict" + options = @{ + option1 = @{ type = "str"; aliases = "alias1" } + option2 = @{ type = "str"; aliases = "alias2" } + } + deprecated_aliases = @( + @{ name = "alias1"; version = "2.10" } + @{ name = "alias2"; version = "2.11" } + ) + } + } + deprecated_aliases = @( + @{ name = "alias1"; version = "2.10" } + @{ name = "alias2"; version = "2.11" } + ) + } + + $complex_args = @{ + alias1 = "alias1" + option2 = "option2" + option3 = @{ + option1 = "option1" + alias2 = "alias2" + } + } + + $m = [Ansible.Basic.AnsibleModule]::Create(@(), $spec) + $failed = $false + try { + $m.ExitJson() + } catch [System.Management.Automation.RuntimeException] { + $failed = $true + $_.Exception.Message | Assert-Equals -Expected "exit: 0" + $actual = [Ansible.Basic.AnsibleModule]::FromJson($_.Exception.InnerException.Output) + } + $failed | Assert-Equals -Expected $true + + $expected = @{ + changed = $false + invocation = @{ + module_args = @{ + alias1 = "alias1" + option1 = "alias1" + option2 = "option2" + option3 = @{ + option1 = "option1" + option2 = "alias2" + alias2 = "alias2" + } + } + } + deprecations = @( + @{ + msg = "Alias 'alias1' is deprecated. See the module docs for more information" + version = "2.10" + }, + @{ + msg = "Alias 'alias2' is deprecated. See the module docs for more information - found in option3" + version = "2.11" + } + ) + } + $actual | Assert-DictionaryEquals -Expected $expected + } + "Required by - single value" = { $spec = @{ options = @{ @@ -1468,8 +1539,9 @@ test_no_log - Invoked with: $failed | Assert-Equals -Expected $true $expected_msg = "internal error: argument spec entry contains an invalid key 'invalid', valid keys: apply_defaults, " - $expected_msg += "aliases, choices, default, elements, mutually_exclusive, no_log, options, removed_in_version, " - $expected_msg += "required, required_by, required_if, required_one_of, required_together, supports_check_mode, type" + $expected_msg += "aliases, choices, default, deprecated_aliases, elements, mutually_exclusive, no_log, options, " + $expected_msg += "removed_in_version, required, required_by, required_if, required_one_of, required_together, " + $expected_msg += "supports_check_mode, type" $actual.Keys.Count | Assert-Equals -Expected 3 $actual.failed | Assert-Equals -Expected $true @@ -1500,9 +1572,9 @@ test_no_log - Invoked with: $failed | Assert-Equals -Expected $true $expected_msg = "internal error: argument spec entry contains an invalid key 'invalid', valid keys: apply_defaults, " - $expected_msg += "aliases, choices, default, elements, mutually_exclusive, no_log, options, removed_in_version, " - $expected_msg += "required, required_by, required_if, required_one_of, required_together, supports_check_mode, type - " - $expected_msg += "found in option_key -> sub_option_key" + $expected_msg += "aliases, choices, default, deprecated_aliases, elements, mutually_exclusive, no_log, options, " + $expected_msg += "removed_in_version, required, required_by, required_if, required_one_of, required_together, " + $expected_msg += "supports_check_mode, type - found in option_key -> sub_option_key" $actual.Keys.Count | Assert-Equals -Expected 3 $actual.failed | Assert-Equals -Expected $true @@ -1588,6 +1660,76 @@ test_no_log - Invoked with: ("exception" -cin $actual.Keys) | Assert-Equals -Expected $true } + "Invalid deprecated aliases entry - no version" = { + $spec = @{ + options = @{ + option_key = @{ + type = "str" + aliases = ,"alias_name" + } + } + + deprecated_aliases = @( + @{name = "alias_name"} + ) + } + + $failed = $false + try { + $m = [Ansible.Basic.AnsibleModule]::Create(@(), $spec) + } catch [System.Management.Automation.RuntimeException] { + $failed = $true + $_.Exception.Message | Assert-Equals -Expected "exit: 1" + $actual = [Ansible.Basic.AnsibleModule]::FromJson($_.Exception.InnerException.Output) + } + $failed | Assert-Equals -Expected $true + + $expected_msg = "internal error: version is required in a deprecated_aliases entry" + + $actual.Keys.Count | Assert-Equals -Expected 3 + $actual.failed | Assert-Equals -Expected $true + $actual.msg | Assert-Equals -Expected $expected_msg + ("exception" -cin $actual.Keys) | Assert-Equals -Expected $true + } + + "Invalid deprecated aliases entry - no name (nested)" = { + $spec = @{ + options = @{ + option_key = @{ + type = "dict" + options = @{ + sub_option_key = @{ + type = "str" + aliases = ,"alias_name" + + } + } + + deprecated_aliases = @( + @{version = "2.10"} + ) + } + } + } + + $complex_args = @{ + option_key = @{ + sub_option_key = "a" + } + } + + $failed = $false + try { + $m = [Ansible.Basic.AnsibleModule]::Create(@(), $spec) + } catch [System.ArgumentException] { + $failed = $true + $expected_msg = "name is required in a deprecated_aliases entry - found in option_key" + $_.Exception.Message | Assert-Equals -Expected $expected_msg + $actual = [Ansible.Basic.AnsibleModule]::FromJson($_.Exception.InnerException.Output) + } + $failed | Assert-Equals -Expected $true + } + "Spec required and default set at the same time" = { $spec = @{ options = @{ @@ -2436,4 +2578,3 @@ try { } Exit-Module -