From 54d8193972ead06dd482e3aff71fe07b32957ab2 Mon Sep 17 00:00:00 2001 From: GomathiselviS Date: Wed, 17 May 2023 12:47:11 -0400 Subject: [PATCH] Add unit and sanity tests to GHA (#614) * Add unit and sanity tests to GHA Signed-off-by: GomathiselviS * Fix sanity issues * Add sanity non voting * Add changelog * Fix typo * Fix typo * Use pytest-ansible * Add support for pytest-ansible --------- Signed-off-by: GomathiselviS --- .github/workflows/ci.yml | 144 +++++++++++++++++++++ changelogs/fragments/gha-sanity-fixes.yaml | 3 + plugins/modules/helm_info.py | 1 + plugins/modules/helm_template.py | 1 + plugins/modules/k8s_drain.py | 1 + plugins/modules/k8s_info.py | 2 + plugins/modules/k8s_log.py | 1 + plugins/modules/k8s_rollback.py | 2 + plugins/modules/k8s_scale.py | 1 + test-requirements.txt | 1 + tests/sanity/ignore-2.15.txt | 2 + tests/sanity/ignore-2.6.txt | 35 +++++ tests/unit/module_utils/test_helm.py | 72 +++++------ 13 files changed, 230 insertions(+), 36 deletions(-) create mode 100644 changelogs/fragments/gha-sanity-fixes.yaml create mode 100644 tests/sanity/ignore-2.6.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4f03be9f..e29e3674 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,130 @@ jobs: uses: ansible-network/github_actions/.github/workflows/changelog.yml@main linters: uses: abikouo/github_actions/.github/workflows/tox-linters.yml@tox_linters + sanity: + uses: ansible-network/github_actions/.github/workflows/sanity.yml@main + with: + matrix_include: "[]" + matrix_exclude: >- + [ + { + "ansible-version": "stable-2.9" + }, + { + "ansible-version": "stable-2.12", + "python-version": "3.7" + }, + { + "ansible-version": "stable-2.12", + "python-version": "3.11" + }, + { + "ansible-version": "stable-2.13", + "python-version": "3.7" + }, + { + "ansible-version": "stable-2.13", + "python-version": "3.11" + }, + { + "ansible-version": "stable-2.14", + "python-version": "3.7" + }, + { + "ansible-version": "stable-2.14", + "python-version": "3.8" + }, + { + "ansible-version": "stable-2.14", + "python-version": "3.11" + }, + { + "ansible-version": "stable-2.15", + "python-version": "3.7" + }, + { + "ansible-version": "stable-2.15", + "python-version": "3.8" + }, + { + "ansible-version": "stable-2.15", + "python-version": "3.11" + }, + { + "ansible-version": "milestone", + "python-version": "3.7" + }, + { + "ansible-version": "milestone", + "python-version": "3.8" + }, + { + "ansible-version": "devel", + "python-version": "3.7" + }, + { + "ansible-version": "devel", + "python-version": "3.8" + } + ] + unit-source: + uses: ansible-network/github_actions/.github/workflows/unit_source.yml@main + with: + matrix_exclude: >- + [ + { + "python-version": "3.11" + }, + { + "ansible-version": "stable-2.12", + "python-version": "3.7" + }, + { + "ansible-version": "stable-2.13", + "python-version": "3.7" + }, + { + "ansible-version": "stable-2.12", + "python-version": "3.8" + }, + { + "ansible-version": "stable-2.13", + "python-version": "3.8" + }, + { + "ansible-version": "stable-2.14", + "python-version": "3.7" + }, + { + "ansible-version": "stable-2.14", + "python-version": "3.8" + }, + { + "ansible-version": "stable-2.15", + "python-version": "3.7" + }, + { + "ansible-version": "stable-2.15", + "python-version": "3.8" + }, + { + "ansible-version": "milestone", + "python-version": "3.7" + }, + { + "ansible-version": "milestone", + "python-version": "3.8" + }, + { + "ansible-version": "devel", + "python-version": "3.7" + }, + { + "ansible-version": "devel", + "python-version": "3.8" + } + ] + collection_pre_install: '' splitter: env: source_dir: "./source" @@ -165,3 +289,23 @@ jobs: ansible_test_environment: | ENABLE_TURBO_MODE=${{ matrix.enable-turbo-mode }} if: steps.read-targets.outputs.ansible_test_targets != '' + all_green: + if: ${{ always() }} + needs: + - changelog + - linters + - sanity + - unit-source + - integration + runs-on: ubuntu-latest + steps: + - run: >- + python -c "assert set([ + '${{ needs.changelog.result }}', + '${{ needs.linters.result }}', + '${{ needs.unit-source.result }}', + '${{ needs.integration.result }}' + ]) == {'success'}" + - run: >- + python -c "assert '${{ needs.sanity.result }}' + in ['success', 'failure']" diff --git a/changelogs/fragments/gha-sanity-fixes.yaml b/changelogs/fragments/gha-sanity-fixes.yaml new file mode 100644 index 00000000..b034fbde --- /dev/null +++ b/changelogs/fragments/gha-sanity-fixes.yaml @@ -0,0 +1,3 @@ +--- +trivial: + - add github workflow and fix sanity failures (https://github.com/ansible-collections/kubernetes.core/pull/614). diff --git a/plugins/modules/helm_info.py b/plugins/modules/helm_info.py index aefd4477..5a7a5616 100644 --- a/plugins/modules/helm_info.py +++ b/plugins/modules/helm_info.py @@ -52,6 +52,7 @@ options: - If set to C(uninstalling), show releases that are currently being uninstalled. type: list elements: str + default: [] version_added: "2.3.0" get_all_values: description: diff --git a/plugins/modules/helm_template.py b/plugins/modules/helm_template.py index ab50f871..2a187257 100644 --- a/plugins/modules/helm_template.py +++ b/plugins/modules/helm_template.py @@ -98,6 +98,7 @@ options: required: false type: list elements: str + default: [] version_added: 2.4.0 values_files: description: diff --git a/plugins/modules/k8s_drain.py b/plugins/modules/k8s_drain.py index 31596d8c..3165dad4 100644 --- a/plugins/modules/k8s_drain.py +++ b/plugins/modules/k8s_drain.py @@ -43,6 +43,7 @@ options: type: str delete_options: type: dict + default: {} description: - Specify options to delete pods. - This option has effect only when C(state) is set to I(drain). diff --git a/plugins/modules/k8s_info.py b/plugins/modules/k8s_info.py index 4b29be11..fdd5093a 100644 --- a/plugins/modules/k8s_info.py +++ b/plugins/modules/k8s_info.py @@ -38,10 +38,12 @@ options: description: List of label selectors to use to filter results type: list elements: str + default: [] field_selectors: description: List of field selectors to use to filter results type: list elements: str + default: [] extends_documentation_fragment: - kubernetes.core.k8s_auth_options diff --git a/plugins/modules/k8s_log.py b/plugins/modules/k8s_log.py index e52d5bce..48537e4a 100644 --- a/plugins/modules/k8s_log.py +++ b/plugins/modules/k8s_log.py @@ -47,6 +47,7 @@ options: - Only one of I(name) or I(label_selectors) may be provided. type: list elements: str + default: [] container: description: - Use to specify the container within a pod to grab the log from. diff --git a/plugins/modules/k8s_rollback.py b/plugins/modules/k8s_rollback.py index 8dfab459..8dd68629 100644 --- a/plugins/modules/k8s_rollback.py +++ b/plugins/modules/k8s_rollback.py @@ -24,10 +24,12 @@ options: description: List of label selectors to use to filter results. type: list elements: str + default: [] field_selectors: description: List of field selectors to use to filter results. type: list elements: str + default: [] extends_documentation_fragment: - kubernetes.core.k8s_auth_options - kubernetes.core.k8s_name_options diff --git a/plugins/modules/k8s_scale.py b/plugins/modules/k8s_scale.py index 6671ceb7..20d69290 100644 --- a/plugins/modules/k8s_scale.py +++ b/plugins/modules/k8s_scale.py @@ -36,6 +36,7 @@ options: description: List of label selectors to use to filter results. type: list elements: str + default: [] version_added: 2.0.0 continue_on_error: description: diff --git a/test-requirements.txt b/test-requirements.txt index 0ab679ec..47283a2f 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -6,3 +6,4 @@ pytest-xdist pytest-mock pytest-forked virtualenv +pytest-ansible diff --git a/tests/sanity/ignore-2.15.txt b/tests/sanity/ignore-2.15.txt index 95cd652a..0ac84097 100644 --- a/tests/sanity/ignore-2.15.txt +++ b/tests/sanity/ignore-2.15.txt @@ -16,11 +16,13 @@ plugins/module_utils/k8sdynamicclient.py import-3.8!skip plugins/module_utils/k8sdynamicclient.py import-3.9!skip plugins/module_utils/k8sdynamicclient.py import-3.10!skip plugins/module_utils/k8sdynamicclient.py import-3.11!skip +plugins/module_utils/version.py pylint!skip plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc plugins/modules/k8s_scale.py validate-modules:parameter-type-not-in-doc plugins/modules/k8s_service.py validate-modules:parameter-type-not-in-doc tests/unit/module_utils/fixtures/definitions.yml yamllint!skip tests/unit/module_utils/fixtures/deployments.yml yamllint!skip +tests/integration/targets/k8s_delete/files/deployments.yaml yamllint!skip tests/unit/module_utils/fixtures/pods.yml yamllint!skip tests/integration/targets/helm/files/appversionless-chart-v2/templates/configmap.yaml yamllint!skip tests/integration/targets/helm/files/appversionless-chart/templates/configmap.yaml yamllint!skip diff --git a/tests/sanity/ignore-2.6.txt b/tests/sanity/ignore-2.6.txt new file mode 100644 index 00000000..95cd652a --- /dev/null +++ b/tests/sanity/ignore-2.6.txt @@ -0,0 +1,35 @@ +plugins/module_utils/client/discovery.py import-3.6!skip +plugins/module_utils/client/discovery.py import-3.7!skip +plugins/module_utils/client/discovery.py import-3.8!skip +plugins/module_utils/client/discovery.py import-3.9!skip +plugins/module_utils/client/discovery.py import-3.10!skip +plugins/module_utils/client/discovery.py import-3.11!skip +plugins/module_utils/client/resource.py import-3.6!skip +plugins/module_utils/client/resource.py import-3.7!skip +plugins/module_utils/client/resource.py import-3.8!skip +plugins/module_utils/client/resource.py import-3.9!skip +plugins/module_utils/client/resource.py import-3.10!skip +plugins/module_utils/client/resource.py import-3.11!skip +plugins/module_utils/k8sdynamicclient.py import-3.6!skip +plugins/module_utils/k8sdynamicclient.py import-3.7!skip +plugins/module_utils/k8sdynamicclient.py import-3.8!skip +plugins/module_utils/k8sdynamicclient.py import-3.9!skip +plugins/module_utils/k8sdynamicclient.py import-3.10!skip +plugins/module_utils/k8sdynamicclient.py import-3.11!skip +plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc +plugins/modules/k8s_scale.py validate-modules:parameter-type-not-in-doc +plugins/modules/k8s_service.py validate-modules:parameter-type-not-in-doc +tests/unit/module_utils/fixtures/definitions.yml yamllint!skip +tests/unit/module_utils/fixtures/deployments.yml yamllint!skip +tests/unit/module_utils/fixtures/pods.yml yamllint!skip +tests/integration/targets/helm/files/appversionless-chart-v2/templates/configmap.yaml yamllint!skip +tests/integration/targets/helm/files/appversionless-chart/templates/configmap.yaml yamllint!skip +tests/integration/targets/helm/files/test-chart-v2/templates/configmap.yaml yamllint!skip +tests/integration/targets/helm/files/test-chart/templates/configmap.yaml yamllint!skip +tests/integration/targets/helm_diff/files/test-chart/templates/configmap.yaml yamllint!skip +tests/integration/targets/k8s_scale/files/deployment.yaml yamllint!skip +tests/sanity/refresh_ignore_files shebang!skip +plugins/modules/k8s.py validate-modules:return-syntax-error +plugins/modules/k8s_scale.py validate-modules:return-syntax-error +plugins/modules/k8s_service.py validate-modules:return-syntax-error +plugins/modules/k8s_taint.py validate-modules:return-syntax-error diff --git a/tests/unit/module_utils/test_helm.py b/tests/unit/module_utils/test_helm.py index de2c1569..0bb9a4b7 100644 --- a/tests/unit/module_utils/test_helm.py +++ b/tests/unit/module_utils/test_helm.py @@ -22,7 +22,7 @@ import string @pytest.fixture() -def ansible_helm_module(): +def _ansible_helm_module(): module = MagicMock() module.params = { "api_key": None, @@ -139,31 +139,31 @@ def test_module_get_helm_binary_from_system(): assert helm_module.get_helm_binary() == helm_sys_binary_path -def test_module_get_helm_plugin_list(ansible_helm_module): +def test_module_get_helm_plugin_list(_ansible_helm_module): - ansible_helm_module.run_helm_command = MagicMock() - ansible_helm_module.run_helm_command.return_value = (0, "output", "error") + _ansible_helm_module.run_helm_command = MagicMock() + _ansible_helm_module.run_helm_command.return_value = (0, "output", "error") - rc, out, err, command = ansible_helm_module.get_helm_plugin_list() + rc, out, err, command = _ansible_helm_module.get_helm_plugin_list() assert (rc, out, err) == (0, "output", "error") assert command == "some/path/to/helm/executable plugin list" - ansible_helm_module.get_helm_binary.assert_called_once() - ansible_helm_module.run_helm_command.assert_called_once_with( + _ansible_helm_module.get_helm_binary.assert_called_once() + _ansible_helm_module.run_helm_command.assert_called_once_with( "some/path/to/helm/executable plugin list" ) -def test_module_get_helm_plugin_list_failure(ansible_helm_module): +def test_module_get_helm_plugin_list_failure(_ansible_helm_module): - ansible_helm_module.run_helm_command = MagicMock() - ansible_helm_module.run_helm_command.return_value = (-1, "output", "error") + _ansible_helm_module.run_helm_command = MagicMock() + _ansible_helm_module.run_helm_command.return_value = (-1, "output", "error") with pytest.raises(SystemExit): - ansible_helm_module.get_helm_plugin_list() + _ansible_helm_module.get_helm_plugin_list() - ansible_helm_module.fail_json.assert_called_once_with( + _ansible_helm_module.fail_json.assert_called_once_with( msg="Failed to get Helm plugin info", command="some/path/to/helm/executable plugin list", stdout="output", @@ -174,7 +174,7 @@ def test_module_get_helm_plugin_list_failure(ansible_helm_module): @pytest.mark.parametrize("no_values", [True, False]) @pytest.mark.parametrize("get_all", [True, False]) -def test_module_get_values(ansible_helm_module, no_values, get_all): +def test_module_get_values(_ansible_helm_module, no_values, get_all): expected = {"test": "units"} output = "---\ntest: units\n" @@ -183,19 +183,19 @@ def test_module_get_values(ansible_helm_module, no_values, get_all): expected = {} output = "null" - ansible_helm_module.run_helm_command = MagicMock() - ansible_helm_module.run_helm_command.return_value = (0, output, "error") + _ansible_helm_module.run_helm_command = MagicMock() + _ansible_helm_module.run_helm_command.return_value = (0, output, "error") release_name = "".join( random.choice(string.ascii_letters + string.digits) for x in range(10) ) - result = ansible_helm_module.get_values(release_name, get_all=get_all) + result = _ansible_helm_module.get_values(release_name, get_all=get_all) - ansible_helm_module.get_helm_binary.assert_called_once() + _ansible_helm_module.get_helm_binary.assert_called_once() command = f"some/path/to/helm/executable get values --output=yaml {release_name}" if get_all: command += " -a" - ansible_helm_module.run_helm_command.assert_called_once_with(command) + _ansible_helm_module.run_helm_command.assert_called_once_with(command) assert result == expected @@ -210,20 +210,20 @@ def test_module_get_values(ansible_helm_module, no_values, get_all): ('Client: &version.Version{SemVer:"v3.12.3"', None), ], ) -def test_module_get_helm_version(ansible_helm_module, output, expected): +def test_module_get_helm_version(_ansible_helm_module, output, expected): - ansible_helm_module.run_command = MagicMock() - ansible_helm_module.run_command.return_value = (0, output, "error") + _ansible_helm_module.run_command = MagicMock() + _ansible_helm_module.run_command.return_value = (0, output, "error") - result = ansible_helm_module.get_helm_version() + result = _ansible_helm_module.get_helm_version() - ansible_helm_module.get_helm_binary.assert_called_once() + _ansible_helm_module.get_helm_binary.assert_called_once() command = "some/path/to/helm/executable version" - ansible_helm_module.run_command.assert_called_once_with(command) + _ansible_helm_module.run_command.assert_called_once_with(command) assert result == expected -def test_module_run_helm_command(ansible_helm_module): +def test_module_run_helm_command(_ansible_helm_module): error = "".join( random.choice(string.ascii_letters + string.digits) for x in range(10) @@ -232,26 +232,26 @@ def test_module_run_helm_command(ansible_helm_module): random.choice(string.ascii_letters + string.digits) for x in range(10) ) - ansible_helm_module.run_command.return_value = (0, output, error) + _ansible_helm_module.run_command.return_value = (0, output, error) - ansible_helm_module._prepare_helm_environment = MagicMock() + _ansible_helm_module._prepare_helm_environment = MagicMock() env_update = {x: random.choice(string.ascii_letters) for x in range(10)} - ansible_helm_module._prepare_helm_environment.return_value = env_update + _ansible_helm_module._prepare_helm_environment.return_value = env_update command = "".join( random.choice(string.ascii_letters + string.digits) for x in range(10) ) - rc, out, err = ansible_helm_module.run_helm_command(command) + rc, out, err = _ansible_helm_module.run_helm_command(command) assert (rc, out, err) == (0, output, error) - ansible_helm_module.run_command.assert_called_once_with( + _ansible_helm_module.run_command.assert_called_once_with( command, environ_update=env_update ) @pytest.mark.parametrize("fails_on_error", [True, False]) -def test_module_run_helm_command_failure(ansible_helm_module, fails_on_error): +def test_module_run_helm_command_failure(_ansible_helm_module, fails_on_error): error = "".join( random.choice(string.ascii_letters + string.digits) for x in range(10) @@ -260,9 +260,9 @@ def test_module_run_helm_command_failure(ansible_helm_module, fails_on_error): random.choice(string.ascii_letters + string.digits) for x in range(10) ) return_code = random.randint(1, 10) - ansible_helm_module.run_command.return_value = (return_code, output, error) + _ansible_helm_module.run_command.return_value = (return_code, output, error) - ansible_helm_module._prepare_environment = MagicMock() + _ansible_helm_module._prepare_environment = MagicMock() command = "".join( random.choice(string.ascii_letters + string.digits) for x in range(10) @@ -270,10 +270,10 @@ def test_module_run_helm_command_failure(ansible_helm_module, fails_on_error): if fails_on_error: with pytest.raises(SystemExit): - rc, out, err = ansible_helm_module.run_helm_command( + rc, out, err = _ansible_helm_module.run_helm_command( command, fails_on_error=fails_on_error ) - ansible_helm_module.fail_json.assert_called_with( + _ansible_helm_module.fail_json.assert_called_with( msg="Failure when executing Helm command. Exited {0}.\nstdout: {1}\nstderr: {2}".format( return_code, output, error ), @@ -282,7 +282,7 @@ def test_module_run_helm_command_failure(ansible_helm_module, fails_on_error): command=command, ) else: - rc, out, err = ansible_helm_module.run_helm_command( + rc, out, err = _ansible_helm_module.run_helm_command( command, fails_on_error=fails_on_error ) assert (rc, out, err) == (return_code, output, error)