Skip to content

Commit

Permalink
ansible-test - fix up relative util import for powershell validate-mo…
Browse files Browse the repository at this point in the history
…dules (ansible#69753)

* ansible-test - fix up relative util import for powershell validate-modules

* Use different tactic for generic group

* Use python 2 and 3
  • Loading branch information
jborean93 authored May 29, 2020
1 parent 31bf3a5 commit f5f3ba7
Show file tree
Hide file tree
Showing 19 changed files with 302 additions and 35 deletions.
40 changes: 39 additions & 1 deletion lib/ansible/module_utils/csharp/Ansible.Basic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public class AnsibleModule
public delegate void WriteLineHandler(string line);
public static WriteLineHandler WriteLine = new WriteLineHandler(WriteLineModule);

public static bool _DebugArgSpec = false;

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" };

Expand Down Expand Up @@ -193,12 +195,30 @@ public AnsibleModule(string[] args, IDictionary argumentSpec)
try
{
ValidateArgumentSpec(argumentSpec);

// Used by ansible-test to retrieve the module argument spec, not designed for public use.
if (_DebugArgSpec)
{
// Cannot call exit here because it will be caught with the catch (Exception e) below. Instead
// just throw a new exception with a specific message and the exception block will handle it.
ScriptBlock.Create("Set-Variable -Name ansibleTestArgSpec -Value $args[0] -Scope Global"
).Invoke(argumentSpec);
throw new Exception("ansible-test validate-modules check");
}

// Now make sure all the metadata keys are set to their defaults, this must be done after we've
// potentially output the arg spec for ansible-test.
SetArgumentSpecDefaults(argumentSpec);

Params = GetParams(args);
aliases = GetAliases(argumentSpec, Params);
SetNoLogValues(argumentSpec, Params);
}
catch (Exception e)
{
if (e.Message == "ansible-test validate-modules check")
Exit(0);

Dictionary<string, object> result = new Dictionary<string, object>
{
{ "failed", true },
Expand Down Expand Up @@ -657,8 +677,10 @@ private void ValidateArgumentSpec(IDictionary argumentSpec)
// Outside of the spec iterator, change the values that were casted above
foreach (KeyValuePair<string, object> changedValue in changedValues)
argumentSpec[changedValue.Key] = changedValue.Value;
}

// Now make sure all the metadata keys are set to their defaults
private void SetArgumentSpecDefaults(IDictionary argumentSpec)
{
foreach (KeyValuePair<string, List<object>> metadataEntry in specDefaults)
{
List<object> defaults = metadataEntry.Value;
Expand All @@ -669,6 +691,22 @@ private void ValidateArgumentSpec(IDictionary argumentSpec)
if (!argumentSpec.Contains(metadataEntry.Key))
argumentSpec[metadataEntry.Key] = defaultValue;
}

// Recursively set the defaults for any inner options.
foreach (DictionaryEntry entry in argumentSpec)
{
if (entry.Value == null || entry.Key.ToString() != "options")
continue;

IDictionary optionsSpec = (IDictionary)entry.Value;
foreach (DictionaryEntry optionEntry in optionsSpec)
{
optionsContext.Add((string)optionEntry.Key);
IDictionary optionMeta = (IDictionary)optionEntry.Value;
SetArgumentSpecDefaults(optionMeta);
optionsContext.RemoveAt(optionsContext.Count - 1);
}
}
}

private Dictionary<string, string> GetAliases(IDictionary argumentSpec, IDictionary parameters)
Expand Down
3 changes: 3 additions & 0 deletions shippable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ matrix:
- env: T=fallaxy/2.7/1
- env: T=fallaxy/3.6/1

- env: T=generic/2.7/1
- env: T=generic/3.6/1

- env: T=i/osx/10.11
- env: T=i/rhel/7.8
- env: T=i/rhel/8.2
Expand Down
1 change: 1 addition & 0 deletions test/integration/targets/ansible-test-docker/aliases
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
shippable/generic/group1 # Runs in the default test container so access to tools like pwsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-

# Copyright (c) 2020 Ansible Project
# 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


class ModuleDocFragment:

DOCUMENTATION = r'''
options:
option1:
description:
- Test description
required: yes
aliases:
- alias1
type: str
'''
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright (c) 2020 Ansible Project
# # Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)

Function Get-PSUtilSpec {
<#
.SYNOPSIS
Shared util spec test
#>
@{
options = @{
option1 = @{ type = 'str'; required = $true; aliases = 'alias1' }
}
}
}

Export-ModuleMember -Function Get-PSUtilSpec
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type


def hello(name):
return 'Hello %s' % name
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/python
# 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 = '''
module: hello
short_description: Hello test module
description: Hello test module.
options:
name:
description: Name to say hello to.
type: str
author:
- Ansible Core Team
'''

EXAMPLES = '''
- minimal:
'''

RETURN = ''''''

from ansible.module_utils.basic import AnsibleModule
from ..module_utils.my_util import hello


def main():
module = AnsibleModule(
argument_spec=dict(
name=dict(type='str'),
),
)

module.exit_json(**say_hello(module.params['name']))


def say_hello(name):
return dict(
message=hello(name),
)


if __name__ == '__main__':
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!powershell

# Copyright (c) 2020 Ansible Project
# # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

#AnsibleRequires -CSharpUtil Ansible.Basic
#AnsibleRequires -PowerShell ..module_utils.PSUtil

$spec = @{
options = @{
my_opt = @{ type = "str"; required = $true }
}
}
$util_spec = Get-PSUtilSpec
$spec.options += $util_spec.options

$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$module.ExitJson()
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (c) 2020 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)


ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'core'}

DOCUMENTATION = r'''
---
module: win_util_args
short_description: Short description
description:
- Some test description for the module
options:
my_opt:
description:
- Test description
required: yes
type: str
extends_documentation_fragment:
- ns.col.ps_util
author:
- Ansible Test (@ansible)
'''

EXAMPLES = r'''
- win_util_args:
option1: test
my_opt: test
'''

RETURN = r'''
#
'''
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
- hello:
name: Ansibull
register: hello

- assert:
that:
- hello.message == 'Hello Ansibull'
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type

from .....plugins.module_utils.my_util import hello


def test_hello():
assert hello('Ansibull') == 'Hello Ansibull'
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from __future__ import absolute_import, division, print_function
__metaclass__ = type

from .....plugins.modules.hello import say_hello


def test_say_hello():
assert say_hello('Ansibull') == dict(message='Hello Ansibull')
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash

set -eux -o pipefail

cp -a "${TEST_DIR}/ansible_collections" "${WORK_DIR}"
cd "${WORK_DIR}/ansible_collections/ns/col"

# common args for all tests
# because we are running in shippable/generic/ we are already in the default docker container
common=(--python "${ANSIBLE_TEST_PYTHON_VERSION}" --color --truncate 0 "${@}")

# prime the venv to work around issue with PyYAML detection in ansible-test
ansible-test sanity "${common[@]}" --test ignores

# tests
ansible-test sanity "${common[@]}"
ansible-test units "${common[@]}"
ansible-test integration "${common[@]}"
24 changes: 24 additions & 0 deletions test/integration/targets/ansible-test-docker/runme.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env bash

set -eu -o pipefail

# tests must be executed outside of the ansible source tree
# otherwise ansible-test will test the ansible source instead of the test collection
# the temporary directory provided by ansible-test resides within the ansible source tree
tmp_dir=$(mktemp -d)

trap 'rm -rf "${tmp_dir}"' EXIT

export TEST_DIR
export WORK_DIR

TEST_DIR="$PWD"

for test in collection-tests/*.sh; do
WORK_DIR="${tmp_dir}/$(basename "${test}" ".sh")"
mkdir "${WORK_DIR}"
echo "**********************************************************************"
echo "TEST: ${test}: STARTING"
"${test}" "${@}" || (echo "TEST: ${test}: FAILED" && exit 1)
echo "TEST: ${test}: PASSED"
done
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ def setup_env(filename):
del sys.modules[k]


def get_ps_argument_spec(filename):
# This uses a very small skeleton of Ansible.Basic.AnsibleModule to return the argspec defined by the module. This
# is pretty rudimentary and will probably require something better going forward.
def get_ps_argument_spec(filename, collection):
fqc_name = get_module_name_from_filename(filename, collection)

pwsh = find_executable('pwsh')
if not pwsh:
raise FileNotFoundError('Required program for PowerShell arg spec inspection "pwsh" not found.')
Expand All @@ -102,11 +102,15 @@ def get_ps_argument_spec(filename):
b_module_data = module_fd.read()

ps_dep_finder = PSModuleDepFinder()
ps_dep_finder.scan_module(b_module_data)
ps_dep_finder.scan_module(b_module_data, fqn=fqc_name)

# For ps_argspec.ps1 to compile Ansible.Basic it also needs the AddType module_util.
ps_dep_finder._add_module((b"Ansible.ModuleUtils.AddType", ".psm1", None), wrapper=False)

util_manifest = json.dumps({
'module_path': to_text(module_path, errors='surrogiate_or_strict'),
'ps_utils': dict([(name, info['path']) for name, info in ps_dep_finder.ps_modules.items()])
'ansible_basic': ps_dep_finder.cs_utils_module["Ansible.Basic"]['path'],
'ps_utils': dict([(name, info['path']) for name, info in ps_dep_finder.ps_modules.items()]),
})

script_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'ps_argspec.ps1')
Expand All @@ -115,7 +119,7 @@ def get_ps_argument_spec(filename):
stdout, stderr = proc.communicate()

if proc.returncode != 0:
raise AnsibleModuleImportError(stderr.decode('utf-8'))
raise AnsibleModuleImportError("STDOUT:\n%s\nSTDERR:\n%s" % (stdout.decode('utf-8'), stderr.decode('utf-8')))

kwargs = json.loads(stdout)

Expand Down Expand Up @@ -163,4 +167,4 @@ def get_argument_spec(filename, collection):
if filename.endswith('.py'):
return get_py_argument_spec(filename, collection)
else:
return get_ps_argument_spec(filename)
return get_ps_argument_spec(filename, collection)
Loading

0 comments on commit f5f3ba7

Please sign in to comment.