summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/general/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:52:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:52:22 +0000
commit38b7c80217c4e72b1d8988eb1e60bb6e77334114 (patch)
tree356e9fd3762877d07cde52d21e77070aeff7e789 /ansible_collections/community/general/tests
parentAdding upstream version 7.7.0+dfsg. (diff)
downloadansible-38b7c80217c4e72b1d8988eb1e60bb6e77334114.tar.xz
ansible-38b7c80217c4e72b1d8988eb1e60bb6e77334114.zip
Adding upstream version 9.4.0+dfsg.upstream/9.4.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/community/general/tests')
-rw-r--r--ansible_collections/community/general/tests/.gitignore1
-rw-r--r--ansible_collections/community/general/tests/galaxy-importer.cfg8
-rw-r--r--ansible_collections/community/general/tests/integration/targets/aix_filesystem/tasks/main.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/alternatives/tasks/tests_set_priority.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/apache2_module/tasks/actualtest.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/apk/aliases13
-rw-r--r--ansible_collections/community/general/tests/integration/targets/apk/tasks/main.yml160
-rw-r--r--ansible_collections/community/general/tests/integration/targets/archive/tests/core.yml10
-rw-r--r--ansible_collections/community/general/tests/integration/targets/archive/tests/remove.yml30
-rw-r--r--ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/aliases2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/callback_default_without_diff/aliases6
-rw-r--r--ansible_collections/community/general/tests/integration/targets/callback_default_without_diff/tasks/main.yml65
-rw-r--r--ansible_collections/community/general/tests/integration/targets/cargo/tasks/main.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/cargo/tasks/setup.yml14
-rw-r--r--ansible_collections/community/general/tests/integration/targets/cargo/tasks/test_rustup_cargo.yml23
-rw-r--r--ansible_collections/community/general/tests/integration/targets/cloud_init_data_facts/tasks/main.yml11
-rw-r--r--ansible_collections/community/general/tests/integration/targets/cmd_runner/action_plugins/_unsafe_assert.py56
-rw-r--r--ansible_collections/community/general/tests/integration/targets/cmd_runner/library/cmd_echo.py5
-rw-r--r--ansible_collections/community/general/tests/integration/targets/cmd_runner/meta/main.yml7
-rw-r--r--ansible_collections/community/general/tests/integration/targets/cmd_runner/tasks/main.yml1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/cmd_runner/tasks/test_cmd_echo.yml28
-rw-r--r--ansible_collections/community/general/tests/integration/targets/cmd_runner/vars/main.yml139
-rw-r--r--ansible_collections/community/general/tests/integration/targets/connection_incus/aliases6
-rwxr-xr-xansible_collections/community/general/tests/integration/targets/connection_incus/runme.sh21
-rw-r--r--ansible_collections/community/general/tests/integration/targets/connection_incus/test_connection.inventory11
-rw-r--r--ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_auth_method.yml74
-rw-r--r--ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_binding_rule.yml73
-rw-r--r--ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_general.yml76
-rw-r--r--ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_kv.yml57
-rw-r--r--ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_policy.yml72
-rw-r--r--ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_role.yml194
-rw-r--r--ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_session.yml57
-rw-r--r--ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_token.yml88
-rw-r--r--ansible_collections/community/general/tests/integration/targets/consul/tasks/main.yml28
-rw-r--r--ansible_collections/community/general/tests/integration/targets/consul/templates/consul_config.hcl.j25
-rw-r--r--ansible_collections/community/general/tests/integration/targets/copr/tasks/main.yml6
-rw-r--r--ansible_collections/community/general/tests/integration/targets/deploy_helper/tasks/main.yml18
-rw-r--r--ansible_collections/community/general/tests/integration/targets/django_manage/aliases5
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ejabberd_user/aliases11
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ejabberd_user/handlers/main.yml9
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ejabberd_user/meta/main.yml7
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ejabberd_user/tasks/main.yml122
-rw-r--r--ansible_collections/community/general/tests/integration/targets/etcd3/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filesize/tasks/sparse.yml122
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filesystem/defaults/main.yml28
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filesystem/tasks/main.yml3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filesystem/tasks/reset_fs_uuid.yml59
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filesystem/tasks/set_fs_uuid_on_creation.yml44
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filesystem/tasks/set_fs_uuid_on_creation_with_opts.yml33
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_counter/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_dict/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_dict_kv/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_from_csv/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_from_ini/tasks/main.yml60
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_from_ini/vars/main.yml8
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_groupby_as_dict/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_hashids/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_jc/aliases3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_json_query/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_lists/aliases5
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_lists/tasks/main.yml64
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_lists/vars/main.yml8
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_path_join_shim/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_random_mac/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_time/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_to_ini/tasks/main.yml58
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_to_ini/vars/main.yml8
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_unicode_normalize/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/filter_version_sort/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gem/tasks/main.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config/files/gitconfig5
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config/tasks/get_set_state_present_file.yml1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config/tasks/main.yml3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config/tasks/set_multi_value.yml79
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config/tasks/set_value.yml39
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config/tasks/set_value_with_tilde.yml4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_multi_value.yml28
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config_info/aliases7
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config_info/files/gitconfig11
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config_info/meta/main.yml7
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/error_handling.yml26
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/get_all_values.yml19
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/get_multi_value.yml20
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/get_simple_value.yml38
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/main.yml33
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/setup.yml15
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/setup_file.yml16
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/setup_global.yml16
-rw-r--r--ansible_collections/community/general/tests/integration/targets/git_config_info/vars/main.yml7
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_group_access_token/aliases7
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_group_access_token/defaults/main.yml15
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_group_access_token/tasks/main.yml221
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_instance_variable/aliases5
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_instance_variable/tasks/main.yml606
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_issue/aliases6
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_issue/defaults/main.yml15
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_issue/files/description.md9
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_issue/tasks/main.yml150
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_label/README.md19
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_label/aliases6
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_label/defaults/main.yml17
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_label/tasks/main.yml463
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_merge_request/aliases6
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_merge_request/defaults/main.yml14
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_merge_request/files/description.md9
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_merge_request/tasks/main.yml129
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_milestone/README.md19
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_milestone/aliases6
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_milestone/defaults/main.yml18
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_milestone/tasks/main.yml388
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_project_access_token/aliases7
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_project_access_token/defaults/main.yml15
-rw-r--r--ansible_collections/community/general/tests/integration/targets/gitlab_project_access_token/tasks/main.yml221
-rw-r--r--ansible_collections/community/general/tests/integration/targets/homebrew/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/homebrew/tasks/casks.yml99
-rw-r--r--ansible_collections/community/general/tests/integration/targets/homebrew/tasks/formulae.yml99
-rw-r--r--ansible_collections/community/general/tests/integration/targets/homebrew/tasks/main.yml92
-rw-r--r--ansible_collections/community/general/tests/integration/targets/homebrew_cask/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/homectl/aliases2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/htpasswd/aliases7
-rw-r--r--ansible_collections/community/general/tests/integration/targets/htpasswd/handlers/main.yml9
-rw-r--r--ansible_collections/community/general/tests/integration/targets/htpasswd/meta/main.yml7
-rw-r--r--ansible_collections/community/general/tests/integration/targets/htpasswd/tasks/main.yml83
-rw-r--r--ansible_collections/community/general/tests/integration/targets/htpasswd/vars/main.yml6
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ini_file/tasks/main.yml12
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/00-basic.yml4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/04-symlink.yml59
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/05-ignore_spaces.yml123
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/06-modify_inactive_option.yml123
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/07-section_name_spaces.yml103
-rw-r--r--ansible_collections/community/general/tests/integration/targets/interfaces_file/tasks/main.yml58
-rw-r--r--ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/main.yml6
-rw-r--r--ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/tests/02-partial-restore.yml66
-rw-r--r--ansible_collections/community/general/tests/integration/targets/iso_create/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/iso_customize/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize_exception.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/iso_extract/aliases4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/java_cert/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/java_cert/files/setupSSLServer.py13
-rw-r--r--ansible_collections/community/general/tests/integration/targets/java_cert/tasks/state_change.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/java_keystore/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/kernel_blacklist/aliases4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/kernel_blacklist/handlers/main.yml10
-rw-r--r--ansible_collections/community/general/tests/integration/targets/kernel_blacklist/tasks/main.yml72
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/readme.adoc2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/aliases5
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/Containerfile4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/Containerfile.license3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/META-INF/keycloak-scripts.json14
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/META-INF/keycloak-scripts.json.license3
-rwxr-xr-xansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/build-policy.sh2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/build-policy.sh.license3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/policy-1.js1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/policy-1.js.license3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/policy-2.js1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/policy-2.js.license3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/readme.adoc27
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/tasks/main.yml168
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/vars/main.yml11
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_permission/aliases6
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_permission/readme.adoc27
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_permission/tasks/main.yml567
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_authz_permission/vars/main.yml11
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_client/README.md20
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_client/docker-compose.yml31
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_client/tasks/main.yml120
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_client/vars/main.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_component_info/README.md20
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_component_info/aliases5
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_component_info/tasks/main.yml266
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_component_info/vars/main.yml19
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_group/readme.adoc2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_group_rolemapping/README.md21
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_group_rolemapping/aliases4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_group_rolemapping/tasks/main.yml160
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_group_rolemapping/vars/main.yml15
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/tasks/main.yml113
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_realm_key/aliases5
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_realm_key/readme.adoc27
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_realm_key/tasks/main.yml373
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_realm_key/vars/main.yml48
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_role/README.md20
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_role/tasks/main.yml233
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_role/vars/main.yml27
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_user/README.md21
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_user/aliases4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_user/tasks/main.yml114
-rw-r--r--ansible_collections/community/general/tests/integration/targets/keycloak_user/vars/main.yml46
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/auth.yml47
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/basic.yml14
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/pages.yml24
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/schema.yml25
-rw-r--r--ansible_collections/community/general/tests/integration/targets/listen_ports_facts/tasks/main.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/locale_gen/aliases2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/basic.yml102
-rw-r--r--ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/locale_gen.yml99
-rw-r--r--ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/main.yml10
-rw-r--r--ansible_collections/community/general/tests/integration/targets/locale_gen/vars/main.yml17
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lookup_cartesian/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lookup_dependent/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lookup_dig/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lookup_etcd3/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lookup_flattened/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/aliases1
-rwxr-xr-xansible_collections/community/general/tests/integration/targets/lookup_merge_variables/runme.sh3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_all_hosts.yml64
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_inventory_all_hosts.yml52
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lookup_random_pet/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lookup_random_string/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lookup_random_words/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvg/tasks/main.yml10
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvg/tasks/setup.yml20
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvg/tasks/setup_missing_pv.yml18
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvg/tasks/teardown.yml14
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvg/tasks/teardown_missing_pv.yml8
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_active_change.yml163
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_active_create.yml71
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_pvresize.yml16
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_uuid_reset.yml107
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvg_rename/aliases13
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvg_rename/meta/main.yml9
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvg_rename/tasks/main.yml25
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvg_rename/tasks/setup.yml50
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvg_rename/tasks/teardown.yml46
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvg_rename/tasks/test.yml105
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvol/aliases12
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvol/meta/main.yml8
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvol/tasks/main.yml24
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvol/tasks/setup.yml57
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvol/tasks/teardown.yml40
-rw-r--r--ansible_collections/community/general/tests/integration/targets/lvol/tasks/test_pvs.yml64
-rw-r--r--ansible_collections/community/general/tests/integration/targets/mail/tasks/main.yml171
-rw-r--r--ansible_collections/community/general/tests/integration/targets/mas/tasks/main.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/monit/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/mssql_script/tasks/main.yml37
-rw-r--r--ansible_collections/community/general/tests/integration/targets/nomad/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/npm/tasks/test.yml13
-rw-r--r--ansible_collections/community/general/tests/integration/targets/odbc/aliases2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/odbc/tasks/main.yml1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/one_host/tasks/main.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/osx_defaults/tasks/main.yml6
-rw-r--r--ansible_collections/community/general/tests/integration/targets/pacman/handlers/main.yml23
-rw-r--r--ansible_collections/community/general/tests/integration/targets/pacman/tasks/locally_installed_package.yml3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/pacman/tasks/main.yml1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/pacman/tasks/remove_nosave.yml6
-rw-r--r--ansible_collections/community/general/tests/integration/targets/pacman/tasks/yay-become.yml66
-rw-r--r--ansible_collections/community/general/tests/integration/targets/pids/tasks/main.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/pids/templates/obtainpid.sh.j2 (renamed from ansible_collections/community/general/tests/integration/targets/pids/templates/obtainpid.sh)0
-rw-r--r--ansible_collections/community/general/tests/integration/targets/pipx/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/pipx/tasks/main.yml25
-rw-r--r--ansible_collections/community/general/tests/integration/targets/pipx_info/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/pkgng/tasks/freebsd.yml9
-rw-r--r--ansible_collections/community/general/tests/integration/targets/pnpm/aliases8
-rw-r--r--ansible_collections/community/general/tests/integration/targets/pnpm/meta/main.yml9
-rw-r--r--ansible_collections/community/general/tests/integration/targets/pnpm/tasks/main.yml27
-rw-r--r--ansible_collections/community/general/tests/integration/targets/pnpm/tasks/run.yml322
-rw-r--r--ansible_collections/community/general/tests/integration/targets/pnpm/templates/package.j213
-rw-r--r--ansible_collections/community/general/tests/integration/targets/proxmox/tasks/main.yml36
-rw-r--r--ansible_collections/community/general/tests/integration/targets/proxmox_pool/aliases7
-rw-r--r--ansible_collections/community/general/tests/integration/targets/proxmox_pool/defaults/main.yml7
-rw-r--r--ansible_collections/community/general/tests/integration/targets/proxmox_pool/tasks/main.yml220
-rw-r--r--ansible_collections/community/general/tests/integration/targets/proxmox_template/aliases6
-rw-r--r--ansible_collections/community/general/tests/integration/targets/proxmox_template/tasks/main.yml136
-rw-r--r--ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/pagination.yml4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/scaleway_container_registry/tasks/main.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/scaleway_image_info/tasks/main.yml4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/scaleway_ip_info/tasks/main.yml4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/scaleway_organization_info/tasks/main.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/scaleway_security_group_info/tasks/main.yml4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/scaleway_server_info/tasks/main.yml4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/scaleway_snapshot_info/tasks/main.yml4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/scaleway_volume_info/tasks/main.yml4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_docker/handlers/main.yml18
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_docker/tasks/main.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_etcd3/tasks/main.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_java_keytool/tasks/main.yml12
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Alpine.yml3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Archlinux.yml3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Debian-12.yml8
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Debian.yml3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/RedHat.yml3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Suse.yml3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_openldap/files/cert_cnconfig.ldif15
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_openldap/files/cert_cnconfig.ldif.license (renamed from ansible_collections/community/general/tests/sanity/ignore-2.11.txt.license)0
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_openldap/files/initial_config.ldif19
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_openldap/tasks/main.yml22
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_pkg_mgr/tasks/archlinux.yml5
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/tasks/main.yml26
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Debian-12-py3.yml13
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-9.2.yml6
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-9.3.yml6
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_certificate.pem4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_certificate.pem.license3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_key.pem4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_key.pem.license3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_certificate.pem4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_certificate.pem.license3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_key.pem4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_key.pem.license3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_certificate.pem4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_certificate.pem.license3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_key.pem4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_key.pem.license3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/shutdown/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/shutdown/tasks/main.yml73
-rw-r--r--ansible_collections/community/general/tests/integration/targets/snap/meta/main.yml1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/snap/tasks/main.yml244
-rw-r--r--ansible_collections/community/general/tests/integration/targets/snap/tasks/test.yml235
-rw-r--r--ansible_collections/community/general/tests/integration/targets/snap/tasks/test_3dash.yml31
-rw-r--r--ansible_collections/community/general/tests/integration/targets/snap/tasks/test_channel.yml81
-rw-r--r--ansible_collections/community/general/tests/integration/targets/snap/tasks/test_dangerous.yml53
-rw-r--r--ansible_collections/community/general/tests/integration/targets/snap/tasks/test_empty_list.yml14
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ssh_config/aliases1
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ssh_config/tasks/options.yml94
-rw-r--r--ansible_collections/community/general/tests/integration/targets/sudoers/tasks/main.yml15
-rw-r--r--ansible_collections/community/general/tests/integration/targets/sysrc/tasks/main.yml4
-rw-r--r--ansible_collections/community/general/tests/integration/targets/terraform/tasks/main.yml2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/aliases5
-rwxr-xr-xansible_collections/community/general/tests/integration/targets/test_fqdn_valid/runme.sh15
-rw-r--r--ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/runme.yml8
-rw-r--r--ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/tasks/fqdn_valid_1.yml58
-rw-r--r--ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/tasks/main.yml7
-rw-r--r--ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/vars/main.yml15
-rw-r--r--ansible_collections/community/general/tests/integration/targets/timezone/tasks/main.yml3
-rw-r--r--ansible_collections/community/general/tests/integration/targets/ufw/aliases2
-rw-r--r--ansible_collections/community/general/tests/integration/targets/xml/tasks/main.yml9
-rwxr-xr-xansible_collections/community/general/tests/sanity/extra/botmeta.py6
-rwxr-xr-xansible_collections/community/general/tests/sanity/extra/extra-docs.py2
-rw-r--r--ansible_collections/community/general/tests/sanity/ignore-2.11.txt28
-rw-r--r--ansible_collections/community/general/tests/sanity/ignore-2.12.txt21
-rw-r--r--ansible_collections/community/general/tests/sanity/ignore-2.13.txt20
-rw-r--r--ansible_collections/community/general/tests/sanity/ignore-2.14.txt20
-rw-r--r--ansible_collections/community/general/tests/sanity/ignore-2.15.txt17
-rw-r--r--ansible_collections/community/general/tests/sanity/ignore-2.16.txt14
-rw-r--r--ansible_collections/community/general/tests/sanity/ignore-2.17.txt17
-rw-r--r--ansible_collections/community/general/tests/sanity/ignore-2.17.txt.license (renamed from ansible_collections/community/general/tests/sanity/ignore-2.12.txt.license)0
-rw-r--r--ansible_collections/community/general/tests/unit/mock/loader.py2
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/become/helper.py2
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/callback/test_loganalytics.py7
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/connection/test_lxc.py134
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/inventory/test_icinga2.py3
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/inventory/test_linode.py18
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/inventory/test_proxmox.py45
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/lookup/conftest.py (renamed from ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_conftest.py)8
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_common.py210
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_01.json4
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_04.json67
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_04.json.license3
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_05.json102
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_05.json.license3
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/lookup/test_bitwarden.py47
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/lookup/test_bitwarden_secrets_manager.py83
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/lookup/test_github_app_access_token.py52
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/lookup/test_merge_variables.py161
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/lookup/test_onepassword.py67
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/module_utils/hwc/test_hwc_utils.py25
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/module_utils/test_cmd_runner.py102
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/module_utils/test_module_helper.py80
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/module_utils/test_vardict.py134
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/gitlab.py60
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/helper.py181
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_cpanm.py277
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_cpanm.yaml220
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_dnf_config_manager.py402
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_dnsimple.py4
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_dnsimple_info.py2
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_facter_facts.py14
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_facter_facts.yaml40
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2.py106
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2.yaml117
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2_info.py93
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2_info.yaml28
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_gem.py2
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_gio_mime.py14
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_gio_mime.yaml70
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_group_access_token.py107
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_project_access_token.py107
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_hana_query.py103
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_ini_file.py51
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_otptoken.py22
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_pwpolicy.py123
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_ipbase.py187
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_jenkins_build.py40
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_jenkins_build_info.py180
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_authentication_required_actions.py835
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_client_rolemapping.py7
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_role.py370
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_user.py354
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_user_federation.py4
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_lvg_rename.py160
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_modprobe.py4
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_nmcli.py588
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_nomad_token.py222
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_npm.py38
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_opkg.py231
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_opkg.yaml142
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty.py30
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty_alert.py113
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_pkgin.py16
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_kvm.py164
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_snap.py8
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_storage_contents_info.py90
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_tasks_info.py10
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_template.py66
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_vm_info.py714
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_puppet.py211
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_puppet.yaml192
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_redhat_subscription.py4
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_redis_info.py36
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_rhsm_release.py14
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_rhsm_repository.py833
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_sap_task_list_execute.py91
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_sapcar_extract.py54
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_simpleinit_msb.py200
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_slack.py6
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_snap.py474
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_usb_facts.py105
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf.py296
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf.yaml185
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf_info.py163
-rw-r--r--ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf_info.yaml83
-rw-r--r--ansible_collections/community/general/tests/unit/requirements.txt14
-rw-r--r--ansible_collections/community/general/tests/utils/constraints.txt2
-rwxr-xr-xansible_collections/community/general/tests/utils/shippable/shippable.sh11
428 files changed, 20413 insertions, 2951 deletions
diff --git a/ansible_collections/community/general/tests/.gitignore b/ansible_collections/community/general/tests/.gitignore
index 6edf5dc10..0d36555dd 100644
--- a/ansible_collections/community/general/tests/.gitignore
+++ b/ansible_collections/community/general/tests/.gitignore
@@ -3,3 +3,4 @@
# SPDX-License-Identifier: GPL-3.0-or-later
output/
+integration/inventory
diff --git a/ansible_collections/community/general/tests/galaxy-importer.cfg b/ansible_collections/community/general/tests/galaxy-importer.cfg
new file mode 100644
index 000000000..5ab20d06a
--- /dev/null
+++ b/ansible_collections/community/general/tests/galaxy-importer.cfg
@@ -0,0 +1,8 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+[galaxy-importer]
+# This is only needed to make Zuul's third-party-check happy.
+# It is not needed by anything else.
+run_ansible_doc=false
diff --git a/ansible_collections/community/general/tests/integration/targets/aix_filesystem/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/aix_filesystem/tasks/main.yml
index 25146062d..878088f4e 100644
--- a/ansible_collections/community/general/tests/integration/targets/aix_filesystem/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/aix_filesystem/tasks/main.yml
@@ -90,7 +90,7 @@
size: -2G
state: present
-- name: Resizing /mksysb to 100G (no enought space)
+- name: Resizing /mksysb to 100G (not enough space)
aix_filesystem:
filesystem: /mksysb
size: +100G
diff --git a/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/tests_set_priority.yml b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/tests_set_priority.yml
index 46cf48e59..9bc523b0d 100644
--- a/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/tests_set_priority.yml
+++ b/ansible_collections/community/general/tests/integration/targets/alternatives/tasks/tests_set_priority.yml
@@ -22,7 +22,7 @@
assert:
that:
- 'alternative is changed'
- - 'cmd.stdout == "dummy{{ item }}"'
+ - 'cmd.stdout == "dummy" ~ item'
- name: check that alternative has been updated
command: "grep -Pzq '/bin/dummy{{ item }}\\n{{ 60 + item|int }}' '{{ alternatives_dir }}/dummy'"
diff --git a/ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/aliases b/ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/aliases
index 13655b194..297477ac9 100644
--- a/ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/ansible_galaxy_install/aliases
@@ -4,5 +4,4 @@
azp/posix/3
destructive
-skip/python2.6
context/controller # While this is not really true, this module mainly is run on the controller, *and* needs access to the ansible-galaxy CLI tool
diff --git a/ansible_collections/community/general/tests/integration/targets/apache2_module/tasks/actualtest.yml b/ansible_collections/community/general/tests/integration/targets/apache2_module/tasks/actualtest.yml
index 3301a16b1..6fd10ce57 100644
--- a/ansible_collections/community/general/tests/integration/targets/apache2_module/tasks/actualtest.yml
+++ b/ansible_collections/community/general/tests/integration/targets/apache2_module/tasks/actualtest.yml
@@ -73,7 +73,7 @@
state: absent
force: true
- - name: reenable autoindex
+ - name: re-enable autoindex
community.general.apache2_module:
name: autoindex
state: present
diff --git a/ansible_collections/community/general/tests/integration/targets/apk/aliases b/ansible_collections/community/general/tests/integration/targets/apk/aliases
new file mode 100644
index 000000000..6e8c01586
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/apk/aliases
@@ -0,0 +1,13 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+azp/posix/2
+needs/root
+destructive
+skip/aix
+skip/osx
+skip/macos
+skip/freebsd
+skip/rhel
+skip/ubuntu \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/apk/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/apk/tasks/main.yml
new file mode 100644
index 000000000..0e1b0ae42
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/apk/tasks/main.yml
@@ -0,0 +1,160 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) 2024, Max Maxopoly <max@dermax.org>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+- name: Run apk tests on Alpine
+ when: ansible_distribution in ['Alpine']
+ block:
+ - name: Ensure vim is not installed
+ community.general.apk:
+ name: vim
+ state: absent
+
+ - name: Install vim
+ community.general.apk:
+ name: vim
+ state: present
+ register: results
+
+ - name: Ensure vim was installed
+ ansible.builtin.assert:
+ that:
+ - results is changed
+ - (results.packages | length) >= 1 # vim has dependencies, so depending on the base image this number may vary
+
+ - name: Install vim again
+ community.general.apk:
+ name: vim
+ state: present
+ register: results
+
+ - name: Ensure vim was not installed again
+ ansible.builtin.assert:
+ that:
+ - results is not changed
+ - (results.packages | default([]) | length) == 0
+
+ - name: Ensure vim is not installed
+ community.general.apk:
+ name: vim
+ state: absent
+ register: results
+
+ - name: Ensure vim was uninstalled
+ ansible.builtin.assert:
+ that:
+ - results is changed
+ - (results.packages | length) >= 1
+
+ - name: Install vim without cache
+ community.general.apk:
+ name: vim
+ state: present
+ no_cache: true
+ register: results
+
+ - name: Ensure vim was installed without cache
+ ansible.builtin.assert:
+ that:
+ - results is changed
+
+ - name: Install vim again without cache
+ community.general.apk:
+ name: vim
+ state: present
+ no_cache: true
+ register: results
+
+ - name: Ensure vim was not installed again without cache
+ ansible.builtin.assert:
+ that:
+ - results is not changed
+ - (results.packages | default([]) | length) == 0
+
+ - name: Ensure a bunch of packages aren't installed
+ community.general.apk:
+ name:
+ - less
+ - nano
+ - vim
+ state: absent
+
+ - name: Install a bunch of packages
+ community.general.apk:
+ name:
+ - less
+ - nano
+ - vim
+ state: present
+ register: results
+
+ - name: Ensure a bunch of packages were installed
+ ansible.builtin.assert:
+ that:
+ - results is changed
+ - (results.packages | length) >= 3
+
+ - name: Install a bunch of packages again
+ community.general.apk:
+ name:
+ - less
+ - nano
+ - vim
+ state: present
+ register: results
+
+ - name: Ensure a bunch of packages were not installed again
+ ansible.builtin.assert:
+ that:
+ - results is not changed
+ - (results.packages | default([]) | length) == 0
+
+ - name: Ensure a bunch of packages are not installed
+ community.general.apk:
+ name:
+ - less
+ - nano
+ - vim
+ state: absent
+ register: results
+
+ - name: Ensure a bunch of packages were uninstalled
+ ansible.builtin.assert:
+ that:
+ - results is changed
+ - (results.packages | length) >= 3
+
+ - name: Install a bunch of packages without cache
+ community.general.apk:
+ name:
+ - less
+ - nano
+ - vim
+ state: present
+ no_cache: true
+ register: results
+
+ - name: Ensure a bunch of packages were installed without cache
+ ansible.builtin.assert:
+ that:
+ - results is changed
+
+ - name: Install a bunch of packages again without cache
+ community.general.apk:
+ name:
+ - less
+ - nano
+ - vim
+ state: present
+ no_cache: true
+ register: results
+
+ - name: Ensure a bunch of packages were not installed again without cache
+ ansible.builtin.assert:
+ that:
+ - results is not changed
+ - (results.packages | default([]) | length) == 0
diff --git a/ansible_collections/community/general/tests/integration/targets/archive/tests/core.yml b/ansible_collections/community/general/tests/integration/targets/archive/tests/core.yml
index 1c4f4d1aa..21b07038f 100644
--- a/ansible_collections/community/general/tests/integration/targets/archive/tests/core.yml
+++ b/ansible_collections/community/general/tests/integration/targets/archive/tests/core.yml
@@ -29,7 +29,7 @@
that:
- archive_no_options is changed
- "archive_no_options.dest_state == 'archive'"
- - "{{ archive_no_options.archived | length }} == 3"
+ - "archive_no_options.archived | length == 3"
- name: Remove the archive - no options ({{ format }})
file:
@@ -54,7 +54,7 @@
that:
- archive_file_options_stat is not changed
- "archive_file_options.mode == '0600'"
- - "{{ archive_file_options.archived | length }} == 3"
+ - "archive_file_options.archived | length == 3"
- name: Remove the archive - file options ({{ format }})
file:
@@ -146,7 +146,7 @@
assert:
that:
- archive_path_list is changed
- - "{{ archive_path_list.archived | length }} == 3"
+ - "archive_path_list.archived | length == 3"
- name: Remove archive - path list ({{ format }})
file:
@@ -168,8 +168,8 @@
that:
- archive_missing_paths is changed
- "archive_missing_paths.dest_state == 'incomplete'"
- - "'{{ remote_tmp_dir }}/dne.txt' in archive_missing_paths.missing"
- - "'{{ remote_tmp_dir }}/foo.txt' not in archive_missing_paths.missing"
+ - "(remote_tmp_dir ~ '/dne.txt') in archive_missing_paths.missing"
+ - "(remote_tmp_dir ~ '/foo.txt') not in archive_missing_paths.missing"
- name: Remove archive - missing paths ({{ format }})
file:
diff --git a/ansible_collections/community/general/tests/integration/targets/archive/tests/remove.yml b/ansible_collections/community/general/tests/integration/targets/archive/tests/remove.yml
index 8f0b8cff8..a7e151d25 100644
--- a/ansible_collections/community/general/tests/integration/targets/archive/tests/remove.yml
+++ b/ansible_collections/community/general/tests/integration/targets/archive/tests/remove.yml
@@ -20,21 +20,28 @@
assert:
that:
- archive_remove_source_files is changed
- - "{{ archive_remove_source_files.archived | length }} == 3"
+ - "archive_remove_source_files.archived | length == 3"
- name: Remove Archive - remove source files ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_remove_source_files.{{ format }}"
state: absent
-- name: Assert that source files were removed - remove source files ({{ format }})
- assert:
- that:
- - "'{{ remote_tmp_dir }}/{{ item }}' is not exists"
+- name: Remove source files in check mode ({{ format }})
+ file:
+ path: "{{ remote_tmp_dir }}/{{ item }}"
+ state: absent
+ check_mode: true
with_items:
- foo.txt
- bar.txt
- empty.txt
+ register: remove_files
+
+- name: Assert that source files were removed - remove source files ({{ format }})
+ assert:
+ that:
+ - remove_files is not changed
- name: Copy source files - remove source directory ({{ format }})
copy:
@@ -76,17 +83,24 @@
assert:
that:
- archive_remove_source_directory is changed
- - "{{ archive_remove_source_directory.archived | length }} == 3"
+ - "archive_remove_source_directory.archived | length == 3"
- name: Remove archive - remove source directory ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_remove_source_directory.{{ format }}"
state: absent
+- name: Remove source source directory in check mode ({{ format }})
+ file:
+ path: "{{ remote_tmp_dir }}/tmpdir"
+ state: absent
+ check_mode: true
+ register: remove_dir
+
- name: Verify source directory was removed - remove source directory ({{ format }})
assert:
that:
- - "'{{ remote_tmp_dir }}/tmpdir' is not exists"
+ - remove_dir is not changed
- name: Create temporary directory - remove source excluding path ({{ format }})
file:
@@ -120,7 +134,7 @@
assert:
that:
- archive_remove_source_excluding_path is changed
- - "{{ archive_remove_source_excluding_path.archived | length }} == 2"
+ - "archive_remove_source_excluding_path.archived | length == 2"
- name: Remove archive - remove source excluding path ({{ format }})
file:
diff --git a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/aliases b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/aliases
index 914c36ad3..f5b6800db 100644
--- a/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/btrfs_subvolume/aliases
@@ -1,4 +1,4 @@
-# Copyright (c) Ansible Projec
+# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/ansible_collections/community/general/tests/integration/targets/callback_default_without_diff/aliases b/ansible_collections/community/general/tests/integration/targets/callback_default_without_diff/aliases
new file mode 100644
index 000000000..3e2dd244c
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/callback_default_without_diff/aliases
@@ -0,0 +1,6 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+azp/posix/3
+needs/target/callback
diff --git a/ansible_collections/community/general/tests/integration/targets/callback_default_without_diff/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/callback_default_without_diff/tasks/main.yml
new file mode 100644
index 000000000..5fc656e84
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/callback_default_without_diff/tasks/main.yml
@@ -0,0 +1,65 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- block:
+ - name: Create temporary file
+ tempfile:
+ register: tempfile
+
+ - name: Run tests
+ include_role:
+ name: callback
+ vars:
+ tests:
+ - name: Basic file diff
+ environment:
+ ANSIBLE_NOCOLOR: 'true'
+ ANSIBLE_FORCE_COLOR: 'false'
+ ANSIBLE_DIFF_ALWAYS: 'true'
+ ANSIBLE_PYTHON_INTERPRETER: "{{ ansible_python_interpreter }}"
+ ANSIBLE_STDOUT_CALLBACK: community.general.default_without_diff
+ playbook: |
+ - hosts: testhost
+ gather_facts: true
+ tasks:
+ - name: Create file
+ copy:
+ dest: "{{ tempfile.path }}"
+ content: |
+ Foo bar
+
+ - name: Modify file
+ copy:
+ dest: "{{ tempfile.path }}"
+ content: |
+ Foo bar
+ Bar baz bam!
+ expected_output: [
+ "",
+ "PLAY [testhost] ****************************************************************",
+ "",
+ "TASK [Gathering Facts] *********************************************************",
+ "ok: [testhost]",
+ "",
+ "TASK [Create file] *************************************************************",
+ "changed: [testhost]",
+ "",
+ "TASK [Modify file] *************************************************************",
+ "changed: [testhost]",
+ "",
+ "PLAY RECAP *********************************************************************",
+ "testhost : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 ",
+ ]
+
+ always:
+ - name: Clean up temp file
+ file:
+ path: "{{ tempfile.path }}"
+ state: absent
diff --git a/ansible_collections/community/general/tests/integration/targets/cargo/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/main.yml
index bb22e27c0..29f27c3fd 100644
--- a/ansible_collections/community/general/tests/integration/targets/cargo/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/main.yml
@@ -18,3 +18,5 @@
- import_tasks: test_version.yml
environment: "{{ cargo_environment }}"
when: has_cargo | default(false)
+- import_tasks: test_rustup_cargo.yml
+ when: rustup_cargo_bin | default(false)
diff --git a/ansible_collections/community/general/tests/integration/targets/cargo/tasks/setup.yml b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/setup.yml
index 232658ab4..7eec97ac4 100644
--- a/ansible_collections/community/general/tests/integration/targets/cargo/tasks/setup.yml
+++ b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/setup.yml
@@ -26,3 +26,17 @@
has_cargo: true
when:
- ansible_system == 'FreeBSD' and ansible_distribution_version is version('13.0', '>')
+
+- block:
+ - name: Download rustup
+ get_url:
+ url: https://sh.rustup.rs
+ dest: /tmp/sh.rustup.rs
+ mode: "0750"
+ force: true
+ - name: Install rustup cargo
+ command: /tmp/sh.rustup.rs -y
+ - set_fact:
+ rustup_cargo_bin: "{{ lookup('env', 'HOME') }}/.cargo/bin/cargo"
+ when:
+ - ansible_distribution != 'CentOS' or ansible_distribution_version is version('7.0', '>=')
diff --git a/ansible_collections/community/general/tests/integration/targets/cargo/tasks/test_rustup_cargo.yml b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/test_rustup_cargo.yml
new file mode 100644
index 000000000..ec2cf6e6d
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/cargo/tasks/test_rustup_cargo.yml
@@ -0,0 +1,23 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+- name: Install application helloworld
+ community.general.cargo:
+ executable: "{{ rustup_cargo_bin }}"
+ name: helloworld
+ register: rustup_install_absent_helloworld
+
+- name: Uninstall application helloworld
+ community.general.cargo:
+ executable: "{{ rustup_cargo_bin }}"
+ state: absent
+ name: helloworld
+ register: rustup_uninstall_present_helloworld
+
+- name: Check assertions helloworld
+ assert:
+ that:
+ - rustup_install_absent_helloworld is changed
+ - rustup_uninstall_present_helloworld is changed
diff --git a/ansible_collections/community/general/tests/integration/targets/cloud_init_data_facts/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/cloud_init_data_facts/tasks/main.yml
index 40e762d68..2b67b5c17 100644
--- a/ansible_collections/community/general/tests/integration/targets/cloud_init_data_facts/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/cloud_init_data_facts/tasks/main.yml
@@ -8,6 +8,14 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
+- name: Help debugging
+ debug:
+ msg: >-
+ distribution={{ ansible_distribution }},
+ distribution major version={{ ansible_distribution_major_version }},
+ os_family={{ ansible_os_family }},
+ Python version={{ ansible_python.version.major }}
+
- name: test cloud-init
# TODO: check for a workaround
# install 'cloud-init'' failed: dpkg-divert: error: `diversion of /etc/init/ureadahead.conf
@@ -15,10 +23,11 @@
# /etc/init/ureadahead.conf to /etc/init/ureadahead.conf.distrib
# https://bugs.launchpad.net/ubuntu/+source/ureadahead/+bug/997838
# Will also have to skip on OpenSUSE when running on Python 2 on newer Leap versions
- # (!= 42 and >= 15) ascloud-init will install the Python 3 package, breaking our build on py2.
+ # (!= 42 and >= 15) as cloud-init will install the Python 3 package, breaking our build on py2.
when:
- not (ansible_distribution == "Ubuntu" and ansible_distribution_major_version|int == 14)
- not (ansible_os_family == "Suse" and ansible_distribution_major_version|int != 42 and ansible_python.version.major != 3)
+ - not (ansible_os_family == "Suse" and ansible_distribution_major_version|int == 15)
- not (ansible_distribution == "CentOS" and ansible_distribution_major_version|int == 8) # TODO: cannot start service
- not (ansible_distribution == 'Archlinux') # TODO: package seems to be broken, cannot be downloaded from mirrors?
- not (ansible_distribution == 'Alpine') # TODO: not sure what's wrong here, the module doesn't return what the tests expect
diff --git a/ansible_collections/community/general/tests/integration/targets/cmd_runner/action_plugins/_unsafe_assert.py b/ansible_collections/community/general/tests/integration/targets/cmd_runner/action_plugins/_unsafe_assert.py
new file mode 100644
index 000000000..498e8258d
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/cmd_runner/action_plugins/_unsafe_assert.py
@@ -0,0 +1,56 @@
+# Copyright 2012, Dag Wieers <dag@wieers.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from ansible.errors import AnsibleError
+from ansible.playbook.conditional import Conditional
+from ansible.plugins.action import ActionBase
+
+
+class ActionModule(ActionBase):
+ ''' Fail with custom message '''
+
+ _requires_connection = False
+
+ _VALID_ARGS = frozenset(('msg', 'that'))
+
+ def _make_safe(self, text):
+ # A simple str(text) won't do it since AnsibleUnsafeText is clever :-)
+ return ''.join(chr(ord(x)) for x in text)
+
+ def run(self, tmp=None, task_vars=None):
+ if task_vars is None:
+ task_vars = dict()
+
+ result = super(ActionModule, self).run(tmp, task_vars)
+ del tmp # tmp no longer has any effect
+
+ if 'that' not in self._task.args:
+ raise AnsibleError('conditional required in "that" string')
+
+ fail_msg = 'Assertion failed'
+ success_msg = 'All assertions passed'
+
+ thats = self._task.args['that']
+
+ cond = Conditional(loader=self._loader)
+ result['_ansible_verbose_always'] = True
+
+ for that in thats:
+ cond.when = [str(self._make_safe(that))]
+ test_result = cond.evaluate_conditional(templar=self._templar, all_vars=task_vars)
+ if not test_result:
+ result['failed'] = True
+ result['evaluated_to'] = test_result
+ result['assertion'] = that
+
+ result['msg'] = fail_msg
+
+ return result
+
+ result['changed'] = False
+ result['msg'] = success_msg
+ return result
diff --git a/ansible_collections/community/general/tests/integration/targets/cmd_runner/library/cmd_echo.py b/ansible_collections/community/general/tests/integration/targets/cmd_runner/library/cmd_echo.py
index cd8766264..ec0beb98e 100644
--- a/ansible_collections/community/general/tests/integration/targets/cmd_runner/library/cmd_echo.py
+++ b/ansible_collections/community/general/tests/integration/targets/cmd_runner/library/cmd_echo.py
@@ -21,11 +21,14 @@ from ansible_collections.community.general.plugins.module_utils.cmd_runner impor
def main():
module = AnsibleModule(
argument_spec=dict(
+ cmd=dict(type="str", default="echo"),
+ path_prefix=dict(type="str"),
arg_formats=dict(type="dict", default={}),
arg_order=dict(type="raw", required=True),
arg_values=dict(type="dict", default={}),
check_mode_skip=dict(type="bool", default=False),
aa=dict(type="raw"),
+ tt=dict(),
),
supports_check_mode=True,
)
@@ -40,7 +43,7 @@ def main():
arg_formats[arg] = func(*args)
- runner = CmdRunner(module, ['echo', '--'], arg_formats=arg_formats)
+ runner = CmdRunner(module, [module.params["cmd"], '--'], arg_formats=arg_formats, path_prefix=module.params["path_prefix"])
with runner.context(p['arg_order'], check_mode_skip=p['check_mode_skip']) as ctx:
result = ctx.run(**p['arg_values'])
diff --git a/ansible_collections/community/general/tests/integration/targets/cmd_runner/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/cmd_runner/meta/main.yml
new file mode 100644
index 000000000..982de6eb0
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/cmd_runner/meta/main.yml
@@ -0,0 +1,7 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+dependencies:
+ - setup_remote_tmp_dir
diff --git a/ansible_collections/community/general/tests/integration/targets/cmd_runner/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/cmd_runner/tasks/main.yml
index 36ab039f0..e955c5d3d 100644
--- a/ansible_collections/community/general/tests/integration/targets/cmd_runner/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/cmd_runner/tasks/main.yml
@@ -6,3 +6,4 @@
ansible.builtin.include_tasks:
file: test_cmd_echo.yml
loop: "{{ cmd_echo_tests }}"
+ when: item.condition | default(true) | bool
diff --git a/ansible_collections/community/general/tests/integration/targets/cmd_runner/tasks/test_cmd_echo.yml b/ansible_collections/community/general/tests/integration/targets/cmd_runner/tasks/test_cmd_echo.yml
index 1c2caf2b5..a2a9fb8b7 100644
--- a/ansible_collections/community/general/tests/integration/targets/cmd_runner/tasks/test_cmd_echo.yml
+++ b/ansible_collections/community/general/tests/integration/targets/cmd_runner/tasks/test_cmd_echo.yml
@@ -3,17 +3,27 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-- name: test cmd_echo [{{ item.name }}]
+- name: create copy of /bin/echo ({{ item.name }})
+ ansible.builtin.copy:
+ src: /bin/echo
+ dest: "{{ item.copy_to }}/echo"
+ mode: "0755"
+ remote_src: true
+ when: item.copy_to is defined
+
+- name: test cmd_echo module ({{ item.name }})
cmd_echo:
- arg_formats: "{{ item.arg_formats|default(omit) }}"
+ cmd: "{{ item.cmd | default(omit) }}"
+ path_prefix: "{{ item.path_prefix | default(omit) }}"
+ arg_formats: "{{ item.arg_formats | default(omit) }}"
arg_order: "{{ item.arg_order }}"
- arg_values: "{{ item.arg_values|default(omit) }}"
- check_mode_skip: "{{ item.check_mode_skip|default(omit) }}"
- aa: "{{ item.aa|default(omit) }}"
+ arg_values: "{{ item.arg_values | default(omit) }}"
+ check_mode_skip: "{{ item.check_mode_skip | default(omit) }}"
+ aa: "{{ item.aa | default(omit) }}"
register: test_result
- check_mode: "{{ item.check_mode|default(omit) }}"
- ignore_errors: "{{ item.expect_error|default(omit) }}"
+ check_mode: "{{ item.check_mode | default(omit) }}"
+ ignore_errors: "{{ item.expect_error | default(omit) }}"
-- name: check results [{{ item.name }}]
- assert:
+- name: check results ({{ item.name }})
+ _unsafe_assert:
that: "{{ item.assertions }}"
diff --git a/ansible_collections/community/general/tests/integration/targets/cmd_runner/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/cmd_runner/vars/main.yml
index 7f0027d49..f9a715338 100644
--- a/ansible_collections/community/general/tests/integration/targets/cmd_runner/vars/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/cmd_runner/vars/main.yml
@@ -121,3 +121,142 @@ cmd_echo_tests:
- test_result.rc == None
- test_result.out == None
- test_result.err == None
+
+ - name: set aa and tt value
+ arg_formats:
+ aa:
+ func: as_opt_eq_val
+ args: [--answer]
+ tt:
+ func: as_opt_val
+ args: [--tt-arg]
+ arg_order: 'aa tt'
+ arg_values:
+ tt: potatoes
+ aa: 11
+ assertions:
+ - test_result.rc == 0
+ - test_result.out == "-- --answer=11 --tt-arg potatoes\n"
+ - test_result.err == ""
+
+ - name: use cmd echo
+ cmd: echo
+ arg_formats:
+ aa:
+ func: as_opt_eq_val
+ args: [--answer]
+ tt:
+ func: as_opt_val
+ args: [--tt-arg]
+ arg_order: 'aa tt'
+ arg_values:
+ tt: potatoes
+ aa: 11
+ assertions:
+ - test_result.rc == 0
+ - test_result.out == "-- --answer=11 --tt-arg potatoes\n"
+ - test_result.err == ""
+
+ - name: use cmd /bin/echo
+ cmd: /bin/echo
+ arg_formats:
+ aa:
+ func: as_opt_eq_val
+ args: [--answer]
+ tt:
+ func: as_opt_val
+ args: [--tt-arg]
+ arg_order: 'aa tt'
+ arg_values:
+ tt: potatoes
+ aa: 11
+ assertions:
+ - test_result.rc == 0
+ - test_result.out == "-- --answer=11 --tt-arg potatoes\n"
+ - test_result.err == ""
+
+ # this will not be in the regular set of paths get_bin_path() searches
+ - name: use cmd {{ remote_tmp_dir }}/echo
+ condition: >
+ {{
+ ansible_distribution != "MacOSX" and
+ not (ansible_distribution == "CentOS" and ansible_distribution_major_version is version('7.0', '<'))
+ }}
+ copy_to: "{{ remote_tmp_dir }}"
+ cmd: "{{ remote_tmp_dir }}/echo"
+ arg_formats:
+ aa:
+ func: as_opt_eq_val
+ args: [--answer]
+ tt:
+ func: as_opt_val
+ args: [--tt-arg]
+ arg_order: 'aa tt'
+ arg_values:
+ tt: potatoes
+ aa: 11
+ assertions:
+ - test_result.rc == 0
+ - test_result.out == "-- --answer=11 --tt-arg potatoes\n"
+ - test_result.err == ""
+
+ - name: use cmd echo with path_prefix {{ remote_tmp_dir }}
+ cmd: echo
+ condition: >
+ {{
+ ansible_distribution != "MacOSX" and
+ not (ansible_distribution == "CentOS" and ansible_distribution_major_version is version('7.0', '<'))
+ }}
+ copy_to: "{{ remote_tmp_dir }}"
+ path_prefix: "{{ remote_tmp_dir }}"
+ arg_formats:
+ aa:
+ func: as_opt_eq_val
+ args: [--answer]
+ tt:
+ func: as_opt_val
+ args: [--tt-arg]
+ arg_order: 'aa tt'
+ arg_values:
+ tt: potatoes
+ aa: 11
+ assertions:
+ - test_result.rc == 0
+ - test_result.out == "-- --answer=11 --tt-arg potatoes\n"
+ - test_result.err == ""
+
+ - name: use cmd never-existed
+ cmd: never-existed
+ arg_formats:
+ aa:
+ func: as_opt_eq_val
+ args: [--answer]
+ tt:
+ func: as_opt_val
+ args: [--tt-arg]
+ arg_order: 'aa tt'
+ arg_values:
+ tt: potatoes
+ aa: 11
+ expect_error: true
+ assertions:
+ - >
+ "Failed to find required executable" in test_result.msg
+
+ - name: use cmd /usr/bin/never-existed
+ cmd: /usr/bin/never-existed
+ arg_formats:
+ aa:
+ func: as_opt_eq_val
+ args: [--answer]
+ tt:
+ func: as_opt_val
+ args: [--tt-arg]
+ arg_order: 'aa tt'
+ arg_values:
+ tt: potatoes
+ aa: 11
+ expect_error: true
+ assertions:
+ - >
+ "No such file or directory" in test_result.msg
diff --git a/ansible_collections/community/general/tests/integration/targets/connection_incus/aliases b/ansible_collections/community/general/tests/integration/targets/connection_incus/aliases
new file mode 100644
index 000000000..5a0c47032
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/connection_incus/aliases
@@ -0,0 +1,6 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+non_local
+unsupported
diff --git a/ansible_collections/community/general/tests/integration/targets/connection_incus/runme.sh b/ansible_collections/community/general/tests/integration/targets/connection_incus/runme.sh
new file mode 100755
index 000000000..9f31da64d
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/connection_incus/runme.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+set -eux
+
+# Connection tests for POSIX platforms use this script by linking to it from the appropriate 'connection_' target dir.
+# The name of the inventory group to test is extracted from the directory name following the 'connection_' prefix.
+
+group=$(python -c \
+ "from os import path; print(path.basename(path.abspath(path.dirname('$0'))).replace('connection_', ''))")
+
+cd ../connection
+
+INVENTORY="../connection_${group}/test_connection.inventory" ./test.sh \
+ -e target_hosts="${group}" \
+ -e action_prefix= \
+ -e local_tmp=/tmp/ansible-local \
+ -e remote_tmp=/tmp/ansible-remote \
+ "$@"
diff --git a/ansible_collections/community/general/tests/integration/targets/connection_incus/test_connection.inventory b/ansible_collections/community/general/tests/integration/targets/connection_incus/test_connection.inventory
new file mode 100644
index 000000000..84b69faf7
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/connection_incus/test_connection.inventory
@@ -0,0 +1,11 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+[incus]
+incus-pipelining ansible_ssh_pipelining=true
+incus-no-pipelining ansible_ssh_pipelining=false
+[incus:vars]
+ansible_host=ubuntu-2204
+ansible_connection=community.general.incus
+ansible_python_interpreter=python3
diff --git a/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_auth_method.yml b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_auth_method.yml
new file mode 100644
index 000000000..611d67309
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_auth_method.yml
@@ -0,0 +1,74 @@
+---
+# Copyright (c) 2024, Florian Apolloner (@apollo13)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Create an auth method
+ community.general.consul_auth_method:
+ name: test
+ type: jwt
+ config:
+ jwt_validation_pubkeys:
+ - |
+ -----BEGIN PUBLIC KEY-----
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
+ 4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u
+ +qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh
+ kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ
+ 0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg
+ cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc
+ mwIDAQAB
+ -----END PUBLIC KEY-----
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.auth_method.Type == 'jwt'
+ - result.operation == 'create'
+
+- name: Update auth method
+ community.general.consul_auth_method:
+ name: test
+ max_token_ttl: 30m80s
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.auth_method.Type == 'jwt'
+ - result.operation == 'update'
+
+- name: Update auth method (noop)
+ community.general.consul_auth_method:
+ name: test
+ max_token_ttl: 30m80s
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - result.auth_method.Type == 'jwt'
+ - result.operation is not defined
+
+- name: Delete auth method
+ community.general.consul_auth_method:
+ name: test
+ state: absent
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.operation == 'remove'
+
+- name: Delete auth method (noop)
+ community.general.consul_auth_method:
+ name: test
+ state: absent
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - result.operation is not defined
diff --git a/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_binding_rule.yml b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_binding_rule.yml
new file mode 100644
index 000000000..218daf982
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_binding_rule.yml
@@ -0,0 +1,73 @@
+---
+# Copyright (c) 2024, Florian Apolloner (@apollo13)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Create an auth method
+ community.general.consul_auth_method:
+ name: test
+ type: jwt
+ config:
+ jwt_validation_pubkeys:
+ - |
+ -----BEGIN PUBLIC KEY-----
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
+ 4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u
+ +qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh
+ kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ
+ 0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg
+ cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc
+ mwIDAQAB
+ -----END PUBLIC KEY-----
+
+- name: Create a binding rule
+ community.general.consul_binding_rule:
+ name: test-binding
+ description: my description
+ auth_method: test
+ bind_type: service
+ bind_name: yolo
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.binding_rule.AuthMethod == 'test'
+ - result.binding.Description == 'test-binding: my description'
+ - result.operation == 'create'
+
+- name: Update a binding rule
+ community.general.consul_binding_rule:
+ name: test-binding
+ auth_method: test
+ bind_name: yolo2
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.binding.Description == 'test-binding: my description'
+ - result.operation == 'update'
+
+- name: Update a binding rule (noop)
+ community.general.consul_binding_rule:
+ name: test-binding
+ auth_method: test
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - result.binding.Description == 'test-binding: my description'
+ - result.operation is not defined
+
+- name: Delete a binding rule
+ community.general.consul_binding_rule:
+ name: test-binding
+ auth_method: test
+ state: absent
+ register: result
+- assert:
+ that:
+ - result is changed
+ - result.operation == 'remove' \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_general.yml b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_general.yml
new file mode 100644
index 000000000..2fc28efc2
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_general.yml
@@ -0,0 +1,76 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: ensure unknown scheme fails
+ consul_session:
+ state: info
+ id: dummy
+ scheme: non_existent
+ token: "{{ consul_management_token }}"
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is failed
+
+- name: ensure SSL certificate is checked
+ consul_session:
+ state: info
+ id: dummy
+ port: 8501
+ scheme: https
+ token: "{{ consul_management_token }}"
+ register: result
+ ignore_errors: true
+
+- name: previous task should fail since certificate is not known
+ assert:
+ that:
+ - result is failed
+ - "'certificate verify failed' in result.msg"
+
+- name: ensure SSL certificate isn't checked when validate_certs is disabled
+ consul_session:
+ state: info
+ id: dummy
+ port: 8501
+ scheme: https
+ token: "{{ consul_management_token }}"
+ validate_certs: false
+ register: result
+
+- name: previous task should succeed since certificate isn't checked
+ assert:
+ that:
+ - result is changed
+
+- name: ensure a secure connection is possible
+ consul_session:
+ state: info
+ id: dummy
+ port: 8501
+ scheme: https
+ token: "{{ consul_management_token }}"
+ ca_path: '{{ remote_dir }}/cert.pem'
+ register: result
+
+- assert:
+ that:
+ - result is changed
+
+- name: ensure connection errors are handled properly
+ consul_session:
+ state: info
+ id: dummy
+ token: "{{ consul_management_token }}"
+ port: 1234
+ register: result
+ ignore_errors: true
+
+- assert:
+ that:
+ - result is failed
+ - result.msg.startswith('Could not connect to consul agent at localhost:1234, error was')
diff --git a/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_kv.yml b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_kv.yml
new file mode 100644
index 000000000..6cca73137
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_kv.yml
@@ -0,0 +1,57 @@
+---
+# Copyright (c) 2024, Florian Apolloner (@apollo13)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Create a key
+ consul_kv:
+ key: somekey
+ value: somevalue
+ token: "{{ consul_management_token }}"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.data.Value == 'somevalue'
+
+#- name: Test the lookup
+# assert:
+# that:
+# - lookup('community.general.consul_kv', 'somekey', token=consul_management_token) == 'somevalue'
+
+- name: Update a key with the same data
+ consul_kv:
+ key: somekey
+ value: somevalue
+ token: "{{ consul_management_token }}"
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - result.data.Value == 'somevalue'
+
+- name: Remove a key from the store
+ consul_kv:
+ key: somekey
+ state: absent
+ token: "{{ consul_management_token }}"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.data.Value == 'somevalue'
+
+- name: Remove a non-existant key from the store
+ consul_kv:
+ key: somekey
+ state: absent
+ token: "{{ consul_management_token }}"
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - not result.data \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_policy.yml b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_policy.yml
new file mode 100644
index 000000000..336324f03
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_policy.yml
@@ -0,0 +1,72 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Create a policy with rules
+ consul_policy:
+ name: foo-access
+ rules: |
+ key "foo" {
+ policy = "read"
+ }
+ key "private/foo" {
+ policy = "deny"
+ }
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.policy.Name == 'foo-access'
+ - result.operation == 'create'
+
+- name: Update the rules associated to a policy
+ consul_policy:
+ name: foo-access
+ rules: |
+ key "foo" {
+ policy = "read"
+ }
+ key "private/foo" {
+ policy = "deny"
+ }
+ event "bbq" {
+ policy = "write"
+ }
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.operation == 'update'
+
+- name: Update reports not changed when updating again without changes
+ consul_policy:
+ name: foo-access
+ rules: |
+ key "foo" {
+ policy = "read"
+ }
+ key "private/foo" {
+ policy = "deny"
+ }
+ event "bbq" {
+ policy = "write"
+ }
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - result.operation is not defined
+
+- name: Remove a policy
+ consul_policy:
+ name: foo-access
+ state: absent
+ register: result
+- assert:
+ that:
+ - result is changed
+ - result.operation == 'remove' \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_role.yml b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_role.yml
new file mode 100644
index 000000000..9b0504e0b
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_role.yml
@@ -0,0 +1,194 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Create a policy with rules
+ consul_policy:
+ name: foo-access-for-role
+ rules: |
+ key "foo" {
+ policy = "read"
+ }
+ key "private/foo" {
+ policy = "deny"
+ }
+ register: policy_result
+
+- name: Create another policy with rules
+ consul_policy:
+ name: bar-access-for-role
+ rules: |
+ key "bar" {
+ policy = "read"
+ }
+ key "private/bar" {
+ policy = "deny"
+ }
+ register: policy_result
+
+- name: Create a role with policy
+ consul_role:
+ name: foo-role-with-policy
+ policies:
+ - name: "foo-access-for-role"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.role.Name == 'foo-role-with-policy'
+ - result.operation == 'create'
+
+- name: Update policy description, in check mode
+ consul_role:
+ name: foo-role-with-policy
+ description: "Testing updating description"
+ check_mode: yes
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.role.Description == "Testing updating description"
+ - result.role.Policies.0.Name == 'foo-access-for-role'
+ - result.operation == 'update'
+
+- name: Update policy to add the description
+ consul_role:
+ name: foo-role-with-policy
+ description: "Role for testing policies"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.role.Description == "Role for testing policies"
+ - result.role.Policies.0.Name == 'foo-access-for-role'
+ - result.operation == 'update'
+
+- name: Update the role with another policy, also testing leaving description blank
+ consul_role:
+ name: foo-role-with-policy
+ policies:
+ - name: "foo-access-for-role"
+ - name: "bar-access-for-role"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.role.Policies.0.Name == 'foo-access-for-role'
+ - result.role.Policies.1.Name == 'bar-access-for-role'
+ - result.role.Description == "Role for testing policies"
+ - result.operation == 'update'
+
+- name: Create a role with service identity
+ consul_role:
+ name: role-with-service-identity
+ service_identities:
+ - name: web
+ datacenters:
+ - dc1
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.role.ServiceIdentities.0.ServiceName == "web"
+ - result.role.ServiceIdentities.0.Datacenters.0 == "dc1"
+
+- name: Update the role with service identity in check mode
+ consul_role:
+ name: role-with-service-identity
+ service_identities:
+ - name: web
+ datacenters:
+ - dc2
+ register: result
+ check_mode: yes
+
+- assert:
+ that:
+ - result is changed
+ - result.role.ServiceIdentities.0.ServiceName == "web"
+ - result.role.ServiceIdentities.0.Datacenters.0 == "dc2"
+
+- name: Update the role with service identity to add a policy, leaving the service id unchanged
+ consul_role:
+ name: role-with-service-identity
+ policies:
+ - name: "foo-access-for-role"
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.role.ServiceIdentities.0.ServiceName == "web"
+ - result.role.ServiceIdentities.0.Datacenters.0 == "dc1"
+ - result.role.Policies.0.Name == 'foo-access-for-role'
+
+- name: Update the role with service identity to remove the policies
+ consul_role:
+ name: role-with-service-identity
+ policies: []
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.role.ServiceIdentities.0.ServiceName == "web"
+ - result.role.ServiceIdentities.0.Datacenters.0 == "dc1"
+ - result.role.Policies is not defined
+
+- name: Update the role with service identity to remove the node identities, in check mode
+ consul_role:
+ name: role-with-service-identity
+ node_identities: []
+ register: result
+ check_mode: yes
+
+- assert:
+ that:
+ - result is changed
+ - result.role.ServiceIdentities.0.ServiceName == "web"
+ - result.role.ServiceIdentities.0.Datacenters.0 == "dc1"
+ - result.role.Policies is not defined
+ - result.role.NodeIdentities == [] # in check mode the cleared field is returned as an empty array
+
+- name: Update the role with service identity to remove the service identities
+ consul_role:
+ name: role-with-service-identity
+ service_identities: []
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.role.ServiceIdentities is not defined # in normal mode the dictionary is removed from the result
+ - result.role.Policies is not defined
+
+- name: Create a role with node identity
+ consul_role:
+ name: role-with-node-identity
+ node_identities:
+ - name: node-1
+ datacenter: dc2
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.role.NodeIdentities.0.NodeName == "node-1"
+ - result.role.NodeIdentities.0.Datacenter == "dc2"
+
+- name: Remove the last role
+ consul_role:
+ name: role-with-node-identity
+ state: absent
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.operation == 'remove' \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_session.yml b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_session.yml
index 543668964..7f852a36d 100644
--- a/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_session.yml
+++ b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_session.yml
@@ -52,7 +52,7 @@
- name: ensure session was created
assert:
that:
- - test_session_found|default(False)
+ - test_session_found|default(false)
- name: fetch info about a session
consul_session:
@@ -75,61 +75,6 @@
that:
- result is failed
-- name: ensure unknown scheme fails
- consul_session:
- state: info
- id: '{{ session_id }}'
- scheme: non_existent
- register: result
- ignore_errors: true
-
-- assert:
- that:
- - result is failed
-
-- name: ensure SSL certificate is checked
- consul_session:
- state: info
- id: '{{ session_id }}'
- port: 8501
- scheme: https
- register: result
- ignore_errors: true
-
-- name: previous task should fail since certificate is not known
- assert:
- that:
- - result is failed
- - "'certificate verify failed' in result.msg"
-
-- name: ensure SSL certificate isn't checked when validate_certs is disabled
- consul_session:
- state: info
- id: '{{ session_id }}'
- port: 8501
- scheme: https
- validate_certs: false
- register: result
-
-- name: previous task should succeed since certificate isn't checked
- assert:
- that:
- - result is changed
-
-- name: ensure a secure connection is possible
- consul_session:
- state: info
- id: '{{ session_id }}'
- port: 8501
- scheme: https
- environment:
- REQUESTS_CA_BUNDLE: '{{ remote_dir }}/cert.pem'
- register: result
-
-- assert:
- that:
- - result is changed
-
- name: delete a session
consul_session:
state: absent
diff --git a/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_token.yml b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_token.yml
new file mode 100644
index 000000000..9b3679ef1
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/consul/tasks/consul_token.yml
@@ -0,0 +1,88 @@
+---
+# Copyright (c) 2024, Florian Apolloner (@apollo13)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Create a policy with rules
+ community.general.consul_policy:
+ name: "{{ item }}"
+ rules: |
+ key "foo" {
+ policy = "read"
+ }
+ loop:
+ - foo-access
+ - foo-access2
+
+- name: Create token without accessor
+ community.general.consul_token:
+ state: present
+ register: simple_create_result
+
+- assert:
+ that:
+ - simple_create_result is changed
+ - simple_create_result.token.AccessorID
+ - simple_create_result.operation == 'create'
+
+- name: Create token
+ community.general.consul_token:
+ state: present
+ accessor_id: 07a7de84-c9c7-448a-99cc-beaf682efd21
+ service_identities:
+ - service_name: test
+ datacenters: [test1, test2]
+ node_identities:
+ - node_name: test
+ datacenter: test
+ policies:
+ - name: foo-access
+ - name: foo-access2
+ expiration_ttl: 1h
+ register: create_result
+
+- assert:
+ that:
+ - create_result is changed
+ - create_result.token.AccessorID == "07a7de84-c9c7-448a-99cc-beaf682efd21"
+ - create_result.operation == 'create'
+
+- name: Update token
+ community.general.consul_token:
+ state: present
+ accessor_id: 07a7de84-c9c7-448a-99cc-beaf682efd21
+ description: Testing
+ policies:
+ - id: "{{ create_result.token.Policies[-1].ID }}"
+ service_identities: []
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - result.operation == 'update'
+
+- name: Update token (noop)
+ community.general.consul_token:
+ state: present
+ accessor_id: 07a7de84-c9c7-448a-99cc-beaf682efd21
+ policies:
+ - id: "{{ create_result.token.Policies[-1].ID }}"
+ register: result
+
+- assert:
+ that:
+ - result is not changed
+ - result.operation is not defined
+
+- name: Remove token
+ community.general.consul_token:
+ state: absent
+ accessor_id: 07a7de84-c9c7-448a-99cc-beaf682efd21
+ register: result
+
+- assert:
+ that:
+ - result is changed
+ - not result.token
+ - result.operation == 'remove'
diff --git a/ansible_collections/community/general/tests/integration/targets/consul/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/consul/tasks/main.yml
index a2b63ac95..6fef2b998 100644
--- a/ansible_collections/community/general/tests/integration/targets/consul/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/consul/tasks/main.yml
@@ -10,8 +10,8 @@
- name: Install Consul and test
vars:
- consul_version: 1.5.0
- consul_uri: https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/consul/consul_{{ consul_version }}_{{ ansible_system | lower }}_{{ consul_arch }}.zip
+ consul_version: 1.13.2
+ consul_uri: https://releases.hashicorp.com/consul/{{ consul_version }}/consul_{{ consul_version }}_{{ ansible_system | lower }}_{{ consul_arch }}.zip
consul_cmd: '{{ remote_tmp_dir }}/consul'
block:
- name: Install requests<2.20 (CentOS/RHEL 6)
@@ -76,14 +76,32 @@
dest: '{{ remote_tmp_dir }}/consul_config.hcl'
- name: Start Consul (dev mode enabled)
shell: nohup {{ consul_cmd }} agent -dev -config-file {{ remote_tmp_dir }}/consul_config.hcl </dev/null >/dev/null 2>&1 &
+ - name: Bootstrap ACL
+ consul_acl_bootstrap:
+ register: consul_bootstrap_result
+ - set_fact:
+ consul_management_token: '{{ consul_bootstrap_result.result.SecretID }}'
- name: Create some data
- command: '{{ consul_cmd }} kv put data/value{{ item }} foo{{ item }}'
+ command: '{{ consul_cmd }} kv put -token={{consul_management_token}} data/value{{ item }} foo{{ item }}'
loop:
- 1
- 2
- 3
- - import_tasks: consul_session.yml
+ - import_tasks: consul_general.yml
+ - import_tasks: consul_kv.yml
+
+ - block:
+ - import_tasks: consul_session.yml
+ - import_tasks: consul_policy.yml
+ - import_tasks: consul_role.yml
+ - import_tasks: consul_token.yml
+ - import_tasks: consul_auth_method.yml
+ - import_tasks: consul_binding_rule.yml
+ module_defaults:
+ group/community.general.consul:
+ token: "{{ consul_management_token }}"
+
always:
- name: Kill consul process
shell: kill $(cat {{ remote_tmp_dir }}/consul.pid)
- ignore_errors: true
+ ignore_errors: true \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/consul/templates/consul_config.hcl.j2 b/ansible_collections/community/general/tests/integration/targets/consul/templates/consul_config.hcl.j2
index 96da5d664..91bfb08ae 100644
--- a/ansible_collections/community/general/tests/integration/targets/consul/templates/consul_config.hcl.j2
+++ b/ansible_collections/community/general/tests/integration/targets/consul/templates/consul_config.hcl.j2
@@ -12,3 +12,8 @@ ports {
}
key_file = "{{ remote_dir }}/privatekey.pem"
cert_file = "{{ remote_dir }}/cert.pem"
+acl {
+ enabled = true
+ default_policy = "deny"
+ down_policy = "extend-cache"
+}
diff --git a/ansible_collections/community/general/tests/integration/targets/copr/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/copr/tasks/main.yml
index 0e4651724..0d6637811 100644
--- a/ansible_collections/community/general/tests/integration/targets/copr/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/copr/tasks/main.yml
@@ -10,12 +10,6 @@
ansible_distribution == 'Fedora'
or (ansible_os_family == 'RedHat' and ansible_distribution != 'Fedora'
and ansible_distribution_major_version | int >= 8)
- # The copr module imports dnf which is only available for the system Python
- # interpreter.
- - >
- not (ansible_distribution == 'CentOS' and
- ansible_distribution_major_version | int == 8 and not
- ansible_python_version.startswith('3.6'))
block:
- debug: var=copr_chroot
- name: enable copr project
diff --git a/ansible_collections/community/general/tests/integration/targets/deploy_helper/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/deploy_helper/tasks/main.yml
index fdd8bd87b..9bd5f4150 100644
--- a/ansible_collections/community/general/tests/integration/targets/deploy_helper/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/deploy_helper/tasks/main.yml
@@ -17,25 +17,25 @@
assert:
that:
- "'project_path' in deploy_helper"
- - "deploy_helper.current_path == '{{ deploy_helper.project_path }}/current'"
- - "deploy_helper.releases_path == '{{ deploy_helper.project_path }}/releases'"
- - "deploy_helper.shared_path == '{{ deploy_helper.project_path }}/shared'"
+ - "deploy_helper.current_path == deploy_helper.project_path ~ '/current'"
+ - "deploy_helper.releases_path == deploy_helper.project_path ~ '/releases'"
+ - "deploy_helper.shared_path == deploy_helper.project_path ~ '/shared'"
- "deploy_helper.unfinished_filename == 'DEPLOY_UNFINISHED'"
- "'previous_release' in deploy_helper"
- "'previous_release_path' in deploy_helper"
- "'new_release' in deploy_helper"
- "'new_release_path' in deploy_helper"
- - "deploy_helper.new_release_path == '{{ deploy_helper.releases_path }}/{{ deploy_helper.new_release }}'"
+ - "deploy_helper.new_release_path == deploy_helper.releases_path ~ '/' ~ deploy_helper.new_release"
- name: State=query with relative overridden paths
deploy_helper: path={{ deploy_helper_test_root }} current_path=CURRENT_PATH releases_path=RELEASES_PATH shared_path=SHARED_PATH state=query
- name: Assert State=query with relative overridden paths
assert:
that:
- - "deploy_helper.current_path == '{{ deploy_helper.project_path }}/CURRENT_PATH'"
- - "deploy_helper.releases_path == '{{ deploy_helper.project_path }}/RELEASES_PATH'"
- - "deploy_helper.shared_path == '{{ deploy_helper.project_path }}/SHARED_PATH'"
- - "deploy_helper.new_release_path == '{{ deploy_helper.releases_path }}/{{ deploy_helper.new_release}}'"
+ - "deploy_helper.current_path == deploy_helper.project_path ~ '/CURRENT_PATH'"
+ - "deploy_helper.releases_path == deploy_helper.project_path ~ '/RELEASES_PATH'"
+ - "deploy_helper.shared_path == deploy_helper.project_path ~ '/SHARED_PATH'"
+ - "deploy_helper.new_release_path == deploy_helper.releases_path ~ '/' ~ deploy_helper.new_release"
- name: State=query with absolute overridden paths
deploy_helper: path={{ deploy_helper_test_root }} current_path=/CURRENT_PATH releases_path=/RELEASES_PATH shared_path=/SHARED_PATH state=query
@@ -45,7 +45,7 @@
- "deploy_helper.current_path == '/CURRENT_PATH'"
- "deploy_helper.releases_path == '/RELEASES_PATH'"
- "deploy_helper.shared_path == '/SHARED_PATH'"
- - "deploy_helper.new_release_path == '{{ deploy_helper.releases_path }}/{{ deploy_helper.new_release}}'"
+ - "deploy_helper.new_release_path == deploy_helper.releases_path ~ '/' ~ deploy_helper.new_release"
- name: State=query with overridden unfinished_filename
deploy_helper: path={{ deploy_helper_test_root }} unfinished_filename=UNFINISHED_DEPLOY state=query
diff --git a/ansible_collections/community/general/tests/integration/targets/django_manage/aliases b/ansible_collections/community/general/tests/integration/targets/django_manage/aliases
index 98aed9e9d..979054916 100644
--- a/ansible_collections/community/general/tests/integration/targets/django_manage/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/django_manage/aliases
@@ -11,5 +11,10 @@ skip/rhel8.2
skip/rhel8.3
skip/rhel8.4
skip/rhel8.5
+skip/rhel8.6
+skip/rhel8.7
+skip/rhel8.8
skip/rhel9.0
skip/rhel9.1
+skip/rhel9.2
+skip/rhel9.3
diff --git a/ansible_collections/community/general/tests/integration/targets/ejabberd_user/aliases b/ansible_collections/community/general/tests/integration/targets/ejabberd_user/aliases
new file mode 100644
index 000000000..11c37f6bb
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/ejabberd_user/aliases
@@ -0,0 +1,11 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+azp/posix/3
+skip/osx
+skip/macos
+skip/freebsd
+skip/alpine
+skip/rhel
+destructive
diff --git a/ansible_collections/community/general/tests/integration/targets/ejabberd_user/handlers/main.yml b/ansible_collections/community/general/tests/integration/targets/ejabberd_user/handlers/main.yml
new file mode 100644
index 000000000..16eed4733
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/ejabberd_user/handlers/main.yml
@@ -0,0 +1,9 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+---
+- name: Remove ejabberd
+ ansible.builtin.package:
+ name: ejabberd
+ state: absent
diff --git a/ansible_collections/community/general/tests/integration/targets/ejabberd_user/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/ejabberd_user/meta/main.yml
new file mode 100644
index 000000000..2fcd152f9
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/ejabberd_user/meta/main.yml
@@ -0,0 +1,7 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+dependencies:
+ - setup_pkg_mgr
diff --git a/ansible_collections/community/general/tests/integration/targets/ejabberd_user/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/ejabberd_user/tasks/main.yml
new file mode 100644
index 000000000..33e07b785
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/ejabberd_user/tasks/main.yml
@@ -0,0 +1,122 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Bail out if not supported
+ ansible.builtin.meta: end_play
+ when: ansible_distribution in ('Alpine', 'openSUSE Leap', 'CentOS', 'Fedora')
+
+
+- name: Remove ejabberd
+ ansible.builtin.package:
+ name: ejabberd
+ state: absent
+
+- name: Create user without ejabberdctl installed
+ community.general.ejabberd_user:
+ host: localhost
+ username: alice
+ password: pa$$w0rd
+ state: present
+ register: user_no_ejabberdctl
+ ignore_errors: true
+
+- name: Install ejabberd
+ ansible.builtin.package:
+ name: ejabberd
+ state: present
+ notify: Remove ejabberd
+
+- name: Make runnable on Arch
+ community.general.ini_file:
+ path: /usr/lib/systemd/system/ejabberd.service
+ section: Service
+ option: "{{ item }}"
+ state: absent
+ loop:
+ - PrivateDevices
+ - AmbientCapabilities
+ when: ansible_distribution == 'Archlinux'
+
+- name: Make installable on Arch
+ systemd:
+ daemon_reload: true
+ when: ansible_distribution == 'Archlinux'
+
+- ansible.builtin.service:
+ name: ejabberd
+ state: started
+
+- name: Create user alice (check)
+ community.general.ejabberd_user:
+ host: localhost
+ username: alice
+ password: pa$$w0rd
+ state: present
+ check_mode: true
+ register: user_alice_check
+
+- name: Create user alice
+ community.general.ejabberd_user:
+ host: localhost
+ username: alice
+ password: pa$$w0rd
+ state: present
+ register: user_alice
+
+- name: Create user alice (idempotency)
+ community.general.ejabberd_user:
+ host: localhost
+ username: alice
+ password: pa$$w0rd
+ state: present
+ register: user_alice_idempot
+
+- name: Create user alice (change password)
+ community.general.ejabberd_user:
+ host: localhost
+ username: alice
+ password: different_pa$$w0rd
+ state: present
+ register: user_alice_chgpw
+
+- name: Remove user alice (check)
+ community.general.ejabberd_user:
+ host: localhost
+ username: alice
+ state: absent
+ register: remove_alice_check
+ check_mode: true
+
+- name: Remove user alice
+ community.general.ejabberd_user:
+ host: localhost
+ username: alice
+ state: absent
+ register: remove_alice
+
+- name: Remove user alice (idempotency)
+ community.general.ejabberd_user:
+ host: localhost
+ username: alice
+ state: absent
+ register: remove_alice_idempot
+
+- name: Assertions
+ ansible.builtin.assert:
+ that:
+ - user_no_ejabberdctl is failed
+ - "'Failed to find required executable' in user_no_ejabberdctl.msg"
+ - user_alice_check is changed
+ - user_alice is changed
+ - user_alice_idempot is not changed
+ - user_alice_chgpw is changed
+ - remove_alice_check is changed
+ - remove_alice is changed
+ - remove_alice_idempot is not changed
diff --git a/ansible_collections/community/general/tests/integration/targets/etcd3/aliases b/ansible_collections/community/general/tests/integration/targets/etcd3/aliases
index 264446580..78b1fff07 100644
--- a/ansible_collections/community/general/tests/integration/targets/etcd3/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/etcd3/aliases
@@ -8,5 +8,4 @@ skip/aix
skip/osx
skip/macos
skip/freebsd
-skip/python2.6 # installing etcd3 python module will fail on python < 2.7
disabled # see https://github.com/ansible-collections/community.general/issues/322
diff --git a/ansible_collections/community/general/tests/integration/targets/filesize/tasks/sparse.yml b/ansible_collections/community/general/tests/integration/targets/filesize/tasks/sparse.yml
index 79145b6e2..348a1eea1 100644
--- a/ansible_collections/community/general/tests/integration/targets/filesize/tasks/sparse.yml
+++ b/ansible_collections/community/general/tests/integration/targets/filesize/tasks/sparse.yml
@@ -5,10 +5,10 @@
# Test module with sparse files
-- name: Create a huge sparse file of 4TB (check mode)
+- name: Create a huge sparse file of 2TB (check mode)
community.general.filesize:
path: "{{ filesize_testfile }}"
- size: 4TB
+ size: 2TB
sparse: true
register: filesize_test_sparse_01
check_mode: true
@@ -20,10 +20,10 @@
register: filesize_stat_sparse_01
-- name: Create a huge sparse file of 4TB
+- name: Create a huge sparse file of 2TB
community.general.filesize:
path: "{{ filesize_testfile }}"
- size: 4TB
+ size: 2TB
sparse: true
register: filesize_test_sparse_02
@@ -34,34 +34,34 @@
register: filesize_stat_sparse_02
-- name: Create a huge sparse file of 4TB (4000GB) (check mode, idempotency)
+- name: Create a huge sparse file of 2TB (2000GB) (check mode, idempotency)
community.general.filesize:
path: "{{ filesize_testfile }}"
- size: 4000GB
+ size: 2000GB
sparse: true
register: filesize_test_sparse_03
check_mode: true
-- name: Create a huge sparse file of 4TB (4000GB) (idempotency)
+- name: Create a huge sparse file of 2TB (2000GB) (idempotency)
community.general.filesize:
path: "{{ filesize_testfile }}"
- size: 4000GB
+ size: 2000GB
sparse: true
register: filesize_test_sparse_04
-- name: Create a huge sparse file of 4TB (4000000 × 1MB) (check mode, idempotency)
+- name: Create a huge sparse file of 2TB (2000000 × 1MB) (check mode, idempotency)
community.general.filesize:
path: "{{ filesize_testfile }}"
- size: 4000000
+ size: 2000000
blocksize: 1MB
sparse: true
register: filesize_test_sparse_05
check_mode: true
-- name: Create a huge sparse file of 4TB (4000000 × 1MB) (idempotency)
+- name: Create a huge sparse file of 2TB (2000000 × 1MB) (idempotency)
community.general.filesize:
path: "{{ filesize_testfile }}"
- size: 4000000
+ size: 2000000
blocksize: 1MB
sparse: true
register: filesize_test_sparse_06
@@ -89,15 +89,15 @@
- filesize_test_sparse_05.cmd is undefined
- filesize_test_sparse_06.cmd is undefined
- - filesize_test_sparse_01.filesize.bytes == 4*1000**4
- - filesize_test_sparse_02.filesize.bytes == 4*1000**4
- - filesize_test_sparse_03.filesize.bytes == 4*1000**4
- - filesize_test_sparse_04.filesize.bytes == 4*1000**4
- - filesize_test_sparse_05.filesize.bytes == 4*1000**4
- - filesize_test_sparse_06.filesize.bytes == 4*1000**4
+ - filesize_test_sparse_01.filesize.bytes == 2*1000**4
+ - filesize_test_sparse_02.filesize.bytes == 2*1000**4
+ - filesize_test_sparse_03.filesize.bytes == 2*1000**4
+ - filesize_test_sparse_04.filesize.bytes == 2*1000**4
+ - filesize_test_sparse_05.filesize.bytes == 2*1000**4
+ - filesize_test_sparse_06.filesize.bytes == 2*1000**4
- - filesize_test_sparse_01.size_diff == 4*1000**4
- - filesize_test_sparse_02.size_diff == 4*1000**4
+ - filesize_test_sparse_01.size_diff == 2*1000**4
+ - filesize_test_sparse_02.size_diff == 2*1000**4
- filesize_test_sparse_03.size_diff == 0
- filesize_test_sparse_04.size_diff == 0
- filesize_test_sparse_05.size_diff == 0
@@ -106,24 +106,24 @@
- filesize_test_sparse_01.state is undefined
- filesize_test_sparse_02.state in ["file"]
- filesize_test_sparse_01.size is undefined
- - filesize_test_sparse_02.size == 4*1000**4
- - filesize_test_sparse_03.size == 4*1000**4
- - filesize_test_sparse_04.size == 4*1000**4
- - filesize_test_sparse_05.size == 4*1000**4
- - filesize_test_sparse_06.size == 4*1000**4
+ - filesize_test_sparse_02.size == 2*1000**4
+ - filesize_test_sparse_03.size == 2*1000**4
+ - filesize_test_sparse_04.size == 2*1000**4
+ - filesize_test_sparse_05.size == 2*1000**4
+ - filesize_test_sparse_06.size == 2*1000**4
- not filesize_stat_sparse_01.stat.exists
- filesize_stat_sparse_02.stat.exists
- filesize_stat_sparse_02.stat.isreg
- - filesize_stat_sparse_02.stat.size == 4*1000**4
- - filesize_stat_sparse_06.stat.size == 4*1000**4
+ - filesize_stat_sparse_02.stat.size == 2*1000**4
+ - filesize_stat_sparse_06.stat.size == 2*1000**4
-- name: Change sparse file size to 4TiB (check mode)
+- name: Change sparse file size to 2TiB (check mode)
community.general.filesize:
path: "{{ filesize_testfile }}"
- size: 4TiB
+ size: 2TiB
sparse: true
register: filesize_test_sparse_11
check_mode: true
@@ -135,10 +135,10 @@
register: filesize_stat_sparse_11
-- name: Change sparse file size to 4TiB
+- name: Change sparse file size to 2TiB
community.general.filesize:
path: "{{ filesize_testfile }}"
- size: 4TiB
+ size: 2TiB
sparse: true
register: filesize_test_sparse_12
@@ -149,18 +149,18 @@
register: filesize_stat_sparse_12
-- name: Change sparse file size to 4TiB (4096GiB) (check mode, idempotency)
+- name: Change sparse file size to 2TiB (2048GiB) (check mode, idempotency)
community.general.filesize:
path: "{{ filesize_testfile }}"
- size: 4096GiB
+ size: 2048GiB
sparse: true
register: filesize_test_sparse_13
check_mode: true
-- name: Change sparse file size to 4TiB (4096GiB) (idempotency)
+- name: Change sparse file size to 2TiB (2048GiB) (idempotency)
community.general.filesize:
path: "{{ filesize_testfile }}"
- size: 4096GiB
+ size: 2048GiB
sparse: true
register: filesize_test_sparse_14
@@ -183,26 +183,26 @@
- filesize_test_sparse_13.cmd is undefined
- filesize_test_sparse_14.cmd is undefined
- - filesize_test_sparse_11.size_diff == 398046511104
- - filesize_test_sparse_12.size_diff == 398046511104
+ - filesize_test_sparse_11.size_diff == 199023255552
+ - filesize_test_sparse_12.size_diff == 199023255552
- filesize_test_sparse_13.size_diff == 0
- filesize_test_sparse_14.size_diff == 0
- - filesize_test_sparse_11.size == 4000000000000
- - filesize_test_sparse_12.size == 4398046511104
- - filesize_test_sparse_13.size == 4398046511104
- - filesize_test_sparse_14.size == 4398046511104
+ - filesize_test_sparse_11.size == 2000000000000
+ - filesize_test_sparse_12.size == 2199023255552
+ - filesize_test_sparse_13.size == 2199023255552
+ - filesize_test_sparse_14.size == 2199023255552
- - filesize_stat_sparse_11.stat.size == 4000000000000
- - filesize_stat_sparse_12.stat.size == 4398046511104
- - filesize_stat_sparse_14.stat.size == 4398046511104
+ - filesize_stat_sparse_11.stat.size == 2000000000000
+ - filesize_stat_sparse_12.stat.size == 2199023255552
+ - filesize_stat_sparse_14.stat.size == 2199023255552
-- name: Change sparse file size to 4.321TB (check mode)
+- name: Change sparse file size to 2.321TB (check mode)
community.general.filesize:
path: "{{ filesize_testfile }}"
- size: 4.321TB
+ size: 2.321TB
sparse: true
register: filesize_test_sparse_21
check_mode: true
@@ -214,10 +214,10 @@
register: filesize_stat_sparse_21
-- name: Change sparse file size to 4.321TB
+- name: Change sparse file size to 2.321TB
community.general.filesize:
path: "{{ filesize_testfile }}"
- size: 4.321TB
+ size: 2.321TB
sparse: true
register: filesize_test_sparse_22
@@ -228,19 +228,19 @@
register: filesize_stat_sparse_22
-- name: Change sparse file size to 4321×1GB (check mode, idempotency)
+- name: Change sparse file size to 2321×1GB (check mode, idempotency)
community.general.filesize:
path: "{{ filesize_testfile }}"
- size: 4321
+ size: 2321
blocksize: 1GB
sparse: true
register: filesize_test_sparse_23
check_mode: true
-- name: Change sparse file size to 4321×1GB (idempotency)
+- name: Change sparse file size to 2321×1GB (idempotency)
community.general.filesize:
path: "{{ filesize_testfile }}"
- size: 4321
+ size: 2321
blocksize: 1GB
sparse: true
register: filesize_test_sparse_24
@@ -264,19 +264,19 @@
- filesize_test_sparse_23.cmd is undefined
- filesize_test_sparse_24.cmd is undefined
- - filesize_test_sparse_21.size_diff == 4321*1000**3 - 4*1024**4
- - filesize_test_sparse_22.size_diff == 4321*1000**3 - 4*1024**4
+ - filesize_test_sparse_21.size_diff == 2321*1000**3 - 2*1024**4
+ - filesize_test_sparse_22.size_diff == 2321*1000**3 - 2*1024**4
- filesize_test_sparse_23.size_diff == 0
- filesize_test_sparse_24.size_diff == 0
- - filesize_test_sparse_21.size == 4398046511104
- - filesize_test_sparse_22.size == 4321000000000
- - filesize_test_sparse_23.size == 4321000000000
- - filesize_test_sparse_24.size == 4321000000000
+ - filesize_test_sparse_21.size == 2199023255552
+ - filesize_test_sparse_22.size == 2321000000000
+ - filesize_test_sparse_23.size == 2321000000000
+ - filesize_test_sparse_24.size == 2321000000000
- - filesize_stat_sparse_21.stat.size == 4398046511104
- - filesize_stat_sparse_22.stat.size == 4321000000000
- - filesize_stat_sparse_24.stat.size == 4321000000000
+ - filesize_stat_sparse_21.stat.size == 2199023255552
+ - filesize_stat_sparse_22.stat.size == 2321000000000
+ - filesize_stat_sparse_24.stat.size == 2321000000000
diff --git a/ansible_collections/community/general/tests/integration/targets/filesystem/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/filesystem/defaults/main.yml
index 0448d8602..ec446d241 100644
--- a/ansible_collections/community/general/tests/integration/targets/filesystem/defaults/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/filesystem/defaults/main.yml
@@ -11,23 +11,23 @@ tested_filesystems:
# - XFS: 20Mo
# - Btrfs: 150Mo (50Mo when "--metadata single" is used and 100Mb when on newer Fedora versions)
# - f2fs:
- # - 1.2.0 requires at leat 116Mo
+ # - 1.2.0 requires at least 116Mo
# - 1.7.0 requires at least 30Mo
# - 1.10.0 requires at least 38Mo
# - resizefs asserts when initial fs is smaller than 60Mo and seems to require 1.10.0
- ext4: {fssize: 10, grow: true}
- ext4dev: {fssize: 10, grow: true}
- ext3: {fssize: 10, grow: true}
- ext2: {fssize: 10, grow: true}
- xfs: {fssize: 300, grow: false} # grow requires a mounted filesystem
- btrfs: {fssize: 150, grow: false} # grow requires a mounted filesystem
- reiserfs: {fssize: 33, grow: false} # grow not implemented
- vfat: {fssize: 20, grow: true}
- ocfs2: {fssize: '{{ ocfs2_fssize }}', grow: false} # grow not implemented
- f2fs: {fssize: '{{ f2fs_fssize|default(60) }}', grow: 'f2fs_version is version("1.10.0", ">=")'}
- lvm: {fssize: 20, grow: true}
- swap: {fssize: 10, grow: false} # grow not implemented
- ufs: {fssize: 10, grow: true}
+ ext4: {fssize: 10, grow: true, new_uuid: 'random'}
+ ext4dev: {fssize: 10, grow: true, new_uuid: 'random'}
+ ext3: {fssize: 10, grow: true, new_uuid: 'random'}
+ ext2: {fssize: 10, grow: true, new_uuid: 'random'}
+ xfs: {fssize: 300, grow: false, new_uuid: 'generate'} # grow requires a mounted filesystem
+ btrfs: {fssize: 150, grow: false, new_uuid: null} # grow requires a mounted filesystem
+ reiserfs: {fssize: 33, grow: false, new_uuid: null} # grow not implemented
+ vfat: {fssize: 20, grow: true, new_uuid: null}
+ ocfs2: {fssize: '{{ ocfs2_fssize }}', grow: false, new_uuid: null} # grow not implemented
+ f2fs: {fssize: '{{ f2fs_fssize|default(60) }}', grow: 'f2fs_version is version("1.10.0", ">=")', new_uuid: null}
+ lvm: {fssize: 20, grow: true, new_uuid: 'something'}
+ swap: {fssize: 10, grow: false, new_uuid: null} # grow not implemented
+ ufs: {fssize: 10, grow: true, new_uuid: null}
get_uuid_any: "blkid -c /dev/null -o value -s UUID {{ dev }}"
diff --git a/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/main.yml
index 0ff0f2309..0c15c2155 100644
--- a/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/main.yml
@@ -29,6 +29,7 @@
fstype: '{{ item.0.key }}'
fssize: '{{ item.0.value.fssize }}'
grow: '{{ item.0.value.grow }}'
+ new_uuid: '{{ item.0.value.new_uuid }}'
action: '{{ item.1 }}'
when:
# FreeBSD limited support
@@ -83,7 +84,7 @@
# TODO: something seems to be broken on Alpine
- 'not (ansible_distribution == "Alpine")'
- loop: "{{ query('dict', tested_filesystems)|product(['create_fs', 'overwrite_another_fs', 'remove_fs'])|list }}"
+ loop: "{{ query('dict', tested_filesystems)|product(['create_fs', 'reset_fs_uuid', 'overwrite_another_fs', 'remove_fs', 'set_fs_uuid_on_creation', 'set_fs_uuid_on_creation_with_opts'])|list }}"
# With FreeBSD extended support (util-linux is not available before 12.2)
diff --git a/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/reset_fs_uuid.yml b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/reset_fs_uuid.yml
new file mode 100644
index 000000000..77dad2203
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/reset_fs_uuid.yml
@@ -0,0 +1,59 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Skip UUID reset tests for FreeBSD due to "xfs_admin: only 'rewrite' supported on V5 fs"
+- when:
+ - new_uuid | default(False)
+ - not (ansible_system == "FreeBSD" and fstype == "xfs")
+ block:
+ - name: "Create filesystem ({{ fstype }})"
+ community.general.filesystem:
+ dev: '{{ dev }}'
+ fstype: '{{ fstype }}'
+ register: fs_result
+
+ - name: "Get UUID of created filesystem"
+ ansible.builtin.shell:
+ cmd: "{{ get_uuid_cmd }}"
+ changed_when: false
+ register: uuid
+
+ - name: "Reset filesystem ({{ fstype }}) UUID"
+ community.general.filesystem:
+ dev: '{{ dev }}'
+ fstype: '{{ fstype }}'
+ uuid: "{{ new_uuid }}"
+ register: fs_resetuuid_result
+
+ - name: "Get UUID of the filesystem"
+ ansible.builtin.shell:
+ cmd: "{{ get_uuid_cmd }}"
+ changed_when: false
+ register: uuid2
+
+ - name: "Assert that filesystem UUID is changed"
+ ansible.builtin.assert:
+ that:
+ - 'fs_resetuuid_result is changed'
+ - 'fs_resetuuid_result is success'
+ - 'uuid.stdout != uuid2.stdout'
+
+ - when:
+ - (grow | bool and (fstype != "vfat" or resize_vfat)) or
+ (fstype == "xfs" and ansible_system == "Linux" and
+ ansible_distribution not in ["CentOS", "Ubuntu"])
+ block:
+ - name: "Reset filesystem ({{ fstype }}) UUID and resizefs"
+ ignore_errors: true
+ community.general.filesystem:
+ dev: '{{ dev }}'
+ fstype: '{{ fstype }}'
+ uuid: "{{ new_uuid }}"
+ resizefs: true
+ register: fs_resetuuid_and_resizefs_result
+
+ - name: "Assert that filesystem UUID reset and resizefs failed"
+ ansible.builtin.assert:
+ that: fs_resetuuid_and_resizefs_result is failed
diff --git a/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/set_fs_uuid_on_creation.yml b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/set_fs_uuid_on_creation.yml
new file mode 100644
index 000000000..f52c44d65
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/set_fs_uuid_on_creation.yml
@@ -0,0 +1,44 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: "Generate a random UUID"
+ ansible.builtin.set_fact:
+ random_uuid: '{{ "first_random_uuid" | ansible.builtin.to_uuid }}'
+
+# Skip UUID set at creation tests for FreeBSD due to "xfs_admin: only 'rewrite' supported on V5 fs"
+- when:
+ - new_uuid | default(False)
+ - not (ansible_system == "FreeBSD" and fstype == "xfs")
+ block:
+ - name: "Create filesystem ({{ fstype }}) with UUID"
+ community.general.filesystem:
+ dev: '{{ dev }}'
+ fstype: '{{ fstype }}'
+ uuid: '{{ random_uuid }}'
+ register: fs_result
+
+ - name: "Get UUID of the created filesystem"
+ ansible.builtin.shell:
+ cmd: "{{ get_uuid_cmd }}"
+ changed_when: false
+ register: uuid
+
+ - name: "Assert that filesystem UUID is the random UUID set on creation"
+ ansible.builtin.assert:
+ that: (random_uuid | replace('-','')) == ( uuid.stdout | replace('-',''))
+
+- when: not (new_uuid | default(False))
+ block:
+ - name: "Create filesystem ({{ fstype }}) without UUID support"
+ ignore_errors: true
+ community.general.filesystem:
+ dev: '{{ dev }}'
+ fstype: '{{ fstype }}'
+ uuid: '{{ random_uuid }}'
+ register: fs_result
+
+ - name: "Assert that filesystem creation failed"
+ ansible.builtin.assert:
+ that: fs_result is failed
diff --git a/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/set_fs_uuid_on_creation_with_opts.yml b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/set_fs_uuid_on_creation_with_opts.yml
new file mode 100644
index 000000000..fc73e57ee
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filesystem/tasks/set_fs_uuid_on_creation_with_opts.yml
@@ -0,0 +1,33 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# UUID set at creation with opts for XFS is not supported
+- when:
+ - new_uuid | default(False)
+ - fstype != "xfs"
+ block:
+
+ - name: "Generate random UUIDs"
+ ansible.builtin.set_fact:
+ random_uuid: '{{ "first_random_uuid" | ansible.builtin.to_uuid }}'
+ random_uuid2: '{{ "second_random_uuid" | ansible.builtin.to_uuid }}'
+
+ - name: "Create filesystem ({{ fstype }}) with fix UUID as opt"
+ community.general.filesystem:
+ dev: '{{ dev }}'
+ fstype: '{{ fstype }}'
+ opts: "{{ ((fstype == 'lvm') | ansible.builtin.ternary('--norestorefile --uuid ', '-U ')) + random_uuid2 }}"
+ uuid: '{{ random_uuid }}'
+ register: fs_result2
+
+ - name: "Get UUID of the created filesystem"
+ ansible.builtin.shell:
+ cmd: "{{ get_uuid_cmd }}"
+ changed_when: false
+ register: uuid2
+
+ - name: "Assert that filesystem UUID is the one set on creation with opt"
+ ansible.builtin.assert:
+ that: (random_uuid2 | replace('-','')) == ( uuid2.stdout | replace('-',''))
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_counter/aliases b/ansible_collections/community/general/tests/integration/targets/filter_counter/aliases
index bc9b4bc99..12d1d6617 100644
--- a/ansible_collections/community/general/tests/integration/targets/filter_counter/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/filter_counter/aliases
@@ -3,4 +3,3 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/2
-skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_dict/aliases b/ansible_collections/community/general/tests/integration/targets/filter_dict/aliases
index e8051e042..343f119da 100644
--- a/ansible_collections/community/general/tests/integration/targets/filter_dict/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/filter_dict/aliases
@@ -3,4 +3,3 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/3
-skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_dict_kv/aliases b/ansible_collections/community/general/tests/integration/targets/filter_dict_kv/aliases
index bc9b4bc99..12d1d6617 100644
--- a/ansible_collections/community/general/tests/integration/targets/filter_dict_kv/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/filter_dict_kv/aliases
@@ -3,4 +3,3 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/2
-skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_from_csv/aliases b/ansible_collections/community/general/tests/integration/targets/filter_from_csv/aliases
index bc9b4bc99..12d1d6617 100644
--- a/ansible_collections/community/general/tests/integration/targets/filter_from_csv/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/filter_from_csv/aliases
@@ -3,4 +3,3 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/2
-skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_from_ini/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_from_ini/tasks/main.yml
new file mode 100644
index 000000000..a2eca36a6
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_from_ini/tasks/main.yml
@@ -0,0 +1,60 @@
+---
+# Copyright (c) 2023, Steffen Scheib <steffen@scheib.me>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: 'Define ini_test_dict'
+ ansible.builtin.set_fact:
+ ini_test_dict:
+ section_name:
+ key_name: 'key value'
+
+ another_section:
+ connection: 'ssh'
+
+- name: 'Write INI file that reflects ini_test_dict to {{ ini_test_file }}'
+ ansible.builtin.copy:
+ dest: '{{ ini_test_file }}'
+ content: |
+ [section_name]
+ key_name=key value
+
+ [another_section]
+ connection=ssh
+
+- name: 'Slurp the test file: {{ ini_test_file }}'
+ ansible.builtin.slurp:
+ src: '{{ ini_test_file }}'
+ register: 'ini_file_content'
+
+- name: >-
+ Ensure defined ini_test_dict is the same when retrieved
+ from {{ ini_test_file }}
+ ansible.builtin.assert:
+ that:
+ - 'ini_file_content.content | b64decode | community.general.from_ini ==
+ ini_test_dict'
+
+- name: 'Create a file that is not INI formatted: {{ ini_bad_file }}'
+ ansible.builtin.copy:
+ dest: '{{ ini_bad_file }}'
+ content: |
+ Testing a not INI formatted file.
+
+- name: 'Slurp the file that is not INI formatted: {{ ini_bad_file }}'
+ ansible.builtin.slurp:
+ src: '{{ ini_bad_file }}'
+ register: 'ini_bad_file_content'
+
+- name: 'Try parsing the bad file with from_ini: {{ ini_bad_file }}'
+ ansible.builtin.debug:
+ var: ini_bad_file_content | b64decode | community.general.from_ini
+ register: 'ini_bad_file_debug'
+ ignore_errors: true
+
+- name: 'Ensure from_ini raised the correct exception'
+ ansible.builtin.assert:
+ that:
+ - "'from_ini failed to parse given string' in ini_bad_file_debug.msg"
+ - "'File contains no section headers' in ini_bad_file_debug.msg"
+...
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_from_ini/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_from_ini/vars/main.yml
new file mode 100644
index 000000000..8c4d79327
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_from_ini/vars/main.yml
@@ -0,0 +1,8 @@
+---
+# Copyright (c) 2023, Steffen Scheib <steffen@scheib.me>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+ini_test_file: '/tmp/test.ini'
+ini_bad_file: '/tmp/bad.file'
+...
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_groupby_as_dict/aliases b/ansible_collections/community/general/tests/integration/targets/filter_groupby_as_dict/aliases
index e8051e042..343f119da 100644
--- a/ansible_collections/community/general/tests/integration/targets/filter_groupby_as_dict/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/filter_groupby_as_dict/aliases
@@ -3,4 +3,3 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/3
-skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_hashids/aliases b/ansible_collections/community/general/tests/integration/targets/filter_hashids/aliases
index bc9b4bc99..12d1d6617 100644
--- a/ansible_collections/community/general/tests/integration/targets/filter_hashids/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/filter_hashids/aliases
@@ -3,4 +3,3 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/2
-skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_jc/aliases b/ansible_collections/community/general/tests/integration/targets/filter_jc/aliases
index 0e799090e..4e1151566 100644
--- a/ansible_collections/community/general/tests/integration/targets/filter_jc/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/filter_jc/aliases
@@ -3,5 +3,6 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/2
-skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
skip/python2.7 # jc only supports python3.x
+skip/freebsd13.3 # FIXME - ruyaml compilation fails
+skip/freebsd14.0 # FIXME - ruyaml compilation fails
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_json_query/aliases b/ansible_collections/community/general/tests/integration/targets/filter_json_query/aliases
index cee9abd2c..dadd9f37a 100644
--- a/ansible_collections/community/general/tests/integration/targets/filter_json_query/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/filter_json_query/aliases
@@ -3,5 +3,4 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/2
-skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
skip/aix
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_lists/aliases b/ansible_collections/community/general/tests/integration/targets/filter_lists/aliases
new file mode 100644
index 000000000..12d1d6617
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_lists/aliases
@@ -0,0 +1,5 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+azp/posix/2
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_lists/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_lists/tasks/main.yml
new file mode 100644
index 000000000..a146e1293
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_lists/tasks/main.yml
@@ -0,0 +1,64 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Test predictive list union
+ ansible.builtin.assert:
+ that:
+ - 'list1 | community.general.lists_union(list2, list3) == [1, 2, 5, 3, 4, 10, 11, 99, 101]'
+ - '[list1, list2, list3] | community.general.lists_union(flatten=True) == [1, 2, 5, 3, 4, 10, 11, 99, 101]'
+ - '[1, 2, 3] | community.general.lists_union([4, 5, 6]) == [1, 2, 3, 4, 5, 6]'
+ - '[1, 2, 3] | community.general.lists_union([3, 4, 5, 6]) == [1, 2, 3, 4, 5, 6]'
+ - '[1, 2, 3] | community.general.lists_union([3, 2, 1]) == [1, 2, 3]'
+ - '["a", "A", "b"] | community.general.lists_union(["B", "c", "C"]) == ["a", "A", "b", "B", "c", "C"]'
+ - '["a", "A", "b"] | community.general.lists_union(["b", "B", "c", "C"]) == ["a", "A", "b", "B", "c", "C"]'
+ - '["a", "A", "b"] | community.general.lists_union(["b", "A", "a"]) == ["a", "A", "b"]'
+ - '[["a"]] | community.general.lists_union([["b"], ["a"]]) == [["a"], ["b"]]'
+ - '[["a"]] | community.general.lists_union([["b"]], ["a"]) == [["a"], ["b"], "a"]'
+ - '[["a"]] | community.general.lists_union(["b"], ["a"]) == [["a"], "b", "a"]'
+
+- name: Test predictive list intersection
+ ansible.builtin.assert:
+ that:
+ - 'list1 | community.general.lists_intersect(list2, list3) == [1, 2, 5, 4]'
+ - '[list1, list2, list3] | community.general.lists_intersect(flatten=True) == [1, 2, 5, 4]'
+ - '[1, 2, 3] | community.general.lists_intersect([4, 5, 6]) == []'
+ - '[1, 2, 3] | community.general.lists_intersect([3, 4, 5, 6]) == [3]'
+ - '[1, 2, 3] | community.general.lists_intersect([3, 2, 1]) == [1, 2, 3]'
+ - '["a", "A", "b"] | community.general.lists_intersect(["B", "c", "C"]) == []'
+ - '["a", "A", "b"] | community.general.lists_intersect(["b", "B", "c", "C"]) == ["b"]'
+ - '["a", "A", "b"] | community.general.lists_intersect(["b", "A", "a"]) == ["a", "A", "b"]'
+ - '[["a"]] | community.general.lists_intersect([["b"], ["a"]]) == [["a"]]'
+ - '[["a"]] | community.general.lists_intersect([["b"]], ["a"]) == []'
+ - '[["a"]] | community.general.lists_intersect(["b"], ["a"]) == []'
+
+- name: Test predictive list difference
+ ansible.builtin.assert:
+ that:
+ - 'list1 | community.general.lists_difference(list2, list3) == []'
+ - '[list1, list2, list3] | community.general.lists_difference(flatten=True) == []'
+ - '[1, 2, 3] | community.general.lists_difference([4, 5, 6]) == [1, 2, 3]'
+ - '[1, 2, 3] | community.general.lists_difference([3, 4, 5, 6]) == [1, 2]'
+ - '[1, 2, 3] | community.general.lists_difference([3, 2, 1]) == []'
+ - '["a", "A", "b"] | community.general.lists_difference(["B", "c", "C"]) == ["a", "A", "b"]'
+ - '["a", "A", "b"] | community.general.lists_difference(["b", "B", "c", "C"]) == ["a", "A"]'
+ - '["a", "A", "b"] | community.general.lists_difference(["b", "A", "a"]) == []'
+ - '[["a"]] | community.general.lists_difference([["b"], ["a"]]) == []'
+ - '[["a"]] | community.general.lists_difference([["b"]], ["a"]) == [["a"]]'
+ - '[["a"]] | community.general.lists_difference(["b"], ["a"]) == [["a"]]'
+
+- name: Test predictive list symmetric difference
+ ansible.builtin.assert:
+ that:
+ - 'list1 | community.general.lists_symmetric_difference(list2, list3) == [11, 1, 2, 4, 5, 101]'
+ - '[list1, list2, list3] | community.general.lists_symmetric_difference(flatten=True) == [11, 1, 2, 4, 5, 101]'
+ - '[1, 2, 3] | community.general.lists_symmetric_difference([4, 5, 6]) == [1, 2, 3, 4, 5, 6]'
+ - '[1, 2, 3] | community.general.lists_symmetric_difference([3, 4, 5, 6]) == [1, 2, 4, 5, 6]'
+ - '[1, 2, 3] | community.general.lists_symmetric_difference([3, 2, 1]) == []'
+ - '["a", "A", "b"] | community.general.lists_symmetric_difference(["B", "c", "C"]) == ["a", "A", "b", "B", "c", "C"]'
+ - '["a", "A", "b"] | community.general.lists_symmetric_difference(["b", "B", "c", "C"]) == ["a", "A", "B", "c", "C"]'
+ - '["a", "A", "b"] | community.general.lists_symmetric_difference(["b", "A", "a"]) == []'
+ - '[["a"]] | community.general.lists_symmetric_difference([["b"], ["a"]]) == [["b"]]'
+ - '[["a"]] | community.general.lists_symmetric_difference([["b"]], ["a"]) == [["a"], ["b"], "a"]'
+ - '[["a"]] | community.general.lists_symmetric_difference(["b"], ["a"]) == [["a"], "b", "a"]'
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_lists/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_lists/vars/main.yml
new file mode 100644
index 000000000..a67af1dad
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_lists/vars/main.yml
@@ -0,0 +1,8 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+list1: [1, 2, 5, 3, 4, 10]
+list2: [1, 2, 3, 4, 5, 11, 99]
+list3: [1, 2, 4, 5, 10, 99, 101]
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/aliases b/ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/aliases
index bc9b4bc99..12d1d6617 100644
--- a/ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/filter_lists_mergeby/aliases
@@ -3,4 +3,3 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/2
-skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_path_join_shim/aliases b/ansible_collections/community/general/tests/integration/targets/filter_path_join_shim/aliases
index 51baa3d7a..afda346c4 100644
--- a/ansible_collections/community/general/tests/integration/targets/filter_path_join_shim/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/filter_path_join_shim/aliases
@@ -3,4 +3,3 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/1
-skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_random_mac/aliases b/ansible_collections/community/general/tests/integration/targets/filter_random_mac/aliases
index cee9abd2c..dadd9f37a 100644
--- a/ansible_collections/community/general/tests/integration/targets/filter_random_mac/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/filter_random_mac/aliases
@@ -3,5 +3,4 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/2
-skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
skip/aix
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_time/aliases b/ansible_collections/community/general/tests/integration/targets/filter_time/aliases
index bc9b4bc99..12d1d6617 100644
--- a/ansible_collections/community/general/tests/integration/targets/filter_time/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/filter_time/aliases
@@ -3,4 +3,3 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/2
-skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_to_ini/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_to_ini/tasks/main.yml
new file mode 100644
index 000000000..877d4471d
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_to_ini/tasks/main.yml
@@ -0,0 +1,58 @@
+---
+# Copyright (c) 2023, Steffen Scheib <steffen@scheib.me>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: >-
+ Write INI file that reflects using to_ini to {{ ini_test_file_filter }}
+ ansible.builtin.copy:
+ dest: '{{ ini_test_file_filter }}'
+ content: '{{ ini_test_dict | community.general.to_ini }}'
+ vars:
+ ini_test_dict:
+ section_name:
+ key_name: 'key value'
+
+ another_section:
+ connection: 'ssh'
+
+- name: 'Write INI file manually to {{ ini_test_file }}'
+ ansible.builtin.copy:
+ dest: '{{ ini_test_file }}'
+ content: |
+ [section_name]
+ key_name = key value
+
+ [another_section]
+ connection = ssh
+
+- name: 'Slurp the manually created test file: {{ ini_test_file }}'
+ ansible.builtin.slurp:
+ src: '{{ ini_test_file }}'
+ register: 'ini_file_content'
+
+- name: 'Slurp the test file created with to_ini: {{ ini_test_file_filter }}'
+ ansible.builtin.slurp:
+ src: '{{ ini_test_file_filter }}'
+ register: 'ini_file_filter_content'
+
+- name: >-
+ Ensure the manually created test file and the test file created with
+ to_ini are identical
+ ansible.builtin.assert:
+ that:
+ - 'ini_file_content.content | b64decode ==
+ ini_file_filter_content.content | b64decode'
+
+- name: 'Try to convert an empty dictionary with to_ini'
+ ansible.builtin.debug:
+ msg: '{{ {} | community.general.to_ini }}'
+ register: 'ini_empty_dict'
+ ignore_errors: true
+
+- name: 'Ensure the correct exception was raised'
+ ansible.builtin.assert:
+ that:
+ - "'to_ini received an empty dict. An empty dict cannot be converted.' in
+ ini_empty_dict.msg"
+...
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_to_ini/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/filter_to_ini/vars/main.yml
new file mode 100644
index 000000000..9c950726b
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/filter_to_ini/vars/main.yml
@@ -0,0 +1,8 @@
+---
+# Copyright (c) 2023, Steffen Scheib <steffen@scheib.me>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+ini_test_file: '/tmp/test.ini'
+ini_test_file_filter: '/tmp/test_filter.ini'
+...
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_unicode_normalize/aliases b/ansible_collections/community/general/tests/integration/targets/filter_unicode_normalize/aliases
index bc9b4bc99..12d1d6617 100644
--- a/ansible_collections/community/general/tests/integration/targets/filter_unicode_normalize/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/filter_unicode_normalize/aliases
@@ -3,4 +3,3 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/2
-skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/filter_version_sort/aliases b/ansible_collections/community/general/tests/integration/targets/filter_version_sort/aliases
index bc9b4bc99..12d1d6617 100644
--- a/ansible_collections/community/general/tests/integration/targets/filter_version_sort/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/filter_version_sort/aliases
@@ -3,4 +3,3 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/2
-skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/gem/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gem/tasks/main.yml
index 362c126bf..2d615304f 100644
--- a/ansible_collections/community/general/tests/integration/targets/gem/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/gem/tasks/main.yml
@@ -109,7 +109,7 @@
- current_gems.stdout is not search('gist\s+\([0-9.]+\)')
when: ansible_user_uid == 0
- # Check cutom gem directory
+ # Check custom gem directory
- name: Install gem in a custom directory with incorrect options
gem:
name: gist
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/files/gitconfig b/ansible_collections/community/general/tests/integration/targets/git_config/files/gitconfig
index 92eeb7eb9..29d3e8a0e 100644
--- a/ansible_collections/community/general/tests/integration/targets/git_config/files/gitconfig
+++ b/ansible_collections/community/general/tests/integration/targets/git_config/files/gitconfig
@@ -4,3 +4,8 @@
[http]
proxy = foo
+
+[push]
+pushoption = merge_request.create
+pushoption = merge_request.draft
+pushoption = merge_request.target=foobar
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/get_set_state_present_file.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/get_set_state_present_file.yml
index a61ffcc68..c410bfe18 100644
--- a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/get_set_state_present_file.yml
+++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/get_set_state_present_file.yml
@@ -30,3 +30,4 @@
- set_result.diff.after == option_value + "\n"
- get_result is not changed
- get_result.config_value == option_value
+... \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/main.yml
index 4dc72824c..5fddaf764 100644
--- a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/main.yml
@@ -13,6 +13,7 @@
import_tasks: setup.yml
- block:
+ - import_tasks: set_value.yml
# testing parameters exclusion: state and list_all
- import_tasks: exclusion_state_list-all.yml
# testing get/set option without state
@@ -31,5 +32,7 @@
- import_tasks: unset_check_mode.yml
# testing for case in issue #1776
- import_tasks: set_value_with_tilde.yml
+ - import_tasks: set_multi_value.yml
+ - import_tasks: unset_multi_value.yml
when: git_installed is succeeded and git_version.stdout is version(git_version_supporting_includes, ">=")
...
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/set_multi_value.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/set_multi_value.yml
new file mode 100644
index 000000000..8d2710b76
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/set_multi_value.yml
@@ -0,0 +1,79 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- import_tasks: setup_no_value.yml
+
+- name: setting value
+ git_config:
+ name: push.pushoption
+ add_mode: add
+ value: "{{ item }}"
+ state: present
+ scope: global
+ loop:
+ - 'merge_request.create'
+ - 'merge_request.draft'
+ - 'merge_request.target=foobar'
+ register: set_result1
+
+- name: setting value
+ git_config:
+ name: push.pushoption
+ add_mode: add
+ value: "{{ item }}"
+ state: present
+ scope: global
+ loop:
+ - 'merge_request.create'
+ - 'merge_request.draft'
+ - 'merge_request.target=foobar'
+ register: set_result2
+
+- name: getting the multi-value
+ git_config:
+ name: push.pushoption
+ scope: global
+ register: get_single_result
+
+- name: getting all values for the single option
+ git_config_info:
+ name: push.pushoption
+ scope: global
+ register: get_all_result
+
+- name: replace-all values
+ git_config:
+ name: push.pushoption
+ add_mode: replace-all
+ value: merge_request.create
+ state: present
+ scope: global
+ register: set_result3
+
+- name: assert set changed and value is correct
+ assert:
+ that:
+ - set_result1.results[0] is changed
+ - set_result1.results[1] is changed
+ - set_result1.results[2] is changed
+ - set_result2.results[0] is not changed
+ - set_result2.results[1] is not changed
+ - set_result2.results[2] is not changed
+ - set_result3 is changed
+ - get_single_result.config_value == 'merge_request.create'
+ - 'get_all_result.config_values == {"push.pushoption": ["merge_request.create", "merge_request.draft", "merge_request.target=foobar"]}'
+
+- name: assert the diffs are also right
+ assert:
+ that:
+ - set_result1.results[0].diff.before == "\n"
+ - set_result1.results[0].diff.after == "merge_request.create\n"
+ - set_result1.results[1].diff.before == "merge_request.create\n"
+ - set_result1.results[1].diff.after == ["merge_request.create", "merge_request.draft"]
+ - set_result1.results[2].diff.before == ["merge_request.create", "merge_request.draft"]
+ - set_result1.results[2].diff.after == ["merge_request.create", "merge_request.draft", "merge_request.target=foobar"]
+ - set_result3.diff.before == ["merge_request.create", "merge_request.draft", "merge_request.target=foobar"]
+ - set_result3.diff.after == "merge_request.create\n"
+...
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/set_value.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/set_value.yml
new file mode 100644
index 000000000..774e3136a
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/set_value.yml
@@ -0,0 +1,39 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- import_tasks: setup_no_value.yml
+
+- name: setting value
+ git_config:
+ name: core.name
+ value: foo
+ scope: global
+ register: set_result1
+
+- name: setting another value for same name
+ git_config:
+ name: core.name
+ value: bar
+ scope: global
+ register: set_result2
+
+- name: getting value
+ git_config:
+ name: core.name
+ scope: global
+ register: get_result
+
+- name: assert set changed and value is correct
+ assert:
+ that:
+ - set_result1 is changed
+ - set_result2 is changed
+ - get_result is not changed
+ - get_result.config_value == 'bar'
+ - set_result1.diff.before == "\n"
+ - set_result1.diff.after == "foo\n"
+ - set_result2.diff.before == "foo\n"
+ - set_result2.diff.after == "bar\n"
+...
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/set_value_with_tilde.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/set_value_with_tilde.yml
index f78e709bd..3ca9023aa 100644
--- a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/set_value_with_tilde.yml
+++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/set_value_with_tilde.yml
@@ -11,7 +11,7 @@
value: '~/foo/bar'
state: present
scope: global
- register: set_result
+ register: set_result1
- name: setting value again
git_config:
@@ -30,7 +30,7 @@
- name: assert set changed and value is correct
assert:
that:
- - set_result is changed
+ - set_result1 is changed
- set_result2 is not changed
- get_result is not changed
- get_result.config_value == '~/foo/bar'
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_multi_value.yml b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_multi_value.yml
new file mode 100644
index 000000000..4cb9dee49
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/git_config/tasks/unset_multi_value.yml
@@ -0,0 +1,28 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- import_tasks: setup_value.yml
+
+- name: unsetting "push.pushoption"
+ git_config:
+ name: push.pushoption
+ scope: global
+ state: absent
+ register: unset_result
+
+- name: getting all pushoptions values
+ git_config_info:
+ name: push.pushoption
+ scope: global
+ register: get_all_result
+
+- name: assert unsetting muti-values
+ assert:
+ that:
+ - unset_result is changed
+ - 'get_all_result.config_values == {"push.pushoption": []}'
+ - unset_result.diff.before == ["merge_request.create", "merge_request.draft", "merge_request.target=foobar"]
+ - unset_result.diff.after == "\n"
+...
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config_info/aliases b/ansible_collections/community/general/tests/integration/targets/git_config_info/aliases
new file mode 100644
index 000000000..7b8c653de
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/git_config_info/aliases
@@ -0,0 +1,7 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+azp/posix/3
+skip/aix
+destructive
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config_info/files/gitconfig b/ansible_collections/community/general/tests/integration/targets/git_config_info/files/gitconfig
new file mode 100644
index 000000000..d0590b3f8
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/git_config_info/files/gitconfig
@@ -0,0 +1,11 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+[credential "https://some.com"]
+ username = yolo
+[user]
+ name = foobar
+[push]
+ pushoption = merge_request.create
+ pushoption = merge_request.draft
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config_info/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/git_config_info/meta/main.yml
new file mode 100644
index 000000000..982de6eb0
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/git_config_info/meta/main.yml
@@ -0,0 +1,7 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+dependencies:
+ - setup_remote_tmp_dir
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/error_handling.yml b/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/error_handling.yml
new file mode 100644
index 000000000..1b84fee50
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/error_handling.yml
@@ -0,0 +1,26 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- import_tasks: "setup_file.yml"
+
+- name: getting all system configs
+ git_config_info:
+ scope: system
+ register: get_result1
+
+- name: getting all system configs for a key
+ git_config_info:
+ name: user.name
+ scope: system
+ register: get_result2
+
+- name: assert value is correct
+ assert:
+ that:
+ - get_result1.config_value == ""
+ - 'get_result1.config_values == {}'
+ - get_result2.config_value == ""
+ - 'get_result2.config_values == {"user.name": []}'
+...
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/get_all_values.yml b/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/get_all_values.yml
new file mode 100644
index 000000000..301051a42
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/get_all_values.yml
@@ -0,0 +1,19 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- include_tasks: "{{ item.import_file }}"
+
+- name: getting all values (as list) for a file config
+ git_config_info:
+ scope: "{{ item.git_scope }}"
+ path: "{{ item.git_file | default(omit) }}"
+ register: get_result
+
+- name: assert value is correct
+ assert:
+ that:
+ - get_result.config_value == ""
+ - 'get_result.config_values == {"credential.https://some.com.username": ["yolo"], "user.name": ["foobar"], "push.pushoption": ["merge_request.create", "merge_request.draft"]}'
+...
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/get_multi_value.yml b/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/get_multi_value.yml
new file mode 100644
index 000000000..14fa2800c
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/get_multi_value.yml
@@ -0,0 +1,20 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- include_tasks: "{{ item.import_file }}"
+
+- name: getting only a single value (as string) from an option with multiple values in the git config file
+ git_config_info:
+ name: push.pushoption
+ scope: "{{ item.git_scope }}"
+ path: "{{ item.git_file | default(omit) }}"
+ register: get_result
+
+- name: assert value is correct
+ assert:
+ that:
+ - get_result.config_value == "merge_request.create"
+ - 'get_result.config_values == {"push.pushoption": ["merge_request.create", "merge_request.draft"]}'
+...
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/get_simple_value.yml b/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/get_simple_value.yml
new file mode 100644
index 000000000..83cd19a0b
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/get_simple_value.yml
@@ -0,0 +1,38 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- include_tasks: "{{ item.import_file }}"
+
+- name: getting simple file value1
+ git_config_info:
+ name: user.name
+ scope: "{{ item.git_scope }}"
+ path: "{{ item.git_file | default(omit) }}"
+ register: get_result1
+
+- name: getting simple file value2
+ git_config_info:
+ name: "credential.https://some.com.username"
+ scope: "{{ item.git_scope }}"
+ path: "{{ item.git_file | default(omit) }}"
+ register: get_result2
+
+- name: getting not existing value
+ git_config_info:
+ name: "user.email"
+ scope: "{{ item.git_scope }}"
+ path: "{{ item.git_file | default(omit) }}"
+ register: get_result3
+
+- name: assert value is correct
+ assert:
+ that:
+ - get_result1.config_value == "foobar"
+ - 'get_result1.config_values == {"user.name": ["foobar"]}'
+ - get_result2.config_value == "yolo"
+ - 'get_result2.config_values == {"credential.https://some.com.username": ["yolo"]}'
+ - get_result3.config_value == ""
+ - 'get_result3.config_values == {"user.email": []}'
+...
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/main.yml
new file mode 100644
index 000000000..993238805
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/main.yml
@@ -0,0 +1,33 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# test code for the git_config_info module
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: setup
+ import_tasks: setup.yml
+
+- block:
+ - include_tasks: get_simple_value.yml
+ loop:
+ - { import_file: setup_global.yml, git_scope: 'global' }
+ - { import_file: setup_file.yml, git_scope: 'file', git_file: "{{ remote_tmp_dir }}/gitconfig_file" }
+
+ - include_tasks: get_multi_value.yml
+ loop:
+ - { import_file: setup_global.yml, git_scope: 'global' }
+ - { import_file: setup_file.yml, git_scope: 'file', git_file: "{{ remote_tmp_dir }}/gitconfig_file" }
+
+ - include_tasks: get_all_values.yml
+ loop:
+ - { import_file: setup_global.yml, git_scope: 'global' }
+ - { import_file: setup_file.yml, git_scope: 'file', git_file: "{{ remote_tmp_dir }}/gitconfig_file" }
+
+ - include_tasks: error_handling.yml
+ when: git_installed is succeeded and git_version.stdout is version(git_version_supporting_includes, ">=")
+...
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/setup.yml b/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/setup.yml
new file mode 100644
index 000000000..6e5516da5
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/setup.yml
@@ -0,0 +1,15 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: verify that git is installed so this test can continue
+ command: which git
+ register: git_installed
+ ignore_errors: true
+
+- name: get git version, only newer than {{git_version_supporting_includes}} has includes option
+ shell: "git --version | grep 'git version' | sed 's/git version //'"
+ register: git_version
+ ignore_errors: true
+...
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/setup_file.yml b/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/setup_file.yml
new file mode 100644
index 000000000..854b10997
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/setup_file.yml
@@ -0,0 +1,16 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# ------
+# set up : set gitconfig with value
+- name: delete global config
+ file:
+ path: ~/.gitconfig
+ state: absent
+
+- name: set up file config
+ copy:
+ src: gitconfig
+ dest: "{{ remote_tmp_dir }}/gitconfig_file"
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/setup_global.yml b/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/setup_global.yml
new file mode 100644
index 000000000..a9e045a57
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/git_config_info/tasks/setup_global.yml
@@ -0,0 +1,16 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# ------
+# set up : set gitconfig with value
+- name: delete file config
+ file:
+ path: "{{ remote_tmp_dir }}/gitconfig_file"
+ state: absent
+
+- name: setup global config
+ copy:
+ src: gitconfig
+ dest: ~/.gitconfig
diff --git a/ansible_collections/community/general/tests/integration/targets/git_config_info/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/git_config_info/vars/main.yml
new file mode 100644
index 000000000..55c3d1738
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/git_config_info/vars/main.yml
@@ -0,0 +1,7 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+git_version_supporting_includes: 1.7.10
+...
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_group_access_token/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_group_access_token/aliases
new file mode 100644
index 000000000..fc0e157c9
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_group_access_token/aliases
@@ -0,0 +1,7 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+azp/posix/1
+gitlab/ci
+disabled
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_group_access_token/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_group_access_token/defaults/main.yml
new file mode 100644
index 000000000..1b0dab289
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_group_access_token/defaults/main.yml
@@ -0,0 +1,15 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) 2024, Zoran Krleza <zoran.krleza@true-north.hr>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+gitlab_api_token:
+gitlab_api_url:
+gitlab_validate_certs: false
+gitlab_group_name:
+gitlab_token_name:
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_group_access_token/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_group_access_token/tasks/main.yml
new file mode 100644
index 000000000..4e6234238
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_group_access_token/tasks/main.yml
@@ -0,0 +1,221 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) 2024, Zoran Krleza <zoran.krleza@true-north.hr>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Install required libs
+ pip:
+ name: python-gitlab
+ state: present
+
+- block:
+ - name: Try to create access token in nonexisting group
+ community.general.gitlab_group_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ group: "some_nonexisting_group"
+ name: "{{ gitlab_token_name }}"
+ state: present
+ expires_at: '2025-01-01'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ register: create_pfail_token_status
+ always:
+ - name: Assert that token creation in nonexisting group failed
+ assert:
+ that:
+ - create_pfail_token_status is failed
+ ignore_errors: true
+
+- block:
+ - name: Try to create access token with nonvalid expires_at
+ community.general.gitlab_group_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ group: "some_nonexisting_group"
+ name: "{{ gitlab_token_name }}"
+ state: present
+ expires_at: '2025-13-01'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ register: create_efail_token_status
+ always:
+ - name: Assert that token creation with invalid expires_at failed
+ assert:
+ that:
+ - create_efail_token_status is failed
+ ignore_errors: true
+
+- name: Create access token
+ community.general.gitlab_group_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ group: "{{ gitlab_group_name }}"
+ name: "{{ gitlab_token_name }}"
+ state: present
+ expires_at: '2024-12-31'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ register: create_token_status
+- name: Assert that token creation with valid arguments is successfull
+ assert:
+ that:
+ - create_token_status is changed
+ - create_token_status.access_token.token is defined
+
+- name: Check existing access token recreate=never (default)
+ community.general.gitlab_group_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ group: "{{ gitlab_group_name }}"
+ name: "{{ gitlab_token_name }}"
+ state: present
+ expires_at: '2024-12-31'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ register: check_token_status
+- name: Assert that token creation without changes and recreate=never succeeds with status not changed
+ assert:
+ that:
+ - check_token_status is not changed
+ - check_token_status.access_token.token is not defined
+
+- name: Check existing access token with recreate=state_change
+ community.general.gitlab_group_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ group: "{{ gitlab_group_name }}"
+ name: "{{ gitlab_token_name }}"
+ state: present
+ expires_at: '2024-12-31'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ recreate: state_change
+ register: check_recreate_token_status
+- name: Assert that token creation without changes and recreate=state_change succeeds with status not changed
+ assert:
+ that:
+ - check_recreate_token_status is not changed
+ - check_recreate_token_status.access_token.token is not defined
+
+- block:
+ - name: Try to change existing access token with recreate=never
+ community.general.gitlab_group_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ group: "{{ gitlab_group_name }}"
+ name: "{{ gitlab_token_name }}"
+ state: present
+ expires_at: '2025-01-01'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ register: change_token_status
+ always:
+ - name: Assert that token change with recreate=never fails
+ assert:
+ that:
+ - change_token_status is failed
+ ignore_errors: true
+
+- name: Try to change existing access token with recreate=state_change
+ community.general.gitlab_group_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ group: "{{ gitlab_group_name }}"
+ name: "{{ gitlab_token_name }}"
+ state: present
+ expires_at: '2025-01-01'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ recreate: state_change
+ register: change_recreate_token_status
+- name: Assert that token change with recreate=state_change succeeds
+ assert:
+ that:
+ - change_recreate_token_status is changed
+ - change_recreate_token_status.access_token.token is defined
+
+- name: Try to change existing access token with recreate=always
+ community.general.gitlab_group_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ group: "{{ gitlab_group_name }}"
+ name: "{{ gitlab_token_name }}"
+ state: present
+ expires_at: '2025-01-01'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ recreate: always
+ register: change_recreate1_token_status
+- name: Assert that token change with recreate=always succeeds
+ assert:
+ that:
+ - change_recreate1_token_status is changed
+ - change_recreate1_token_status.access_token.token is defined
+
+- name: Revoke access token
+ community.general.gitlab_group_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ group: "{{ gitlab_group_name }}"
+ name: "{{ gitlab_token_name }}"
+ state: absent
+ expires_at: '2024-12-31'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ register: revoke_token_status
+- name: Assert that token revocation succeeds
+ assert:
+ that:
+ - revoke_token_status is changed
+
+- name: Revoke nonexisting access token
+ community.general.gitlab_group_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ group: "{{ gitlab_group_name }}"
+ name: "{{ gitlab_token_name }}"
+ state: absent
+ expires_at: '2024-12-31'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ register: revoke_token_status
+- name: Assert that token revocation succeeds with status not changed
+ assert:
+ that:
+ - revoke_token_status is not changed \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_instance_variable/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_instance_variable/aliases
new file mode 100644
index 000000000..bd1f02444
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_instance_variable/aliases
@@ -0,0 +1,5 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+unsupported
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_instance_variable/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_instance_variable/tasks/main.yml
new file mode 100644
index 000000000..94a81698b
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_instance_variable/tasks/main.yml
@@ -0,0 +1,606 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Install required libs
+ pip:
+ name: python-gitlab
+ state: present
+
+- name: purge all variables for check_mode test
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ purge: true
+
+- name: add a variable value in check_mode
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: checkmode
+ check_mode: true
+ register: gitlab_instance_variable_state
+
+- name: check_mode state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is changed
+
+- name: apply add value from check_mode test
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: checkmode
+ register: gitlab_instance_variable_state
+
+- name: state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is changed
+
+- name: apply same value again again
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: checkmode
+ register: gitlab_instance_variable_state
+
+- name: state must be not changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is not changed
+
+- name: change protected attribute
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: checkmode
+ protected: true
+ register: gitlab_instance_variable_state
+
+- name: state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is changed
+
+- name: revert protected attribute
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: checkmode
+ protected: false
+ register: gitlab_instance_variable_state
+
+- name: state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is changed
+
+- name: change masked attribute
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: checkmode
+ masked: true
+ register: gitlab_instance_variable_state
+
+- name: state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is changed
+
+- name: revert masked attribute by not mention it
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: checkmode
+ masked: false
+ register: gitlab_instance_variable_state
+
+- name: state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is changed
+
+- name: revert again masked attribute by not mention it (idempotent)
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: checkmode
+ register: gitlab_instance_variable_state
+
+- name: state must be not changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is not changed
+
+- name: set all attributes
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: checkmode
+ masked: true
+ protected: true
+ variable_type: env_var
+ register: gitlab_instance_variable_state
+
+- name: state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is changed
+
+- name: set again all attributes (idempotent)
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: checkmode
+ masked: true
+ protected: true
+ variable_type: env_var
+ register: gitlab_instance_variable_state
+
+- name: state must not be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is not changed
+
+- name: revert both (masked and protected) attribute
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: checkmode
+ protected: false
+ register: gitlab_instance_variable_state
+
+- name: state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is changed
+
+- name: change a variable value in check_mode again
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: checkmode
+ check_mode: true
+ register: gitlab_instance_variable_state
+
+- name: check_mode state must not be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is not changed
+
+- name: apply again the value change from check_mode test
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: checkmode
+ register: gitlab_instance_variable_state
+
+- name: state must not be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is not changed
+
+- name: purge all variables again
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ purge: true
+
+- name: set two test variables
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: abc123
+ - name: SECRET_ACCESS_KEY
+ value: 321cba
+ register: gitlab_instance_variable_state
+
+- name: set two test variables state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is changed
+ - gitlab_instance_variable_state.instance_variable.added|length == 2
+ - gitlab_instance_variable_state.instance_variable.untouched|length == 0
+ - gitlab_instance_variable_state.instance_variable.removed|length == 0
+ - gitlab_instance_variable_state.instance_variable.updated|length == 0
+
+- name: re-set two test variables
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: abc123
+ - name: SECRET_ACCESS_KEY
+ value: 321cba
+ register: gitlab_instance_variable_state
+
+- name: re-set two test variables state must not be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is not changed
+ - gitlab_instance_variable_state.instance_variable.added|length == 0
+ - gitlab_instance_variable_state.instance_variable.untouched|length == 2
+ - gitlab_instance_variable_state.instance_variable.removed|length == 0
+ - gitlab_instance_variable_state.instance_variable.updated|length == 0
+
+- name: edit one variable
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: changed
+ purge: false
+ register: gitlab_instance_variable_state
+
+- name: edit one variable state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state.changed
+ - gitlab_instance_variable_state.instance_variable.added|length == 0
+ - gitlab_instance_variable_state.instance_variable.untouched|length == 1
+ - gitlab_instance_variable_state.instance_variable.removed|length == 0
+ - gitlab_instance_variable_state.instance_variable.updated|length == 1
+ - gitlab_instance_variable_state.instance_variable.updated[0] == "ACCESS_KEY_ID"
+
+- name: append one variable
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: some
+ value: value
+ purge: false
+ register: gitlab_instance_variable_state
+
+- name: append one variable state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state.changed
+ - gitlab_instance_variable_state.instance_variable.added|length == 1
+ - gitlab_instance_variable_state.instance_variable.untouched|length == 2
+ - gitlab_instance_variable_state.instance_variable.removed|length == 0
+ - gitlab_instance_variable_state.instance_variable.updated|length == 0
+ - gitlab_instance_variable_state.instance_variable.added[0] == "some"
+
+- name: re-set all variables
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: ACCESS_KEY_ID
+ value: changed
+ - name: SECRET_ACCESS_KEY
+ value: 321cba
+ - name: some
+ value: value
+ register: gitlab_instance_variable_state
+
+- name: re-set all variables state must not be changed
+ assert:
+ that:
+ - not gitlab_instance_variable_state.changed
+ - gitlab_instance_variable_state.instance_variable.added|length == 0
+ - gitlab_instance_variable_state.instance_variable.untouched|length == 3
+ - gitlab_instance_variable_state.instance_variable.removed|length == 0
+ - gitlab_instance_variable_state.instance_variable.updated|length == 0
+
+- name: set one variables and purge all others
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: some
+ value: value
+ purge: true
+ register: gitlab_instance_variable_state
+
+- name: set one variables and purge all others state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state.changed
+ - gitlab_instance_variable_state.instance_variable.added|length == 0
+ - gitlab_instance_variable_state.instance_variable.untouched|length == 1
+ - gitlab_instance_variable_state.instance_variable.removed|length == 2
+ - gitlab_instance_variable_state.instance_variable.updated|length == 0
+
+- name: only one variable is left
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: some
+ value: value
+ purge: false
+ register: gitlab_instance_variable_state
+
+- name: only one variable is left state must not be changed
+ assert:
+ that:
+ - not gitlab_instance_variable_state.changed
+ - gitlab_instance_variable_state.instance_variable.added|length == 0
+ - gitlab_instance_variable_state.instance_variable.untouched|length == 1
+ - gitlab_instance_variable_state.instance_variable.removed|length == 0
+ - gitlab_instance_variable_state.instance_variable.updated|length == 0
+ - gitlab_instance_variable_state.instance_variable.untouched[0] == "some"
+
+- name: test integer values
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: some
+ value: 42
+ purge: false
+ register: gitlab_instance_variable_state
+
+- name: only one variable is left state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state.changed
+ - gitlab_instance_variable_state.instance_variable.added|length == 0
+ - gitlab_instance_variable_state.instance_variable.untouched|length == 0
+ - gitlab_instance_variable_state.instance_variable.removed|length == 0
+ - gitlab_instance_variable_state.instance_variable.updated|length == 1
+
+- name: test float values
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: some
+ value: 42.23
+ purge: false
+ register: gitlab_instance_variable_state
+
+- name: only one variable is left state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state.changed
+ - gitlab_instance_variable_state.instance_variable.added|length == 0
+ - gitlab_instance_variable_state.instance_variable.untouched|length == 0
+ - gitlab_instance_variable_state.instance_variable.removed|length == 0
+ - gitlab_instance_variable_state.instance_variable.updated|length == 1
+
+- name: delete the last left variable
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ state: absent
+ variables:
+ - name: some
+ register: gitlab_instance_variable_state
+
+- name: no variable is left state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state.changed
+ - gitlab_instance_variable_state.instance_variable.added|length == 0
+ - gitlab_instance_variable_state.instance_variable.untouched|length == 0
+ - gitlab_instance_variable_state.instance_variable.removed|length == 1
+ - gitlab_instance_variable_state.instance_variable.updated|length == 0
+ - gitlab_instance_variable_state.instance_variable.removed[0] == "some"
+
+- name: add one variable with variable_type file
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: my_test_var
+ value: my_test_value
+ variable_type: file
+ purge: false
+ register: gitlab_instance_variable_state
+
+- name: append one variable state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state.changed
+ - gitlab_instance_variable_state.instance_variable.added|length == 1
+ - gitlab_instance_variable_state.instance_variable.untouched|length == 0
+ - gitlab_instance_variable_state.instance_variable.removed|length == 0
+ - gitlab_instance_variable_state.instance_variable.updated|length == 0
+ # VALUE_SPECIFIED_IN_NO_LOG_PARAMETER
+ #- gitlab_instance_variable_state.instance_variable.added[0] == "my_test_var"
+
+- name: change variable_type attribute
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: my_test_var
+ value: my_test_value
+ variable_type: env_var
+ register: gitlab_instance_variable_state
+
+- name: state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is changed
+
+- name: revert variable_type attribute
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: my_test_var
+ value: my_test_value
+ variable_type: file
+ register: gitlab_instance_variable_state
+
+- name: state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is changed
+
+- name: delete the variable_type file variable
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ state: absent
+ variables:
+ - name: my_test_var
+ register: gitlab_instance_variable_state
+
+- name: no variable is left state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state.changed
+ - gitlab_instance_variable_state.instance_variable.added|length == 0
+ - gitlab_instance_variable_state.instance_variable.untouched|length == 0
+ - gitlab_instance_variable_state.instance_variable.removed|length == 1
+ - gitlab_instance_variable_state.instance_variable.updated|length == 0
+ - gitlab_instance_variable_state.instance_variable.removed[0] == "my_test_var"
+
+- name: set complete page and purge existing ones
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: page1_var01
+ value: value
+ - name: page1_var02
+ value: value
+ - name: page1_var03
+ value: value
+ - name: page1_var04
+ value: value
+ - name: page1_var05
+ value: value
+ - name: page1_var06
+ value: value
+ - name: page1_var07
+ value: value
+ - name: page1_var08
+ value: value
+ - name: page1_var09
+ value: value
+ - name: page1_var10
+ value: value
+ - name: page1_var11
+ value: value
+ - name: page1_var12
+ value: value
+ - name: page1_var13
+ value: value
+ - name: page1_var14
+ value: value
+ - name: page1_var15
+ value: value
+ - name: page1_var16
+ value: value
+ - name: page1_var17
+ value: value
+ - name: page1_var18
+ value: value
+ - name: page1_var19
+ value: value
+ - name: page1_var20
+ value: value
+ purge: true
+ register: gitlab_instance_variable_state
+
+- name: complete page added state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state is changed
+ - gitlab_instance_variable_state.instance_variable.added|length == 20
+ - gitlab_instance_variable_state.instance_variable.untouched|length == 0
+
+- name: check that no variables are left
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ purge: true
+ register: gitlab_instance_variable_state
+
+- name: check that no variables are untouched state must be changed
+ assert:
+ that:
+ - gitlab_instance_variable_state.changed
+ - gitlab_instance_variable_state.instance_variable.added|length == 0
+ - gitlab_instance_variable_state.instance_variable.untouched|length == 0
+ - gitlab_instance_variable_state.instance_variable.removed|length == 20
+ - gitlab_instance_variable_state.instance_variable.updated|length == 0
+
+- name: throw error when state is present but no value is given
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ variables:
+ - name: no_value
+ register: gitlab_instance_variable_state
+ ignore_errors: true
+
+- name: verify fail
+ assert:
+ that:
+ - gitlab_instance_variable_state.failed
+ - gitlab_instance_variable_state is not changed
+
+- name: set a new variable to delete it later
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ purge: true
+ variables:
+ - name: delete_me
+ value: ansible
+ register: gitlab_instance_variable_state
+
+- name: verify the change
+ assert:
+ that:
+ - gitlab_instance_variable_state.changed
+
+- name: delete variable without referencing its value
+ gitlab_instance_variable:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_login_token }}"
+ state: absent
+ variables:
+ - name: delete_me
+ register: gitlab_instance_variable_state
+
+- name: verify deletion
+ assert:
+ that:
+ - gitlab_instance_variable_state.changed
+ - gitlab_instance_variable_state.instance_variable.removed|length == 1
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_issue/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_issue/aliases
new file mode 100644
index 000000000..4f4e3dcc9
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_issue/aliases
@@ -0,0 +1,6 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+gitlab/ci
+disabled
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_issue/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_issue/defaults/main.yml
new file mode 100644
index 000000000..f94530fbf
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_issue/defaults/main.yml
@@ -0,0 +1,15 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+gitlab_branch: ansible_test_branch
+gitlab_project_name: ansible_test_project
+gitlab_project_group: ansible_test_group
+gitlab_host: ansible_test_host
+gitlab_api_token: ansible_test_api_token
+gitlab_labels: ansible_test_label
+gitlab_milestone_search: ansible_test_milestone_search
+gitlab_milestone_group_id: ansible_test_milestone_group_id
+gitlab_assignee_ids: ansible_test_assignee_ids
+gitlab_description_path: ansible_test_description_path \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_issue/files/description.md b/ansible_collections/community/general/tests/integration/targets/gitlab_issue/files/description.md
new file mode 100644
index 000000000..f0ff12a77
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_issue/files/description.md
@@ -0,0 +1,9 @@
+<!--
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
+-->
+
+### Description
+
+Issue test description \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_issue/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_issue/tasks/main.yml
new file mode 100644
index 000000000..af1416c3d
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_issue/tasks/main.yml
@@ -0,0 +1,150 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Install required libs
+ pip:
+ name: python-gitlab
+ state: present
+
+- block:
+ - name: Create {{ gitlab_project_name }} project
+ gitlab_project:
+ api_url: "{{ gitlab_host }}"
+ validate_certs: true
+ api_token: "{{ gitlab_api_token }}"
+ name: "{{ gitlab_project_name }}"
+ group: "{{ gitlab_project_group }}"
+ default_branch: "{{ gitlab_branch }}"
+ initialize_with_readme: true
+ state: present
+
+ - name: Create Issue
+ gitlab_issue:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ description: "Test description"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ state: present
+ title: "Ansible test issue"
+ register: gitlab_issue_create
+
+ - name: Test Issue Created
+ assert:
+ that:
+ - gitlab_issue_create is changed
+
+ - name: Create Issue ( Idempotency test )
+ gitlab_issue:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ description: "Test description"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ state: present
+ title: "Ansible test issue"
+ register: gitlab_issue_create_idempotence
+
+ - name: Test Create Issue is Idempotent
+ assert:
+ that:
+ - gitlab_issue_create_idempotence is not changed
+
+ - name: Update Issue Test ( Additions )
+ gitlab_issue:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ assignee_ids: "{{ gitlab_assignee_ids }}"
+ description_path: "{{ gitlab_description_path }}"
+ labels: "{{ gitlab_labels }}"
+ milestone_search: "{{ gitlab_milestone_search }}"
+ milestone_group_id: "{{ gitlab_milestone_group_id }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ state: present
+ title: "Ansible test issue"
+ register: gitlab_issue_update_additions
+
+ - name: Test Issue Updated ( Additions )
+ assert:
+ that:
+ - gitlab_issue_update_additions.issue.labels[0] == "{{ gitlab_labels[0] }}"
+ - gitlab_issue_update_additions.issue.assignees[0].username == "{{ gitlab_assignee_ids[0] }}"
+ - "'### Description\n\nIssue test description' in gitlab_issue_update_additions.issue.description"
+ - gitlab_issue_update_additions.issue.milestone.title == "{{ gitlab_milestone_search }}"
+
+ - name: Update Issue Test ( Persistence )
+ gitlab_issue:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ description_path: "{{ gitlab_description_path }}"
+ milestone_search: "{{ gitlab_milestone_search }}"
+ milestone_group_id: "{{ gitlab_milestone_group_id }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ state: present
+ title: "Ansible test issue"
+ register: gitlab_issue_update_persistence
+
+ - name: Test issue Not Updated ( Persistence )
+ assert:
+ that:
+ - gitlab_issue_update_persistence.issue.labels[0] == "{{ gitlab_labels[0] }}"
+ - gitlab_issue_update_persistence.issue.assignees[0].username == "{{ gitlab_assignee_ids[0] }}"
+
+ - name: Update Issue Test ( Removals )
+ gitlab_issue:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ assignee_ids: []
+ description_path: "{{ gitlab_description_path }}"
+ labels: []
+ milestone_search: ""
+ milestone_group_id: ""
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ state: present
+ title: "Ansible test issue"
+ register: gitlab_issue_update_removal
+
+ - name: Test issue updated
+ assert:
+ that:
+ - gitlab_issue_update_removal.issue.labels == []
+ - gitlab_issue_update_removal.issue.assignees == []
+ - gitlab_issue_update_removal.issue.milestone == None
+
+ - name: Delete Issue
+ gitlab_issue:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_api_token }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ title: "Ansible test issue"
+ state: absent
+ register: gitlab_issue_delete
+
+ - name: Test issue is deleted
+ assert:
+ that:
+ - gitlab_issue_delete is changed
+
+ always:
+ - name: Delete Issue
+ gitlab_issue:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_api_token }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ title: "Ansible test issue"
+ state_filter: "opened"
+ state: absent
+ register: gitlab_issue_delete
+ - name: Clean up {{ gitlab_project_name }}
+ gitlab_project:
+ api_url: "{{ gitlab_host }}"
+ validate_certs: false
+ api_token: "{{ gitlab_api_token }}"
+ name: "{{ gitlab_project_name }}"
+ group: "{{ gitlab_project_group }}"
+ state: absent
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_label/README.md b/ansible_collections/community/general/tests/integration/targets/gitlab_label/README.md
new file mode 100644
index 000000000..e27cb74c8
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_label/README.md
@@ -0,0 +1,19 @@
+<!--
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
+-->
+
+# Gitlab integration tests
+
+1. to run integration tests locally, I've setup a podman pod with both gitlab-ee image and the testing image
+2. gitlab's related information were taken from [here](https://docs.gitlab.com/ee/install/docker.html), about the variable it needs (hostname, ports, volumes); volumes were pre-made before launching the image
+3. image that run integration tests is started with `podman run --rm -it --pod <pod_name> --name <image_name> --network=host --volume <path_to_git_repo>/ansible_community/community.general:<container_path_to>/workspace/ansible_collections/community/general quay.io/ansible/azure-pipelines-test-container:4.0.1`
+4. into the testing image, run
+```sh
+pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check
+cd <container_path_to>/workspace/ansible_collections/community/general
+ansible-test integration gitlab_label -vvv
+```
+
+While debugging with `q` package, open a second terminal and run `podman exec -it <image_name> /bin/bash` and inside it do `tail -f /tmp/q` .
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_label/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_label/aliases
new file mode 100644
index 000000000..4f4e3dcc9
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_label/aliases
@@ -0,0 +1,6 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+gitlab/ci
+disabled
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_label/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_label/defaults/main.yml
new file mode 100644
index 000000000..315cbd77f
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_label/defaults/main.yml
@@ -0,0 +1,17 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+gitlab_project_name: ansible_test_project
+gitlab_host: ansible_test_host
+gitlab_api_token: ansible_test_api_token
+gitlab_project_group: ansible_test_group
+gitlab_branch: ansible_test_branch
+gitlab_first_label: ansible_test_label
+gitlab_first_label_color: "#112233"
+gitlab_first_label_description: "label description"
+gitlab_first_label_priority: 10
+gitlab_second_label: ansible_test_second_label
+gitlab_second_label_color: "#445566"
+gitlab_second_label_new_name: ansible_test_second_label_new_name \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_label/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_label/tasks/main.yml
new file mode 100644
index 000000000..880b72ceb
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_label/tasks/main.yml
@@ -0,0 +1,463 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Install required libs
+ pip:
+ name: python-gitlab
+ state: present
+
+- block:
+###
+### Group label
+###
+
+ - name: Create {{ gitlab_project_group }}
+ gitlab_group:
+ api_url: "{{ gitlab_host }}"
+ validate_certs: true
+ api_token: "{{ gitlab_api_token }}"
+ name: "{{ gitlab_project_group }}"
+ state: present
+
+ - name: Purge all group labels for check_mode test
+ gitlab_label:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_api_token }}"
+ group: "{{ gitlab_project_group }}"
+ purge: true
+
+ - name: Group label - Add a label in check_mode
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ group: "{{ gitlab_project_group }}"
+ labels:
+ - name: "{{ gitlab_second_label }}"
+ color: "{{ gitlab_second_label_color }}"
+ check_mode: true
+ register: gitlab_group_label_state
+
+ - name: Group label - Check_mode state must be changed
+ assert:
+ that:
+ - gitlab_group_label_state is changed
+
+ - name: Group label - Create label {{ gitlab_first_label }} and {{ gitlab_second_label }}
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ group: "{{ gitlab_project_group }}"
+ labels:
+ - name: "{{ gitlab_first_label }}"
+ color: "{{ gitlab_first_label_color }}"
+ description: "{{ gitlab_first_label_description }}"
+ priority: "{{ gitlab_first_label_priority }}"
+ - name: "{{ gitlab_second_label }}"
+ color: "{{ gitlab_second_label_color }}"
+ state: present
+ register: gitlab_group_label_create
+
+ - name: Group label - Test Label Created
+ assert:
+ that:
+ - gitlab_group_label_create is changed
+ - gitlab_group_label_create.labels.added|length == 2
+ - gitlab_group_label_create.labels.untouched|length == 0
+ - gitlab_group_label_create.labels.removed|length == 0
+ - gitlab_group_label_create.labels.updated|length == 0
+ - gitlab_group_label_create.labels.added[0] == "{{ gitlab_first_label }}"
+ - gitlab_group_label_create.labels.added[1] == "{{ gitlab_second_label }}"
+
+ - name: Group label - Create Label ( Idempotency test )
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ group: "{{ gitlab_project_group }}"
+ labels:
+ - name: "{{ gitlab_first_label }}"
+ color: "{{ gitlab_first_label_color }}"
+ description: "{{ gitlab_first_label_description }}"
+ priority: "{{ gitlab_first_label_priority }}"
+ state: present
+ register: gitlab_group_label_create_idempotence
+
+ - name: Group label - Test Create Label is Idempotent
+ assert:
+ that:
+ - gitlab_group_label_create_idempotence is not changed
+
+ - name: Group label - Update Label {{ gitlab_first_label }} changing color
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ group: "{{ gitlab_project_group }}"
+ labels:
+ - name: "{{ gitlab_first_label }}"
+ color: "{{ gitlab_second_label_color }}"
+ state: present
+ register: gitlab_group_label_update
+
+ - name: Group label - Test Label Updated
+ assert:
+ that:
+ - gitlab_group_label_update.labels.added|length == 0
+ - gitlab_group_label_update.labels.untouched|length == 0
+ - gitlab_group_label_update.labels.removed|length == 0
+ - gitlab_group_label_update.labels.updated|length == 1
+ - gitlab_group_label_update.labels.updated[0] == "{{ gitlab_first_label }}"
+
+ - name: Group label - Change label {{ gitlab_second_label }} name to {{ gitlab_second_label_new_name }}
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ group: "{{ gitlab_project_group }}"
+ labels:
+ - name: "{{ gitlab_second_label }}"
+ new_name: "{{ gitlab_second_label_new_name }}"
+ state: present
+ register: gitlab_group_label_new_name
+
+ - name: Group label - Test Label name changed
+ assert:
+ that:
+ - gitlab_group_label_new_name.labels.added|length == 0
+ - gitlab_group_label_new_name.labels.untouched|length == 0
+ - gitlab_group_label_new_name.labels.removed|length == 0
+ - gitlab_group_label_new_name.labels.updated|length == 1
+ - gitlab_group_label_new_name.labels.updated[0] == "{{ gitlab_second_label }}"
+
+ - name: Group label - Change label name back to {{ gitlab_second_label }}
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ group: "{{ gitlab_project_group }}"
+ labels:
+ - name: "{{ gitlab_second_label_new_name }}"
+ new_name: "{{ gitlab_second_label }}"
+ state: present
+ register: gitlab_group_label_orig_name
+
+ - name: Group label - Test Label name changed back
+ assert:
+ that:
+ - gitlab_group_label_orig_name.labels.added|length == 0
+ - gitlab_group_label_orig_name.labels.untouched|length == 0
+ - gitlab_group_label_orig_name.labels.removed|length == 0
+ - gitlab_group_label_orig_name.labels.updated|length == 1
+ - gitlab_group_label_orig_name.labels.updated[0] == "{{ gitlab_second_label_new_name }}"
+
+ - name: Group label - Update Label Test ( Additions )
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ group: "{{ gitlab_project_group }}"
+ labels:
+ - name: "{{ gitlab_second_label }}"
+ description: "{{ gitlab_first_label_description }}"
+ priority: "{{ gitlab_first_label_priority }}"
+ state: present
+ register: gitlab_group_label_update_additions
+
+ - name: Group label - Test Label Updated ( Additions )
+ assert:
+ that:
+ - gitlab_group_label_update_additions.labels.added|length == 0
+ - gitlab_group_label_update_additions.labels.untouched|length == 0
+ - gitlab_group_label_update_additions.labels.removed|length == 0
+ - gitlab_group_label_update_additions.labels.updated|length == 1
+ - gitlab_group_label_update_additions.labels.updated[0] == "{{ gitlab_second_label }}"
+
+ - name: Group label - Delete Label {{ gitlab_second_label }}
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ group: "{{ gitlab_project_group }}"
+ labels:
+ - name: "{{ gitlab_second_label }}"
+ state: absent
+ register: gitlab_group_label_delete
+
+ - name: Group label - Test label is deleted
+ assert:
+ that:
+ - gitlab_group_label_delete is changed
+ - gitlab_group_label_delete.labels.added|length == 0
+ - gitlab_group_label_delete.labels.untouched|length == 0
+ - gitlab_group_label_delete.labels.removed|length == 1
+ - gitlab_group_label_delete.labels.updated|length == 0
+ - gitlab_group_label_delete.labels.removed[0] == "{{ gitlab_second_label }}"
+
+ - name: Group label - Create label {{ gitlab_second_label }} again purging the other
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ group: "{{ gitlab_project_group }}"
+ purge: true
+ labels:
+ - name: "{{ gitlab_second_label }}"
+ color: "{{ gitlab_second_label_color }}"
+ state: present
+ register: gitlab_group_label_create_purging
+
+ - name: Group label - Test Label Created again
+ assert:
+ that:
+ - gitlab_group_label_create_purging is changed
+ - gitlab_group_label_create_purging.labels.added|length == 1
+ - gitlab_group_label_create_purging.labels.untouched|length == 0
+ - gitlab_group_label_create_purging.labels.removed|length == 1
+ - gitlab_group_label_create_purging.labels.updated|length == 0
+ - gitlab_group_label_create_purging.labels.added[0] == "{{ gitlab_second_label }}"
+ - gitlab_group_label_create_purging.labels.removed[0] == "{{ gitlab_first_label }}"
+
+###
+### Project label
+###
+
+ - name: Create {{ gitlab_project_name }}
+ gitlab_project:
+ api_url: "{{ gitlab_host }}"
+ validate_certs: true
+ api_token: "{{ gitlab_api_token }}"
+ name: "{{ gitlab_project_name }}"
+ group: "{{ gitlab_project_group }}"
+ default_branch: "{{ gitlab_branch }}"
+ initialize_with_readme: true
+ state: present
+
+ - name: Purge all labels for check_mode test
+ gitlab_label:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_api_token }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ purge: true
+
+ - name: Add a label in check_mode
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ labels:
+ - name: "{{ gitlab_second_label }}"
+ color: "{{ gitlab_second_label_color }}"
+ check_mode: true
+ register: gitlab_first_label_state
+
+ - name: Check_mode state must be changed
+ assert:
+ that:
+ - gitlab_first_label_state is changed
+
+ - name: Create label {{ gitlab_first_label }} and {{ gitlab_second_label }}
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ labels:
+ - name: "{{ gitlab_first_label }}"
+ color: "{{ gitlab_first_label_color }}"
+ description: "{{ gitlab_first_label_description }}"
+ priority: "{{ gitlab_first_label_priority }}"
+ - name: "{{ gitlab_second_label }}"
+ color: "{{ gitlab_second_label_color }}"
+ state: present
+ register: gitlab_first_label_create
+
+ - name: Test Label Created
+ assert:
+ that:
+ - gitlab_first_label_create is changed
+ - gitlab_first_label_create.labels.added|length == 2
+ - gitlab_first_label_create.labels.untouched|length == 0
+ - gitlab_first_label_create.labels.removed|length == 0
+ - gitlab_first_label_create.labels.updated|length == 0
+ - gitlab_first_label_create.labels.added[0] == "{{ gitlab_first_label }}"
+ - gitlab_first_label_create.labels.added[1] == "{{ gitlab_second_label }}"
+
+ - name: Create Label ( Idempotency test )
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ labels:
+ - name: "{{ gitlab_first_label }}"
+ color: "{{ gitlab_first_label_color }}"
+ description: "{{ gitlab_first_label_description }}"
+ priority: "{{ gitlab_first_label_priority }}"
+ state: present
+ register: gitlab_first_label_create_idempotence
+
+ - name: Test Create Label is Idempotent
+ assert:
+ that:
+ - gitlab_first_label_create_idempotence is not changed
+
+ - name: Update Label {{ gitlab_first_label }} changing color
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ labels:
+ - name: "{{ gitlab_first_label }}"
+ color: "{{ gitlab_second_label_color }}"
+ state: present
+ register: gitlab_first_label_update
+
+ - name: Test Label Updated
+ assert:
+ that:
+ - gitlab_first_label_update.labels.added|length == 0
+ - gitlab_first_label_update.labels.untouched|length == 0
+ - gitlab_first_label_update.labels.removed|length == 0
+ - gitlab_first_label_update.labels.updated|length == 1
+ - gitlab_first_label_update.labels.updated[0] == "{{ gitlab_first_label }}"
+
+ - name: Change label {{ gitlab_second_label }} name to {{ gitlab_second_label_new_name }}
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ labels:
+ - name: "{{ gitlab_second_label }}"
+ new_name: "{{ gitlab_second_label_new_name }}"
+ state: present
+ register: gitlab_first_label_new_name
+
+ - name: Test Label name changed
+ assert:
+ that:
+ - gitlab_first_label_new_name.labels.added|length == 0
+ - gitlab_first_label_new_name.labels.untouched|length == 0
+ - gitlab_first_label_new_name.labels.removed|length == 0
+ - gitlab_first_label_new_name.labels.updated|length == 1
+ - gitlab_first_label_new_name.labels.updated[0] == "{{ gitlab_second_label }}"
+
+ - name: Change label name back to {{ gitlab_second_label }}
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ labels:
+ - name: "{{ gitlab_second_label_new_name }}"
+ new_name: "{{ gitlab_second_label }}"
+ state: present
+ register: gitlab_first_label_orig_name
+
+ - name: Test Label name changed back
+ assert:
+ that:
+ - gitlab_first_label_orig_name.labels.added|length == 0
+ - gitlab_first_label_orig_name.labels.untouched|length == 0
+ - gitlab_first_label_orig_name.labels.removed|length == 0
+ - gitlab_first_label_orig_name.labels.updated|length == 1
+ - gitlab_first_label_orig_name.labels.updated[0] == "{{ gitlab_second_label_new_name }}"
+
+ - name: Update Label Test ( Additions )
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ labels:
+ - name: "{{ gitlab_second_label }}"
+ description: "{{ gitlab_first_label_description }}"
+ priority: "{{ gitlab_first_label_priority }}"
+ state: present
+ register: gitlab_first_label_update_additions
+
+ - name: Test Label Updated ( Additions )
+ assert:
+ that:
+ - gitlab_first_label_update_additions.labels.added|length == 0
+ - gitlab_first_label_update_additions.labels.untouched|length == 0
+ - gitlab_first_label_update_additions.labels.removed|length == 0
+ - gitlab_first_label_update_additions.labels.updated|length == 1
+ - gitlab_first_label_update_additions.labels.updated[0] == "{{ gitlab_second_label }}"
+
+ - name: Delete Label {{ gitlab_second_label }}
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ labels:
+ - name: "{{ gitlab_second_label }}"
+ state: absent
+ register: gitlab_first_label_delete
+
+ - name: Test label is deleted
+ assert:
+ that:
+ - gitlab_first_label_delete is changed
+ - gitlab_first_label_delete.labels.added|length == 0
+ - gitlab_first_label_delete.labels.untouched|length == 0
+ - gitlab_first_label_delete.labels.removed|length == 1
+ - gitlab_first_label_delete.labels.updated|length == 0
+ - gitlab_first_label_delete.labels.removed[0] == "{{ gitlab_second_label }}"
+
+ - name: Create label {{ gitlab_second_label }} again purging the other
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ purge: true
+ labels:
+ - name: "{{ gitlab_second_label }}"
+ color: "{{ gitlab_second_label_color }}"
+ state: present
+ register: gitlab_first_label_create_purging
+
+ - name: Test Label Created again
+ assert:
+ that:
+ - gitlab_first_label_create_purging is changed
+ - gitlab_first_label_create_purging.labels.added|length == 1
+ - gitlab_first_label_create_purging.labels.untouched|length == 0
+ - gitlab_first_label_create_purging.labels.removed|length == 1
+ - gitlab_first_label_create_purging.labels.updated|length == 0
+ - gitlab_first_label_create_purging.labels.added[0] == "{{ gitlab_second_label }}"
+ - gitlab_first_label_create_purging.labels.removed[0] == "{{ gitlab_first_label }}"
+
+ always:
+ - name: Delete Labels
+ gitlab_label:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ purge: true
+ labels:
+ - name: "{{ gitlab_first_label }}"
+ - name: "{{ gitlab_second_label }}"
+ state: absent
+ register: gitlab_first_label_always_delete
+
+ - name: Test label are deleted
+ assert:
+ that:
+ - gitlab_first_label_always_delete is changed
+ - gitlab_first_label_always_delete.labels.added|length == 0
+ - gitlab_first_label_always_delete.labels.untouched|length == 0
+ - gitlab_first_label_always_delete.labels.removed|length > 0
+ - gitlab_first_label_always_delete.labels.updated|length == 0
+
+ - name: Clean up {{ gitlab_project_name }}
+ gitlab_project:
+ api_url: "{{ gitlab_host }}"
+ validate_certs: false
+ api_token: "{{ gitlab_api_token }}"
+ name: "{{ gitlab_project_name }}"
+ group: "{{ gitlab_project_group }}"
+ state: absent
+
+ - name: Clean up {{ gitlab_project_group }}
+ gitlab_group:
+ api_url: "{{ gitlab_host }}"
+ validate_certs: true
+ api_token: "{{ gitlab_api_token }}"
+ name: "{{ gitlab_project_group }}"
+ state: absent \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_merge_request/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_merge_request/aliases
new file mode 100644
index 000000000..4f4e3dcc9
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_merge_request/aliases
@@ -0,0 +1,6 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+gitlab/ci
+disabled
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_merge_request/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_merge_request/defaults/main.yml
new file mode 100644
index 000000000..eb27b0b68
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_merge_request/defaults/main.yml
@@ -0,0 +1,14 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+gitlab_source_branch: ansible_test_source_branch
+gitlab_target_branch: ansible_test_target_project
+gitlab_project_name: ansible_test_project
+gitlab_project_group: ansible_test_group
+gitlab_host: ansible_test_host
+gitlab_api_token: ansible_test_api_token
+gitlab_labels: ansible_test_label
+gitlab_assignee_ids: ansible_test_assignee_ids
+gitlab_description_path: ansible_test_description_path \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_merge_request/files/description.md b/ansible_collections/community/general/tests/integration/targets/gitlab_merge_request/files/description.md
new file mode 100644
index 000000000..3f662eff8
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_merge_request/files/description.md
@@ -0,0 +1,9 @@
+<!--
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
+-->
+
+### Description
+
+Merge Request test description \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_merge_request/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_merge_request/tasks/main.yml
new file mode 100644
index 000000000..18da900a2
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_merge_request/tasks/main.yml
@@ -0,0 +1,129 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Install required libs
+ pip:
+ name: python-gitlab
+ state: present
+
+- block:
+ - name: Create {{ gitlab_project_name }}
+ gitlab_project:
+ api_url: "{{ gitlab_host }}"
+ validate_certs: true
+ api_token: "{{ gitlab_api_token }}"
+ name: "{{ gitlab_project_name }}"
+ group: "{{ gitlab_project_group }}"
+ default_branch: "{{ gitlab_target_branch }}"
+ initialize_with_readme: true
+ state: present
+
+ - name: Create branch {{ gitlab_source_branch }}
+ gitlab_branch:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_api_token }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ branch: "{{ gitlab_source_branch }}"
+ ref_branch: "{{ gitlab_target_branch }}"
+ state: present
+
+ - name: Create Merge Request
+ gitlab_merge_request:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_api_token }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ source_branch: "{{gitlab_source_branch}}"
+ target_branch: "{{gitlab_target_branch}}"
+ title: "Ansible test merge request"
+ description: "Test description"
+ labels: ""
+ state_filter: "opened"
+ assignee_ids: ""
+ reviewer_ids: ""
+ remove_source_branch: True
+ state: present
+ register: gitlab_merge_request_create
+
+ - name: Test Merge Request Created
+ assert:
+ that:
+ - gitlab_merge_request_create is changed
+
+ - name: Create Merge Request ( Idempotency test )
+ gitlab_merge_request:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_api_token }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ source_branch: "{{gitlab_source_branch}}"
+ target_branch: "{{gitlab_target_branch}}"
+ title: "Ansible test merge request"
+ description: "Test description"
+ labels: ""
+ state_filter: "opened"
+ assignee_ids: ""
+ reviewer_ids: ""
+ remove_source_branch: True
+ state: present
+ register: gitlab_merge_request_create_idempotence
+
+ - name: Test module is idempotent
+ assert:
+ that:
+ - gitlab_merge_request_create_idempotence is not changed
+
+ - name: Update Merge Request Test
+ gitlab_merge_request:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_api_token }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ source_branch: "{{gitlab_source_branch}}"
+ target_branch: "{{gitlab_target_branch}}"
+ title: "Ansible test merge request"
+ description_path: "{{gitlab_description_path}}"
+ labels: "{{ gitlab_labels }}"
+ state_filter: "opened"
+ assignee_ids: "{{ gitlab_assignee_ids }}"
+ reviewer_ids: ""
+ remove_source_branch: True
+ state: present
+ register: gitlab_merge_request_udpate
+
+ - name: Test merge request updated
+ assert:
+ that:
+ - gitlab_merge_request_udpate.mr.labels[0] == "{{ gitlab_labels }}"
+ - gitlab_merge_request_udpate.mr.assignees[0].username == "{{ gitlab_assignee_ids }}"
+ - "'### Description\n\nMerge Request test description' in gitlab_merge_request_udpate.mr.description"
+
+ - name: Delete Merge Request
+ gitlab_merge_request:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_api_token }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ source_branch: "{{gitlab_source_branch}}"
+ target_branch: "{{gitlab_target_branch}}"
+ title: "Ansible test merge request"
+ state: absent
+ register: gitlab_merge_request_delete
+
+ - name: Test merge request is deleted
+ assert:
+ that:
+ - gitlab_merge_request_delete is changed
+
+ always:
+ - name: Clean up {{ gitlab_project_name }}
+ gitlab_project:
+ api_url: "{{ gitlab_host }}"
+ validate_certs: false
+ api_token: "{{ gitlab_api_token }}"
+ name: "{{ gitlab_project_name }}"
+ group: "{{ gitlab_project_group }}"
+ state: absent
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_milestone/README.md b/ansible_collections/community/general/tests/integration/targets/gitlab_milestone/README.md
new file mode 100644
index 000000000..95bb3410b
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_milestone/README.md
@@ -0,0 +1,19 @@
+<!--
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
+-->
+
+# Gitlab integration tests
+
+1. to run integration tests locally, I've setup a podman pod with both gitlab-ee image and the testing image
+2. gitlab's related information were taken from [here](https://docs.gitlab.com/ee/install/docker.html), about the variable it needs (hostname, ports, volumes); volumes were pre-made before launching the image
+3. image that run integration tests is started with `podman run --rm -it --pod <pod_name> --name <image_name> --network=host --volume <path_to_git_repo>/ansible_community/community.general:<container_path_to>/workspace/ansible_collections/community/general quay.io/ansible/azure-pipelines-test-container:4.0.1`
+4. into the testing image, run
+```sh
+pip install https://github.com/ansible/ansible/archive/devel.tar.gz --disable-pip-version-check
+cd <container_path_to>/workspace/ansible_collections/community/general
+ansible-test integration gitlab_milestone -vvv
+```
+
+While debugging with `q` package, open a second terminal and run `podman exec -it <image_name> /bin/bash` and inside it do `tail -f /tmp/q` .
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_milestone/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_milestone/aliases
new file mode 100644
index 000000000..1d485e509
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_milestone/aliases
@@ -0,0 +1,6 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+gitlab/ci
+disabled \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_milestone/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_milestone/defaults/main.yml
new file mode 100644
index 000000000..d11001295
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_milestone/defaults/main.yml
@@ -0,0 +1,18 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+gitlab_project_name: ansible_test_project
+gitlab_host: ansible_test_host
+gitlab_api_token: ansible_test_api_token
+gitlab_project_group: ansible_test_group
+gitlab_branch: ansible_test_branch
+gitlab_first_milestone: ansible_test_milestone
+gitlab_first_milestone_description: "milestone description"
+gitlab_first_milestone_start_date: "2024-01-01"
+gitlab_first_milestone_due_date: "2024-12-31"
+gitlab_first_milestone_new_start_date: "2024-05-01"
+gitlab_first_milestone_new_due_date: "2024-10-31"
+gitlab_second_milestone: ansible_test_second_milestone
+gitlab_second_milestone_description: "new milestone"
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_milestone/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_milestone/tasks/main.yml
new file mode 100644
index 000000000..ce78c2eef
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_milestone/tasks/main.yml
@@ -0,0 +1,388 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Install required libs
+ pip:
+ name: python-gitlab
+ state: present
+
+- block:
+###
+### Group milestone
+###
+ - name: Create {{ gitlab_project_group }}
+ gitlab_group:
+ api_url: "{{ gitlab_host }}"
+ validate_certs: true
+ api_token: "{{ gitlab_api_token }}"
+ name: "{{ gitlab_project_group }}"
+ state: present
+
+ - name: Purge all group milestones for check_mode test
+ gitlab_milestone:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_api_token }}"
+ group: "{{ gitlab_project_group }}"
+ purge: true
+
+ - name: Group milestone - Add a milestone in check_mode
+ gitlab_milestone:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ group: "{{ gitlab_project_group }}"
+ milestones:
+ - title: "{{ gitlab_second_milestone }}"
+ check_mode: true
+ register: gitlab_group_milestone_state
+
+ - name: Group milestone - Check_mode state must be changed
+ assert:
+ that:
+ - gitlab_group_milestone_state is changed
+
+ - name: Purge all group milestones for project milestone test - cannot exist with same name
+ gitlab_milestone:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_api_token }}"
+ group: "{{ gitlab_project_group }}"
+ purge: true
+ register: gitlab_group_milestone_purged
+
+ - name: Group milestone - Create milestone {{ gitlab_first_milestone }} and {{ gitlab_second_milestone }}
+ gitlab_milestone:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ group: "{{ gitlab_project_group }}"
+ milestones:
+ - title: "{{ gitlab_first_milestone }}"
+ start_date: "{{ gitlab_first_milestone_start_date }}"
+ due_date: "{{ gitlab_first_milestone_due_date }}"
+ - title: "{{ gitlab_second_milestone }}"
+ state: present
+ register: gitlab_group_milestone_create
+
+ - name: Group milestone - Test milestone Created
+ assert:
+ that:
+ - gitlab_group_milestone_create is changed
+ - gitlab_group_milestone_create.milestones.added|length == 2
+ - gitlab_group_milestone_create.milestones.untouched|length == 0
+ - gitlab_group_milestone_create.milestones.removed|length == 0
+ - gitlab_group_milestone_create.milestones.updated|length == 0
+ - gitlab_group_milestone_create.milestones.added[0] == "{{ gitlab_first_milestone }}"
+ - gitlab_group_milestone_create.milestones.added[1] == "{{ gitlab_second_milestone }}"
+
+ - name: Group milestone - Create milestone ( Idempotency test )
+ gitlab_milestone:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ group: "{{ gitlab_project_group }}"
+ milestones:
+ - title: "{{ gitlab_first_milestone }}"
+ start_date: "{{ gitlab_first_milestone_start_date }}"
+ due_date: "{{ gitlab_first_milestone_due_date }}"
+ state: present
+ register: gitlab_group_milestone_create_idempotence
+
+ - name: Group milestone - Test Create milestone is Idempotent
+ assert:
+ that:
+ - gitlab_group_milestone_create_idempotence is not changed
+
+ - name: Group milestone - Update milestone {{ gitlab_first_milestone }} changing dates
+ gitlab_milestone:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ group: "{{ gitlab_project_group }}"
+ milestones:
+ - title: "{{ gitlab_first_milestone }}"
+ start_date: "{{ gitlab_first_milestone_new_start_date }}"
+ due_date: "{{ gitlab_first_milestone_new_due_date }}"
+ state: present
+ register: gitlab_group_milestone_update
+
+ - name: Group milestone - Test milestone Updated
+ assert:
+ that:
+ - gitlab_group_milestone_update.milestones.added|length == 0
+ - gitlab_group_milestone_update.milestones.untouched|length == 0
+ - gitlab_group_milestone_update.milestones.removed|length == 0
+ - gitlab_group_milestone_update.milestones.updated|length == 1
+ - gitlab_group_milestone_update.milestones.updated[0] == "{{ gitlab_first_milestone }}"
+
+ - name: Group milestone - Update milestone Test ( Additions )
+ gitlab_milestone:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ group: "{{ gitlab_project_group }}"
+ milestones:
+ - title: "{{ gitlab_second_milestone }}"
+ description: "{{ gitlab_first_milestone_description }}"
+ state: present
+ register: gitlab_group_milestone_update_additions
+
+ - name: Group milestone - Test milestone Updated ( Additions )
+ assert:
+ that:
+ - gitlab_group_milestone_update_additions.milestones.added|length == 0
+ - gitlab_group_milestone_update_additions.milestones.untouched|length == 0
+ - gitlab_group_milestone_update_additions.milestones.removed|length == 0
+ - gitlab_group_milestone_update_additions.milestones.updated|length == 1
+ - gitlab_group_milestone_update_additions.milestones.updated[0] == "{{ gitlab_second_milestone }}"
+
+ - name: Group milestone - Delete milestone {{ gitlab_second_milestone }}
+ gitlab_milestone:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ group: "{{ gitlab_project_group }}"
+ milestones:
+ - title: "{{ gitlab_second_milestone }}"
+ state: absent
+ register: gitlab_group_milestone_delete
+
+ - name: Group milestone - Test milestone is deleted
+ assert:
+ that:
+ - gitlab_group_milestone_delete is changed
+ - gitlab_group_milestone_delete.milestones.added|length == 0
+ - gitlab_group_milestone_delete.milestones.untouched|length == 0
+ - gitlab_group_milestone_delete.milestones.removed|length == 1
+ - gitlab_group_milestone_delete.milestones.updated|length == 0
+ - gitlab_group_milestone_delete.milestones.removed[0] == "{{ gitlab_second_milestone }}"
+
+ - name: Group milestone - Create group milestone {{ gitlab_second_milestone }} again purging the other
+ gitlab_milestone:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ group: "{{ gitlab_project_group }}"
+ purge: true
+ milestones:
+ - title: "{{ gitlab_second_milestone }}"
+ state: present
+ register: gitlab_group_milestone_create_purging
+
+ - name: Group milestone - Test milestone Created again
+ assert:
+ that:
+ - gitlab_group_milestone_create_purging is changed
+ - gitlab_group_milestone_create_purging.milestones.added|length == 1
+ - gitlab_group_milestone_create_purging.milestones.untouched|length == 0
+ - gitlab_group_milestone_create_purging.milestones.removed|length == 1
+ - gitlab_group_milestone_create_purging.milestones.updated|length == 0
+ - gitlab_group_milestone_create_purging.milestones.added[0] == "{{ gitlab_second_milestone }}"
+ - gitlab_group_milestone_create_purging.milestones.removed[0] == "{{ gitlab_first_milestone }}"
+
+###
+### Project milestone
+###
+ - name: Purge all group milestones for project milestone test - cannot exist with same name
+ gitlab_milestone:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_api_token }}"
+ group: "{{ gitlab_project_group }}"
+ purge: true
+ register: gitlab_group_milestone_purged
+
+ - name: Create {{ gitlab_project_name }}
+ gitlab_project:
+ api_url: "{{ gitlab_host }}"
+ validate_certs: true
+ api_token: "{{ gitlab_api_token }}"
+ name: "{{ gitlab_project_name }}"
+ group: "{{ gitlab_project_group }}"
+ default_branch: "{{ gitlab_branch }}"
+ initialize_with_readme: true
+ state: present
+
+ - name: Purge all milestones for check_mode test
+ gitlab_milestone:
+ api_url: "{{ gitlab_host }}"
+ api_token: "{{ gitlab_api_token }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ purge: true
+
+ - name: Add a milestone in check_mode
+ gitlab_milestone:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ milestones:
+ - title: "{{ gitlab_second_milestone }}"
+ description: "{{ gitlab_second_milestone_description }}"
+ check_mode: true
+ register: gitlab_first_milestone_state
+
+ - name: Check_mode state must be changed
+ assert:
+ that:
+ - gitlab_first_milestone_state is changed
+
+ - name: Create milestone {{ gitlab_first_milestone }} and {{ gitlab_second_milestone }}
+ gitlab_milestone:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ milestones:
+ - title: "{{ gitlab_first_milestone }}"
+ start_date: "{{ gitlab_first_milestone_start_date }}"
+ due_date: "{{ gitlab_first_milestone_due_date }}"
+ - title: "{{ gitlab_second_milestone }}"
+ state: present
+ register: gitlab_milestones_create
+
+ - name: Test milestone Created
+ assert:
+ that:
+ - gitlab_milestones_create is changed
+ - gitlab_milestones_create.milestones.added|length == 2
+ - gitlab_milestones_create.milestones.untouched|length == 0
+ - gitlab_milestones_create.milestones.removed|length == 0
+ - gitlab_milestones_create.milestones.updated|length == 0
+ - gitlab_milestones_create.milestones.added[0] == "{{ gitlab_first_milestone }}"
+ - gitlab_milestones_create.milestones.added[1] == "{{ gitlab_second_milestone }}"
+
+ - name: Create milestone ( Idempotency test )
+ gitlab_milestone:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ milestones:
+ - title: "{{ gitlab_first_milestone }}"
+ start_date: "{{ gitlab_first_milestone_start_date }}"
+ due_date: "{{ gitlab_first_milestone_due_date }}"
+ state: present
+ register: gitlab_first_milestone_create_idempotence
+
+ - name: Test Create milestone is Idempotent
+ assert:
+ that:
+ - gitlab_first_milestone_create_idempotence is not changed
+
+ - name: Update milestone {{ gitlab_first_milestone }} changing dates
+ gitlab_milestone:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ milestones:
+ - title: "{{ gitlab_first_milestone }}"
+ start_date: "{{ gitlab_first_milestone_new_start_date }}"
+ due_date: "{{ gitlab_first_milestone_new_due_date }}"
+ state: present
+ register: gitlab_first_milestone_update
+
+ - name: Test milestone Updated
+ assert:
+ that:
+ - gitlab_first_milestone_update.milestones.added|length == 0
+ - gitlab_first_milestone_update.milestones.untouched|length == 0
+ - gitlab_first_milestone_update.milestones.removed|length == 0
+ - gitlab_first_milestone_update.milestones.updated|length == 1
+ - gitlab_first_milestone_update.milestones.updated[0] == "{{ gitlab_first_milestone }}"
+
+ - name: Update milestone Test ( Additions )
+ gitlab_milestone:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ milestones:
+ - title: "{{ gitlab_second_milestone }}"
+ description: "{{ gitlab_second_milestone_description }}"
+ state: present
+ register: gitlab_first_milestone_update_additions
+
+ - name: Test milestone Updated ( Additions )
+ assert:
+ that:
+ - gitlab_first_milestone_update_additions.milestones.added|length == 0
+ - gitlab_first_milestone_update_additions.milestones.untouched|length == 0
+ - gitlab_first_milestone_update_additions.milestones.removed|length == 0
+ - gitlab_first_milestone_update_additions.milestones.updated|length == 1
+ - gitlab_first_milestone_update_additions.milestones.updated[0] == "{{ gitlab_second_milestone }}"
+
+ - name: Delete milestone {{ gitlab_second_milestone }}
+ gitlab_milestone:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ milestones:
+ - title: "{{ gitlab_second_milestone }}"
+ state: absent
+ register: gitlab_first_milestone_delete
+
+ - name: Test milestone is deleted
+ assert:
+ that:
+ - gitlab_first_milestone_delete is changed
+ - gitlab_first_milestone_delete.milestones.added|length == 0
+ - gitlab_first_milestone_delete.milestones.untouched|length == 0
+ - gitlab_first_milestone_delete.milestones.removed|length == 1
+ - gitlab_first_milestone_delete.milestones.updated|length == 0
+ - gitlab_first_milestone_delete.milestones.removed[0] == "{{ gitlab_second_milestone }}"
+
+ - name: Create milestone {{ gitlab_second_milestone }} again purging the other
+ gitlab_milestone:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ purge: true
+ milestones:
+ - title: "{{ gitlab_second_milestone }}"
+ state: present
+ register: gitlab_first_milestone_create_purging
+
+ - name: Test milestone Created again
+ assert:
+ that:
+ - gitlab_first_milestone_create_purging is changed
+ - gitlab_first_milestone_create_purging.milestones.added|length == 1
+ - gitlab_first_milestone_create_purging.milestones.untouched|length == 0
+ - gitlab_first_milestone_create_purging.milestones.removed|length == 1
+ - gitlab_first_milestone_create_purging.milestones.updated|length == 0
+ - gitlab_first_milestone_create_purging.milestones.added[0] == "{{ gitlab_second_milestone }}"
+ - gitlab_first_milestone_create_purging.milestones.removed[0] == "{{ gitlab_first_milestone }}"
+
+ always:
+ - name: Delete milestones
+ gitlab_milestone:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_host }}"
+ project: "{{ gitlab_project_group }}/{{ gitlab_project_name }}"
+ purge: true
+ milestones:
+ - title: "{{ gitlab_first_milestone }}"
+ - title: "{{ gitlab_second_milestone }}"
+ state: absent
+ register: gitlab_first_milestone_always_delete
+
+ - name: Test milestone are deleted
+ assert:
+ that:
+ - gitlab_first_milestone_always_delete is changed
+ - gitlab_first_milestone_always_delete.milestones.added|length == 0
+ - gitlab_first_milestone_always_delete.milestones.untouched|length == 0
+ - gitlab_first_milestone_always_delete.milestones.removed|length > 0
+ - gitlab_first_milestone_always_delete.milestones.updated|length == 0
+
+ - name: Clean up {{ gitlab_project_name }}
+ gitlab_project:
+ api_url: "{{ gitlab_host }}"
+ validate_certs: false
+ api_token: "{{ gitlab_api_token }}"
+ name: "{{ gitlab_project_name }}"
+ group: "{{ gitlab_project_group }}"
+ state: absent
+
+ - name: Clean up {{ gitlab_project_group }}
+ gitlab_group:
+ api_url: "{{ gitlab_host }}"
+ validate_certs: true
+ api_token: "{{ gitlab_api_token }}"
+ name: "{{ gitlab_project_group }}"
+ state: absent \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_project_access_token/aliases b/ansible_collections/community/general/tests/integration/targets/gitlab_project_access_token/aliases
new file mode 100644
index 000000000..fc0e157c9
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_project_access_token/aliases
@@ -0,0 +1,7 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+azp/posix/1
+gitlab/ci
+disabled
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_project_access_token/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_project_access_token/defaults/main.yml
new file mode 100644
index 000000000..579584d62
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_project_access_token/defaults/main.yml
@@ -0,0 +1,15 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) 2024, Zoran Krleza <zoran.krleza@true-north.hr>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+gitlab_api_token:
+gitlab_api_url:
+gitlab_validate_certs: false
+gitlab_project_name:
+gitlab_token_name:
diff --git a/ansible_collections/community/general/tests/integration/targets/gitlab_project_access_token/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/gitlab_project_access_token/tasks/main.yml
new file mode 100644
index 000000000..c3125d740
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/gitlab_project_access_token/tasks/main.yml
@@ -0,0 +1,221 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) 2024, Zoran Krleza <zoran.krleza@true-north.hr>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Install required libs
+ pip:
+ name: python-gitlab
+ state: present
+
+- block:
+ - name: Try to create access token in nonexisting project
+ community.general.gitlab_project_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ project: "some_nonexisting_project"
+ name: "{{ gitlab_token_name }}"
+ state: present
+ expires_at: '2025-01-01'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ register: create_pfail_token_status
+ always:
+ - name: Assert that token creation in nonexisting project failed
+ assert:
+ that:
+ - create_pfail_token_status is failed
+ ignore_errors: true
+
+- block:
+ - name: Try to create access token with nonvalid expires_at
+ community.general.gitlab_project_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ project: "some_nonexisting_project"
+ name: "{{ gitlab_token_name }}"
+ state: present
+ expires_at: '2025-13-01'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ register: create_efail_token_status
+ always:
+ - name: Assert that token creation with invalid expires_at failed
+ assert:
+ that:
+ - create_efail_token_status is failed
+ ignore_errors: true
+
+- name: Create access token
+ community.general.gitlab_project_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ project: "{{ gitlab_project_name }}"
+ name: "{{ gitlab_token_name }}"
+ state: present
+ expires_at: '2024-12-31'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ register: create_token_status
+- name: Assert that token creation with valid arguments is successfull
+ assert:
+ that:
+ - create_token_status is changed
+ - create_token_status.access_token.token is defined
+
+- name: Check existing access token recreate=never (default)
+ community.general.gitlab_project_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ project: "{{ gitlab_project_name }}"
+ name: "{{ gitlab_token_name }}"
+ state: present
+ expires_at: '2024-12-31'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ register: check_token_status
+- name: Assert that token creation without changes and recreate=never succeeds with status not changed
+ assert:
+ that:
+ - check_token_status is not changed
+ - check_token_status.access_token.token is not defined
+
+- name: Check existing access token with recreate=state_change
+ community.general.gitlab_project_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ project: "{{ gitlab_project_name }}"
+ name: "{{ gitlab_token_name }}"
+ state: present
+ expires_at: '2024-12-31'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ recreate: state_change
+ register: check_recreate_token_status
+- name: Assert that token creation without changes and recreate=state_change succeeds with status not changed
+ assert:
+ that:
+ - check_recreate_token_status is not changed
+ - check_recreate_token_status.access_token.token is not defined
+
+- block:
+ - name: Try to change existing access token with recreate=never
+ community.general.gitlab_project_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ project: "{{ gitlab_project_name }}"
+ name: "{{ gitlab_token_name }}"
+ state: present
+ expires_at: '2025-01-01'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ register: change_token_status
+ always:
+ - name: Assert that token change with recreate=never fails
+ assert:
+ that:
+ - change_token_status is failed
+ ignore_errors: true
+
+- name: Try to change existing access token with recreate=state_change
+ community.general.gitlab_project_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ project: "{{ gitlab_project_name }}"
+ name: "{{ gitlab_token_name }}"
+ state: present
+ expires_at: '2025-01-01'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ recreate: state_change
+ register: change_recreate_token_status
+- name: Assert that token change with recreate=state_change succeeds
+ assert:
+ that:
+ - change_recreate_token_status is changed
+ - change_recreate_token_status.access_token.token is defined
+
+- name: Try to change existing access token with recreate=always
+ community.general.gitlab_project_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ project: "{{ gitlab_project_name }}"
+ name: "{{ gitlab_token_name }}"
+ state: present
+ expires_at: '2025-01-01'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ recreate: always
+ register: change_recreate1_token_status
+- name: Assert that token change with recreate=always succeeds
+ assert:
+ that:
+ - change_recreate1_token_status is changed
+ - change_recreate1_token_status.access_token.token is defined
+
+- name: Revoke access token
+ community.general.gitlab_project_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ project: "{{ gitlab_project_name }}"
+ name: "{{ gitlab_token_name }}"
+ state: absent
+ expires_at: '2024-12-31'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ register: revoke_token_status
+- name: Assert that token revocation succeeds
+ assert:
+ that:
+ - revoke_token_status is changed
+
+- name: Revoke nonexisting access token
+ community.general.gitlab_project_access_token:
+ api_token: "{{ gitlab_api_token }}"
+ api_url: "{{ gitlab_api_url }}"
+ validate_certs: "{{ gitlab_validate_certs }}"
+ project: "{{ gitlab_project_name }}"
+ name: "{{ gitlab_token_name }}"
+ state: absent
+ expires_at: '2024-12-31'
+ access_level: developer
+ scopes:
+ - api
+ - read_api
+ register: revoke_token_status
+- name: Assert that token revocation succeeds with status not changed
+ assert:
+ that:
+ - revoke_token_status is not changed \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/homebrew/aliases b/ansible_collections/community/general/tests/integration/targets/homebrew/aliases
index 11bb9a086..bd478505d 100644
--- a/ansible_collections/community/general/tests/integration/targets/homebrew/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/homebrew/aliases
@@ -7,4 +7,3 @@ skip/aix
skip/freebsd
skip/rhel
skip/docker
-skip/python2.6
diff --git a/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/casks.yml b/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/casks.yml
new file mode 100644
index 000000000..42d3515bf
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/casks.yml
@@ -0,0 +1,99 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Test code for the homebrew module.
+# Copyright (c) 2020, Abhijeet Kasurde <akasurde@redhat.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Find brew binary
+ command: which brew
+ register: brew_which
+ when: ansible_distribution in ['MacOSX']
+
+- name: Get owner of brew binary
+ stat:
+ path: "{{ brew_which.stdout }}"
+ register: brew_stat
+ when: ansible_distribution in ['MacOSX']
+
+#- name: Use ignored-pinned option while upgrading all
+# homebrew:
+# upgrade_all: true
+# upgrade_options: ignore-pinned
+# become: true
+# become_user: "{{ brew_stat.stat.pw_name }}"
+# register: upgrade_option_result
+# environment:
+# HOMEBREW_NO_AUTO_UPDATE: True
+
+#- assert:
+# that:
+# - upgrade_option_result.changed
+
+- vars:
+ package_name: kitty
+
+ block:
+ - name: Make sure {{ package_name }} package is not installed
+ homebrew:
+ name: "{{ package_name }}"
+ state: absent
+ update_homebrew: false
+ become: true
+ become_user: "{{ brew_stat.stat.pw_name }}"
+
+ - name: Install {{ package_name }} package using homebrew
+ homebrew:
+ name: "{{ package_name }}"
+ state: present
+ update_homebrew: false
+ become: true
+ become_user: "{{ brew_stat.stat.pw_name }}"
+ register: package_result
+
+ - assert:
+ that:
+ - package_result.changed
+
+ - name: Again install {{ package_name }} package using homebrew
+ homebrew:
+ name: "{{ package_name }}"
+ state: present
+ update_homebrew: false
+ become: true
+ become_user: "{{ brew_stat.stat.pw_name }}"
+ register: package_result
+
+ - assert:
+ that:
+ - not package_result.changed
+
+ - name: Uninstall {{ package_name }} package using homebrew
+ homebrew:
+ name: "{{ package_name }}"
+ state: absent
+ update_homebrew: false
+ become: true
+ become_user: "{{ brew_stat.stat.pw_name }}"
+ register: package_result
+
+ - assert:
+ that:
+ - package_result.changed
+
+ - name: Again uninstall {{ package_name }} package using homebrew
+ homebrew:
+ name: "{{ package_name }}"
+ state: absent
+ update_homebrew: false
+ become: true
+ become_user: "{{ brew_stat.stat.pw_name }}"
+ register: package_result
+
+ - assert:
+ that:
+ - not package_result.changed
diff --git a/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/formulae.yml b/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/formulae.yml
new file mode 100644
index 000000000..1db3ef1a6
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/formulae.yml
@@ -0,0 +1,99 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Test code for the homebrew module.
+# Copyright (c) 2020, Abhijeet Kasurde <akasurde@redhat.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Find brew binary
+ command: which brew
+ register: brew_which
+ when: ansible_distribution in ['MacOSX']
+
+- name: Get owner of brew binary
+ stat:
+ path: "{{ brew_which.stdout }}"
+ register: brew_stat
+ when: ansible_distribution in ['MacOSX']
+
+#- name: Use ignored-pinned option while upgrading all
+# homebrew:
+# upgrade_all: true
+# upgrade_options: ignore-pinned
+# become: true
+# become_user: "{{ brew_stat.stat.pw_name }}"
+# register: upgrade_option_result
+# environment:
+# HOMEBREW_NO_AUTO_UPDATE: True
+
+#- assert:
+# that:
+# - upgrade_option_result.changed
+
+- vars:
+ package_name: gnu-tar
+
+ block:
+ - name: Make sure {{ package_name }} package is not installed
+ homebrew:
+ name: "{{ package_name }}"
+ state: absent
+ update_homebrew: false
+ become: true
+ become_user: "{{ brew_stat.stat.pw_name }}"
+
+ - name: Install {{ package_name }} package using homebrew
+ homebrew:
+ name: "{{ package_name }}"
+ state: present
+ update_homebrew: false
+ become: true
+ become_user: "{{ brew_stat.stat.pw_name }}"
+ register: package_result
+
+ - assert:
+ that:
+ - package_result.changed
+
+ - name: Again install {{ package_name }} package using homebrew
+ homebrew:
+ name: "{{ package_name }}"
+ state: present
+ update_homebrew: false
+ become: true
+ become_user: "{{ brew_stat.stat.pw_name }}"
+ register: package_result
+
+ - assert:
+ that:
+ - not package_result.changed
+
+ - name: Uninstall {{ package_name }} package using homebrew
+ homebrew:
+ name: "{{ package_name }}"
+ state: absent
+ update_homebrew: false
+ become: true
+ become_user: "{{ brew_stat.stat.pw_name }}"
+ register: package_result
+
+ - assert:
+ that:
+ - package_result.changed
+
+ - name: Again uninstall {{ package_name }} package using homebrew
+ homebrew:
+ name: "{{ package_name }}"
+ state: absent
+ update_homebrew: false
+ become: true
+ become_user: "{{ brew_stat.stat.pw_name }}"
+ register: package_result
+
+ - assert:
+ that:
+ - not package_result.changed
diff --git a/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/main.yml
index 1db3ef1a6..f5479917e 100644
--- a/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/homebrew/tasks/main.yml
@@ -9,91 +9,9 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-- name: Find brew binary
- command: which brew
- register: brew_which
- when: ansible_distribution in ['MacOSX']
-
-- name: Get owner of brew binary
- stat:
- path: "{{ brew_which.stdout }}"
- register: brew_stat
- when: ansible_distribution in ['MacOSX']
-
-#- name: Use ignored-pinned option while upgrading all
-# homebrew:
-# upgrade_all: true
-# upgrade_options: ignore-pinned
-# become: true
-# become_user: "{{ brew_stat.stat.pw_name }}"
-# register: upgrade_option_result
-# environment:
-# HOMEBREW_NO_AUTO_UPDATE: True
-
-#- assert:
-# that:
-# - upgrade_option_result.changed
-
-- vars:
- package_name: gnu-tar
-
+- block:
+ - include_tasks: 'formulae.yml'
+
+- when: ansible_distribution in ['MacOSX']
block:
- - name: Make sure {{ package_name }} package is not installed
- homebrew:
- name: "{{ package_name }}"
- state: absent
- update_homebrew: false
- become: true
- become_user: "{{ brew_stat.stat.pw_name }}"
-
- - name: Install {{ package_name }} package using homebrew
- homebrew:
- name: "{{ package_name }}"
- state: present
- update_homebrew: false
- become: true
- become_user: "{{ brew_stat.stat.pw_name }}"
- register: package_result
-
- - assert:
- that:
- - package_result.changed
-
- - name: Again install {{ package_name }} package using homebrew
- homebrew:
- name: "{{ package_name }}"
- state: present
- update_homebrew: false
- become: true
- become_user: "{{ brew_stat.stat.pw_name }}"
- register: package_result
-
- - assert:
- that:
- - not package_result.changed
-
- - name: Uninstall {{ package_name }} package using homebrew
- homebrew:
- name: "{{ package_name }}"
- state: absent
- update_homebrew: false
- become: true
- become_user: "{{ brew_stat.stat.pw_name }}"
- register: package_result
-
- - assert:
- that:
- - package_result.changed
-
- - name: Again uninstall {{ package_name }} package using homebrew
- homebrew:
- name: "{{ package_name }}"
- state: absent
- update_homebrew: false
- become: true
- become_user: "{{ brew_stat.stat.pw_name }}"
- register: package_result
-
- - assert:
- that:
- - not package_result.changed
+ - include_tasks: 'casks.yml'
diff --git a/ansible_collections/community/general/tests/integration/targets/homebrew_cask/aliases b/ansible_collections/community/general/tests/integration/targets/homebrew_cask/aliases
index 11bb9a086..bd478505d 100644
--- a/ansible_collections/community/general/tests/integration/targets/homebrew_cask/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/homebrew_cask/aliases
@@ -7,4 +7,3 @@ skip/aix
skip/freebsd
skip/rhel
skip/docker
-skip/python2.6
diff --git a/ansible_collections/community/general/tests/integration/targets/homectl/aliases b/ansible_collections/community/general/tests/integration/targets/homectl/aliases
index b87db2e43..ea9b44230 100644
--- a/ansible_collections/community/general/tests/integration/targets/homectl/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/homectl/aliases
@@ -9,3 +9,5 @@ skip/osx
skip/macos
skip/rhel9.0 # See https://www.reddit.com/r/Fedora/comments/si7nzk/homectl/
skip/rhel9.1 # See https://www.reddit.com/r/Fedora/comments/si7nzk/homectl/
+skip/rhel9.2 # See https://www.reddit.com/r/Fedora/comments/si7nzk/homectl/
+skip/rhel9.3 # See https://www.reddit.com/r/Fedora/comments/si7nzk/homectl/
diff --git a/ansible_collections/community/general/tests/integration/targets/htpasswd/aliases b/ansible_collections/community/general/tests/integration/targets/htpasswd/aliases
new file mode 100644
index 000000000..e3339b210
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/htpasswd/aliases
@@ -0,0 +1,7 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+azp/posix/2
+destructive
+needs/root
diff --git a/ansible_collections/community/general/tests/integration/targets/htpasswd/handlers/main.yml b/ansible_collections/community/general/tests/integration/targets/htpasswd/handlers/main.yml
new file mode 100644
index 000000000..6befa0cd3
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/htpasswd/handlers/main.yml
@@ -0,0 +1,9 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: remove passlib
+ ansible.builtin.pip:
+ name: passlib
+ state: absent
diff --git a/ansible_collections/community/general/tests/integration/targets/htpasswd/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/htpasswd/meta/main.yml
new file mode 100644
index 000000000..982de6eb0
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/htpasswd/meta/main.yml
@@ -0,0 +1,7 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+dependencies:
+ - setup_remote_tmp_dir
diff --git a/ansible_collections/community/general/tests/integration/targets/htpasswd/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/htpasswd/tasks/main.yml
new file mode 100644
index 000000000..7b5dc3c51
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/htpasswd/tasks/main.yml
@@ -0,0 +1,83 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: install passlib
+ ansible.builtin.pip:
+ name: passlib
+ notify: remove passlib
+
+- name: add bob (check mode)
+ community.general.htpasswd:
+ path: "{{ htpasswd_path }}"
+ name: bob
+ password: c00lbob
+ check_mode: true
+ register: add_bob_check
+
+- name: add bob
+ community.general.htpasswd:
+ path: "{{ htpasswd_path }}"
+ name: bob
+ password: c00lbob
+ register: add_bob
+
+- name: add bob (idempotency)
+ community.general.htpasswd:
+ path: "{{ htpasswd_path }}"
+ name: bob
+ password: c00lbob
+ register: add_bob_idempot
+
+- name: add bob new password
+ community.general.htpasswd:
+ path: "{{ htpasswd_path }}"
+ name: bob
+ password: SUPERsecret
+ register: add_bob_newpw
+
+- name: add bob new password (idempotency)
+ community.general.htpasswd:
+ path: "{{ htpasswd_path }}"
+ name: bob
+ password: SUPERsecret
+ register: add_bob_newpw_idempot
+
+- name: test add bob assertions
+ ansible.builtin.assert:
+ that:
+ - add_bob_check is changed
+ - add_bob is changed
+ - add_bob_idempot is not changed
+ - add_bob_newpw is changed
+ - add_bob_newpw_idempot is not changed
+
+- name: remove bob (check mode)
+ community.general.htpasswd:
+ path: "{{ htpasswd_path }}"
+ name: bob
+ state: absent
+ check_mode: true
+ register: del_bob_check
+
+- name: remove bob
+ community.general.htpasswd:
+ path: "{{ htpasswd_path }}"
+ name: bob
+ state: absent
+ register: del_bob
+
+- name: remove bob (idempotency)
+ community.general.htpasswd:
+ path: "{{ htpasswd_path }}"
+ name: bob
+ state: absent
+ register: del_bob_idempot
+
+- name: test remove bob assertions
+ ansible.builtin.assert:
+ that:
+ - del_bob_check is changed
+ - del_bob is changed
+ - del_bob_idempot is not changed
diff --git a/ansible_collections/community/general/tests/integration/targets/htpasswd/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/htpasswd/vars/main.yml
new file mode 100644
index 000000000..ff81959c4
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/htpasswd/vars/main.yml
@@ -0,0 +1,6 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+htpasswd_path: "{{ remote_tmp_dir }}/dot_htpasswd"
diff --git a/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/main.yml
index 11c5bf3b2..0ed3c2817 100644
--- a/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/main.yml
@@ -38,3 +38,15 @@
- name: include tasks to test regressions
include_tasks: tests/03-encoding.yml
+
+ - name: include tasks to test symlink handling
+ include_tasks: tests/04-symlink.yml
+
+ - name: include tasks to test ignore_spaces
+ include_tasks: tests/05-ignore_spaces.yml
+
+ - name: include tasks to test modify_inactive_option
+ include_tasks: tests/06-modify_inactive_option.yml
+
+ - name: include tasks to test optional spaces in section headings
+ include_tasks: tests/07-section_name_spaces.yml
diff --git a/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/00-basic.yml b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/00-basic.yml
index c619e937a..f36fd54c5 100644
--- a/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/00-basic.yml
+++ b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/00-basic.yml
@@ -3,7 +3,7 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-## basiscs
+## basics
- name: test-basic 1 - specify both "value" and "values" and fail
ini_file:
@@ -39,4 +39,4 @@
that:
- result_basic_2 is not changed
- result_basic_2 is failed
- - result_basic_2.msg == "Destination {{ non_existing_file }} does not exist!"
+ - result_basic_2.msg == "Destination " ~ non_existing_file ~ " does not exist!"
diff --git a/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/04-symlink.yml b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/04-symlink.yml
new file mode 100644
index 000000000..7e83a010d
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/04-symlink.yml
@@ -0,0 +1,59 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- block: &prepare
+ - name: Create the final file
+ ansible.builtin.copy:
+ content: |
+ [main]
+ foo=BAR
+ dest: "{{ remote_tmp_dir }}/my_original_file.ini"
+ - name: Clean up symlink.ini
+ ansible.builtin.file:
+ path: "{{ remote_tmp_dir }}/symlink.ini"
+ state: absent
+ - name: Create a symbolic link
+ ansible.builtin.file:
+ src: my_original_file.ini
+ dest: "{{ remote_tmp_dir }}/symlink.ini"
+ state: link
+
+- name: Set the proxy key on the symlink which will be converted as a file
+ community.general.ini_file:
+ path: "{{ remote_tmp_dir }}/symlink.ini"
+ section: main
+ option: proxy
+ value: 'http://proxy.myorg.org:3128'
+- name: Set the proxy key on the final file that is still unchanged
+ community.general.ini_file:
+ path: "{{ remote_tmp_dir }}/my_original_file.ini"
+ section: main
+ option: proxy
+ value: 'http://proxy.myorg.org:3128'
+ register: result
+- ansible.builtin.assert:
+ that:
+ - result is changed
+
+# With follow
+- block: *prepare
+- name: Set the proxy key on the symlink which will be preserved
+ community.general.ini_file:
+ path: "{{ remote_tmp_dir }}/symlink.ini"
+ section: main
+ option: proxy
+ value: 'http://proxy.myorg.org:3128'
+ follow: true
+ register: result
+- name: Set the proxy key on the target directly that was changed in the previous step
+ community.general.ini_file:
+ path: "{{ remote_tmp_dir }}/my_original_file.ini"
+ section: main
+ option: proxy
+ value: 'http://proxy.myorg.org:3128'
+ register: result
+- ansible.builtin.assert:
+ that:
+ - "not (result is changed)"
diff --git a/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/05-ignore_spaces.yml b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/05-ignore_spaces.yml
new file mode 100644
index 000000000..3c4b068fb
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/05-ignore_spaces.yml
@@ -0,0 +1,123 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+## testing ignore_spaces option
+
+- name: test-ignore_spaces 1 (commented line updated) - create test file
+ copy:
+ dest: "{{ output_file }}"
+ content: "[foo]\n; bar=baz\n"
+
+- name: test-ignore_spaces 1 - set new value
+ ini_file:
+ path: "{{ output_file }}"
+ section: foo
+ option: bar
+ value: frelt
+ ignore_spaces: true
+ register: result
+
+- name: test-ignore_spaces 1 - read content from output file
+ slurp:
+ src: "{{ output_file }}"
+ register: output_content
+
+- name: test-ignore_spaces 1 - verify results
+ vars:
+ actual_content: "{{ output_content.content | b64decode }}"
+ expected_content: "[foo]\nbar = frelt\n"
+ assert:
+ that:
+ - actual_content == expected_content
+ - result is changed
+ - result.msg == 'option changed'
+
+- name: test-ignore_spaces 2 (uncommented line updated) - create test file
+ copy:
+ dest: "{{ output_file }}"
+ content: "[foo]\nbar=baz\n"
+
+- name: test-ignore_spaces 2 - set new value
+ ini_file:
+ path: "{{ output_file }}"
+ section: foo
+ option: bar
+ value: frelt
+ ignore_spaces: true
+ register: result
+
+- name: test-ignore_spaces 2 - read content from output file
+ slurp:
+ src: "{{ output_file }}"
+ register: output_content
+
+- name: test-ignore_spaces 2 - verify results
+ vars:
+ actual_content: "{{ output_content.content | b64decode }}"
+ expected_content: "[foo]\nbar = frelt\n"
+ assert:
+ that:
+ - actual_content == expected_content
+ - result is changed
+ - result.msg == 'option changed'
+
+- name: test-ignore_spaces 3 (spaces on top of no spaces) - create test file
+ copy:
+ dest: "{{ output_file }}"
+ content: "[foo]\nbar=baz\n"
+
+- name: test-ignore_spaces 3 - try to set value
+ ini_file:
+ path: "{{ output_file }}"
+ section: foo
+ option: bar
+ value: baz
+ ignore_spaces: true
+ register: result
+
+- name: test-ignore_spaces 3 - read content from output file
+ slurp:
+ src: "{{ output_file }}"
+ register: output_content
+
+- name: test-ignore_spaces 3 - verify results
+ vars:
+ actual_content: "{{ output_content.content | b64decode }}"
+ expected_content: "[foo]\nbar=baz\n"
+ assert:
+ that:
+ - actual_content == expected_content
+ - result is not changed
+ - result.msg == "OK"
+
+- name: test-ignore_spaces 4 (no spaces on top of spaces) - create test file
+ copy:
+ dest: "{{ output_file }}"
+ content: "[foo]\nbar = baz\n"
+
+- name: test-ignore_spaces 4 - try to set value
+ ini_file:
+ path: "{{ output_file }}"
+ section: foo
+ option: bar
+ value: baz
+ ignore_spaces: true
+ no_extra_spaces: true
+ register: result
+
+- name: test-ignore_spaces 4 - read content from output file
+ slurp:
+ src: "{{ output_file }}"
+ register: output_content
+
+- name: test-ignore_spaces 4 - verify results
+ vars:
+ actual_content: "{{ output_content.content | b64decode }}"
+ expected_content: "[foo]\nbar = baz\n"
+ assert:
+ that:
+ - actual_content == expected_content
+ - result is not changed
+ - result.msg == "OK"
diff --git a/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/06-modify_inactive_option.yml b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/06-modify_inactive_option.yml
new file mode 100644
index 000000000..2d1d04928
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/06-modify_inactive_option.yml
@@ -0,0 +1,123 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+## testing modify_inactive_option option
+
+- name: test-modify_inactive_option 1 - create test file
+ copy:
+ content: |
+
+ [section1]
+ # Uncomment the line below to enable foo
+ # foo = bar
+
+ dest: "{{ output_file }}"
+
+- name: test-modify_inactive_option 1 - set value for foo with modify_inactive_option set to true
+ ini_file:
+ path: "{{ output_file }}"
+ section: section1
+ option: foo
+ value: bar
+ modify_inactive_option: true
+ register: result1
+
+- name: test-modify_inactive_option 1 - read content from output file
+ slurp:
+ src: "{{ output_file }}"
+ register: output_content
+
+- name: test-modify_inactive_option 1 - set expected content and get current ini file content
+ set_fact:
+ expected1: |
+
+ [section1]
+ # Uncomment the line below to enable foo
+ foo = bar
+
+ content1: "{{ output_content.content | b64decode }}"
+
+- name: test-modify_inactive_option 1 - assert 'changed' is true, content is OK and option changed
+ assert:
+ that:
+ - result1 is changed
+ - result1.msg == 'option changed'
+ - content1 == expected1
+
+
+- name: test-modify_inactive_option 2 - create test file
+ copy:
+ content: |
+
+ [section1]
+ # Uncomment the line below to enable foo
+ # foo = bar
+
+ dest: "{{ output_file }}"
+
+- name: test-modify_inactive_option 2 - set value for foo with modify_inactive_option set to false
+ ini_file:
+ path: "{{ output_file }}"
+ section: section1
+ option: foo
+ value: bar
+ modify_inactive_option: false
+ register: result2
+
+- name: test-modify_inactive_option 2 - read content from output file
+ slurp:
+ src: "{{ output_file }}"
+ register: output_content
+
+- name: test-modify_inactive_option 2 - set expected content and get current ini file content
+ set_fact:
+ expected2: |
+
+ [section1]
+ foo = bar
+ # Uncomment the line below to enable foo
+ # foo = bar
+
+ content2: "{{ output_content.content | b64decode }}"
+
+- name: test-modify_inactive_option 2 - assert 'changed' is true and content is OK and option added
+ assert:
+ that:
+ - result2 is changed
+ - result2.msg == 'option added'
+ - content2 == expected2
+
+
+- name: test-modify_inactive_option 3 - remove foo=bar with modify_inactive_option set to true to ensure it doesn't have effect for removal
+ ini_file:
+ path: "{{ output_file }}"
+ section: section1
+ option: foo
+ value: bar
+ modify_inactive_option: true
+ state: absent
+ register: result3
+
+- name: test-modify_inactive_option 3 - read content from output file
+ slurp:
+ src: "{{ output_file }}"
+ register: output_content
+
+- name: test-modify_inactive_option 3 - set expected content and get current ini file content
+ set_fact:
+ expected3: |
+
+ [section1]
+ # Uncomment the line below to enable foo
+ # foo = bar
+
+ content3: "{{ output_content.content | b64decode }}"
+
+- name: test-modify_inactive_option 3 - assert 'changed' is true and content is OK and active option removed
+ assert:
+ that:
+ - result3 is changed
+ - result3.msg == 'option changed'
+ - content3 == expected3 \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/07-section_name_spaces.yml b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/07-section_name_spaces.yml
new file mode 100644
index 000000000..6cdcfef40
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/ini_file/tasks/tests/07-section_name_spaces.yml
@@ -0,0 +1,103 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+## testing support for optional spaces between brackets and section names
+
+- name: Test-section_name_spaces 1 (does legacy workaround still work) - create test file
+ ansible.builtin.copy: # noqa risky-file-permissions
+ dest: "{{ output_file }}"
+ content: |
+ [ foo ]
+ ; bar=baz
+
+- name: Test-section_name_spaces 1 - update with optional spaces specified
+ community.general.ini_file: # noqa risky-file-permissions
+ path: "{{ output_file }}"
+ section: ' foo '
+ option: bar
+ value: frelt
+ register: result
+
+- name: Test-section_name_spaces 1 - read content from output file
+ ansible.builtin.slurp:
+ src: "{{ output_file }}"
+ register: output_content
+
+- name: Test-section_name_spaces 1 - verify results
+ vars:
+ actual_content: "{{ output_content.content | b64decode }}"
+ expected_content: |
+ [ foo ]
+ bar = frelt
+ ansible.builtin.assert:
+ that:
+ - actual_content == expected_content
+ - result is changed
+ - result.msg == 'option changed'
+
+
+- name: Test-section_name_spaces 2 (optional spaces omitted) - create test file
+ ansible.builtin.copy: # noqa risky-file-permissions
+ dest: "{{ output_file }}"
+ content: |
+ [ foo ]
+ bar=baz"
+
+- name: Test-section_name_spaces 2 - update without optional spaces
+ community.general.ini_file: # noqa risky-file-permissions
+ path: "{{ output_file }}"
+ section: foo
+ option: bar
+ value: frelt
+ ignore_spaces: true
+ register: result
+
+- name: Test-section_name_spaces 2 - read content from output file
+ ansible.builtin.slurp:
+ src: "{{ output_file }}"
+ register: output_content
+
+- name: Test-section_name_spaces 2 - verify results
+ vars:
+ actual_content: "{{ output_content.content | b64decode }}"
+ expected_content: "[ foo ]\nbar = frelt\n"
+ ansible.builtin.assert:
+ that:
+ - actual_content == expected_content
+ - result is changed
+ - result.msg == 'option changed'
+
+
+- name: Test-section_name_spaces 3 (legacy workaround when not required) - create test file
+ ansible.builtin.copy: # noqa risky-file-permissions
+ dest: "{{ output_file }}"
+ content: |
+ [foo]
+ ; bar=baz
+
+- name: Test-section_name_spaces 3 - update with optional spaces specified
+ community.general.ini_file: # noqa risky-file-permissions
+ path: "{{ output_file }}"
+ section: ' foo '
+ option: bar
+ value: frelt
+ register: result
+
+- name: Test-section_name_spaces 3 - read content from output file
+ ansible.builtin.slurp:
+ src: "{{ output_file }}"
+ register: output_content
+
+- name: Test-section_name_spaces 3 - verify results
+ vars:
+ actual_content: "{{ output_content.content | b64decode }}"
+ expected_content: |
+ [foo]
+ bar = frelt
+ ansible.builtin.assert:
+ that:
+ - actual_content == expected_content
+ - result is changed
+ - result.msg == 'option changed'
diff --git a/ansible_collections/community/general/tests/integration/targets/interfaces_file/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/interfaces_file/tasks/main.yml
index 918a32331..18af12f5a 100644
--- a/ansible_collections/community/general/tests/integration/targets/interfaces_file/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/interfaces_file/tasks/main.yml
@@ -7,6 +7,7 @@
set_fact:
interfaces_testfile: '{{ remote_tmp_dir }}/interfaces'
interfaces_testfile_3841: '{{ remote_tmp_dir }}/interfaces_3841'
+ interfaces_testfile_7610: '{{ remote_tmp_dir }}/interfaces_7610'
- name: Copy interfaces file
copy:
@@ -65,3 +66,60 @@
that:
- ifile_3841_a is changed
- ifile_3841_b is not changed
+
+- name: 7610 - create file
+ copy:
+ dest: '{{ interfaces_testfile_7610 }}'
+ content: |
+ iface ens3 inet dhcp
+ iface ens3 inet6 auto
+
+- name: 7610 - modify file
+ interfaces_file:
+ dest: '{{ interfaces_testfile_7610 }}'
+ iface: ens3
+ address_family: "inet6"
+ option: "{{ item.option }}"
+ value: "{{ item.value }}"
+ loop:
+ - option: "method"
+ value: "static"
+ - option: "address"
+ value: "1:2::3/48"
+
+- name: 7610 - read file
+ slurp:
+ src: '{{ interfaces_testfile_7610 }}'
+ register: content_7610
+
+- name: 7610 - check assertions
+ assert:
+ that:
+ - content_7610.content | b64decode == expected_content
+ vars:
+ expected_content: |
+ iface ens3 inet dhcp
+ iface ens3 inet6 static
+ address 1:2::3/48
+
+- name: 7610 - modify file again
+ interfaces_file:
+ dest: '{{ interfaces_testfile_7610 }}'
+ iface: ens3
+ option: method
+ value: foobar
+
+- name: 7610 - read file
+ slurp:
+ src: '{{ interfaces_testfile_7610 }}'
+ register: content_7610
+
+- name: 7610 - check assertions
+ assert:
+ that:
+ - content_7610.content | b64decode == expected_content
+ vars:
+ expected_content: |
+ iface ens3 inet foobar
+ iface ens3 inet6 foobar
+ address 1:2::3/48
diff --git a/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/main.yml
index a74e74df4..d55007067 100644
--- a/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/main.yml
@@ -29,6 +29,12 @@
when:
- xtables_lock is undefined
+ - name: include tasks to test partial restore files
+ include_tasks: tests/02-partial-restore.yml
+ when:
+ - xtables_lock is undefined
+
+
- name: include tasks to test rollbacks
include_tasks: tests/10-rollback.yml
when:
diff --git a/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/tests/02-partial-restore.yml b/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/tests/02-partial-restore.yml
new file mode 100644
index 000000000..6da4814af
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/iptables_state/tasks/tests/02-partial-restore.yml
@@ -0,0 +1,66 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: "Create initial rule set to use"
+ copy:
+ dest: "{{ iptables_tests }}"
+ content: |
+ *filter
+ :INPUT ACCEPT [0:0]
+ :FORWARD ACCEPT [0:0]
+ :OUTPUT ACCEPT [0:0]
+ -A INPUT -m state --state NEW,ESTABLISHED -j ACCEPT
+ COMMIT
+ *nat
+ :PREROUTING ACCEPT [151:17304]
+ :INPUT ACCEPT [151:17304]
+ :OUTPUT ACCEPT [151:17304]
+ :POSTROUTING ACCEPT [151:17304]
+ -A POSTROUTING -o eth0 -j MASQUERADE
+ COMMIT
+
+- name: "Restore initial state"
+ iptables_state:
+ path: "{{ iptables_tests }}"
+ state: restored
+ async: "{{ ansible_timeout }}"
+ poll: 0
+
+- name: "Create partial ruleset only specifying input"
+ copy:
+ dest: "{{ iptables_tests }}"
+ content: |
+ *filter
+ :INPUT ACCEPT [0:0]
+ :FORWARD ACCEPT [0:0]
+ :OUTPUT ACCEPT [0:0]
+ -A INPUT -m state --state NEW,ESTABLISHED -j ACCEPT
+ COMMIT
+
+- name: "Check restoring partial state"
+ iptables_state:
+ path: "{{ iptables_tests }}"
+ state: restored
+ check_mode: true
+ register: iptables_state
+
+
+- name: "assert that no changes are detected in check mode"
+ assert:
+ that:
+ - iptables_state is not changed
+
+- name: "Restore partial state"
+ iptables_state:
+ path: "{{ iptables_tests }}"
+ state: restored
+ register: iptables_state
+ async: "{{ ansible_timeout }}"
+ poll: 0
+
+- name: "assert that no changes are made"
+ assert:
+ that:
+ - iptables_state is not changed \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/iso_create/aliases b/ansible_collections/community/general/tests/integration/targets/iso_create/aliases
index 4fb0bec81..73bf1b2d1 100644
--- a/ansible_collections/community/general/tests/integration/targets/iso_create/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/iso_create/aliases
@@ -5,4 +5,3 @@
azp/posix/1
destructive
skip/aix
-skip/python2.6
diff --git a/ansible_collections/community/general/tests/integration/targets/iso_customize/aliases b/ansible_collections/community/general/tests/integration/targets/iso_customize/aliases
index 54a0f1a04..394289c08 100644
--- a/ansible_collections/community/general/tests/integration/targets/iso_customize/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/iso_customize/aliases
@@ -8,6 +8,5 @@ destructive
skip/aix
skip/freebsd
skip/alpine
-skip/python2.6
skip/docker
needs/root
diff --git a/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize_exception.yml b/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize_exception.yml
index b2130bb6b..715f2fd38 100644
--- a/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize_exception.yml
+++ b/ansible_collections/community/general/tests/integration/targets/iso_customize/tasks/iso_customize_exception.yml
@@ -59,7 +59,7 @@
failed_when: customized_result.msg.find("does not exist") == -1
# Test: filenames with whitespaces
-# We report error: the user should be reponsible for the it
+# We report error: the user should be responsible for the it
- name: "Testcase: filenames with whitespaces"
community.general.iso_customize:
src_iso: "{{ test_dir }}/test.iso"
diff --git a/ansible_collections/community/general/tests/integration/targets/iso_extract/aliases b/ansible_collections/community/general/tests/integration/targets/iso_extract/aliases
index 33041456a..5ddca1ecb 100644
--- a/ansible_collections/community/general/tests/integration/targets/iso_extract/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/iso_extract/aliases
@@ -9,5 +9,9 @@ skip/aix
skip/osx # FIXME
skip/rhel9.0 # FIXME
skip/rhel9.1 # FIXME
+skip/rhel9.2 # FIXME
+skip/rhel9.3 # FIXME
skip/freebsd12.4 # FIXME
skip/freebsd13.2 # FIXME
+skip/freebsd13.3 # FIXME
+skip/freebsd14.0 # FIXME
diff --git a/ansible_collections/community/general/tests/integration/targets/java_cert/aliases b/ansible_collections/community/general/tests/integration/targets/java_cert/aliases
index 573cb189b..0d8271ff7 100644
--- a/ansible_collections/community/general/tests/integration/targets/java_cert/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/java_cert/aliases
@@ -9,3 +9,4 @@ skip/osx
skip/macos
skip/freebsd
needs/root
+skip/rhel # FIXME: keytool seems to be broken on newer RHELs
diff --git a/ansible_collections/community/general/tests/integration/targets/java_cert/files/setupSSLServer.py b/ansible_collections/community/general/tests/integration/targets/java_cert/files/setupSSLServer.py
index 4b0a42185..b5a333b47 100644
--- a/ansible_collections/community/general/tests/integration/targets/java_cert/files/setupSSLServer.py
+++ b/ansible_collections/community/general/tests/integration/targets/java_cert/files/setupSSLServer.py
@@ -18,7 +18,14 @@ except ModuleNotFoundError:
from http.server import HTTPServer, SimpleHTTPRequestHandler
httpd = HTTPServer(('localhost', port), SimpleHTTPRequestHandler)
-httpd.socket = ssl.wrap_socket(httpd.socket, server_side=True,
- certfile=os.path.join(root_dir, 'cert.pem'),
- keyfile=os.path.join(root_dir, 'key.pem'))
+try:
+ httpd.socket = ssl.wrap_socket(httpd.socket, server_side=True,
+ certfile=os.path.join(root_dir, 'cert.pem'),
+ keyfile=os.path.join(root_dir, 'key.pem'))
+except AttributeError:
+ # Python 3.12 or newer:
+ context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
+ context.load_cert_chain(certfile=os.path.join(root_dir, 'cert.pem'),
+ keyfile=os.path.join(root_dir, 'key.pem'))
+ httpd.socket = context.wrap_socket(httpd.socket)
httpd.handle_request()
diff --git a/ansible_collections/community/general/tests/integration/targets/java_cert/tasks/state_change.yml b/ansible_collections/community/general/tests/integration/targets/java_cert/tasks/state_change.yml
index e135a60a3..f2898ddc2 100644
--- a/ansible_collections/community/general/tests/integration/targets/java_cert/tasks/state_change.yml
+++ b/ansible_collections/community/general/tests/integration/targets/java_cert/tasks/state_change.yml
@@ -181,7 +181,7 @@
- result_x509_changed is changed
- name: |
- We also want to make sure that the status doesnt change if we import the same cert
+ We also want to make sure that the status does not change if we import the same cert
community.general.java_cert:
cert_alias: test_cert
cert_path: "{{ test_cert2_path }}"
diff --git a/ansible_collections/community/general/tests/integration/targets/java_keystore/aliases b/ansible_collections/community/general/tests/integration/targets/java_keystore/aliases
index 573cb189b..0d8271ff7 100644
--- a/ansible_collections/community/general/tests/integration/targets/java_keystore/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/java_keystore/aliases
@@ -9,3 +9,4 @@ skip/osx
skip/macos
skip/freebsd
needs/root
+skip/rhel # FIXME: keytool seems to be broken on newer RHELs
diff --git a/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/aliases b/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/aliases
index afda346c4..b85ae6419 100644
--- a/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/aliases
@@ -3,3 +3,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/1
+skip/aix
+skip/freebsd
+skip/osx
+skip/macos
diff --git a/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/handlers/main.yml b/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/handlers/main.yml
new file mode 100644
index 000000000..814c9c51a
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/handlers/main.yml
@@ -0,0 +1,10 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Remove modprobe.d
+ ansible.builtin.file:
+ path: /etc/modprobe.d
+ state: absent
+ \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/tasks/main.yml
index e169d5479..48cd38a93 100644
--- a/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/kernel_blacklist/tasks/main.yml
@@ -36,25 +36,37 @@
path: '{{ bl_file }}'
register: stat_test_1
+- name: show bl_test_1
+ ansible.builtin.debug:
+ var: bl_test_1_depr_msgs
+ vars:
+ bl_test_1_depr_msgs: "{{ (bl_test_1.deprecations | default([])) | map(attribute='msg') }}"
+ # q('ansible.builtin.subelements', bl_test_1, 'deprecations', {'skip_missing': True}) }}"
+
- name: assert file is unchanged
assert:
that:
- - bl_test_1 is not changed
- - bl_test_1a is not changed
- - orig_stat.stat.size == stat_test_1.stat.size
- - orig_stat.stat.checksum == stat_test_1.stat.checksum
- - orig_stat.stat.mtime == stat_test_1.stat.mtime
- - stat_test_1.stat.checksum == expected_content | checksum
+ - bl_test_1 is not changed
+ - bl_test_1a is not changed
+ - orig_stat.stat.size == stat_test_1.stat.size
+ - orig_stat.stat.checksum == stat_test_1.stat.checksum
+ - orig_stat.stat.mtime == stat_test_1.stat.mtime
+ - stat_test_1.stat.checksum == expected_content | checksum
vars:
expected_content: |
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
- # SPDX-{{ '' }}License-Identifier: GPL-3.0-or-later
+ # SPDX{{ '' }}-License-Identifier: GPL-3.0-or-later
blacklist aaaa
blacklist bbbb
blacklist cccc
+- name: test deprecation
+ assert:
+ that:
+ - "'deprecations' not in bl_test_1 or (ansible_version.major == 2 and ansible_version.minor == 12)"
+
- name: add new item to list
community.general.kernel_blacklist:
blacklist_file: '{{ bl_file }}'
@@ -70,13 +82,13 @@
- name: assert element is added
assert:
that:
- - bl_test_2 is changed
- - slurp_test_2.content|b64decode == content
+ - bl_test_2 is changed
+ - slurp_test_2.content|b64decode == content
vars:
content: |
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
- # SPDX-{{ '' }}License-Identifier: GPL-3.0-or-later
+ # SPDX{{ '' }}-License-Identifier: GPL-3.0-or-later
blacklist aaaa
blacklist bbbb
@@ -95,17 +107,49 @@
src: '{{ bl_file }}'
register: slurp_test_3
-- name: assert element is added
+- name: assert element is removed
assert:
that:
- - bl_test_3 is changed
- - slurp_test_3.content|b64decode == content
+ - bl_test_3 is changed
+ - slurp_test_3.content|b64decode == content
vars:
content: |
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
- # SPDX-{{ '' }}License-Identifier: GPL-3.0-or-later
+ # SPDX{{ '' }}-License-Identifier: GPL-3.0-or-later
blacklist aaaa
blacklist cccc
blacklist dddd
+
+############################################################################################################################################
+#
+# Issue 7362
+#
+
+- name: Create /etc/modprobe.d
+ ansible.builtin.file:
+ path: /etc/modprobe.d
+ state: directory
+ mode: '0755'
+ owner: root
+ group: root
+ notify: Remove modprobe.d
+
+- name: Create cls_rsvp file
+ ansible.builtin.copy:
+ dest: /etc/modprobe.d/cls_rsvp-blacklist.conf
+ content: |
+ blacklist cls_rsvp
+ mode: '0644'
+
+- name: Block potentially affected (and unused) modules (7362)
+ community.general.kernel_blacklist:
+ name: "{{ line_item }}"
+ state: present
+ blacklist_file: "/etc/modprobe.d/{{ line_item }}-blacklist.conf"
+ with_items:
+ - cifs
+ - cls_rsvp
+ loop_control:
+ loop_var: line_item
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/readme.adoc b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/readme.adoc
index 1941e54ef..8e052920c 100644
--- a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/readme.adoc
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_authorization_scope/readme.adoc
@@ -20,7 +20,7 @@ docker run --name mykeycloak -p 8080:8080 -e KC_HTTP_RELATIVE_PATH=/auth -e KEYC
This test suite can run against a fresh unconfigured server instance
(no preconfiguration required) and cleans up after itself (undoes all
-its config changes) as long as it runs through completly. While its active
+its config changes) as long as it runs through completely. While its active
it changes the server configuration in the following ways:
* creating, modifying and deleting some keycloak groups
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/aliases b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/aliases
new file mode 100644
index 000000000..bd1f02444
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/aliases
@@ -0,0 +1,5 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+unsupported
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/Containerfile b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/Containerfile
new file mode 100644
index 000000000..3303066da
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/Containerfile
@@ -0,0 +1,4 @@
+FROM quay.io/keycloak/keycloak:20.0.2
+
+COPY policy.jar /opt/keycloak/providers/
+RUN /opt/keycloak/bin/kc.sh build
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/Containerfile.license b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/Containerfile.license
new file mode 100644
index 000000000..a1390a69e
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/Containerfile.license
@@ -0,0 +1,3 @@
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/META-INF/keycloak-scripts.json b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/META-INF/keycloak-scripts.json
new file mode 100644
index 000000000..9370c16cb
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/META-INF/keycloak-scripts.json
@@ -0,0 +1,14 @@
+{
+ "policies": [
+ {
+ "name": "MyPolicy1",
+ "fileName": "policy-1.js",
+ "description": "My Policy 1"
+ },
+ {
+ "name": "MyPolicy2",
+ "fileName": "policy-2.js",
+ "description": "My Policy 2"
+ }
+ ]
+}
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/META-INF/keycloak-scripts.json.license b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/META-INF/keycloak-scripts.json.license
new file mode 100644
index 000000000..a1390a69e
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/META-INF/keycloak-scripts.json.license
@@ -0,0 +1,3 @@
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/build-policy.sh b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/build-policy.sh
new file mode 100755
index 000000000..eeca22f7e
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/build-policy.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+zip -r policy.jar META-INF/keycloak-scripts.json policy-1.js policy-2.js
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/build-policy.sh.license b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/build-policy.sh.license
new file mode 100644
index 000000000..a1390a69e
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/build-policy.sh.license
@@ -0,0 +1,3 @@
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/policy-1.js b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/policy-1.js
new file mode 100644
index 000000000..fa42b0719
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/policy-1.js
@@ -0,0 +1 @@
+$evaluation.grant();
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/policy-1.js.license b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/policy-1.js.license
new file mode 100644
index 000000000..a1390a69e
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/policy-1.js.license
@@ -0,0 +1,3 @@
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/policy-2.js b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/policy-2.js
new file mode 100644
index 000000000..fa42b0719
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/policy-2.js
@@ -0,0 +1 @@
+$evaluation.grant();
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/policy-2.js.license b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/policy-2.js.license
new file mode 100644
index 000000000..a1390a69e
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/policy/policy-2.js.license
@@ -0,0 +1,3 @@
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/readme.adoc b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/readme.adoc
new file mode 100644
index 000000000..cc014158f
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/readme.adoc
@@ -0,0 +1,27 @@
+// Copyright (c) Ansible Project
+// GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+To be able to run these integration tests a keycloak server must be
+reachable under a specific url with a specific admin user and password.
+The exact values expected for these parameters can be found in
+'vars/main.yml' file. A vanilla Keycloak server will not be sufficient:
+you will need to deploy a custom JAR file with two policies:
+
+* _MyPolicy1:_ policy-1.js
+* _MyPolicy2:_ policy-2.js
+
+To create a customized Keycloak test instance running on Podman first
+install the "zip" command, go to the policy subdirectory and then do
+
+[source,shell]
+----
+./build-policy.sh
+podman build --tag keycloak_authz_custom_policy_test:1.0.0 .
+podman rm mykeycloak && podman run --name mykeycloak -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=password -e KC_HTTP_RELATIVE_PATH=/auth localhost/keycloak_authz_custom_policy_test:1.0.0 start-dev
+----
+
+This process probably also work with Docker just by replacing _podman_ with
+_docker_. Modify the FROM argument in Containerfile to change Keycloak version
+to test against. Quarkus versions of Keycloak should work - older versions
+will not.
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/tasks/main.yml
new file mode 100644
index 000000000..b22d75121
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/tasks/main.yml
@@ -0,0 +1,168 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+- name: Remove keycloak client to avoid failures from previous failed runs
+ community.general.keycloak_client:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ state: absent
+
+- name: Create keycloak client with authorization services enabled
+ community.general.keycloak_client:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ state: present
+ enabled: true
+ public_client: false
+ service_accounts_enabled: true
+ authorization_services_enabled: true
+
+- name: Create first custom policy (check_mode)
+ community.general.keycloak_authz_custom_policy:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: FirstCustomPolicy
+ state: present
+ policy_type: script-policy-1.js
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ check_mode: true
+ register: result
+
+- name: Assert that first custom policy was not created
+ assert:
+ that:
+ - result is changed
+ - result.end_state != {}
+ - result.end_state.name == "FirstCustomPolicy"
+ - result.end_state.type == "script-policy-1.js"
+ - result.msg == 'Would create custom policy FirstCustomPolicy'
+
+- name: Create first custom policy
+ community.general.keycloak_authz_custom_policy:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: FirstCustomPolicy
+ state: present
+ policy_type: script-policy-1.js
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ register: result
+
+- name: Assert that first custom policy was created
+ assert:
+ that:
+ - result is changed
+ - result.end_state != {}
+ - result.end_state.name == "FirstCustomPolicy"
+ - result.end_state.type == "script-policy-1.js"
+ - result.msg == 'Custom policy FirstCustomPolicy created'
+
+- name: Attempt to update first custom policy (not possible)
+ community.general.keycloak_authz_custom_policy:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: FirstCustomPolicy
+ state: present
+ policy_type: script-policy-2.js
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ register: result
+
+- name: Assert that first custom policy was not modified
+ assert:
+ that:
+ - result is not changed
+ - result.end_state != {}
+ - result.end_state.name == "FirstCustomPolicy"
+ - result.end_state.type == "script-policy-2.js"
+ - result.msg == 'Custom policy FirstCustomPolicy already exists'
+
+# Ensure that we can create multiple instances of the custom policy
+- name: Create second instance of the custom policy
+ community.general.keycloak_authz_custom_policy:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: SecondCustomPolicy
+ state: present
+ policy_type: script-policy-1.js
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ register: result
+
+- name: Assert that second instance of the custom policy was created
+ assert:
+ that:
+ - result is changed
+ - result.end_state != {}
+ - result.end_state.name == "SecondCustomPolicy"
+ - result.end_state.type == "script-policy-1.js"
+ - result.msg == 'Custom policy SecondCustomPolicy created'
+
+- name: Remove second instance of the custom policy (check_mode)
+ community.general.keycloak_authz_custom_policy:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: SecondCustomPolicy
+ state: absent
+ policy_type: script-policy-1.js
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ check_mode: true
+ register: result
+
+- name: Assert that second custom policy was not removed
+ assert:
+ that:
+ - result is changed
+ - result.end_state == {}
+ - result.msg == 'Would remove custom policy SecondCustomPolicy'
+
+- name: Remove second instance of the custom policy
+ community.general.keycloak_authz_custom_policy:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: SecondCustomPolicy
+ state: absent
+ policy_type: script-policy-1.js
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ register: result
+
+- name: Assert that second custom policy was removed
+ assert:
+ that:
+ - result is changed
+ - result.end_state == {}
+ - result.msg == 'Custom policy SecondCustomPolicy removed'
+
+- name: Remove keycloak client
+ community.general.keycloak_client:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ state: absent
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/vars/main.yml
new file mode 100644
index 000000000..c1d5fc983
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_custom_policy/vars/main.yml
@@ -0,0 +1,11 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+url: http://localhost:8080/auth
+admin_realm: master
+admin_user: admin
+admin_password: password
+realm: master
+client_id: authz
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_permission/aliases b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_permission/aliases
new file mode 100644
index 000000000..e1f8d6b4b
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_permission/aliases
@@ -0,0 +1,6 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+unsupported
+keycloak_authz_permission_info
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_permission/readme.adoc b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_permission/readme.adoc
new file mode 100644
index 000000000..8e052920c
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_permission/readme.adoc
@@ -0,0 +1,27 @@
+// Copyright (c) Ansible Project
+// GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+To be able to run these integration tests a keycloak server must be
+reachable under a specific url with a specific admin user and password.
+The exact values expected for these parameters can be found in
+'vars/main.yml' file. A simple way to do this is to use the official
+keycloak docker images like this:
+
+----
+docker run --name mykeycloak -p 8080:8080 -e KC_HTTP_RELATIVE_PATH=<url-path> -e KEYCLOAK_ADMIN=<admin_user> -e KEYCLOAK_ADMIN_PASSWORD=<admin_password> quay.io/keycloak/keycloak:20.0.2 start-dev
+----
+
+Example with concrete values inserted:
+
+----
+docker run --name mykeycloak -p 8080:8080 -e KC_HTTP_RELATIVE_PATH=/auth -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=password quay.io/keycloak/keycloak:20.0.2 start-dev
+----
+
+This test suite can run against a fresh unconfigured server instance
+(no preconfiguration required) and cleans up after itself (undoes all
+its config changes) as long as it runs through completely. While its active
+it changes the server configuration in the following ways:
+
+ * creating, modifying and deleting some keycloak groups
+
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_permission/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_permission/tasks/main.yml
new file mode 100644
index 000000000..16cb6806f
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_permission/tasks/main.yml
@@ -0,0 +1,567 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+- name: Remove keycloak client to avoid failures from previous failed runs
+ community.general.keycloak_client:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ state: absent
+
+- name: Create keycloak client with authorization services enabled
+ community.general.keycloak_client:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ state: present
+ enabled: true
+ public_client: false
+ service_accounts_enabled: true
+ authorization_services_enabled: true
+
+- name: Create file:create authorization scope
+ community.general.keycloak_authz_authorization_scope:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: present
+ name: "file:create"
+ display_name: "File create"
+ icon_uri: "http://localhost/icon.png"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Create file:delete authorization scope
+ community.general.keycloak_authz_authorization_scope:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: present
+ name: "file:delete"
+ display_name: "File delete"
+ icon_uri: "http://localhost/icon.png"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Create permission without type (test for failure)
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: present
+ name: "ScopePermission"
+ description: "Scope permission"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+ failed_when: result.msg.find('missing required arguments') == -1
+
+- name: Create scope permission without scopes (test for failure)
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: present
+ name: "ScopePermission"
+ description: "Scope permission"
+ permission_type: scope
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+ failed_when: result.msg.find('Scopes need to defined when permission type is set to scope!') == -1
+
+- name: Create scope permission with multiple resources (test for failure)
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: present
+ name: "ScopePermission"
+ description: "Scope permission"
+ resources:
+ - "Default Resource"
+ - "Other Resource"
+ permission_type: scope
+ scopes:
+ - "file:delete"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+ failed_when: result.msg.find('Only one resource can be defined for a scope permission!') == -1
+
+- name: Create scope permission with invalid policy name (test for failure)
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: present
+ name: "ScopePermission"
+ description: "Scope permission"
+ permission_type: scope
+ scopes:
+ - "file:delete"
+ policies:
+ - "Missing Policy"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+ failed_when: result.msg.find('Unable to find authorization policy with name') == -1
+
+- name: Create scope permission
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: present
+ name: "ScopePermission"
+ description: "Scope permission"
+ permission_type: scope
+ scopes:
+ - "file:delete"
+ policies:
+ - "Default Policy"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that scope permission was created
+ assert:
+ that:
+ - result is changed
+ - result.end_state != {}
+ - result.end_state.name == "ScopePermission"
+ - result.end_state.description == "Scope permission"
+ - result.end_state.type == "scope"
+ - result.end_state.resources == []
+ - result.end_state.policies|length == 1
+ - result.end_state.scopes|length == 1
+
+- name: Query state
+ community.general.keycloak_authz_permission_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "ScopePermission"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that queried state matches desired end state
+ assert:
+ that:
+ - result.queried_state.name == "ScopePermission"
+ - result.queried_state.description == "Scope permission"
+
+- name: Create scope permission (test for idempotency)
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: present
+ name: "ScopePermission"
+ description: "Scope permission"
+ permission_type: scope
+ scopes:
+ - "file:delete"
+ policies:
+ - "Default Policy"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that nothing changed
+ assert:
+ that:
+ - result.end_state != {}
+ - result.end_state.name == "ScopePermission"
+ - result.end_state.description == "Scope permission"
+ - result.end_state.type == "scope"
+ - result.end_state.resources == []
+ - result.end_state.policies|length == 1
+ - result.end_state.scopes|length == 1
+
+- name: Query state
+ community.general.keycloak_authz_permission_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "ScopePermission"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that queried state matches desired end state
+ assert:
+ that:
+ - result.queried_state.name == "ScopePermission"
+ - result.queried_state.description == "Scope permission"
+
+- name: Update scope permission
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: present
+ name: "ScopePermission"
+ description: "Scope permission changed"
+ permission_type: scope
+ decision_strategy: 'AFFIRMATIVE'
+ scopes:
+ - "file:create"
+ - "file:delete"
+ policies: []
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that scope permission was updated correctly
+ assert:
+ that:
+ - result.changed == True
+ - result.end_state != {}
+ - result.end_state.scopes|length == 2
+ - result.end_state.policies == []
+ - result.end_state.resources == []
+ - result.end_state.name == "ScopePermission"
+ - result.end_state.description == "Scope permission changed"
+
+- name: Query state
+ community.general.keycloak_authz_permission_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "ScopePermission"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that queried state matches desired end state
+ assert:
+ that:
+ - result.queried_state.name == "ScopePermission"
+ - result.queried_state.description == "Scope permission changed"
+
+- name: Update scope permission (test for idempotency)
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: present
+ name: "ScopePermission"
+ description: "Scope permission changed"
+ permission_type: scope
+ decision_strategy: 'AFFIRMATIVE'
+ scopes:
+ - "file:create"
+ - "file:delete"
+ policies: []
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that nothing changed
+ assert:
+ that:
+ - result.changed == True
+ - result.end_state != {}
+ - result.end_state.scopes|length == 2
+ - result.end_state.policies == []
+ - result.end_state.resources == []
+ - result.end_state.name == "ScopePermission"
+ - result.end_state.description == "Scope permission changed"
+
+- name: Query state
+ community.general.keycloak_authz_permission_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "ScopePermission"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that queried state matches desired end state
+ assert:
+ that:
+ - result.queried_state.name == "ScopePermission"
+ - result.queried_state.description == "Scope permission changed"
+
+- name: Remove scope permission
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: absent
+ name: "ScopePermission"
+ permission_type: scope
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that scope permission was removed
+ assert:
+ that:
+ - result is changed
+ - result.end_state == {}
+
+- name: Remove scope permission (test for idempotency)
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: absent
+ name: "ScopePermission"
+ permission_type: scope
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that nothing has changed
+ assert:
+ that:
+ - result is not changed
+ - result.end_state == {}
+
+- name: Create resource permission without resources (test for failure)
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: present
+ name: "ResourcePermission"
+ description: "Resource permission"
+ permission_type: resource
+ policies:
+ - "Default Policy"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+ failed_when: result.msg.find('A resource need to defined when permission type is set to resource!') == -1
+
+- name: Create resource permission with scopes (test for failure)
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: present
+ name: "ResourcePermission"
+ description: "Resource permission"
+ permission_type: resource
+ resources:
+ - "Default Resource"
+ policies:
+ - "Default Policy"
+ scopes:
+ - "file:delete"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+ failed_when: result.msg.find('Scopes cannot be defined when permission type is set to resource!') == -1
+
+- name: Create resource permission
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: present
+ name: "ResourcePermission"
+ description: "Resource permission"
+ resources:
+ - "Default Resource"
+ permission_type: resource
+ policies:
+ - "Default Policy"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that resource permission was created
+ assert:
+ that:
+ - result is changed
+ - result.end_state != {}
+ - result.end_state.policies|length == 1
+ - result.end_state.resources|length == 1
+ - result.end_state.name == "ResourcePermission"
+ - result.end_state.description == "Resource permission"
+
+- name: Query state
+ community.general.keycloak_authz_permission_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "ResourcePermission"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that queried state matches desired end state
+ assert:
+ that:
+ - result.queried_state.name == "ResourcePermission"
+ - result.queried_state.description == "Resource permission"
+
+- name: Create resource permission (test for idempotency)
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: present
+ name: "ResourcePermission"
+ description: "Resource permission"
+ resources:
+ - "Default Resource"
+ permission_type: resource
+ policies:
+ - "Default Policy"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that nothing has changed
+ assert:
+ that:
+ - result.end_state != {}
+ - result.end_state.policies|length == 1
+ - result.end_state.resources|length == 1
+ - result.end_state.name == "ResourcePermission"
+ - result.end_state.description == "Resource permission"
+
+- name: Query state
+ community.general.keycloak_authz_permission_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "ResourcePermission"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that queried state matches desired end state
+ assert:
+ that:
+ - result.queried_state.name == "ResourcePermission"
+ - result.queried_state.description == "Resource permission"
+
+- name: Update resource permission
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: present
+ name: "ResourcePermission"
+ description: "Resource permission changed"
+ resources:
+ - "Default Resource"
+ permission_type: resource
+ policies: []
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that resource permission was updated correctly
+ assert:
+ that:
+ - result.changed == True
+ - result.end_state != {}
+ - result.end_state.policies == []
+ - result.end_state.resources|length == 1
+ - result.end_state.name == "ResourcePermission"
+ - result.end_state.description == "Resource permission changed"
+
+- name: Query state
+ community.general.keycloak_authz_permission_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "ResourcePermission"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that queried state matches desired end state
+ assert:
+ that:
+ - result.queried_state.name == "ResourcePermission"
+ - result.queried_state.description == "Resource permission changed"
+
+- name: Remove resource permission
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: absent
+ name: "ResourcePermission"
+ permission_type: resource
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that resource permission was removed
+ assert:
+ that:
+ - result is changed
+ - result.end_state == {}
+
+- name: Remove resource permission (test for idempotency)
+ community.general.keycloak_authz_permission:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ state: absent
+ name: "ResourcePermission"
+ permission_type: resource
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert that nothing has changed
+ assert:
+ that:
+ - result is not changed
+ - result.end_state == {}
+
+- name: Remove keycloak client
+ community.general.keycloak_client:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ state: absent
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_authz_permission/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_permission/vars/main.yml
new file mode 100644
index 000000000..c1d5fc983
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_authz_permission/vars/main.yml
@@ -0,0 +1,11 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+url: http://localhost:8080/auth
+admin_realm: master
+admin_user: admin
+admin_password: password
+realm: master
+client_id: authz
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_client/README.md b/ansible_collections/community/general/tests/integration/targets/keycloak_client/README.md
index d8bcc08ec..f2b1012aa 100644
--- a/ansible_collections/community/general/tests/integration/targets/keycloak_client/README.md
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_client/README.md
@@ -4,14 +4,16 @@ GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://w
SPDX-License-Identifier: GPL-3.0-or-later
-->
-The integration test can be performed as follows:
+# Running keycloak_client module integration test
-```
-# 1. Start docker-compose:
-docker-compose -f tests/integration/targets/keycloak_client/docker-compose.yml stop
-docker-compose -f tests/integration/targets/keycloak_client/docker-compose.yml rm -f -v
-docker-compose -f tests/integration/targets/keycloak_client/docker-compose.yml up -d
+To run Keycloak client module's integration test, start a keycloak server using Docker:
-# 2. Run the integration tests:
-ansible-test integration keycloak_client --allow-unsupported -v
-```
+ docker run -d --rm --name mykeycloak -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=password quay.io/keycloak/keycloak:latest start-dev --http-relative-path /auth
+
+Run the integration tests:
+
+ ansible-test integration -v keycloak_client --allow-unsupported --docker fedora35 --docker-network host
+
+Cleanup:
+
+ docker stop mykeycloak
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_client/docker-compose.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_client/docker-compose.yml
deleted file mode 100644
index 5e14e9aac..000000000
--- a/ansible_collections/community/general/tests/integration/targets/keycloak_client/docker-compose.yml
+++ /dev/null
@@ -1,31 +0,0 @@
----
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-version: '3.4'
-
-services:
- postgres:
- image: postgres:9.6
- restart: always
- environment:
- POSTGRES_USER: postgres
- POSTGRES_DB: postgres
- POSTGRES_PASSWORD: postgres
-
- keycloak:
- image: jboss/keycloak:12.0.4
- ports:
- - 8080:8080
-
- environment:
- DB_VENDOR: postgres
- DB_ADDR: postgres
- DB_DATABASE: postgres
- DB_USER: postgres
- DB_SCHEMA: public
- DB_PASSWORD: postgres
-
- KEYCLOAK_USER: admin
- KEYCLOAK_PASSWORD: password
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_client/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_client/tasks/main.yml
index 513d5836b..5e7c7fae3 100644
--- a/ansible_collections/community/general/tests/integration/targets/keycloak_client/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_client/tasks/main.yml
@@ -2,58 +2,78 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
+- name: Wait for Keycloak
+ uri:
+ url: "{{ url }}/admin/"
+ status_code: 200
+ validate_certs: no
+ register: result
+ until: result.status == 200
+ retries: 10
+ delay: 10
- name: Delete realm
- community.general.keycloak_realm: "{{ auth_args | combine(call_args) }}"
- vars:
- call_args:
- id: "{{ realm }}"
- realm: "{{ realm }}"
- state: absent
+ community.general.keycloak_realm:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ id: "{{ realm }}"
+ realm: "{{ realm }}"
+ state: absent
- name: Create realm
- community.general.keycloak_realm: "{{ auth_args | combine(call_args) }}"
- vars:
- call_args:
- id: "{{ realm }}"
- realm: "{{ realm }}"
- state: present
+ community.general.keycloak_realm:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ id: "{{ realm }}"
+ realm: "{{ realm }}"
+ state: present
- name: Desire client
- community.general.keycloak_client: "{{ auth_args | combine(call_args) }}"
- vars:
- call_args:
- realm: "{{ realm }}"
- client_id: "{{ client_id }}"
- state: present
- redirect_uris: '{{redirect_uris1}}'
- attributes: '{{client_attributes1}}'
- protocol_mappers: '{{protocol_mappers1}}'
+ community.general.keycloak_client:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ state: present
+ redirect_uris: '{{redirect_uris1}}'
+ attributes: '{{client_attributes1}}'
+ protocol_mappers: '{{protocol_mappers1}}'
register: desire_client_not_present
- name: Desire client again with same props
- community.general.keycloak_client: "{{ auth_args | combine(call_args) }}"
- vars:
- call_args:
- realm: "{{ realm }}"
- client_id: "{{ client_id }}"
- state: present
- redirect_uris: '{{redirect_uris1}}'
- attributes: '{{client_attributes1}}'
- protocol_mappers: '{{protocol_mappers1}}'
+ community.general.keycloak_client:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ state: present
+ redirect_uris: '{{redirect_uris1}}'
+ attributes: '{{client_attributes1}}'
+ protocol_mappers: '{{protocol_mappers1}}'
register: desire_client_when_present_and_same
- name: Check client again with same props
- community.general.keycloak_client: "{{ auth_args | combine(call_args) }}"
+ community.general.keycloak_client:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ state: present
+ redirect_uris: '{{redirect_uris1}}'
+ attributes: '{{client_attributes1}}'
+ protocol_mappers: '{{protocol_mappers1}}'
+ authorization_services_enabled: False
check_mode: true
- vars:
- call_args:
- realm: "{{ realm }}"
- client_id: "{{ client_id }}"
- state: present
- redirect_uris: '{{redirect_uris1}}'
- attributes: '{{client_attributes1}}'
- protocol_mappers: '{{protocol_mappers1}}'
register: check_client_when_present_and_same
- name: Assert changes not detected in last two tasks (desire when same, and check)
@@ -61,3 +81,25 @@
that:
- desire_client_when_present_and_same is not changed
- check_client_when_present_and_same is not changed
+
+- name: Check client again with changed props
+ community.general.keycloak_client:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ state: present
+ redirect_uris: '{{redirect_uris1}}'
+ attributes: '{{client_attributes1}}'
+ protocol_mappers: '{{protocol_mappers1}}'
+ authorization_services_enabled: False
+ service_accounts_enabled: True
+ check_mode: true
+ register: check_client_when_present_and_changed
+
+- name: Assert changes detected in last tasks
+ assert:
+ that:
+ - check_client_when_present_and_changed is changed
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_client/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_client/vars/main.yml
index 53ba35fca..498f93e70 100644
--- a/ansible_collections/community/general/tests/integration/targets/keycloak_client/vars/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_client/vars/main.yml
@@ -24,7 +24,7 @@ redirect_uris1:
- "http://example.b.com/"
- "http://example.a.com/"
-client_attributes1: {"backchannel.logout.session.required": true, "backchannel.logout.revoke.offline.tokens": false}
+client_attributes1: {"backchannel.logout.session.required": true, "backchannel.logout.revoke.offline.tokens": false, "client.secret.creation.time": 0}
protocol_mappers1:
- name: 'email'
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_component_info/README.md b/ansible_collections/community/general/tests/integration/targets/keycloak_component_info/README.md
new file mode 100644
index 000000000..cf4f222b0
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_component_info/README.md
@@ -0,0 +1,20 @@
+<!--
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
+-->
+# Running keycloak_component_info module integration test
+
+To run Keycloak component info module's integration test, start a keycloak server using Docker:
+
+ docker run -d --rm --name myldap -p 389:389 minkwe/389ds:latest
+ docker run -d --rm --name mykeycloak --link myldap:ldap.example.com -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=password quay.io/keycloak/keycloak:latest start-dev --http-relative-path /auth
+
+Run integration tests:
+ ansible-test integration -v keycloak_component_info --allow-unsupported --docker fedora35 --docker-network host
+
+Cleanup:
+
+ docker stop myldap mykeycloak
+
+
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_component_info/aliases b/ansible_collections/community/general/tests/integration/targets/keycloak_component_info/aliases
new file mode 100644
index 000000000..bd1f02444
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_component_info/aliases
@@ -0,0 +1,5 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+unsupported
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_component_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_component_info/tasks/main.yml
new file mode 100644
index 000000000..c0ca5600f
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_component_info/tasks/main.yml
@@ -0,0 +1,266 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+- name: Wait for Keycloak
+ uri:
+ url: "{{ url }}/admin/"
+ status_code: 200
+ validate_certs: no
+ register: result
+ until: result.status == 200
+ retries: 10
+ delay: 10
+
+- name: Delete realm if exists
+ community.general.keycloak_realm:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ state: absent
+
+- name: Create realm
+ community.general.keycloak_realm:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ id: "{{ realm }}"
+ realm: "{{ realm }}"
+ state: present
+
+- name: Retrive ldap info when absent
+ community.general.keycloak_component_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "{{ federation }}"
+ provider_type: "org.keycloak.storage.UserStorageProvider"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert ldap is missing
+ assert:
+ that:
+ - result is not changed
+ - result.components | length == 0
+
+- name: Create new user federation
+ community.general.keycloak_user_federation:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ name: "{{ federation }}"
+ state: present
+ provider_id: ldap
+ provider_type: org.keycloak.storage.UserStorageProvider
+ config:
+ enabled: true
+ priority: 0
+ fullSyncPeriod: -1
+ changedSyncPeriod: -1
+ cachePolicy: DEFAULT
+ batchSizeForSync: 1000
+ editMode: READ_ONLY
+ importEnabled: true
+ syncRegistrations: false
+ vendor: other
+ usernameLDAPAttribute: uid
+ rdnLDAPAttribute: uid
+ uuidLDAPAttribute: entryUUID
+ userObjectClasses: "inetOrgPerson, organizationalPerson"
+ connectionUrl: "ldap://ldap.example.com"
+ usersDn: "ou=Users,dc=example,dc=com"
+ authType: simple
+ bindDn: cn=directory reader
+ bindCredential: secret
+ searchScope: 1
+ validatePasswordPolicy: false
+ trustEmail: false
+ useTruststoreSpi: "ldapsOnly"
+ connectionPooling: true
+ pagination: true
+ allowKerberosAuthentication: false
+ useKerberosForPasswordAuthentication: false
+ debug: false
+
+- name: Retrive ldap info
+ community.general.keycloak_component_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "{{ federation }}"
+ provider_type: "org.keycloak.storage.UserStorageProvider"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert ldap exists
+ assert:
+ that:
+ - result is not changed
+ - result.components | length == 1
+ - result.components[0].name == federation
+
+- name: Save ldap id
+ set_fact:
+ myLdapId: "{{ result.components[0].id }}"
+
+- name: Retrive ldap subcomponents info
+ community.general.keycloak_component_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ parent_id: "{{ myLdapId }}"
+ register: result
+
+- name: Assert components exists
+ assert:
+ that:
+ - result is not changed
+ - result.components | length > 0
+
+- name: Retrive ldap subcomponents filter by name
+ community.general.keycloak_component_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ parent_id: "{{ myLdapId }}"
+ name: "email"
+ register: result
+
+- name: Assert sub component with name "email" exists
+ assert:
+ that:
+ - result is not changed
+ - result.components | length == 1
+ - result.components[0].name == "email"
+
+- name: Retrive ldap subcomponents filter by type
+ community.general.keycloak_component_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ parent_id: "{{ myLdapId }}"
+ provider_type: "org.keycloak.storage.ldap.mappers.LDAPStorageMapper"
+ register: result
+
+- name: Assert ldap sub components filter by type
+ assert:
+ that:
+ - result is not changed
+ - result.components | length > 0
+ - result.components[0].providerType == "org.keycloak.storage.ldap.mappers.LDAPStorageMapper"
+
+- name: Retrive key info when absent
+ community.general.keycloak_component_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "{{ realm_key_name }}"
+ provider_type: "org.keycloak.keys.KeyProvider"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert key is missing
+ assert:
+ that:
+ - result is not changed
+ - result.components | length == 0
+
+- name: Create custom realm key
+ community.general.keycloak_realm_key:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "{{ realm_key_name }}"
+ state: present
+ parent_id: "{{ realm }}"
+ config:
+ private_key: "{{ realm_private_key }}"
+ certificate: ""
+ enabled: true
+ active: true
+ priority: 150
+ register: result
+
+- name: Retrive key info
+ community.general.keycloak_component_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "{{ realm_key_name }}"
+ provider_type: "org.keycloak.keys.KeyProvider"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert key exists
+ assert:
+ that:
+ - result is not changed
+ - result.components | length == 1
+
+- name: Retrive all realm components
+ community.general.keycloak_component_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ register: result
+
+- name: Assert key exists
+ assert:
+ that:
+ - result is not changed
+ - result.components | length > 0
+
+- name: Retrive all ldap in realm
+ community.general.keycloak_component_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ provider_type: "org.keycloak.storage.UserStorageProvider"
+ register: result
+
+- name: Assert key exists
+ assert:
+ that:
+ - result is not changed
+ - result.components | length == 1
+ - result.components[0].providerType == "org.keycloak.storage.UserStorageProvider"
+ - result.components[0].name == "myldap"
+
+- name: Retrive component by name only
+ community.general.keycloak_component_info:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ name: "{{ realm_key_name }}"
+ register: result
+
+- name: Assert key exists
+ assert:
+ that:
+ - result is not changed
+ - result.components | length == 1
+ - result.components[0].providerType == "org.keycloak.keys.KeyProvider"
+ - result.components[0].name == realm_key_name
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_component_info/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_component_info/vars/main.yml
new file mode 100644
index 000000000..7f18d8459
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_component_info/vars/main.yml
@@ -0,0 +1,19 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+url: http://localhost:8080/auth
+admin_realm: master
+admin_user: admin
+admin_password: password
+realm: myrealm
+
+federation: myldap
+
+
+realm_key_name: testkey
+realm_private_key: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC9Mi7IKXPhqGiWGwgEYEXnqc8nytG1pHbC6QYZe1gUa43jDtGYQln36It02BGw4e5XydCUj+M26X2sH+kKaV+KHEnJtcEqdAuVX1QaDVzeiOoo1/B9HC8By6NZBsOSdxpat3EvilQ+R7NP9yi53J08+vfeZSEGyPmKV1me7nJnRh3/zcRsOi92GTsBd7gApKfP8sorDjY8m9NRuPLwleK2nh/aRvj1yK8x3UAqUIbOCVaE39bSN6VUTFK2Q/+MX3vF0Zugsk7PKKmfqcEW6wj7dtSElbX4uhrfTkGMmwIWdIiLDNRA/jVRvGxUB1SyMy6kmMC8jC2QGWpZgfkSKtHlAgMBAAECggEACWkSVh7ntmjtwM+Z47vVJkt2NBS8vxPt206DYOeXbzaVUV6mkrP0LSZKL3bi1GE8fW3am9UXWF8fQt04dm3c1G4JRojtkXrBq72Y3Y3eGWyGdx8chWCOPwDdwFsbhbC6ZRo8PUDcZVekJd1Vj38XbBXQl+WAUcnTzauAF+1kz9mhJq1gpglIbB+8l7VjMXwXeaGWJQ5OL/MSsq7r3P1elVjHwprFBM7HHA5+RTu/KY/GcEutgm5uwTRqRZNC1IBXAQtBO7HQJbuLqDPTQ3RRCPEur8R+0dk5bF+8IyzQ8Bh+Dhuou9xzfS/A7lV6L/CZSpv4Bvq1H3Uxk+orXf2Q2QKBgQDBOf1nSJB0VgQdIcdtgVpVgQ2SyWAd+N8Qk7QsyVQf9f7ZqiFLejWJbaaeY9WtfZ01D8tgHJfPqsO1/Jux255mtkyk2K2c6dav1Lsd4l+iPfidsDJNWkcd59nQqwC9BLjzWK/J4rO20apm34abLaZ9oVk8Mgz8VWJWOxTgCr+COQKBgQD6qP1lm6rzlCSIEz9eCuGPkQkVo+NIP437e3i+sxtkLlMgnmfzSwSJdVF8AKH3gXi3NyWjfBVYeAZEkm1kHF8IWOiK4U1y95Vx3uud3NX4SC+cjePc+pDPQJiz9L+zq9I6WFZWmm7n/9heTxu/l0vxI4FHaBmt95BMwLJNkzbdDQKBgCHGwUUMqjOr1YxCG1pJAkFwDa9bBDI5DsUXDKfHia0Mkz/5PVi0RCeBw15slS1+h7x+xk5GsULb1to5Df5JJadOtpcaST7koWKbDRpsN8tkidEGu8RJw6S2opyXR8nCyZHALvpbZo7Ol7rj1+PIVxIe4jpjhWGWi1oHed6wAkoBAoGAJx2F5XxEUhx1EvMF+XPzPQciBsl7Z0PbsTnUXtXuWVTNThLKH/I99AFlxNcIb2o530VwzzFG13Zra/n5rhyrS88sArgj8OPn40wpMopKraL+Iw0VWN+VB3KKIdL4s14FwWsVlhAlbHjFV/o6V0yR4kBrJSx+jWJLl16etHJbpmUCgYBUWCQwcT1aw9XHWJXiNYTnQSYg88hgGYhts1qSzhfu+n1t2BlAlxM0gu2+gez21mM8uiYsqbU2OZeG2U4as6kdai8Q4tzNQt2f1r3ZewJN/QHrkx6FT94PNa0w4ILiQ9Eu7xssaHcYjHyrI1NlbMKypVy6waDG2ajLOFAVeHGpOg==
+ -----END PRIVATE KEY-----
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_group/readme.adoc b/ansible_collections/community/general/tests/integration/targets/keycloak_group/readme.adoc
index 1941e54ef..8e052920c 100644
--- a/ansible_collections/community/general/tests/integration/targets/keycloak_group/readme.adoc
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_group/readme.adoc
@@ -20,7 +20,7 @@ docker run --name mykeycloak -p 8080:8080 -e KC_HTTP_RELATIVE_PATH=/auth -e KEYC
This test suite can run against a fresh unconfigured server instance
(no preconfiguration required) and cleans up after itself (undoes all
-its config changes) as long as it runs through completly. While its active
+its config changes) as long as it runs through completely. While its active
it changes the server configuration in the following ways:
* creating, modifying and deleting some keycloak groups
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_group_rolemapping/README.md b/ansible_collections/community/general/tests/integration/targets/keycloak_group_rolemapping/README.md
new file mode 100644
index 000000000..db58acb7b
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_group_rolemapping/README.md
@@ -0,0 +1,21 @@
+<!--
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
+-->
+
+# `keycloak_group_rolemapping` Integration Tests
+
+## Test Server
+
+Prepare a development server, tested with Keycloak versions tagged 22.0 and 23.0:
+
+```sh
+docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=password --rm quay.io/keycloak/keycloak:22.0 start-dev
+```
+
+## Run Tests
+
+```sh
+ansible localhost --module-name include_role --args name=keycloak_group_rolemapping
+```
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_group_rolemapping/aliases b/ansible_collections/community/general/tests/integration/targets/keycloak_group_rolemapping/aliases
new file mode 100644
index 000000000..9e2cd0dc4
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_group_rolemapping/aliases
@@ -0,0 +1,4 @@
+# Copyright (c) 2023, Alexander Groß (@agross)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+unsupported
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_group_rolemapping/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_group_rolemapping/tasks/main.yml
new file mode 100644
index 000000000..f1e6371e2
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_group_rolemapping/tasks/main.yml
@@ -0,0 +1,160 @@
+# Copyright (c) 2023, Alexander Groß (@agross)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Create realm
+ community.general.keycloak_realm:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+
+ id: "{{ realm }}"
+ realm: "{{ realm }}"
+ state: present
+
+- name: Create realm roles
+ community.general.keycloak_role:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+
+ realm: "{{ realm }}"
+ name: "{{ item }}"
+ state: present
+ loop:
+ - "{{ role_1 }}"
+ - "{{ role_2 }}"
+
+- name: Create group
+ community.general.keycloak_group:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+
+ realm: "{{ realm }}"
+ name: "{{ group }}"
+ state: present
+
+- name: Map realm roles to group
+ community.general.keycloak_realm_rolemapping:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+
+ realm: "{{ realm }}"
+ group_name: "{{ group }}"
+ roles:
+ - name: "{{ role_1 }}"
+ - name: "{{ role_2 }}"
+ state: present
+ register: result
+
+- name: Assert realm roles are assigned to group
+ ansible.builtin.assert:
+ that:
+ - result is changed
+ - result.end_state | count == 2
+
+- name: Map realm roles to group again (idempotency)
+ community.general.keycloak_realm_rolemapping:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+
+ realm: "{{ realm }}"
+ group_name: "{{ group }}"
+ roles:
+ - name: "{{ role_1 }}"
+ - name: "{{ role_2 }}"
+ state: present
+ register: result
+
+- name: Assert realm roles stay assigned to group
+ ansible.builtin.assert:
+ that:
+ - result is not changed
+
+- name: Unmap realm role 1 from group
+ community.general.keycloak_realm_rolemapping:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+
+ realm: "{{ realm }}"
+ group_name: "{{ group }}"
+ roles:
+ - name: "{{ role_1 }}"
+ state: absent
+ register: result
+
+- name: Assert realm role 1 is unassigned from group
+ ansible.builtin.assert:
+ that:
+ - result is changed
+ - result.end_state | count == 1
+ - result.end_state[0] == role_2
+
+- name: Unmap realm role 1 from group again (idempotency)
+ community.general.keycloak_realm_rolemapping:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+
+ realm: "{{ realm }}"
+ group_name: "{{ group }}"
+ roles:
+ - name: "{{ role_1 }}"
+ state: absent
+ register: result
+
+- name: Assert realm role 1 stays unassigned from group
+ ansible.builtin.assert:
+ that:
+ - result is not changed
+
+- name: Unmap realm role 2 from group
+ community.general.keycloak_realm_rolemapping:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+
+ realm: "{{ realm }}"
+ group_name: "{{ group }}"
+ roles:
+ - name: "{{ role_2 }}"
+ state: absent
+ register: result
+
+- name: Assert no realm roles are assigned to group
+ ansible.builtin.assert:
+ that:
+ - result is changed
+ - result.end_state | count == 0
+
+- name: Unmap realm role 2 from group again (idempotency)
+ community.general.keycloak_realm_rolemapping:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+
+ realm: "{{ realm }}"
+ group_name: "{{ group }}"
+ roles:
+ - name: "{{ role_2 }}"
+ state: absent
+ register: result
+
+- name: Assert no realm roles are assigned to group
+ ansible.builtin.assert:
+ that:
+ - result is not changed
+ - result.end_state | count == 0
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_group_rolemapping/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_group_rolemapping/vars/main.yml
new file mode 100644
index 000000000..0848499e7
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_group_rolemapping/vars/main.yml
@@ -0,0 +1,15 @@
+---
+# Copyright (c) 2023, Alexander Groß (@agross)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+url: http://localhost:8080
+admin_realm: master
+admin_user: admin
+admin_password: password
+realm: myrealm
+
+role_1: myrole-1
+role_2: myrole-2
+
+group: mygroup
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/tasks/main.yml
index 79ba33049..afad9740e 100644
--- a/ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_identity_provider/tasks/main.yml
@@ -35,14 +35,14 @@
syncMode: FORCE
mappers:
- name: "first_name"
- identityProviderAlias: "oidc-idp"
+ identityProviderAlias: "{{ idp }}"
identityProviderMapper: "oidc-user-attribute-idp-mapper"
config:
claim: "first_name"
user.attribute: "first_name"
syncMode: "INHERIT"
- name: "last_name"
- identityProviderAlias: "oidc-idp"
+ identityProviderAlias: "{{ idp }}"
identityProviderMapper: "oidc-user-attribute-idp-mapper"
config:
claim: "last_name"
@@ -84,14 +84,14 @@
syncMode: FORCE
mappers:
- name: "first_name"
- identityProviderAlias: "oidc-idp"
+ identityProviderAlias: "{{ idp }}"
identityProviderMapper: "oidc-user-attribute-idp-mapper"
config:
claim: "first_name"
user.attribute: "first_name"
syncMode: "INHERIT"
- name: "last_name"
- identityProviderAlias: "oidc-idp"
+ identityProviderAlias: "{{ idp }}"
identityProviderMapper: "oidc-user-attribute-idp-mapper"
config:
claim: "last_name"
@@ -109,7 +109,7 @@
that:
- result is not changed
-- name: Update existing identity provider (with change)
+- name: Update existing identity provider (with change, no mapper change)
community.general.keycloak_identity_provider:
auth_keycloak_url: "{{ url }}"
auth_realm: "{{ admin_realm }}"
@@ -132,6 +132,109 @@
- result.existing.enabled == true
- result.end_state.enabled == false
+- name: Update existing identity provider (delete mapper)
+ community.general.keycloak_identity_provider:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ alias: "{{ idp }}"
+ state: present
+ mappers:
+ - name: "first_name"
+ identityProviderAlias: "{{ idp }}"
+ identityProviderMapper: "oidc-user-attribute-idp-mapper"
+ config:
+ claim: "first_name"
+ user.attribute: "first_name"
+ syncMode: "INHERIT"
+ register: result
+
+- name: Debug
+ debug:
+ var: result
+
+- name: Assert identity provider updated
+ assert:
+ that:
+ - result is changed
+ - result.existing.mappers | length == 2
+ - result.end_state.mappers | length == 1
+ - result.end_state.mappers[0].name == "first_name"
+
+- name: Update existing identity provider (add mapper)
+ community.general.keycloak_identity_provider:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ alias: "{{ idp }}"
+ state: present
+ mappers:
+ - name: "last_name"
+ identityProviderAlias: "{{ idp }}"
+ identityProviderMapper: "oidc-user-attribute-idp-mapper"
+ config:
+ claim: "last_name"
+ user.attribute: "last_name"
+ syncMode: "INHERIT"
+ - name: "first_name"
+ identityProviderAlias: "{{ idp }}"
+ identityProviderMapper: "oidc-user-attribute-idp-mapper"
+ config:
+ claim: "first_name"
+ user.attribute: "first_name"
+ syncMode: "INHERIT"
+ register: result
+
+- name: Debug
+ debug:
+ var: result
+
+- name: Assert identity provider updated
+ assert:
+ that:
+ - result is changed
+ - result.existing.mappers | length == 1
+ - result.end_state.mappers | length == 2
+
+- name: Update existing identity provider (no change, test mapper idempotency)
+ community.general.keycloak_identity_provider:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ alias: "{{ idp }}"
+ state: present
+ mappers:
+ - name: "last_name"
+ identityProviderAlias: "{{ idp }}"
+ identityProviderMapper: "oidc-user-attribute-idp-mapper"
+ config:
+ claim: "last_name"
+ user.attribute: "last_name"
+ syncMode: "INHERIT"
+ - name: "first_name"
+ identityProviderAlias: "{{ idp }}"
+ identityProviderMapper: "oidc-user-attribute-idp-mapper"
+ config:
+ claim: "first_name"
+ user.attribute: "first_name"
+ syncMode: "INHERIT"
+ register: result
+
+- name: Debug
+ debug:
+ var: result
+
+- name: Assert identity provider updated
+ assert:
+ that:
+ - result is not changed
+
- name: Delete existing identity provider
community.general.keycloak_identity_provider:
auth_keycloak_url: "{{ url }}"
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_realm_key/aliases b/ansible_collections/community/general/tests/integration/targets/keycloak_realm_key/aliases
new file mode 100644
index 000000000..bd1f02444
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_realm_key/aliases
@@ -0,0 +1,5 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+unsupported
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_realm_key/readme.adoc b/ansible_collections/community/general/tests/integration/targets/keycloak_realm_key/readme.adoc
new file mode 100644
index 000000000..8e052920c
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_realm_key/readme.adoc
@@ -0,0 +1,27 @@
+// Copyright (c) Ansible Project
+// GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+To be able to run these integration tests a keycloak server must be
+reachable under a specific url with a specific admin user and password.
+The exact values expected for these parameters can be found in
+'vars/main.yml' file. A simple way to do this is to use the official
+keycloak docker images like this:
+
+----
+docker run --name mykeycloak -p 8080:8080 -e KC_HTTP_RELATIVE_PATH=<url-path> -e KEYCLOAK_ADMIN=<admin_user> -e KEYCLOAK_ADMIN_PASSWORD=<admin_password> quay.io/keycloak/keycloak:20.0.2 start-dev
+----
+
+Example with concrete values inserted:
+
+----
+docker run --name mykeycloak -p 8080:8080 -e KC_HTTP_RELATIVE_PATH=/auth -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=password quay.io/keycloak/keycloak:20.0.2 start-dev
+----
+
+This test suite can run against a fresh unconfigured server instance
+(no preconfiguration required) and cleans up after itself (undoes all
+its config changes) as long as it runs through completely. While its active
+it changes the server configuration in the following ways:
+
+ * creating, modifying and deleting some keycloak groups
+
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_realm_key/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_realm_key/tasks/main.yml
new file mode 100644
index 000000000..c02950600
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_realm_key/tasks/main.yml
@@ -0,0 +1,373 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+- name: Remove Keycloak test realm to avoid failures from previous failed runs
+ community.general.keycloak_realm:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ id: "{{ realm }}"
+ state: absent
+
+- name: Create Keycloak test realm
+ community.general.keycloak_realm:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ id: "{{ realm }}"
+ state: present
+
+- name: Create custom realm key (check mode)
+ community.general.keycloak_realm_key:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: testkey
+ state: present
+ parent_id: "{{ realm }}"
+ config:
+ private_key: "{{ realm_private_key }}"
+ certificate: ""
+ enabled: true
+ active: true
+ priority: 150
+ check_mode: true
+ register: result
+
+- name: Assert that nothing has changed
+ assert:
+ that:
+ - result is changed
+ - result.end_state != {}
+ - result.end_state.name == "testkey"
+ - result.end_state.parentId == "realm_key_test"
+ - result.end_state.providerId == "rsa"
+ - result.end_state.providerType == "org.keycloak.keys.KeyProvider"
+ - result.end_state.config.active == ["true"]
+ - result.end_state.config.enabled == ["true"]
+ - result.end_state.config.algorithm == ["RS256"]
+ - result.end_state.config.priority == ["150"]
+ - result.msg == "Realm key testkey would be created"
+
+- name: Create custom realm key
+ community.general.keycloak_realm_key:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: testkey
+ state: present
+ parent_id: "{{ realm }}"
+ config:
+ private_key: "{{ realm_private_key }}"
+ certificate: ""
+ enabled: true
+ active: true
+ priority: 150
+ diff: true
+ register: result
+
+- name: Assert that realm key was created
+ assert:
+ that:
+ - result is changed
+ - result.end_state != {}
+ - result.end_state.name == "testkey"
+ - result.end_state.parentId == "realm_key_test"
+ - result.end_state.providerId == "rsa"
+ - result.end_state.providerType == "org.keycloak.keys.KeyProvider"
+ - result.end_state.config.active == ["true"]
+ - result.end_state.config.enabled == ["true"]
+ - result.end_state.config.algorithm == ["RS256"]
+ - result.end_state.config.priority == ["150"]
+ - result.msg == "Realm key testkey created"
+
+- name: Create custom realm key (test for idempotency)
+ community.general.keycloak_realm_key:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: testkey
+ state: present
+ parent_id: "{{ realm }}"
+ config:
+ private_key: "{{ realm_private_key }}"
+ certificate: ""
+ enabled: true
+ active: true
+ priority: 150
+ register: result
+
+- name: Assert that nothing has changed
+ assert:
+ that:
+ - result is not changed
+ - result.end_state != {}
+ - result.end_state.name == "testkey"
+ - result.end_state.parentId == "realm_key_test"
+ - result.end_state.providerId == "rsa"
+ - result.end_state.providerType == "org.keycloak.keys.KeyProvider"
+ - result.end_state.config.active == ["true"]
+ - result.end_state.config.enabled == ["true"]
+ - result.end_state.config.algorithm == ["RS256"]
+ - result.end_state.config.priority == ["150"]
+ - result.msg == "Realm key testkey was in sync"
+
+- name: Update custom realm key (check mode)
+ community.general.keycloak_realm_key:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: testkey
+ state: present
+ parent_id: "{{ realm }}"
+ config:
+ private_key: "{{ realm_private_key }}"
+ certificate: ""
+ enabled: true
+ active: true
+ priority: 140
+ check_mode: true
+ register: result
+
+- name: Assert that nothing has changed
+ assert:
+ that:
+ - result is changed
+ - result.end_state != {}
+ - result.end_state.name == "testkey"
+ - result.end_state.parentId == "realm_key_test"
+ - result.end_state.providerId == "rsa"
+ - result.end_state.providerType == "org.keycloak.keys.KeyProvider"
+ - result.end_state.config.active == ["true"]
+ - result.end_state.config.enabled == ["true"]
+ - result.end_state.config.algorithm == ["RS256"]
+ - result.end_state.config.priority == ["140"]
+ - result.msg == "Realm key testkey would be changed: config.priority ['150'] -> ['140']"
+
+- name: Update custom realm key
+ community.general.keycloak_realm_key:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: testkey
+ state: present
+ parent_id: "{{ realm }}"
+ config:
+ private_key: "{{ realm_private_key }}"
+ certificate: ""
+ enabled: true
+ active: true
+ priority: 140
+ diff: true
+ register: result
+
+- name: Assert that realm key was updated
+ assert:
+ that:
+ - result is changed
+ - result.end_state != {}
+ - result.end_state.name == "testkey"
+ - result.end_state.parentId == "realm_key_test"
+ - result.end_state.providerId == "rsa"
+ - result.end_state.providerType == "org.keycloak.keys.KeyProvider"
+ - result.end_state.config.active == ["true"]
+ - result.end_state.config.enabled == ["true"]
+ - result.end_state.config.algorithm == ["RS256"]
+ - result.end_state.config.priority == ["140"]
+ - result.msg == "Realm key testkey changed: config.priority ['150'] -> ['140']"
+
+- name: Update custom realm key (test for idempotency)
+ community.general.keycloak_realm_key:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: testkey
+ state: present
+ parent_id: "{{ realm }}"
+ config:
+ private_key: "{{ realm_private_key }}"
+ certificate: ""
+ enabled: true
+ active: true
+ priority: 140
+ register: result
+
+- name: Assert that nothing has changed
+ assert:
+ that:
+ - result is not changed
+ - result.end_state != {}
+ - result.end_state.name == "testkey"
+ - result.end_state.parentId == "realm_key_test"
+ - result.end_state.providerId == "rsa"
+ - result.end_state.providerType == "org.keycloak.keys.KeyProvider"
+ - result.end_state.config.active == ["true"]
+ - result.end_state.config.enabled == ["true"]
+ - result.end_state.config.algorithm == ["RS256"]
+ - result.end_state.config.priority == ["140"]
+ - result.msg == "Realm key testkey was in sync"
+
+- name: Force update custom realm key
+ community.general.keycloak_realm_key:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: testkey
+ force: true
+ state: present
+ parent_id: "{{ realm }}"
+ config:
+ private_key: "{{ realm_private_key_2 }}"
+ certificate: ""
+ enabled: true
+ active: true
+ priority: 140
+ register: result
+
+- name: Assert that forced update ran correctly
+ assert:
+ that:
+ - result is changed
+ - result.end_state != {}
+ - result.end_state.name == "testkey"
+ - result.end_state.parentId == "realm_key_test"
+ - result.end_state.providerId == "rsa"
+ - result.end_state.providerType == "org.keycloak.keys.KeyProvider"
+ - result.end_state.config.active == ["true"]
+ - result.end_state.config.enabled == ["true"]
+ - result.end_state.config.algorithm == ["RS256"]
+ - result.end_state.config.priority == ["140"]
+ - result.msg == "Realm key testkey was forcibly updated"
+
+- name: Remove custom realm key
+ community.general.keycloak_realm_key:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: testkey
+ state: absent
+ parent_id: "{{ realm }}"
+ config:
+ private_key: "{{ realm_private_key }}"
+ certificate: ""
+ priority: 140
+ diff: true
+ register: result
+
+- name: Assert that realm key was deleted
+ assert:
+ that:
+ - result is changed
+ - result.end_state == {}
+ - result.msg == "Realm key testkey deleted"
+
+- name: Remove custom realm key (test for idempotency)
+ community.general.keycloak_realm_key:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: testkey
+ state: absent
+ parent_id: "{{ realm }}"
+ config:
+ private_key: "{{ realm_private_key }}"
+ certificate: ""
+ priority: 140
+ register: result
+
+- name: Assert that nothing has changed
+ assert:
+ that:
+ - result is not changed
+ - result.end_state == {}
+ - result.msg == "Realm key testkey not present"
+
+- name: Create custom realm key with a custom certificate
+ community.general.keycloak_realm_key:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: testkey_with_certificate
+ state: present
+ parent_id: "{{ realm }}"
+ config:
+ private_key: "{{ realm_private_key }}"
+ certificate: "{{ realm_certificate }}"
+ enabled: true
+ active: true
+ priority: 150
+ diff: true
+ register: result
+
+- name: Assert that realm key with custom certificate was created
+ assert:
+ that:
+ - result is changed
+ - result.end_state != {}
+ - result.end_state.name == "testkey_with_certificate"
+ - result.end_state.parentId == "realm_key_test"
+ - result.end_state.providerId == "rsa"
+ - result.end_state.providerType == "org.keycloak.keys.KeyProvider"
+ - result.end_state.config.active == ["true"]
+ - result.end_state.config.enabled == ["true"]
+ - result.end_state.config.algorithm == ["RS256"]
+ - result.end_state.config.priority == ["150"]
+ - result.msg == "Realm key testkey_with_certificate created"
+
+- name: Attempt to change the private key and the certificate
+ community.general.keycloak_realm_key:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: testkey_with_certificate
+ state: present
+ parent_id: "{{ realm }}"
+ config:
+ private_key: "a different private key string"
+ certificate: "a different certificate string"
+ enabled: true
+ active: true
+ priority: 150
+ diff: true
+ register: result
+
+- name: Assert that nothing has changed
+ assert:
+ that:
+ - result is not changed
+ - result.end_state != {}
+ - result.end_state.name == "testkey_with_certificate"
+ - result.end_state.parentId == "realm_key_test"
+ - result.end_state.providerId == "rsa"
+ - result.end_state.providerType == "org.keycloak.keys.KeyProvider"
+ - result.end_state.config.active == ["true"]
+ - result.end_state.config.enabled == ["true"]
+ - result.end_state.config.algorithm == ["RS256"]
+ - result.end_state.config.priority == ["150"]
+ - result.msg == "Realm key testkey_with_certificate was in sync"
+
+- name: Remove Keycloak test realm
+ community.general.keycloak_realm:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ id: "{{ realm }}"
+ state: absent
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_realm_key/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_realm_key/vars/main.yml
new file mode 100644
index 000000000..d39cf8f73
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_realm_key/vars/main.yml
@@ -0,0 +1,48 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+url: http://localhost:8080/auth
+admin_realm: master
+admin_user: admin
+admin_password: password
+realm: realm_key_test
+realm_private_key_name: testkey
+realm_private_key: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC9Mi7IKXPhqGiWGwgEYEXnqc8nytG1pHbC6QYZe1gUa43jDtGYQln36It02BGw4e5XydCUj+M26X2sH+kKaV+KHEnJtcEqdAuVX1QaDVzeiOoo1/B9HC8By6NZBsOSdxpat3EvilQ+R7NP9yi53J08+vfeZSEGyPmKV1me7nJnRh3/zcRsOi92GTsBd7gApKfP8sorDjY8m9NRuPLwleK2nh/aRvj1yK8x3UAqUIbOCVaE39bSN6VUTFK2Q/+MX3vF0Zugsk7PKKmfqcEW6wj7dtSElbX4uhrfTkGMmwIWdIiLDNRA/jVRvGxUB1SyMy6kmMC8jC2QGWpZgfkSKtHlAgMBAAECggEACWkSVh7ntmjtwM+Z47vVJkt2NBS8vxPt206DYOeXbzaVUV6mkrP0LSZKL3bi1GE8fW3am9UXWF8fQt04dm3c1G4JRojtkXrBq72Y3Y3eGWyGdx8chWCOPwDdwFsbhbC6ZRo8PUDcZVekJd1Vj38XbBXQl+WAUcnTzauAF+1kz9mhJq1gpglIbB+8l7VjMXwXeaGWJQ5OL/MSsq7r3P1elVjHwprFBM7HHA5+RTu/KY/GcEutgm5uwTRqRZNC1IBXAQtBO7HQJbuLqDPTQ3RRCPEur8R+0dk5bF+8IyzQ8Bh+Dhuou9xzfS/A7lV6L/CZSpv4Bvq1H3Uxk+orXf2Q2QKBgQDBOf1nSJB0VgQdIcdtgVpVgQ2SyWAd+N8Qk7QsyVQf9f7ZqiFLejWJbaaeY9WtfZ01D8tgHJfPqsO1/Jux255mtkyk2K2c6dav1Lsd4l+iPfidsDJNWkcd59nQqwC9BLjzWK/J4rO20apm34abLaZ9oVk8Mgz8VWJWOxTgCr+COQKBgQD6qP1lm6rzlCSIEz9eCuGPkQkVo+NIP437e3i+sxtkLlMgnmfzSwSJdVF8AKH3gXi3NyWjfBVYeAZEkm1kHF8IWOiK4U1y95Vx3uud3NX4SC+cjePc+pDPQJiz9L+zq9I6WFZWmm7n/9heTxu/l0vxI4FHaBmt95BMwLJNkzbdDQKBgCHGwUUMqjOr1YxCG1pJAkFwDa9bBDI5DsUXDKfHia0Mkz/5PVi0RCeBw15slS1+h7x+xk5GsULb1to5Df5JJadOtpcaST7koWKbDRpsN8tkidEGu8RJw6S2opyXR8nCyZHALvpbZo7Ol7rj1+PIVxIe4jpjhWGWi1oHed6wAkoBAoGAJx2F5XxEUhx1EvMF+XPzPQciBsl7Z0PbsTnUXtXuWVTNThLKH/I99AFlxNcIb2o530VwzzFG13Zra/n5rhyrS88sArgj8OPn40wpMopKraL+Iw0VWN+VB3KKIdL4s14FwWsVlhAlbHjFV/o6V0yR4kBrJSx+jWJLl16etHJbpmUCgYBUWCQwcT1aw9XHWJXiNYTnQSYg88hgGYhts1qSzhfu+n1t2BlAlxM0gu2+gez21mM8uiYsqbU2OZeG2U4as6kdai8Q4tzNQt2f1r3ZewJN/QHrkx6FT94PNa0w4ILiQ9Eu7xssaHcYjHyrI1NlbMKypVy6waDG2ajLOFAVeHGpOg==
+ -----END PRIVATE KEY-----
+realm_certificate: |
+ -----BEGIN CERTIFICATE-----
+ MIIDQDCCAiigAwIBAgIUMfPlHWcZn6xfeSjfbhgmt4yy6mMwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDAeFw0yMzA4MTgxMTU5MDFaFw0zMzA4MTUxMTU5MDFaMEIxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9Mi7IKXPhqGiWGwgEYEXnqc8nytG1pHbC6QYZe1gUa43jDtGYQln36It02BGw4e5XydCUj+M26X2sH+kKaV+KHEnJtcEqdAuVX1QaDVzeiOoo1/B9HC8By6NZBsOSdxpat3EvilQ+R7NP9yi53J08+vfeZSEGyPmKV1me7nJnRh3/zcRsOi92GTsBd7gApKfP8sorDjY8m9NRuPLwleK2nh/aRvj1yK8x3UAqUIbOCVaE39bSN6VUTFK2Q/+MX3vF0Zugsk7PKKmfqcEW6wj7dtSElbX4uhrfTkGMmwIWdIiLDNRA/jVRvGxUB1SyMy6kmMC8jC2QGWpZgfkSKtHlAgMBAAGjLjAsMAsGA1UdDwQEAwIEkDAdBgNVHQ4EFgQUcZirWRV5EzRhanUVSQ9rmAavVbEwDQYJKoZIhvcNAQELBQADggEBAIt2aFr/sxvtZfDc+Nb9tgspBuoX8f9Gf9mrS6dTdvdqSMHQrcoejSEEAZNljdSpKAhnhyR3+uCIev++WS4tixZoooQ8aYxDGNIwyry51GNEK7LKXVRmkbZFODidRuYZ1XWQORaJoaXWplaPaNtLvUr1swachz36K4n8/UIi109w/addajOHFbFGAzUmGRR4saMZPGrQCaNFje7G1o5wb/mQD1L+Jfk81Id5/F6NFBsSEIi+/O7Xs7fOWuab6cdfwI7zQQclEo55WQkLXefFLn+Ju0Ftgl023awpNEE4pjl6jD5VSEOkQ+I2sxGvymgjz7Av4zPOD/Lr05lRnMxf8dA=
+ -----END CERTIFICATE-----
+realm_private_key_2: |
+ -----BEGIN PRIVATE KEY-----
+ MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCyQ5FKuqbnWEtt
+ KI0FHKFvd+G/RyEI2ow29Ytjs3fZ7/gMYfXozHLLJl3jgCOvSf9Ta55arL0XnCCf
+ RKQb0vpgMmOTQw++A1UmNXe8atTczZMRiHMHFdLhXUvUKthcMGOrTH8xegCnm0bG
+ rZwimjQDog/kROMAN78Uv8SD1lMpGBxPr2DWXNl4kRF670m/jC0cM7SeDGCCKVF5
+ SEh6rMDgI62AxKnbtxuAbF9SOO/6kTsYv5+dc8wxDEb0aaT1jC1CLhjAVmjc6vO7
+ WHE0LLas+ARs4ghMONLN6VdOkJxBuEtlLqM3M+/viD1TRftZCeLarYLWdEsg8Yz9
+ Ufb0oawzAgMBAAECggEARqPDxWsljHNOoFj7WNU5m6RTzqpvCsUf3v96Vu3dRn1z
+ O+Ttv2yU6K+xcN9sRJ/8D6CLxb7Bx8NUoghfR69ZDBmrn8VpTZCgg12Yrw9efojw
+ CHibrGkXgbqou9CmoBGEzXKozIBeFgzQBRby0jts9SuZRImPspxkmeJMCzo5BgUg
+ ksNibaWikvUJYMgFc7PdXEvxhCKcWTTGC3fxJwpRxXkqKsYDa3JhdhloH8hHqynm
+ o7WEXeGAn4UV7C1tg3OdTciHn/ONMRItPcyonwk19meZTvsEub6ZsNjVg/5oJVBr
+ WG8vPZBi1VzAMayDXxDOnEAKW5eJXRSNX1vZ7EQTEQKBgQDXg5pSp9hVdVZc+eN/
+ Ab/1NMMdgrQdbyTeB9esjLiwNuXysQm/KaG8gTkLpiKVvJ8R7SOcxb9Y5Gt9Y5Ej
+ eu943V4zLDIzNt/ST4bXGW/gQ84zkMBdhKz9hKA5tartVjI1ycznjpDbgn/jAYPI
+ 8VXGmjID2oDIJ7P+dLD8lMBDvQKBgQDTwIyimy+4EwFUuuppfWArXRsqsWUScGWD
+ +06xbc+Ld92LJBvakvSTdDNnS/PlYGl/fJjqQ4wq5UPREJYCi3UW9I5jtfsIg8Pl
+ oCnIhEYkn8xPZ7X8grU4emkM6QAPhstCDlXE6t0T202TpYVYjtEEDRQu4rKAbJ0h
+ gqSh5Ge2rwKBgEjrx6jWEBYCaOF20ComTmxKmQaANi+Lbt8NqkVBLDC7spymmJSt
+ IoOk+cdeRG+D7hLjuVwPcQpD57b6nJ5zt1mfFYOdHbNEiwEfVZGskrVAXCIIhX5f
+ KSVy3cAJHzfFJaIbkRB8pbkQc/M8jPnN5ucXP3scUNzoyjd8BnLAZjnFAoGAWwwY
+ rDYTz48EbH0uG4uYFS0kaDf8YHBJhfVBgdLYgXxZmuE8xL+ZP+mfzJOA3CiXVASr
+ 71Z551vKzBLYnWF/SA6BRuhRdvjI+2vha2FMk6TOAXpzao59AzrG/pEUwJhRvyZQ
+ xKnDwyzxb0GlU02dG6PQANTisYuCCI2W4jFGUusCgYB72p5o5uBr7qrFMTdMMxxe
+ f/9Go/9QBR/uNYk3D/rWj0F/bXGbiYMddNMD4v3XE24NL4ZvBJn0Po64Tuz5+wtu
+ 5ICKc6ED1l55MPsKdegVMpXGIFRjZt2TtCk4FE68m5QJpT1IIK7I9jv0+FGKjFYa
+ ukdTEghu13cANd8eKpxBsQ==
+ -----END PRIVATE KEY-----
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_role/README.md b/ansible_collections/community/general/tests/integration/targets/keycloak_role/README.md
new file mode 100644
index 000000000..ccb4c8ffa
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_role/README.md
@@ -0,0 +1,20 @@
+<!--
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
+-->
+# Running keycloak_user module integration test
+
+To run Keycloak user module's integration test, start a keycloak server using Docker or Podman:
+
+ podman|docker run -d --rm --name mykeycloak -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=password quay.io/keycloak/keycloak:latest start-dev --http-relative-path /auth
+
+Source Ansible env-setup from ansible github repository
+
+Run integration tests:
+
+ ansible-test integration keycloak_role --python 3.10 --allow-unsupported
+
+Cleanup:
+
+ podman|docker stop mykeycloak
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_role/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_role/tasks/main.yml
index 61b62629a..c649b8680 100644
--- a/ansible_collections/community/general/tests/integration/targets/keycloak_role/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_role/tasks/main.yml
@@ -248,3 +248,236 @@
that:
- result is not changed
- result.end_state == {}
+
+- name: Create realm role with composites
+ community.general.keycloak_role:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "{{ keycloak_role_name }}"
+ realm: "{{ realm }}"
+ description: "{{ keycloak_role_description }}"
+ composite: "{{ keycloak_role_composite }}"
+ composites: "{{ keycloak_role_composites }}"
+ state: present
+ register: result
+
+- name: Debug
+ debug:
+ var: result
+
+- name: Assert realm role is created with composites
+ assert:
+ that:
+ - result is changed
+ - result.end_state.composites | length == 3
+
+- name: Change realm role with composites no change
+ community.general.keycloak_role:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "{{ keycloak_role_name }}"
+ realm: "{{ realm }}"
+ description: "{{ keycloak_role_description }}"
+ composite: "{{ keycloak_role_composite }}"
+ composites: "{{ keycloak_role_composites }}"
+ state: present
+ register: result
+
+- name: Debug
+ debug:
+ var: result
+
+- name: Assert realm role with composites have not changed
+ assert:
+ that:
+ - result is not changed
+ - result.end_state.composites | length == 3
+
+- name: Remove composite from realm role with composites
+ community.general.keycloak_role:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "{{ keycloak_role_name }}"
+ realm: "{{ realm }}"
+ description: "{{ keycloak_role_description }}"
+ composite: "{{ keycloak_role_composite }}"
+ composites: "{{ keycloak_role_composites_with_absent }}"
+ state: present
+ register: result
+
+- name: Debug
+ debug:
+ var: result
+
+- name: Assert composite was removed from realm role with composites
+ assert:
+ that:
+ - result is changed
+ - result.end_state.composites | length == 2
+
+- name: Delete realm role with composites
+ community.general.keycloak_role:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ name: "{{ keycloak_role_name }}"
+ state: absent
+ register: result
+
+- name: Debug
+ debug:
+ var: result
+
+- name: Assert realm role deleted
+ assert:
+ that:
+ - result is changed
+ - result.end_state == {}
+
+- name: Delete absent realm role with composites
+ community.general.keycloak_role:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ name: "{{ keycloak_role_name }}"
+ state: absent
+ register: result
+
+- name: Debug
+ debug:
+ var: result
+
+- name: Assert not changed and realm role absent
+ assert:
+ that:
+ - result is not changed
+ - result.end_state == {}
+
+- name: Create client role with composites
+ community.general.keycloak_role:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "{{ keycloak_role_name }}"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ description: "{{ keycloak_role_description }}"
+ composite: "{{ keycloak_role_composite }}"
+ composites: "{{ keycloak_role_composites }}"
+ state: present
+ register: result
+
+- name: Debug
+ debug:
+ var: result
+
+- name: Assert client role is created with composites
+ assert:
+ that:
+ - result is changed
+ - result.end_state.composites | length == 3
+
+- name: Change client role with composites no change
+ community.general.keycloak_role:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "{{ keycloak_role_name }}"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ description: "{{ keycloak_role_description }}"
+ composite: "{{ keycloak_role_composite }}"
+ composites: "{{ keycloak_role_composites }}"
+ state: present
+ register: result
+
+- name: Debug
+ debug:
+ var: result
+
+- name: Assert client role with composites have not changed
+ assert:
+ that:
+ - result is not changed
+ - result.end_state.composites | length == 3
+
+- name: Remove composite from client role with composites
+ community.general.keycloak_role:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ name: "{{ keycloak_role_name }}"
+ client_id: "{{ client_id }}"
+ realm: "{{ realm }}"
+ description: "{{ keycloak_role_description }}"
+ composite: "{{ keycloak_role_composite }}"
+ composites: "{{ keycloak_role_composites_with_absent }}"
+ state: present
+ register: result
+
+- name: Debug
+ debug:
+ var: result
+
+- name: Assert composite was removed from client role with composites
+ assert:
+ that:
+ - result is changed
+ - result.end_state.composites | length == 2
+
+- name: Delete client role with composites
+ community.general.keycloak_role:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ name: "{{ keycloak_role_name }}"
+ client_id: "{{ client_id }}"
+ state: absent
+ register: result
+
+- name: Debug
+ debug:
+ var: result
+
+- name: Assert client role deleted
+ assert:
+ that:
+ - result is changed
+ - result.end_state == {}
+
+- name: Delete absent client role with composites
+ community.general.keycloak_role:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ name: "{{ keycloak_role_name }}"
+ client_id: "{{ client_id }}"
+ state: absent
+ register: result
+
+- name: Debug
+ debug:
+ var: result
+
+- name: Assert not changed and client role absent
+ assert:
+ that:
+ - result is not changed
+ - result.end_state == {} \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_role/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_role/vars/main.yml
index b003311e0..0af55dfc5 100644
--- a/ansible_collections/community/general/tests/integration/targets/keycloak_role/vars/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_role/vars/main.yml
@@ -12,3 +12,30 @@ client_id: myclient
role: myrole
description_1: desc 1
description_2: desc 2
+
+keycloak_role_name: test
+keycloak_role_description: test
+keycloak_role_composite: true
+keycloak_role_composites:
+ - name: view-clients
+ client_id: "realm-management"
+ state: present
+ - name: query-clients
+ client_id: "realm-management"
+ state: present
+ - name: offline_access
+ state: present
+keycloak_client_id: test-client
+keycloak_client_name: test-client
+keycloak_client_description: This is a client for testing purpose
+role_state: present
+
+keycloak_role_composites_with_absent:
+ - name: view-clients
+ client_id: "realm-management"
+ state: present
+ - name: query-clients
+ client_id: "realm-management"
+ state: present
+ - name: offline_access
+ state: absent \ No newline at end of file
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_user/README.md b/ansible_collections/community/general/tests/integration/targets/keycloak_user/README.md
new file mode 100644
index 000000000..07ecc3f83
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_user/README.md
@@ -0,0 +1,21 @@
+<!--
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
+-->
+# Running keycloak_user module integration test
+
+To run Keycloak user module's integration test, start a keycloak server using Docker or Podman:
+
+ podman|docker run -d --rm --name mykeycloak -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=password quay.io/keycloak/keycloak:latest start-dev --http-relative-path /auth
+
+Source Ansible env-setup from ansible github repository
+
+Run integration tests:
+
+ ansible-test integration keycloak_user --python 3.10 --allow-unsupported
+
+Cleanup:
+
+ podman|docker stop mykeycloak
+
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_user/aliases b/ansible_collections/community/general/tests/integration/targets/keycloak_user/aliases
new file mode 100644
index 000000000..0abc6a467
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_user/aliases
@@ -0,0 +1,4 @@
+# Copyright (c) 2023, INSPQ Philippe Gauthier (@elfelip)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+unsupported
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_user/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_user/tasks/main.yml
new file mode 100644
index 000000000..0f1fe152d
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_user/tasks/main.yml
@@ -0,0 +1,114 @@
+# Copyright (c) 2022, Dušan Marković (@bratwurzt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Create realm
+ community.general.keycloak_realm:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ id: "{{ realm }}"
+ realm: "{{ realm }}"
+ state: present
+
+- name: Create new realm role
+ community.general.keycloak_role:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ name: "{{ role }}"
+ description: "{{ description_1 }}"
+ state: present
+
+- name: Create client
+ community.general.keycloak_client:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ service_accounts_enabled: true
+ state: present
+ register: client
+
+
+- name: Create new client role
+ community.general.keycloak_role:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ client_id: "{{ client_id }}"
+ name: "{{ keycloak_client_role }}"
+ description: "{{ description_1 }}"
+ state: present
+
+- name: Create new groups
+ community.general.keycloak_group:
+ auth_keycloak_url: "{{ url }}"
+ auth_realm: "{{ admin_realm }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ realm: "{{ realm }}"
+ name: "{{ item.name }}"
+ state: present
+ with_items: "{{ keycloak_user_groups }}"
+
+- name: Create user
+ community.general.keycloak_user:
+ auth_keycloak_url: "{{ url }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ auth_realm: "{{ admin_realm }}"
+ username: "{{ keycloak_username }}"
+ realm: "{{ realm }}"
+ first_name: Ceciestes
+ last_name: Untestes
+ email: ceciestuntestes@test.com
+ groups: "{{ keycloak_user_groups }}"
+ attributes: "{{ keycloak_user_attributes }}"
+ state: present
+ register: create_result
+
+- name: debug
+ debug:
+ var: create_result
+
+- name: Assert user is created
+ assert:
+ that:
+ - create_result.changed
+ - create_result.end_state.username == 'test'
+ - create_result.end_state.attributes | length == 3
+ - create_result.end_state.groups | length == 2
+
+- name: Delete User
+ community.general.keycloak_user:
+ auth_keycloak_url: "{{ url }}"
+ auth_username: "{{ admin_user }}"
+ auth_password: "{{ admin_password }}"
+ auth_realm: "{{ admin_realm }}"
+ username: "{{ keycloak_username }}"
+ realm: "{{ realm }}"
+ first_name: Ceciestes
+ last_name: Untestes
+ email: ceciestuntestes@test.com
+ groups: "{{ keycloak_user_groups }}"
+ attributes: "{{ keycloak_user_attributes }}"
+ state: absent
+ register: delete_result
+
+- name: debug
+ debug:
+ var: delete_result
+
+- name: Assert user is deleted
+ assert:
+ that:
+ - delete_result.changed
+ - delete_result.end_state | length == 0
diff --git a/ansible_collections/community/general/tests/integration/targets/keycloak_user/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/keycloak_user/vars/main.yml
new file mode 100644
index 000000000..9962aba54
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/keycloak_user/vars/main.yml
@@ -0,0 +1,46 @@
+---
+# Copyright (c) 2022, Dušan Marković (@bratwurzt)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+url: http://localhost:8080/auth
+admin_realm: master
+admin_user: admin
+admin_password: password
+realm: myrealm
+client_id: myclient
+role: myrole
+description_1: desc 1
+description_2: desc 2
+
+keycloak_username: test
+keycloak_service_account_client_id: "{{ client_id }}"
+keycloak_user_realm_roles:
+ - name: offline_access
+ - name: "{{ role }}"
+keycloak_client_role: test
+keycloak_user_client_roles:
+ - client_id: "{{ client_id }}"
+ roles:
+ - name: "{{ keycloak_client_role }}"
+ - client_id: "{{ realm }}-realm"
+ roles:
+ - name: view-users
+ - name: query-users
+keycloak_user_attributes:
+ - name: attr1
+ values:
+ - value1s
+ state: present
+ - name: attr2
+ values:
+ - value2s
+ state: present
+ - name: attr3
+ values:
+ - value3s
+ state: present
+keycloak_user_groups:
+ - name: test
+ state: present
+ - name: test2
diff --git a/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/auth.yml b/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/auth.yml
new file mode 100644
index 000000000..a8c7a13ee
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/auth.yml
@@ -0,0 +1,47 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- debug:
+ msg: Running tests/auth.yml
+
+####################################################################
+## Search ##########################################################
+####################################################################
+- name: Test simple search for password authenticated user
+ ldap_search:
+ dn: "ou=users,dc=example,dc=com"
+ scope: "onelevel"
+ filter: "(uid=ldaptest)"
+ bind_dn: "uid=ldaptest,ou=users,dc=example,dc=com"
+ bind_pw: "test1pass!"
+ ignore_errors: true
+ register: output
+
+- name: assert that test LDAP user can read its password
+ assert:
+ that:
+ - output is not failed
+ - output.results | length == 1
+ - output.results.0.userPassword is defined
+
+- name: Test simple search for cert authenticated user
+ ldap_search:
+ dn: "ou=users,dc=example,dc=com"
+ server_uri: "ldap://localhost/"
+ start_tls: true
+ ca_path: /usr/local/share/ca-certificates/ca.crt
+ scope: "onelevel"
+ filter: "(uid=ldaptest)"
+ client_cert: "/root/user.crt"
+ client_key: "/root/user.key"
+ ignore_errors: true
+ register: output
+
+- name: assert that test LDAP user can read its password
+ assert:
+ that:
+ - output is not failed
+ - output.results | length == 1
+ - output.results.0.userPassword is defined
diff --git a/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/basic.yml b/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/basic.yml
index 36d245d39..11e5d6562 100644
--- a/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/basic.yml
+++ b/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/basic.yml
@@ -23,3 +23,17 @@
- output is not failed
- output.results | length == 1
- output.results.0.displayName == "LDAP Test"
+
+- name: Test simple search for a user with no results
+ ldap_search:
+ dn: "ou=users,dc=example,dc=com"
+ scope: "onelevel"
+ filter: "(uid=nonexistent)"
+ ignore_errors: true
+ register: output
+
+- name: assert that the output is empty
+ assert:
+ that:
+ - output is not failed
+ - output.results | length == 0
diff --git a/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/pages.yml b/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/pages.yml
new file mode 100644
index 000000000..32575854b
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/pages.yml
@@ -0,0 +1,24 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- debug:
+ msg: Running tests/pages.yml
+
+####################################################################
+## Search ##########################################################
+####################################################################
+- name: Test paged search for all users
+ ldap_search:
+ dn: "ou=users,dc=example,dc=com"
+ scope: "onelevel"
+ page_size: 1
+ ignore_errors: true
+ register: output
+
+- name: assert that the right number of results are returned
+ assert:
+ that:
+ - output is not failed
+ - output.results | length == 2
diff --git a/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/schema.yml b/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/schema.yml
new file mode 100644
index 000000000..892eac3cb
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/ldap_search/tasks/tests/schema.yml
@@ -0,0 +1,25 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- debug:
+ msg: Running tests/schema.yml
+
+####################################################################
+## Search ##########################################################
+####################################################################
+- name: Test for ldap schema output
+ ldap_search:
+ dn: "ou=users,dc=example,dc=com"
+ scope: "onelevel"
+ schema: true
+ ignore_errors: true
+ register: output
+
+- name: Assert that the schema output is correct
+ assert:
+ that:
+ - output is not failed
+ - output.results | length >= 1
+ - "{{ 'displayName' in output.results.0.attrs }}"
diff --git a/ansible_collections/community/general/tests/integration/targets/listen_ports_facts/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/listen_ports_facts/tasks/main.yml
index 70649f505..0e583e7a1 100644
--- a/ansible_collections/community/general/tests/integration/targets/listen_ports_facts/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/listen_ports_facts/tasks/main.yml
@@ -13,7 +13,7 @@
ansible.builtin.package:
name:
- net-tools
- - netcat
+ - netcat-openbsd
state: latest
when: ansible_os_family == "Debian"
diff --git a/ansible_collections/community/general/tests/integration/targets/locale_gen/aliases b/ansible_collections/community/general/tests/integration/targets/locale_gen/aliases
index f7f4063f6..a5d3e27f9 100644
--- a/ansible_collections/community/general/tests/integration/targets/locale_gen/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/locale_gen/aliases
@@ -6,3 +6,5 @@ azp/posix/3
destructive
needs/root
skip/aix
+skip/freebsd
+skip/macos
diff --git a/ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/basic.yml b/ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/basic.yml
new file mode 100644
index 000000000..8718e0be8
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/basic.yml
@@ -0,0 +1,102 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Is the locale we're going to test against installed? {{ locale_basic.localegen }}
+ command: locale -a
+ register: initial_state
+ ignore_errors: true
+
+- name: Make sure the locale is not installed {{ locale_basic.localegen }}
+ locale_gen:
+ name: "{{ locale_basic.localegen }}"
+ state: absent
+
+- name: Is the locale present? {{ locale_basic.localegen }}
+ command: locale -a
+ register: cleaned
+ ignore_errors: true
+
+- name: Make sure the locale is not present {{ locale_basic.localegen }}
+ assert:
+ that:
+ - locale_basic.skip_removal or locale_basic.locales | intersect(cleaned.stdout_lines) == []
+
+- name: Install the locale {{ locale_basic.localegen }}
+ locale_gen:
+ name: "{{ locale_basic.localegen }}"
+ state: present
+ register: output_present
+
+- name: Is the locale present? {{ locale_basic.localegen }}
+ command: locale -a
+ register: post_check_output_present
+ ignore_errors: true
+
+- name: Make sure the locale is present and we say we installed it {{ locale_basic.localegen }}
+ assert:
+ that:
+ - locale_basic.locales | intersect(post_check_output_present.stdout_lines) != []
+ - locale_basic.skip_removal or output_present is changed
+
+- name: Install the locale a second time {{ locale_basic.localegen }}
+ locale_gen:
+ name: "{{ locale_basic.localegen }}"
+ state: present
+ register: output_present_idempotent
+
+- name: Is the locale present? {{ locale_basic.localegen }}
+ command: locale -a
+ register: post_check_output_present_idempotent
+ ignore_errors: true
+
+- name: Make sure the locale is present and we reported no change {{ locale_basic.localegen }}
+ assert:
+ that:
+ - locale_basic.locales | intersect(post_check_output_present_idempotent.stdout_lines) != []
+ - output_present_idempotent is not changed
+
+- name: Removals
+ when: locale_basic.skip_removal is false
+ block:
+ - name: Remove the locale {{ locale_basic.localegen }}
+ locale_gen:
+ name: "{{ locale_basic.localegen }}"
+ state: absent
+ register: output_absent
+
+ - name: Is the locale present? {{ locale_basic.localegen }}
+ command: locale -a
+ register: post_check_output_absent
+ ignore_errors: true
+
+ - name: Make sure the locale is absent and we reported a change {{ locale_basic.localegen }}
+ assert:
+ that:
+ - locale_basic.locales | intersect(post_check_output_absent.stdout_lines) == []
+ - output_absent is changed
+
+ - name: Remove the locale a second time {{ locale_basic.localegen }}
+ locale_gen:
+ name: "{{ locale_basic.localegen }}"
+ state: absent
+ register: output_absent_idempotent
+
+ - name: Is the locale present? {{ locale_basic.localegen }}
+ command: locale -a
+ register: post_check_output_absent_idempotent
+ ignore_errors: true
+
+ - name: Make sure the locale is absent and we reported no change {{ locale_basic.localegen }}
+ assert:
+ that:
+ - locale_basic.locales | intersect(post_check_output_absent_idempotent.stdout_lines) == []
+ - output_absent_idempotent is not changed
+
+# Cleanup
+- name: Reinstall the locale we tested against if it was initially installed {{ locale_basic.localegen }}
+ locale_gen:
+ name: "{{ locale_basic.localegen }}"
+ state: present
+ when: locale_basic.locales | intersect(initial_state.stdout_lines) != []
diff --git a/ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/locale_gen.yml b/ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/locale_gen.yml
deleted file mode 100644
index c6bdcc046..000000000
--- a/ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/locale_gen.yml
+++ /dev/null
@@ -1,99 +0,0 @@
----
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-- name: Is the locale we're going to test against installed?
- shell: locale -a | grep pt_BR
- register: initial_state
- ignore_errors: true
-
-- name: Make sure the locale is not installed
- locale_gen:
- name: pt_BR
- state: absent
-
-- name: Is the locale present?
- shell: locale -a | grep pt_BR
- register: cleaned
- ignore_errors: true
-
-- name: Make sure the locale is not present
- assert:
- that:
- - "cleaned.rc == 1"
-
-- name: Install the locale
- locale_gen:
- name: pt_BR
- state: present
- register: output
-
-- name: Is the locale present?
- shell: locale -a | grep pt_BR
- register: post_check_output
- ignore_errors: true
-
-- name: Make sure the locale is present and we say we installed it
- assert:
- that:
- - "post_check_output.rc == 0"
- - "output.changed"
-
-- name: Install the locale a second time
- locale_gen:
- name: pt_BR
- state: present
- register: output
-
-- name: Is the locale present?
- shell: locale -a | grep pt_BR
- register: post_check_output
- ignore_errors: true
-
-- name: Make sure the locale is present and we reported no change
- assert:
- that:
- - "post_check_output.rc == 0"
- - "not output.changed"
-
-- name: Remove the locale
- locale_gen:
- name: pt_BR
- state: absent
- register: output
-
-- name: Is the locale present?
- shell: locale -a | grep pt_BR
- register: post_check_output
- ignore_errors: true
-
-- name: Make sure the locale is absent and we reported a change
- assert:
- that:
- - "post_check_output.rc == 1"
- - "output.changed"
-
-- name: Remove the locale a second time
- locale_gen:
- name: pt_BR
- state: absent
- register: output
-
-- name: Is the locale present?
- shell: locale -a | grep pt_BR
- register: post_check_output
- ignore_errors: true
-
-- name: Make sure the locale is absent and we reported no change
- assert:
- that:
- - "post_check_output.rc == 1"
- - "not output.changed"
-
-# Cleanup
-- name: Reinstall the locale we tested against if it was initially installed
- locale_gen:
- name: pt_BR
- state: present
- when: initial_state.rc == 0
diff --git a/ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/main.yml
index de3e673be..2d9dfcee0 100644
--- a/ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/locale_gen/tasks/main.yml
@@ -8,5 +8,11 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-- include_tasks: 'locale_gen.yml'
- when: ansible_distribution in ('Ubuntu', 'Debian')
+- name: Bail out if not supported
+ ansible.builtin.meta: end_play
+ when: ansible_distribution not in ('Ubuntu', 'Debian')
+
+- include_tasks: basic.yml
+ loop: "{{ locale_list_basic }}"
+ loop_control:
+ loop_var: locale_basic
diff --git a/ansible_collections/community/general/tests/integration/targets/locale_gen/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/locale_gen/vars/main.yml
new file mode 100644
index 000000000..44327ddd3
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/locale_gen/vars/main.yml
@@ -0,0 +1,17 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# locale_basic: pt_BR
+
+locale_list_basic:
+ - localegen: pt_BR
+ locales: [pt_BR]
+ skip_removal: false
+ - localegen: C.UTF-8
+ locales: [C.utf8, C.UTF-8]
+ skip_removal: true
+ - localegen: eo
+ locales: [eo]
+ skip_removal: false
diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_cartesian/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_cartesian/aliases
index 2bdcc0113..5e6585203 100644
--- a/ansible_collections/community/general/tests/integration/targets/lookup_cartesian/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/lookup_cartesian/aliases
@@ -4,4 +4,3 @@
azp/posix/1
skip/aix
-skip/python2.6 # lookups are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_dependent/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_dependent/aliases
index 26ad5c244..12d1d6617 100644
--- a/ansible_collections/community/general/tests/integration/targets/lookup_dependent/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/lookup_dependent/aliases
@@ -3,4 +3,3 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/2
-skip/python2.6 # lookups are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_dig/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_dig/aliases
index eb449a9cf..afda346c4 100644
--- a/ansible_collections/community/general/tests/integration/targets/lookup_dig/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/lookup_dig/aliases
@@ -3,4 +3,3 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/1
-skip/python2.6 # lookups are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/aliases
index b9f3395f7..de1f51cb5 100644
--- a/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/lookup_etcd3/aliases
@@ -10,5 +10,4 @@ skip/aix
skip/osx
skip/macos
skip/freebsd
-skip/python2.6 # lookups are controller only, and we no longer support Python 2.6 on the controller
disabled # see https://github.com/ansible-collections/community.general/issues/322
diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_flattened/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_flattened/aliases
index 0ac9bad98..dadd9f37a 100644
--- a/ansible_collections/community/general/tests/integration/targets/lookup_flattened/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/lookup_flattened/aliases
@@ -4,4 +4,3 @@
azp/posix/2
skip/aix
-skip/python2.6 # lookups are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/aliases
index 66632fb4a..9c7febe24 100644
--- a/ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/lookup_lmdb_kv/aliases
@@ -5,4 +5,3 @@
azp/posix/2
destructive
skip/aix
-skip/python2.6 # lookups are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/aliases
index eb449a9cf..afda346c4 100644
--- a/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/aliases
@@ -3,4 +3,3 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/1
-skip/python2.6 # lookups are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/runme.sh b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/runme.sh
index 52a38f4a5..4e66476be 100755
--- a/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/runme.sh
+++ b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/runme.sh
@@ -11,3 +11,6 @@ ANSIBLE_LOG_PATH=/tmp/ansible-test-merge-variables \
ANSIBLE_LOG_PATH=/tmp/ansible-test-merge-variables \
ANSIBLE_MERGE_VARIABLES_PATTERN_TYPE=suffix \
ansible-playbook test_with_env.yml "$@"
+
+ANSIBLE_LOG_PATH=/tmp/ansible-test-merge-variables \
+ ansible-playbook -i test_inventory_all_hosts.yml test_all_hosts.yml "$@"
diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_all_hosts.yml b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_all_hosts.yml
new file mode 100644
index 000000000..3070087bb
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_all_hosts.yml
@@ -0,0 +1,64 @@
+---
+# Copyright (c) 2020, Thales Netherlands
+# Copyright (c) 2021, Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Test merge_variables lookup plugin (multiple hosts)
+ hosts: host0
+ gather_facts: false
+ tasks:
+ - name: Test merge dicts via all group
+ delegate_to: localhost
+ vars:
+ merged_dict: "{{ lookup('community.general.merge_variables', '__merge_dict_ex', pattern_type='suffix', groups=['all']) }}"
+ block:
+ - name: Test merge dicts via all group - Print the merged dict
+ ansible.builtin.debug:
+ msg: "{{ merged_dict }}"
+
+ - name: Test merge dicts via all group - Validate that the dict is complete
+ ansible.builtin.assert:
+ that:
+ - "(merged_dict.keys() | list | length) == 4"
+ - "'item1' in merged_dict"
+ - "'item2' in merged_dict"
+ - "'item3' in merged_dict"
+ - "'list_item' in merged_dict"
+ - "merged_dict.list_item | length == 3"
+ - name: Test merge dicts via two of three groups
+ delegate_to: localhost
+ vars:
+ merged_dict: "{{ lookup('community.general.merge_variables', '__merge_dict_in', pattern_type='suffix', groups=['dummy1', 'dummy2']) }}"
+ block:
+ - name: Test merge dicts via two of three groups - Print the merged dict
+ ansible.builtin.debug:
+ msg: "{{ merged_dict }}"
+
+ - name: Test merge dicts via two of three groups - Validate that the dict is complete
+ ansible.builtin.assert:
+ that:
+ - "(merged_dict.keys() | list | length) == 3"
+ - "'item1' in merged_dict"
+ - "'item2' in merged_dict"
+ - "'list_item' in merged_dict"
+ - "merged_dict.list_item | length == 2"
+ - name: Test merge dicts via two of three groups with inital value
+ delegate_to: localhost
+ vars:
+ initial_dict:
+ initial: initial_value
+ merged_dict: "{{ lookup('community.general.merge_variables', '__merge_dict_in', initial_value=initial_dict, pattern_type='suffix', groups=['dummy1', 'dummy2']) }}"
+ block:
+ - name: Test merge dicts via two of three groups with inital value - Print the merged dict
+ ansible.builtin.debug:
+ msg: "{{ merged_dict }}"
+
+ - name: Test merge dicts via two of three groups with inital value - Validate that the dict is complete
+ ansible.builtin.assert:
+ that:
+ - "(merged_dict.keys() | list | length) == 4"
+ - "'item1' in merged_dict"
+ - "'item2' in merged_dict"
+ - "'list_item' in merged_dict"
+ - "merged_dict.list_item | length == 2"
diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_inventory_all_hosts.yml b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_inventory_all_hosts.yml
new file mode 100644
index 000000000..edf5a9e46
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lookup_merge_variables/test_inventory_all_hosts.yml
@@ -0,0 +1,52 @@
+---
+# Copyright (c) 2020, Thales Netherlands
+# Copyright (c) 2021, Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+all:
+ hosts:
+ host0:
+ host1:
+ testdict1__merge_dict_ex:
+ item1: value1
+ list_item:
+ - test1
+
+ testdict2__merge_dict_ex:
+ item2: value2
+ list_item:
+ - test2
+
+ testdict__merge_dict_in:
+ item1: value1
+ list_item:
+ - test1
+ host2:
+ testdict3__merge_dict_ex:
+ item3: value3
+ list_item:
+ - test3
+
+ testdict__merge_dict_in:
+ item2: value2
+ list_item:
+ - test2
+
+ host3:
+ testdict__merge_dict_in:
+ item3: value3
+ list_item:
+ - test3
+
+dummy1:
+ hosts:
+ host1:
+
+dummy2:
+ hosts:
+ host2:
+
+dummy3:
+ hosts:
+ host3:
diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/aliases
index 0d4c5af3b..f02225028 100644
--- a/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/lookup_passwordstore/aliases
@@ -6,6 +6,5 @@ azp/posix/1
destructive
skip/aix
skip/rhel
-skip/python2.6 # lookups are controller only, and we no longer support Python 2.6 on the controller
skip/osx # FIXME https://github.com/ansible-collections/community.general/issues/2978
skip/macos # FIXME https://github.com/ansible-collections/community.general/issues/2978
diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_random_pet/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_random_pet/aliases
index 0ac9bad98..dadd9f37a 100644
--- a/ansible_collections/community/general/tests/integration/targets/lookup_random_pet/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/lookup_random_pet/aliases
@@ -4,4 +4,3 @@
azp/posix/2
skip/aix
-skip/python2.6 # lookups are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_random_string/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_random_string/aliases
index 0ac9bad98..dadd9f37a 100644
--- a/ansible_collections/community/general/tests/integration/targets/lookup_random_string/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/lookup_random_string/aliases
@@ -4,4 +4,3 @@
azp/posix/2
skip/aix
-skip/python2.6 # lookups are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/lookup_random_words/aliases b/ansible_collections/community/general/tests/integration/targets/lookup_random_words/aliases
index 0ac9bad98..dadd9f37a 100644
--- a/ansible_collections/community/general/tests/integration/targets/lookup_random_words/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/lookup_random_words/aliases
@@ -4,4 +4,3 @@
azp/posix/2
skip/aix
-skip/python2.6 # lookups are controller only, and we no longer support Python 2.6 on the controller
diff --git a/ansible_collections/community/general/tests/integration/targets/lvg/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/main.yml
index e14c48c3f..15af2d08c 100644
--- a/ansible_collections/community/general/tests/integration/targets/lvg/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/main.yml
@@ -18,10 +18,20 @@
block:
- import_tasks: setup.yml
+ - import_tasks: setup_missing_pv.yml
+
- import_tasks: test_indempotency.yml
- import_tasks: test_grow_reduce.yml
- import_tasks: test_pvresize.yml
+
+ - import_tasks: test_active_change.yml
+
+ - import_tasks: test_active_create.yml
+
+ - import_tasks: test_uuid_reset.yml
always:
- import_tasks: teardown.yml
+
+ - import_tasks: teardown_missing_pv.yml
diff --git a/ansible_collections/community/general/tests/integration/targets/lvg/tasks/setup.yml b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/setup.yml
index 3984b9fc3..45209c6a6 100644
--- a/ansible_collections/community/general/tests/integration/targets/lvg/tasks/setup.yml
+++ b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/setup.yml
@@ -4,8 +4,8 @@
# SPDX-License-Identifier: GPL-3.0-or-later
- name: "Create files to use as a disk devices"
- command: "dd if=/dev/zero of={{ remote_tmp_dir }}/img{{ item }} bs=1M count=10"
- with_sequence: 'count=2'
+ command: "dd if=/dev/zero of={{ remote_tmp_dir }}/img{{ item }} bs=1M count=36"
+ with_sequence: 'count=4'
- name: "Show next free loop device"
command: "losetup -f"
@@ -21,7 +21,23 @@
- name: "Create loop device for file"
command: "losetup -f {{ remote_tmp_dir }}/img2"
+- name: "Show next free loop device"
+ command: "losetup -f"
+ register: loop_device3
+
+- name: "Create loop device for file"
+ command: "losetup -f {{ remote_tmp_dir }}/img3"
+
+- name: "Show next free loop device"
+ command: "losetup -f"
+ register: loop_device4
+
+- name: "Create loop device for file"
+ command: "losetup -f {{ remote_tmp_dir }}/img4"
+
- name: "Affect name on disk to work on"
set_fact:
loop_device1: "{{ loop_device1.stdout }}"
loop_device2: "{{ loop_device2.stdout }}"
+ loop_device3: "{{ loop_device3.stdout }}"
+ loop_device4: "{{ loop_device4.stdout }}"
diff --git a/ansible_collections/community/general/tests/integration/targets/lvg/tasks/setup_missing_pv.yml b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/setup_missing_pv.yml
new file mode 100644
index 000000000..863ef8757
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/setup_missing_pv.yml
@@ -0,0 +1,18 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: "Prepare VG for missing PV"
+ lvg:
+ vg: vg_with_missing_pv
+ pvs:
+ - "{{ loop_device3 }}"
+ - "{{ loop_device4 }}"
+
+- name: Save loop_device4 pvid
+ shell: "pvs -ouuid --noheadings {{ loop_device4 }} | xargs -n1 | tr -d '-'"
+ register: loop_device4_pvid_result
+
+- name: Detach loop_device4
+ command: "losetup -d {{ loop_device4 }}"
diff --git a/ansible_collections/community/general/tests/integration/targets/lvg/tasks/teardown.yml b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/teardown.yml
index de4957321..2d147dee0 100644
--- a/ansible_collections/community/general/tests/integration/targets/lvg/tasks/teardown.yml
+++ b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/teardown.yml
@@ -6,13 +6,25 @@
- name: Remove test volume group
lvg:
vg: testvg
+ force: true
state: absent
+- name: Remove LVM devices
+ loop:
+ - "{{ loop_device1 | default('') }}"
+ - "{{ loop_device2 | default('') }}"
+ - "{{ loop_device3 | default('') }}"
+ when:
+ - item|length > 0
+ command: "lvmdevices --deldev {{ item }}"
+ ignore_errors: true
+
- name: Detach loop devices
command: "losetup -d {{ item }}"
loop:
- "{{ loop_device1 | default('') }}"
- "{{ loop_device2 | default('') }}"
+ - "{{ loop_device3 | default('') }}"
when:
- item != ''
@@ -20,4 +32,4 @@
file:
path: "{{ remote_tmp_dir }}/img{{ item }}"
state: absent
- with_sequence: 'count=2'
+ with_sequence: 'count=4'
diff --git a/ansible_collections/community/general/tests/integration/targets/lvg/tasks/teardown_missing_pv.yml b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/teardown_missing_pv.yml
new file mode 100644
index 000000000..4cff68003
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/teardown_missing_pv.yml
@@ -0,0 +1,8 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Remove loop_device4 LVM device
+ command: "lvmdevices --delpvid {{ loop_device4_pvid_result.stdout }}"
+ ignore_errors: true
diff --git a/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_active_change.yml b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_active_change.yml
new file mode 100644
index 000000000..7df52683f
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_active_change.yml
@@ -0,0 +1,163 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Create volume group on disk device
+ lvg:
+ vg: testvg
+ pvs: "{{ loop_device1 }}"
+
+- name: Create logical volumes on volume group
+ loop:
+ - lv1
+ - lv2
+ lvol:
+ vg: testvg
+ lv: "{{ item }}"
+ size: 2m
+
+- name: Create snapshot volumes of origin logical volumes
+ loop:
+ - lv1
+ - lv2
+ lvol:
+ vg: testvg
+ lv: "{{ item }}"
+ snapshot: "{{ item }}_snap"
+ size: 50%ORIGIN
+
+- name: Collect all lv active status in testvg
+ shell: vgs -olv_active --noheadings testvg | xargs -n1
+ register: initial_lv_status_result
+
+- name: Assert all lv in testvg are active
+ loop: "{{ initial_lv_status_result.stdout_lines }}"
+ assert:
+ that:
+ - "'active' == item"
+
+- name: Deactivate volume group
+ lvg:
+ state: inactive
+ vg: testvg
+ register: vg_deactivate_result
+
+- name: Collect all lv active status in testvg
+ shell: vgs -olv_active --noheadings testvg | xargs -n1
+ register: deactivated_lv_status_result
+
+- name: Do all assertions to verify expected results
+ assert:
+ that:
+ - vg_deactivate_result is changed
+ - "'active' not in deactivated_lv_status_result.stdout"
+
+- name: Deactivate volume group again to verify idempotence
+ lvg:
+ state: inactive
+ vg: testvg
+ register: repeated_vg_deactivate_result
+
+- name: Verify vg deactivation idempontency
+ assert:
+ that:
+ - repeated_vg_deactivate_result is not changed
+
+- name: Activate volume group in check mode
+ lvg:
+ state: active
+ vg: testvg
+ register: check_mode_vg_activate_result
+ check_mode: true
+
+- name: Collect all lv active status in testvg
+ shell: vgs -olv_active --noheadings testvg | xargs -n1
+ register: check_mode_activate_lv_status_result
+
+- name: Verify VG activation in check mode changed without activating LVs
+ assert:
+ that:
+ - check_mode_vg_activate_result is changed
+ - "'active' not in check_mode_activate_lv_status_result.stdout"
+
+- name: Activate volume group
+ lvg:
+ state: active
+ vg: testvg
+ register: vg_activate_result
+
+- name: Collect all lv active status in testvg
+ shell: vgs -olv_active --noheadings testvg | xargs -n1
+ register: activate_lv_status_result
+
+- name: Verify vg activation
+ assert:
+ that:
+ - vg_activate_result is changed
+
+- name: Assert all lv in testvg are active
+ loop: "{{ activate_lv_status_result.stdout_lines }}"
+ assert:
+ that:
+ - "'active' == item"
+
+- name: Activate volume group again to verify idempontency
+ lvg:
+ state: active
+ vg: testvg
+ register: repeated_vg_activate_result
+
+- name: Verify vg activation idempontency
+ assert:
+ that:
+ - repeated_vg_activate_result is not changed
+
+- name: Deactivate lv2 in testvg
+ lvol:
+ vg: testvg
+ lv: lv2
+ active: false
+
+- name: Activate volume group again to verify partially activated vg activation
+ lvg:
+ state: active
+ vg: testvg
+ register: partial_vg_activate_result
+
+- name: Verify partially activated vg activation
+ assert:
+ that:
+ - partial_vg_activate_result is changed
+
+- name: Collect all lv active status in testvg
+ shell: vgs -olv_active --noheadings testvg | xargs -n1
+ register: activate_partial_lv_status_result
+
+- name: Assert all lv in testvg are active
+ loop: "{{ activate_partial_lv_status_result.stdout_lines }}"
+ assert:
+ that:
+ - "'active' == item"
+
+- name: Deactivate volume group in check mode
+ lvg:
+ state: inactive
+ vg: testvg
+ register: check_mode_vg_deactivate_result
+ check_mode: true
+
+- name: Collect all lv active status in testvg
+ shell: vgs -olv_active --noheadings testvg | xargs -n1
+ register: check_mode_deactivate_lv_status_result
+
+- name: Verify check mode vg deactivation changed
+ assert:
+ that:
+ - check_mode_vg_deactivate_result is changed
+
+- name: Assert all lv in testvg are still active
+ loop: "{{ check_mode_deactivate_lv_status_result.stdout_lines }}"
+ assert:
+ that:
+ - "'active' == item"
diff --git a/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_active_create.yml b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_active_create.yml
new file mode 100644
index 000000000..7ac1ffedd
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_active_create.yml
@@ -0,0 +1,71 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Collect vgcreate help
+ command: "vgcreate --help"
+ register: vgcreate_help_result
+
+- when: "'--setautoactivation' in vgcreate_help_result.stdout"
+ block:
+ - name: Create autoactivated volume group on disk device
+ lvg:
+ state: active
+ vg: vg_autoact_test
+ pvs: "{{ loop_device2 }}"
+
+ - name: Collect vg autoactivation status for vg_autoact_test
+ shell: vgs -oautoactivation --noheadings vg_autoact_test | xargs -n1
+ register: active_vg_autoact_status_result
+
+ - name: Assert vg autoactivation is set for vg_autoact_test
+ assert:
+ that: "'enabled' == active_vg_autoact_status_result.stdout"
+
+ - name: Remove vg_autoact_test for the next test
+ lvg:
+ state: absent
+ vg: vg_autoact_test
+ force: true
+
+ - name: Create auttoactivation disabled volume group on disk device
+ lvg:
+ state: inactive
+ vg: vg_autoact_test
+ pvs: "{{ loop_device2 }}"
+
+ - name: Collect vg autoactivation status for vg_autoact_test
+ shell: vgs -oautoactivation --noheadings vg_autoact_test | xargs -n1
+ register: inactive_vg_autoact_status_result
+
+ - name: Assert vg autoactivation disabled for vg_autoact_test
+ assert:
+ that: "inactive_vg_autoact_status_result.stdout | length == 0"
+
+ - name: Remove vg_autoact_test for the next test
+ lvg:
+ state: absent
+ vg: vg_autoact_test
+ force: true
+
+ - name: Create auttoactivation disabled by option volume group on disk device
+ lvg:
+ state: active
+ vg: vg_autoact_test
+ vg_options: "--setautoactivation n"
+ pvs: "{{ loop_device2 }}"
+
+ - name: Collect vg autoactivation status for vg_autoact_test
+ shell: vgs -oautoactivation --noheadings vg_autoact_test | xargs -n1
+ register: inactive_by_option_vg_autoact_status_result
+
+ - name: Assert vg autoactivation disabled by option for vg_autoact_test
+ assert:
+ that: "inactive_by_option_vg_autoact_status_result.stdout | length == 0"
+ always:
+ - name: Cleanup vg_autoact_test
+ lvg:
+ state: absent
+ vg: vg_autoact_test
+ force: true
diff --git a/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_pvresize.yml b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_pvresize.yml
index f15add91c..3f3b9dbdd 100644
--- a/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_pvresize.yml
+++ b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_pvresize.yml
@@ -12,10 +12,10 @@
shell: vgs -v testvg -o pv_size --noheading --units b | xargs
register: cmd_result
-- name: Assert the testvg size is 8388608B
+- name: Assert the testvg size is 33554432B
assert:
that:
- - "'8388608B' == cmd_result.stdout"
+ - "'33554432B' == cmd_result.stdout"
- name: Increases size in file
command: "dd if=/dev/zero bs=8MiB count=1 of={{ remote_tmp_dir }}/img1 conv=notrunc oflag=append"
@@ -38,10 +38,10 @@
shell: vgs -v testvg -o pv_size --noheading --units b | xargs
register: cmd_result
-- name: Assert the testvg size is still 8388608B
+- name: Assert the testvg size is still 33554432B
assert:
that:
- - "'8388608B' == cmd_result.stdout"
+ - "'33554432B' == cmd_result.stdout"
- name: "Reruns lvg with pvresize:yes and check_mode:yes"
lvg:
@@ -60,10 +60,10 @@
shell: vgs -v testvg -o pv_size --noheading --units b | xargs
register: cmd_result
-- name: Assert the testvg size is still 8388608B
+- name: Assert the testvg size is still 33554432B
assert:
that:
- - "'8388608B' == cmd_result.stdout"
+ - "'33554432B' == cmd_result.stdout"
- name: "Reruns lvg with pvresize:yes"
lvg:
@@ -75,7 +75,7 @@
shell: vgs -v testvg -o pv_size --noheading --units b | xargs
register: cmd_result
-- name: Assert the testvg size is now 16777216B
+- name: Assert the testvg size is now 41943040B
assert:
that:
- - "'16777216B' == cmd_result.stdout"
+ - "'41943040B' == cmd_result.stdout"
diff --git a/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_uuid_reset.yml b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_uuid_reset.yml
new file mode 100644
index 000000000..8de50ace5
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvg/tasks/test_uuid_reset.yml
@@ -0,0 +1,107 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Create volume group on disk device
+ lvg:
+ vg: testvg
+ pvs: "{{ loop_device1 }}"
+
+- name: Save testvg uuid
+ shell: vgs -ouuid --noheadings testvg | xargs -n1
+ register: orig_vg_uuid_cmd_result
+
+- name: Save pv uuid
+ shell: "pvs -ouuid --noheadings {{ loop_device1 }} | xargs -n1"
+ register: orig_pv_uuid_cmd_result
+
+- name: Deactivate and reset vg/pv uuid
+ lvg:
+ state: inactive
+ vg: testvg
+ pvs: "{{ loop_device1 }}"
+ reset_vg_uuid: true
+ reset_pv_uuid: true
+ register: vg_uuid_reset
+
+- name: Save testvg uuid
+ shell: vgs -ouuid --noheadings testvg | xargs -n1
+ register: new_vg_uuid_cmd_result
+
+- name: Save pv uuid
+ shell: "pvs -ouuid --noheadings {{ loop_device1 }} | xargs -n1"
+ register: new_pv_uuid_cmd_result
+
+- name: Do all assertions to verify expected results
+ assert:
+ that:
+ - vg_uuid_reset is changed
+ - orig_vg_uuid_cmd_result.stdout != new_vg_uuid_cmd_result.stdout
+ - orig_pv_uuid_cmd_result.stdout != new_pv_uuid_cmd_result.stdout
+
+- name: Reset vg uuid again to verify non-idempotence
+ lvg:
+ vg: testvg
+ reset_vg_uuid: true
+ register: repeat_vg_uuid_reset
+
+- name: Reset pv uuid again to verify non-idempotence
+ lvg:
+ vg: testvg
+ reset_pv_uuid: true
+ pvs: "{{ loop_device1 }}"
+ register: repeat_pv_uuid_reset
+
+- name: Save testvg uuid
+ shell: vgs -ouuid --noheadings testvg | xargs -n1
+ register: repeat_vg_uuid_cmd_result
+
+- name: Save pv uuid
+ shell: "pvs -ouuid --noheadings {{ loop_device1 }} | xargs -n1"
+ register: repeat_pv_uuid_cmd_result
+
+- name: Do all assertions to verify expected results
+ assert:
+ that:
+ - repeat_vg_uuid_reset is changed
+ - repeat_pv_uuid_reset is changed
+ - new_vg_uuid_cmd_result.stdout != repeat_vg_uuid_cmd_result.stdout
+ - new_pv_uuid_cmd_result.stdout != repeat_pv_uuid_cmd_result.stdout
+
+- name: Reset vg uuid in check mode
+ lvg:
+ vg: testvg
+ reset_vg_uuid: true
+ register: check_mode_vg_uuid_reset
+ check_mode: true
+
+- name: Reset pv uuid in check mode
+ lvg:
+ vg: testvg
+ reset_pv_uuid: true
+ pvs: "{{ loop_device1 }}"
+ register: check_mode_pv_uuid_reset
+ check_mode: true
+
+- name: Save testvg uuid
+ shell: vgs -ouuid --noheadings testvg | xargs -n1
+ register: check_mode_vg_uuid_cmd_result
+
+- name: Save pv uuid
+ shell: "pvs -ouuid --noheadings {{ loop_device1 }} | xargs -n1"
+ register: check_mode_pv_uuid_cmd_result
+
+- name: Do all assertions to verify expected results
+ assert:
+ that:
+ - check_mode_vg_uuid_reset is changed
+ - check_mode_pv_uuid_reset is changed
+ - check_mode_vg_uuid_cmd_result.stdout == repeat_vg_uuid_cmd_result.stdout
+ - check_mode_pv_uuid_cmd_result.stdout == repeat_pv_uuid_cmd_result.stdout
+
+- name: Activate volume group
+ lvg:
+ state: active
+ vg: testvg
+ register: vg_activate
diff --git a/ansible_collections/community/general/tests/integration/targets/lvg_rename/aliases b/ansible_collections/community/general/tests/integration/targets/lvg_rename/aliases
new file mode 100644
index 000000000..64d439099
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvg_rename/aliases
@@ -0,0 +1,13 @@
+# Copyright (c) Contributors to the Ansible project
+# Based on the integraton test for the lvg module
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+azp/posix/1
+azp/posix/vm
+destructive
+needs/privileged
+skip/aix
+skip/freebsd
+skip/osx
+skip/macos
diff --git a/ansible_collections/community/general/tests/integration/targets/lvg_rename/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/lvg_rename/meta/main.yml
new file mode 100644
index 000000000..90c5d5cb8
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvg_rename/meta/main.yml
@@ -0,0 +1,9 @@
+---
+# Copyright (c) Ansible Project
+# Based on the integraton test for the lvg module
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+dependencies:
+ - setup_pkg_mgr
+ - setup_remote_tmp_dir
diff --git a/ansible_collections/community/general/tests/integration/targets/lvg_rename/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/lvg_rename/tasks/main.yml
new file mode 100644
index 000000000..18dd6f1ba
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvg_rename/tasks/main.yml
@@ -0,0 +1,25 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) Contributors to the Ansible project
+# Based on the integraton test for the lvg module
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Install required packages (Linux)
+ when: ansible_system == 'Linux'
+ ansible.builtin.package:
+ name: lvm2
+ state: present
+
+- name: Test lvg_rename module
+ block:
+ - import_tasks: setup.yml
+
+ - import_tasks: test.yml
+
+ always:
+ - import_tasks: teardown.yml
diff --git a/ansible_collections/community/general/tests/integration/targets/lvg_rename/tasks/setup.yml b/ansible_collections/community/general/tests/integration/targets/lvg_rename/tasks/setup.yml
new file mode 100644
index 000000000..01721e42d
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvg_rename/tasks/setup.yml
@@ -0,0 +1,50 @@
+---
+# Copyright (c) Contributors to the Ansible project
+# Based on the integraton test for the lvg module
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Create files to use as disk devices
+ with_sequence: 'count=2'
+ ansible.builtin.command:
+ cmd: "dd if=/dev/zero of={{ remote_tmp_dir }}/img{{ item }} bs=1M count=10"
+ creates: "{{ remote_tmp_dir }}/img{{ item }}"
+
+- name: Show next free loop device
+ ansible.builtin.command:
+ cmd: "losetup -f"
+ changed_when: false
+ register: loop_device1
+
+- name: "Create loop device for file {{ remote_tmp_dir }}/img1"
+ ansible.builtin.command:
+ cmd: "losetup -f {{ remote_tmp_dir }}/img1"
+ changed_when: true
+
+- name: Show next free loop device
+ ansible.builtin.command:
+ cmd: "losetup -f"
+ changed_when: false
+ register: loop_device2
+
+- name: "Create loop device for file {{ remote_tmp_dir }}/img2"
+ ansible.builtin.command:
+ cmd: "losetup -f {{ remote_tmp_dir }}/img2"
+ changed_when: true
+
+- name: Affect name on disk to work on
+ ansible.builtin.set_fact:
+ loop_device1: "{{ loop_device1.stdout }}"
+ loop_device2: "{{ loop_device2.stdout }}"
+
+- name: "Create test volume group testvg on {{ loop_device1 }}"
+ community.general.lvg:
+ vg: "testvg"
+ state: present
+ pvs: "{{ loop_device1 }}"
+
+- name: "Create test volume group testvg2 on {{ loop_device2 }}"
+ community.general.lvg:
+ vg: "testvg2"
+ state: present
+ pvs: "{{ loop_device2 }}"
diff --git a/ansible_collections/community/general/tests/integration/targets/lvg_rename/tasks/teardown.yml b/ansible_collections/community/general/tests/integration/targets/lvg_rename/tasks/teardown.yml
new file mode 100644
index 000000000..71c33d56d
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvg_rename/tasks/teardown.yml
@@ -0,0 +1,46 @@
+---
+# Copyright (c) Contributors to the Ansible project
+# Based on the integraton test for the lvg module
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Collect test volume groups
+ ansible.builtin.command:
+ cmd: "pvs --noheadings -ovg_name {{ loop_device1 | default('') }} {{ loop_device2 | default('') }}"
+ register: test_vgs_output
+ changed_when: false
+
+- name: Remove test volume groups
+ loop: "{{ test_vgs_output.stdout_lines }}"
+ loop_control:
+ label: "{{ item | trim }}"
+ community.general.lvg:
+ vg: "{{ item | trim }}"
+ state: absent
+
+- name: Remove lvmdevices
+ loop:
+ - "{{ loop_device1 | default('') }}"
+ - "{{ loop_device2 | default('') }}"
+ when:
+ - item | length > 0
+ ansible.builtin.command:
+ cmd: "lvmdevices --deldev {{ item }}"
+ failed_when: false
+ changed_when: true
+
+- name: Detach loop devices
+ loop:
+ - "{{ loop_device1 | default('') }}"
+ - "{{ loop_device2 | default('') }}"
+ when:
+ - item | length > 0
+ ansible.builtin.command:
+ cmd: "losetup -d {{ item }}"
+ changed_when: true
+
+- name: Remove device files
+ with_sequence: 'count=2'
+ ansible.builtin.file:
+ path: "{{ remote_tmp_dir }}/img{{ item }}"
+ state: absent
diff --git a/ansible_collections/community/general/tests/integration/targets/lvg_rename/tasks/test.yml b/ansible_collections/community/general/tests/integration/targets/lvg_rename/tasks/test.yml
new file mode 100644
index 000000000..ab62b679b
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvg_rename/tasks/test.yml
@@ -0,0 +1,105 @@
+---
+# Copyright (c) Contributors to the Ansible project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Rename a VG in check mode
+ community.general.lvg_rename:
+ vg: testvg
+ vg_new: testvg_renamed
+ check_mode: true
+ register: check_mode_vg_rename
+
+- name: Check if testvg still exists
+ ansible.builtin.command:
+ cmd: vgs testvg
+ changed_when: false
+
+- name: Assert that renaming a VG is changed - check mode
+ assert:
+ that:
+ - check_mode_vg_rename is changed
+
+- name: Rename testvg to testvg_renamed
+ community.general.lvg_rename:
+ vg: testvg
+ vg_new: testvg_renamed
+ register: vg_renamed
+
+- name: Assert that renaming a VG is changed
+ assert:
+ that:
+ - vg_renamed is changed
+
+- name: Check if testvg does not exists
+ ansible.builtin.command:
+ cmd: vgs testvg
+ register: check_testvg_existence_result
+ failed_when: check_testvg_existence_result.rc == 0
+ changed_when: false
+
+- name: Check if testvg_renamed exists
+ ansible.builtin.command:
+ cmd: vgs testvg_renamed
+ changed_when: false
+
+- name: Rename testvg to testvg_renamed again for testing idempotency - check mode
+ community.general.lvg_rename:
+ vg: testvg
+ vg_new: testvg_renamed
+ check_mode: true
+ register: check_mode_vg_renamed_again
+
+- name: Rename testvg to testvg_renamed again for testing idempotency
+ community.general.lvg_rename:
+ vg: testvg
+ vg_new: testvg_renamed
+ register: vg_renamed_again
+
+- name: Assert that renaming a VG again is not changed
+ assert:
+ that:
+ - check_mode_vg_renamed_again is not changed
+ - vg_renamed_again is not changed
+
+- name: Rename a non-existing VG - check mode
+ community.general.lvg_rename:
+ vg: testvg
+ vg_new: testvg_ne
+ check_mode: true
+ ignore_errors: true
+ register: check_mode_ne_vg_rename
+
+- name: Rename a non-existing VG
+ community.general.lvg_rename:
+ vg: testvg
+ vg_new: testvg_ne
+ ignore_errors: true
+ register: ne_vg_rename
+
+- name: Assert that renaming a no-existing VG failed
+ assert:
+ that:
+ - check_mode_ne_vg_rename is failed
+ - ne_vg_rename is failed
+
+- name: Rename testvg_renamed to the existing testvg2 name - check mode
+ community.general.lvg_rename:
+ vg: testvg_renamed
+ vg_new: testvg2
+ check_mode: true
+ ignore_errors: true
+ register: check_mode_vg_rename_collision
+
+- name: Rename testvg_renamed to the existing testvg2 name
+ community.general.lvg_rename:
+ vg: testvg_renamed
+ vg_new: testvg2
+ ignore_errors: true
+ register: vg_rename_collision
+
+- name: Assert that renaming to an existing VG name failed
+ assert:
+ that:
+ - check_mode_vg_rename_collision is failed
+ - vg_rename_collision is failed
diff --git a/ansible_collections/community/general/tests/integration/targets/lvol/aliases b/ansible_collections/community/general/tests/integration/targets/lvol/aliases
new file mode 100644
index 000000000..3b92ba75c
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvol/aliases
@@ -0,0 +1,12 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+azp/posix/1
+azp/posix/vm
+destructive
+needs/privileged
+skip/aix
+skip/freebsd
+skip/osx
+skip/macos
diff --git a/ansible_collections/community/general/tests/integration/targets/lvol/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/lvol/meta/main.yml
new file mode 100644
index 000000000..ca1915e05
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvol/meta/main.yml
@@ -0,0 +1,8 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+dependencies:
+ - setup_pkg_mgr
+ - setup_remote_tmp_dir
diff --git a/ansible_collections/community/general/tests/integration/targets/lvol/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/lvol/tasks/main.yml
new file mode 100644
index 000000000..0e3bfa1a3
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvol/tasks/main.yml
@@ -0,0 +1,24 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Install required packages (Linux)
+ package:
+ name: lvm2
+ state: present
+ when: ansible_system == 'Linux'
+
+- name: Test lvol module
+ block:
+ - import_tasks: setup.yml
+
+ - import_tasks: test_pvs.yml
+
+ always:
+ - import_tasks: teardown.yml
diff --git a/ansible_collections/community/general/tests/integration/targets/lvol/tasks/setup.yml b/ansible_collections/community/general/tests/integration/targets/lvol/tasks/setup.yml
new file mode 100644
index 000000000..195c50111
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvol/tasks/setup.yml
@@ -0,0 +1,57 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: "Create files to use as a disk devices"
+ command: "dd if=/dev/zero of={{ remote_tmp_dir }}/img{{ item }} bs=1M count=36"
+ with_sequence: 'count=4'
+
+- name: "Show next free loop device"
+ command: "losetup -f"
+ register: loop_device1
+
+- name: "Create loop device for file"
+ command: "losetup -f {{ remote_tmp_dir }}/img1"
+
+- name: "Show next free loop device"
+ command: "losetup -f"
+ register: loop_device2
+
+- name: "Create loop device for file"
+ command: "losetup -f {{ remote_tmp_dir }}/img2"
+
+- name: "Show next free loop device"
+ command: "losetup -f"
+ register: loop_device3
+
+- name: "Create loop device for file"
+ command: "losetup -f {{ remote_tmp_dir }}/img3"
+
+- name: "Show next free loop device"
+ command: "losetup -f"
+ register: loop_device4
+
+- name: "Create loop device for file"
+ command: "losetup -f {{ remote_tmp_dir }}/img4"
+
+- name: "Set loop device names"
+ set_fact:
+ loop_device1: "{{ loop_device1.stdout }}"
+ loop_device2: "{{ loop_device2.stdout }}"
+ loop_device3: "{{ loop_device3.stdout }}"
+ loop_device4: "{{ loop_device4.stdout }}"
+
+- name: Create testvg1 volume group on disk devices
+ lvg:
+ vg: testvg1
+ pvs:
+ - "{{ loop_device1 }}"
+ - "{{ loop_device2 }}"
+
+- name: Create testvg2 volume group on disk devices
+ lvg:
+ vg: testvg2
+ pvs:
+ - "{{ loop_device3 }}"
+ - "{{ loop_device4 }}"
diff --git a/ansible_collections/community/general/tests/integration/targets/lvol/tasks/teardown.yml b/ansible_collections/community/general/tests/integration/targets/lvol/tasks/teardown.yml
new file mode 100644
index 000000000..a8080f874
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvol/tasks/teardown.yml
@@ -0,0 +1,40 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Remove test volume groups
+ loop:
+ - testvg1
+ - testvg2
+ lvg:
+ vg: "{{ item }}"
+ force: true
+ state: absent
+
+- name: Remove LVM devices
+ loop:
+ - "{{ loop_device1 | default('') }}"
+ - "{{ loop_device2 | default('') }}"
+ - "{{ loop_device3 | default('') }}"
+ - "{{ loop_device4 | default('') }}"
+ when:
+ - item|length > 0
+ command: "lvmdevices --deldev {{ item }}"
+ ignore_errors: true
+
+- name: Detach loop devices
+ command: "losetup -d {{ item }}"
+ loop:
+ - "{{ loop_device1 | default('') }}"
+ - "{{ loop_device2 | default('') }}"
+ - "{{ loop_device3 | default('') }}"
+ - "{{ loop_device4 | default('') }}"
+ when:
+ - item != ''
+
+- name: Remove device files
+ file:
+ path: "{{ remote_tmp_dir }}/img{{ item }}"
+ state: absent
+ with_sequence: 'count=4'
diff --git a/ansible_collections/community/general/tests/integration/targets/lvol/tasks/test_pvs.yml b/ansible_collections/community/general/tests/integration/targets/lvol/tasks/test_pvs.yml
new file mode 100644
index 000000000..c1cd3d1a8
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/lvol/tasks/test_pvs.yml
@@ -0,0 +1,64 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Create logical volume (testlv1) with pvs set as comma separated string
+ lvol:
+ vg: testvg1
+ lv: testlv1
+ size: 50%PVS
+ pvs: "{{ loop_device1 }},{{ loop_device2 }}"
+ register: css_pvs_create_testlv1_result
+
+- name: Assert logical volume (testlv1) created with pvs set as comma separated string
+ assert:
+ that:
+ - css_pvs_create_testlv1_result is success
+ - css_pvs_create_testlv1_result is changed
+
+- name: Create logical volume (testlv1) with pvs set as list
+ lvol:
+ vg: testvg1
+ lv: testlv1
+ size: 50%PVS
+ pvs:
+ - "{{ loop_device1 }}"
+ - "{{ loop_device2 }}"
+ register: list_pvs_create_testlv1_result
+
+- name: Assert logical volume (testlv1) creation idempotency with pvs set as list on second execution
+ assert:
+ that:
+ - list_pvs_create_testlv1_result is success
+ - list_pvs_create_testlv1_result is not changed
+
+- name: Create logical volume (testlv2) with pvs set as list
+ lvol:
+ vg: testvg2
+ lv: testlv2
+ size: 50%PVS
+ pvs:
+ - "{{ loop_device3 }}"
+ - "{{ loop_device4 }}"
+ register: list_pvs_create_testlv2_result
+
+- name: Assert logical volume (testlv2) created with pvs set as list
+ assert:
+ that:
+ - list_pvs_create_testlv2_result is success
+ - list_pvs_create_testlv2_result is changed
+
+- name: Create logical volume (testlv2) with pvs set as comma separated string
+ lvol:
+ vg: testvg2
+ lv: testlv2
+ size: 50%PVS
+ pvs: "{{ loop_device3 }},{{ loop_device4 }}"
+ register: css_pvs_create_testlv2_result
+
+- name: Assert logical volume (testlv2) creation idempotency with pvs set as comma separated string on second execution
+ assert:
+ that:
+ - css_pvs_create_testlv2_result is success
+ - css_pvs_create_testlv2_result is not changed
diff --git a/ansible_collections/community/general/tests/integration/targets/mail/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/mail/tasks/main.yml
index 4f3f90a51..83c242ad2 100644
--- a/ansible_collections/community/general/tests/integration/targets/mail/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/mail/tasks/main.yml
@@ -10,96 +10,101 @@
# TODO: Our current implementation does not handle SMTP authentication
-# NOTE: If the system does not support smtpd-tls (python 2.6 and older) we do basic tests
-- name: Attempt to install smtpd-tls
- pip:
- name: smtpd-tls
- state: present
- ignore_errors: true
- register: smtpd_tls
+- when:
+ # TODO: https://github.com/ansible-collections/community.general/issues/4656
+ - ansible_python.version.major != 3 or ansible_python.version.minor < 12
+ block:
-- name: Install test smtpserver
- copy:
- src: '{{ item }}'
- dest: '{{ remote_tmp_dir }}/{{ item }}'
- loop:
- - smtpserver.py
- - smtpserver.crt
- - smtpserver.key
+ # NOTE: If the system does not support smtpd-tls (python 2.6 and older) we do basic tests
+ - name: Attempt to install smtpd-tls
+ pip:
+ name: smtpd-tls
+ state: present
+ ignore_errors: true
+ register: smtpd_tls
-# FIXME: Verify the mail after it was send would be nice
-# This would require either dumping the content, or registering async task output
-- name: Start test smtpserver
- shell: '{{ ansible_python.executable }} {{ remote_tmp_dir }}/smtpserver.py 10025:10465'
- async: 45
- poll: 0
- register: smtpserver
+ - name: Install test smtpserver
+ copy:
+ src: '{{ item }}'
+ dest: '{{ remote_tmp_dir }}/{{ item }}'
+ loop:
+ - smtpserver.py
+ - smtpserver.crt
+ - smtpserver.key
-- name: Send a basic test-mail
- mail:
- port: 10025
- subject: Test mail 1 (smtp)
- secure: never
+ # FIXME: Verify the mail after it was send would be nice
+ # This would require either dumping the content, or registering async task output
+ - name: Start test smtpserver
+ shell: '{{ ansible_python.executable }} {{ remote_tmp_dir }}/smtpserver.py 10025:10465'
+ async: 45
+ poll: 0
+ register: smtpserver
-- name: Send a test-mail with body and specific recipient
- mail:
- port: 10025
- from: ansible@localhost
- to: root@localhost
- subject: Test mail 2 (smtp + body)
- body: Test body 2
- secure: never
+ - name: Send a basic test-mail
+ mail:
+ port: 10025
+ subject: Test mail 1 (smtp)
+ secure: never
-- name: Send a test-mail with attachment
- mail:
- port: 10025
- from: ansible@localhost
- to: root@localhost
- subject: Test mail 3 (smtp + body + attachment)
- body: Test body 3
- attach: /etc/group
- secure: never
+ - name: Send a test-mail with body and specific recipient
+ mail:
+ port: 10025
+ from: ansible@localhost
+ to: root@localhost
+ subject: Test mail 2 (smtp + body)
+ body: Test body 2
+ secure: never
-# NOTE: This might fail if smtpd-tls is missing or python 2.7.8 or older is used
-- name: Send a test-mail using starttls
- mail:
- port: 10025
- from: ansible@localhost
- to: root@localhost
- subject: Test mail 4 (smtp + starttls + body + attachment)
- body: Test body 4
- attach: /etc/group
- secure: starttls
- ignore_errors: true
- register: starttls_support
+ - name: Send a test-mail with attachment
+ mail:
+ port: 10025
+ from: ansible@localhost
+ to: root@localhost
+ subject: Test mail 3 (smtp + body + attachment)
+ body: Test body 3
+ attach: /etc/group
+ secure: never
-# NOTE: This might fail if smtpd-tls is missing or python 2.7.8 or older is used
-- name: Send a test-mail using TLS
- mail:
- port: 10465
- from: ansible@localhost
- to: root@localhost
- subject: Test mail 5 (smtp + tls + body + attachment)
- body: Test body 5
- attach: /etc/group
- secure: always
- ignore_errors: true
- register: tls_support
+ # NOTE: This might fail if smtpd-tls is missing or python 2.7.8 or older is used
+ - name: Send a test-mail using starttls
+ mail:
+ port: 10025
+ from: ansible@localhost
+ to: root@localhost
+ subject: Test mail 4 (smtp + starttls + body + attachment)
+ body: Test body 4
+ attach: /etc/group
+ secure: starttls
+ ignore_errors: true
+ register: starttls_support
-- fail:
- msg: Sending mail using starttls failed.
- when: smtpd_tls is succeeded and starttls_support is failed and tls_support is succeeded
+ # NOTE: This might fail if smtpd-tls is missing or python 2.7.8 or older is used
+ - name: Send a test-mail using TLS
+ mail:
+ port: 10465
+ from: ansible@localhost
+ to: root@localhost
+ subject: Test mail 5 (smtp + tls + body + attachment)
+ body: Test body 5
+ attach: /etc/group
+ secure: always
+ ignore_errors: true
+ register: tls_support
-- fail:
- msg: Send mail using TLS failed.
- when: smtpd_tls is succeeded and tls_support is failed and starttls_support is succeeded
+ - fail:
+ msg: Sending mail using starttls failed.
+ when: smtpd_tls is succeeded and starttls_support is failed and tls_support is succeeded
-- name: Send a test-mail with body, specific recipient and specific ehlohost
- mail:
- port: 10025
- ehlohost: some.domain.tld
- from: ansible@localhost
- to: root@localhost
- subject: Test mail 6 (smtp + body + ehlohost)
- body: Test body 6
- secure: never
+ - fail:
+ msg: Send mail using TLS failed.
+ when: smtpd_tls is succeeded and tls_support is failed and starttls_support is succeeded
+
+ - name: Send a test-mail with body, specific recipient and specific ehlohost
+ mail:
+ port: 10025
+ ehlohost: some.domain.tld
+ from: ansible@localhost
+ to: root@localhost
+ subject: Test mail 6 (smtp + body + ehlohost)
+ body: Test body 6
+ secure: never
diff --git a/ansible_collections/community/general/tests/integration/targets/mas/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/mas/tasks/main.yml
index f659160dc..839620779 100644
--- a/ansible_collections/community/general/tests/integration/targets/mas/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/mas/tasks/main.yml
@@ -117,7 +117,7 @@
that:
- install_status.stat.exists == true
-- name: Unistall Rested
+- name: Uninstall Rested
mas:
id: 421879749
state: absent
diff --git a/ansible_collections/community/general/tests/integration/targets/monit/aliases b/ansible_collections/community/general/tests/integration/targets/monit/aliases
index ca39d1353..c78104339 100644
--- a/ansible_collections/community/general/tests/integration/targets/monit/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/monit/aliases
@@ -9,6 +9,5 @@ skip/osx
skip/macos
skip/freebsd
skip/aix
-skip/python2.6 # python-daemon package used in integration tests requires >=2.7
skip/rhel # FIXME
unstable # TODO: the tests fail a lot; 'unstable' only requires them to pass when the module itself has been modified
diff --git a/ansible_collections/community/general/tests/integration/targets/mssql_script/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/mssql_script/tasks/main.yml
index 6fa4d3501..481522216 100644
--- a/ansible_collections/community/general/tests/integration/targets/mssql_script/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/mssql_script/tasks/main.yml
@@ -49,6 +49,19 @@
login_port: "{{ mssql_port }}"
script: "SELECT 1"
+- name: Execute a malformed query
+ community.general.mssql_script:
+ login_user: "{{ mssql_login_user }}"
+ login_password: "{{ mssql_login_password }}"
+ login_host: "{{ mssql_host }}"
+ login_port: "{{ mssql_port }}"
+ script: "SELCT 1"
+ failed_when: false
+ register: bad_query
+- assert:
+ that:
+ - bad_query.error.startswith('ProgrammingError')
+
- name: two batches with default output
community.general.mssql_script:
login_user: "{{ mssql_login_user }}"
@@ -135,6 +148,30 @@
- result_batches_dict.query_results_dict[0][0] | length == 1 # one row in first select
- result_batches_dict.query_results_dict[0][0][0]['b0s0'] == 'Batch 0 - Select 0' # column 'b0s0' of first row
+- name: Multiple batches with no resultsets and mixed-case GO
+ community.general.mssql_script:
+ login_user: "{{ mssql_login_user }}"
+ login_password: "{{ mssql_login_password }}"
+ login_host: "{{ mssql_host }}"
+ login_port: "{{ mssql_port }}"
+ script: |
+ CREATE TABLE #integration56yH2 (c1 VARCHAR(10), c2 VARCHAR(10))
+ Go
+ INSERT INTO #integration56yH2 VALUES ('C1_VALUE1', 'C2_VALUE1')
+ gO
+ UPDATE #integration56yH2 SET c2 = 'C2_VALUE2' WHERE c1 = 'C1_VALUE1'
+ go
+ SELECT * from #integration56yH2
+ GO
+ DROP TABLE #integration56yH2
+ register: empty_batches
+- assert:
+ that:
+ - empty_batches.query_results | length == 5 # five batch results
+ - empty_batches.query_results[3][0] | length == 1 # one row in select
+ - empty_batches.query_results[3][0][0] | length == 2 # two columns in row
+ - empty_batches.query_results[3][0][0][1] == 'C2_VALUE2' # value has been updated
+
- name: Stored procedure may return multiple result sets
community.general.mssql_script:
login_user: "{{ mssql_login_user }}"
diff --git a/ansible_collections/community/general/tests/integration/targets/nomad/aliases b/ansible_collections/community/general/tests/integration/targets/nomad/aliases
index ad2435c82..5886d4799 100644
--- a/ansible_collections/community/general/tests/integration/targets/nomad/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/nomad/aliases
@@ -8,3 +8,4 @@ destructive
skip/aix
skip/centos6
skip/freebsd
+disabled # TODO
diff --git a/ansible_collections/community/general/tests/integration/targets/npm/tasks/test.yml b/ansible_collections/community/general/tests/integration/targets/npm/tasks/test.yml
index c8e83f602..0ab8a98d2 100644
--- a/ansible_collections/community/general/tests/integration/targets/npm/tasks/test.yml
+++ b/ansible_collections/community/general/tests/integration/targets/npm/tasks/test.yml
@@ -12,10 +12,10 @@
# sample: node-v8.2.0-linux-x64.tar.xz
node_path: '{{ remote_dir }}/{{ nodejs_path }}/bin'
package: 'iconv-lite'
+ environment:
+ PATH: '{{ node_path }}:{{ ansible_env.PATH }}'
block:
- shell: npm --version
- environment:
- PATH: '{{ node_path }}:{{ ansible_env.PATH }}'
register: npm_version
- debug:
@@ -24,11 +24,8 @@
- name: 'Install simple package without dependency'
npm:
path: '{{ remote_dir }}'
- executable: '{{ node_path }}/npm'
state: present
name: '{{ package }}'
- environment:
- PATH: '{{ node_path }}:{{ ansible_env.PATH }}'
register: npm_install
- assert:
@@ -39,11 +36,8 @@
- name: 'Reinstall simple package without dependency'
npm:
path: '{{ remote_dir }}'
- executable: '{{ node_path }}/npm'
state: present
name: '{{ package }}'
- environment:
- PATH: '{{ node_path }}:{{ ansible_env.PATH }}'
register: npm_reinstall
- name: Check there is no change
@@ -60,11 +54,8 @@
- name: 'reinstall simple package'
npm:
path: '{{ remote_dir }}'
- executable: '{{ node_path }}/npm'
state: present
name: '{{ package }}'
- environment:
- PATH: '{{ node_path }}:{{ ansible_env.PATH }}'
register: npm_fix_install
- name: Check result is changed and successful
diff --git a/ansible_collections/community/general/tests/integration/targets/odbc/aliases b/ansible_collections/community/general/tests/integration/targets/odbc/aliases
index e8465c50e..91a616725 100644
--- a/ansible_collections/community/general/tests/integration/targets/odbc/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/odbc/aliases
@@ -9,4 +9,6 @@ skip/macos
skip/rhel8.0
skip/rhel9.0
skip/rhel9.1
+skip/rhel9.2
+skip/rhel9.3
skip/freebsd
diff --git a/ansible_collections/community/general/tests/integration/targets/odbc/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/odbc/tasks/main.yml
index ce55ea8aa..af5f57cb2 100644
--- a/ansible_collections/community/general/tests/integration/targets/odbc/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/odbc/tasks/main.yml
@@ -10,6 +10,7 @@
- when:
- ansible_os_family != 'Archlinux' # TODO install driver from AUR: https://aur.archlinux.org/packages/psqlodbc
+ - ansible_os_family != 'RedHat' or ansible_distribution_major_version != '7' # CentOS 7 stopped working
block:
#
diff --git a/ansible_collections/community/general/tests/integration/targets/one_host/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/one_host/tasks/main.yml
index ffd5ac04c..3b2c1cedf 100644
--- a/ansible_collections/community/general/tests/integration/targets/one_host/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/one_host/tasks/main.yml
@@ -9,7 +9,7 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-# ENVIRONENT PREPARACTION
+# ENVIRONMENT PREPARACTION
- set_fact: test_number= 0
diff --git a/ansible_collections/community/general/tests/integration/targets/osx_defaults/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/osx_defaults/tasks/main.yml
index f7bcb8944..3ca3180f0 100644
--- a/ansible_collections/community/general/tests/integration/targets/osx_defaults/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/osx_defaults/tasks/main.yml
@@ -21,7 +21,7 @@
- name: Test if state and value are required together
assert:
that:
- - "'following are missing: value' in '{{ missing_value['msg'] }}'"
+ - "'following are missing: value' in missing_value['msg']"
- name: Change value of AppleMeasurementUnits to centimeter in check_mode
osx_defaults:
@@ -194,7 +194,7 @@
register: test_data_types
- assert:
- that: "{{ item.changed }}"
+ that: "item is changed"
with_items: "{{ test_data_types.results }}"
- name: Use different data types and delete them
@@ -208,7 +208,7 @@
register: test_data_types
- assert:
- that: "{{ item.changed }}"
+ that: "item is changed"
with_items: "{{ test_data_types.results }}"
diff --git a/ansible_collections/community/general/tests/integration/targets/pacman/handlers/main.yml b/ansible_collections/community/general/tests/integration/targets/pacman/handlers/main.yml
new file mode 100644
index 000000000..484482bba
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/pacman/handlers/main.yml
@@ -0,0 +1,23 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Remove user yaybuilder
+ ansible.builtin.user:
+ name: yaybuilder
+ state: absent
+
+- name: Remove yay
+ ansible.builtin.package:
+ name: yay
+ state: absent
+
+- name: Remove packages for yay-become
+ ansible.builtin.package:
+ name:
+ - base-devel
+ - yay
+ - git
+ - nmap
+ state: absent
diff --git a/ansible_collections/community/general/tests/integration/targets/pacman/tasks/locally_installed_package.yml b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/locally_installed_package.yml
index a5f183236..f67950c75 100644
--- a/ansible_collections/community/general/tests/integration/targets/pacman/tasks/locally_installed_package.yml
+++ b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/locally_installed_package.yml
@@ -7,11 +7,12 @@
package_name: ansible-test-foo
username: ansible-regular-user
block:
- - name: Install fakeroot
+ - name: Install dependencies
pacman:
state: present
name:
- fakeroot
+ - debugedit
- name: Create user
user:
diff --git a/ansible_collections/community/general/tests/integration/targets/pacman/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/main.yml
index 12d28a2d3..1f6001a66 100644
--- a/ansible_collections/community/general/tests/integration/targets/pacman/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/main.yml
@@ -17,3 +17,4 @@
- include_tasks: 'update_cache.yml'
- include_tasks: 'locally_installed_package.yml'
- include_tasks: 'reason.yml'
+ - include_tasks: 'yay-become.yml'
diff --git a/ansible_collections/community/general/tests/integration/targets/pacman/tasks/remove_nosave.yml b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/remove_nosave.yml
index 2271ebc03..a410775c2 100644
--- a/ansible_collections/community/general/tests/integration/targets/pacman/tasks/remove_nosave.yml
+++ b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/remove_nosave.yml
@@ -4,13 +4,14 @@
# SPDX-License-Identifier: GPL-3.0-or-later
- vars:
- package_name: xinetd
- config_file: /etc/xinetd.conf
+ package_name: mdadm
+ config_file: /etc/mdadm.conf
block:
- name: Make sure that {{ package_name }} is not installed
pacman:
name: '{{ package_name }}'
state: absent
+
- name: Make sure {{config_file}}.pacsave file doesn't exist
file:
path: '{{config_file}}.pacsave'
@@ -32,6 +33,7 @@
pacman:
name: '{{ package_name }}'
state: absent
+
- name: Make sure {{config_file}}.pacsave exists
stat:
path: '{{config_file}}.pacsave'
diff --git a/ansible_collections/community/general/tests/integration/targets/pacman/tasks/yay-become.yml b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/yay-become.yml
new file mode 100644
index 000000000..95698d5ce
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/pacman/tasks/yay-become.yml
@@ -0,0 +1,66 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# This is more convoluted that one might expect, because:
+# - yay is not available or installation in ArchLinux (as it is in Manjaro - issue 6184 reports using it)
+# - to install yay in ArchLinux requires building the package
+# - makepkg cannot be run as root, but the user running it must have sudo to install the resulting package
+
+- name: create user
+ ansible.builtin.user:
+ name: yaybuilder
+ state: present
+ notify: Remove user yaybuilder
+
+- name: grant sudo powers to builder
+ community.general.sudoers:
+ name: yaybuilder
+ user: yaybuilder
+ commands: ALL
+ nopassword: true
+
+- name: Install base packages
+ ansible.builtin.package:
+ name:
+ - base-devel
+ - git
+ - go
+ state: present
+ notify: Remove packages for yay-become
+
+- name: Hack permissions for the remote_tmp_dir
+ ansible.builtin.file:
+ path: "{{ remote_tmp_dir }}"
+ mode: '0777'
+
+- name: Create temp directory for builder
+ ansible.builtin.file:
+ path: "{{ remote_tmp_dir }}/builder"
+ owner: yaybuilder
+ state: directory
+ mode: '0755'
+
+- name: clone yay git repo
+ become: true
+ become_user: yaybuilder
+ ansible.builtin.git:
+ repo: https://aur.archlinux.org/yay.git
+ dest: "{{ remote_tmp_dir }}/builder/yay"
+ depth: 1
+
+- name: make package
+ become: true
+ become_user: yaybuilder
+ ansible.builtin.command:
+ chdir: "{{ remote_tmp_dir }}/builder/yay"
+ cmd: makepkg -si --noconfirm
+ notify: Remove yay
+
+- name: Install nmap
+ community.general.pacman:
+ name: nmap
+ state: present
+ executable: yay
+ extra_args: --builddir /var/cache/yay
diff --git a/ansible_collections/community/general/tests/integration/targets/pids/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/pids/tasks/main.yml
index 2ba7f3754..c8feaacf3 100644
--- a/ansible_collections/community/general/tests/integration/targets/pids/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/pids/tasks/main.yml
@@ -45,7 +45,7 @@
- name: Copy templated helper script
template:
- src: obtainpid.sh
+ src: obtainpid.sh.j2
dest: "{{ remote_tmp_dir }}/obtainpid.sh"
mode: 0755
diff --git a/ansible_collections/community/general/tests/integration/targets/pids/templates/obtainpid.sh b/ansible_collections/community/general/tests/integration/targets/pids/templates/obtainpid.sh.j2
index ecbf56aab..ecbf56aab 100644
--- a/ansible_collections/community/general/tests/integration/targets/pids/templates/obtainpid.sh
+++ b/ansible_collections/community/general/tests/integration/targets/pids/templates/obtainpid.sh.j2
diff --git a/ansible_collections/community/general/tests/integration/targets/pipx/aliases b/ansible_collections/community/general/tests/integration/targets/pipx/aliases
index 9f87ec348..66e6e1a3e 100644
--- a/ansible_collections/community/general/tests/integration/targets/pipx/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/pipx/aliases
@@ -6,3 +6,4 @@ azp/posix/2
destructive
skip/python2
skip/python3.5
+disabled # TODO
diff --git a/ansible_collections/community/general/tests/integration/targets/pipx/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/pipx/tasks/main.yml
index 567405ec4..7eb0f11a6 100644
--- a/ansible_collections/community/general/tests/integration/targets/pipx/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/pipx/tasks/main.yml
@@ -314,3 +314,28 @@
that:
- install_tox_sitewide is changed
- usrlocaltox.stat.exists
+
+##############################################################################
+# Test for issue 7497
+- name: ensure application pyinstaller is uninstalled
+ community.general.pipx:
+ name: pyinstaller
+ state: absent
+
+- name: Install Python Package pyinstaller
+ community.general.pipx:
+ name: pyinstaller
+ state: present
+ system_site_packages: true
+ pip_args: "--no-cache-dir"
+ register: install_pyinstaller
+
+- name: cleanup pyinstaller
+ community.general.pipx:
+ name: pyinstaller
+ state: absent
+
+- name: check assertions
+ assert:
+ that:
+ - install_pyinstaller is changed
diff --git a/ansible_collections/community/general/tests/integration/targets/pipx_info/aliases b/ansible_collections/community/general/tests/integration/targets/pipx_info/aliases
index a28278bbc..e262b485a 100644
--- a/ansible_collections/community/general/tests/integration/targets/pipx_info/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/pipx_info/aliases
@@ -6,3 +6,4 @@ azp/posix/3
destructive
skip/python2
skip/python3.5
+disabled # TODO
diff --git a/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/freebsd.yml b/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/freebsd.yml
index 0c8001899..9d4ecf8bb 100644
--- a/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/freebsd.yml
+++ b/ansible_collections/community/general/tests/integration/targets/pkgng/tasks/freebsd.yml
@@ -515,11 +515,18 @@
# NOTE: FreeBSD 13.2 fails to update the package catalogue for unknown reasons (someone with FreeBSD
# knowledge has to take a look)
#
+ # NOTE: FreeBSD 13.3 fails to update the package catalogue for unknown reasons (someone with FreeBSD
+ # knowledge has to take a look)
+ #
+ # NOTE: FreeBSD 14.0 fails to update the package catalogue for unknown reasons (someone with FreeBSD
+ # knowledge has to take a look)
+ #
# See also
# https://github.com/ansible-collections/community.general/issues/5795
when: >-
(ansible_distribution_version is version('12.01', '>=') and ansible_distribution_version is version('12.3', '<'))
- or ansible_distribution_version is version('13.3', '>=')
+ or (ansible_distribution_version is version('13.4', '>=') and ansible_distribution_version is version('14.0', '<'))
+ or ansible_distribution_version is version('14.1', '>=')
block:
- name: Setup testjail
include_tasks: setup-testjail.yml
diff --git a/ansible_collections/community/general/tests/integration/targets/pnpm/aliases b/ansible_collections/community/general/tests/integration/targets/pnpm/aliases
new file mode 100644
index 000000000..afeb1a6ee
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/pnpm/aliases
@@ -0,0 +1,8 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+azp/posix/2
+destructive
+skip/macos
+skip/freebsd
diff --git a/ansible_collections/community/general/tests/integration/targets/pnpm/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/pnpm/meta/main.yml
new file mode 100644
index 000000000..6147ad33e
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/pnpm/meta/main.yml
@@ -0,0 +1,9 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+dependencies:
+ - setup_pkg_mgr
+ - setup_gnutar
+ - setup_remote_tmp_dir
diff --git a/ansible_collections/community/general/tests/integration/targets/pnpm/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/pnpm/tasks/main.yml
new file mode 100644
index 000000000..10f42618f
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/pnpm/tasks/main.yml
@@ -0,0 +1,27 @@
+---
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# test code for the pnpm module
+# Copyright (c) 2023 Aritra Sen <aretrosen@proton.me>
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# -------------------------------------------------------------
+# Setup steps
+
+- name: Run tests on OSes
+ ansible.builtin.include_tasks: run.yml
+ vars:
+ ansible_system_os: "{{ ansible_system | lower }}"
+ nodejs_version: "{{ item.node_version }}"
+ nodejs_path: "node-v{{ nodejs_version }}-{{ ansible_system_os }}-x{{ ansible_userspace_bits }}"
+ pnpm_version: "{{ item.pnpm_version }}"
+ pnpm_path: "pnpm-{{ 'macos' if ansible_system_os == 'darwin' else 'linuxstatic' }}-x{{ ansible_userspace_bits }}"
+ with_items:
+ - { node_version: 16.20.0, pnpm_version: 8.7.0 }
+ when:
+ - not(ansible_distribution == 'Alpine') and not(ansible_distribution == 'CentOS' and ansible_distribution_major_version == '6')
diff --git a/ansible_collections/community/general/tests/integration/targets/pnpm/tasks/run.yml b/ansible_collections/community/general/tests/integration/targets/pnpm/tasks/run.yml
new file mode 100644
index 000000000..66f5eb622
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/pnpm/tasks/run.yml
@@ -0,0 +1,322 @@
+---
+# Copyright (c) 2023 Aritra Sen <aretrosen@proton.me>
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Download nodejs
+ ansible.builtin.unarchive:
+ src: "https://nodejs.org/dist/v{{ nodejs_version }}/{{ nodejs_path }}.tar.gz"
+ dest: "{{ remote_tmp_dir }}"
+ remote_src: true
+ creates: "{{ remote_tmp_dir }}/{{ nodejs_path }}.tar.gz"
+
+- name: Create a temporary directory for pnpm binary
+ ansible.builtin.tempfile:
+ state: directory
+ register: tmp_dir
+
+- name: Download pnpm binary to the temporary directory
+ ansible.builtin.get_url:
+ url: "https://github.com/pnpm/pnpm/releases/download/v{{ pnpm_version }}/{{ pnpm_path }}"
+ dest: "{{ tmp_dir.path }}/pnpm"
+ mode: "755"
+
+- name: Setting up pnpm via command
+ ansible.builtin.command: "{{ tmp_dir.path }}/pnpm setup --force"
+ environment:
+ PNPM_HOME: "{{ ansible_env.HOME }}/.local/share/pnpm"
+ SHELL: /bin/sh
+ ENV: "{{ ansible_env.HOME }}/.shrc"
+
+- name: Remove the temporary directory
+ ansible.builtin.file:
+ path: "{{ tmp_dir.path }}"
+ state: absent
+
+- name: Remove any previous Nodejs modules
+ ansible.builtin.file:
+ path: "{{ remote_tmp_dir }}/node_modules"
+ state: absent
+
+- name: CI tests to run
+ vars:
+ node_bin_path: "{{ remote_tmp_dir }}/{{ nodejs_path }}/bin"
+ pnpm_bin_path: "{{ ansible_env.HOME }}/.local/share/pnpm"
+ package: "tailwindcss"
+ environment:
+ PATH: "{{ node_bin_path }}:{{ ansible_env.PATH }}"
+
+ block:
+ - name: Create dummy package.json
+ ansible.builtin.template:
+ src: package.j2
+ dest: "{{ remote_tmp_dir }}/package.json"
+ mode: "644"
+
+ - name: Install reading-time package via package.json
+ pnpm:
+ path: "{{ remote_tmp_dir }}"
+ executable: "{{ pnpm_bin_path }}/pnpm"
+ state: present
+ environment:
+ PATH: "{{ node_bin_path }}:{{ ansible_env.PATH }}"
+
+ - name: Install the same package from package.json again
+ pnpm:
+ path: "{{ remote_tmp_dir }}"
+ executable: "{{ pnpm_bin_path }}/pnpm"
+ name: "reading-time"
+ state: present
+ environment:
+ PATH: "{{ node_bin_path }}:{{ ansible_env.PATH }}"
+ register: pnpm_install
+
+ - name: Assert that result is not changed
+ ansible.builtin.assert:
+ that:
+ - not (pnpm_install is changed)
+
+ - name: Install all packages in check mode
+ pnpm:
+ path: "{{ remote_tmp_dir }}"
+ executable: "{{ pnpm_bin_path }}/pnpm"
+ state: present
+ environment:
+ PATH: "{{ node_bin_path }}:{{ ansible_env.PATH }}"
+ check_mode: true
+ register: pnpm_install_check
+
+ - name: Verify test pnpm global installation in check mode
+ ansible.builtin.assert:
+ that:
+ - pnpm_install_check.err is defined
+ - pnpm_install_check.out is defined
+ - pnpm_install_check.err is none
+ - pnpm_install_check.out is none
+
+ - name: Install package without dependency
+ pnpm:
+ path: "{{ remote_tmp_dir }}"
+ executable: "{{ pnpm_bin_path }}/pnpm"
+ state: present
+ name: "{{ package }}"
+ environment:
+ PATH: "{{ node_bin_path }}:{{ ansible_env.PATH }}"
+ register: pnpm_install
+
+ - name: Assert that result is changed and successful
+ ansible.builtin.assert:
+ that:
+ - pnpm_install is success
+ - pnpm_install is changed
+
+ - name: Reinstall package without dependency
+ pnpm:
+ path: "{{ remote_tmp_dir }}"
+ executable: "{{ pnpm_bin_path }}/pnpm"
+ state: present
+ name: "{{ package }}"
+ environment:
+ PATH: "{{ node_bin_path }}:{{ ansible_env.PATH }}"
+ register: pnpm_reinstall
+
+ - name: Assert that there is no change
+ ansible.builtin.assert:
+ that:
+ - pnpm_reinstall is success
+ - not (pnpm_reinstall is changed)
+
+ - name: Reinstall package
+ pnpm:
+ path: "{{ remote_tmp_dir }}"
+ executable: "{{ pnpm_bin_path }}/pnpm"
+ state: latest
+ name: "{{ package }}"
+ environment:
+ PATH: "{{ node_bin_path }}:{{ ansible_env.PATH }}"
+ register: pnpm_fix_install
+
+ - name: Assert that result is changed and successful
+ ansible.builtin.assert:
+ that:
+ - pnpm_fix_install is success
+ - pnpm_fix_install is not changed
+
+ - name: Install package with version, without executable path
+ pnpm:
+ name: "{{ package }}"
+ version: 0.1.3
+ path: "{{ remote_tmp_dir }}"
+ state: present
+ environment:
+ PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
+ register: pnpm_install
+
+ - name: Assert that package with version is installed
+ ansible.builtin.assert:
+ that:
+ - pnpm_install is success
+ - pnpm_install is changed
+
+ - name: Reinstall package with version, without explicit executable path
+ pnpm:
+ name: "{{ package }}"
+ version: 0.1.3
+ path: "{{ remote_tmp_dir }}"
+ state: present
+ environment:
+ PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
+ register: pnpm_reinstall
+
+ - name: Assert that there is no change
+ ansible.builtin.assert:
+ that:
+ - pnpm_reinstall is success
+ - not (pnpm_reinstall is changed)
+
+ - name: Update package, without executable path
+ pnpm:
+ name: "{{ package }}"
+ path: "{{ remote_tmp_dir }}"
+ state: latest
+ environment:
+ PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
+ register: pnpm_update
+
+ - name: Assert that result is changed and successful
+ ansible.builtin.assert:
+ that:
+ - pnpm_update is success
+ - pnpm_update is changed
+
+ - name: Remove package, without executable path
+ pnpm:
+ name: "{{ package }}"
+ path: "{{ remote_tmp_dir }}"
+ state: absent
+ environment:
+ PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
+ register: pnpm_absent
+
+ - name: Assert that result is changed and successful
+ ansible.builtin.assert:
+ that:
+ - pnpm_absent is success
+ - pnpm_absent is changed
+
+ - name: Install package with version and alias, without executable path
+ pnpm:
+ name: "{{ package }}"
+ alias: tailwind-1
+ version: 0.1.3
+ path: "{{ remote_tmp_dir }}"
+ state: present
+ environment:
+ PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
+ register: pnpm_install
+
+ - name: Assert that package with version and alias is installed
+ ansible.builtin.assert:
+ that:
+ - pnpm_install is success
+ - pnpm_install is changed
+
+ - name: Reinstall package with version and alias, without explicit executable path
+ pnpm:
+ name: "{{ package }}"
+ alias: tailwind-1
+ version: 0.1.3
+ path: "{{ remote_tmp_dir }}"
+ state: present
+ environment:
+ PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
+ register: pnpm_reinstall
+
+ - name: Assert that there is no change
+ ansible.builtin.assert:
+ that:
+ - pnpm_reinstall is success
+ - not (pnpm_reinstall is changed)
+
+ - name: Remove package with alias, without executable path
+ pnpm:
+ name: tailwindcss
+ alias: tailwind-1
+ path: "{{ remote_tmp_dir }}"
+ state: absent
+ environment:
+ PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
+ register: pnpm_absent
+
+ - name: Assert that result is changed and successful
+ ansible.builtin.assert:
+ that:
+ - pnpm_absent is success
+ - pnpm_absent is changed
+
+ - name: Install package without dependency globally
+ pnpm:
+ name: "{{ package }}"
+ executable: "{{ pnpm_bin_path }}/pnpm"
+ state: present
+ global: true
+ environment:
+ PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
+ PNPM_HOME: "{{ pnpm_bin_path }}"
+ register: pnpm_install
+
+ - name: Assert that result is changed and successful
+ ansible.builtin.assert:
+ that:
+ - pnpm_install is success
+ - pnpm_install is changed
+
+ - name: Reinstall package globally, without explicit executable path
+ pnpm:
+ name: "{{ package }}"
+ state: present
+ global: true
+ environment:
+ PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
+ PNPM_HOME: "{{ pnpm_bin_path }}"
+ register: pnpm_reinstall
+
+ - name: Assert that there is no change
+ ansible.builtin.assert:
+ that:
+ - pnpm_reinstall is success
+ - not (pnpm_reinstall is changed)
+
+ - name: Updating package globally, without explicit executable path
+ pnpm:
+ name: "{{ package }}"
+ state: latest
+ global: true
+ environment:
+ PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
+ PNPM_HOME: "{{ pnpm_bin_path }}"
+ register: pnpm_reinstall
+
+ - name: Assert that there is no change
+ ansible.builtin.assert:
+ that:
+ - pnpm_reinstall is success
+ - pnpm_reinstall is not changed
+
+ - name: Remove package without dependency globally
+ pnpm:
+ name: "{{ package }}"
+ executable: "{{ pnpm_bin_path }}/pnpm"
+ global: true
+ state: absent
+ environment:
+ PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
+ PNPM_HOME: "{{ pnpm_bin_path }}"
+ register: pnpm_absent
+
+ - name: Assert that result is changed and successful
+ ansible.builtin.assert:
+ that:
+ - pnpm_absent is success
+ - pnpm_absent is changed
diff --git a/ansible_collections/community/general/tests/integration/targets/pnpm/templates/package.j2 b/ansible_collections/community/general/tests/integration/targets/pnpm/templates/package.j2
new file mode 100644
index 000000000..429ea9bee
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/pnpm/templates/package.j2
@@ -0,0 +1,13 @@
+{#
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
+#}
+{
+ "name": "ansible-pnpm-testing",
+ "version": "1.0.0",
+ "license": "MIT",
+ "dependencies": {
+ "reading-time": "^1.5.0"
+ }
+}
diff --git a/ansible_collections/community/general/tests/integration/targets/proxmox/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/proxmox/tasks/main.yml
index 22d7fcd29..1b529d111 100644
--- a/ansible_collections/community/general/tests/integration/targets/proxmox/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/proxmox/tasks/main.yml
@@ -129,6 +129,25 @@
- results_storage.proxmox_storages|length == 1
- results_storage.proxmox_storages[0].storage == "{{ storage }}"
+- name: List content on storage
+ proxmox_storage_contents_info:
+ api_host: "{{ api_host }}"
+ api_user: "{{ user }}@{{ domain }}"
+ api_password: "{{ api_password | default(omit) }}"
+ api_token_id: "{{ api_token_id | default(omit) }}"
+ api_token_secret: "{{ api_token_secret | default(omit) }}"
+ validate_certs: "{{ validate_certs }}"
+ storage: "{{ storage }}"
+ node: "{{ node }}"
+ content: images
+ register: results_list_storage
+
+- assert:
+ that:
+ - results_storage is not changed
+ - results_storage.proxmox_storage_content is defined
+ - results_storage.proxmox_storage_content |length == 1
+
- name: VM creation
tags: [ 'create' ]
block:
@@ -577,3 +596,20 @@
- results_kvm_destroy is changed
- results_kvm_destroy.vmid == {{ vmid }}
- results_kvm_destroy.msg == "VM {{ vmid }} removed"
+
+- name: Retrieve information about nodes
+ proxmox_node_info:
+ api_host: "{{ api_host }}"
+ api_user: "{{ user }}@{{ domain }}"
+ api_password: "{{ api_password | default(omit) }}"
+ api_token_id: "{{ api_token_id | default(omit) }}"
+ api_token_secret: "{{ api_token_secret | default(omit) }}"
+ validate_certs: "{{ validate_certs }}"
+ register: results
+
+- assert:
+ that:
+ - results is not changed
+ - results.proxmox_nodes is defined
+ - results.proxmox_nodes|length >= 1
+ - results.proxmox_nodes[0].type == 'node'
diff --git a/ansible_collections/community/general/tests/integration/targets/proxmox_pool/aliases b/ansible_collections/community/general/tests/integration/targets/proxmox_pool/aliases
new file mode 100644
index 000000000..525dcd332
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/proxmox_pool/aliases
@@ -0,0 +1,7 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+unsupported
+proxmox_pool
+proxmox_pool_member
diff --git a/ansible_collections/community/general/tests/integration/targets/proxmox_pool/defaults/main.yml b/ansible_collections/community/general/tests/integration/targets/proxmox_pool/defaults/main.yml
new file mode 100644
index 000000000..5a518ac73
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/proxmox_pool/defaults/main.yml
@@ -0,0 +1,7 @@
+# Copyright (c) 2023, Sergei Antipov <greendayonfire at gmail.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+poolid: test
+member: local
+member_type: storage
diff --git a/ansible_collections/community/general/tests/integration/targets/proxmox_pool/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/proxmox_pool/tasks/main.yml
new file mode 100644
index 000000000..2b22960f2
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/proxmox_pool/tasks/main.yml
@@ -0,0 +1,220 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) 2023, Sergei Antipov <greendayonfire at gmail.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Proxmox VE pool and pool membership management
+ tags: ["pool"]
+ block:
+ - name: Make sure poolid parameter is not missing
+ proxmox_pool:
+ api_host: "{{ api_host }}"
+ api_user: "{{ user }}@{{ domain }}"
+ api_password: "{{ api_password | default(omit) }}"
+ api_token_id: "{{ api_token_id | default(omit) }}"
+ api_token_secret: "{{ api_token_secret | default(omit) }}"
+ validate_certs: "{{ validate_certs }}"
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "'missing required arguments: poolid' in result.msg"
+
+ - name: Create pool (Check)
+ proxmox_pool:
+ api_host: "{{ api_host }}"
+ api_user: "{{ user }}@{{ domain }}"
+ api_password: "{{ api_password | default(omit) }}"
+ api_token_id: "{{ api_token_id | default(omit) }}"
+ api_token_secret: "{{ api_token_secret | default(omit) }}"
+ validate_certs: "{{ validate_certs }}"
+ poolid: "{{ poolid }}"
+ check_mode: true
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result is success
+
+ - name: Create pool
+ proxmox_pool:
+ api_host: "{{ api_host }}"
+ api_user: "{{ user }}@{{ domain }}"
+ api_password: "{{ api_password | default(omit) }}"
+ api_token_id: "{{ api_token_id | default(omit) }}"
+ api_token_secret: "{{ api_token_secret | default(omit) }}"
+ validate_certs: "{{ validate_certs }}"
+ poolid: "{{ poolid }}"
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result is success
+ - result.poolid == "{{ poolid }}"
+
+ - name: Delete pool (Check)
+ proxmox_pool:
+ api_host: "{{ api_host }}"
+ api_user: "{{ user }}@{{ domain }}"
+ api_password: "{{ api_password | default(omit) }}"
+ api_token_id: "{{ api_token_id | default(omit) }}"
+ api_token_secret: "{{ api_token_secret | default(omit) }}"
+ validate_certs: "{{ validate_certs }}"
+ poolid: "{{ poolid }}"
+ state: absent
+ check_mode: true
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result is success
+
+ - name: Delete non-existing pool should do nothing
+ proxmox_pool:
+ api_host: "{{ api_host }}"
+ api_user: "{{ user }}@{{ domain }}"
+ api_password: "{{ api_password | default(omit) }}"
+ api_token_id: "{{ api_token_id | default(omit) }}"
+ api_token_secret: "{{ api_token_secret | default(omit) }}"
+ validate_certs: "{{ validate_certs }}"
+ poolid: "non-existing-poolid"
+ state: absent
+ register: result
+
+ - assert:
+ that:
+ - result is not changed
+ - result is success
+
+ - name: Deletion of non-empty pool fails
+ block:
+ - name: Add storage into pool
+ proxmox_pool_member:
+ api_host: "{{ api_host }}"
+ api_user: "{{ user }}@{{ domain }}"
+ api_password: "{{ api_password | default(omit) }}"
+ api_token_id: "{{ api_token_id | default(omit) }}"
+ api_token_secret: "{{ api_token_secret | default(omit) }}"
+ validate_certs: "{{ validate_certs }}"
+ poolid: "{{ poolid }}"
+ member: "{{ member }}"
+ type: "{{ member_type }}"
+ diff: true
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result is success
+ - "'{{ member }}' in result.diff.after.members"
+
+ - name: Add non-existing storage into pool should fail
+ proxmox_pool_member:
+ api_host: "{{ api_host }}"
+ api_user: "{{ user }}@{{ domain }}"
+ api_password: "{{ api_password | default(omit) }}"
+ api_token_id: "{{ api_token_id | default(omit) }}"
+ api_token_secret: "{{ api_token_secret | default(omit) }}"
+ validate_certs: "{{ validate_certs }}"
+ poolid: "{{ poolid }}"
+ member: "non-existing-storage"
+ type: "{{ member_type }}"
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "'Storage non-existing-storage doesn\\'t exist in the cluster' in result.msg"
+
+ - name: Delete non-empty pool
+ proxmox_pool:
+ api_host: "{{ api_host }}"
+ api_user: "{{ user }}@{{ domain }}"
+ api_password: "{{ api_password | default(omit) }}"
+ api_token_id: "{{ api_token_id | default(omit) }}"
+ api_token_secret: "{{ api_token_secret | default(omit) }}"
+ validate_certs: "{{ validate_certs }}"
+ poolid: "{{ poolid }}"
+ state: absent
+ ignore_errors: true
+ register: result
+
+ - assert:
+ that:
+ - result is failed
+ - "'Please remove members from pool first.' in result.msg"
+
+ - name: Delete storage from the pool
+ proxmox_pool_member:
+ api_host: "{{ api_host }}"
+ api_user: "{{ user }}@{{ domain }}"
+ api_password: "{{ api_password | default(omit) }}"
+ api_token_id: "{{ api_token_id | default(omit) }}"
+ api_token_secret: "{{ api_token_secret | default(omit) }}"
+ validate_certs: "{{ validate_certs }}"
+ poolid: "{{ poolid }}"
+ member: "{{ member }}"
+ type: "{{ member_type }}"
+ state: absent
+ register: result
+
+ - assert:
+ that:
+ - result is success
+ - result is changed
+
+ rescue:
+ - name: Delete storage from the pool if it is added
+ proxmox_pool_member:
+ api_host: "{{ api_host }}"
+ api_user: "{{ user }}@{{ domain }}"
+ api_password: "{{ api_password | default(omit) }}"
+ api_token_id: "{{ api_token_id | default(omit) }}"
+ api_token_secret: "{{ api_token_secret | default(omit) }}"
+ validate_certs: "{{ validate_certs }}"
+ poolid: "{{ poolid }}"
+ member: "{{ member }}"
+ type: "{{ member_type }}"
+ state: absent
+ ignore_errors: true
+
+ - name: Delete pool
+ proxmox_pool:
+ api_host: "{{ api_host }}"
+ api_user: "{{ user }}@{{ domain }}"
+ api_password: "{{ api_password | default(omit) }}"
+ api_token_id: "{{ api_token_id | default(omit) }}"
+ api_token_secret: "{{ api_token_secret | default(omit) }}"
+ validate_certs: "{{ validate_certs }}"
+ poolid: "{{ poolid }}"
+ state: absent
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result is success
+ - result.poolid == "{{ poolid }}"
+
+ rescue:
+ - name: Delete test pool if it is created
+ proxmox_pool:
+ api_host: "{{ api_host }}"
+ api_user: "{{ user }}@{{ domain }}"
+ api_password: "{{ api_password | default(omit) }}"
+ api_token_id: "{{ api_token_id | default(omit) }}"
+ api_token_secret: "{{ api_token_secret | default(omit) }}"
+ validate_certs: "{{ validate_certs }}"
+ poolid: "{{ poolid }}"
+ state: absent
+ ignore_errors: true
diff --git a/ansible_collections/community/general/tests/integration/targets/proxmox_template/aliases b/ansible_collections/community/general/tests/integration/targets/proxmox_template/aliases
new file mode 100644
index 000000000..5d9af8101
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/proxmox_template/aliases
@@ -0,0 +1,6 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+unsupported
+proxmox_template
diff --git a/ansible_collections/community/general/tests/integration/targets/proxmox_template/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/proxmox_template/tasks/main.yml
new file mode 100644
index 000000000..2d1187e89
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/proxmox_template/tasks/main.yml
@@ -0,0 +1,136 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Copyright (c) 2023, Sergei Antipov <greendayonfire at gmail.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Proxmox VE virtual machines templates management
+ tags: ['template']
+ vars:
+ filename: /tmp/dummy.iso
+ block:
+ - name: Create dummy ISO file
+ ansible.builtin.command:
+ cmd: 'truncate -s 300M {{ filename }}'
+
+ - name: Delete requests_toolbelt module if it is installed
+ ansible.builtin.pip:
+ name: requests_toolbelt
+ state: absent
+
+ - name: Install latest proxmoxer
+ ansible.builtin.pip:
+ name: proxmoxer
+ state: latest
+
+ - name: Upload ISO as template to Proxmox VE cluster should fail
+ proxmox_template:
+ api_host: '{{ api_host }}'
+ api_user: '{{ user }}@{{ domain }}'
+ api_password: '{{ api_password | default(omit) }}'
+ api_token_id: '{{ api_token_id | default(omit) }}'
+ api_token_secret: '{{ api_token_secret | default(omit) }}'
+ validate_certs: '{{ validate_certs }}'
+ node: '{{ node }}'
+ src: '{{ filename }}'
+ content_type: iso
+ force: true
+ register: result
+ ignore_errors: true
+
+ - assert:
+ that:
+ - result is failed
+ - result.msg is match('\'requests_toolbelt\' module is required to upload files larger than 256MB')
+
+ - name: Install old (1.1.2) version of proxmoxer
+ ansible.builtin.pip:
+ name: proxmoxer==1.1.1
+ state: present
+
+ - name: Upload ISO as template to Proxmox VE cluster should be successful
+ proxmox_template:
+ api_host: '{{ api_host }}'
+ api_user: '{{ user }}@{{ domain }}'
+ api_password: '{{ api_password | default(omit) }}'
+ api_token_id: '{{ api_token_id | default(omit) }}'
+ api_token_secret: '{{ api_token_secret | default(omit) }}'
+ validate_certs: '{{ validate_certs }}'
+ node: '{{ node }}'
+ src: '{{ filename }}'
+ content_type: iso
+ force: true
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result is success
+ - result.msg is match('template with volid=local:iso/dummy.iso uploaded')
+
+ - name: Install latest proxmoxer
+ ansible.builtin.pip:
+ name: proxmoxer
+ state: latest
+
+ - name: Make smaller dummy file
+ ansible.builtin.command:
+ cmd: 'truncate -s 128M {{ filename }}'
+
+ - name: Upload ISO as template to Proxmox VE cluster should be successful
+ proxmox_template:
+ api_host: '{{ api_host }}'
+ api_user: '{{ user }}@{{ domain }}'
+ api_password: '{{ api_password | default(omit) }}'
+ api_token_id: '{{ api_token_id | default(omit) }}'
+ api_token_secret: '{{ api_token_secret | default(omit) }}'
+ validate_certs: '{{ validate_certs }}'
+ node: '{{ node }}'
+ src: '{{ filename }}'
+ content_type: iso
+ force: true
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result is success
+ - result.msg is match('template with volid=local:iso/dummy.iso uploaded')
+
+ - name: Install requests_toolbelt
+ ansible.builtin.pip:
+ name: requests_toolbelt
+ state: present
+
+ - name: Make big dummy file
+ ansible.builtin.command:
+ cmd: 'truncate -s 300M {{ filename }}'
+
+ - name: Upload ISO as template to Proxmox VE cluster should be successful
+ proxmox_template:
+ api_host: '{{ api_host }}'
+ api_user: '{{ user }}@{{ domain }}'
+ api_password: '{{ api_password | default(omit) }}'
+ api_token_id: '{{ api_token_id | default(omit) }}'
+ api_token_secret: '{{ api_token_secret | default(omit) }}'
+ validate_certs: '{{ validate_certs }}'
+ node: '{{ node }}'
+ src: '{{ filename }}'
+ content_type: iso
+ force: true
+ register: result
+
+ - assert:
+ that:
+ - result is changed
+ - result is success
+ - result.msg is match('template with volid=local:iso/dummy.iso uploaded')
+
+ always:
+ - name: Delete ISO file from host
+ ansible.builtin.file:
+ path: '{{ filename }}'
+ state: absent
diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/pagination.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/pagination.yml
index 5a674b801..b2429e39f 100644
--- a/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/pagination.yml
+++ b/ansible_collections/community/general/tests/integration/targets/scaleway_compute/tasks/pagination.yml
@@ -23,7 +23,7 @@
commercial_type: '{{ scaleway_commerial_type }}'
wait: true
-- name: Get server informations of the first page
+- name: Get server information of the first page
scaleway_server_info:
region: par1
query_parameters:
@@ -37,7 +37,7 @@
that:
- first_page is success
-- name: Get server informations of the second page
+- name: Get server information of the second page
scaleway_server_info:
region: par1
query_parameters:
diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry/tasks/main.yml
index 91cea20f3..84d733a10 100644
--- a/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/scaleway_container_registry/tasks/main.yml
@@ -159,7 +159,7 @@
- cr_deletion_task is success
- cr_deletion_task is changed
-- name: Delete container regitry (Confirmation)
+- name: Delete container registry (Confirmation)
community.general.scaleway_container_registry:
state: absent
name: '{{ name }}'
diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_image_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_image_info/tasks/main.yml
index 2cdf34fdd..3dfe20f37 100644
--- a/ansible_collections/community/general/tests/integration/targets/scaleway_image_info/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/scaleway_image_info/tasks/main.yml
@@ -8,7 +8,7 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-- name: Get image informations and register it in a variable
+- name: Get image information and register it in a variable
scaleway_image_info:
region: par1
register: images
@@ -22,7 +22,7 @@
that:
- images is success
-- name: Get image informations from ams1 and register it in a variable
+- name: Get image information from ams1 and register it in a variable
scaleway_image_info:
region: ams1
register: images_ams1
diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_ip_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_ip_info/tasks/main.yml
index b560b5658..6aad9b52e 100644
--- a/ansible_collections/community/general/tests/integration/targets/scaleway_ip_info/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/scaleway_ip_info/tasks/main.yml
@@ -8,7 +8,7 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-- name: Get ip informations and register it in a variable
+- name: Get ip information and register it in a variable
scaleway_ip_info:
region: par1
register: ips
@@ -22,7 +22,7 @@
that:
- ips is success
-- name: Get ip informations and register it in a variable
+- name: Get ip information and register it in a variable
scaleway_ip_info:
region: ams1
register: ips_ams1
diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_organization_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_organization_info/tasks/main.yml
index 7326ca226..247a4012b 100644
--- a/ansible_collections/community/general/tests/integration/targets/scaleway_organization_info/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/scaleway_organization_info/tasks/main.yml
@@ -8,7 +8,7 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-- name: Get organization informations and register it in a variable
+- name: Get organization information and register it in a variable
scaleway_organization_info:
register: organizations
diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_info/tasks/main.yml
index 8029a1e9a..608d76409 100644
--- a/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_info/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/scaleway_security_group_info/tasks/main.yml
@@ -8,7 +8,7 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-- name: Get security group informations and register it in a variable
+- name: Get security group information and register it in a variable
scaleway_security_group_info:
region: par1
register: security_groups
@@ -22,7 +22,7 @@
that:
- security_groups is success
-- name: Get security group informations and register it in a variable (AMS1)
+- name: Get security group information and register it in a variable (AMS1)
scaleway_security_group_info:
region: ams1
register: ams1_security_groups
diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_server_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_server_info/tasks/main.yml
index 7274e8a85..516b2df0e 100644
--- a/ansible_collections/community/general/tests/integration/targets/scaleway_server_info/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/scaleway_server_info/tasks/main.yml
@@ -8,7 +8,7 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-- name: Get server informations and register it in a variable
+- name: Get server information and register it in a variable
scaleway_server_info:
region: par1
register: servers
@@ -22,7 +22,7 @@
that:
- servers is success
-- name: Get server informations and register it in a variable
+- name: Get server information and register it in a variable
scaleway_server_info:
region: ams1
register: ams1_servers
diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_snapshot_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_snapshot_info/tasks/main.yml
index 44f15d515..f80a9ced1 100644
--- a/ansible_collections/community/general/tests/integration/targets/scaleway_snapshot_info/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/scaleway_snapshot_info/tasks/main.yml
@@ -8,7 +8,7 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-- name: Get snapshot informations and register it in a variable
+- name: Get snapshot information and register it in a variable
scaleway_snapshot_info:
region: par1
register: snapshots
@@ -22,7 +22,7 @@
that:
- snapshots is success
-- name: Get snapshot informations and register it in a variable (AMS1)
+- name: Get snapshot information and register it in a variable (AMS1)
scaleway_snapshot_info:
region: ams1
register: ams1_snapshots
diff --git a/ansible_collections/community/general/tests/integration/targets/scaleway_volume_info/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/scaleway_volume_info/tasks/main.yml
index 45995a54c..2202e6bf9 100644
--- a/ansible_collections/community/general/tests/integration/targets/scaleway_volume_info/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/scaleway_volume_info/tasks/main.yml
@@ -8,7 +8,7 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-- name: Get volume informations and register it in a variable
+- name: Get volume information and register it in a variable
scaleway_volume_info:
region: par1
register: volumes
@@ -22,7 +22,7 @@
that:
- volumes is success
-- name: Get volume informations and register it in a variable (AMS1)
+- name: Get volume information and register it in a variable (AMS1)
scaleway_volume_info:
region: ams1
register: ams1_volumes
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_docker/handlers/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_docker/handlers/main.yml
index 283496714..52e13e3c4 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_docker/handlers/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/setup_docker/handlers/main.yml
@@ -3,17 +3,29 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
+- name: Remove python requests
+ ansible.builtin.pip:
+ name:
+ - requests
+ state: absent
+
+- name: Stop docker service
+ become: true
+ ansible.builtin.service:
+ name: docker
+ state: stopped
+
- name: Remove Docker packages
- package:
+ ansible.builtin.package:
name: "{{ docker_packages }}"
state: absent
- name: "D-Fedora : Remove repository"
- file:
+ ansible.builtin.file:
path: /etc/yum.repos.d/docker-ce.repo
state: absent
- name: "D-Fedora : Remove dnf-plugins-core"
- package:
+ ansible.builtin.package:
name: dnf-plugins-core
state: absent
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_docker/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_docker/tasks/main.yml
index 4f41da31a..19bc7aa8c 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_docker/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/setup_docker/tasks/main.yml
@@ -41,6 +41,7 @@
ansible.builtin.service:
name: docker
state: started
+ notify: Stop docker service
- name: Cheat on the docker socket permissions
become: true
@@ -53,3 +54,4 @@
name:
- requests
state: present
+ notify: Remove python requests
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_etcd3/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_etcd3/tasks/main.yml
index fe6b9cd02..1da52e225 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_etcd3/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/setup_etcd3/tasks/main.yml
@@ -50,7 +50,7 @@
state: present
# Check if re-installing etcd3 is required
- - name: Check if etcd3ctl exists for re-use.
+ - name: Check if etcd3ctl exists for reuse.
shell: "ETCDCTL_API=3 {{ etcd3_path }}/etcdctl --endpoints=localhost:2379 get foo"
args:
executable: /bin/bash
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/tasks/main.yml
index 2ab57d59d..9f156425d 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/tasks/main.yml
@@ -16,11 +16,19 @@
}}
- name: Include OS-specific variables
- include_vars: '{{ ansible_os_family }}.yml'
+ include_vars: '{{ lookup("first_found", params) }}'
+ vars:
+ params:
+ files:
+ - '{{ ansible_distribution }}-{{ ansible_distribution_version }}.yml'
+ - '{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml'
+ - '{{ ansible_os_family }}.yml'
+ paths:
+ - '{{ role_path }}/vars'
when: has_java_keytool
- name: Install keytool
package:
- name: '{{ keytool_package_name }}'
+ name: '{{ keytool_package_names }}'
become: true
when: has_java_keytool
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Alpine.yml b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Alpine.yml
index 4ff75ae8c..c314c80ba 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Alpine.yml
+++ b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Alpine.yml
@@ -3,4 +3,5 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-keytool_package_name: openjdk11-jre-headless
+keytool_package_names:
+ - openjdk11-jre-headless
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Archlinux.yml b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Archlinux.yml
index 9e29065b3..b342911b6 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Archlinux.yml
+++ b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Archlinux.yml
@@ -3,4 +3,5 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-keytool_package_name: jre11-openjdk-headless
+keytool_package_names:
+ - jre11-openjdk-headless
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Debian-12.yml b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Debian-12.yml
new file mode 100644
index 000000000..17f8a53d0
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Debian-12.yml
@@ -0,0 +1,8 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+keytool_package_names:
+ - ca-certificates-java
+ - openjdk-17-jre-headless
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Debian.yml b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Debian.yml
index 30ae5cd04..cb5551a58 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Debian.yml
+++ b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Debian.yml
@@ -3,4 +3,5 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-keytool_package_name: ca-certificates-java
+keytool_package_names:
+ - ca-certificates-java
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/RedHat.yml b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/RedHat.yml
index c200091f8..8f4126e5d 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/RedHat.yml
+++ b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/RedHat.yml
@@ -3,4 +3,5 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-keytool_package_name: java-11-openjdk-headless
+keytool_package_names:
+ - java-11-openjdk-headless
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Suse.yml b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Suse.yml
index c200091f8..8f4126e5d 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Suse.yml
+++ b/ansible_collections/community/general/tests/integration/targets/setup_java_keytool/vars/Suse.yml
@@ -3,4 +3,5 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-keytool_package_name: java-11-openjdk-headless
+keytool_package_names:
+ - java-11-openjdk-headless
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/cert_cnconfig.ldif b/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/cert_cnconfig.ldif
new file mode 100644
index 000000000..fc97e5f5c
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/cert_cnconfig.ldif
@@ -0,0 +1,15 @@
+dn: cn=config
+add: olcTLSCACertificateFile
+olcTLSCACertificateFile: /usr/local/share/ca-certificates/ca.crt
+-
+add: olcTLSCertificateFile
+olcTLSCertificateFile: /etc/ldap/localhost.crt
+-
+add: olcTLSCertificateKeyFile
+olcTLSCertificateKeyFile: /etc/ldap/localhost.key
+-
+add: olcAuthzRegexp
+olcAuthzRegexp: {0}"UID=([^,]*)" uid=$1,ou=users,dc=example,dc=com
+-
+add: olcTLSVerifyClient
+olcTLSVerifyClient: allow
diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.11.txt.license b/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/cert_cnconfig.ldif.license
index edff8c768..edff8c768 100644
--- a/ansible_collections/community/general/tests/sanity/ignore-2.11.txt.license
+++ b/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/cert_cnconfig.ldif.license
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/initial_config.ldif b/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/initial_config.ldif
index 8f8c537bd..cb21f2cfd 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/initial_config.ldif
+++ b/ansible_collections/community/general/tests/integration/targets/setup_openldap/files/initial_config.ldif
@@ -18,5 +18,24 @@ homeDirectory: /home/ldaptest
cn: LDAP Test
gecos: LDAP Test
displayName: LDAP Test
+userPassword: test1pass!
mail: ldap.test@example.com
sn: Test
+
+dn: uid=second,ou=users,dc=example,dc=com
+uid: second
+uidNumber: 1112
+gidNUmber: 102
+objectClass: top
+objectClass: posixAccount
+objectClass: shadowAccount
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+loginShell: /bin/sh
+homeDirectory: /home/second
+cn: Second Test
+gecos: Second Test
+displayName: Second Test
+mail: second.test@example.com
+sn: Test
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_openldap/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_openldap/tasks/main.yml
index 25077de16..00f8f6a10 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_openldap/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/setup_openldap/tasks/main.yml
@@ -44,6 +44,22 @@
cmd: "export DEBIAN_FRONTEND=noninteractive; cat /root/debconf-slapd.conf | debconf-set-selections; dpkg-reconfigure -f noninteractive slapd"
creates: "/root/slapd_configured"
+ - name: Enable secure ldap
+ lineinfile:
+ path: /etc/default/slapd
+ regexp: "^SLAPD_SERVICES"
+ line: 'SLAPD_SERVICES="ldap:/// ldaps:/// ldapi:///"'
+
+ - name: Create certificates
+ shell: |
+ openssl req -x509 -batch -sha256 -days 1825 -newkey rsa:2048 -nodes -keyout /root/ca.key -out /usr/local/share/ca-certificates/ca.crt
+ openssl req -batch -sha256 -days 365 -newkey rsa:2048 -subj "/CN=$(hostname)" -addext "subjectAltName = DNS:localhost" -nodes -keyout /etc/ldap/localhost.key -out /etc/ldap/localhost.csr
+ openssl x509 -req -CA /usr/local/share/ca-certificates/ca.crt -CAkey /root/ca.key -CAcreateserial -in /etc/ldap/localhost.csr -out /etc/ldap/localhost.crt
+ chgrp openldap /etc/ldap/localhost.key
+ chmod 0640 /etc/ldap/localhost.key
+ openssl req -batch -sha256 -days 365 -newkey rsa:2048 -subj "/UID=ldaptest" -nodes -keyout /root/user.key -out /root/user.csr
+ openssl x509 -req -CA /usr/local/share/ca-certificates/ca.crt -CAkey /root/ca.key -CAcreateserial -in /root/user.csr -out /root/user.crt
+
- name: Start OpenLDAP service
become: true
service:
@@ -61,10 +77,14 @@
mode: '0644'
loop:
- rootpw_cnconfig.ldif
+ - cert_cnconfig.ldif
- initial_config.ldif
- name: Configure admin password for cn=config
- shell: "ldapmodify -Y EXTERNAL -H ldapi:/// -f /tmp/rootpw_cnconfig.ldif"
+ shell: "ldapmodify -Y EXTERNAL -H ldapi:/// -f /tmp/{{ item }}"
+ loop:
+ - rootpw_cnconfig.ldif
+ - cert_cnconfig.ldif
- name: Add initial config
become: true
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_pkg_mgr/tasks/archlinux.yml b/ansible_collections/community/general/tests/integration/targets/setup_pkg_mgr/tasks/archlinux.yml
index fc75f84df..f471cc19a 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_pkg_mgr/tasks/archlinux.yml
+++ b/ansible_collections/community/general/tests/integration/targets/setup_pkg_mgr/tasks/archlinux.yml
@@ -21,3 +21,8 @@
update_cache: true
upgrade: true
when: archlinux_upgrade_tag is changed
+
+- name: Remove EXTERNALLY-MANAGED file
+ file:
+ path: /usr/lib/python{{ ansible_python.version.major }}.{{ ansible_python.version.minor }}/EXTERNALLY-MANAGED
+ state: absent
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/tasks/main.yml
index 3dac4a098..99668ebc9 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/tasks/main.yml
@@ -127,6 +127,32 @@
seconds: 5
when: ansible_os_family == 'Suse'
+- name: Make installable on Arch
+ community.general.ini_file:
+ path: /usr/lib/systemd/system/postgresql.service
+ section: Service
+ option: "{{ item }}"
+ state: absent
+ loop:
+ - PrivateTmp
+ - ProtectHome
+ - ProtectSystem
+ - NoNewPrivileges
+ - ProtectControlGroups
+ - ProtectKernelModules
+ - ProtectKernelTunables
+ - PrivateDevices
+ - RestrictAddressFamilies
+ - RestrictNamespaces
+ - RestrictRealtime
+ - SystemCallArchitectures
+ when: ansible_distribution == 'Archlinux'
+
+- name: Make installable on Arch
+ systemd:
+ daemon_reload: true
+ when: ansible_distribution == 'Archlinux'
+
- name: Initialize postgres (Suse)
service: name=postgresql state=started
when: ansible_os_family == 'Suse'
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Debian-12-py3.yml b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Debian-12-py3.yml
new file mode 100644
index 000000000..c92240237
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/setup_postgresql_db/vars/Debian-12-py3.yml
@@ -0,0 +1,13 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+postgresql_packages:
+ - "postgresql"
+ - "postgresql-common"
+ - "python3-psycopg2"
+
+pg_hba_location: "/etc/postgresql/15/main/pg_hba.conf"
+pg_dir: "/var/lib/postgresql/15/main"
+pg_ver: 15
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-9.2.yml b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-9.2.yml
new file mode 100644
index 000000000..5bbfaff12
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-9.2.yml
@@ -0,0 +1,6 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Do nothing
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-9.3.yml b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-9.3.yml
new file mode 100644
index 000000000..5bbfaff12
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/setup_snap/tasks/D-RedHat-9.3.yml
@@ -0,0 +1,6 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# Do nothing
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_certificate.pem b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_certificate.pem
index 130e0a2da..a438d9266 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_certificate.pem
+++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_certificate.pem
@@ -1,7 +1,3 @@
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-----BEGIN CERTIFICATE-----
MIIDAjCCAeqgAwIBAgIJANguFROhaWocMA0GCSqGSIb3DQEBCwUAMDExIDAeBgNV
BAMMF1RMU0dlblNlbGZTaWduZWR0Um9vdENBMQ0wCwYDVQQHDAQkJCQkMB4XDTE5
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_certificate.pem.license b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_certificate.pem.license
new file mode 100644
index 000000000..a1390a69e
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_certificate.pem.license
@@ -0,0 +1,3 @@
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_key.pem b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_key.pem
index d9dc5ca0f..0a950eda0 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_key.pem
+++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_key.pem
@@ -1,7 +1,3 @@
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDqVt84czSxWnWW
4Ng6hmKE3NarbLsycwtjrYBokV7Kk7Mp7PrBbYF05FOgSdJLvL6grlRSQK2VPsXd
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_key.pem.license b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_key.pem.license
new file mode 100644
index 000000000..a1390a69e
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/ca_key.pem.license
@@ -0,0 +1,3 @@
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_certificate.pem b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_certificate.pem
index 9e956e6b0..501d83897 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_certificate.pem
+++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_certificate.pem
@@ -1,7 +1,3 @@
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-----BEGIN CERTIFICATE-----
MIIDRjCCAi6gAwIBAgIBAjANBgkqhkiG9w0BAQsFADAxMSAwHgYDVQQDDBdUTFNH
ZW5TZWxmU2lnbmVkdFJvb3RDQTENMAsGA1UEBwwEJCQkJDAeFw0xOTAxMTEwODMz
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_certificate.pem.license b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_certificate.pem.license
new file mode 100644
index 000000000..a1390a69e
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_certificate.pem.license
@@ -0,0 +1,3 @@
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_key.pem b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_key.pem
index 3848ad7cf..850260a87 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_key.pem
+++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_key.pem
@@ -1,7 +1,3 @@
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAqDPjkNxwpwlAAM/Shhk8FgfYUG1HwGV5v7LZW9v7jgKd6zcM
QJQrP4IspgRxOiLupqytNOlZ/mfYm6iKw9i7gjsXLtucvIKKhutk4HT+bGvcEfuf
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_key.pem.license b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_key.pem.license
new file mode 100644
index 000000000..a1390a69e
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/client_key.pem.license
@@ -0,0 +1,3 @@
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_certificate.pem b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_certificate.pem
index b714ddbfb..4a0ebc6ec 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_certificate.pem
+++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_certificate.pem
@@ -1,7 +1,3 @@
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-----BEGIN CERTIFICATE-----
MIIDRjCCAi6gAwIBAgIBATANBgkqhkiG9w0BAQsFADAxMSAwHgYDVQQDDBdUTFNH
ZW5TZWxmU2lnbmVkdFJvb3RDQTENMAsGA1UEBwwEJCQkJDAeFw0xOTAxMTEwODMz
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_certificate.pem.license b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_certificate.pem.license
new file mode 100644
index 000000000..a1390a69e
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_certificate.pem.license
@@ -0,0 +1,3 @@
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_key.pem b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_key.pem
index ec0134993..c79ab6480 100644
--- a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_key.pem
+++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_key.pem
@@ -1,7 +1,3 @@
-# Copyright (c) Ansible Project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAyMBKx8AHrEQX3fR4mZJgd1WIdvHNUJBPSPJ2MhySl9mQVIQM
yvofNAZHEySfeNuualsgAh/8JeeF3v6HxVBaxmuL89Ks+FJC/yiNDhsNvGOKpyna
diff --git a/ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_key.pem.license b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_key.pem.license
new file mode 100644
index 000000000..a1390a69e
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/setup_tls/files/server_key.pem.license
@@ -0,0 +1,3 @@
+Copyright (c) Ansible Project
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
diff --git a/ansible_collections/community/general/tests/integration/targets/shutdown/aliases b/ansible_collections/community/general/tests/integration/targets/shutdown/aliases
index afda346c4..428e8289d 100644
--- a/ansible_collections/community/general/tests/integration/targets/shutdown/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/shutdown/aliases
@@ -3,3 +3,4 @@
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/1
+destructive
diff --git a/ansible_collections/community/general/tests/integration/targets/shutdown/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/shutdown/tasks/main.yml
index dadeb6269..2c9bc6bd6 100644
--- a/ansible_collections/community/general/tests/integration/targets/shutdown/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/shutdown/tasks/main.yml
@@ -7,13 +7,7 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-
-- name: Install systemd-sysv on Ubuntu 18 and Debian
- apt:
- name: systemd-sysv
- state: present
- when: (ansible_distribution == 'Ubuntu' and ansible_distribution_major_version is version('18', '>=')) or (ansible_distribution == 'Debian')
- register: systemd_sysv_install
+#
- name: Execute shutdown with custom message and delay
community.general.shutdown:
@@ -34,29 +28,44 @@
- '"Custom Message" in shutdown_result["shutdown_command"]'
- '"Shut down initiated by Ansible" in shutdown_result_minus["shutdown_command"]'
- '"Custom Message" not in shutdown_result_minus["shutdown_command"]'
- when: ansible_os_family not in ['Alpine', 'AIX']
+ when:
+ - 'ansible_os_family not in ["Alpine", "AIX"]'
+ - '"systemctl" not in shutdown_result["shutdown_command"]'
+ - '"systemctl" not in shutdown_result_minus["shutdown_command"]'
-- name: Verify shutdown command is present except Alpine, VMKernel
+- name: Verify shutdown command is present except Alpine or AIX or systemd
assert:
that: '"shutdown" in shutdown_result["shutdown_command"]'
- when: ansible_os_family != 'Alpine' and ansible_system != 'VMKernel'
+ when:
+ - "ansible_os_family != 'Alpine'"
+ - "ansible_system != 'VMKernel'"
+ - '"systemctl" not in shutdown_result["shutdown_command"]'
-- name: Verify shutdown command is present in Alpine
+- name: Verify shutdown command is present in Alpine except systemd
assert:
that: '"poweroff" in shutdown_result["shutdown_command"]'
- when: ansible_os_family == 'Alpine'
+ when:
+ - "ansible_os_family == 'Alpine'"
+ - '"systemctl" not in shutdown_result["shutdown_command"]'
-- name: Verify shutdown command is present in VMKernel
+
+- name: Verify shutdown command is present in VMKernel except systemd
assert:
that: '"halt" in shutdown_result["shutdown_command"]'
- when: ansible_system == 'VMKernel'
+ when:
+ - "ansible_system == 'VMKernel'"
+ - '"systemctl" not in shutdown_result["shutdown_command"]'
-- name: Verify shutdown delay is present in minutes in Linux
+- name: Verify shutdown delay is present in minutes in Linux except systemd
assert:
that:
- '"-h 1" in shutdown_result["shutdown_command"]'
- '"-h 0" in shutdown_result_minus["shutdown_command"]'
- when: ansible_system == 'Linux' and ansible_os_family != 'Alpine'
+ when:
+ - "ansible_system == 'Linux'"
+ - "ansible_os_family != 'Alpine'"
+ - '"systemctl" not in shutdown_result["shutdown_command"]'
+ - '"systemctl" not in shutdown_result_minus["shutdown_command"]'
- name: Verify shutdown delay is present in minutes in Void, MacOSX, OpenBSD
assert:
@@ -68,8 +77,8 @@
- name: Verify shutdown delay is present in seconds in FreeBSD
assert:
that:
- - '"-h +100s" in shutdown_result["shutdown_command"]'
- - '"-h +0s" in shutdown_result_minus["shutdown_command"]'
+ - '"-p +100s" in shutdown_result["shutdown_command"]'
+ - '"-p +0s" in shutdown_result_minus["shutdown_command"]'
when: ansible_system == 'FreeBSD'
- name: Verify shutdown delay is present in seconds in Solaris, SunOS
@@ -86,8 +95,30 @@
- '"-d 0" in shutdown_result_minus["shutdown_command"]'
when: ansible_system == 'VMKernel'
-- name: Remove systemd-sysv in ubuntu 18 in case it has been installed in test
+- name: Ensure that systemd-sysv is absent in Ubuntu 18 and Debian
apt:
- name: systemd-sysv
+ name: sytemd-sysv
state: absent
- when: systemd_sysv_install is changed
+ when: (ansible_distribution == 'Ubuntu' and ansible_distribution_major_version is version('18', '>=')) or (ansible_distribution == 'Debian')
+ register: systemd_sysv_install
+
+- name: Gather package facts
+ package_facts:
+ manager: apt
+ when: (ansible_distribution == 'Ubuntu' and ansible_distribution_major_version is version('18', '>=')) or (ansible_distribution == 'Debian')
+
+- name: Execute shutdown if no systemd-sysv
+ community.general.shutdown:
+ register: shutdown_result
+ check_mode: true
+ when:
+ - "(ansible_distribution == 'Ubuntu' and ansible_distribution_major_version is version('18', '>=')) or (ansible_distribution == 'Debian')"
+ - '"systemd-sysv" not in ansible_facts.packages'
+
+- name: Install systemd_sysv in case it has been removed in test
+ apt:
+ name: systemd-sysv
+ state: present
+ when:
+ - "(ansible_distribution == 'Ubuntu' and ansible_distribution_major_version is version('18', '>=')) or (ansible_distribution == 'Debian')"
+ - "systemd_sysv_install is changed"
diff --git a/ansible_collections/community/general/tests/integration/targets/snap/meta/main.yml b/ansible_collections/community/general/tests/integration/targets/snap/meta/main.yml
index f36427f71..5c4a48a41 100644
--- a/ansible_collections/community/general/tests/integration/targets/snap/meta/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/snap/meta/main.yml
@@ -5,3 +5,4 @@
dependencies:
- setup_snap
+ - setup_remote_tmp_dir
diff --git a/ansible_collections/community/general/tests/integration/targets/snap/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/snap/tasks/main.yml
index 0f24e69f3..2a683617a 100644
--- a/ansible_collections/community/general/tests/integration/targets/snap/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/snap/tasks/main.yml
@@ -8,236 +8,18 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-- name: Has-snap block
+- name: Has-snap include
when: has_snap
block:
- - name: Make sure package is not installed (hello-world)
- community.general.snap:
- name: hello-world
- state: absent
-
- - name: Install package (hello-world) (check mode)
- community.general.snap:
- name: hello-world
- state: present
- register: install_check
- check_mode: true
-
- - name: Install package (hello-world)
- community.general.snap:
- name: hello-world
- state: present
- register: install
-
- - name: Install package again (hello-world) (check mode)
- community.general.snap:
- name: hello-world
- state: present
- register: install_again_check
- check_mode: true
-
- - name: Install package again (hello-world)
- community.general.snap:
- name: hello-world
- state: present
- register: install_again
-
- - name: Assert package has been installed just once (hello-world)
- assert:
- that:
- - install is changed
- - install_check is changed
- - install_again is not changed
- - install_again_check is not changed
-
- - name: Check package has been installed correctly (hello-world)
- command: hello-world
- environment:
- PATH: /snap/bin/
-
- - name: Remove package (hello-world) (check mode)
- community.general.snap:
- name: hello-world
- state: absent
- register: remove_check
- check_mode: true
-
- - name: Remove package (hello-world)
- community.general.snap:
- name: hello-world
- state: absent
- register: remove
-
- - name: Remove package again (hello-world) (check mode)
- community.general.snap:
- name: hello-world
- state: absent
- register: remove_again_check
- check_mode: true
-
- - name: Remove package again (hello-world)
- community.general.snap:
- name: hello-world
- state: absent
- register: remove_again
-
- - name: Assert package has been removed just once (hello-world)
- assert:
- that:
- - remove is changed
- - remove_check is changed
- - remove_again is not changed
- - remove_again_check is not changed
-
- - name: Make sure package from classic snap is not installed (nvim)
- community.general.snap:
- name: nvim
- state: absent
-
- - name: Install package from classic snap (nvim)
- community.general.snap:
- name: nvim
- state: present
- classic: true
- register: classic_install
-
- # testing classic idempotency
- - name: Install package from classic snap again (nvim)
- community.general.snap:
- name: nvim
- state: present
- classic: true
- register: classic_install_again
-
- - name: Assert package has been installed just once (nvim)
- assert:
- that:
- - classic_install is changed
- - classic_install_again is not changed
-
- # this is just testing if a package which has been installed
- # with true classic can be removed without setting classic to true
- - name: Remove package from classic snap without setting classic to true (nvim)
- community.general.snap:
- name: nvim
- state: absent
- register: classic_remove_without_true_classic
-
- - name: Remove package from classic snap with setting classic to true (nvim)
- community.general.snap:
- name: nvim
- state: absent
- classic: true
- register: classic_remove_with_true_classic
-
- - name: Assert package has been removed without setting classic to true (nvim)
- assert:
- that:
- - classic_remove_without_true_classic is changed
- - classic_remove_with_true_classic is not changed
-
-
- - name: Make sure package is not installed (uhttpd)
- community.general.snap:
- name: uhttpd
- state: absent
-
- - name: Install package (uhttpd)
- community.general.snap:
- name: uhttpd
- state: present
- register: install
-
- - name: Install package (uhttpd)
- community.general.snap:
- name: uhttpd
- state: present
- options:
- - "listening-port=8080"
- register: install_with_option
-
- - name: Install package again with option (uhttpd)
- community.general.snap:
- name: uhttpd
- state: present
- options:
- - "listening-port=8080"
- register: install_with_option_again
-
- - name: Install package again with different options (uhttpd)
- community.general.snap:
- name: uhttpd
- state: present
- options:
- - "listening-port=8088"
- - "document-root-dir=/tmp"
- register: install_with_option_changed
-
- - name: Remove package (uhttpd)
- community.general.snap:
- name: uhttpd
- state: absent
- register: remove
-
- - name: Assert package has been installed with options just once and only changed options trigger a change (uhttpd)
- assert:
- that:
- - install is changed
- - install_with_option is changed
- - "install_with_option.options_changed[0] == 'uhttpd:listening-port=8080'"
- - install_with_option_again is not changed
- - install_with_option_changed is changed
- - "'uhttpd:listening-port=8088' in install_with_option_changed.options_changed"
- - "'uhttpd:document-root-dir=/tmp' in install_with_option_changed.options_changed"
- - "'uhttpd:listening-port=8080' not in install_with_option_changed.options_changed"
- - remove is changed
-
- - name: Install two packages at the same time
- community.general.snap:
- name:
- - hello-world
- - uhttpd
- state: present
- register: install_two
-
- - name: Install two packages at the same time (again)
- community.general.snap:
- name:
- - hello-world
- - uhttpd
- state: present
- register: install_two_again
-
- - name: Remove packages (hello-world & uhttpd)
- community.general.snap:
- name:
- - hello-world
- - uhttpd
- state: absent
- register: install_two_remove
-
- - name: Remove packages again (hello-world & uhttpd)
- community.general.snap:
- name:
- - hello-world
- - uhttpd
- state: absent
- register: install_two_remove_again
-
- - name: Assert installation of two packages
- assert:
- that:
- - install_two is changed
- - "'hello-world' in install_two.snaps_installed"
- - "'uhttpd' in install_two.snaps_installed"
- - install_two.snaps_removed is not defined
- - install_two_again is not changed
- - install_two_again.snaps_installed is not defined
- - install_two_again.snaps_removed is not defined
- - install_two_remove is changed
- - install_two_again.snaps_installed is not defined
- - "'hello-world' in install_two_remove.snaps_removed"
- - "'uhttpd' in install_two_remove.snaps_removed"
- - install_two_remove_again is not changed
- - install_two_remove_again.snaps_installed is not defined
- - install_two_remove_again.snaps_removed is not defined
+ - name: Include test
+ ansible.builtin.include_tasks: test.yml
+ # TODO: Find better package to install from a channel - microk8s installation takes multiple minutes, and even removal takes one minute!
+ # - name: Include test_channel
+ # ansible.builtin.include_tasks: test_channel.yml
+ # TODO: Find bettter package to download and install from sources - cider 1.6.0 takes over 35 seconds to install
+ # - name: Include test_dangerous
+ # ansible.builtin.include_tasks: test_dangerous.yml
+ - name: Include test_3dash
+ ansible.builtin.include_tasks: test_3dash.yml
+ - name: Include test_empty_list
+ ansible.builtin.include_tasks: test_empty_list.yml
diff --git a/ansible_collections/community/general/tests/integration/targets/snap/tasks/test.yml b/ansible_collections/community/general/tests/integration/targets/snap/tasks/test.yml
new file mode 100644
index 000000000..3a77704b3
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/snap/tasks/test.yml
@@ -0,0 +1,235 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Make sure package is not installed (hello-world)
+ community.general.snap:
+ name: hello-world
+ state: absent
+
+- name: Install package (hello-world) (check mode)
+ community.general.snap:
+ name: hello-world
+ state: present
+ register: install_check
+ check_mode: true
+
+- name: Install package (hello-world)
+ community.general.snap:
+ name: hello-world
+ state: present
+ register: install
+
+- name: Install package again (hello-world) (check mode)
+ community.general.snap:
+ name: hello-world
+ state: present
+ register: install_again_check
+ check_mode: true
+
+- name: Install package again (hello-world)
+ community.general.snap:
+ name: hello-world
+ state: present
+ register: install_again
+
+- name: Assert package has been installed just once (hello-world)
+ assert:
+ that:
+ - install is changed
+ - install_check is changed
+ - install_again is not changed
+ - install_again_check is not changed
+
+- name: Check package has been installed correctly (hello-world)
+ command: hello-world
+ environment:
+ PATH: /snap/bin/
+
+- name: Remove package (hello-world) (check mode)
+ community.general.snap:
+ name: hello-world
+ state: absent
+ register: remove_check
+ check_mode: true
+
+- name: Remove package (hello-world)
+ community.general.snap:
+ name: hello-world
+ state: absent
+ register: remove
+
+- name: Remove package again (hello-world) (check mode)
+ community.general.snap:
+ name: hello-world
+ state: absent
+ register: remove_again_check
+ check_mode: true
+
+- name: Remove package again (hello-world)
+ community.general.snap:
+ name: hello-world
+ state: absent
+ register: remove_again
+
+- name: Assert package has been removed just once (hello-world)
+ assert:
+ that:
+ - remove is changed
+ - remove_check is changed
+ - remove_again is not changed
+ - remove_again_check is not changed
+
+- name: Make sure package from classic snap is not installed (nvim)
+ community.general.snap:
+ name: nvim
+ state: absent
+
+- name: Install package from classic snap (nvim)
+ community.general.snap:
+ name: nvim
+ state: present
+ classic: true
+ register: classic_install
+
+# testing classic idempotency
+- name: Install package from classic snap again (nvim)
+ community.general.snap:
+ name: nvim
+ state: present
+ classic: true
+ register: classic_install_again
+
+- name: Assert package has been installed just once (nvim)
+ assert:
+ that:
+ - classic_install is changed
+ - classic_install_again is not changed
+
+# this is just testing if a package which has been installed
+# with true classic can be removed without setting classic to true
+- name: Remove package from classic snap without setting classic to true (nvim)
+ community.general.snap:
+ name: nvim
+ state: absent
+ register: classic_remove_without_true_classic
+
+- name: Remove package from classic snap with setting classic to true (nvim)
+ community.general.snap:
+ name: nvim
+ state: absent
+ classic: true
+ register: classic_remove_with_true_classic
+
+- name: Assert package has been removed without setting classic to true (nvim)
+ assert:
+ that:
+ - classic_remove_without_true_classic is changed
+ - classic_remove_with_true_classic is not changed
+
+
+- name: Make sure package is not installed (uhttpd)
+ community.general.snap:
+ name: uhttpd
+ state: absent
+
+- name: Install package (uhttpd)
+ community.general.snap:
+ name: uhttpd
+ state: present
+ register: install
+
+- name: Install package (uhttpd)
+ community.general.snap:
+ name: uhttpd
+ state: present
+ options:
+ - "listening-port=8080"
+ register: install_with_option
+
+- name: Install package again with option (uhttpd)
+ community.general.snap:
+ name: uhttpd
+ state: present
+ options:
+ - "listening-port=8080"
+ register: install_with_option_again
+
+- name: Install package again with different options (uhttpd)
+ community.general.snap:
+ name: uhttpd
+ state: present
+ options:
+ - "listening-port=8088"
+ - "document-root-dir=/tmp"
+ register: install_with_option_changed
+
+- name: Remove package (uhttpd)
+ community.general.snap:
+ name: uhttpd
+ state: absent
+ register: remove
+
+- name: Assert package has been installed with options just once and only changed options trigger a change (uhttpd)
+ assert:
+ that:
+ - install is changed
+ - install_with_option is changed
+ - "install_with_option.options_changed[0] == 'uhttpd:listening-port=8080'"
+ - install_with_option_again is not changed
+ - install_with_option_changed is changed
+ - "'uhttpd:listening-port=8088' in install_with_option_changed.options_changed"
+ - "'uhttpd:document-root-dir=/tmp' in install_with_option_changed.options_changed"
+ - "'uhttpd:listening-port=8080' not in install_with_option_changed.options_changed"
+ - remove is changed
+
+- name: Install two packages at the same time
+ community.general.snap:
+ name:
+ - hello-world
+ - uhttpd
+ state: present
+ register: install_two
+
+- name: Install two packages at the same time (again)
+ community.general.snap:
+ name:
+ - hello-world
+ - uhttpd
+ state: present
+ register: install_two_again
+
+- name: Remove packages (hello-world & uhttpd)
+ community.general.snap:
+ name:
+ - hello-world
+ - uhttpd
+ state: absent
+ register: install_two_remove
+
+- name: Remove packages again (hello-world & uhttpd)
+ community.general.snap:
+ name:
+ - hello-world
+ - uhttpd
+ state: absent
+ register: install_two_remove_again
+
+- name: Assert installation of two packages
+ assert:
+ that:
+ - install_two is changed
+ - "'hello-world' in install_two.snaps_installed"
+ - "'uhttpd' in install_two.snaps_installed"
+ - install_two.snaps_removed is not defined
+ - install_two_again is not changed
+ - install_two_again.snaps_installed is not defined
+ - install_two_again.snaps_removed is not defined
+ - install_two_remove is changed
+ - install_two_again.snaps_installed is not defined
+ - "'hello-world' in install_two_remove.snaps_removed"
+ - "'uhttpd' in install_two_remove.snaps_removed"
+ - install_two_remove_again is not changed
+ - install_two_remove_again.snaps_installed is not defined
+ - install_two_remove_again.snaps_removed is not defined
diff --git a/ansible_collections/community/general/tests/integration/targets/snap/tasks/test_3dash.yml b/ansible_collections/community/general/tests/integration/targets/snap/tasks/test_3dash.yml
new file mode 100644
index 000000000..4d39c0a48
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/snap/tasks/test_3dash.yml
@@ -0,0 +1,31 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Make sure packages are not installed (3 dashes)
+ community.general.snap:
+ name:
+ - bw
+ - shellcheck
+ state: absent
+
+- name: Install package with 3 dashes in description (check)
+ community.general.snap:
+ name:
+ - bw
+ - shellcheck
+ state: present
+ check_mode: true
+ register: install_3dash_check
+
+- name: Remove packages (3 dashes)
+ community.general.snap:
+ name:
+ - bw
+ - shellcheck
+ state: absent
+
+- assert:
+ that:
+ - install_3dash_check is changed
diff --git a/ansible_collections/community/general/tests/integration/targets/snap/tasks/test_channel.yml b/ansible_collections/community/general/tests/integration/targets/snap/tasks/test_channel.yml
new file mode 100644
index 000000000..e9eb19c89
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/snap/tasks/test_channel.yml
@@ -0,0 +1,81 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# NOTE This is currently disabled for performance reasons!
+
+- name: Make sure package is not installed (microk8s)
+ community.general.snap:
+ name: microk8s
+ state: absent
+
+# Test for https://github.com/ansible-collections/community.general/issues/1606
+- name: Install package (microk8s)
+ community.general.snap:
+ name: microk8s
+ classic: true
+ state: present
+ register: install_microk8s
+
+- name: Install package with channel (microk8s)
+ community.general.snap:
+ name: microk8s
+ classic: true
+ channel: 1.20/stable
+ state: present
+ register: install_microk8s_chan
+
+- name: Install package with channel (microk8s) again
+ community.general.snap:
+ name: microk8s
+ classic: true
+ channel: 1.20/stable
+ state: present
+ register: install_microk8s_chan_again
+
+- name: Remove package (microk8s)
+ community.general.snap:
+ name: microk8s
+ state: absent
+ register: remove_microk8s
+
+- assert:
+ that:
+ - install_microk8s is changed
+ - install_microk8s_chan is changed
+ - install_microk8s_chan_again is not changed
+ - remove_microk8s is changed
+
+- name: Install package (shellcheck)
+ community.general.snap:
+ name: shellcheck
+ state: present
+ register: install_shellcheck
+
+- name: Install package with channel (shellcheck)
+ community.general.snap:
+ name: shellcheck
+ channel: edge
+ state: present
+ register: install_shellcheck_chan
+
+- name: Install package with channel (shellcheck) again
+ community.general.snap:
+ name: shellcheck
+ channel: edge
+ state: present
+ register: install_shellcheck_chan_again
+
+- name: Remove package (shellcheck)
+ community.general.snap:
+ name: shellcheck
+ state: absent
+ register: remove_shellcheck
+
+- assert:
+ that:
+ - install_shellcheck is changed
+ - install_shellcheck_chan is changed
+ - install_shellcheck_chan_again is not changed
+ - remove_shellcheck is changed
diff --git a/ansible_collections/community/general/tests/integration/targets/snap/tasks/test_dangerous.yml b/ansible_collections/community/general/tests/integration/targets/snap/tasks/test_dangerous.yml
new file mode 100644
index 000000000..8fe4edee0
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/snap/tasks/test_dangerous.yml
@@ -0,0 +1,53 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# NOTE This is currently disabled for performance reasons!
+
+- name: Make sure package is not installed (cider)
+ community.general.snap:
+ name: cider
+ state: absent
+
+- name: Download cider snap
+ ansible.builtin.get_url:
+ url: https://github.com/ciderapp/cider-releases/releases/download/v1.6.0/cider_1.6.0_amd64.snap
+ dest: "{{ remote_tmp_dir }}/cider_1.6.0_amd64.snap"
+ mode: "0644"
+
+# Test for https://github.com/ansible-collections/community.general/issues/5715
+- name: Install package from file (check)
+ community.general.snap:
+ name: "{{ remote_tmp_dir }}/cider_1.6.0_amd64.snap"
+ dangerous: true
+ state: present
+ check_mode: true
+ register: install_dangerous_check
+
+- name: Install package from file
+ community.general.snap:
+ name: "{{ remote_tmp_dir }}/cider_1.6.0_amd64.snap"
+ dangerous: true
+ state: present
+ register: install_dangerous
+
+- name: Install package from file
+ community.general.snap:
+ name: "{{ remote_tmp_dir }}/cider_1.6.0_amd64.snap"
+ dangerous: true
+ state: present
+ register: install_dangerous_idempot
+
+- name: Remove package
+ community.general.snap:
+ name: cider
+ state: absent
+ register: remove_dangerous
+
+- assert:
+ that:
+ - install_dangerous_check is changed
+ - install_dangerous is changed
+ - install_dangerous_idempot is not changed
+ - remove_dangerous is changed
diff --git a/ansible_collections/community/general/tests/integration/targets/snap/tasks/test_empty_list.yml b/ansible_collections/community/general/tests/integration/targets/snap/tasks/test_empty_list.yml
new file mode 100644
index 000000000..bea73cb5e
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/snap/tasks/test_empty_list.yml
@@ -0,0 +1,14 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Empty list present
+ community.general.snap:
+ name: []
+ state: present
+
+- name: Empty list absent
+ community.general.snap:
+ name: []
+ state: absent
diff --git a/ansible_collections/community/general/tests/integration/targets/ssh_config/aliases b/ansible_collections/community/general/tests/integration/targets/ssh_config/aliases
index 6011128da..2c5cf3237 100644
--- a/ansible_collections/community/general/tests/integration/targets/ssh_config/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/ssh_config/aliases
@@ -4,6 +4,5 @@
azp/posix/2
destructive
-skip/python2.6 # stromssh only supports python3
skip/python2.7 # stromssh only supports python3
skip/freebsd # stromssh installation fails on freebsd
diff --git a/ansible_collections/community/general/tests/integration/targets/ssh_config/tasks/options.yml b/ansible_collections/community/general/tests/integration/targets/ssh_config/tasks/options.yml
index 406de6831..f88f99081 100644
--- a/ansible_collections/community/general/tests/integration/targets/ssh_config/tasks/options.yml
+++ b/ansible_collections/community/general/tests/integration/targets/ssh_config/tasks/options.yml
@@ -15,7 +15,12 @@
host: "options.example.com"
proxycommand: "ssh jumphost.example.com -W %h:%p"
forward_agent: true
+ add_keys_to_agent: true
host_key_algorithms: "+ssh-rsa"
+ identities_only: true
+ controlmaster: "auto"
+ controlpath: "~/.ssh/sockets/%r@%h-%p"
+ controlpersist: yes
state: present
register: options_add
check_mode: true
@@ -33,7 +38,7 @@
src: "{{ ssh_config_test }}"
register: slurp_ssh_config
-- name: "Options - Verify that nothign was added to {{ ssh_config_test }} during change mode"
+- name: "Options - Verify that nothing was added to {{ ssh_config_test }} during change mode"
assert:
that:
- "'options.example.com' not in slurp_ssh_config['content'] | b64decode"
@@ -44,7 +49,12 @@
host: "options.example.com"
proxycommand: "ssh jumphost.example.com -W %h:%p"
forward_agent: true
+ add_keys_to_agent: true
host_key_algorithms: "+ssh-rsa"
+ identities_only: true
+ controlmaster: "auto"
+ controlpath: "~/.ssh/sockets/%r@%h-%p"
+ controlpersist: yes
state: present
register: options_add
@@ -62,7 +72,12 @@
host: "options.example.com"
proxycommand: "ssh jumphost.example.com -W %h:%p"
forward_agent: true
+ add_keys_to_agent: true
host_key_algorithms: "+ssh-rsa"
+ identities_only: true
+ controlmaster: "auto"
+ controlpath: "~/.ssh/sockets/%r@%h-%p"
+ controlpersist: yes
state: present
register: options_add_again
@@ -84,7 +99,12 @@
that:
- "'proxycommand ssh jumphost.example.com -W %h:%p' in slurp_ssh_config['content'] | b64decode"
- "'forwardagent yes' in slurp_ssh_config['content'] | b64decode"
+ - "'addkeystoagent yes' in slurp_ssh_config['content'] | b64decode"
- "'hostkeyalgorithms +ssh-rsa' in slurp_ssh_config['content'] | b64decode"
+ - "'identitiesonly yes' in slurp_ssh_config['content'] | b64decode"
+ - "'controlmaster auto' in slurp_ssh_config['content'] | b64decode"
+ - "'controlpath ~/.ssh/sockets/%r@%h-%p' in slurp_ssh_config['content'] | b64decode"
+ - "'controlpersist yes' in slurp_ssh_config['content'] | b64decode"
- name: Options - Update host
community.general.ssh_config:
@@ -92,7 +112,12 @@
host: "options.example.com"
proxycommand: "ssh new-jumphost.example.com -W %h:%p"
forward_agent: false
+ add_keys_to_agent: false
host_key_algorithms: "+ssh-ed25519"
+ identities_only: false
+ controlmaster: no
+ controlpath: "~/.ssh/new-sockets/%r@%h-%p"
+ controlpersist: "600"
state: present
register: options_update
@@ -112,7 +137,12 @@
host: "options.example.com"
proxycommand: "ssh new-jumphost.example.com -W %h:%p"
forward_agent: false
+ add_keys_to_agent: false
host_key_algorithms: "+ssh-ed25519"
+ identities_only: false
+ controlmaster: no
+ controlpath: "~/.ssh/new-sockets/%r@%h-%p"
+ controlpersist: "600"
state: present
register: options_update
@@ -135,7 +165,12 @@
that:
- "'proxycommand ssh new-jumphost.example.com -W %h:%p' in slurp_ssh_config['content'] | b64decode"
- "'forwardagent no' in slurp_ssh_config['content'] | b64decode"
+ - "'addkeystoagent no' in slurp_ssh_config['content'] | b64decode"
- "'hostkeyalgorithms +ssh-ed25519' in slurp_ssh_config['content'] | b64decode"
+ - "'identitiesonly no' in slurp_ssh_config['content'] | b64decode"
+ - "'controlmaster no' in slurp_ssh_config['content'] | b64decode"
+ - "'controlpath ~/.ssh/new-sockets/%r@%h-%p' in slurp_ssh_config['content'] | b64decode"
+ - "'controlpersist 600' in slurp_ssh_config['content'] | b64decode"
- name: Options - Ensure no update in case option exist in ssh_config file but wasn't defined in playbook
community.general.ssh_config:
@@ -163,7 +198,12 @@
that:
- "'proxycommand ssh new-jumphost.example.com -W %h:%p' in slurp_ssh_config['content'] | b64decode"
- "'forwardagent no' in slurp_ssh_config['content'] | b64decode"
+ - "'addkeystoagent no' in slurp_ssh_config['content'] | b64decode"
- "'hostkeyalgorithms +ssh-ed25519' in slurp_ssh_config['content'] | b64decode"
+ - "'identitiesonly no' in slurp_ssh_config['content'] | b64decode"
+ - "'controlmaster no' in slurp_ssh_config['content'] | b64decode"
+ - "'controlpath ~/.ssh/new-sockets/%r@%h-%p' in slurp_ssh_config['content'] | b64decode"
+ - "'controlpersist 600' in slurp_ssh_config['content'] | b64decode"
- name: Debug
debug:
@@ -209,7 +249,12 @@
that:
- "'proxycommand ssh new-jumphost.example.com -W %h:%p' not in slurp_ssh_config['content'] | b64decode"
- "'forwardagent no' not in slurp_ssh_config['content'] | b64decode"
+ - "'addkeystoagent no' not in slurp_ssh_config['content'] | b64decode"
- "'hostkeyalgorithms +ssh-ed25519' not in slurp_ssh_config['content'] | b64decode"
+ - "'identitiesonly no' not in slurp_ssh_config['content'] | b64decode"
+ - "'controlmaster auto' not in slurp_ssh_config['content'] | b64decode"
+ - "'controlpath ~/.ssh/sockets/%r@%h-%p' not in slurp_ssh_config['content'] | b64decode"
+ - "'controlpersist yes' not in slurp_ssh_config['content'] | b64decode"
# Proxycommand and ProxyJump are mutually exclusive.
# Reset ssh_config before testing options with proxyjump
@@ -225,7 +270,12 @@
host: "options.example.com"
proxyjump: "jumphost.example.com"
forward_agent: true
+ add_keys_to_agent: true
host_key_algorithms: "+ssh-rsa"
+ identities_only: true
+ controlmaster: "auto"
+ controlpath: "~/.ssh/sockets/%r@%h-%p"
+ controlpersist: yes
state: present
register: options_add
check_mode: true
@@ -243,7 +293,7 @@
src: "{{ ssh_config_test }}"
register: slurp_ssh_config
-- name: "Options - Verify that nothign was added to {{ ssh_config_test }} during change mode"
+- name: "Options - Verify that nothing was added to {{ ssh_config_test }} during change mode"
assert:
that:
- "'options.example.com' not in slurp_ssh_config['content'] | b64decode"
@@ -254,7 +304,12 @@
host: "options.example.com"
proxyjump: "jumphost.example.com"
forward_agent: true
+ add_keys_to_agent: true
host_key_algorithms: "+ssh-rsa"
+ identities_only: true
+ controlmaster: "auto"
+ controlpath: "~/.ssh/sockets/%r@%h-%p"
+ controlpersist: yes
state: present
register: options_add
@@ -272,7 +327,12 @@
host: "options.example.com"
proxyjump: "jumphost.example.com"
forward_agent: true
+ add_keys_to_agent: true
host_key_algorithms: "+ssh-rsa"
+ identities_only: true
+ controlmaster: "auto"
+ controlpath: "~/.ssh/sockets/%r@%h-%p"
+ controlpersist: yes
state: present
register: options_add_again
@@ -294,7 +354,12 @@
that:
- "'proxyjump jumphost.example.com' in slurp_ssh_config['content'] | b64decode"
- "'forwardagent yes' in slurp_ssh_config['content'] | b64decode"
+ - "'addkeystoagent yes' in slurp_ssh_config['content'] | b64decode"
- "'hostkeyalgorithms +ssh-rsa' in slurp_ssh_config['content'] | b64decode"
+ - "'identitiesonly yes' in slurp_ssh_config['content'] | b64decode"
+ - "'controlmaster auto' in slurp_ssh_config['content'] | b64decode"
+ - "'controlpath ~/.ssh/sockets/%r@%h-%p' in slurp_ssh_config['content'] | b64decode"
+ - "'controlpersist yes' in slurp_ssh_config['content'] | b64decode"
- name: Options - Update host
community.general.ssh_config:
@@ -302,7 +367,12 @@
host: "options.example.com"
proxyjump: "new-jumphost.example.com"
forward_agent: false
+ add_keys_to_agent: false
host_key_algorithms: "+ssh-ed25519"
+ identities_only: false
+ controlmaster: no
+ controlpath: "~/.ssh/new-sockets/%r@%h-%p"
+ controlpersist: "600"
state: present
register: options_update
@@ -322,7 +392,12 @@
host: "options.example.com"
proxyjump: "new-jumphost.example.com"
forward_agent: false
+ add_keys_to_agent: false
host_key_algorithms: "+ssh-ed25519"
+ identities_only: false
+ controlmaster: no
+ controlpath: "~/.ssh/new-sockets/%r@%h-%p"
+ controlpersist: "600"
state: present
register: options_update
@@ -345,7 +420,12 @@
that:
- "'proxyjump new-jumphost.example.com' in slurp_ssh_config['content'] | b64decode"
- "'forwardagent no' in slurp_ssh_config['content'] | b64decode"
+ - "'addkeystoagent no' in slurp_ssh_config['content'] | b64decode"
- "'hostkeyalgorithms +ssh-ed25519' in slurp_ssh_config['content'] | b64decode"
+ - "'identitiesonly no' in slurp_ssh_config['content'] | b64decode"
+ - "'controlmaster no' in slurp_ssh_config['content'] | b64decode"
+ - "'controlpath ~/.ssh/new-sockets/%r@%h-%p' in slurp_ssh_config['content'] | b64decode"
+ - "'controlpersist 600' in slurp_ssh_config['content'] | b64decode"
- name: Options - Ensure no update in case option exist in ssh_config file but wasn't defined in playbook
community.general.ssh_config:
@@ -373,7 +453,12 @@
that:
- "'proxyjump new-jumphost.example.com' in slurp_ssh_config['content'] | b64decode"
- "'forwardagent no' in slurp_ssh_config['content'] | b64decode"
+ - "'addkeystoagent no' in slurp_ssh_config['content'] | b64decode"
- "'hostkeyalgorithms +ssh-ed25519' in slurp_ssh_config['content'] | b64decode"
+ - "'identitiesonly no' in slurp_ssh_config['content'] | b64decode"
+ - "'controlmaster no' in slurp_ssh_config['content'] | b64decode"
+ - "'controlpath ~/.ssh/new-sockets/%r@%h-%p' in slurp_ssh_config['content'] | b64decode"
+ - "'controlpersist 600' in slurp_ssh_config['content'] | b64decode"
- name: Debug
debug:
@@ -419,4 +504,9 @@
that:
- "'proxyjump new-jumphost.example.com' not in slurp_ssh_config['content'] | b64decode"
- "'forwardagent no' not in slurp_ssh_config['content'] | b64decode"
+ - "'addkeystoagent no' not in slurp_ssh_config['content'] | b64decode"
- "'hostkeyalgorithms +ssh-ed25519' not in slurp_ssh_config['content'] | b64decode"
+ - "'identitiesonly no' not in slurp_ssh_config['content'] | b64decode"
+ - "'controlmaster auto' not in slurp_ssh_config['content'] | b64decode"
+ - "'controlpath ~/.ssh/sockets/%r@%h-%p' not in slurp_ssh_config['content'] | b64decode"
+ - "'controlpersist yes' not in slurp_ssh_config['content'] | b64decode"
diff --git a/ansible_collections/community/general/tests/integration/targets/sudoers/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/sudoers/tasks/main.yml
index dd62025d5..36397f41a 100644
--- a/ansible_collections/community/general/tests/integration/targets/sudoers/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/sudoers/tasks/main.yml
@@ -159,6 +159,20 @@
src: "{{ sudoers_path }}/my-sudo-rule-8"
register: rule_8_contents
+- name: Create rule with noexec parameters
+ community.general.sudoers:
+ name: my-sudo-rule-9
+ state: present
+ user: alice
+ commands: /usr/local/bin/command
+ noexec: true
+ register: rule_9
+
+- name: Grab contents of my-sudo-rule-9
+ ansible.builtin.slurp:
+ src: "{{ sudoers_path }}/my-sudo-rule-9"
+ register: rule_9_contents
+
- name: Revoke rule 1
community.general.sudoers:
name: my-sudo-rule-1
@@ -257,6 +271,7 @@
- "rule_6_contents['content'] | b64decode == 'alice ALL=(bob)NOPASSWD: /usr/local/bin/command\n'"
- "rule_7_contents['content'] | b64decode == 'alice host-1=NOPASSWD: /usr/local/bin/command\n'"
- "rule_8_contents['content'] | b64decode == 'alice ALL=NOPASSWD:SETENV: /usr/local/bin/command\n'"
+ - "rule_9_contents['content'] | b64decode == 'alice ALL=NOEXEC:NOPASSWD: /usr/local/bin/command\n'"
- name: Check revocation stat
ansible.builtin.assert:
diff --git a/ansible_collections/community/general/tests/integration/targets/sysrc/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/sysrc/tasks/main.yml
index 2c45c3b1c..ace38202f 100644
--- a/ansible_collections/community/general/tests/integration/targets/sysrc/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/sysrc/tasks/main.yml
@@ -140,11 +140,11 @@
- name: Test within jail
#
# NOTE: currently fails with FreeBSD 12 with minor version less than 4
- # NOTE: currently fails with FreeBSD 13 with minor version less than 1
+ # NOTE: currently fails with FreeBSD 13 with minor version less than 2
#
when: >-
ansible_distribution_version is version('12.4', '>=') and ansible_distribution_version is version('13', '<')
- or ansible_distribution_version is version('13.1', '>=')
+ or ansible_distribution_version is version('13.2', '>=')
block:
- name: Setup testjail
include_tasks: setup-testjail.yml
diff --git a/ansible_collections/community/general/tests/integration/targets/terraform/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/terraform/tasks/main.yml
index 1c66990be..d04757d8e 100644
--- a/ansible_collections/community/general/tests/integration/targets/terraform/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/terraform/tasks/main.yml
@@ -63,5 +63,5 @@
loop_control:
index_var: provider_index
-- name: Test Complex Varibles
+- name: Test Complex Variables
ansible.builtin.include_tasks: complex_variables.yml
diff --git a/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/aliases b/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/aliases
new file mode 100644
index 000000000..12d1d6617
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/aliases
@@ -0,0 +1,5 @@
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+azp/posix/2
diff --git a/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/runme.sh b/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/runme.sh
new file mode 100755
index 000000000..cd97ac949
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/runme.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+set -eux
+
+source virtualenv.sh
+
+# Requirements have to be installed prior to running ansible-playbook
+# because plugins and requirements are loaded before the task runs
+
+pip install fqdn
+
+ANSIBLE_ROLES_PATH=../ ansible-playbook runme.yml "$@"
diff --git a/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/runme.yml b/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/runme.yml
new file mode 100644
index 000000000..37f965b28
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/runme.yml
@@ -0,0 +1,8 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- hosts: localhost
+ roles:
+ - {role: test_fqdn_valid}
diff --git a/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/tasks/fqdn_valid_1.yml b/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/tasks/fqdn_valid_1.yml
new file mode 100644
index 000000000..36cfffad0
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/tasks/fqdn_valid_1.yml
@@ -0,0 +1,58 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Debug ansible_version
+ ansible.builtin.debug:
+ var: ansible_version
+ when: debug_test|d(false)|bool
+ tags: t0
+
+- name: 1. Test valid hostnames. Default options.
+ block:
+ - name: "1. Default min_labels=1, allow_underscores=False"
+ ansible.builtin.debug:
+ msg: "hosts_invalid: {{ hosts_invalid }}"
+ when: debug_test|d(false)|bool
+ - name: Assert
+ ansible.builtin.assert:
+ that: hosts_invalid|difference(result)|length == 0
+ vars:
+ hosts_valid: "{{ names1|select('community.general.fqdn_valid') }}"
+ hosts_invalid: "{{ names1|difference(hosts_valid) }}"
+ result: [-rv.example.com, -rv, s_v]
+ tags: t1
+
+- name: 2. Test valid hostnames. allow_underscores=True
+ block:
+ - name: "2. allow_underscores=True, default min_labels=1"
+ ansible.builtin.debug:
+ msg: "hosts_invalid: {{ hosts_invalid }}"
+ when: debug_test|d(false)|bool
+ - name: Assert
+ ansible.builtin.assert:
+ that: hosts_invalid|difference(result)|length == 0
+ vars:
+ hosts_valid: "{{ names2|select('community.general.fqdn_valid',
+ allow_underscores=True) }}"
+ hosts_invalid: "{{ names2|difference(hosts_valid) }}"
+ result: [-rv]
+ tags: t2
+
+- name: 3. Test valid hostnames. min_labels=2, allow_underscores=True
+ block:
+ - name: "3. allow_underscores=True, min_labels=2"
+ ansible.builtin.debug:
+ msg: "hosts_invalid: {{ hosts_invalid }}"
+ when: debug_test|d(false)|bool
+ - name: Assert
+ ansible.builtin.assert:
+ that: hosts_invalid|difference(result)|length == 0
+ vars:
+ hosts_valid: "{{ names3|select('community.general.fqdn_valid',
+ min_labels=2,
+ allow_underscores=True) }}"
+ hosts_invalid: "{{ names3|difference(hosts_valid) }}"
+ result: [9rv, s_v-.x.y]
+ tags: t3
diff --git a/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/tasks/main.yml
new file mode 100644
index 000000000..3bb62555a
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/tasks/main.yml
@@ -0,0 +1,7 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+- name: Test fqdn_valid
+ ansible.builtin.import_tasks: fqdn_valid_1.yml
diff --git a/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/vars/main.yml b/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/vars/main.yml
new file mode 100644
index 000000000..ba0a0eb08
--- /dev/null
+++ b/ansible_collections/community/general/tests/integration/targets/test_fqdn_valid/vars/main.yml
@@ -0,0 +1,15 @@
+---
+# Copyright (c) Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+names1:
+ - srv.example.com
+ - 9rv.example.com
+ - -rv.example.com
+ - srv
+ - 9rv
+ - -rv
+ - s_v
+names2: [9rv, -rv, s_v]
+names3: [9rv, srv.x, s_v.x.y, s_v-.x.y]
diff --git a/ansible_collections/community/general/tests/integration/targets/timezone/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/timezone/tasks/main.yml
index 3644eeafa..721341592 100644
--- a/ansible_collections/community/general/tests/integration/targets/timezone/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/timezone/tasks/main.yml
@@ -77,6 +77,7 @@
when:
- ansible_facts.distribution ~ ansible_facts.distribution_major_version not in ['Fedora31', 'Fedora32']
- not (ansible_os_family == 'Alpine') # TODO
+ - not (ansible_distribution == 'Archlinux') # TODO
block:
- name: set timezone to Etc/UTC
timezone:
@@ -93,4 +94,4 @@
- name: Restore original system timezone - {{ original_timezone.diff.before.name }}
timezone:
name: "{{ original_timezone.diff.before.name }}"
- when: original_timezone is changed
+ when: original_timezone is changed and original_timezone.diff.before.name != 'n/a'
diff --git a/ansible_collections/community/general/tests/integration/targets/ufw/aliases b/ansible_collections/community/general/tests/integration/targets/ufw/aliases
index 2ef1a4133..209a1153e 100644
--- a/ansible_collections/community/general/tests/integration/targets/ufw/aliases
+++ b/ansible_collections/community/general/tests/integration/targets/ufw/aliases
@@ -11,6 +11,8 @@ skip/freebsd
skip/rhel8.0 # FIXME
skip/rhel9.0 # FIXME
skip/rhel9.1 # FIXME
+skip/rhel9.2 # FIXME
+skip/rhel9.3 # FIXME
skip/docker
needs/root
needs/target/setup_epel
diff --git a/ansible_collections/community/general/tests/integration/targets/xml/tasks/main.yml b/ansible_collections/community/general/tests/integration/targets/xml/tasks/main.yml
index fe46b3ae5..8235f1a6b 100644
--- a/ansible_collections/community/general/tests/integration/targets/xml/tasks/main.yml
+++ b/ansible_collections/community/general/tests/integration/targets/xml/tasks/main.yml
@@ -14,6 +14,15 @@
state: present
when: ansible_os_family == "FreeBSD"
+- name: Install requirements (RHEL 8)
+ package:
+ name:
+ - libxml2-devel
+ - libxslt-devel
+ - python3-lxml
+ state: present
+ when: ansible_distribution == "RedHat" and ansible_distribution_major_version == "8"
+
# Needed for MacOSX !
- name: Install lxml
pip:
diff --git a/ansible_collections/community/general/tests/sanity/extra/botmeta.py b/ansible_collections/community/general/tests/sanity/extra/botmeta.py
index 3b6c34834..459d3ba14 100755
--- a/ansible_collections/community/general/tests/sanity/extra/botmeta.py
+++ b/ansible_collections/community/general/tests/sanity/extra/botmeta.py
@@ -18,6 +18,10 @@ from voluptuous.humanize import humanize_error
IGNORE_NO_MAINTAINERS = [
+ 'docs/docsite/rst/filter_guide.rst',
+ 'docs/docsite/rst/filter_guide_abstract_informations.rst',
+ 'docs/docsite/rst/filter_guide_paths.rst',
+ 'docs/docsite/rst/filter_guide_selecting_json_data.rst',
'plugins/cache/memcached.py',
'plugins/cache/redis.py',
'plugins/callback/cgroup_memory_recap.py',
@@ -197,7 +201,7 @@ def main():
# Scan all files
unmatched = set(files)
- for dirs in ('plugins', 'tests', 'changelogs'):
+ for dirs in ('docs/docsite/rst', 'plugins', 'tests', 'changelogs'):
for dirpath, dirnames, filenames in os.walk(dirs):
for file in sorted(filenames):
if file.endswith('.pyc'):
diff --git a/ansible_collections/community/general/tests/sanity/extra/extra-docs.py b/ansible_collections/community/general/tests/sanity/extra/extra-docs.py
index c636beb08..251e6d70f 100755
--- a/ansible_collections/community/general/tests/sanity/extra/extra-docs.py
+++ b/ansible_collections/community/general/tests/sanity/extra/extra-docs.py
@@ -17,7 +17,7 @@ def main():
suffix = ':{env}'.format(env=env["ANSIBLE_COLLECTIONS_PATH"]) if 'ANSIBLE_COLLECTIONS_PATH' in env else ''
env['ANSIBLE_COLLECTIONS_PATH'] = '{root}{suffix}'.format(root=os.path.dirname(os.path.dirname(os.path.dirname(os.getcwd()))), suffix=suffix)
p = subprocess.run(
- ['antsibull-docs', 'lint-collection-docs', '--plugin-docs', '--disallow-semantic-markup', '--skip-rstcheck', '.'],
+ ['antsibull-docs', 'lint-collection-docs', '--plugin-docs', '--skip-rstcheck', '.'],
env=env,
check=False,
)
diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.11.txt b/ansible_collections/community/general/tests/sanity/ignore-2.11.txt
deleted file mode 100644
index f2c30270c..000000000
--- a/ansible_collections/community/general/tests/sanity/ignore-2.11.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-.azure-pipelines/scripts/publish-codecov.py compile-2.6!skip # Uses Python 3.6+ syntax
-.azure-pipelines/scripts/publish-codecov.py compile-2.7!skip # Uses Python 3.6+ syntax
-.azure-pipelines/scripts/publish-codecov.py compile-3.5!skip # Uses Python 3.6+ syntax
-.azure-pipelines/scripts/publish-codecov.py future-import-boilerplate
-.azure-pipelines/scripts/publish-codecov.py metaclass-boilerplate
-.azure-pipelines/scripts/publish-codecov.py replace-urlopen
-plugins/modules/consul.py validate-modules:doc-missing-type
-plugins/modules/consul.py validate-modules:undocumented-parameter
-plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice
-plugins/modules/gconftool2.py validate-modules:parameter-state-invalid-choice # state=get - removed in 8.0.0
-plugins/modules/iptables_state.py validate-modules:undocumented-parameter # params _back and _timeout used by action plugin
-plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen
-plugins/modules/manageiq_policies.py validate-modules:parameter-state-invalid-choice # state=list - removed in 8.0.0
-plugins/modules/manageiq_provider.py validate-modules:doc-choices-do-not-match-spec # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:doc-missing-type # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:parameter-type-not-in-doc # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:undocumented-parameter # missing docs on suboptions
-plugins/modules/manageiq_tags.py validate-modules:parameter-state-invalid-choice
-plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice
-plugins/modules/parted.py validate-modules:parameter-state-invalid-choice
-plugins/modules/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0
-plugins/modules/rax_files_objects.py use-argspec-type-path
-plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice
-plugins/modules/rax.py use-argspec-type-path # fix needed
-plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
-plugins/modules/xfconf.py validate-modules:return-syntax-error
-tests/integration/targets/django_manage/files/base_test/simple_project/p1/manage.py compile-2.6 # django generated code
-tests/integration/targets/django_manage/files/base_test/simple_project/p1/manage.py compile-2.7 # django generated code
diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.12.txt b/ansible_collections/community/general/tests/sanity/ignore-2.12.txt
deleted file mode 100644
index a8e04ff30..000000000
--- a/ansible_collections/community/general/tests/sanity/ignore-2.12.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-.azure-pipelines/scripts/publish-codecov.py replace-urlopen
-plugins/modules/consul.py validate-modules:doc-missing-type
-plugins/modules/consul.py validate-modules:undocumented-parameter
-plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice
-plugins/modules/gconftool2.py validate-modules:parameter-state-invalid-choice # state=get - removed in 8.0.0
-plugins/modules/iptables_state.py validate-modules:undocumented-parameter # params _back and _timeout used by action plugin
-plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen
-plugins/modules/manageiq_policies.py validate-modules:parameter-state-invalid-choice # state=list - removed in 8.0.0
-plugins/modules/manageiq_provider.py validate-modules:doc-choices-do-not-match-spec # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:doc-missing-type # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:parameter-type-not-in-doc # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:undocumented-parameter # missing docs on suboptions
-plugins/modules/manageiq_tags.py validate-modules:parameter-state-invalid-choice
-plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice
-plugins/modules/parted.py validate-modules:parameter-state-invalid-choice
-plugins/modules/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0
-plugins/modules/rax_files_objects.py use-argspec-type-path
-plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice
-plugins/modules/rax.py use-argspec-type-path # fix needed
-plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
-plugins/modules/xfconf.py validate-modules:return-syntax-error
diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.13.txt b/ansible_collections/community/general/tests/sanity/ignore-2.13.txt
index a8e04ff30..0665ddc1a 100644
--- a/ansible_collections/community/general/tests/sanity/ignore-2.13.txt
+++ b/ansible_collections/community/general/tests/sanity/ignore-2.13.txt
@@ -1,21 +1,15 @@
.azure-pipelines/scripts/publish-codecov.py replace-urlopen
-plugins/modules/consul.py validate-modules:doc-missing-type
-plugins/modules/consul.py validate-modules:undocumented-parameter
+plugins/lookup/etcd.py validate-modules:invalid-documentation
+plugins/lookup/etcd3.py validate-modules:invalid-documentation
plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice
-plugins/modules/gconftool2.py validate-modules:parameter-state-invalid-choice # state=get - removed in 8.0.0
plugins/modules/iptables_state.py validate-modules:undocumented-parameter # params _back and _timeout used by action plugin
plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen
-plugins/modules/manageiq_policies.py validate-modules:parameter-state-invalid-choice # state=list - removed in 8.0.0
-plugins/modules/manageiq_provider.py validate-modules:doc-choices-do-not-match-spec # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:doc-missing-type # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:parameter-type-not-in-doc # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:undocumented-parameter # missing docs on suboptions
-plugins/modules/manageiq_tags.py validate-modules:parameter-state-invalid-choice
plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice
plugins/modules/parted.py validate-modules:parameter-state-invalid-choice
-plugins/modules/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0
-plugins/modules/rax_files_objects.py use-argspec-type-path
-plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice
-plugins/modules/rax.py use-argspec-type-path # fix needed
+plugins/modules/rax_files_objects.py use-argspec-type-path # module deprecated - removed in 9.0.0
+plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice # module deprecated - removed in 9.0.0
+plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0
+plugins/modules/read_csv.py validate-modules:invalid-documentation
plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
plugins/modules/xfconf.py validate-modules:return-syntax-error
+tests/unit/plugins/modules/test_gio_mime.yaml no-smart-quotes
diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.14.txt b/ansible_collections/community/general/tests/sanity/ignore-2.14.txt
index 7e00143a6..fed147e44 100644
--- a/ansible_collections/community/general/tests/sanity/ignore-2.14.txt
+++ b/ansible_collections/community/general/tests/sanity/ignore-2.14.txt
@@ -1,23 +1,17 @@
.azure-pipelines/scripts/publish-codecov.py replace-urlopen
-plugins/modules/consul.py validate-modules:doc-missing-type
-plugins/modules/consul.py validate-modules:undocumented-parameter
+plugins/lookup/etcd.py validate-modules:invalid-documentation
+plugins/lookup/etcd3.py validate-modules:invalid-documentation
plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice
-plugins/modules/gconftool2.py validate-modules:parameter-state-invalid-choice # state=get - removed in 8.0.0
plugins/modules/homectl.py import-3.11 # Uses deprecated stdlib library 'crypt'
plugins/modules/iptables_state.py validate-modules:undocumented-parameter # params _back and _timeout used by action plugin
plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen
-plugins/modules/manageiq_policies.py validate-modules:parameter-state-invalid-choice # state=list - removed in 8.0.0
-plugins/modules/manageiq_provider.py validate-modules:doc-choices-do-not-match-spec # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:doc-missing-type # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:parameter-type-not-in-doc # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:undocumented-parameter # missing docs on suboptions
-plugins/modules/manageiq_tags.py validate-modules:parameter-state-invalid-choice
plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice
plugins/modules/parted.py validate-modules:parameter-state-invalid-choice
-plugins/modules/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0
-plugins/modules/rax_files_objects.py use-argspec-type-path
-plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice
-plugins/modules/rax.py use-argspec-type-path # fix needed
+plugins/modules/rax_files_objects.py use-argspec-type-path # module deprecated - removed in 9.0.0
+plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice # module deprecated - removed in 9.0.0
+plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0
+plugins/modules/read_csv.py validate-modules:invalid-documentation
plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
plugins/modules/udm_user.py import-3.11 # Uses deprecated stdlib library 'crypt'
plugins/modules/xfconf.py validate-modules:return-syntax-error
+tests/unit/plugins/modules/test_gio_mime.yaml no-smart-quotes
diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.15.txt b/ansible_collections/community/general/tests/sanity/ignore-2.15.txt
index 7e00143a6..d4c92c4d9 100644
--- a/ansible_collections/community/general/tests/sanity/ignore-2.15.txt
+++ b/ansible_collections/community/general/tests/sanity/ignore-2.15.txt
@@ -1,23 +1,14 @@
.azure-pipelines/scripts/publish-codecov.py replace-urlopen
-plugins/modules/consul.py validate-modules:doc-missing-type
-plugins/modules/consul.py validate-modules:undocumented-parameter
plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice
-plugins/modules/gconftool2.py validate-modules:parameter-state-invalid-choice # state=get - removed in 8.0.0
plugins/modules/homectl.py import-3.11 # Uses deprecated stdlib library 'crypt'
plugins/modules/iptables_state.py validate-modules:undocumented-parameter # params _back and _timeout used by action plugin
plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen
-plugins/modules/manageiq_policies.py validate-modules:parameter-state-invalid-choice # state=list - removed in 8.0.0
-plugins/modules/manageiq_provider.py validate-modules:doc-choices-do-not-match-spec # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:doc-missing-type # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:parameter-type-not-in-doc # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:undocumented-parameter # missing docs on suboptions
-plugins/modules/manageiq_tags.py validate-modules:parameter-state-invalid-choice
plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice
plugins/modules/parted.py validate-modules:parameter-state-invalid-choice
-plugins/modules/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0
-plugins/modules/rax_files_objects.py use-argspec-type-path
-plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice
-plugins/modules/rax.py use-argspec-type-path # fix needed
+plugins/modules/rax_files_objects.py use-argspec-type-path # module deprecated - removed in 9.0.0
+plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice # module deprecated - removed in 9.0.0
+plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0
plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
plugins/modules/udm_user.py import-3.11 # Uses deprecated stdlib library 'crypt'
plugins/modules/xfconf.py validate-modules:return-syntax-error
+tests/unit/plugins/modules/test_gio_mime.yaml no-smart-quotes
diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.16.txt b/ansible_collections/community/general/tests/sanity/ignore-2.16.txt
index 5fa3d90ba..397c6d986 100644
--- a/ansible_collections/community/general/tests/sanity/ignore-2.16.txt
+++ b/ansible_collections/community/general/tests/sanity/ignore-2.16.txt
@@ -1,23 +1,15 @@
-.azure-pipelines/scripts/publish-codecov.py replace-urlopen
-plugins/modules/consul.py validate-modules:doc-missing-type
-plugins/modules/consul.py validate-modules:undocumented-parameter
plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice
-plugins/modules/gconftool2.py validate-modules:parameter-state-invalid-choice # state=get - removed in 8.0.0
plugins/modules/homectl.py import-3.11 # Uses deprecated stdlib library 'crypt'
+plugins/modules/homectl.py import-3.12 # Uses deprecated stdlib library 'crypt'
plugins/modules/iptables_state.py validate-modules:undocumented-parameter # params _back and _timeout used by action plugin
plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen
-plugins/modules/manageiq_policies.py validate-modules:parameter-state-invalid-choice # state=list - removed in 8.0.0
-plugins/modules/manageiq_provider.py validate-modules:doc-choices-do-not-match-spec # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:doc-missing-type # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:parameter-type-not-in-doc # missing docs on suboptions
-plugins/modules/manageiq_provider.py validate-modules:undocumented-parameter # missing docs on suboptions
-plugins/modules/manageiq_tags.py validate-modules:parameter-state-invalid-choice # state=list - removed in 8.0.0
plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice
plugins/modules/parted.py validate-modules:parameter-state-invalid-choice
-plugins/modules/puppet.py validate-modules:parameter-invalid # invalid alias - removed in 7.0.0
plugins/modules/rax_files_objects.py use-argspec-type-path # module deprecated - removed in 9.0.0
plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice # module deprecated - removed in 9.0.0
plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0
plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
plugins/modules/udm_user.py import-3.11 # Uses deprecated stdlib library 'crypt'
+plugins/modules/udm_user.py import-3.12 # Uses deprecated stdlib library 'crypt'
plugins/modules/xfconf.py validate-modules:return-syntax-error
+tests/unit/plugins/modules/test_gio_mime.yaml no-smart-quotes
diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.17.txt b/ansible_collections/community/general/tests/sanity/ignore-2.17.txt
new file mode 100644
index 000000000..d75aaeac2
--- /dev/null
+++ b/ansible_collections/community/general/tests/sanity/ignore-2.17.txt
@@ -0,0 +1,17 @@
+plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice
+plugins/modules/homectl.py import-3.11 # Uses deprecated stdlib library 'crypt'
+plugins/modules/homectl.py import-3.12 # Uses deprecated stdlib library 'crypt'
+plugins/modules/iptables_state.py validate-modules:undocumented-parameter # params _back and _timeout used by action plugin
+plugins/modules/lxc_container.py validate-modules:use-run-command-not-popen
+plugins/modules/osx_defaults.py validate-modules:parameter-state-invalid-choice
+plugins/modules/parted.py validate-modules:parameter-state-invalid-choice
+plugins/modules/rax_files_objects.py use-argspec-type-path # module deprecated - removed in 9.0.0
+plugins/modules/rax_files.py validate-modules:parameter-state-invalid-choice # module deprecated - removed in 9.0.0
+plugins/modules/rax.py use-argspec-type-path # module deprecated - removed in 9.0.0
+plugins/modules/rhevm.py validate-modules:parameter-state-invalid-choice
+plugins/modules/udm_user.py import-3.11 # Uses deprecated stdlib library 'crypt'
+plugins/modules/udm_user.py import-3.12 # Uses deprecated stdlib library 'crypt'
+plugins/modules/xfconf.py validate-modules:return-syntax-error
+plugins/module_utils/univention_umc.py pylint:use-yield-from # suggested construct does not work with Python 2
+tests/unit/compat/mock.py pylint:use-yield-from # suggested construct does not work with Python 2
+tests/unit/plugins/modules/test_gio_mime.yaml no-smart-quotes
diff --git a/ansible_collections/community/general/tests/sanity/ignore-2.12.txt.license b/ansible_collections/community/general/tests/sanity/ignore-2.17.txt.license
index edff8c768..edff8c768 100644
--- a/ansible_collections/community/general/tests/sanity/ignore-2.12.txt.license
+++ b/ansible_collections/community/general/tests/sanity/ignore-2.17.txt.license
diff --git a/ansible_collections/community/general/tests/unit/mock/loader.py b/ansible_collections/community/general/tests/unit/mock/loader.py
index 948f4eecd..f7aff17c3 100644
--- a/ansible_collections/community/general/tests/unit/mock/loader.py
+++ b/ansible_collections/community/general/tests/unit/mock/loader.py
@@ -17,7 +17,7 @@ class DictDataLoader(DataLoader):
def __init__(self, file_mapping=None):
file_mapping = {} if file_mapping is None else file_mapping
- assert type(file_mapping) == dict
+ assert isinstance(file_mapping, dict)
super(DictDataLoader, self).__init__()
diff --git a/ansible_collections/community/general/tests/unit/plugins/become/helper.py b/ansible_collections/community/general/tests/unit/plugins/become/helper.py
index 9949e1bef..d2a7df97f 100644
--- a/ansible_collections/community/general/tests/unit/plugins/become/helper.py
+++ b/ansible_collections/community/general/tests/unit/plugins/become/helper.py
@@ -12,7 +12,7 @@ from ansible.plugins.loader import become_loader, get_shell_plugin
def call_become_plugin(task, var_options, cmd, executable=None):
- """Helper function to call become plugin simiarly on how Ansible itself handles this."""
+ """Helper function to call become plugin similarly on how Ansible itself handles this."""
plugin = become_loader.get(task['become_method'])
plugin.set_options(task_keys=task, var_options=var_options)
shell = get_shell_plugin(executable=executable)
diff --git a/ansible_collections/community/general/tests/unit/plugins/callback/test_loganalytics.py b/ansible_collections/community/general/tests/unit/plugins/callback/test_loganalytics.py
index f9fef3c5d..17932ed5f 100644
--- a/ansible_collections/community/general/tests/unit/plugins/callback/test_loganalytics.py
+++ b/ansible_collections/community/general/tests/unit/plugins/callback/test_loganalytics.py
@@ -12,6 +12,7 @@ from ansible_collections.community.general.plugins.callback.loganalytics import
from datetime import datetime
import json
+import sys
class TestAzureLogAnalytics(unittest.TestCase):
@@ -27,6 +28,10 @@ class TestAzureLogAnalytics(unittest.TestCase):
self.mock_host = Mock('MockHost')
self.mock_host.name = 'myhost'
+ # Add backward compatibility
+ if sys.version_info < (3, 2):
+ self.assertRegex = self.assertRegexpMatches
+
@patch('ansible_collections.community.general.plugins.callback.loganalytics.datetime')
@patch('ansible_collections.community.general.plugins.callback.loganalytics.open_url')
def test_overall(self, open_url_mock, mock_datetime):
@@ -62,5 +67,5 @@ class TestAzureLogAnalytics(unittest.TestCase):
args, kwargs = open_url_mock.call_args
headers = kwargs['headers']
- self.assertRegexpMatches(headers['Authorization'], r'^SharedKey 01234567-0123-0123-0123-01234567890a:.*=$')
+ self.assertRegex(headers['Authorization'], r'^SharedKey 01234567-0123-0123-0123-01234567890a:.*=$')
self.assertEqual(headers['Log-Type'], 'ansible_playbook')
diff --git a/ansible_collections/community/general/tests/unit/plugins/connection/test_lxc.py b/ansible_collections/community/general/tests/unit/plugins/connection/test_lxc.py
index 8733a92e0..bebd42772 100644
--- a/ansible_collections/community/general/tests/unit/plugins/connection/test_lxc.py
+++ b/ansible_collections/community/general/tests/unit/plugins/connection/test_lxc.py
@@ -6,20 +6,138 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
+import pytest
+import sys
+
from io import StringIO
-from ansible_collections.community.general.tests.unit.compat import unittest
-from ansible_collections.community.general.plugins.connection import lxc
+from ansible.errors import AnsibleError
from ansible.playbook.play_context import PlayContext
+from ansible.plugins.loader import connection_loader
+from ansible_collections.community.general.tests.unit.compat import mock
+
+
+@pytest.fixture(autouse=True)
+def lxc(request):
+ """Fixture to import/load the lxc plugin module.
+
+ The fixture parameter is used to determine the presence of liblxc.
+ When true (default), a mocked liblxc module is injected. If False,
+ no liblxc will be present.
+ """
+ liblxc_present = getattr(request, 'param', True)
+
+ class ContainerMock():
+ # dict of container name to its state
+ _container_states = {}
+
+ def __init__(self, name):
+ super(ContainerMock, self).__init__()
+ self.name = name
+
+ @property
+ def state(self):
+ return ContainerMock._container_states.get(self.name, 'STARTED')
+
+ liblxc_module_mock = mock.MagicMock()
+ liblxc_module_mock.Container = ContainerMock
+
+ with mock.patch.dict('sys.modules'):
+ if liblxc_present:
+ sys.modules['lxc'] = liblxc_module_mock
+ elif 'lxc' in sys.modules:
+ del sys.modules['lxc']
+
+ from ansible_collections.community.general.plugins.connection import lxc as lxc_plugin_module
+
+ assert lxc_plugin_module.HAS_LIBLXC == liblxc_present
+ assert bool(getattr(lxc_plugin_module, '_lxc', None)) == liblxc_present
+
+ yield lxc_plugin_module
+
+
+class TestLXCConnectionClass():
+
+ @pytest.mark.parametrize('lxc', [True, False], indirect=True)
+ def test_lxc_connection_module(self, lxc):
+ """Test that a connection can be created with the plugin."""
+ play_context = PlayContext()
+ in_stream = StringIO()
+ conn = connection_loader.get('lxc', play_context, in_stream)
+ assert conn
+ assert isinstance(conn, lxc.Connection)
-class TestLXCConnectionClass(unittest.TestCase):
+ @pytest.mark.parametrize('lxc', [False], indirect=True)
+ def test_lxc_connection_liblxc_error(self, lxc):
+ """Test that on connect an error is thrown if liblxc is not present."""
+ play_context = PlayContext()
+ in_stream = StringIO()
+ conn = connection_loader.get('lxc', play_context, in_stream)
+
+ with pytest.raises(AnsibleError, match='lxc python bindings are not installed'):
+ conn._connect()
- def test_lxc_connection_module(self):
+ def test_remote_addr_option(self):
+ """Test that the remote_addr option is used"""
play_context = PlayContext()
- play_context.prompt = (
- '[sudo via ansible, key=ouzmdnewuhucvuaabtjmweasarviygqq] password: '
- )
in_stream = StringIO()
+ conn = connection_loader.get('lxc', play_context, in_stream)
+
+ container_name = 'my-container'
+ conn.set_option('remote_addr', container_name)
+ assert conn.get_option('remote_addr') == container_name
+
+ conn._connect()
+ assert conn.container_name == container_name
+
+ def test_error_when_stopped(self, lxc):
+ """Test that on connect an error is thrown if the container is stopped."""
+ play_context = PlayContext()
+ in_stream = StringIO()
+ conn = connection_loader.get('lxc', play_context, in_stream)
+ conn.set_option('remote_addr', 'my-container')
+
+ lxc._lxc.Container._container_states['my-container'] = 'STOPPED'
+
+ with pytest.raises(AnsibleError, match='my-container is not running'):
+ conn._connect()
+
+ def test_container_name_change(self):
+ """Test connect method reconnects when remote_addr changes"""
+ play_context = PlayContext()
+ in_stream = StringIO()
+ conn = connection_loader.get('lxc', play_context, in_stream)
+
+ # setting the option does nothing
+ container1_name = 'my-container'
+ conn.set_option('remote_addr', container1_name)
+ assert conn.container_name is None
+ assert conn.container is None
+
+ # first call initializes the connection
+ conn._connect()
+ assert conn.container_name is container1_name
+ assert conn.container is not None
+ assert conn.container.name == container1_name
+ container1 = conn.container
+
+ # second call is basically a no-op
+ conn._connect()
+ assert conn.container_name is container1_name
+ assert conn.container is container1
+ assert conn.container.name == container1_name
+
+ # setting the option does again nothing
+ container2_name = 'my-other-container'
+ conn.set_option('remote_addr', container2_name)
+ assert conn.container_name == container1_name
+ assert conn.container is container1
+ assert conn.container.name == container1_name
- self.assertIsInstance(lxc.Connection(play_context, in_stream), lxc.Connection)
+ # first call with a different remote_addr changes the connection
+ conn._connect()
+ assert conn.container_name == container2_name
+ assert conn.container is not None
+ assert conn.container is not container1
+ assert conn.container.name == container2_name
diff --git a/ansible_collections/community/general/tests/unit/plugins/inventory/test_icinga2.py b/ansible_collections/community/general/tests/unit/plugins/inventory/test_icinga2.py
index e3928b0db..859f29d3b 100644
--- a/ansible_collections/community/general/tests/unit/plugins/inventory/test_icinga2.py
+++ b/ansible_collections/community/general/tests/unit/plugins/inventory/test_icinga2.py
@@ -86,6 +86,8 @@ def get_option(option):
return {}
elif option == 'strict':
return False
+ elif option == 'group_by_hostgroups':
+ return True
else:
return None
@@ -96,6 +98,7 @@ def test_populate(inventory, mocker):
inventory.icinga2_password = 'password'
inventory.icinga2_url = 'https://localhost:5665' + '/v1'
inventory.inventory_attr = "address"
+ inventory.group_by_hostgroups = True
# bypass authentication and API fetch calls
inventory._check_api = mocker.MagicMock(side_effect=check_api)
diff --git a/ansible_collections/community/general/tests/unit/plugins/inventory/test_linode.py b/ansible_collections/community/general/tests/unit/plugins/inventory/test_linode.py
index a4f556761..0f239f2dd 100644
--- a/ansible_collections/community/general/tests/unit/plugins/inventory/test_linode.py
+++ b/ansible_collections/community/general/tests/unit/plugins/inventory/test_linode.py
@@ -37,11 +37,25 @@ def test_missing_access_token_lookup(inventory):
assert 'Could not retrieve Linode access token' in error_message
-def test_verify_file(tmp_path, inventory):
+def test_verify_file_yml(tmp_path, inventory):
file = tmp_path / "foobar.linode.yml"
file.touch()
assert inventory.verify_file(str(file)) is True
+def test_verify_file_yaml(tmp_path, inventory):
+ file = tmp_path / "foobar.linode.yaml"
+ file.touch()
+ assert inventory.verify_file(str(file)) is True
+
+
+def test_verify_file_bad_config_yml(inventory):
+ assert inventory.verify_file("foobar.linode.yml") is False
+
+
+def test_verify_file_bad_config_yaml(inventory):
+ assert inventory.verify_file("foobar.linode.yaml") is False
+
+
def test_verify_file_bad_config(inventory):
- assert inventory.verify_file('foobar.linode.yml') is False
+ assert inventory.verify_file("foobar.wrongcloud.yml") is False
diff --git a/ansible_collections/community/general/tests/unit/plugins/inventory/test_proxmox.py b/ansible_collections/community/general/tests/unit/plugins/inventory/test_proxmox.py
index 13832c938..ea6c84bcd 100644
--- a/ansible_collections/community/general/tests/unit/plugins/inventory/test_proxmox.py
+++ b/ansible_collections/community/general/tests/unit/plugins/inventory/test_proxmox.py
@@ -646,13 +646,15 @@ def test_populate(inventory, mocker):
inventory.group_prefix = 'proxmox_'
inventory.facts_prefix = 'proxmox_'
inventory.strict = False
+ inventory.exclude_nodes = False
opts = {
'group_prefix': 'proxmox_',
'facts_prefix': 'proxmox_',
'want_facts': True,
'want_proxmox_nodes_ansible_host': True,
- 'qemu_extended_statuses': True
+ 'qemu_extended_statuses': True,
+ 'exclude_nodes': False
}
# bypass authentication and API fetch calls
@@ -723,13 +725,15 @@ def test_populate_missing_qemu_extended_groups(inventory, mocker):
inventory.group_prefix = 'proxmox_'
inventory.facts_prefix = 'proxmox_'
inventory.strict = False
+ inventory.exclude_nodes = False
opts = {
'group_prefix': 'proxmox_',
'facts_prefix': 'proxmox_',
'want_facts': True,
'want_proxmox_nodes_ansible_host': True,
- 'qemu_extended_statuses': False
+ 'qemu_extended_statuses': False,
+ 'exclude_nodes': False
}
# bypass authentication and API fetch calls
@@ -743,3 +747,40 @@ def test_populate_missing_qemu_extended_groups(inventory, mocker):
# make sure that ['prelaunch', 'paused'] are not in the group list
for group in ['paused', 'prelaunch']:
assert ('%sall_%s' % (inventory.group_prefix, group)) not in inventory.inventory.groups
+
+
+def test_populate_exclude_nodes(inventory, mocker):
+ # module settings
+ inventory.proxmox_user = 'root@pam'
+ inventory.proxmox_password = 'password'
+ inventory.proxmox_url = 'https://localhost:8006'
+ inventory.group_prefix = 'proxmox_'
+ inventory.facts_prefix = 'proxmox_'
+ inventory.strict = False
+ inventory.exclude_nodes = True
+
+ opts = {
+ 'group_prefix': 'proxmox_',
+ 'facts_prefix': 'proxmox_',
+ 'want_facts': True,
+ 'want_proxmox_nodes_ansible_host': True,
+ 'qemu_extended_statuses': False,
+ 'exclude_nodes': True
+ }
+
+ # bypass authentication and API fetch calls
+ inventory._get_auth = mocker.MagicMock(side_effect=get_auth)
+ inventory._get_json = mocker.MagicMock(side_effect=get_json)
+ inventory._get_vm_snapshots = mocker.MagicMock(side_effect=get_vm_snapshots)
+ inventory.get_option = mocker.MagicMock(side_effect=get_option(opts))
+ inventory._can_add_host = mocker.MagicMock(return_value=True)
+ inventory._populate()
+
+ # make sure that nodes are not in the inventory
+ for node in ['testnode', 'testnode2']:
+ assert node not in inventory.inventory.hosts
+ # make sure that nodes group is absent
+ assert ('%s_nodes' % (inventory.group_prefix)) not in inventory.inventory.groups
+ # make sure that nodes are not in the "ungrouped" group
+ for node in ['testnode', 'testnode2']:
+ assert node not in inventory.inventory.get_groups_dict()["ungrouped"]
diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_conftest.py b/ansible_collections/community/general/tests/unit/plugins/lookup/conftest.py
index 18afae1a3..d4ae42ab8 100644
--- a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_conftest.py
+++ b/ansible_collections/community/general/tests/unit/plugins/lookup/conftest.py
@@ -10,17 +10,11 @@ import pytest
from ansible_collections.community.general.plugins.lookup.onepassword import OnePass
-OP_VERSION_FIXTURES = [
- "opv1",
- "opv2"
-]
-
-
@pytest.fixture
def fake_op(mocker):
def _fake_op(version):
mocker.patch("ansible_collections.community.general.plugins.lookup.onepassword.OnePassCLIBase.get_current_version", return_value=version)
- op = OnePass(None, None, None, None, None)
+ op = OnePass()
op._config._config_file_path = "/home/jin/.op/config"
mocker.patch.object(op._cli, "_run")
diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_common.py b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_common.py
index 092979225..bf0cc35c1 100644
--- a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_common.py
+++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_common.py
@@ -81,5 +81,215 @@ MOCK_ENTRIES = {
"expected": ["first value"],
"output": load_file("v2_out_03.json")
},
+ {
+ # Request data from an omitted value (label lookup, no section)
+ "vault_name": "Test Vault",
+ "queries": ["Omitted values"],
+ "kwargs": {
+ "field": "label-without-value",
+ },
+ "expected": [""],
+ "output": load_file("v2_out_04.json")
+ },
+ {
+ # Request data from an omitted value (id lookup, no section)
+ "vault_name": "Test Vault",
+ "queries": ["Omitted values"],
+ "kwargs": {
+ "field": "67890q7mspf4x6zrlw3qejn7m",
+ },
+ "expected": [""],
+ "output": load_file("v2_out_04.json")
+ },
+ {
+ # Request data from an omitted value (label lookup, with section)
+ "vault_name": "Test Vault",
+ "queries": ["Omitted values"],
+ "kwargs": {
+ "field": "section-label-without-value",
+ "section": "Section-Without-Values"
+ },
+ "expected": [""],
+ "output": load_file("v2_out_04.json")
+ },
+ {
+ # Request data from an omitted value (id lookup, with section)
+ "vault_name": "Test Vault",
+ "queries": ["Omitted values"],
+ "kwargs": {
+ "field": "123345q7mspf4x6zrlw3qejn7m",
+ "section": "section-without-values",
+ },
+ "expected": [""],
+ "output": load_file("v2_out_04.json")
+ },
+ {
+ # Query item without section by lowercase id (case matching)
+ "vault_name": "Test Vault",
+ "queries": ["LabelCasing"],
+ "kwargs": {
+ "field": "lowercaseid",
+ },
+ "expected": ["lowercaseid"],
+ "output": load_file("v2_out_05.json")
+ },
+ {
+ # Query item without section by lowercase id (case not matching)
+ "vault_name": "Test Vault",
+ "queries": ["LabelCasing"],
+ "kwargs": {
+ "field": "LOWERCASEID",
+ },
+ "expected": ["lowercaseid"],
+ "output": load_file("v2_out_05.json")
+ },
+ {
+ # Query item without section by lowercase label (case matching)
+ "vault_name": "Test Vault",
+ "queries": ["LabelCasing"],
+ "kwargs": {
+ "field": "lowercaselabel",
+ },
+ "expected": ["lowercaselabel"],
+ "output": load_file("v2_out_05.json")
+ },
+ {
+ # Query item without section by lowercase label (case not matching)
+ "vault_name": "Test Vault",
+ "queries": ["LabelCasing"],
+ "kwargs": {
+ "field": "LOWERCASELABEL",
+ },
+ "expected": ["lowercaselabel"],
+ "output": load_file("v2_out_05.json")
+ },
+ {
+ # Query item without section by mixed case id (case matching)
+ "vault_name": "Test Vault",
+ "queries": ["LabelCasing"],
+ "kwargs": {
+ "field": "MiXeDcAsEiD",
+ },
+ "expected": ["mixedcaseid"],
+ "output": load_file("v2_out_05.json")
+ },
+ {
+ # Query item without section by mixed case id (case not matching)
+ "vault_name": "Test Vault",
+ "queries": ["LabelCasing"],
+ "kwargs": {
+ "field": "mixedcaseid",
+ },
+ "expected": ["mixedcaseid"],
+ "output": load_file("v2_out_05.json")
+ },
+ {
+ # Query item without section by mixed case label (case matching)
+ "vault_name": "Test Vault",
+ "queries": ["LabelCasing"],
+ "kwargs": {
+ "field": "MiXeDcAsElAbEl",
+ },
+ "expected": ["mixedcaselabel"],
+ "output": load_file("v2_out_05.json")
+ },
+ {
+ # Query item without section by mixed case label (case not matching)
+ "vault_name": "Test Vault",
+ "queries": ["LabelCasing"],
+ "kwargs": {
+ "field": "mixedcaselabel",
+ },
+ "expected": ["mixedcaselabel"],
+ "output": load_file("v2_out_05.json")
+ },
+ {
+ # Query item with section by lowercase id (case matching)
+ "vault_name": "Test Vault",
+ "queries": ["LabelCasing"],
+ "kwargs": {
+ "field": "sectionlowercaseid",
+ "section": "section-with-values",
+ },
+ "expected": ["sectionlowercaseid"],
+ "output": load_file("v2_out_05.json")
+ },
+ {
+ # Query item with section by lowercase id (case not matching)
+ "vault_name": "Test Vault",
+ "queries": ["LabelCasing"],
+ "kwargs": {
+ "field": "SECTIONLOWERCASEID",
+ "section": "section-with-values",
+ },
+ "expected": ["sectionlowercaseid"],
+ "output": load_file("v2_out_05.json")
+ },
+ {
+ # Query item with section by lowercase label (case matching)
+ "vault_name": "Test Vault",
+ "queries": ["LabelCasing"],
+ "kwargs": {
+ "field": "sectionlowercaselabel",
+ "section": "section-with-values",
+ },
+ "expected": ["sectionlowercaselabel"],
+ "output": load_file("v2_out_05.json")
+ },
+ {
+ # Query item with section by lowercase label (case not matching)
+ "vault_name": "Test Vault",
+ "queries": ["LabelCasing"],
+ "kwargs": {
+ "field": "SECTIONLOWERCASELABEL",
+ "section": "section-with-values",
+ },
+ "expected": ["sectionlowercaselabel"],
+ "output": load_file("v2_out_05.json")
+ },
+ {
+ # Query item with section by lowercase id (case matching)
+ "vault_name": "Test Vault",
+ "queries": ["LabelCasing"],
+ "kwargs": {
+ "field": "SeCtIoNmIxEdCaSeId",
+ "section": "section-with-values",
+ },
+ "expected": ["sectionmixedcaseid"],
+ "output": load_file("v2_out_05.json")
+ },
+ {
+ # Query item with section by lowercase id (case not matching)
+ "vault_name": "Test Vault",
+ "queries": ["LabelCasing"],
+ "kwargs": {
+ "field": "sectionmixedcaseid",
+ "section": "section-with-values",
+ },
+ "expected": ["sectionmixedcaseid"],
+ "output": load_file("v2_out_05.json")
+ },
+ {
+ # Query item with section by lowercase label (case matching)
+ "vault_name": "Test Vault",
+ "queries": ["LabelCasing"],
+ "kwargs": {
+ "field": "SeCtIoNmIxEdCaSeLaBeL",
+ "section": "section-with-values",
+ },
+ "expected": ["sectionmixedcaselabel"],
+ "output": load_file("v2_out_05.json")
+ },
+ {
+ # Query item with section by lowercase label (case not matching)
+ "vault_name": "Test Vault",
+ "queries": ["LabelCasing"],
+ "kwargs": {
+ "field": "sectionmixedcaselabel",
+ "section": "section-with-values",
+ },
+ "expected": ["sectionmixedcaselabel"],
+ "output": load_file("v2_out_05.json")
+ },
],
}
diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_01.json b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_01.json
index 7ef0bb0c2..0ace5c825 100644
--- a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_01.json
+++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_01.json
@@ -13,10 +13,10 @@
"additional_information": "Jan 18, 2015, 08:13:38",
"fields": [
{
- "id": "password",
+ "id": "Password",
"type": "CONCEALED",
"purpose": "PASSWORD",
- "label": "password",
+ "label": "Password",
"value": "OctoberPoppyNuttyDraperySabbath",
"reference": "op://Test Vault/Authy Backup/password",
"password_details": {
diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_04.json b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_04.json
new file mode 100644
index 000000000..13b6cc2aa
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_04.json
@@ -0,0 +1,67 @@
+{
+ "id": "bgqegp3xcxnpfkb45olwigpkpi",
+ "title": "OmittedValues",
+ "version": 1,
+ "vault": {
+ "id": "stpebbaccrq72xulgouxsk4p7y",
+ "name": "Private"
+ },
+ "category": "LOGIN",
+ "last_edited_by": "WOUTERRUYBH7BFPHMZ2KKGL6AU",
+ "created_at": "2023-09-12T08:30:07Z",
+ "updated_at": "2023-09-12T08:30:07Z",
+ "additional_information": "fluxility",
+ "sections": [
+ {
+ "id": "7osqcvd43i75teocdzbb6d7mie",
+ "label": "section-without-values"
+ }
+ ],
+ "fields": [
+ {
+ "id": "username",
+ "type": "STRING",
+ "purpose": "USERNAME",
+ "label": "username",
+ "value": "fluxility",
+ "reference": "op://Testcase/OmittedValues/username"
+ },
+ {
+ "id": "password",
+ "type": "CONCEALED",
+ "purpose": "PASSWORD",
+ "label": "password",
+ "value": "j89Dyb7psat*hkbkyLUQyq@GR.a-g2pQH_V_xtMhrn37rQ_2uRYoRiozj6TjWVLy2pbfEvjnse",
+ "entropy": 427.01202392578125,
+ "reference": "op://Testcase/OmittedValues/password",
+ "password_details": {
+ "entropy": 427,
+ "generated": true,
+ "strength": "FANTASTIC"
+ }
+ },
+ {
+ "id": "notesPlain",
+ "type": "STRING",
+ "purpose": "NOTES",
+ "label": "notesPlain",
+ "reference": "op://Testcase/OmittedValues/notesPlain"
+ },
+ {
+ "id": "67890q7mspf4x6zrlw3qejn7m",
+ "type": "URL",
+ "label": "label-without-value",
+ "reference": "op://01202392578125/OmittedValues/section-without-values/section-without-value"
+ },
+ {
+ "id": "123345q7mspf4x6zrlw3qejn7m",
+ "section": {
+ "id": "6hbtca5yrlmoptgy3nw74222",
+ "label": "section-without-values"
+ },
+ "type": "URL",
+ "label": "section-label-without-value",
+ "reference": "op://01202392578125/OmittedValues/section-without-values/section-without-value"
+ }
+ ]
+}
diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_04.json.license b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_04.json.license
new file mode 100644
index 000000000..969b956c2
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_04.json.license
@@ -0,0 +1,3 @@
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
+SPDX-FileCopyrightText: 2022, Ansible Project
diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_05.json b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_05.json
new file mode 100644
index 000000000..f925476e1
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_05.json
@@ -0,0 +1,102 @@
+{
+ "id": "bgqegp3xcxnpfkb45olwigpkpi",
+ "title": "LabelCasing",
+ "version": 1,
+ "vault": {
+ "id": "stpebbaccrq72xulgouxsk4p7y",
+ "name": "Private"
+ },
+ "category": "LOGIN",
+ "last_edited_by": "WOUTERRUYBH7BFPHMZ2KKGL6AU",
+ "created_at": "2023-09-12T08:30:07Z",
+ "updated_at": "2023-09-12T08:30:07Z",
+ "additional_information": "fluxility",
+ "sections": [
+ {
+ "id": "7osqcvd43i75teocdzbb6d7mie",
+ "label": "section-with-values"
+ }
+ ],
+ "fields": [
+ {
+ "id": "lowercaseid",
+ "type": "STRING",
+ "purpose": "USERNAME",
+ "label": "label0",
+ "value": "lowercaseid",
+ "reference": "op://Testcase/LabelCasing/lowercase"
+ },
+ {
+ "id": "MiXeDcAsEiD",
+ "type": "STRING",
+ "purpose": "USERNAME",
+ "label": "label1",
+ "value": "mixedcaseid",
+ "reference": "op://Testcase/LabelCasing/lowercase"
+ },
+ {
+ "id": "id1",
+ "type": "STRING",
+ "purpose": "USERNAME",
+ "label": "lowercaselabel",
+ "value": "lowercaselabel",
+ "reference": "op://Testcase/LabelCasing/lowercase"
+ },
+ {
+ "id": "id2",
+ "type": "STRING",
+ "purpose": "USERNAME",
+ "label": "MiXeDcAsElAbEl",
+ "value": "mixedcaselabel",
+ "reference": "op://Testcase/LabelCasing/lowercase"
+ },
+ {
+ "id": "sectionlowercaseid",
+ "type": "STRING",
+ "purpose": "USERNAME",
+ "label": "label2",
+ "value": "sectionlowercaseid",
+ "reference": "op://Testcase/LabelCasing/lowercase",
+ "section": {
+ "id": "7osqcvd43i75teocdzbb6d7mie",
+ "label": "section-with-values"
+ }
+ },
+ {
+ "id": "SeCtIoNmIxEdCaSeId",
+ "type": "STRING",
+ "purpose": "USERNAME",
+ "label": "label3",
+ "value": "sectionmixedcaseid",
+ "reference": "op://Testcase/LabelCasing/lowercase",
+ "section": {
+ "id": "7osqcvd43i75teocdzbb6d7mie",
+ "label": "section-with-values"
+ }
+ },
+ {
+ "id": "id3",
+ "type": "STRING",
+ "purpose": "USERNAME",
+ "label": "sectionlowercaselabel",
+ "value": "sectionlowercaselabel",
+ "reference": "op://Testcase/LabelCasing/lowercase",
+ "section": {
+ "id": "7osqcvd43i75teocdzbb6d7mie",
+ "label": "section-with-values"
+ }
+ },
+ {
+ "id": "id2",
+ "type": "STRING",
+ "purpose": "USERNAME",
+ "label": "SeCtIoNmIxEdCaSeLaBeL",
+ "value": "sectionmixedcaselabel",
+ "reference": "op://Testcase/LabelCasing/lowercase",
+ "section": {
+ "id": "7osqcvd43i75teocdzbb6d7mie",
+ "label": "section-with-values"
+ }
+ }
+ ]
+}
diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_05.json.license b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_05.json.license
new file mode 100644
index 000000000..969b956c2
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/lookup/onepassword_fixtures/v2_out_05.json.license
@@ -0,0 +1,3 @@
+GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+SPDX-License-Identifier: GPL-3.0-or-later
+SPDX-FileCopyrightText: 2022, Ansible Project
diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/test_bitwarden.py b/ansible_collections/community/general/tests/unit/plugins/lookup/test_bitwarden.py
index d45263965..9270dd44e 100644
--- a/ansible_collections/community/general/tests/unit/plugins/lookup/test_bitwarden.py
+++ b/ansible_collections/community/general/tests/unit/plugins/lookup/test_bitwarden.py
@@ -14,10 +14,13 @@ from ansible.module_utils import six
from ansible.plugins.loader import lookup_loader
from ansible_collections.community.general.plugins.lookup.bitwarden import Bitwarden
+MOCK_COLLECTION_ID = "3b12a9da-7c49-40b8-ad33-aede017a7ead"
MOCK_RECORDS = [
{
- "collectionIds": [],
+ "collectionIds": [
+ MOCK_COLLECTION_ID
+ ],
"deletedDate": None,
"favorite": False,
"fields": [
@@ -65,7 +68,9 @@ MOCK_RECORDS = [
"type": 1
},
{
- "collectionIds": [],
+ "collectionIds": [
+ MOCK_COLLECTION_ID
+ ],
"deletedDate": None,
"favorite": False,
"folderId": None,
@@ -85,7 +90,9 @@ MOCK_RECORDS = [
"type": 1
},
{
- "collectionIds": [],
+ "collectionIds": [
+ MOCK_COLLECTION_ID
+ ],
"deletedDate": None,
"favorite": False,
"folderId": None,
@@ -111,7 +118,10 @@ class MockBitwarden(Bitwarden):
unlocked = True
- def _get_matches(self, search_value, search_field="name", collection_id=None):
+ def _get_matches(self, search_value=None, search_field="name", collection_id=None):
+ if not search_value and collection_id:
+ return list(filter(lambda record: collection_id in record['collectionIds'], MOCK_RECORDS))
+
return list(filter(lambda record: record[search_field] == search_value, MOCK_RECORDS))
@@ -156,5 +166,32 @@ class TestLookupModule(unittest.TestCase):
def test_bitwarden_plugin_unlocked(self):
record = MOCK_RECORDS[0]
record_name = record['name']
- with self.assertRaises(AnsibleError):
+ with self.assertRaises(AnsibleError) as raised_error:
self.lookup.run([record_name], field='password')
+
+ self.assertEqual("Bitwarden Vault locked. Run 'bw unlock'.", str(raised_error.exception))
+
+ def test_bitwarden_plugin_without_session_option(self):
+ mock_bitwarden = MockBitwarden()
+ with patch("ansible_collections.community.general.plugins.lookup.bitwarden._bitwarden", mock_bitwarden):
+ record = MOCK_RECORDS[0]
+ record_name = record['name']
+ session = 'session'
+
+ self.lookup.run([record_name], field=None)
+ self.assertIsNone(mock_bitwarden.session)
+
+ def test_bitwarden_plugin_session_option(self):
+ mock_bitwarden = MockBitwarden()
+ with patch("ansible_collections.community.general.plugins.lookup.bitwarden._bitwarden", mock_bitwarden):
+ record = MOCK_RECORDS[0]
+ record_name = record['name']
+ session = 'session'
+
+ self.lookup.run([record_name], field=None, bw_session=session)
+ self.assertEqual(mock_bitwarden.session, session)
+
+ @patch('ansible_collections.community.general.plugins.lookup.bitwarden._bitwarden', new=MockBitwarden())
+ def test_bitwarden_plugin_full_collection(self):
+ # Try to retrieve the full records of the given collection.
+ self.assertEqual(MOCK_RECORDS, self.lookup.run(None, collection_id=MOCK_COLLECTION_ID)[0])
diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/test_bitwarden_secrets_manager.py b/ansible_collections/community/general/tests/unit/plugins/lookup/test_bitwarden_secrets_manager.py
new file mode 100644
index 000000000..aaeaf79ea
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/lookup/test_bitwarden_secrets_manager.py
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2023, jantari (https://github.com/jantari)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+
+__metaclass__ = type
+
+import json
+from ansible_collections.community.general.tests.unit.compat import unittest
+from ansible_collections.community.general.tests.unit.compat.mock import patch
+
+from ansible.errors import AnsibleLookupError
+from ansible.plugins.loader import lookup_loader
+from ansible_collections.community.general.plugins.lookup.bitwarden_secrets_manager import BitwardenSecretsManager
+
+
+MOCK_SECRETS = [
+ {
+ "object": "secret",
+ "id": "ababc4a8-c242-4e54-bceb-77d17cdf2e07",
+ "organizationId": "3c33066c-a0bf-4e70-9a3c-24cda6aaddd5",
+ "projectId": "81869439-bfe5-442f-8b4e-b172e68b0ab2",
+ "key": "TEST_SECRET",
+ "value": "1234supersecret5678",
+ "note": "A test secret to use when developing the ansible bitwarden_secrets_manager lookup plugin",
+ "creationDate": "2023-04-23T13:13:37.7507017Z",
+ "revisionDate": "2023-04-23T13:13:37.7507017Z"
+ },
+ {
+ "object": "secret",
+ "id": "d4b7c8fa-fc95-40d7-a13c-6e186ee69d53",
+ "organizationId": "3c33066c-a0bf-4e70-9a3c-24cda6aaddd5",
+ "projectId": "81869439-bfe5-442f-8b4e-b172e68b0ab2",
+ "key": "TEST_SECRET_2",
+ "value": "abcd_such_secret_very_important_efgh",
+ "note": "notes go here",
+ "creationDate": "2023-04-23T13:26:44.0392906Z",
+ "revisionDate": "2023-04-23T13:26:44.0392906Z"
+ }
+]
+
+
+class MockBitwardenSecretsManager(BitwardenSecretsManager):
+
+ def _run(self, args, stdin=None):
+ # secret_id is the last argument passed to the bws CLI
+ secret_id = args[-1]
+ rc = 1
+ out = ""
+ err = ""
+ found_secrets = list(filter(lambda record: record["id"] == secret_id, MOCK_SECRETS))
+
+ if len(found_secrets) == 0:
+ err = "simulated bws CLI error: 404 no secret with such id"
+ elif len(found_secrets) == 1:
+ rc = 0
+ # The real bws CLI will only ever return one secret / json object for the "get secret <secret-id>" command
+ out = json.dumps(found_secrets[0])
+ else:
+ # This should never happen unless there's an error in the test MOCK_SECRETS.
+ # The real Bitwarden Secrets Manager assigns each secret a unique ID.
+ raise ValueError("More than 1 secret found with id: '{0}'. Impossible!".format(secret_id))
+
+ return out, err, rc
+
+
+class TestLookupModule(unittest.TestCase):
+
+ def setUp(self):
+ self.lookup = lookup_loader.get('community.general.bitwarden_secrets_manager')
+
+ @patch('ansible_collections.community.general.plugins.lookup.bitwarden_secrets_manager._bitwarden_secrets_manager', new=MockBitwardenSecretsManager())
+ def test_bitwarden_secrets_manager(self):
+ # Getting a secret by its id should return the full secret info
+ self.assertEqual([MOCK_SECRETS[0]], self.lookup.run(['ababc4a8-c242-4e54-bceb-77d17cdf2e07'], bws_access_token='123'))
+
+ @patch('ansible_collections.community.general.plugins.lookup.bitwarden_secrets_manager._bitwarden_secrets_manager', new=MockBitwardenSecretsManager())
+ def test_bitwarden_secrets_manager_no_match(self):
+ # Getting a nonexistent secret id throws exception
+ with self.assertRaises(AnsibleLookupError):
+ self.lookup.run(['nonexistant_id'], bws_access_token='123')
diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/test_github_app_access_token.py b/ansible_collections/community/general/tests/unit/plugins/lookup/test_github_app_access_token.py
new file mode 100644
index 000000000..4bf9c7e70
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/lookup/test_github_app_access_token.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2023, Poh Wei Sheng <weisheng-p@hotmail.sg>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+import json
+
+from ansible_collections.community.general.tests.unit.compat import unittest
+from ansible_collections.community.general.tests.unit.compat.mock import (
+ patch,
+ MagicMock,
+ mock_open
+)
+from ansible.plugins.loader import lookup_loader
+
+
+class MockJWT(MagicMock):
+ def encode(self, payload, key, alg):
+ return 'Foobar'
+
+
+class MockResponse(MagicMock):
+ response_token = 'Bar'
+
+ def read(self):
+ return json.dumps({
+ "token": self.response_token,
+ }).encode('utf-8')
+
+
+class TestLookupModule(unittest.TestCase):
+
+ def test_get_token(self):
+ with patch.multiple("ansible_collections.community.general.plugins.lookup.github_app_access_token",
+ open=mock_open(read_data="foo_bar"),
+ open_url=MagicMock(return_value=MockResponse()),
+ jwk_from_pem=MagicMock(return_value='private_key'),
+ jwt_instance=MockJWT(),
+ HAS_JWT=True):
+ lookup = lookup_loader.get('community.general.github_app_access_token')
+ self.assertListEqual(
+ [MockResponse.response_token],
+ lookup.run(
+ [],
+ key_path="key",
+ app_id="app_id",
+ installation_id="installation_id",
+ token_expiry=600
+ )
+ )
diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/test_merge_variables.py b/ansible_collections/community/general/tests/unit/plugins/lookup/test_merge_variables.py
index 5085797b3..66cb2f08b 100644
--- a/ansible_collections/community/general/tests/unit/plugins/lookup/test_merge_variables.py
+++ b/ansible_collections/community/general/tests/unit/plugins/lookup/test_merge_variables.py
@@ -24,7 +24,7 @@ class TestMergeVariablesLookup(unittest.TestCase):
self.merge_vars_lookup = merge_variables.LookupModule(loader=self.loader, templar=self.templar)
@patch.object(AnsiblePlugin, 'set_options')
- @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'ignore', 'suffix'])
+ @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'ignore', 'suffix', None])
@patch.object(Templar, 'template', side_effect=[['item1'], ['item3']])
def test_merge_list(self, mock_set_options, mock_get_option, mock_template):
results = self.merge_vars_lookup.run(['__merge_list'], {
@@ -36,7 +36,7 @@ class TestMergeVariablesLookup(unittest.TestCase):
self.assertEqual(results, [['item1', 'item3']])
@patch.object(AnsiblePlugin, 'set_options')
- @patch.object(AnsiblePlugin, 'get_option', side_effect=[['initial_item'], 'ignore', 'suffix'])
+ @patch.object(AnsiblePlugin, 'get_option', side_effect=[['initial_item'], 'ignore', 'suffix', None])
@patch.object(Templar, 'template', side_effect=[['item1'], ['item3']])
def test_merge_list_with_initial_value(self, mock_set_options, mock_get_option, mock_template):
results = self.merge_vars_lookup.run(['__merge_list'], {
@@ -48,7 +48,7 @@ class TestMergeVariablesLookup(unittest.TestCase):
self.assertEqual(results, [['initial_item', 'item1', 'item3']])
@patch.object(AnsiblePlugin, 'set_options')
- @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'ignore', 'suffix'])
+ @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'ignore', 'suffix', None])
@patch.object(Templar, 'template', side_effect=[{'item1': 'test', 'list_item': ['test1']},
{'item2': 'test', 'list_item': ['test2']}])
def test_merge_dict(self, mock_set_options, mock_get_option, mock_template):
@@ -73,7 +73,7 @@ class TestMergeVariablesLookup(unittest.TestCase):
@patch.object(AnsiblePlugin, 'set_options')
@patch.object(AnsiblePlugin, 'get_option', side_effect=[{'initial_item': 'random value', 'list_item': ['test0']},
- 'ignore', 'suffix'])
+ 'ignore', 'suffix', None])
@patch.object(Templar, 'template', side_effect=[{'item1': 'test', 'list_item': ['test1']},
{'item2': 'test', 'list_item': ['test2']}])
def test_merge_dict_with_initial_value(self, mock_set_options, mock_get_option, mock_template):
@@ -98,7 +98,7 @@ class TestMergeVariablesLookup(unittest.TestCase):
])
@patch.object(AnsiblePlugin, 'set_options')
- @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'warn', 'suffix'])
+ @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'warn', 'suffix', None])
@patch.object(Templar, 'template', side_effect=[{'item': 'value1'}, {'item': 'value2'}])
@patch.object(Display, 'warning')
def test_merge_dict_non_unique_warning(self, mock_set_options, mock_get_option, mock_template, mock_display):
@@ -111,7 +111,7 @@ class TestMergeVariablesLookup(unittest.TestCase):
self.assertEqual(results, [{'item': 'value2'}])
@patch.object(AnsiblePlugin, 'set_options')
- @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'error', 'suffix'])
+ @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'error', 'suffix', None])
@patch.object(Templar, 'template', side_effect=[{'item': 'value1'}, {'item': 'value2'}])
def test_merge_dict_non_unique_error(self, mock_set_options, mock_get_option, mock_template):
with self.assertRaises(AnsibleError):
@@ -121,7 +121,7 @@ class TestMergeVariablesLookup(unittest.TestCase):
})
@patch.object(AnsiblePlugin, 'set_options')
- @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'ignore', 'suffix'])
+ @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'ignore', 'suffix', None])
@patch.object(Templar, 'template', side_effect=[{'item1': 'test', 'list_item': ['test1']},
['item2', 'item3']])
def test_merge_list_and_dict(self, mock_set_options, mock_get_option, mock_template):
@@ -133,3 +133,150 @@ class TestMergeVariablesLookup(unittest.TestCase):
},
'testdict__merge_var': ['item2', 'item3']
})
+
+ @patch.object(AnsiblePlugin, 'set_options')
+ @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'ignore', 'suffix', ['all']])
+ @patch.object(Templar, 'template', side_effect=[
+ {'var': [{'item1': 'value1', 'item2': 'value2'}]},
+ {'var': [{'item5': 'value5', 'item6': 'value6'}]},
+ ])
+ def test_merge_dict_group_all(self, mock_set_options, mock_get_option, mock_template):
+ results = self.merge_vars_lookup.run(['__merge_var'], {
+ 'inventory_hostname': 'host1',
+ 'hostvars': {
+ 'host1': {
+ 'group_names': ['dummy1'],
+ 'inventory_hostname': 'host1',
+ '1testlist__merge_var': {
+ 'var': [{'item1': 'value1', 'item2': 'value2'}]
+ }
+ },
+ 'host2': {
+ 'group_names': ['dummy1'],
+ 'inventory_hostname': 'host2',
+ '2otherlist__merge_var': {
+ 'var': [{'item5': 'value5', 'item6': 'value6'}]
+ }
+ }
+ }
+ })
+
+ self.assertEqual(results, [
+ {'var': [
+ {'item1': 'value1', 'item2': 'value2'},
+ {'item5': 'value5', 'item6': 'value6'}
+ ]}
+ ])
+
+ @patch.object(AnsiblePlugin, 'set_options')
+ @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'ignore', 'suffix', ['dummy1']])
+ @patch.object(Templar, 'template', side_effect=[
+ {'var': [{'item1': 'value1', 'item2': 'value2'}]},
+ {'var': [{'item5': 'value5', 'item6': 'value6'}]},
+ ])
+ def test_merge_dict_group_single(self, mock_set_options, mock_get_option, mock_template):
+ results = self.merge_vars_lookup.run(['__merge_var'], {
+ 'inventory_hostname': 'host1',
+ 'hostvars': {
+ 'host1': {
+ 'group_names': ['dummy1'],
+ 'inventory_hostname': 'host1',
+ '1testlist__merge_var': {
+ 'var': [{'item1': 'value1', 'item2': 'value2'}]
+ }
+ },
+ 'host2': {
+ 'group_names': ['dummy1'],
+ 'inventory_hostname': 'host2',
+ '2otherlist__merge_var': {
+ 'var': [{'item5': 'value5', 'item6': 'value6'}]
+ }
+ },
+ 'host3': {
+ 'group_names': ['dummy2'],
+ 'inventory_hostname': 'host3',
+ '3otherlist__merge_var': {
+ 'var': [{'item3': 'value3', 'item4': 'value4'}]
+ }
+ }
+ }
+ })
+
+ self.assertEqual(results, [
+ {'var': [
+ {'item1': 'value1', 'item2': 'value2'},
+ {'item5': 'value5', 'item6': 'value6'}
+ ]}
+ ])
+
+ @patch.object(AnsiblePlugin, 'set_options')
+ @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'ignore', 'suffix', ['dummy1', 'dummy2']])
+ @patch.object(Templar, 'template', side_effect=[
+ {'var': [{'item1': 'value1', 'item2': 'value2'}]},
+ {'var': [{'item5': 'value5', 'item6': 'value6'}]},
+ ])
+ def test_merge_dict_group_multiple(self, mock_set_options, mock_get_option, mock_template):
+ results = self.merge_vars_lookup.run(['__merge_var'], {
+ 'inventory_hostname': 'host1',
+ 'hostvars': {
+ 'host1': {
+ 'group_names': ['dummy1'],
+ 'inventory_hostname': 'host1',
+ '1testlist__merge_var': {
+ 'var': [{'item1': 'value1', 'item2': 'value2'}]
+ }
+ },
+ 'host2': {
+ 'group_names': ['dummy2'],
+ 'inventory_hostname': 'host2',
+ '2otherlist__merge_var': {
+ 'var': [{'item5': 'value5', 'item6': 'value6'}]
+ }
+ },
+ 'host3': {
+ 'group_names': ['dummy3'],
+ 'inventory_hostname': 'host3',
+ '3otherlist__merge_var': {
+ 'var': [{'item3': 'value3', 'item4': 'value4'}]
+ }
+ }
+ }
+ })
+
+ self.assertEqual(results, [
+ {'var': [
+ {'item1': 'value1', 'item2': 'value2'},
+ {'item5': 'value5', 'item6': 'value6'}
+ ]}
+ ])
+
+ @patch.object(AnsiblePlugin, 'set_options')
+ @patch.object(AnsiblePlugin, 'get_option', side_effect=[None, 'ignore', 'suffix', ['dummy1', 'dummy2']])
+ @patch.object(Templar, 'template', side_effect=[
+ ['item1'],
+ ['item5'],
+ ])
+ def test_merge_list_group_multiple(self, mock_set_options, mock_get_option, mock_template):
+ print()
+ results = self.merge_vars_lookup.run(['__merge_var'], {
+ 'inventory_hostname': 'host1',
+ 'hostvars': {
+ 'host1': {
+ 'group_names': ['dummy1'],
+ 'inventory_hostname': 'host1',
+ '1testlist__merge_var': ['item1']
+ },
+ 'host2': {
+ 'group_names': ['dummy2'],
+ 'inventory_hostname': 'host2',
+ '2otherlist__merge_var': ['item5']
+ },
+ 'host3': {
+ 'group_names': ['dummy3'],
+ 'inventory_hostname': 'host3',
+ '3otherlist__merge_var': ['item3']
+ }
+ }
+ })
+
+ self.assertEqual(results, [['item1', 'item5']])
diff --git a/ansible_collections/community/general/tests/unit/plugins/lookup/test_onepassword.py b/ansible_collections/community/general/tests/unit/plugins/lookup/test_onepassword.py
index ab7f3def2..dc00e5703 100644
--- a/ansible_collections/community/general/tests/unit/plugins/lookup/test_onepassword.py
+++ b/ansible_collections/community/general/tests/unit/plugins/lookup/test_onepassword.py
@@ -10,15 +10,9 @@ import itertools
import json
import pytest
-from .onepassword_conftest import ( # noqa: F401, pylint: disable=unused-import
- OP_VERSION_FIXTURES,
- fake_op,
- opv1,
- opv2,
-)
from .onepassword_common import MOCK_ENTRIES
-from ansible.errors import AnsibleLookupError
+from ansible.errors import AnsibleLookupError, AnsibleOptionsError
from ansible.plugins.loader import lookup_loader
from ansible_collections.community.general.plugins.lookup.onepassword import (
OnePassCLIv1,
@@ -26,6 +20,12 @@ from ansible_collections.community.general.plugins.lookup.onepassword import (
)
+OP_VERSION_FIXTURES = [
+ "opv1",
+ "opv2"
+]
+
+
@pytest.mark.parametrize(
("args", "rc", "expected_call_args", "expected_call_kwargs", "expected"),
(
@@ -82,6 +82,12 @@ def test_assert_logged_in_v2(mocker, args, out, expected_call_args, expected_cal
assert result == expected
+def test_assert_logged_in_v2_connect():
+ op_cli = OnePassCLIv2(connect_host="http://localhost:8080", connect_token="foobar")
+ result = op_cli.assert_logged_in()
+ assert result
+
+
def test_full_signin_v2(mocker):
mocker.patch.object(OnePassCLIv2, "_run", return_value=[0, "", ""])
@@ -264,5 +270,50 @@ def test_signin(op_fixture, request):
op = request.getfixturevalue(op_fixture)
op._cli.master_password = "master_pass"
op._cli.signin()
- print(op._cli.version)
op._cli._run.assert_called_once_with(['signin', '--raw'], command_input=b"master_pass")
+
+
+def test_op_doc(mocker):
+ document_contents = "Document Contents\n"
+
+ mocker.patch("ansible_collections.community.general.plugins.lookup.onepassword.OnePass.assert_logged_in", return_value=True)
+ mocker.patch("ansible_collections.community.general.plugins.lookup.onepassword.OnePassCLIBase._run", return_value=(0, document_contents, ""))
+
+ op_lookup = lookup_loader.get("community.general.onepassword_doc")
+ result = op_lookup.run(["Private key doc"])
+
+ assert result == [document_contents]
+
+
+@pytest.mark.parametrize(
+ ("plugin", "connect_host", "connect_token"),
+ [
+ (plugin, connect_host, connect_token)
+ for plugin in ("community.general.onepassword", "community.general.onepassword_raw")
+ for (connect_host, connect_token) in
+ (
+ ("http://localhost", None),
+ (None, "foobar"),
+ )
+ ]
+)
+def test_op_connect_partial_args(plugin, connect_host, connect_token, mocker):
+ op_lookup = lookup_loader.get(plugin)
+
+ mocker.patch("ansible_collections.community.general.plugins.lookup.onepassword.OnePass._get_cli_class", OnePassCLIv2)
+
+ with pytest.raises(AnsibleOptionsError):
+ op_lookup.run("login", vault_name="test vault", connect_host=connect_host, connect_token=connect_token)
+
+
+@pytest.mark.parametrize(
+ ("kwargs"),
+ (
+ {"connect_host": "http://localhost", "connect_token": "foobar"},
+ {"service_account_token": "foobar"},
+ )
+)
+def test_opv1_unsupported_features(kwargs):
+ op_cli = OnePassCLIv1(**kwargs)
+ with pytest.raises(AnsibleLookupError):
+ op_cli.full_signin()
diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/hwc/test_hwc_utils.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/hwc/test_hwc_utils.py
index 1344496b1..9b0be0bb4 100644
--- a/ansible_collections/community/general/tests/unit/plugins/module_utils/hwc/test_hwc_utils.py
+++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/hwc/test_hwc_utils.py
@@ -6,11 +6,20 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
+import sys
+
from ansible_collections.community.general.tests.unit.compat import unittest
from ansible_collections.community.general.plugins.module_utils.hwc_utils import (HwcModuleException, navigate_value)
class HwcUtilsTestCase(unittest.TestCase):
+ def setUp(self):
+ super(HwcUtilsTestCase, self).setUp()
+
+ # Add backward compatibility
+ if sys.version_info < (3, 0):
+ self.assertRaisesRegex = self.assertRaisesRegexp
+
def test_navigate_value(self):
value = {
'foo': {
@@ -29,12 +38,12 @@ class HwcUtilsTestCase(unittest.TestCase):
{"foo.quiet.trees": 1}),
1)
- self.assertRaisesRegexp(HwcModuleException,
- r".* key\(q\) is not exist in dict",
- navigate_value, value, ["foo", "q", "tree"])
+ self.assertRaisesRegex(HwcModuleException,
+ r".* key\(q\) is not exist in dict",
+ navigate_value, value, ["foo", "q", "tree"])
- self.assertRaisesRegexp(HwcModuleException,
- r".* the index is out of list",
- navigate_value, value,
- ["foo", "quiet", "trees"],
- {"foo.quiet.trees": 2})
+ self.assertRaisesRegex(HwcModuleException,
+ r".* the index is out of list",
+ navigate_value, value,
+ ["foo", "quiet", "trees"],
+ {"foo.quiet.trees": 2})
diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_cmd_runner.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_cmd_runner.py
index 7cec215a7..86576e8ce 100644
--- a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_cmd_runner.py
+++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_cmd_runner.py
@@ -11,44 +11,44 @@ from sys import version_info
import pytest
from ansible_collections.community.general.tests.unit.compat.mock import MagicMock, PropertyMock
-from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, fmt
+from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt
TC_FORMATS = dict(
- simple_boolean__true=(fmt.as_bool, ("--superflag",), True, ["--superflag"]),
- simple_boolean__false=(fmt.as_bool, ("--superflag",), False, []),
- simple_boolean__none=(fmt.as_bool, ("--superflag",), None, []),
- simple_boolean_both__true=(fmt.as_bool, ("--superflag", "--falseflag"), True, ["--superflag"]),
- simple_boolean_both__false=(fmt.as_bool, ("--superflag", "--falseflag"), False, ["--falseflag"]),
- simple_boolean_both__none=(fmt.as_bool, ("--superflag", "--falseflag"), None, ["--falseflag"]),
- simple_boolean_both__none_ig=(fmt.as_bool, ("--superflag", "--falseflag", True), None, []),
- simple_boolean_not__true=(fmt.as_bool_not, ("--superflag",), True, []),
- simple_boolean_not__false=(fmt.as_bool_not, ("--superflag",), False, ["--superflag"]),
- simple_boolean_not__none=(fmt.as_bool_not, ("--superflag",), None, ["--superflag"]),
- simple_optval__str=(fmt.as_optval, ("-t",), "potatoes", ["-tpotatoes"]),
- simple_optval__int=(fmt.as_optval, ("-t",), 42, ["-t42"]),
- simple_opt_val__str=(fmt.as_opt_val, ("-t",), "potatoes", ["-t", "potatoes"]),
- simple_opt_val__int=(fmt.as_opt_val, ("-t",), 42, ["-t", "42"]),
- simple_opt_eq_val__str=(fmt.as_opt_eq_val, ("--food",), "potatoes", ["--food=potatoes"]),
- simple_opt_eq_val__int=(fmt.as_opt_eq_val, ("--answer",), 42, ["--answer=42"]),
- simple_list_potato=(fmt.as_list, (), "literal_potato", ["literal_potato"]),
- simple_list_42=(fmt.as_list, (), 42, ["42"]),
- simple_map=(fmt.as_map, ({'a': 1, 'b': 2, 'c': 3},), 'b', ["2"]),
- simple_default_type__list=(fmt.as_default_type, ("list",), [1, 2, 3, 5, 8], ["--1", "--2", "--3", "--5", "--8"]),
- simple_default_type__bool_true=(fmt.as_default_type, ("bool", "what"), True, ["--what"]),
- simple_default_type__bool_false=(fmt.as_default_type, ("bool", "what"), False, []),
- simple_default_type__potato=(fmt.as_default_type, ("any-other-type", "potato"), "42", ["--potato", "42"]),
- simple_fixed_true=(fmt.as_fixed, [("--always-here", "--forever")], True, ["--always-here", "--forever"]),
- simple_fixed_false=(fmt.as_fixed, [("--always-here", "--forever")], False, ["--always-here", "--forever"]),
- simple_fixed_none=(fmt.as_fixed, [("--always-here", "--forever")], None, ["--always-here", "--forever"]),
- simple_fixed_str=(fmt.as_fixed, [("--always-here", "--forever")], "something", ["--always-here", "--forever"]),
+ simple_boolean__true=(cmd_runner_fmt.as_bool, ("--superflag",), True, ["--superflag"]),
+ simple_boolean__false=(cmd_runner_fmt.as_bool, ("--superflag",), False, []),
+ simple_boolean__none=(cmd_runner_fmt.as_bool, ("--superflag",), None, []),
+ simple_boolean_both__true=(cmd_runner_fmt.as_bool, ("--superflag", "--falseflag"), True, ["--superflag"]),
+ simple_boolean_both__false=(cmd_runner_fmt.as_bool, ("--superflag", "--falseflag"), False, ["--falseflag"]),
+ simple_boolean_both__none=(cmd_runner_fmt.as_bool, ("--superflag", "--falseflag"), None, ["--falseflag"]),
+ simple_boolean_both__none_ig=(cmd_runner_fmt.as_bool, ("--superflag", "--falseflag", True), None, []),
+ simple_boolean_not__true=(cmd_runner_fmt.as_bool_not, ("--superflag",), True, []),
+ simple_boolean_not__false=(cmd_runner_fmt.as_bool_not, ("--superflag",), False, ["--superflag"]),
+ simple_boolean_not__none=(cmd_runner_fmt.as_bool_not, ("--superflag",), None, ["--superflag"]),
+ simple_optval__str=(cmd_runner_fmt.as_optval, ("-t",), "potatoes", ["-tpotatoes"]),
+ simple_optval__int=(cmd_runner_fmt.as_optval, ("-t",), 42, ["-t42"]),
+ simple_opt_val__str=(cmd_runner_fmt.as_opt_val, ("-t",), "potatoes", ["-t", "potatoes"]),
+ simple_opt_val__int=(cmd_runner_fmt.as_opt_val, ("-t",), 42, ["-t", "42"]),
+ simple_opt_eq_val__str=(cmd_runner_fmt.as_opt_eq_val, ("--food",), "potatoes", ["--food=potatoes"]),
+ simple_opt_eq_val__int=(cmd_runner_fmt.as_opt_eq_val, ("--answer",), 42, ["--answer=42"]),
+ simple_list_potato=(cmd_runner_fmt.as_list, (), "literal_potato", ["literal_potato"]),
+ simple_list_42=(cmd_runner_fmt.as_list, (), 42, ["42"]),
+ simple_map=(cmd_runner_fmt.as_map, ({'a': 1, 'b': 2, 'c': 3},), 'b', ["2"]),
+ simple_default_type__list=(cmd_runner_fmt.as_default_type, ("list",), [1, 2, 3, 5, 8], ["--1", "--2", "--3", "--5", "--8"]),
+ simple_default_type__bool_true=(cmd_runner_fmt.as_default_type, ("bool", "what"), True, ["--what"]),
+ simple_default_type__bool_false=(cmd_runner_fmt.as_default_type, ("bool", "what"), False, []),
+ simple_default_type__potato=(cmd_runner_fmt.as_default_type, ("any-other-type", "potato"), "42", ["--potato", "42"]),
+ simple_fixed_true=(cmd_runner_fmt.as_fixed, [("--always-here", "--forever")], True, ["--always-here", "--forever"]),
+ simple_fixed_false=(cmd_runner_fmt.as_fixed, [("--always-here", "--forever")], False, ["--always-here", "--forever"]),
+ simple_fixed_none=(cmd_runner_fmt.as_fixed, [("--always-here", "--forever")], None, ["--always-here", "--forever"]),
+ simple_fixed_str=(cmd_runner_fmt.as_fixed, [("--always-here", "--forever")], "something", ["--always-here", "--forever"]),
)
if tuple(version_info) >= (3, 1):
from collections import OrderedDict
# needs OrderedDict to provide a consistent key order
TC_FORMATS["simple_default_type__dict"] = ( # type: ignore
- fmt.as_default_type,
+ cmd_runner_fmt.as_default_type,
("dict",),
OrderedDict((('a', 1), ('b', 2))),
["--a=1", "--b=2"]
@@ -76,11 +76,11 @@ TC_RUNNER = dict(
# param1=dict(
# type="int",
# value=11,
- # fmt_func=fmt.as_opt_eq_val,
+ # fmt_func=cmd_runner_fmt.as_opt_eq_val,
# fmt_arg="--answer",
# ),
# param2=dict(
- # fmt_func=fmt.as_bool,
+ # fmt_func=cmd_runner_fmt.as_bool,
# fmt_arg="--bb-here",
# )
# ),
@@ -119,8 +119,8 @@ TC_RUNNER = dict(
aa_bb=(
dict(
args_bundle=dict(
- aa=dict(type="int", value=11, fmt_func=fmt.as_opt_eq_val, fmt_arg="--answer"),
- bb=dict(fmt_func=fmt.as_bool, fmt_arg="--bb-here"),
+ aa=dict(type="int", value=11, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"),
+ bb=dict(fmt_func=cmd_runner_fmt.as_bool, fmt_arg="--bb-here"),
),
runner_init_args=dict(),
runner_ctx_args=dict(args_order=['aa', 'bb']),
@@ -137,8 +137,8 @@ TC_RUNNER = dict(
aa_bb_default_order=(
dict(
args_bundle=dict(
- aa=dict(type="int", value=11, fmt_func=fmt.as_opt_eq_val, fmt_arg="--answer"),
- bb=dict(fmt_func=fmt.as_bool, fmt_arg="--bb-here"),
+ aa=dict(type="int", value=11, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"),
+ bb=dict(fmt_func=cmd_runner_fmt.as_bool, fmt_arg="--bb-here"),
),
runner_init_args=dict(default_args_order=['bb', 'aa']),
runner_ctx_args=dict(),
@@ -155,8 +155,8 @@ TC_RUNNER = dict(
aa_bb_default_order_args_order=(
dict(
args_bundle=dict(
- aa=dict(type="int", value=11, fmt_func=fmt.as_opt_eq_val, fmt_arg="--answer"),
- bb=dict(fmt_func=fmt.as_bool, fmt_arg="--bb-here"),
+ aa=dict(type="int", value=11, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"),
+ bb=dict(fmt_func=cmd_runner_fmt.as_bool, fmt_arg="--bb-here"),
),
runner_init_args=dict(default_args_order=['bb', 'aa']),
runner_ctx_args=dict(args_order=['aa', 'bb']),
@@ -173,8 +173,8 @@ TC_RUNNER = dict(
aa_bb_dup_in_args_order=(
dict(
args_bundle=dict(
- aa=dict(type="int", value=11, fmt_func=fmt.as_opt_eq_val, fmt_arg="--answer"),
- bb=dict(fmt_func=fmt.as_bool, fmt_arg="--bb-here"),
+ aa=dict(type="int", value=11, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"),
+ bb=dict(fmt_func=cmd_runner_fmt.as_bool, fmt_arg="--bb-here"),
),
runner_init_args=dict(),
runner_ctx_args=dict(args_order=['aa', 'bb', 'aa']),
@@ -189,8 +189,8 @@ TC_RUNNER = dict(
aa_bb_process_output=(
dict(
args_bundle=dict(
- aa=dict(type="int", value=11, fmt_func=fmt.as_opt_eq_val, fmt_arg="--answer"),
- bb=dict(fmt_func=fmt.as_bool, fmt_arg="--bb-here"),
+ aa=dict(type="int", value=11, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"),
+ bb=dict(fmt_func=cmd_runner_fmt.as_bool, fmt_arg="--bb-here"),
),
runner_init_args=dict(default_args_order=['bb', 'aa']),
runner_ctx_args=dict(
@@ -209,8 +209,8 @@ TC_RUNNER = dict(
aa_bb_ignore_none_with_none=(
dict(
args_bundle=dict(
- aa=dict(type="int", value=49, fmt_func=fmt.as_opt_eq_val, fmt_arg="--answer"),
- bb=dict(fmt_func=fmt.as_bool, fmt_arg="--bb-here"),
+ aa=dict(type="int", value=49, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"),
+ bb=dict(fmt_func=cmd_runner_fmt.as_bool, fmt_arg="--bb-here"),
),
runner_init_args=dict(default_args_order=['bb', 'aa']),
runner_ctx_args=dict(
@@ -228,8 +228,8 @@ TC_RUNNER = dict(
aa_bb_ignore_not_none_with_none=(
dict(
args_bundle=dict(
- aa=dict(type="int", value=49, fmt_func=fmt.as_opt_eq_val, fmt_arg="--answer"),
- bb=dict(fmt_func=fmt.as_bool, fmt_arg="--bb-here"),
+ aa=dict(type="int", value=49, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"),
+ bb=dict(fmt_func=cmd_runner_fmt.as_bool, fmt_arg="--bb-here"),
),
runner_init_args=dict(default_args_order=['bb', 'aa']),
runner_ctx_args=dict(
@@ -247,8 +247,8 @@ TC_RUNNER = dict(
aa_bb_fixed=(
dict(
args_bundle=dict(
- aa=dict(type="int", value=11, fmt_func=fmt.as_opt_eq_val, fmt_arg="--answer"),
- bb=dict(fmt_func=fmt.as_fixed, fmt_arg=["fixed", "args"]),
+ aa=dict(type="int", value=11, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"),
+ bb=dict(fmt_func=cmd_runner_fmt.as_fixed, fmt_arg=["fixed", "args"]),
),
runner_init_args=dict(),
runner_ctx_args=dict(args_order=['aa', 'bb']),
@@ -265,8 +265,8 @@ TC_RUNNER = dict(
aa_bb_map=(
dict(
args_bundle=dict(
- aa=dict(type="int", value=11, fmt_func=fmt.as_opt_eq_val, fmt_arg="--answer"),
- bb=dict(fmt_func=fmt.as_map, fmt_arg={"v1": 111, "v2": 222}),
+ aa=dict(type="int", value=11, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"),
+ bb=dict(fmt_func=cmd_runner_fmt.as_map, fmt_arg={"v1": 111, "v2": 222}),
),
runner_init_args=dict(),
runner_ctx_args=dict(args_order=['aa', 'bb']),
@@ -283,8 +283,8 @@ TC_RUNNER = dict(
aa_bb_map_default=(
dict(
args_bundle=dict(
- aa=dict(type="int", value=11, fmt_func=fmt.as_opt_eq_val, fmt_arg="--answer"),
- bb=dict(fmt_func=fmt.as_map, fmt_arg={"v1": 111, "v2": 222}),
+ aa=dict(type="int", value=11, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"),
+ bb=dict(fmt_func=cmd_runner_fmt.as_map, fmt_arg={"v1": 111, "v2": 222}),
),
runner_init_args=dict(),
runner_ctx_args=dict(args_order=['aa', 'bb']),
diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_module_helper.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_module_helper.py
index 3d8a4b654..b2cd58690 100644
--- a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_module_helper.py
+++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_module_helper.py
@@ -10,88 +10,10 @@ __metaclass__ = type
import pytest
from ansible_collections.community.general.plugins.module_utils.module_helper import (
- ArgFormat, DependencyCtxMgr, VarMeta, VarDict, cause_changes
+ DependencyCtxMgr, VarMeta, VarDict, cause_changes
)
-def single_lambda_2star(x, y, z):
- return ["piggies=[{0},{1},{2}]".format(x, y, z)]
-
-
-ARG_FORMATS = dict(
- simple_boolean_true=("--superflag", ArgFormat.BOOLEAN, 0,
- True, ["--superflag"]),
- simple_boolean_false=("--superflag", ArgFormat.BOOLEAN, 0,
- False, []),
- simple_boolean_none=("--superflag", ArgFormat.BOOLEAN, 0,
- None, []),
- simple_boolean_not_true=("--superflag", ArgFormat.BOOLEAN_NOT, 0,
- True, []),
- simple_boolean_not_false=("--superflag", ArgFormat.BOOLEAN_NOT, 0,
- False, ["--superflag"]),
- simple_boolean_not_none=("--superflag", ArgFormat.BOOLEAN_NOT, 0,
- None, ["--superflag"]),
- single_printf=("--param=%s", ArgFormat.PRINTF, 0,
- "potatoes", ["--param=potatoes"]),
- single_printf_no_substitution=("--param", ArgFormat.PRINTF, 0,
- "potatoes", ["--param"]),
- single_printf_none=("--param=%s", ArgFormat.PRINTF, 0,
- None, []),
- multiple_printf=(["--param", "free-%s"], ArgFormat.PRINTF, 0,
- "potatoes", ["--param", "free-potatoes"]),
- single_format=("--param={0}", ArgFormat.FORMAT, 0,
- "potatoes", ["--param=potatoes"]),
- single_format_none=("--param={0}", ArgFormat.FORMAT, 0,
- None, []),
- single_format_no_substitution=("--param", ArgFormat.FORMAT, 0,
- "potatoes", ["--param"]),
- multiple_format=(["--param", "free-{0}"], ArgFormat.FORMAT, 0,
- "potatoes", ["--param", "free-potatoes"]),
- multiple_format_none=(["--param", "free-{0}"], ArgFormat.FORMAT, 0,
- None, []),
- single_lambda_0star=((lambda v: ["piggies=[{0},{1},{2}]".format(v[0], v[1], v[2])]), None, 0,
- ['a', 'b', 'c'], ["piggies=[a,b,c]"]),
- single_lambda_0star_none=((lambda v: ["piggies=[{0},{1},{2}]".format(v[0], v[1], v[2])]), None, 0,
- None, []),
- single_lambda_1star=((lambda a, b, c: ["piggies=[{0},{1},{2}]".format(a, b, c)]), None, 1,
- ['a', 'b', 'c'], ["piggies=[a,b,c]"]),
- single_lambda_1star_none=((lambda a, b, c: ["piggies=[{0},{1},{2}]".format(a, b, c)]), None, 1,
- None, []),
- single_lambda_2star=(single_lambda_2star, None, 2,
- dict(z='c', x='a', y='b'), ["piggies=[a,b,c]"]),
- single_lambda_2star_none=(single_lambda_2star, None, 2,
- None, []),
-)
-ARG_FORMATS_IDS = sorted(ARG_FORMATS.keys())
-
-
-@pytest.mark.parametrize('fmt, style, stars, value, expected',
- (ARG_FORMATS[tc] for tc in ARG_FORMATS_IDS),
- ids=ARG_FORMATS_IDS)
-def test_arg_format(fmt, style, stars, value, expected):
- af = ArgFormat('name', fmt, style, stars)
- actual = af.to_text(value)
- print("formatted string = {0}".format(actual))
- assert actual == expected, "actual = {0}".format(actual)
-
-
-ARG_FORMATS_FAIL = dict(
- int_fmt=(3, None, 0, "", [""]),
- bool_fmt=(True, None, 0, "", [""]),
-)
-ARG_FORMATS_FAIL_IDS = sorted(ARG_FORMATS_FAIL.keys())
-
-
-@pytest.mark.parametrize('fmt, style, stars, value, expected',
- (ARG_FORMATS_FAIL[tc] for tc in ARG_FORMATS_FAIL_IDS),
- ids=ARG_FORMATS_FAIL_IDS)
-def test_arg_format_fail(fmt, style, stars, value, expected):
- with pytest.raises(TypeError):
- af = ArgFormat('name', fmt, style, stars)
- actual = af.to_text(value)
- print("formatted string = {0}".format(actual))
-
-
def test_dependency_ctxmgr():
ctx = DependencyCtxMgr("POTATOES", "Potatoes must be installed")
with ctx:
diff --git a/ansible_collections/community/general/tests/unit/plugins/module_utils/test_vardict.py b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_vardict.py
new file mode 100644
index 000000000..01d710b44
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/module_utils/test_vardict.py
@@ -0,0 +1,134 @@
+# -*- coding: utf-8 -*-
+# (c) 2023, Alexei Znamensky <russoz@gmail.com>
+# Copyright (c) 2023 Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+from ansible_collections.community.general.plugins.module_utils.vardict import VarDict
+
+
+def test_var_simple():
+ vd = VarDict()
+ vd["a"] = 123
+
+ var = vd._var("a")
+ assert var.output is True
+ assert var.diff is False
+ assert var.change is False
+ assert var.fact is False
+ assert var.initial_value == 123
+ assert var.value == 123
+
+ vd.a = 456
+ assert var.output is True
+ assert var.diff is False
+ assert var.change is False
+ assert var.fact is False
+ assert var.initial_value == 123
+ assert var.value == 456
+
+
+def test_var_diff_scalar():
+ vd = VarDict()
+ vd.set("aa", 123, diff=True)
+
+ var = vd._var("aa")
+ assert var.output is True
+ assert var.diff is True
+ assert var.change is True
+ assert var.fact is False
+ assert var.initial_value == 123
+ assert var.value == 123
+ assert vd.diff() is None
+
+ vd.aa = 456
+ assert var.output is True
+ assert var.diff is True
+ assert var.change is True
+ assert var.fact is False
+ assert var.initial_value == 123
+ assert var.value == 456
+ assert vd.diff() == {"before": {"aa": 123}, "after": {"aa": 456}}, "actual={0}".format(vd.diff())
+
+
+def test_var_diff_dict():
+ val_before = dict(x=0, y=10, z=10)
+ val_after = dict(x=0, y=30, z=10)
+
+ vd = VarDict()
+ vd.set("dd", val_before, diff=True)
+
+ var = vd._var("dd")
+ assert var.output is True
+ assert var.diff is True
+ assert var.change is True
+ assert var.fact is False
+ assert var.initial_value == val_before
+ assert var.value == val_before
+ assert vd.diff() is None
+
+ vd.dd = val_after
+ assert var.output is True
+ assert var.diff is True
+ assert var.change is True
+ assert var.fact is False
+ assert var.initial_value == val_before
+ assert var.value == val_after
+ assert vd.diff() == {"before": {"dd": val_before}, "after": {"dd": val_after}}, "actual={0}".format(vd.diff())
+
+ vd.set("aa", 123, diff=True)
+ vd.aa = 456
+ assert vd.diff() == {"before": {"aa": 123, "dd": val_before}, "after": {"aa": 456, "dd": val_after}}, "actual={0}".format(vd.diff())
+
+
+def test_vardict_set_meta():
+ vd = VarDict()
+ vd["jj"] = 123
+
+ var = vd._var("jj")
+ assert var.output is True
+ assert var.diff is False
+ assert var.change is False
+ assert var.fact is False
+ assert var.initial_value == 123
+ assert var.value == 123
+
+ vd.set_meta("jj", diff=True)
+ assert var.diff is True
+ assert var.change is True
+
+ vd.set_meta("jj", diff=False)
+ assert var.diff is False
+ assert var.change is False
+
+ vd.set_meta("jj", change=False)
+ vd.set_meta("jj", diff=True)
+ assert var.diff is True
+ assert var.change is False
+
+
+def test_vardict_change():
+ vd = VarDict()
+ vd.set("xx", 123, change=True)
+ vd.set("yy", 456, change=True)
+ vd.set("zz", 789, change=True)
+
+ vd.xx = 123
+ vd.yy = 456
+ assert vd.has_changed is False
+ vd.xx = 12345
+ assert vd.has_changed is True
+
+
+def test_vardict_dict():
+ vd = VarDict()
+ vd.set("xx", 123)
+ vd.set("yy", 456)
+ vd.set("zz", 789)
+
+ assert vd.as_dict() == {"xx": 123, "yy": 456, "zz": 789}
+ assert vd.get_meta("xx") == {"output": True, "change": False, "diff": False, "fact": False, "verbosity": 0}
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/gitlab.py b/ansible_collections/community/general/tests/unit/plugins/modules/gitlab.py
index c64d99fff..7a52dc355 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/gitlab.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/gitlab.py
@@ -284,6 +284,36 @@ def resp_delete_group(url, request):
return response(204, content, headers, None, 5, request)
+@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1/access_tokens", method="get")
+def resp_list_group_access_tokens(url, request):
+ headers = {'content-type': 'application/json'}
+ content = ('[{"user_id" : 1, "scopes" : ["api"], "name" : "token1", "expires_at" : "2021-01-31",'
+ '"id" : 1, "active" : false, "created_at" : "2021-01-20T22:11:48.151Z", "revoked" : true,'
+ '"access_level": 40},{"user_id" : 2, "scopes" : ["api"], "name" : "token2", "expires_at" : "2021-02-31",'
+ '"id" : 2, "active" : true, "created_at" : "2021-02-20T22:11:48.151Z", "revoked" : false,'
+ '"access_level": 40}]')
+ content = content.encode("utf-8")
+ return response(200, content, headers, None, 5, request)
+
+
+@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1/access_tokens", method="post")
+def resp_create_group_access_tokens(url, request):
+ headers = {'content-type': 'application/json'}
+ content = ('{"user_id" : 1, "scopes" : ["api"], "name" : "token1", "expires_at" : "2021-01-31",'
+ '"id" : 1, "active" : false, "created_at" : "2021-01-20T22:11:48.151Z", "revoked" : true,'
+ '"access_level": 40, "token": "Der423FErcdv35qEEWc"}')
+ content = content.encode("utf-8")
+ return response(201, content, headers, None, 5, request)
+
+
+@urlmatch(scheme="http", netloc="localhost", path="/api/v4/groups/1/access_tokens/1", method="delete")
+def resp_revoke_group_access_tokens(url, request):
+ headers = {'content-type': 'application/json'}
+ content = ('')
+ content = content.encode("utf-8")
+ return response(204, content, headers, None, 5, request)
+
+
'''
GROUP MEMBER API
'''
@@ -534,6 +564,36 @@ def resp_delete_protected_branch(url, request):
return response(204, content, headers, None, 5, request)
+@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1/access_tokens", method="get")
+def resp_list_project_access_tokens(url, request):
+ headers = {'content-type': 'application/json'}
+ content = ('[{"user_id" : 1, "scopes" : ["api"], "name" : "token1", "expires_at" : "2021-01-31",'
+ '"id" : 1, "active" : false, "created_at" : "2021-01-20T22:11:48.151Z", "revoked" : true,'
+ '"access_level": 40},{"user_id" : 2, "scopes" : ["api"], "name" : "token2", "expires_at" : "2021-02-31",'
+ '"id" : 2, "active" : true, "created_at" : "2021-02-20T22:11:48.151Z", "revoked" : false,'
+ '"access_level": 40}]')
+ content = content.encode("utf-8")
+ return response(200, content, headers, None, 5, request)
+
+
+@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1/access_tokens", method="post")
+def resp_create_project_access_tokens(url, request):
+ headers = {'content-type': 'application/json'}
+ content = ('{"user_id" : 1, "scopes" : ["api"], "name" : "token1", "expires_at" : "2021-01-31",'
+ '"id" : 1, "active" : false, "created_at" : "2021-01-20T22:11:48.151Z", "revoked" : true,'
+ '"access_level": 40, "token": "Der423FErcdv35qEEWc"}')
+ content = content.encode("utf-8")
+ return response(201, content, headers, None, 5, request)
+
+
+@urlmatch(scheme="http", netloc="localhost", path="/api/v4/projects/1/access_tokens/1", method="delete")
+def resp_revoke_project_access_tokens(url, request):
+ headers = {'content-type': 'application/json'}
+ content = ('')
+ content = content.encode("utf-8")
+ return response(204, content, headers, None, 5, request)
+
+
'''
HOOK API
'''
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/helper.py b/ansible_collections/community/general/tests/unit/plugins/modules/helper.py
new file mode 100644
index 000000000..a7322bf4d
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/helper.py
@@ -0,0 +1,181 @@
+# Copyright (c) Ansible project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import sys
+import json
+from collections import namedtuple
+from itertools import chain, repeat
+
+import pytest
+import yaml
+
+
+ModuleTestCase = namedtuple("ModuleTestCase", ["id", "input", "output", "run_command_calls", "flags"])
+RunCmdCall = namedtuple("RunCmdCall", ["command", "environ", "rc", "out", "err"])
+
+
+class _BaseContext(object):
+ def __init__(self, helper, testcase, mocker, capfd):
+ self.helper = helper
+ self.testcase = testcase
+ self.mocker = mocker
+ self.capfd = capfd
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ return False
+
+ def _run(self):
+ with pytest.raises(SystemExit):
+ self.helper.module_main()
+
+ out, err = self.capfd.readouterr()
+ results = json.loads(out)
+
+ self.check_results(results)
+
+ def test_flags(self, flag=None):
+ flags = self.testcase.flags
+ if flag:
+ flags = flags.get(flag)
+ return flags
+
+ def run(self):
+ func = self._run
+
+ test_flags = self.test_flags()
+ if test_flags.get("skip"):
+ pytest.skip()
+ if test_flags.get("xfail"):
+ pytest.xfail()
+
+ func()
+
+ def check_results(self, results):
+ print("testcase =\n%s" % str(self.testcase))
+ print("results =\n%s" % results)
+ if 'exception' in results:
+ print("exception = \n%s" % results["exception"])
+
+ for test_result in self.testcase.output:
+ assert results[test_result] == self.testcase.output[test_result], \
+ "'{0}': '{1}' != '{2}'".format(test_result, results[test_result], self.testcase.output[test_result])
+
+
+class _RunCmdContext(_BaseContext):
+ def __init__(self, *args, **kwargs):
+ super(_RunCmdContext, self).__init__(*args, **kwargs)
+ self.run_cmd_calls = self.testcase.run_command_calls
+ self.mock_run_cmd = self._make_mock_run_cmd()
+
+ def _make_mock_run_cmd(self):
+ call_results = [(x.rc, x.out, x.err) for x in self.run_cmd_calls]
+ error_call_results = (123,
+ "OUT: testcase has not enough run_command calls",
+ "ERR: testcase has not enough run_command calls")
+ mock_run_command = self.mocker.patch('ansible.module_utils.basic.AnsibleModule.run_command',
+ side_effect=chain(call_results, repeat(error_call_results)))
+ return mock_run_command
+
+ def check_results(self, results):
+ super(_RunCmdContext, self).check_results(results)
+ call_args_list = [(item[0][0], item[1]) for item in self.mock_run_cmd.call_args_list]
+ expected_call_args_list = [(item.command, item.environ) for item in self.run_cmd_calls]
+ print("call args list =\n%s" % call_args_list)
+ print("expected args list =\n%s" % expected_call_args_list)
+
+ assert self.mock_run_cmd.call_count == len(self.run_cmd_calls), "{0} != {1}".format(self.mock_run_cmd.call_count, len(self.run_cmd_calls))
+ if self.mock_run_cmd.call_count:
+ assert call_args_list == expected_call_args_list
+
+
+class Helper(object):
+ @staticmethod
+ def from_list(module_main, list_):
+ helper = Helper(module_main, test_cases=list_)
+ return helper
+
+ @staticmethod
+ def from_file(module_main, filename):
+ with open(filename, "r") as test_cases:
+ helper = Helper(module_main, test_cases=test_cases)
+ return helper
+
+ @staticmethod
+ def from_module(module, test_module_name):
+ basename = module.__name__.split(".")[-1]
+ test_spec = "tests/unit/plugins/modules/test_{0}.yaml".format(basename)
+ helper = Helper.from_file(module.main, test_spec)
+
+ setattr(sys.modules[test_module_name], "patch_bin", helper.cmd_fixture)
+ setattr(sys.modules[test_module_name], "test_module", helper.test_module)
+
+ def __init__(self, module_main, test_cases):
+ self.module_main = module_main
+ self._test_cases = test_cases
+ if isinstance(test_cases, (list, tuple)):
+ self.testcases = test_cases
+ else:
+ self.testcases = self._make_test_cases()
+
+ @property
+ def cmd_fixture(self):
+ @pytest.fixture
+ def patch_bin(mocker):
+ def mockie(self, path, *args, **kwargs):
+ return "/testbin/{0}".format(path)
+ mocker.patch('ansible.module_utils.basic.AnsibleModule.get_bin_path', mockie)
+
+ return patch_bin
+
+ def _make_test_cases(self):
+ test_cases = yaml.safe_load(self._test_cases)
+
+ results = []
+ for tc in test_cases:
+ for tc_param in ["input", "output", "flags"]:
+ if not tc.get(tc_param):
+ tc[tc_param] = {}
+ if tc.get("run_command_calls"):
+ tc["run_command_calls"] = [RunCmdCall(**r) for r in tc["run_command_calls"]]
+ else:
+ tc["run_command_calls"] = []
+ results.append(ModuleTestCase(**tc))
+
+ return results
+
+ @property
+ def testcases_params(self):
+ return [[x.input, x] for x in self.testcases]
+
+ @property
+ def testcases_ids(self):
+ return [item.id for item in self.testcases]
+
+ def __call__(self, *args, **kwargs):
+ return _RunCmdContext(self, *args, **kwargs)
+
+ @property
+ def test_module(self):
+ helper = self
+
+ @pytest.mark.parametrize('patch_ansible_module, testcase',
+ helper.testcases_params, ids=helper.testcases_ids,
+ indirect=['patch_ansible_module'])
+ @pytest.mark.usefixtures('patch_ansible_module')
+ def _test_module(mocker, capfd, patch_bin, testcase):
+ """
+ Run unit tests for test cases listed in TEST_CASES
+ """
+
+ with helper(testcase, mocker, capfd) as testcase_context:
+ testcase_context.run()
+
+ return _test_module
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_cpanm.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_cpanm.py
index 5367a1fab..4eecf000f 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_cpanm.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_cpanm.py
@@ -12,282 +12,9 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-import json
from ansible_collections.community.general.plugins.modules import cpanm
+from .helper import Helper
-import pytest
-TESTED_MODULE = cpanm.__name__
-
-
-@pytest.fixture
-def patch_cpanm(mocker):
- """
- Function used for mocking some parts of redhat_subscription module
- """
- mocker.patch('ansible.module_utils.basic.AnsibleModule.get_bin_path',
- return_value='/testbin/cpanm')
-
-
-TEST_CASES = [
- [
- {'name': 'Dancer'},
- {
- 'id': 'install_dancer_compatibility',
- 'run_command.calls': [
- (
- ['/testbin/cpanm', '-le', 'use Dancer;'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- (2, '', 'error, not installed',), # output rc, out, err
- ),
- (
- ['/testbin/cpanm', 'Dancer'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '', '',), # output rc, out, err
- ),
- ],
- 'changed': True,
- }
- ],
- [
- {'name': 'Dancer'},
- {
- 'id': 'install_dancer_already_installed_compatibility',
- 'run_command.calls': [
- (
- ['/testbin/cpanm', '-le', 'use Dancer;'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- (0, '', '',), # output rc, out, err
- ),
- ],
- 'changed': False,
- }
- ],
- [
- {'name': 'Dancer', 'mode': 'new'},
- {
- 'id': 'install_dancer',
- 'run_command.calls': [(
- ['/testbin/cpanm', 'Dancer'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '', '',), # output rc, out, err
- )],
- 'changed': True,
- }
- ],
- [
- {'name': 'MIYAGAWA/Plack-0.99_05.tar.gz'},
- {
- 'id': 'install_distribution_file_compatibility',
- 'run_command.calls': [(
- ['/testbin/cpanm', 'MIYAGAWA/Plack-0.99_05.tar.gz'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '', '',), # output rc, out, err
- )],
- 'changed': True,
- }
- ],
- [
- {'name': 'MIYAGAWA/Plack-0.99_05.tar.gz', 'mode': 'new'},
- {
- 'id': 'install_distribution_file',
- 'run_command.calls': [(
- ['/testbin/cpanm', 'MIYAGAWA/Plack-0.99_05.tar.gz'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '', '',), # output rc, out, err
- )],
- 'changed': True,
- }
- ],
- [
- {'name': 'Dancer', 'locallib': '/srv/webapps/my_app/extlib', 'mode': 'new'},
- {
- 'id': 'install_into_locallib',
- 'run_command.calls': [(
- ['/testbin/cpanm', '--local-lib', '/srv/webapps/my_app/extlib', 'Dancer'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '', '',), # output rc, out, err
- )],
- 'changed': True,
- }
- ],
- [
- {'from_path': '/srv/webapps/my_app/src/', 'mode': 'new'},
- {
- 'id': 'install_from_local_directory',
- 'run_command.calls': [(
- ['/testbin/cpanm', '/srv/webapps/my_app/src/'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '', '',), # output rc, out, err
- )],
- 'changed': True,
- }
- ],
- [
- {'name': 'Dancer', 'locallib': '/srv/webapps/my_app/extlib', 'notest': True, 'mode': 'new'},
- {
- 'id': 'install_into_locallib_no_unit_testing',
- 'run_command.calls': [(
- ['/testbin/cpanm', '--notest', '--local-lib', '/srv/webapps/my_app/extlib', 'Dancer'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '', '',), # output rc, out, err
- )],
- 'changed': True,
- }
- ],
- [
- {'name': 'Dancer', 'mirror': 'http://cpan.cpantesters.org/', 'mode': 'new'},
- {
- 'id': 'install_from_mirror',
- 'run_command.calls': [(
- ['/testbin/cpanm', '--mirror', 'http://cpan.cpantesters.org/', 'Dancer'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '', '',), # output rc, out, err
- )],
- 'changed': True,
- }
- ],
- [
- {'name': 'Dancer', 'system_lib': True, 'mode': 'new'},
- {
- 'id': 'install_into_system_lib',
- 'run_command.calls': [],
- 'changed': False,
- 'failed': True,
- }
- ],
- [
- {'name': 'Dancer', 'version': '1.0', 'mode': 'new'},
- {
- 'id': 'install_minversion_implicit',
- 'run_command.calls': [(
- ['/testbin/cpanm', 'Dancer~1.0'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '', '',), # output rc, out, err
- )],
- 'changed': True,
- }
- ],
- [
- {'name': 'Dancer', 'version': '~1.5', 'mode': 'new'},
- {
- 'id': 'install_minversion_explicit',
- 'run_command.calls': [(
- ['/testbin/cpanm', 'Dancer~1.5'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '', '',), # output rc, out, err
- )],
- 'changed': True,
- }
- ],
- [
- {'name': 'Dancer', 'version': '@1.7', 'mode': 'new'},
- {
- 'id': 'install_specific_version',
- 'run_command.calls': [(
- ['/testbin/cpanm', 'Dancer@1.7'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '', '',), # output rc, out, err
- )],
- 'changed': True,
- 'failed': False,
- }
- ],
- [
- {'name': 'MIYAGAWA/Plack-0.99_05.tar.gz', 'version': '@1.7', 'mode': 'new'},
- {
- 'id': 'install_specific_version_from_file_error',
- 'run_command.calls': [],
- 'changed': False,
- 'failed': True,
- 'msg': "parameter 'version' must not be used when installing from a file",
- }
- ],
- [
- {'from_path': '~/', 'version': '@1.7', 'mode': 'new'},
- {
- 'id': 'install_specific_version_from_directory_error',
- 'run_command.calls': [],
- 'changed': False,
- 'failed': True,
- 'msg': "parameter 'version' must not be used when installing from a directory",
- }
- ],
- [
- {'name': 'git://github.com/plack/Plack.git', 'version': '@1.7', 'mode': 'new'},
- {
- 'id': 'install_specific_version_from_git_url_explicit',
- 'run_command.calls': [(
- ['/testbin/cpanm', 'git://github.com/plack/Plack.git@1.7'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '', '',), # output rc, out, err
- )],
- 'changed': True,
- 'failed': False,
- }
- ],
- [
- {'name': 'git://github.com/plack/Plack.git', 'version': '2.5', 'mode': 'new'},
- {
- 'id': 'install_specific_version_from_git_url_implicit',
- 'run_command.calls': [(
- ['/testbin/cpanm', 'git://github.com/plack/Plack.git@2.5'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '', '',), # output rc, out, err
- )],
- 'changed': True,
- 'failed': False,
- }
- ],
- [
- {'name': 'git://github.com/plack/Plack.git', 'version': '~2.5', 'mode': 'new'},
- {
- 'id': 'install_version_operator_from_git_url_error',
- 'run_command.calls': [],
- 'changed': False,
- 'failed': True,
- 'msg': "operator '~' not allowed in version parameter when installing from git repository",
- }
- ],
-]
-TEST_CASES_IDS = [item[1]['id'] for item in TEST_CASES]
-
-
-@pytest.mark.parametrize('patch_ansible_module, testcase',
- TEST_CASES,
- ids=TEST_CASES_IDS,
- indirect=['patch_ansible_module'])
-@pytest.mark.usefixtures('patch_ansible_module')
-def test_cpanm(mocker, capfd, patch_cpanm, testcase):
- """
- Run unit tests for test cases listen in TEST_CASES
- """
-
- # Mock function used for running commands first
- call_results = [item[2] for item in testcase['run_command.calls']]
- mock_run_command = mocker.patch(
- 'ansible_collections.community.general.plugins.module_utils.module_helper.AnsibleModule.run_command',
- side_effect=call_results)
-
- # Try to run test case
- with pytest.raises(SystemExit):
- cpanm.main()
-
- out, err = capfd.readouterr()
- results = json.loads(out)
- print("results =\n%s" % results)
-
- assert mock_run_command.call_count == len(testcase['run_command.calls'])
- if mock_run_command.call_count:
- call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list]
- expected_call_args_list = [(item[0], item[1]) for item in testcase['run_command.calls']]
- print("call args list =\n%s" % call_args_list)
- print("expected args list =\n%s" % expected_call_args_list)
- assert call_args_list == expected_call_args_list
-
- assert results.get('changed', False) == testcase['changed']
- if 'failed' in testcase:
- assert results.get('failed', False) == testcase['failed']
- if 'msg' in testcase:
- assert results.get('msg', '') == testcase['msg']
+Helper.from_module(cpanm, __name__)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_cpanm.yaml b/ansible_collections/community/general/tests/unit/plugins/modules/test_cpanm.yaml
new file mode 100644
index 000000000..3ed718d48
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_cpanm.yaml
@@ -0,0 +1,220 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Alexei Znamensky (russoz@gmail.com)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+---
+- id: install_dancer_compatibility
+ input:
+ name: Dancer
+ output:
+ changed: true
+ run_command_calls:
+ - command: [/testbin/perl, -le, 'use Dancer;']
+ environ: &env-def-false {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
+ rc: 2
+ out: ""
+ err: "error, not installed"
+ - command: [/testbin/cpanm, Dancer]
+ environ: &env-def-true {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
+ rc: 0
+ out: ""
+ err: ""
+- id: install_dancer_already_installed_compatibility
+ input:
+ name: Dancer
+ output:
+ changed: false
+ run_command_calls:
+ - command: [/testbin/perl, -le, 'use Dancer;']
+ environ: *env-def-false
+ rc: 0
+ out: ""
+ err: ""
+- id: install_dancer
+ input:
+ name: Dancer
+ mode: new
+ output:
+ changed: true
+ run_command_calls:
+ - command: [/testbin/cpanm, Dancer]
+ environ: *env-def-true
+ rc: 0
+ out: ""
+ err: ""
+- id: install_distribution_file_compatibility
+ input:
+ name: MIYAGAWA/Plack-0.99_05.tar.gz
+ output:
+ changed: true
+ run_command_calls:
+ - command: [/testbin/cpanm, MIYAGAWA/Plack-0.99_05.tar.gz]
+ environ: *env-def-true
+ rc: 0
+ out: ""
+ err: ""
+- id: install_distribution_file
+ input:
+ name: MIYAGAWA/Plack-0.99_05.tar.gz
+ mode: new
+ output:
+ changed: true
+ run_command_calls:
+ - command: [/testbin/cpanm, MIYAGAWA/Plack-0.99_05.tar.gz]
+ environ: *env-def-true
+ rc: 0
+ out: ""
+ err: ""
+- id: install_into_locallib
+ input:
+ name: Dancer
+ mode: new
+ locallib: /srv/webapps/my_app/extlib
+ output:
+ changed: true
+ run_command_calls:
+ - command: [/testbin/cpanm, --local-lib, /srv/webapps/my_app/extlib, Dancer]
+ environ: *env-def-true
+ rc: 0
+ out: ""
+ err: ""
+- id: install_from_local_directory
+ input:
+ from_path: /srv/webapps/my_app/src/
+ mode: new
+ output:
+ changed: true
+ run_command_calls:
+ - command: [/testbin/cpanm, /srv/webapps/my_app/src/]
+ environ: *env-def-true
+ rc: 0
+ out: ""
+ err: ""
+- id: install_into_locallib_no_unit_testing
+ input:
+ name: Dancer
+ notest: true
+ mode: new
+ locallib: /srv/webapps/my_app/extlib
+ output:
+ changed: true
+ run_command_calls:
+ - command: [/testbin/cpanm, --notest, --local-lib, /srv/webapps/my_app/extlib, Dancer]
+ environ: *env-def-true
+ rc: 0
+ out: ""
+ err: ""
+- id: install_from_mirror
+ input:
+ name: Dancer
+ mode: new
+ mirror: "http://cpan.cpantesters.org/"
+ output:
+ changed: true
+ run_command_calls:
+ - command: [/testbin/cpanm, --mirror, "http://cpan.cpantesters.org/", Dancer]
+ environ: *env-def-true
+ rc: 0
+ out: ""
+ err: ""
+- id: install_into_system_lib
+ input:
+ name: Dancer
+ mode: new
+ system_lib: true
+ output:
+ failed: true
+ run_command_calls: []
+- id: install_minversion_implicit
+ input:
+ name: Dancer
+ mode: new
+ version: "1.0"
+ output:
+ changed: true
+ run_command_calls:
+ - command: [/testbin/cpanm, Dancer~1.0]
+ environ: *env-def-true
+ rc: 0
+ out: ""
+ err: ""
+- id: install_minversion_explicit
+ input:
+ name: Dancer
+ mode: new
+ version: "~1.5"
+ output:
+ changed: true
+ run_command_calls:
+ - command: [/testbin/cpanm, Dancer~1.5]
+ environ: *env-def-true
+ rc: 0
+ out: ""
+ err: ""
+- id: install_specific_version
+ input:
+ name: Dancer
+ mode: new
+ version: "@1.7"
+ output:
+ changed: true
+ run_command_calls:
+ - command: [/testbin/cpanm, Dancer@1.7]
+ environ: *env-def-true
+ rc: 0
+ out: ""
+ err: ""
+- id: install_specific_version_from_file_error
+ input:
+ name: MIYAGAWA/Plack-0.99_05.tar.gz
+ mode: new
+ version: "@1.7"
+ output:
+ failed: true
+ msg: parameter 'version' must not be used when installing from a file
+ run_command_calls: []
+- id: install_specific_version_from_directory_error
+ input:
+ from_path: ~/
+ mode: new
+ version: "@1.7"
+ output:
+ failed: true
+ msg: parameter 'version' must not be used when installing from a directory
+ run_command_calls: []
+- id: install_specific_version_from_git_url_explicit
+ input:
+ name: "git://github.com/plack/Plack.git"
+ mode: new
+ version: "@1.7"
+ output:
+ changed: true
+ run_command_calls:
+ - command: [/testbin/cpanm, "git://github.com/plack/Plack.git@1.7"]
+ environ: *env-def-true
+ rc: 0
+ out: ""
+ err: ""
+- id: install_specific_version_from_git_url_implicit
+ input:
+ name: "git://github.com/plack/Plack.git"
+ mode: new
+ version: "2.5"
+ output:
+ changed: true
+ run_command_calls:
+ - command: [/testbin/cpanm, "git://github.com/plack/Plack.git@2.5"]
+ environ: *env-def-true
+ rc: 0
+ out: ""
+ err: ""
+- id: install_version_operator_from_git_url_error
+ input:
+ name: "git://github.com/plack/Plack.git"
+ mode: new
+ version: "~2.5"
+ output:
+ failed: true
+ msg: operator '~' not allowed in version parameter when installing from git repository
+ run_command_calls: []
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_dnf_config_manager.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_dnf_config_manager.py
new file mode 100644
index 000000000..90bffe436
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_dnf_config_manager.py
@@ -0,0 +1,402 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2023, Andrew Hyatt <andy@hyatt.xyz>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+from __future__ import (absolute_import, division, print_function)
+
+__metaclass__ = type
+
+from ansible_collections.community.general.tests.unit.compat.mock import patch, call
+from ansible_collections.community.general.plugins.modules import dnf_config_manager as dnf_config_manager_module
+from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, \
+ ModuleTestCase, set_module_args
+
+# Return value on all-default arguments
+mock_repolist_crb_enabled = """Loaded plugins: builddep, changelog, config-manager, copr, debug, debuginfo-install
+DNF version: 4.14.0
+cachedir: /var/cache/dnf
+Last metadata expiration check: 1:20:49 ago on Fri 22 Dec 2023 06:05:13 PM UTC.
+Repo-id : appstream
+Repo-name : AlmaLinux 9 - AppStream
+Repo-status : enabled
+Repo-revision : 1703240474
+Repo-updated : Fri 22 Dec 2023 10:21:14 AM UTC
+Repo-pkgs : 5,897
+Repo-available-pkgs: 5,728
+Repo-size : 9.5 G
+Repo-mirrors : https://mirrors.almalinux.org/mirrorlist/9/appstream
+Repo-baseurl : http://mirror.cogentco.com/pub/linux/almalinux/9.3/AppStream/x86_64/os/ (9 more)
+Repo-expire : 86,400 second(s) (last: Fri 22 Dec 2023 06:05:11 PM UTC)
+Repo-filename : /etc/yum.repos.d/almalinux-appstream.repo
+
+Repo-id : appstream-debuginfo
+Repo-name : AlmaLinux 9 - AppStream - Debug
+Repo-status : disabled
+Repo-mirrors : https://mirrors.almalinux.org/mirrorlist/9/appstream-debug
+Repo-expire : 86,400 second(s) (last: unknown)
+Repo-filename : /etc/yum.repos.d/almalinux-appstream.repo
+
+Repo-id : appstream-source
+Repo-name : AlmaLinux 9 - AppStream - Source
+Repo-status : disabled
+Repo-mirrors : https://mirrors.almalinux.org/mirrorlist/9/appstream-source
+Repo-expire : 86,400 second(s) (last: unknown)
+Repo-filename : /etc/yum.repos.d/almalinux-appstream.repo
+
+Repo-id : baseos
+Repo-name : AlmaLinux 9 - BaseOS
+Repo-status : enabled
+Repo-revision : 1703240561
+Repo-updated : Fri 22 Dec 2023 10:22:41 AM UTC
+Repo-pkgs : 1,244
+Repo-available-pkgs: 1,244
+Repo-size : 1.3 G
+Repo-mirrors : https://mirrors.almalinux.org/mirrorlist/9/baseos
+Repo-baseurl : http://mirror.cogentco.com/pub/linux/almalinux/9.3/BaseOS/x86_64/os/ (9 more)
+Repo-expire : 86,400 second(s) (last: Fri 22 Dec 2023 06:05:11 PM UTC)
+Repo-filename : /etc/yum.repos.d/almalinux-baseos.repo
+
+Repo-id : baseos-debuginfo
+Repo-name : AlmaLinux 9 - BaseOS - Debug
+Repo-status : disabled
+Repo-mirrors : https://mirrors.almalinux.org/mirrorlist/9/baseos-debug
+Repo-expire : 86,400 second(s) (last: unknown)
+Repo-filename : /etc/yum.repos.d/almalinux-baseos.repo
+
+Repo-id : baseos-source
+Repo-name : AlmaLinux 9 - BaseOS - Source
+Repo-status : disabled
+Repo-mirrors : https://mirrors.almalinux.org/mirrorlist/9/baseos-source
+Repo-expire : 86,400 second(s) (last: unknown)
+Repo-filename : /etc/yum.repos.d/almalinux-baseos.repo
+
+Repo-id : copr:copr.fedorainfracloud.org:uriesk:dracut-crypt-ssh
+Repo-name : Copr repo for dracut-crypt-ssh owned by uriesk
+Repo-status : enabled
+Repo-revision : 1698291016
+Repo-updated : Thu 26 Oct 2023 03:30:16 AM UTC
+Repo-pkgs : 4
+Repo-available-pkgs: 4
+Repo-size : 102 k
+Repo-baseurl : https://download.copr.fedorainfracloud.org/results/uriesk/dracut-crypt-ssh/epel-9-x86_64/
+Repo-expire : 172,800 second(s) (last: Fri 22 Dec 2023 06:05:10 PM UTC)
+Repo-filename : /etc/yum.repos.d/_copr:copr.fedorainfracloud.org:uriesk:dracut-crypt-ssh.repo
+
+Repo-id : crb
+Repo-name : AlmaLinux 9 - CRB
+Repo-status : enabled
+Repo-revision : 1703240590
+Repo-updated : Fri 22 Dec 2023 10:23:10 AM UTC
+Repo-pkgs : 1,730
+Repo-available-pkgs: 1,727
+Repo-size : 13 G
+Repo-mirrors : https://mirrors.almalinux.org/mirrorlist/9/crb
+Repo-baseurl : http://mirror.cogentco.com/pub/linux/almalinux/9.3/CRB/x86_64/os/ (9 more)
+Repo-expire : 86,400 second(s) (last: Fri 22 Dec 2023 06:05:11 PM UTC)
+Repo-filename : /etc/yum.repos.d/almalinux-crb.repo
+
+Repo-id : rpmfusion-nonfree-updates
+Repo-name : RPM Fusion for EL 9 - Nonfree - Updates
+Repo-status : enabled
+Repo-revision : 1703248251
+Repo-tags : binary-x86_64
+Repo-updated : Fri 22 Dec 2023 12:30:53 PM UTC
+Repo-pkgs : 65
+Repo-available-pkgs: 65
+Repo-size : 944 M
+Repo-metalink : http://mirrors.rpmfusion.org/metalink?repo=nonfree-el-updates-released-9&arch=x86_64
+ Updated : Fri 22 Dec 2023 06:05:13 PM UTC
+Repo-baseurl : http://uvermont.mm.fcix.net/rpmfusion/nonfree/el/updates/9/x86_64/ (33 more)
+Repo-expire : 172,800 second(s) (last: Fri 22 Dec 2023 06:05:13 PM UTC)
+Repo-filename : /etc/yum.repos.d/rpmfusion-nonfree-updates.repo
+Total packages: 28,170
+"""
+
+mock_repolist_crb_disabled = """Loaded plugins: builddep, changelog, config-manager, copr, debug, debuginfo-install
+DNF version: 4.14.0
+cachedir: /var/cache/dnf
+Last metadata expiration check: 1:20:49 ago on Fri 22 Dec 2023 06:05:13 PM UTC.
+Repo-id : appstream
+Repo-name : AlmaLinux 9 - AppStream
+Repo-status : enabled
+Repo-revision : 1703240474
+Repo-updated : Fri 22 Dec 2023 10:21:14 AM UTC
+Repo-pkgs : 5,897
+Repo-available-pkgs: 5,728
+Repo-size : 9.5 G
+Repo-mirrors : https://mirrors.almalinux.org/mirrorlist/9/appstream
+Repo-baseurl : http://mirror.cogentco.com/pub/linux/almalinux/9.3/AppStream/x86_64/os/ (9 more)
+Repo-expire : 86,400 second(s) (last: Fri 22 Dec 2023 06:05:11 PM UTC)
+Repo-filename : /etc/yum.repos.d/almalinux-appstream.repo
+
+Repo-id : appstream-debuginfo
+Repo-name : AlmaLinux 9 - AppStream - Debug
+Repo-status : disabled
+Repo-mirrors : https://mirrors.almalinux.org/mirrorlist/9/appstream-debug
+Repo-expire : 86,400 second(s) (last: unknown)
+Repo-filename : /etc/yum.repos.d/almalinux-appstream.repo
+
+Repo-id : appstream-source
+Repo-name : AlmaLinux 9 - AppStream - Source
+Repo-status : disabled
+Repo-mirrors : https://mirrors.almalinux.org/mirrorlist/9/appstream-source
+Repo-expire : 86,400 second(s) (last: unknown)
+Repo-filename : /etc/yum.repos.d/almalinux-appstream.repo
+
+Repo-id : baseos
+Repo-name : AlmaLinux 9 - BaseOS
+Repo-status : enabled
+Repo-revision : 1703240561
+Repo-updated : Fri 22 Dec 2023 10:22:41 AM UTC
+Repo-pkgs : 1,244
+Repo-available-pkgs: 1,244
+Repo-size : 1.3 G
+Repo-mirrors : https://mirrors.almalinux.org/mirrorlist/9/baseos
+Repo-baseurl : http://mirror.cogentco.com/pub/linux/almalinux/9.3/BaseOS/x86_64/os/ (9 more)
+Repo-expire : 86,400 second(s) (last: Fri 22 Dec 2023 06:05:11 PM UTC)
+Repo-filename : /etc/yum.repos.d/almalinux-baseos.repo
+
+Repo-id : baseos-debuginfo
+Repo-name : AlmaLinux 9 - BaseOS - Debug
+Repo-status : disabled
+Repo-mirrors : https://mirrors.almalinux.org/mirrorlist/9/baseos-debug
+Repo-expire : 86,400 second(s) (last: unknown)
+Repo-filename : /etc/yum.repos.d/almalinux-baseos.repo
+
+Repo-id : baseos-source
+Repo-name : AlmaLinux 9 - BaseOS - Source
+Repo-status : disabled
+Repo-mirrors : https://mirrors.almalinux.org/mirrorlist/9/baseos-source
+Repo-expire : 86,400 second(s) (last: unknown)
+Repo-filename : /etc/yum.repos.d/almalinux-baseos.repo
+
+Repo-id : copr:copr.fedorainfracloud.org:uriesk:dracut-crypt-ssh
+Repo-name : Copr repo for dracut-crypt-ssh owned by uriesk
+Repo-status : enabled
+Repo-revision : 1698291016
+Repo-updated : Thu 26 Oct 2023 03:30:16 AM UTC
+Repo-pkgs : 4
+Repo-available-pkgs: 4
+Repo-size : 102 k
+Repo-baseurl : https://download.copr.fedorainfracloud.org/results/uriesk/dracut-crypt-ssh/epel-9-x86_64/
+Repo-expire : 172,800 second(s) (last: Fri 22 Dec 2023 06:05:10 PM UTC)
+Repo-filename : /etc/yum.repos.d/_copr:copr.fedorainfracloud.org:uriesk:dracut-crypt-ssh.repo
+
+Repo-id : crb
+Repo-name : AlmaLinux 9 - CRB
+Repo-status : disabled
+Repo-revision : 1703240590
+Repo-updated : Fri 22 Dec 2023 10:23:10 AM UTC
+Repo-pkgs : 1,730
+Repo-available-pkgs: 1,727
+Repo-size : 13 G
+Repo-mirrors : https://mirrors.almalinux.org/mirrorlist/9/crb
+Repo-baseurl : http://mirror.cogentco.com/pub/linux/almalinux/9.3/CRB/x86_64/os/ (9 more)
+Repo-expire : 86,400 second(s) (last: Fri 22 Dec 2023 06:05:11 PM UTC)
+Repo-filename : /etc/yum.repos.d/almalinux-crb.repo
+
+Repo-id : rpmfusion-nonfree-updates
+Repo-name : RPM Fusion for EL 9 - Nonfree - Updates
+Repo-status : enabled
+Repo-revision : 1703248251
+Repo-tags : binary-x86_64
+Repo-updated : Fri 22 Dec 2023 12:30:53 PM UTC
+Repo-pkgs : 65
+Repo-available-pkgs: 65
+Repo-size : 944 M
+Repo-metalink : http://mirrors.rpmfusion.org/metalink?repo=nonfree-el-updates-released-9&arch=x86_64
+ Updated : Fri 22 Dec 2023 06:05:13 PM UTC
+Repo-baseurl : http://uvermont.mm.fcix.net/rpmfusion/nonfree/el/updates/9/x86_64/ (33 more)
+Repo-expire : 172,800 second(s) (last: Fri 22 Dec 2023 06:05:13 PM UTC)
+Repo-filename : /etc/yum.repos.d/rpmfusion-nonfree-updates.repo
+Total packages: 28,170
+"""
+
+mock_repolist_no_status = """Repo-id : appstream-debuginfo
+Repo-name : AlmaLinux 9 - AppStream - Debug
+Repo-mirrors : https://mirrors.almalinux.org/mirrorlist/9/appstream-debug
+Repo-expire : 86,400 second(s) (last: unknown)
+Repo-filename : /etc/yum.repos.d/almalinux-appstream.repo
+
+Repo-id : appstream-source
+Repo-name : AlmaLinux 9 - AppStream - Source
+Repo-status : disabled
+Repo-mirrors : https://mirrors.almalinux.org/mirrorlist/9/appstream-source
+Repo-expire : 86,400 second(s) (last: unknown)
+Repo-filename : /etc/yum.repos.d/almalinux-appstream.repo
+"""
+
+mock_repolist_status_before_id = """
+Repo-id : appstream-debuginfo
+Repo-status : disabled
+Repo-status : disabled
+"""
+
+expected_repo_states_crb_enabled = {'disabled': ['appstream-debuginfo',
+ 'appstream-source',
+ 'baseos-debuginfo',
+ 'baseos-source'],
+ 'enabled': ['appstream',
+ 'baseos',
+ 'copr:copr.fedorainfracloud.org:uriesk:dracut-crypt-ssh',
+ 'crb',
+ 'rpmfusion-nonfree-updates']}
+
+expected_repo_states_crb_disabled = {'disabled': ['appstream-debuginfo',
+ 'appstream-source',
+ 'baseos-debuginfo',
+ 'baseos-source',
+ 'crb'],
+ 'enabled': ['appstream',
+ 'baseos',
+ 'copr:copr.fedorainfracloud.org:uriesk:dracut-crypt-ssh',
+ 'rpmfusion-nonfree-updates']}
+
+call_get_repo_states = call(['/usr/bin/dnf', 'repolist', '--all', '--verbose'], check_rc=True)
+call_disable_crb = call(['/usr/bin/dnf', 'config-manager', '--set-disabled', 'crb'], check_rc=True)
+call_enable_crb = call(['/usr/bin/dnf', 'config-manager', '--set-enabled', 'crb'], check_rc=True)
+
+
+class TestDNFConfigManager(ModuleTestCase):
+ def setUp(self):
+ super(TestDNFConfigManager, self).setUp()
+ self.mock_run_command = (patch('ansible.module_utils.basic.AnsibleModule.run_command'))
+ self.run_command = self.mock_run_command.start()
+ self.mock_path_exists = (patch('os.path.exists'))
+ self.path_exists = self.mock_path_exists.start()
+ self.path_exists.return_value = True
+ self.module = dnf_config_manager_module
+
+ def tearDown(self):
+ super(TestDNFConfigManager, self).tearDown()
+ self.mock_run_command.stop()
+ self.mock_path_exists.stop()
+
+ def set_command_mock(self, execute_return=(0, '', ''), execute_side_effect=None):
+ self.run_command.reset_mock()
+ self.run_command.return_value = execute_return
+ self.run_command.side_effect = execute_side_effect
+
+ def execute_module(self, failed=False, changed=False):
+ if failed:
+ result = self.failed()
+ self.assertTrue(result['failed'])
+ else:
+ result = self.changed(changed)
+ self.assertEqual(result['changed'], changed)
+
+ return result
+
+ def failed(self):
+ with self.assertRaises(AnsibleFailJson) as exc:
+ self.module.main()
+
+ result = exc.exception.args[0]
+ self.assertTrue(result['failed'])
+ return result
+
+ def changed(self, changed=False):
+ with self.assertRaises(AnsibleExitJson) as exc:
+ self.module.main()
+
+ result = exc.exception.args[0]
+ self.assertEqual(result['changed'], changed)
+ return result
+
+ def test_get_repo_states(self):
+ set_module_args({})
+ self.set_command_mock(execute_return=(0, mock_repolist_crb_enabled, ''))
+ result = self.execute_module(changed=False)
+ self.assertEqual(result['repo_states_pre'], expected_repo_states_crb_enabled)
+ self.assertEqual(result['repo_states_post'], expected_repo_states_crb_enabled)
+ self.assertEqual(result['changed_repos'], [])
+ self.run_command.assert_has_calls(calls=[call_get_repo_states, call_get_repo_states], any_order=False)
+
+ def test_enable_disabled_repo(self):
+ set_module_args({
+ 'name': ['crb'],
+ 'state': 'enabled'
+ })
+ side_effects = [(0, mock_repolist_crb_disabled, ''), (0, '', ''), (0, mock_repolist_crb_enabled, '')]
+ self.set_command_mock(execute_side_effect=side_effects)
+ result = self.execute_module(changed=True)
+ self.assertEqual(result['repo_states_pre'], expected_repo_states_crb_disabled)
+ self.assertEqual(result['repo_states_post'], expected_repo_states_crb_enabled)
+ self.assertEqual(result['changed_repos'], ['crb'])
+ expected_calls = [call_get_repo_states, call_enable_crb, call_get_repo_states]
+ self.run_command.assert_has_calls(calls=expected_calls, any_order=False)
+
+ def test_enable_disabled_repo_check_mode(self):
+ set_module_args({
+ 'name': ['crb'],
+ 'state': 'enabled',
+ '_ansible_check_mode': True
+ })
+ side_effects = [(0, mock_repolist_crb_disabled, ''), (0, mock_repolist_crb_disabled, '')]
+ self.set_command_mock(execute_side_effect=side_effects)
+ result = self.execute_module(changed=True)
+ self.assertEqual(result['changed_repos'], ['crb'])
+ self.run_command.assert_has_calls(calls=[call_get_repo_states], any_order=False)
+
+ def test_disable_enabled_repo(self):
+ set_module_args({
+ 'name': ['crb'],
+ 'state': 'disabled'
+ })
+ side_effects = [(0, mock_repolist_crb_enabled, ''), (0, '', ''), (0, mock_repolist_crb_disabled, '')]
+ self.set_command_mock(execute_side_effect=side_effects)
+ result = self.execute_module(changed=True)
+ self.assertEqual(result['repo_states_pre'], expected_repo_states_crb_enabled)
+ self.assertEqual(result['repo_states_post'], expected_repo_states_crb_disabled)
+ self.assertEqual(result['changed_repos'], ['crb'])
+ expected_calls = [call_get_repo_states, call_disable_crb, call_get_repo_states]
+ self.run_command.assert_has_calls(calls=expected_calls, any_order=False)
+
+ def test_crb_already_enabled(self):
+ set_module_args({
+ 'name': ['crb'],
+ 'state': 'enabled'
+ })
+ side_effects = [(0, mock_repolist_crb_enabled, ''), (0, mock_repolist_crb_enabled, '')]
+ self.set_command_mock(execute_side_effect=side_effects)
+ result = self.execute_module(changed=False)
+ self.assertEqual(result['repo_states_pre'], expected_repo_states_crb_enabled)
+ self.assertEqual(result['repo_states_post'], expected_repo_states_crb_enabled)
+ self.assertEqual(result['changed_repos'], [])
+ self.run_command.assert_has_calls(calls=[call_get_repo_states, call_get_repo_states], any_order=False)
+
+ def test_get_repo_states_fail_no_status(self):
+ set_module_args({})
+ self.set_command_mock(execute_return=(0, mock_repolist_no_status, ''))
+ result = self.execute_module(failed=True)
+ self.assertEqual(result['msg'], 'dnf repolist parse failure: parsed another repo id before next status')
+ self.run_command.assert_has_calls(calls=[call_get_repo_states], any_order=False)
+
+ def test_get_repo_states_fail_status_before_id(self):
+ set_module_args({})
+ self.set_command_mock(execute_return=(0, mock_repolist_status_before_id, ''))
+ result = self.execute_module(failed=True)
+ self.assertEqual(result['msg'], 'dnf repolist parse failure: parsed status before repo id')
+ self.run_command.assert_has_calls(calls=[call_get_repo_states], any_order=False)
+
+ def test_failed__unknown_repo_id(self):
+ set_module_args({
+ 'name': ['fake']
+ })
+ self.set_command_mock(execute_return=(0, mock_repolist_crb_disabled, ''))
+ result = self.execute_module(failed=True)
+ self.assertEqual(result['msg'], "did not find repo with ID 'fake' in dnf repolist --all --verbose")
+ self.run_command.assert_has_calls(calls=[call_get_repo_states], any_order=False)
+
+ def test_failed_state_change_ineffective(self):
+ set_module_args({
+ 'name': ['crb'],
+ 'state': 'enabled'
+ })
+ side_effects = [(0, mock_repolist_crb_disabled, ''), (0, '', ''), (0, mock_repolist_crb_disabled, '')]
+ self.set_command_mock(execute_side_effect=side_effects)
+ result = self.execute_module(failed=True)
+ self.assertEqual(result['msg'], "dnf config-manager failed to make 'crb' enabled")
+ expected_calls = [call_get_repo_states, call_enable_crb, call_get_repo_states]
+ self.run_command.assert_has_calls(calls=expected_calls, any_order=False)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_dnsimple.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_dnsimple.py
index 95a78818d..d5578252d 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_dnsimple.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_dnsimple.py
@@ -45,7 +45,7 @@ class TestDNSimple(ModuleTestCase):
def test_account_token(self, mock_whoami):
mock_whoami.return_value.data.account = 42
ds = self.module.DNSimpleV2('fake', 'fake', True, self.module)
- self.assertEquals(ds.account, 42)
+ self.assertEqual(ds.account, 42)
@patch('dnsimple.service.Accounts.list_accounts')
@patch('dnsimple.service.Identity.whoami')
@@ -61,4 +61,4 @@ class TestDNSimple(ModuleTestCase):
mock_accounts.return_value.data = [42]
mock_whoami.return_value.data.account = None
ds = self.module.DNSimpleV2('fake', 'fake', True, self.module)
- self.assertEquals(ds.account, 42)
+ self.assertEqual(ds.account, 42)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_dnsimple_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_dnsimple_info.py
index 5806ec772..08c5296c8 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_dnsimple_info.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_dnsimple_info.py
@@ -89,7 +89,7 @@ class TestDNSimple_Info(ModuleTestCase):
result = exc_info.exception.args[0]
# nothing should change
self.assertFalse(result['changed'])
- # we should return at least one item with mathing domain
+ # we should return at least one item with matching domain
assert result['dnsimple_records_info'][0]['name'] == name
@with_httmock(records_resp)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_facter_facts.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_facter_facts.py
new file mode 100644
index 000000000..227d8cd15
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_facter_facts.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Alexei Znamensky (russoz@gmail.com)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+from ansible_collections.community.general.plugins.modules import facter_facts
+from .helper import Helper
+
+
+Helper.from_module(facter_facts, __name__)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_facter_facts.yaml b/ansible_collections/community/general/tests/unit/plugins/modules/test_facter_facts.yaml
new file mode 100644
index 000000000..c287fdcfd
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_facter_facts.yaml
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Alexei Znamensky (russoz@gmail.com)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+---
+- id: simple run
+ output:
+ ansible_facts:
+ facter:
+ a: 1
+ b: 2
+ c: 3
+ run_command_calls:
+ - command: [/testbin/facter, --json]
+ environ: &env-def {check_rc: true}
+ rc: 0
+ out: >
+ { "a": 1, "b": 2, "c": 3 }
+ err: ""
+- id: with args
+ input:
+ arguments:
+ - -p
+ - system_uptime
+ - timezone
+ - is_virtual
+ output:
+ ansible_facts:
+ facter:
+ a: 1
+ b: 2
+ c: 3
+ run_command_calls:
+ - command: [/testbin/facter, --json, -p, system_uptime, timezone, is_virtual]
+ environ: *env-def
+ rc: 0
+ out: >
+ { "a": 1, "b": 2, "c": 3 }
+ err: ""
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2.py
index f01f15ef8..9608016e5 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2.py
@@ -6,111 +6,9 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-import json
from ansible_collections.community.general.plugins.modules import gconftool2
+from .helper import Helper
-import pytest
-TESTED_MODULE = gconftool2.__name__
-
-
-@pytest.fixture
-def patch_gconftool2(mocker):
- """
- Function used for mocking some parts of redhat_subscription module
- """
- mocker.patch('ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.get_bin_path',
- return_value='/testbin/gconftool-2')
-
-
-TEST_CASES = [
- [
- {'state': 'get', 'key': '/desktop/gnome/background/picture_filename'},
- {
- 'id': 'test_simple_element_get',
- 'run_command.calls': [
- (
- ['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '100\n', '',),
- ),
- ],
- 'new_value': '100',
- }
- ],
- [
- {'state': 'get', 'key': '/desktop/gnome/background/picture_filename'},
- {
- 'id': 'test_simple_element_get_not_found',
- 'run_command.calls': [
- (
- ['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '', "No value set for `/desktop/gnome/background/picture_filename'\n",),
- ),
- ],
- 'new_value': None,
- }
- ],
- [
- {'state': 'present', 'key': '/desktop/gnome/background/picture_filename', 'value': '200', 'value_type': 'int'},
- {
- 'id': 'test_simple_element_set',
- 'run_command.calls': [
- (
- ['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '100\n', '',),
- ),
- (
- ['/testbin/gconftool-2', '--type', 'int', '--set', '/desktop/gnome/background/picture_filename', '200'],
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- (0, '200\n', '',),
- ),
- ],
- 'new_value': '200',
- }
- ],
-]
-TEST_CASES_IDS = [item[1]['id'] for item in TEST_CASES]
-
-
-@pytest.mark.parametrize('patch_ansible_module, testcase',
- TEST_CASES,
- ids=TEST_CASES_IDS,
- indirect=['patch_ansible_module'])
-@pytest.mark.usefixtures('patch_ansible_module')
-def test_gconftool2(mocker, capfd, patch_gconftool2, testcase):
- """
- Run unit tests for test cases listen in TEST_CASES
- """
-
- # Mock function used for running commands first
- call_results = [item[2] for item in testcase['run_command.calls']]
- mock_run_command = mocker.patch(
- 'ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.run_command',
- side_effect=call_results)
-
- # Try to run test case
- with pytest.raises(SystemExit):
- gconftool2.main()
-
- out, err = capfd.readouterr()
- results = json.loads(out)
- print("testcase =\n%s" % testcase)
- print("results =\n%s" % results)
-
- for conditional_test_result in ('value',):
- if conditional_test_result in testcase:
- assert conditional_test_result in results, "'{0}' not found in {1}".format(conditional_test_result, results)
- assert results[conditional_test_result] == testcase[conditional_test_result], \
- "'{0}': '{1}' != '{2}'".format(conditional_test_result, results[conditional_test_result], testcase[conditional_test_result])
-
- assert mock_run_command.call_count == len(testcase['run_command.calls'])
- if mock_run_command.call_count:
- call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list]
- expected_call_args_list = [(item[0], item[1]) for item in testcase['run_command.calls']]
- print("call args list =\n%s" % call_args_list)
- print("expected args list =\n%s" % expected_call_args_list)
- assert call_args_list == expected_call_args_list
+Helper.from_module(gconftool2, __name__)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2.yaml b/ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2.yaml
new file mode 100644
index 000000000..5114dc45f
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2.yaml
@@ -0,0 +1,117 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Alexei Znamensky (russoz@gmail.com)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+---
+- id: test_simple_element_set
+ input:
+ state: present
+ key: /desktop/gnome/background/picture_filename
+ value: 200
+ value_type: int
+ output:
+ new_value: '200'
+ changed: true
+ run_command_calls:
+ - command: [/testbin/gconftool-2, --get, /desktop/gnome/background/picture_filename]
+ environ: &env-def {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
+ rc: 0
+ out: "100\n"
+ err: ""
+ - command: [/testbin/gconftool-2, --type, int, --set, /desktop/gnome/background/picture_filename, "200"]
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+ - command: [/testbin/gconftool-2, --get, /desktop/gnome/background/picture_filename]
+ environ: *env-def
+ rc: 0
+ out: "200\n"
+ err: ""
+- id: test_simple_element_set_idempotency_int
+ input:
+ state: present
+ key: /desktop/gnome/background/picture_filename
+ value: 200
+ value_type: int
+ output:
+ new_value: '200'
+ changed: false
+ run_command_calls:
+ - command: [/testbin/gconftool-2, --get, /desktop/gnome/background/picture_filename]
+ environ: *env-def
+ rc: 0
+ out: "200\n"
+ err: ""
+ - command: [/testbin/gconftool-2, --type, int, --set, /desktop/gnome/background/picture_filename, "200"]
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+ - command: [/testbin/gconftool-2, --get, /desktop/gnome/background/picture_filename]
+ environ: *env-def
+ rc: 0
+ out: "200\n"
+ err: ""
+- id: test_simple_element_set_idempotency_bool
+ input:
+ state: present
+ key: /apps/gnome_settings_daemon/screensaver/start_screensaver
+ value: false
+ value_type: bool
+ output:
+ new_value: 'false'
+ changed: false
+ run_command_calls:
+ - command: [/testbin/gconftool-2, --get, /apps/gnome_settings_daemon/screensaver/start_screensaver]
+ environ: *env-def
+ rc: 0
+ out: "false\n"
+ err: ""
+ - command: [/testbin/gconftool-2, --type, bool, --set, /apps/gnome_settings_daemon/screensaver/start_screensaver, "False"]
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+ - command: [/testbin/gconftool-2, --get, /apps/gnome_settings_daemon/screensaver/start_screensaver]
+ environ: *env-def
+ rc: 0
+ out: "false\n"
+ err: ""
+- id: test_simple_element_unset
+ input:
+ state: absent
+ key: /desktop/gnome/background/picture_filename
+ output:
+ new_value: null
+ changed: true
+ run_command_calls:
+ - command: [/testbin/gconftool-2, --get, /desktop/gnome/background/picture_filename]
+ environ: *env-def
+ rc: 0
+ out: "200\n"
+ err: ""
+ - command: [/testbin/gconftool-2, --unset, /desktop/gnome/background/picture_filename]
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+- id: test_simple_element_unset_idempotency
+ input:
+ state: absent
+ key: /apps/gnome_settings_daemon/screensaver/start_screensaver
+ output:
+ new_value: null
+ changed: false
+ run_command_calls:
+ - command: [/testbin/gconftool-2, --get, /apps/gnome_settings_daemon/screensaver/start_screensaver]
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+ - command: [/testbin/gconftool-2, --unset, /apps/gnome_settings_daemon/screensaver/start_screensaver]
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2_info.py
index 352af6bb0..54676a12d 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2_info.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2_info.py
@@ -6,98 +6,9 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-import json
from ansible_collections.community.general.plugins.modules import gconftool2_info
+from .helper import Helper
-import pytest
-TESTED_MODULE = gconftool2_info.__name__
-
-
-@pytest.fixture
-def patch_gconftool2_info(mocker):
- """
- Function used for mocking some parts of redhat_subscription module
- """
- mocker.patch('ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.get_bin_path',
- return_value='/testbin/gconftool-2')
-
-
-TEST_CASES = [
- [
- {'key': '/desktop/gnome/background/picture_filename'},
- {
- 'id': 'test_simple_element_get',
- 'run_command.calls': [
- (
- # Calling of following command will be asserted
- ['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- # Mock of returned code, stdout and stderr
- (0, '100\n', '',),
- ),
- ],
- 'value': '100',
- }
- ],
- [
- {'key': '/desktop/gnome/background/picture_filename'},
- {
- 'id': 'test_simple_element_get_not_found',
- 'run_command.calls': [
- (
- # Calling of following command will be asserted
- ['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- # Mock of returned code, stdout and stderr
- (0, '', "No value set for `/desktop/gnome/background/picture_filename'\n",),
- ),
- ],
- 'value': None,
- }
- ],
-]
-TEST_CASES_IDS = [item[1]['id'] for item in TEST_CASES]
-
-
-@pytest.mark.parametrize('patch_ansible_module, testcase',
- TEST_CASES,
- ids=TEST_CASES_IDS,
- indirect=['patch_ansible_module'])
-@pytest.mark.usefixtures('patch_ansible_module')
-def test_gconftool2_info(mocker, capfd, patch_gconftool2_info, testcase):
- """
- Run unit tests for test cases listen in TEST_CASES
- """
-
- # Mock function used for running commands first
- call_results = [item[2] for item in testcase['run_command.calls']]
- mock_run_command = mocker.patch(
- 'ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.run_command',
- side_effect=call_results)
-
- # Try to run test case
- with pytest.raises(SystemExit):
- gconftool2_info.main()
-
- out, err = capfd.readouterr()
- results = json.loads(out)
- print("testcase =\n%s" % testcase)
- print("results =\n%s" % results)
-
- for conditional_test_result in ('value',):
- if conditional_test_result in testcase:
- assert conditional_test_result in results, "'{0}' not found in {1}".format(conditional_test_result, results)
- assert results[conditional_test_result] == testcase[conditional_test_result], \
- "'{0}': '{1}' != '{2}'".format(conditional_test_result, results[conditional_test_result], testcase[conditional_test_result])
-
- assert mock_run_command.call_count == len(testcase['run_command.calls'])
- if mock_run_command.call_count:
- call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list]
- expected_call_args_list = [(item[0], item[1]) for item in testcase['run_command.calls']]
- print("call args list =\n%s" % call_args_list)
- print("expected args list =\n%s" % expected_call_args_list)
- assert call_args_list == expected_call_args_list
+Helper.from_module(gconftool2_info, __name__)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2_info.yaml b/ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2_info.yaml
new file mode 100644
index 000000000..eb8bef750
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gconftool2_info.yaml
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Alexei Znamensky (russoz@gmail.com)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+---
+- id: test_simple_element_get
+ input:
+ key: /desktop/gnome/background/picture_filename
+ output:
+ value: '100'
+ run_command_calls:
+ - command: [/testbin/gconftool-2, --get, /desktop/gnome/background/picture_filename]
+ environ: &env-def {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
+ rc: 0
+ out: "100\n"
+ err: ""
+- id: test_simple_element_get_not_found
+ input:
+ key: /desktop/gnome/background/picture_filename
+ output:
+ value: null
+ run_command_calls:
+ - command: [/testbin/gconftool-2, --get, /desktop/gnome/background/picture_filename]
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: "No value set for `/desktop/gnome/background/picture_filename'\n"
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_gem.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_gem.py
index 92578e062..10c03e537 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_gem.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gem.py
@@ -69,7 +69,7 @@ class TestGem(ModuleTestCase):
assert result['msg'] == "install_dir requires user_install=false"
def test_passes_install_dir_to_gem(self):
- # XXX: This test is extremely fragile, and makes assuptions about the module code, and how
+ # XXX: This test is extremely fragile, and makes assumptions about the module code, and how
# functions are run.
# If you start modifying the code of the module, you might need to modify what this
# test mocks. The only thing that matters is the assertion that this 'gem install' is
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_gio_mime.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_gio_mime.py
new file mode 100644
index 000000000..f2402ac35
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gio_mime.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Alexei Znamensky (russoz@gmail.com)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+from ansible_collections.community.general.plugins.modules import gio_mime
+from .helper import Helper
+
+
+Helper.from_module(gio_mime, __name__)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_gio_mime.yaml b/ansible_collections/community/general/tests/unit/plugins/modules/test_gio_mime.yaml
new file mode 100644
index 000000000..d9e47a60e
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gio_mime.yaml
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Alexei Znamensky (russoz@gmail.com)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+---
+- id: test_set_handler
+ input:
+ handler: google-chrome.desktop
+ mime_type: x-scheme-handler/http
+ output:
+ handler: google-chrome.desktop
+ changed: true
+ run_command_calls:
+ - command: [/testbin/gio, mime, x-scheme-handler/http]
+ environ: &env-def {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
+ rc: 0
+ out: ""
+ err: >
+ No default applications for “x-scheme-handler/http”
+ - command: [/testbin/gio, mime, x-scheme-handler/http, google-chrome.desktop]
+ environ: *env-def
+ rc: 0
+ out: "Set google-chrome.desktop as the default for x-scheme-handler/http\n"
+ err: ""
+- id: test_set_handler_check
+ input:
+ handler: google-chrome.desktop
+ mime_type: x-scheme-handler/http
+ output:
+ handler: google-chrome.desktop
+ changed: true
+ flags:
+ skip: test helper does not support check mode yet
+ run_command_calls:
+ - command: [/testbin/gio, mime, x-scheme-handler/http]
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: >
+ No default applications for “x-scheme-handler/http”
+ - command: [/testbin/gio, mime, x-scheme-handler/http, google-chrome.desktop]
+ environ: *env-def
+ rc: 0
+ out: "Set google-chrome.desktop as the default for x-scheme-handler/http\n"
+ err: ""
+- id: test_set_handler_idempot
+ input:
+ handler: google-chrome.desktop
+ mime_type: x-scheme-handler/http
+ output:
+ handler: google-chrome.desktop
+ changed: false
+ run_command_calls:
+ - command: [/testbin/gio, mime, x-scheme-handler/http]
+ environ: *env-def
+ rc: 0
+ out: |
+ Default application for “x-scheme-handler/https”: google-chrome.desktop
+ Registered applications:
+ brave-browser.desktop
+ firefox.desktop
+ google-chrome.desktop
+ firefox_firefox.desktop
+ Recommended applications:
+ brave-browser.desktop
+ firefox.desktop
+ google-chrome.desktop
+ firefox_firefox.desktop
+ err: ""
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_group_access_token.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_group_access_token.py
new file mode 100644
index 000000000..06af94820
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_group_access_token.py
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2023, Zoran Krleza (zoran.krleza@true-north.hr)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import pytest
+import gitlab
+
+from ansible_collections.community.general.plugins.modules.gitlab_group_access_token import GitLabGroupAccessToken
+
+# python-gitlab 3.1+ is needed for python-gitlab access tokens api
+PYTHON_GITLAB_MINIMAL_VERSION = (3, 1)
+
+
+def python_gitlab_version_match_requirement():
+ return tuple(map(int, gitlab.__version__.split('.'))) >= PYTHON_GITLAB_MINIMAL_VERSION
+
+
+def _dummy(x):
+ """Dummy function. Only used as a placeholder for toplevel definitions when the test is going
+ to be skipped anyway"""
+ return x
+
+
+pytestmark = []
+try:
+ from .gitlab import (GitlabModuleTestCase,
+ resp_get_user,
+ resp_get_group,
+ resp_list_group_access_tokens,
+ resp_create_group_access_tokens,
+ resp_revoke_group_access_tokens)
+
+except ImportError:
+ pytestmark.append(pytest.mark.skip("Could not load gitlab module required for testing"))
+ # Need to set these to something so that we don't fail when parsing
+ GitlabModuleTestCase = object
+ resp_list_group_access_tokens = _dummy
+ resp_create_group_access_tokens = _dummy
+ resp_revoke_group_access_tokens = _dummy
+ resp_get_user = _dummy
+ resp_get_group = _dummy
+
+# Unit tests requirements
+try:
+ from httmock import with_httmock # noqa
+except ImportError:
+ pytestmark.append(pytest.mark.skip("Could not load httmock module required for testing"))
+ with_httmock = _dummy
+
+
+class TestGitlabGroupAccessToken(GitlabModuleTestCase):
+ @with_httmock(resp_get_user)
+ def setUp(self):
+ super(TestGitlabGroupAccessToken, self).setUp()
+ if not python_gitlab_version_match_requirement():
+ self.skipTest("python-gitlab %s+ is needed for gitlab_group_access_token" % ",".join(map(str, PYTHON_GITLAB_MINIMAL_VERSION)))
+
+ self.moduleUtil = GitLabGroupAccessToken(module=self.mock_module, gitlab_instance=self.gitlab_instance)
+
+ @with_httmock(resp_get_group)
+ @with_httmock(resp_list_group_access_tokens)
+ def test_find_access_token(self):
+ group = self.gitlab_instance.groups.get(1)
+ self.assertIsNotNone(group)
+
+ rvalue = self.moduleUtil.find_access_token(group, "token1")
+ self.assertEqual(rvalue, False)
+ self.assertIsNotNone(self.moduleUtil.access_token_object)
+
+ @with_httmock(resp_get_group)
+ @with_httmock(resp_list_group_access_tokens)
+ def test_find_access_token_negative(self):
+ groups = self.gitlab_instance.groups.get(1)
+ self.assertIsNotNone(groups)
+
+ rvalue = self.moduleUtil.find_access_token(groups, "nonexisting")
+ self.assertEqual(rvalue, False)
+ self.assertIsNone(self.moduleUtil.access_token_object)
+
+ @with_httmock(resp_get_group)
+ @with_httmock(resp_create_group_access_tokens)
+ def test_create_access_token(self):
+ groups = self.gitlab_instance.groups.get(1)
+ self.assertIsNotNone(groups)
+
+ rvalue = self.moduleUtil.create_access_token(groups, {'name': "tokenXYZ", 'scopes': ["api"], 'access_level': 20, 'expires_at': "2024-12-31"})
+ self.assertEqual(rvalue, True)
+ self.assertIsNotNone(self.moduleUtil.access_token_object)
+
+ @with_httmock(resp_get_group)
+ @with_httmock(resp_list_group_access_tokens)
+ @with_httmock(resp_revoke_group_access_tokens)
+ def test_revoke_access_token(self):
+ groups = self.gitlab_instance.groups.get(1)
+ self.assertIsNotNone(groups)
+
+ rvalue = self.moduleUtil.find_access_token(groups, "token1")
+ self.assertEqual(rvalue, False)
+ self.assertIsNotNone(self.moduleUtil.access_token_object)
+
+ rvalue = self.moduleUtil.revoke_access_token()
+ self.assertEqual(rvalue, True)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_project_access_token.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_project_access_token.py
new file mode 100644
index 000000000..ebc324b88
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_gitlab_project_access_token.py
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2023, Zoran Krleza (zoran.krleza@true-north.hr)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import pytest
+import gitlab
+
+from ansible_collections.community.general.plugins.modules.gitlab_project_access_token import GitLabProjectAccessToken
+
+# python-gitlab 3.1+ is needed for python-gitlab access tokens api
+PYTHON_GITLAB_MINIMAL_VERSION = (3, 1)
+
+
+def python_gitlab_version_match_requirement():
+ return tuple(map(int, gitlab.__version__.split('.'))) >= PYTHON_GITLAB_MINIMAL_VERSION
+
+
+def _dummy(x):
+ """Dummy function. Only used as a placeholder for toplevel definitions when the test is going
+ to be skipped anyway"""
+ return x
+
+
+pytestmark = []
+try:
+ from .gitlab import (GitlabModuleTestCase,
+ resp_get_user,
+ resp_get_project,
+ resp_list_project_access_tokens,
+ resp_create_project_access_tokens,
+ resp_revoke_project_access_tokens)
+
+except ImportError:
+ pytestmark.append(pytest.mark.skip("Could not load gitlab module required for testing"))
+ # Need to set these to something so that we don't fail when parsing
+ GitlabModuleTestCase = object
+ resp_list_project_access_tokens = _dummy
+ resp_create_project_access_tokens = _dummy
+ resp_revoke_project_access_tokens = _dummy
+ resp_get_user = _dummy
+ resp_get_project = _dummy
+
+# Unit tests requirements
+try:
+ from httmock import with_httmock # noqa
+except ImportError:
+ pytestmark.append(pytest.mark.skip("Could not load httmock module required for testing"))
+ with_httmock = _dummy
+
+
+class TestGitlabProjectAccessToken(GitlabModuleTestCase):
+ @with_httmock(resp_get_user)
+ def setUp(self):
+ super(TestGitlabProjectAccessToken, self).setUp()
+ if not python_gitlab_version_match_requirement():
+ self.skipTest("python-gitlab %s+ is needed for gitlab_project_access_token" % ",".join(map(str, PYTHON_GITLAB_MINIMAL_VERSION)))
+
+ self.moduleUtil = GitLabProjectAccessToken(module=self.mock_module, gitlab_instance=self.gitlab_instance)
+
+ @with_httmock(resp_get_project)
+ @with_httmock(resp_list_project_access_tokens)
+ def test_find_access_token(self):
+ project = self.gitlab_instance.projects.get(1)
+ self.assertIsNotNone(project)
+
+ rvalue = self.moduleUtil.find_access_token(project, "token1")
+ self.assertEqual(rvalue, False)
+ self.assertIsNotNone(self.moduleUtil.access_token_object)
+
+ @with_httmock(resp_get_project)
+ @with_httmock(resp_list_project_access_tokens)
+ def test_find_access_token_negative(self):
+ project = self.gitlab_instance.projects.get(1)
+ self.assertIsNotNone(project)
+
+ rvalue = self.moduleUtil.find_access_token(project, "nonexisting")
+ self.assertEqual(rvalue, False)
+ self.assertIsNone(self.moduleUtil.access_token_object)
+
+ @with_httmock(resp_get_project)
+ @with_httmock(resp_create_project_access_tokens)
+ def test_create_access_token(self):
+ project = self.gitlab_instance.projects.get(1)
+ self.assertIsNotNone(project)
+
+ rvalue = self.moduleUtil.create_access_token(project, {'name': "tokenXYZ", 'scopes': ["api"], 'access_level': 20, 'expires_at': "2024-12-31"})
+ self.assertEqual(rvalue, True)
+ self.assertIsNotNone(self.moduleUtil.access_token_object)
+
+ @with_httmock(resp_get_project)
+ @with_httmock(resp_list_project_access_tokens)
+ @with_httmock(resp_revoke_project_access_tokens)
+ def test_revoke_access_token(self):
+ project = self.gitlab_instance.projects.get(1)
+ self.assertIsNotNone(project)
+
+ rvalue = self.moduleUtil.find_access_token(project, "token1")
+ self.assertEqual(rvalue, False)
+ self.assertIsNotNone(self.moduleUtil.access_token_object)
+
+ rvalue = self.moduleUtil.revoke_access_token()
+ self.assertEqual(rvalue, True)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_hana_query.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_hana_query.py
deleted file mode 100644
index db06e4cef..000000000
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_hana_query.py
+++ /dev/null
@@ -1,103 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2021, Rainer Leber (@rainerleber) <rainerleber@gmail.com>
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-from ansible_collections.community.general.plugins.modules import hana_query
-from ansible_collections.community.general.tests.unit.plugins.modules.utils import (
- AnsibleExitJson,
- AnsibleFailJson,
- ModuleTestCase,
- set_module_args,
-)
-from ansible_collections.community.general.tests.unit.compat.mock import patch
-from ansible.module_utils import basic
-
-
-def get_bin_path(*args, **kwargs):
- """Function to return path of hdbsql"""
- return "/usr/sap/HDB/HDB01/exe/hdbsql"
-
-
-class Testhana_query(ModuleTestCase):
- """Main class for testing hana_query module."""
-
- def setUp(self):
- """Setup."""
- super(Testhana_query, self).setUp()
- self.module = hana_query
- self.mock_get_bin_path = patch.object(basic.AnsibleModule, 'get_bin_path', get_bin_path)
- self.mock_get_bin_path.start()
- self.addCleanup(self.mock_get_bin_path.stop) # ensure that the patching is 'undone'
-
- def tearDown(self):
- """Teardown."""
- super(Testhana_query, self).tearDown()
-
- def test_without_required_parameters(self):
- """Failure must occurs when all parameters are missing."""
- with self.assertRaises(AnsibleFailJson):
- set_module_args({})
- self.module.main()
-
- def test_hana_query(self):
- """Check that result is processed."""
- set_module_args({
- 'sid': "HDB",
- 'instance': "01",
- 'encrypted': False,
- 'host': "localhost",
- 'user': "SYSTEM",
- 'password': "1234Qwer",
- 'database': "HDB",
- 'query': "SELECT * FROM users;"
- })
- with patch.object(basic.AnsibleModule, 'run_command') as run_command:
- run_command.return_value = 0, 'username,name\n testuser,test user \n myuser, my user \n', ''
- with self.assertRaises(AnsibleExitJson) as result:
- hana_query.main()
- self.assertEqual(result.exception.args[0]['query_result'], [[
- {'username': 'testuser', 'name': 'test user'},
- {'username': 'myuser', 'name': 'my user'},
- ]])
- self.assertEqual(run_command.call_count, 1)
-
- def test_hana_userstore_query(self):
- """Check that result is processed with userstore."""
- set_module_args({
- 'sid': "HDB",
- 'instance': "01",
- 'encrypted': False,
- 'host': "localhost",
- 'user': "SYSTEM",
- 'userstore': True,
- 'database': "HDB",
- 'query': "SELECT * FROM users;"
- })
- with patch.object(basic.AnsibleModule, 'run_command') as run_command:
- run_command.return_value = 0, 'username,name\n testuser,test user \n myuser, my user \n', ''
- with self.assertRaises(AnsibleExitJson) as result:
- hana_query.main()
- self.assertEqual(result.exception.args[0]['query_result'], [[
- {'username': 'testuser', 'name': 'test user'},
- {'username': 'myuser', 'name': 'my user'},
- ]])
- self.assertEqual(run_command.call_count, 1)
-
- def test_hana_failed_no_passwd(self):
- """Check that result is failed with no password."""
- with self.assertRaises(AnsibleFailJson):
- set_module_args({
- 'sid': "HDB",
- 'instance': "01",
- 'encrypted': False,
- 'host': "localhost",
- 'user': "SYSTEM",
- 'database': "HDB",
- 'query': "SELECT * FROM users;"
- })
- self.module.main()
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_ini_file.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_ini_file.py
new file mode 100644
index 000000000..a65a9c326
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_ini_file.py
@@ -0,0 +1,51 @@
+# Copyright (c) 2023 Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or
+# https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from ansible_collections.community.general.plugins.modules import ini_file
+
+
+def do_test(option, ignore_spaces, newline, before, expected_after,
+ expected_changed, expected_msg):
+ section_lines = [before]
+ changed_lines = [0]
+ changed, msg = ini_file.update_section_line(
+ option, None, section_lines, 0, changed_lines, ignore_spaces,
+ newline, None)
+ assert section_lines[0] == expected_after
+ assert changed == expected_changed
+ assert changed_lines[0] == 1
+ assert msg == expected_msg
+
+
+def test_ignore_spaces_comment():
+ oldline = ';foobar=baz'
+ newline = 'foobar = baz'
+ do_test('foobar', True, newline, oldline, newline, True, 'option changed')
+
+
+def test_ignore_spaces_changed():
+ oldline = 'foobar=baz'
+ newline = 'foobar = freeble'
+ do_test('foobar', True, newline, oldline, newline, True, 'option changed')
+
+
+def test_ignore_spaces_unchanged():
+ oldline = 'foobar=baz'
+ newline = 'foobar = baz'
+ do_test('foobar', True, newline, oldline, oldline, False, None)
+
+
+def test_no_ignore_spaces_changed():
+ oldline = 'foobar=baz'
+ newline = 'foobar = baz'
+ do_test('foobar', False, newline, oldline, newline, True, 'option changed')
+
+
+def test_no_ignore_spaces_unchanged():
+ newline = 'foobar=baz'
+ do_test('foobar', False, newline, newline, newline, False, None)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_otptoken.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_otptoken.py
index c06e19c3b..23911e5a5 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_otptoken.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_otptoken.py
@@ -104,7 +104,7 @@ class TestIPAOTPToken(ModuleTestCase):
{
'method': 'otptoken_add',
'name': 'NewToken1',
- 'item': {'ipatokendisabled': 'FALSE',
+ 'item': {'ipatokendisabled': False,
'all': True}
}
)
@@ -130,7 +130,7 @@ class TestIPAOTPToken(ModuleTestCase):
{
'method': 'otptoken_add',
'name': 'NewToken1',
- 'item': {'ipatokendisabled': 'FALSE',
+ 'item': {'ipatokendisabled': False,
'all': True}
}
)
@@ -176,7 +176,7 @@ class TestIPAOTPToken(ModuleTestCase):
'ipatokenotpkey': 'KRSXG5CTMVRXEZLUGE======',
'description': 'Test description',
'ipatokenowner': 'pinky',
- 'ipatokendisabled': 'FALSE',
+ 'ipatokendisabled': False,
'ipatokennotbefore': '20200101010101Z',
'ipatokennotafter': '20900101010101Z',
'ipatokenvendor': 'Acme',
@@ -220,7 +220,7 @@ class TestIPAOTPToken(ModuleTestCase):
'ipatokenotpkey': [{'__base64__': 'VGVzdFNlY3JldDE='}],
'description': ['Test description'],
'ipatokenowner': ['pinky'],
- 'ipatokendisabled': ['FALSE'],
+ 'ipatokendisabled': [False],
'ipatokennotbefore': ['20200101010101Z'],
'ipatokennotafter': ['20900101010101Z'],
'ipatokenvendor': ['Acme'],
@@ -271,7 +271,7 @@ class TestIPAOTPToken(ModuleTestCase):
'ipatokenotpkey': [{'__base64__': 'VGVzdFNlY3JldDE='}],
'description': ['Test description'],
'ipatokenowner': ['pinky'],
- 'ipatokendisabled': ['FALSE'],
+ 'ipatokendisabled': [False],
'ipatokennotbefore': ['20200101010101Z'],
'ipatokennotafter': ['20900101010101Z'],
'ipatokenvendor': ['Acme'],
@@ -296,7 +296,7 @@ class TestIPAOTPToken(ModuleTestCase):
'name': 'NewToken1',
'item': {'description': 'Test description',
'ipatokenowner': 'brain',
- 'ipatokendisabled': 'FALSE',
+ 'ipatokendisabled': False,
'ipatokennotbefore': '20200101010101Z',
'ipatokennotafter': '20900101010101Z',
'ipatokenvendor': 'Acme',
@@ -335,7 +335,7 @@ class TestIPAOTPToken(ModuleTestCase):
'ipatokenotpkey': [{'__base64__': 'VGVzdFNlY3JldDE='}],
'description': ['Test description'],
'ipatokenowner': ['pinky'],
- 'ipatokendisabled': ['FALSE'],
+ 'ipatokendisabled': [False],
'ipatokennotbefore': ['20200101010101Z'],
'ipatokennotafter': ['20900101010101Z'],
'ipatokenvendor': ['Acme'],
@@ -360,7 +360,7 @@ class TestIPAOTPToken(ModuleTestCase):
'name': 'NewToken1',
'item': {'description': 'New Test description',
'ipatokenowner': 'pinky',
- 'ipatokendisabled': 'TRUE',
+ 'ipatokendisabled': True,
'ipatokennotbefore': '20200101010102Z',
'ipatokennotafter': '20900101010102Z',
'ipatokenvendor': 'NewAcme',
@@ -384,7 +384,7 @@ class TestIPAOTPToken(ModuleTestCase):
'ipatokenotpkey': [{'__base64__': 'KRSXG5CTMVRXEZLUGE======'}],
'description': ['Test description'],
'ipatokenowner': ['pinky'],
- 'ipatokendisabled': ['FALSE'],
+ 'ipatokendisabled': [False],
'ipatokennotbefore': ['20200101010101Z'],
'ipatokennotafter': ['20900101010101Z'],
'ipatokenvendor': ['Acme'],
@@ -425,7 +425,7 @@ class TestIPAOTPToken(ModuleTestCase):
'ipatokenotpkey': [{'__base64__': 'KRSXG5CTMVRXEZLUGE======'}],
'description': ['Test description'],
'ipatokenowner': ['pinky'],
- 'ipatokendisabled': ['FALSE'],
+ 'ipatokendisabled': [False],
'ipatokennotbefore': ['20200101010101Z'],
'ipatokennotafter': ['20900101010101Z'],
'ipatokenvendor': ['Acme'],
@@ -448,7 +448,7 @@ class TestIPAOTPToken(ModuleTestCase):
{
'method': 'otptoken_mod',
'name': 'NewToken1',
- 'item': {'ipatokendisabled': 'TRUE',
+ 'item': {'ipatokendisabled': True,
'all': True}
}
)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_pwpolicy.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_pwpolicy.py
index b45c566fc..538f61e9a 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_pwpolicy.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_ipa_pwpolicy.py
@@ -100,7 +100,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '16',
'maxfailcount': '6',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {}
mock_calls = (
@@ -124,7 +129,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdminlength': '16',
'krbpwdmaxfailure': '6',
'krbpwdfailurecountinterval': '60',
- 'krbpwdlockoutduration': '600'
+ 'krbpwdlockoutduration': '600',
+ 'passwordgracelimit': '3',
+ 'ipapwdmaxrepeat': '3',
+ 'ipapwdmaxsequence': '3',
+ 'ipapwddictcheck': True,
+ 'ipapwdusercheck': True,
}
}
)
@@ -145,7 +155,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '16',
'maxfailcount': '6',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {}
mock_calls = (
@@ -169,7 +184,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdminlength': '16',
'krbpwdmaxfailure': '6',
'krbpwdfailurecountinterval': '60',
- 'krbpwdlockoutduration': '600'
+ 'krbpwdlockoutduration': '600',
+ 'passwordgracelimit': '3',
+ 'ipapwdmaxrepeat': '3',
+ 'ipapwdmaxsequence': '3',
+ 'ipapwddictcheck': True,
+ 'ipapwdusercheck': True,
}
}
)
@@ -190,7 +210,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '12',
'maxfailcount': '8',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {
'cn': ['sysops'],
@@ -203,6 +228,11 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdmaxfailure': ['6'],
'krbpwdfailurecountinterval': ['60'],
'krbpwdlockoutduration': ['600'],
+ 'passwordgracelimit': ['3'],
+ 'ipapwdmaxrepeat': ['3'],
+ 'ipapwdmaxsequence': ['3'],
+ 'ipapwddictcheck': [True],
+ 'ipapwdusercheck': [True],
'dn': 'cn=sysops,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
'objectclass': ['top', 'nscontainer', 'krbpwdpolicy']
}
@@ -227,7 +257,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdminlength': '12',
'krbpwdmaxfailure': '8',
'krbpwdfailurecountinterval': '60',
- 'krbpwdlockoutduration': '600'
+ 'krbpwdlockoutduration': '600',
+ 'passwordgracelimit': '3',
+ 'ipapwdmaxrepeat': '3',
+ 'ipapwdmaxsequence': '3',
+ 'ipapwddictcheck': True,
+ 'ipapwdusercheck': True,
}
}
)
@@ -248,7 +283,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '16',
'maxfailcount': '6',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {
'cn': ['sysops'],
@@ -281,7 +321,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdminlength': '16',
'krbpwdmaxfailure': '6',
'krbpwdfailurecountinterval': '60',
- 'krbpwdlockoutduration': '600'
+ 'krbpwdlockoutduration': '600',
+ 'passwordgracelimit': '3',
+ 'ipapwdmaxrepeat': '3',
+ 'ipapwdmaxsequence': '3',
+ 'ipapwddictcheck': True,
+ 'ipapwdusercheck': True,
}
}
)
@@ -342,7 +387,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '16',
'maxfailcount': '6',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {
'cn': ['admins'],
@@ -355,6 +405,11 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdmaxfailure': ['6'],
'krbpwdfailurecountinterval': ['60'],
'krbpwdlockoutduration': ['600'],
+ 'passwordgracelimit': ['3'],
+ 'ipapwdmaxrepeat': ['3'],
+ 'ipapwdmaxsequence': ['3'],
+ 'ipapwddictcheck': [True],
+ 'ipapwdusercheck': [True],
'dn': 'cn=admins,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
'objectclass': ['top', 'nscontainer', 'krbpwdpolicy']
}
@@ -409,7 +464,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '12',
'maxfailcount': '8',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {
'cn': ['global_policy'],
@@ -420,6 +480,11 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdmaxfailure': ['6'],
'krbpwdfailurecountinterval': ['60'],
'krbpwdlockoutduration': ['600'],
+ 'passwordgracelimit': ['3'],
+ 'ipapwdmaxrepeat': ['3'],
+ 'ipapwdmaxsequence': ['3'],
+ 'ipapwddictcheck': [True],
+ 'ipapwdusercheck': [True],
'dn': 'cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
'objectclass': ['top', 'nscontainer', 'krbpwdpolicy']
}
@@ -443,7 +508,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdminlength': '12',
'krbpwdmaxfailure': '8',
'krbpwdfailurecountinterval': '60',
- 'krbpwdlockoutduration': '600'
+ 'krbpwdlockoutduration': '600',
+ 'passwordgracelimit': '3',
+ 'ipapwdmaxrepeat': '3',
+ 'ipapwdmaxsequence': '3',
+ 'ipapwddictcheck': True,
+ 'ipapwdusercheck': True,
}
}
)
@@ -461,7 +531,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '16',
'maxfailcount': '6',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {
'cn': ['global_policy'],
@@ -473,6 +548,11 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdmaxfailure': ['6'],
'krbpwdfailurecountinterval': ['60'],
'krbpwdlockoutduration': ['600'],
+ 'passwordgracelimit': ['3'],
+ 'ipapwdmaxrepeat': ['3'],
+ 'ipapwdmaxsequence': ['3'],
+ 'ipapwddictcheck': [True],
+ 'ipapwdusercheck': [True],
'dn': 'cn=global_policy,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
'objectclass': ['top', 'nscontainer', 'krbpwdpolicy']
}
@@ -504,7 +584,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '16',
'maxfailcount': '6',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {}
mock_calls = [
@@ -535,7 +620,12 @@ class TestIPAPwPolicy(ModuleTestCase):
'minlength': '12',
'maxfailcount': '8',
'failinterval': '60',
- 'lockouttime': '600'
+ 'lockouttime': '600',
+ 'gracelimit': 3,
+ 'maxrepeat': 3,
+ 'maxsequence': 3,
+ 'dictcheck': True,
+ 'usercheck': True,
}
return_value = {
'cn': ['sysops'],
@@ -548,6 +638,11 @@ class TestIPAPwPolicy(ModuleTestCase):
'krbpwdmaxfailure': ['6'],
'krbpwdfailurecountinterval': ['60'],
'krbpwdlockoutduration': ['600'],
+ 'passwordgracelimit': ['3'],
+ 'ipapwdmaxrepeat': ['3'],
+ 'ipapwdmaxsequence': ['3'],
+ 'ipapwddictcheck': [True],
+ 'ipapwdusercheck': [True],
'dn': 'cn=sysops,cn=EXAMPLE.COM,cn=kerberos,dc=example,dc=com',
'objectclass': ['top', 'nscontainer', 'krbpwdpolicy']
}
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_ipbase.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_ipbase.py
new file mode 100644
index 000000000..8106889da
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_ipbase.py
@@ -0,0 +1,187 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Ansible project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import json
+
+from ansible_collections.community.general.plugins.modules.ipbase_info import IpbaseInfo
+from ansible_collections.community.general.tests.unit.compat import unittest
+from ansible_collections.community.general.tests.unit.compat.mock import Mock
+
+
+IPBASE_DATA = {
+ "response": b"""
+{
+ "data": {
+ "ip": "1.1.1.1",
+ "hostname": "one.one.one.one",
+ "type": "v4",
+ "range_type": {
+ "type": "PUBLIC",
+ "description": "Public address"
+ },
+ "connection": {
+ "asn": 13335,
+ "organization": "Cloudflare, Inc.",
+ "isp": "APNIC Research and Development",
+ "range": "1.1.1.1/32"
+ },
+ "location": {
+ "geonames_id": 5332870,
+ "latitude": 34.053611755371094,
+ "longitude": -118.24549865722656,
+ "zip": "90012",
+ "continent": {
+ "code": "NA",
+ "name": "North America",
+ "name_translated": "North America"
+ },
+ "country": {
+ "alpha2": "US",
+ "alpha3": "USA",
+ "calling_codes": [
+ "+1"
+ ],
+ "currencies": [
+ {
+ "symbol": "$",
+ "name": "US Dollar",
+ "symbol_native": "$",
+ "decimal_digits": 2,
+ "rounding": 0,
+ "code": "USD",
+ "name_plural": "US dollars"
+ }
+ ],
+ "emoji": "...",
+ "ioc": "USA",
+ "languages": [
+ {
+ "name": "English",
+ "name_native": "English"
+ }
+ ],
+ "name": "United States",
+ "name_translated": "United States",
+ "timezones": [
+ "America/New_York",
+ "America/Detroit",
+ "America/Kentucky/Louisville",
+ "America/Kentucky/Monticello",
+ "America/Indiana/Indianapolis",
+ "America/Indiana/Vincennes",
+ "America/Indiana/Winamac",
+ "America/Indiana/Marengo",
+ "America/Indiana/Petersburg",
+ "America/Indiana/Vevay",
+ "America/Chicago",
+ "America/Indiana/Tell_City",
+ "America/Indiana/Knox",
+ "America/Menominee",
+ "America/North_Dakota/Center",
+ "America/North_Dakota/New_Salem",
+ "America/North_Dakota/Beulah",
+ "America/Denver",
+ "America/Boise",
+ "America/Phoenix",
+ "America/Los_Angeles",
+ "America/Anchorage",
+ "America/Juneau",
+ "America/Sitka",
+ "America/Metlakatla",
+ "America/Yakutat",
+ "America/Nome",
+ "America/Adak",
+ "Pacific/Honolulu"
+ ],
+ "is_in_european_union": false,
+ "fips": "US",
+ "geonames_id": 6252001,
+ "hasc_id": "US",
+ "wikidata_id": "Q30"
+ },
+ "city": {
+ "fips": "644000",
+ "alpha2": null,
+ "geonames_id": 5368753,
+ "hasc_id": null,
+ "wikidata_id": "Q65",
+ "name": "Los Angeles",
+ "name_translated": "Los Angeles"
+ },
+ "region": {
+ "fips": "US06",
+ "alpha2": "US-CA",
+ "geonames_id": 5332921,
+ "hasc_id": "US.CA",
+ "wikidata_id": "Q99",
+ "name": "California",
+ "name_translated": "California"
+ }
+ },
+ "tlds": [
+ ".us"
+ ],
+ "timezone": {
+ "id": "America/Los_Angeles",
+ "current_time": "2023-05-04T04:30:28-07:00",
+ "code": "PDT",
+ "is_daylight_saving": true,
+ "gmt_offset": -25200
+ },
+ "security": {
+ "is_anonymous": false,
+ "is_datacenter": false,
+ "is_vpn": false,
+ "is_bot": false,
+ "is_abuser": true,
+ "is_known_attacker": true,
+ "is_proxy": false,
+ "is_spam": false,
+ "is_tor": false,
+ "is_icloud_relay": false,
+ "threat_score": 100
+ },
+ "domains": {
+ "count": 10943,
+ "domains": [
+ "eliwise.academy",
+ "accountingprose.academy",
+ "pistola.academy",
+ "1and1-test-ntlds-fr.accountant",
+ "omnergy.africa"
+ ]
+ }
+ }
+}
+"""
+}
+
+
+class TestIpbase(unittest.TestCase):
+ def test_info(self,):
+ "test the json data extraction"
+
+ params = {
+ "ip": "1.1.1.1",
+ "apikey": "aaa",
+ "hostname": True,
+ "language": "de",
+ }
+ module = Mock()
+ module.params = params
+
+ data = json.loads(IPBASE_DATA['response'].decode("utf-8"))
+
+ IpbaseInfo._get_url_data = Mock()
+ IpbaseInfo._get_url_data.return_value = data
+ jenkins_plugin = IpbaseInfo(module)
+
+ json_data = jenkins_plugin.info()
+
+ self.maxDiff = None
+ self.assertDictEqual(json_data, data)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_jenkins_build.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_jenkins_build.py
index 44c6307ac..d9013a018 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_jenkins_build.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_jenkins_build.py
@@ -75,6 +75,11 @@ class JenkinsMock():
def get_build_info(self, name, build_number):
if name == "host-delete":
raise jenkins.JenkinsException("job {0} number {1} does not exist".format(name, build_number))
+ elif name == "create-detached":
+ return {
+ "building": True,
+ "result": None
+ }
return {
"building": True,
"result": "SUCCESS"
@@ -222,3 +227,38 @@ class TestJenkinsBuild(unittest.TestCase):
"token": "xyz"
})
jenkins_build.main()
+
+ @patch('ansible_collections.community.general.plugins.modules.jenkins_build.test_dependencies')
+ @patch('ansible_collections.community.general.plugins.modules.jenkins_build.JenkinsBuild.get_jenkins_connection')
+ @patch('ansible_collections.community.general.plugins.modules.jenkins_build.JenkinsBuild.get_build_status')
+ def test_module_create_build_without_detach(self, build_status, jenkins_connection, test_deps):
+ test_deps.return_value = None
+ jenkins_connection.return_value = JenkinsMock()
+ build_status.return_value = JenkinsBuildMock().get_build_status()
+
+ with self.assertRaises(AnsibleExitJson) as return_json:
+ set_module_args({
+ "name": "create-detached",
+ "user": "abc",
+ "token": "xyz"
+ })
+ jenkins_build.main()
+
+ self.assertFalse(return_json.exception.args[0]['changed'])
+
+ @patch('ansible_collections.community.general.plugins.modules.jenkins_build.test_dependencies')
+ @patch('ansible_collections.community.general.plugins.modules.jenkins_build.JenkinsBuild.get_jenkins_connection')
+ def test_module_create_build_detached(self, jenkins_connection, test_deps):
+ test_deps.return_value = None
+ jenkins_connection.return_value = JenkinsMock()
+
+ with self.assertRaises(AnsibleExitJson) as return_json:
+ set_module_args({
+ "name": "create-detached",
+ "user": "abc",
+ "token": "xyz",
+ "detach": True
+ })
+ jenkins_build.main()
+
+ self.assertTrue(return_json.exception.args[0]['changed'])
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_jenkins_build_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_jenkins_build_info.py
new file mode 100644
index 000000000..b5d4126fe
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_jenkins_build_info.py
@@ -0,0 +1,180 @@
+# Copyright (c) Ansible project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from ansible_collections.community.general.tests.unit.compat import unittest
+from ansible_collections.community.general.tests.unit.compat.mock import patch
+from ansible.module_utils import basic
+from ansible.module_utils.common.text.converters import to_bytes
+from ansible_collections.community.general.plugins.modules import jenkins_build_info
+
+import json
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
+ basic._ANSIBLE_ARGS = to_bytes(args)
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+ pass
+
+
+def exit_json(*args, **kwargs):
+ """function to patch over exit_json; package return data into an exception"""
+ if 'changed' not in kwargs:
+ kwargs['changed'] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs):
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs['failed'] = True
+ raise AnsibleFailJson(kwargs)
+
+
+class jenkins:
+ class JenkinsException(Exception):
+ pass
+
+
+class JenkinsBuildMock():
+ def __init__(self, name, build_number=None):
+ self.name = name
+ self.build_number = build_number
+
+ def get_build_status(self):
+ try:
+ instance = JenkinsMock()
+ response = JenkinsMock.get_build_info(instance, self.name, self.build_number)
+ return response
+ except jenkins.JenkinsException:
+ response = {}
+ response["result"] = "ABSENT"
+ return response
+ except Exception as e:
+ fail_json(msg='Unable to fetch build information, {0}'.format(e))
+
+
+class JenkinsMock():
+
+ def get_build_info(self, name, build_number):
+ if name == "job-absent":
+ raise jenkins.JenkinsException()
+
+ return {
+ "result": "SUCCESS",
+ "build_info": {}
+ }
+
+ def get_job_info(self, name):
+ if name == "job-absent":
+ raise jenkins.JenkinsException()
+
+ return {
+ "lastBuild": {
+ "number": 123
+ }
+ }
+
+
+class TestJenkinsBuildInfo(unittest.TestCase):
+
+ def setUp(self):
+ self.mock_module_helper = patch.multiple(basic.AnsibleModule,
+ exit_json=exit_json,
+ fail_json=fail_json)
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+
+ @patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.test_dependencies')
+ def test_module_fail_when_required_args_missing(self, test_deps):
+ test_deps.return_value = None
+ with self.assertRaises(AnsibleFailJson):
+ set_module_args({})
+ jenkins_build_info.main()
+
+ @patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.test_dependencies')
+ @patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.JenkinsBuildInfo.get_jenkins_connection')
+ def test_module_get_build_info(self, jenkins_connection, test_deps):
+ test_deps.return_value = None
+ jenkins_connection.return_value = JenkinsMock()
+
+ with self.assertRaises(AnsibleExitJson) as return_json:
+ set_module_args({
+ "name": "job-present",
+ "user": "abc",
+ "token": "xyz",
+ "build_number": 30
+ })
+ jenkins_build_info.main()
+
+ self.assertFalse(return_json.exception.args[0]["changed"])
+
+ @patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.test_dependencies')
+ @patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.JenkinsBuildInfo.get_jenkins_connection')
+ @patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.JenkinsBuildInfo.get_build_status')
+ def test_module_get_build_info_if_build_does_not_exist(self, build_status, jenkins_connection, test_deps):
+ test_deps.return_value = None
+ jenkins_connection.return_value = JenkinsMock()
+ build_status.return_value = JenkinsBuildMock("job-absent", 30).get_build_status()
+
+ with self.assertRaises(AnsibleExitJson) as return_json:
+ set_module_args({
+ "name": "job-absent",
+ "user": "abc",
+ "token": "xyz",
+ "build_number": 30
+ })
+ jenkins_build_info.main()
+
+ self.assertFalse(return_json.exception.args[0]['changed'])
+ self.assertTrue(return_json.exception.args[0]['failed'])
+ self.assertEqual("ABSENT", return_json.exception.args[0]['build_info']['result'])
+
+ @patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.test_dependencies')
+ @patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.JenkinsBuildInfo.get_jenkins_connection')
+ def test_module_get_build_info_get_last_build(self, jenkins_connection, test_deps):
+ test_deps.return_value = None
+ jenkins_connection.return_value = JenkinsMock()
+
+ with self.assertRaises(AnsibleExitJson) as return_json:
+ set_module_args({
+ "name": "job-present",
+ "user": "abc",
+ "token": "xyz"
+ })
+ jenkins_build_info.main()
+
+ self.assertFalse(return_json.exception.args[0]['changed'])
+ self.assertEqual("SUCCESS", return_json.exception.args[0]['build_info']['result'])
+
+ @patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.test_dependencies')
+ @patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.JenkinsBuildInfo.get_jenkins_connection')
+ @patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.JenkinsBuildInfo.get_build_status')
+ def test_module_get_build_info_if_job_does_not_exist(self, build_status, jenkins_connection, test_deps):
+ test_deps.return_value = None
+ jenkins_connection.return_value = JenkinsMock()
+ build_status.return_value = JenkinsBuildMock("job-absent").get_build_status()
+
+ with self.assertRaises(AnsibleExitJson) as return_json:
+ set_module_args({
+ "name": "job-absent",
+ "user": "abc",
+ "token": "xyz"
+ })
+ jenkins_build_info.main()
+
+ self.assertFalse(return_json.exception.args[0]['changed'])
+ self.assertTrue(return_json.exception.args[0]['failed'])
+ self.assertEqual("ABSENT", return_json.exception.args[0]['build_info']['result'])
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_authentication_required_actions.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_authentication_required_actions.py
new file mode 100644
index 000000000..2adc3a896
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_authentication_required_actions.py
@@ -0,0 +1,835 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021, Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+from contextlib import contextmanager
+
+from ansible_collections.community.general.tests.unit.compat import unittest
+from ansible_collections.community.general.tests.unit.compat.mock import patch
+from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, ModuleTestCase, set_module_args
+
+from ansible_collections.community.general.plugins.modules import keycloak_authentication_required_actions
+
+from itertools import count
+
+from ansible.module_utils.six import StringIO
+
+
+@contextmanager
+def patch_keycloak_api(
+ get_required_actions=None,
+ register_required_action=None,
+ update_required_action=None,
+ delete_required_action=None,
+):
+ """
+ Mock context manager for patching the methods in PwPolicyIPAClient that contact the IPA server
+
+ Patches the `login` and `_post_json` methods
+
+ Keyword arguments are passed to the mock object that patches `_post_json`
+
+ No arguments are passed to the mock object that patches `login` because no tests require it
+
+ Example::
+
+ with patch_ipa(return_value={}) as (mock_login, mock_post):
+ ...
+ """
+
+ obj = keycloak_authentication_required_actions.KeycloakAPI
+ with patch.object(
+ obj,
+ 'get_required_actions',
+ side_effect=get_required_actions
+ ) as mock_get_required_actions:
+ with patch.object(
+ obj,
+ 'register_required_action',
+ side_effect=register_required_action
+ ) as mock_register_required_action:
+ with patch.object(
+ obj,
+ 'update_required_action',
+ side_effect=update_required_action
+ ) as mock_update_required_action:
+ with patch.object(
+ obj,
+ 'delete_required_action',
+ side_effect=delete_required_action
+ ) as mock_delete_required_action:
+ yield (
+ mock_get_required_actions,
+ mock_register_required_action,
+ mock_update_required_action,
+ mock_delete_required_action,
+ )
+
+
+def get_response(object_with_future_response, method, get_id_call_count):
+ if callable(object_with_future_response):
+ return object_with_future_response()
+ if isinstance(object_with_future_response, dict):
+ return get_response(
+ object_with_future_response[method], method, get_id_call_count)
+ if isinstance(object_with_future_response, list):
+ call_number = next(get_id_call_count)
+ return get_response(
+ object_with_future_response[call_number], method, get_id_call_count)
+ return object_with_future_response
+
+
+def build_mocked_request(get_id_user_count, response_dict):
+ def _mocked_requests(*args, **kwargs):
+ url = args[0]
+ method = kwargs['method']
+ future_response = response_dict.get(url, None)
+ return get_response(future_response, method, get_id_user_count)
+ return _mocked_requests
+
+
+def create_wrapper(text_as_string):
+ """Allow to mock many times a call to one address.
+ Without this function, the StringIO is empty for the second call.
+ """
+ def _create_wrapper():
+ return StringIO(text_as_string)
+ return _create_wrapper
+
+
+def mock_good_connection():
+ token_response = {
+ 'http://keycloak.url/auth/realms/master/protocol/openid-connect/token': create_wrapper('{"access_token": "alongtoken"}'), }
+ return patch(
+ 'ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak.open_url',
+ side_effect=build_mocked_request(count(), token_response),
+ autospec=True
+ )
+
+
+class TestKeycloakAuthentication(ModuleTestCase):
+ def setUp(self):
+ super(TestKeycloakAuthentication, self).setUp()
+ self.module = keycloak_authentication_required_actions
+
+ def test_register_required_action(self):
+ """Register a new authentication required action."""
+
+ module_args = {
+ 'auth_client_id': 'admin-cli',
+ 'auth_keycloak_url': 'http://keycloak.url/auth',
+ 'auth_password': 'admin',
+ 'auth_realm': 'master',
+ 'auth_username': 'admin',
+ 'realm': 'master',
+ 'required_actions': [
+ {
+ 'alias': 'test-provider-id',
+ 'name': 'Test provider ID',
+ 'providerId': 'test-provider-id',
+ },
+ {
+ 'alias': 'test-provider-id',
+ 'name': 'Test provider ID (DUPLICATE ALIAS)',
+ 'providerId': 'test-provider-id',
+ },
+ {
+ 'alias': 'test-provider-id',
+ 'name': 'Test provider ID (DIFFERENT PROVIDER ID)',
+ 'providerId': 'test-provider-id-diff',
+ },
+ ],
+ 'state': 'present',
+ }
+
+ return_value_required_actions = [
+ [
+ {
+ 'alias': 'CONFIGURE_TOTP',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Configure OTP',
+ 'priority': 10,
+ 'providerId': 'CONFIGURE_TOTP'
+ },
+ {
+ 'alias': 'TERMS_AND_CONDITIONS',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Terms and conditions',
+ 'priority': 20,
+ 'providerId': 'TERMS_AND_CONDITIONS'
+ },
+ {
+ 'alias': 'UPDATE_PASSWORD',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Update Password',
+ 'priority': 30,
+ 'providerId': 'UPDATE_PASSWORD'
+ },
+ {
+ 'alias': 'UPDATE_PROFILE',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Update Profile',
+ 'priority': 40,
+ 'providerId': 'UPDATE_PROFILE'
+ },
+ {
+ 'alias': 'VERIFY_EMAIL',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Verify Email',
+ 'priority': 50,
+ 'providerId': 'VERIFY_EMAIL'
+ },
+ {
+ 'alias': 'delete_account',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': False,
+ 'name': 'Delete Account',
+ 'priority': 60,
+ 'providerId': 'delete_account'
+ },
+ {
+ 'alias': 'webauthn-register',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Webauthn Register',
+ 'priority': 70,
+ 'providerId': 'webauthn-register'
+ },
+ {
+ 'alias': 'webauthn-register-passwordless',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Webauthn Register Passwordless',
+ 'priority': 80,
+ 'providerId': 'webauthn-register-passwordless'
+ },
+ {
+ 'alias': 'update_user_locale',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Update User Locale',
+ 'priority': 1000,
+ 'providerId': 'update_user_locale'
+ }
+ ],
+ ]
+
+ changed = True
+
+ set_module_args(module_args)
+
+ # Run the module
+ with mock_good_connection():
+ with patch_keycloak_api(
+ get_required_actions=return_value_required_actions,
+ ) as (
+ mock_get_required_actions,
+ mock_register_required_action,
+ mock_update_required_action,
+ mock_delete_required_action,
+ ):
+ with self.assertRaises(AnsibleExitJson) as exec_info:
+ self.module.main()
+
+ # Verify number of call on each mock
+ self.assertEqual(len(mock_get_required_actions.mock_calls), 1)
+ self.assertEqual(len(mock_update_required_action.mock_calls), 1)
+ self.assertEqual(len(mock_register_required_action.mock_calls), 1)
+ self.assertEqual(len(mock_delete_required_action.mock_calls), 0)
+
+ # Verify that the module's changed status matches what is expected
+ self.assertIs(exec_info.exception.args[0]['changed'], changed)
+
+ def test_register_required_action_idempotency(self):
+ """Register an already existing new authentication required action again."""
+
+ module_args = {
+ 'auth_client_id': 'admin-cli',
+ 'auth_keycloak_url': 'http://keycloak.url/auth',
+ 'auth_password': 'admin',
+ 'auth_realm': 'master',
+ 'auth_username': 'admin',
+ 'realm': 'master',
+ 'required_actions': [
+ {
+ 'alias': 'test-provider-id',
+ 'name': 'Test provider ID',
+ 'providerId': 'test-provider-id',
+ },
+ {
+ 'alias': 'test-provider-id',
+ 'name': 'Test provider ID (DUPLICATE ALIAS)',
+ 'providerId': 'test-provider-id',
+ },
+ {
+ 'alias': 'test-provider-id',
+ 'name': 'Test provider ID (DIFFERENT PROVIDER ID)',
+ 'providerId': 'test-provider-id-diff',
+ },
+ ],
+ 'state': 'present',
+ }
+
+ return_value_required_actions = [
+ [
+ {
+ 'alias': 'CONFIGURE_TOTP',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Configure OTP',
+ 'priority': 10,
+ 'providerId': 'CONFIGURE_TOTP'
+ },
+ {
+ 'alias': 'TERMS_AND_CONDITIONS',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Terms and conditions',
+ 'priority': 20,
+ 'providerId': 'TERMS_AND_CONDITIONS'
+ },
+ {
+ 'alias': 'UPDATE_PASSWORD',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Update Password',
+ 'priority': 30,
+ 'providerId': 'UPDATE_PASSWORD'
+ },
+ {
+ 'alias': 'UPDATE_PROFILE',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Update Profile',
+ 'priority': 40,
+ 'providerId': 'UPDATE_PROFILE'
+ },
+ {
+ 'alias': 'VERIFY_EMAIL',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Verify Email',
+ 'priority': 50,
+ 'providerId': 'VERIFY_EMAIL'
+ },
+ {
+ 'alias': 'delete_account',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': False,
+ 'name': 'Delete Account',
+ 'priority': 60,
+ 'providerId': 'delete_account'
+ },
+ {
+ 'alias': 'webauthn-register',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Webauthn Register',
+ 'priority': 70,
+ 'providerId': 'webauthn-register'
+ },
+ {
+ 'alias': 'webauthn-register-passwordless',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Webauthn Register Passwordless',
+ 'priority': 80,
+ 'providerId': 'webauthn-register-passwordless'
+ },
+ {
+ 'alias': 'test-provider-id',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': False,
+ 'name': 'Test provider ID',
+ 'priority': 90,
+ 'providerId': 'test-provider-id'
+ },
+ {
+ 'alias': 'update_user_locale',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Update User Locale',
+ 'priority': 1000,
+ 'providerId': 'update_user_locale'
+ }
+ ],
+ ]
+
+ changed = False
+
+ set_module_args(module_args)
+
+ # Run the module
+ with mock_good_connection():
+ with patch_keycloak_api(
+ get_required_actions=return_value_required_actions,
+ ) as (
+ mock_get_required_actions,
+ mock_register_required_action,
+ mock_update_required_action,
+ mock_delete_required_action,
+ ):
+ with self.assertRaises(AnsibleExitJson) as exec_info:
+ self.module.main()
+
+ # Verify number of call on each mock
+ self.assertEqual(len(mock_get_required_actions.mock_calls), 1)
+ self.assertEqual(len(mock_update_required_action.mock_calls), 0)
+ self.assertEqual(len(mock_register_required_action.mock_calls), 0)
+ self.assertEqual(len(mock_delete_required_action.mock_calls), 0)
+
+ # Verify that the module's changed status matches what is expected
+ self.assertIs(exec_info.exception.args[0]['changed'], changed)
+
+ def test_update_required_actions(self):
+ """Update an authentication required action."""
+
+ module_args = {
+ 'auth_client_id': 'admin-cli',
+ 'auth_keycloak_url': 'http://keycloak.url/auth',
+ 'auth_password': 'admin',
+ 'auth_realm': 'master',
+ 'auth_username': 'admin',
+ 'realm': 'master',
+ 'required_actions': [
+ {
+ 'alias': 'test-provider-id',
+ 'name': 'Test provider ID UPDATED',
+ 'providerId': 'test-provider-id',
+ },
+ {
+ 'alias': 'test-provider-id',
+ 'name': 'Test provider ID UPDATED (DUPLICATE ALIAS)',
+ 'providerId': 'test-provider-id',
+ },
+ {
+ 'alias': 'test-provider-id',
+ 'name': 'Test provider ID UPDATED (DIFFERENT PROVIDER ID)',
+ 'providerId': 'test-provider-id-diff',
+ },
+ ],
+ 'state': 'present',
+ }
+
+ return_value_required_actions = [
+ [
+ {
+ 'alias': 'CONFIGURE_TOTP',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Configure OTP',
+ 'priority': 10,
+ 'providerId': 'CONFIGURE_TOTP'
+ },
+ {
+ 'alias': 'TERMS_AND_CONDITIONS',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Terms and conditions',
+ 'priority': 20,
+ 'providerId': 'TERMS_AND_CONDITIONS'
+ },
+ {
+ 'alias': 'UPDATE_PASSWORD',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Update Password',
+ 'priority': 30,
+ 'providerId': 'UPDATE_PASSWORD'
+ },
+ {
+ 'alias': 'UPDATE_PROFILE',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Update Profile',
+ 'priority': 40,
+ 'providerId': 'UPDATE_PROFILE'
+ },
+ {
+ 'alias': 'VERIFY_EMAIL',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Verify Email',
+ 'priority': 50,
+ 'providerId': 'VERIFY_EMAIL'
+ },
+ {
+ 'alias': 'delete_account',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': False,
+ 'name': 'Delete Account',
+ 'priority': 60,
+ 'providerId': 'delete_account'
+ },
+ {
+ 'alias': 'webauthn-register',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Webauthn Register',
+ 'priority': 70,
+ 'providerId': 'webauthn-register'
+ },
+ {
+ 'alias': 'webauthn-register-passwordless',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Webauthn Register Passwordless',
+ 'priority': 80,
+ 'providerId': 'webauthn-register-passwordless'
+ },
+ {
+ 'alias': 'test-provider-id',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': False,
+ 'name': 'Test provider ID',
+ 'priority': 90,
+ 'providerId': 'test-provider-id'
+ },
+ {
+ 'alias': 'update_user_locale',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Update User Locale',
+ 'priority': 1000,
+ 'providerId': 'update_user_locale'
+ }
+ ],
+ ]
+
+ changed = True
+
+ set_module_args(module_args)
+
+ # Run the module
+ with mock_good_connection():
+ with patch_keycloak_api(
+ get_required_actions=return_value_required_actions,
+ ) as (
+ mock_get_required_actions,
+ mock_register_required_action,
+ mock_update_required_action,
+ mock_delete_required_action,
+ ):
+ with self.assertRaises(AnsibleExitJson) as exec_info:
+ self.module.main()
+
+ # Verify number of call on each mock
+ self.assertEqual(len(mock_get_required_actions.mock_calls), 1)
+ self.assertEqual(len(mock_update_required_action.mock_calls), 1)
+ self.assertEqual(len(mock_register_required_action.mock_calls), 0)
+ self.assertEqual(len(mock_delete_required_action.mock_calls), 0)
+
+ # Verify that the module's changed status matches what is expected
+ self.assertIs(exec_info.exception.args[0]['changed'], changed)
+
+ def test_delete_required_action(self):
+ """Delete a registered authentication required action."""
+
+ module_args = {
+ 'auth_client_id': 'admin-cli',
+ 'auth_keycloak_url': 'http://keycloak.url/auth',
+ 'auth_password': 'admin',
+ 'auth_realm': 'master',
+ 'auth_username': 'admin',
+ 'realm': 'master',
+ 'required_actions': [
+ {
+ 'alias': 'test-provider-id',
+ },
+ ],
+ 'state': 'absent',
+ }
+
+ return_value_required_actions = [
+ [
+ {
+ 'alias': 'CONFIGURE_TOTP',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Configure OTP',
+ 'priority': 10,
+ 'providerId': 'CONFIGURE_TOTP'
+ },
+ {
+ 'alias': 'TERMS_AND_CONDITIONS',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Terms and conditions',
+ 'priority': 20,
+ 'providerId': 'TERMS_AND_CONDITIONS'
+ },
+ {
+ 'alias': 'UPDATE_PASSWORD',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Update Password',
+ 'priority': 30,
+ 'providerId': 'UPDATE_PASSWORD'
+ },
+ {
+ 'alias': 'UPDATE_PROFILE',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Update Profile',
+ 'priority': 40,
+ 'providerId': 'UPDATE_PROFILE'
+ },
+ {
+ 'alias': 'VERIFY_EMAIL',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Verify Email',
+ 'priority': 50,
+ 'providerId': 'VERIFY_EMAIL'
+ },
+ {
+ 'alias': 'delete_account',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': False,
+ 'name': 'Delete Account',
+ 'priority': 60,
+ 'providerId': 'delete_account'
+ },
+ {
+ 'alias': 'webauthn-register',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Webauthn Register',
+ 'priority': 70,
+ 'providerId': 'webauthn-register'
+ },
+ {
+ 'alias': 'webauthn-register-passwordless',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Webauthn Register Passwordless',
+ 'priority': 80,
+ 'providerId': 'webauthn-register-passwordless'
+ },
+ {
+ 'alias': 'test-provider-id',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': False,
+ 'name': 'Test provider ID',
+ 'priority': 90,
+ 'providerId': 'test-provider-id'
+ },
+ {
+ 'alias': 'update_user_locale',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Update User Locale',
+ 'priority': 1000,
+ 'providerId': 'update_user_locale'
+ }
+ ],
+ ]
+
+ changed = True
+
+ set_module_args(module_args)
+
+ # Run the module
+ with mock_good_connection():
+ with patch_keycloak_api(
+ get_required_actions=return_value_required_actions,
+ ) as (
+ mock_get_required_actions,
+ mock_register_required_action,
+ mock_update_required_action,
+ mock_delete_required_action,
+ ):
+ with self.assertRaises(AnsibleExitJson) as exec_info:
+ self.module.main()
+
+ # Verify number of call on each mock
+ self.assertEqual(len(mock_get_required_actions.mock_calls), 1)
+ self.assertEqual(len(mock_update_required_action.mock_calls), 0)
+ self.assertEqual(len(mock_register_required_action.mock_calls), 0)
+ self.assertEqual(len(mock_delete_required_action.mock_calls), 1)
+
+ # Verify that the module's changed status matches what is expected
+ self.assertIs(exec_info.exception.args[0]['changed'], changed)
+
+ def test_delete_required_action_idempotency(self):
+ """Delete an already deleted authentication required action."""
+
+ module_args = {
+ 'auth_client_id': 'admin-cli',
+ 'auth_keycloak_url': 'http://keycloak.url/auth',
+ 'auth_password': 'admin',
+ 'auth_realm': 'master',
+ 'auth_username': 'admin',
+ 'realm': 'master',
+ 'required_actions': [
+ {
+ 'alias': 'test-provider-id',
+ },
+ ],
+ 'state': 'absent',
+ }
+
+ return_value_required_actions = [
+ [
+ {
+ 'alias': 'CONFIGURE_TOTP',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Configure OTP',
+ 'priority': 10,
+ 'providerId': 'CONFIGURE_TOTP'
+ },
+ {
+ 'alias': 'TERMS_AND_CONDITIONS',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Terms and conditions',
+ 'priority': 20,
+ 'providerId': 'TERMS_AND_CONDITIONS'
+ },
+ {
+ 'alias': 'UPDATE_PASSWORD',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Update Password',
+ 'priority': 30,
+ 'providerId': 'UPDATE_PASSWORD'
+ },
+ {
+ 'alias': 'UPDATE_PROFILE',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Update Profile',
+ 'priority': 40,
+ 'providerId': 'UPDATE_PROFILE'
+ },
+ {
+ 'alias': 'VERIFY_EMAIL',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Verify Email',
+ 'priority': 50,
+ 'providerId': 'VERIFY_EMAIL'
+ },
+ {
+ 'alias': 'delete_account',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': False,
+ 'name': 'Delete Account',
+ 'priority': 60,
+ 'providerId': 'delete_account'
+ },
+ {
+ 'alias': 'webauthn-register',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Webauthn Register',
+ 'priority': 70,
+ 'providerId': 'webauthn-register'
+ },
+ {
+ 'alias': 'webauthn-register-passwordless',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Webauthn Register Passwordless',
+ 'priority': 80,
+ 'providerId': 'webauthn-register-passwordless'
+ },
+ {
+ 'alias': 'update_user_locale',
+ 'config': {},
+ 'defaultAction': False,
+ 'enabled': True,
+ 'name': 'Update User Locale',
+ 'priority': 1000,
+ 'providerId': 'update_user_locale'
+ }
+ ],
+ ]
+
+ changed = False
+
+ set_module_args(module_args)
+
+ # Run the module
+ with mock_good_connection():
+ with patch_keycloak_api(
+ get_required_actions=return_value_required_actions,
+ ) as (
+ mock_get_required_actions,
+ mock_register_required_action,
+ mock_update_required_action,
+ mock_delete_required_action,
+ ):
+ with self.assertRaises(AnsibleExitJson) as exec_info:
+ self.module.main()
+
+ # Verify number of call on each mock
+ self.assertEqual(len(mock_get_required_actions.mock_calls), 1)
+ self.assertEqual(len(mock_update_required_action.mock_calls), 0)
+ self.assertEqual(len(mock_register_required_action.mock_calls), 0)
+ self.assertEqual(len(mock_delete_required_action.mock_calls), 0)
+
+ # Verify that the module's changed status matches what is expected
+ self.assertIs(exec_info.exception.args[0]['changed'], changed)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_client_rolemapping.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_client_rolemapping.py
index 58c8b9548..359e6304e 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_client_rolemapping.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_client_rolemapping.py
@@ -120,6 +120,11 @@ class TestKeycloakRealm(ModuleTestCase):
'state': 'present',
'client_id': 'test_client',
'group_name': 'test_group',
+ 'parents': [
+ {
+ 'name': 'parent_group'
+ }
+ ],
'roles': [
{
'name': 'test_role1',
@@ -139,7 +144,7 @@ class TestKeycloakRealm(ModuleTestCase):
"clientRoles": "{}",
"id": "92f2400e-0ecb-4185-8950-12dcef616c2b",
"name": "test_group",
- "path": "/test_group",
+ "path": "/parent_group/test_group",
"realmRoles": "[]",
"subGroups": "[]"
}]
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_role.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_role.py
index c48c9771a..cc2f6e716 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_role.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_role.py
@@ -21,7 +21,9 @@ from ansible.module_utils.six import StringIO
@contextmanager
-def patch_keycloak_api(get_realm_role, create_realm_role=None, update_realm_role=None, delete_realm_role=None):
+def patch_keycloak_api(get_realm_role=None, create_realm_role=None, update_realm_role=None, delete_realm_role=None,
+ get_client_role=None, create_client_role=None, update_client_role=None, delete_client_role=None,
+ get_client_by_id=None, get_role_composites=None):
"""Mock context manager for patching the methods in PwPolicyIPAClient that contact the IPA server
Patches the `login` and `_post_json` methods
@@ -41,7 +43,15 @@ def patch_keycloak_api(get_realm_role, create_realm_role=None, update_realm_role
with patch.object(obj, 'create_realm_role', side_effect=create_realm_role) as mock_create_realm_role:
with patch.object(obj, 'update_realm_role', side_effect=update_realm_role) as mock_update_realm_role:
with patch.object(obj, 'delete_realm_role', side_effect=delete_realm_role) as mock_delete_realm_role:
- yield mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role
+ with patch.object(obj, 'get_client_role', side_effect=get_client_role) as mock_get_client_role:
+ with patch.object(obj, 'create_client_role', side_effect=create_client_role) as mock_create_client_role:
+ with patch.object(obj, 'update_client_role', side_effect=update_client_role) as mock_update_client_role:
+ with patch.object(obj, 'delete_client_role', side_effect=delete_client_role) as mock_delete_client_role:
+ with patch.object(obj, 'get_client_by_id', side_effect=get_client_by_id) as mock_get_client_by_id:
+ with patch.object(obj, 'get_role_composites', side_effect=get_role_composites) as mock_get_role_composites:
+ yield mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role, \
+ mock_get_client_role, mock_create_client_role, mock_update_client_role, mock_delete_client_role, \
+ mock_get_client_by_id, mock_get_role_composites
def get_response(object_with_future_response, method, get_id_call_count):
@@ -125,7 +135,9 @@ class TestKeycloakRealmRole(ModuleTestCase):
with mock_good_connection():
with patch_keycloak_api(get_realm_role=return_value_absent, create_realm_role=return_value_created) \
- as (mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role):
+ as (mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role,
+ mock_get_client_role, mock_create_client_role, mock_update_client_role, mock_delete_client_role,
+ mock_get_client_by_client_id, mock_get_role_composites):
with self.assertRaises(AnsibleExitJson) as exec_info:
self.module.main()
@@ -179,7 +191,9 @@ class TestKeycloakRealmRole(ModuleTestCase):
with mock_good_connection():
with patch_keycloak_api(get_realm_role=return_value_present, update_realm_role=return_value_updated) \
- as (mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role):
+ as (mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role,
+ mock_get_client_role, mock_create_client_role, mock_update_client_role, mock_delete_client_role,
+ mock_get_client_by_client_id, mock_get_role_composites):
with self.assertRaises(AnsibleExitJson) as exec_info:
self.module.main()
@@ -233,7 +247,9 @@ class TestKeycloakRealmRole(ModuleTestCase):
with mock_good_connection():
with patch_keycloak_api(get_realm_role=return_value_present, update_realm_role=return_value_updated) \
- as (mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role):
+ as (mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role,
+ mock_get_client_role, mock_create_client_role, mock_update_client_role, mock_delete_client_role,
+ mock_get_client_by_client_id, mock_get_role_composites):
with self.assertRaises(AnsibleExitJson) as exec_info:
self.module.main()
@@ -244,6 +260,140 @@ class TestKeycloakRealmRole(ModuleTestCase):
# Verify that the module's changed status matches what is expected
self.assertIs(exec_info.exception.args[0]['changed'], changed)
+ def test_create_with_composites_when_present_no_change(self):
+ """Update without change a realm role"""
+
+ module_args = {
+ 'auth_keycloak_url': 'http://keycloak.url/auth',
+ 'auth_password': 'admin',
+ 'auth_realm': 'master',
+ 'auth_username': 'admin',
+ 'auth_client_id': 'admin-cli',
+ 'validate_certs': True,
+ 'realm': 'realm-name',
+ 'name': 'role-name',
+ 'description': 'role-description',
+ 'composite': True,
+ 'composites': [
+ {
+ 'client_id': 'client_1',
+ 'name': 'client-role1'
+ },
+ {
+ 'name': 'realm-role-1'
+ }
+ ]
+
+ }
+ return_value_present = [
+ {
+ "attributes": {},
+ "clientRole": False,
+ "composite": True,
+ "containerId": "realm-name",
+ "description": "role-description",
+ "id": "90f1cdb6-be88-496e-89c6-da1fb6bc6966",
+ "name": "role-name",
+ },
+ {
+ "attributes": {},
+ "clientRole": False,
+ "composite": True,
+ "containerId": "realm-name",
+ "description": "role-description",
+ "id": "90f1cdb6-be88-496e-89c6-da1fb6bc6966",
+ "name": "role-name",
+ }
+ ]
+ return_value_updated = [None]
+ return_get_role_composites = [
+ [
+ {
+ 'clientRole': True,
+ 'containerId': 'c4367fac-f427-11ed-8e2f-aff070d20f0e',
+ 'name': 'client-role1'
+ },
+ {
+ 'clientRole': False,
+ 'containerId': 'realm-name',
+ 'name': 'realm-role-1'
+ }
+ ]
+ ]
+ return_get_client_by_client_id = [
+ {
+ "id": "de152444-f126-4a7a-8273-4ee1544133ad",
+ "clientId": "client_1",
+ "name": "client_1",
+ "description": "client_1",
+ "surrogateAuthRequired": False,
+ "enabled": True,
+ "alwaysDisplayInConsole": False,
+ "clientAuthenticatorType": "client-secret",
+ "redirectUris": [
+ "http://localhost:8080/*",
+ ],
+ "webOrigins": [
+ "*"
+ ],
+ "notBefore": 0,
+ "bearerOnly": False,
+ "consentRequired": False,
+ "standardFlowEnabled": True,
+ "implicitFlowEnabled": False,
+ "directAccessGrantsEnabled": False,
+ "serviceAccountsEnabled": False,
+ "publicClient": False,
+ "frontchannelLogout": False,
+ "protocol": "openid-connect",
+ "attributes": {
+ "backchannel.logout.session.required": "true",
+ "backchannel.logout.revoke.offline.tokens": "false"
+ },
+ "authenticationFlowBindingOverrides": {},
+ "fullScopeAllowed": True,
+ "nodeReRegistrationTimeout": -1,
+ "defaultClientScopes": [
+ "web-origins",
+ "acr",
+ "profile",
+ "roles",
+ "email"
+ ],
+ "optionalClientScopes": [
+ "address",
+ "phone",
+ "offline_access",
+ "microprofile-jwt"
+ ]
+ }
+ ]
+
+ changed = False
+
+ set_module_args(module_args)
+
+ # Run the module
+
+ with mock_good_connection():
+ with patch_keycloak_api(get_realm_role=return_value_present, update_realm_role=return_value_updated,
+ get_client_by_id=return_get_client_by_client_id,
+ get_role_composites=return_get_role_composites) \
+ as (mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role,
+ mock_get_client_role, mock_create_client_role, mock_update_client_role, mock_delete_client_role,
+ mock_get_client_by_client_id, mock_get_role_composites):
+ with self.assertRaises(AnsibleExitJson) as exec_info:
+ self.module.main()
+
+ self.assertEqual(len(mock_get_realm_role.mock_calls), 1)
+ self.assertEqual(len(mock_create_realm_role.mock_calls), 0)
+ self.assertEqual(len(mock_update_realm_role.mock_calls), 0)
+ self.assertEqual(len(mock_get_client_by_client_id.mock_calls), 1)
+ self.assertEqual(len(mock_get_role_composites.mock_calls), 1)
+
+ # Verify that the module's changed status matches what is expected
+ self.assertIs(exec_info.exception.args[0]['changed'], changed)
+
def test_delete_when_absent(self):
"""Remove an absent realm role"""
@@ -268,7 +418,9 @@ class TestKeycloakRealmRole(ModuleTestCase):
with mock_good_connection():
with patch_keycloak_api(get_realm_role=return_value_absent, delete_realm_role=return_value_deleted) \
- as (mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role):
+ as (mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role,
+ mock_get_client_role, mock_create_client_role, mock_update_client_role, mock_delete_client_role,
+ mock_get_client_by_client_id, mock_get_role_composites):
with self.assertRaises(AnsibleExitJson) as exec_info:
self.module.main()
@@ -312,7 +464,9 @@ class TestKeycloakRealmRole(ModuleTestCase):
with mock_good_connection():
with patch_keycloak_api(get_realm_role=return_value_absent, delete_realm_role=return_value_deleted) \
- as (mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role):
+ as (mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role,
+ mock_get_client_role, mock_create_client_role, mock_update_client_role, mock_delete_client_role,
+ mock_get_client_by_client_id, mock_get_role_composites):
with self.assertRaises(AnsibleExitJson) as exec_info:
self.module.main()
@@ -323,5 +477,207 @@ class TestKeycloakRealmRole(ModuleTestCase):
self.assertIs(exec_info.exception.args[0]['changed'], changed)
+class TestKeycloakClientRole(ModuleTestCase):
+ def setUp(self):
+ super(TestKeycloakClientRole, self).setUp()
+ self.module = keycloak_role
+
+ def test_create_client_role_with_composites_when_absent(self):
+ """Update with change a realm role"""
+
+ module_args = {
+ 'auth_keycloak_url': 'http://keycloak.url/auth',
+ 'auth_password': 'admin',
+ 'auth_realm': 'master',
+ 'auth_username': 'admin',
+ 'auth_client_id': 'admin-cli',
+ 'validate_certs': True,
+ 'realm': 'realm-name',
+ 'client_id': 'client-name',
+ 'name': 'role-name',
+ 'description': 'role-description',
+ 'composite': True,
+ 'composites': [
+ {
+ 'client_id': 'client_1',
+ 'name': 'client-role1'
+ },
+ {
+ 'name': 'realm-role-1'
+ }
+ ]
+ }
+ return_get_client_role = [
+ None,
+ {
+ "attributes": {},
+ "clientRole": True,
+ "composite": True,
+ "composites": [
+ {
+ 'client': {
+ 'client1': ['client-role1']
+ }
+ },
+ {
+ 'realm': ['realm-role-1']
+ }
+ ],
+ "containerId": "9ae25ec2-f40a-11ed-9261-b3bacf720f69",
+ "description": "role-description",
+ "id": "90f1cdb6-be88-496e-89c6-da1fb6bc6966",
+ "name": "role-name",
+ }
+ ]
+ changed = True
+
+ set_module_args(module_args)
+
+ # Run the module
+
+ with mock_good_connection():
+ with patch_keycloak_api(get_client_role=return_get_client_role) \
+ as (mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role,
+ mock_get_client_role, mock_create_client_role, mock_update_client_role, mock_delete_client_role,
+ mock_get_client_by_client_id, mock_get_role_composites):
+ with self.assertRaises(AnsibleExitJson) as exec_info:
+ self.module.main()
+
+ self.assertEqual(len(mock_get_realm_role.mock_calls), 0)
+ self.assertEqual(len(mock_create_realm_role.mock_calls), 0)
+ self.assertEqual(len(mock_update_realm_role.mock_calls), 0)
+ self.assertEqual(len(mock_get_client_role.mock_calls), 2)
+ self.assertEqual(len(mock_create_client_role.mock_calls), 1)
+ self.assertEqual(len(mock_update_client_role.mock_calls), 0)
+
+ # Verify that the module's changed status matches what is expected
+ self.assertIs(exec_info.exception.args[0]['changed'], changed)
+
+ def test_create_client_role_with_composites_when_present_no_change(self):
+ """Update with change a realm role"""
+
+ module_args = {
+ 'auth_keycloak_url': 'http://keycloak.url/auth',
+ 'auth_password': 'admin',
+ 'auth_realm': 'master',
+ 'auth_username': 'admin',
+ 'auth_client_id': 'admin-cli',
+ 'validate_certs': True,
+ 'realm': 'realm-name',
+ 'client_id': 'client-name',
+ 'name': 'role-name',
+ 'description': 'role-description',
+ 'composite': True,
+ 'composites': [
+ {
+ 'client_id': 'client_1',
+ 'name': 'client-role1'
+ },
+ {
+ 'name': 'realm-role-1'
+ }
+ ]
+ }
+ return_get_client_role = [
+ {
+ "attributes": {},
+ "clientRole": True,
+ "composite": True,
+ "containerId": "9ae25ec2-f40a-11ed-9261-b3bacf720f69",
+ "description": "role-description",
+ "id": "90f1cdb6-be88-496e-89c6-da1fb6bc6966",
+ "name": "role-name",
+ }
+ ]
+ return_get_role_composites = [
+ [
+ {
+ 'clientRole': True,
+ 'containerId': 'c4367fac-f427-11ed-8e2f-aff070d20f0e',
+ 'name': 'client-role1'
+ },
+ {
+ 'clientRole': False,
+ 'containerId': 'realm-name',
+ 'name': 'realm-role-1'
+ }
+ ]
+ ]
+ return_get_client_by_client_id = [
+ {
+ "id": "de152444-f126-4a7a-8273-4ee1544133ad",
+ "clientId": "client_1",
+ "name": "client_1",
+ "description": "client_1",
+ "surrogateAuthRequired": False,
+ "enabled": True,
+ "alwaysDisplayInConsole": False,
+ "clientAuthenticatorType": "client-secret",
+ "redirectUris": [
+ "http://localhost:8080/*",
+ ],
+ "webOrigins": [
+ "*"
+ ],
+ "notBefore": 0,
+ "bearerOnly": False,
+ "consentRequired": False,
+ "standardFlowEnabled": True,
+ "implicitFlowEnabled": False,
+ "directAccessGrantsEnabled": False,
+ "serviceAccountsEnabled": False,
+ "publicClient": False,
+ "frontchannelLogout": False,
+ "protocol": "openid-connect",
+ "attributes": {
+ "backchannel.logout.session.required": "true",
+ "backchannel.logout.revoke.offline.tokens": "false"
+ },
+ "authenticationFlowBindingOverrides": {},
+ "fullScopeAllowed": True,
+ "nodeReRegistrationTimeout": -1,
+ "defaultClientScopes": [
+ "web-origins",
+ "acr",
+ "profile",
+ "roles",
+ "email"
+ ],
+ "optionalClientScopes": [
+ "address",
+ "phone",
+ "offline_access",
+ "microprofile-jwt"
+ ]
+ }
+ ]
+ changed = False
+
+ set_module_args(module_args)
+
+ # Run the module
+
+ with mock_good_connection():
+ with patch_keycloak_api(get_client_role=return_get_client_role, get_client_by_id=return_get_client_by_client_id,
+ get_role_composites=return_get_role_composites) \
+ as (mock_get_realm_role, mock_create_realm_role, mock_update_realm_role, mock_delete_realm_role,
+ mock_get_client_role, mock_create_client_role, mock_update_client_role, mock_delete_client_role,
+ mock_get_client_by_client_id, mock_get_role_composites):
+ with self.assertRaises(AnsibleExitJson) as exec_info:
+ self.module.main()
+
+ self.assertEqual(len(mock_get_realm_role.mock_calls), 0)
+ self.assertEqual(len(mock_create_realm_role.mock_calls), 0)
+ self.assertEqual(len(mock_update_realm_role.mock_calls), 0)
+ self.assertEqual(len(mock_get_client_role.mock_calls), 1)
+ self.assertEqual(len(mock_create_client_role.mock_calls), 0)
+ self.assertEqual(len(mock_update_client_role.mock_calls), 0)
+ self.assertEqual(len(mock_get_client_by_client_id.mock_calls), 1)
+ self.assertEqual(len(mock_get_role_composites.mock_calls), 1)
+
+ # Verify that the module's changed status matches what is expected
+ self.assertIs(exec_info.exception.args[0]['changed'], changed)
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_user.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_user.py
new file mode 100644
index 000000000..26bc33d82
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_user.py
@@ -0,0 +1,354 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021, Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from contextlib import contextmanager
+
+from ansible_collections.community.general.tests.unit.compat import unittest
+from ansible_collections.community.general.tests.unit.compat.mock import patch
+from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, ModuleTestCase, set_module_args
+
+from ansible_collections.community.general.plugins.modules import keycloak_user
+
+from itertools import count
+
+from ansible.module_utils.six import StringIO
+
+
+@contextmanager
+def patch_keycloak_api(get_user_by_username=None,
+ create_user=None,
+ update_user_groups_membership=None,
+ get_user_groups=None,
+ delete_user=None,
+ update_user=None):
+ """Mock context manager for patching the methods in KeycloakAPI that contact the Keycloak server
+
+ Patches the `get_user_by_username` and `create_user` methods
+
+ """
+
+ obj = keycloak_user.KeycloakAPI
+ with patch.object(obj, 'get_user_by_username', side_effect=get_user_by_username) as mock_get_user_by_username:
+ with patch.object(obj, 'create_user', side_effect=create_user) as mock_create_user:
+ with patch.object(obj, 'update_user_groups_membership', side_effect=update_user_groups_membership) as mock_update_user_groups_membership:
+ with patch.object(obj, 'get_user_groups', side_effect=get_user_groups) as mock_get_user_groups:
+ with patch.object(obj, 'delete_user', side_effect=delete_user) as mock_delete_user:
+ with patch.object(obj, 'update_user', side_effect=update_user) as mock_update_user:
+ yield mock_get_user_by_username, mock_create_user, mock_update_user_groups_membership, \
+ mock_get_user_groups, mock_delete_user, mock_update_user
+
+
+def get_response(object_with_future_response, method, get_id_call_count):
+ if callable(object_with_future_response):
+ return object_with_future_response()
+ if isinstance(object_with_future_response, dict):
+ return get_response(
+ object_with_future_response[method], method, get_id_call_count)
+ if isinstance(object_with_future_response, list):
+ call_number = next(get_id_call_count)
+ return get_response(
+ object_with_future_response[call_number], method, get_id_call_count)
+ return object_with_future_response
+
+
+def build_mocked_request(get_id_user_count, response_dict):
+ def _mocked_requests(*args, **kwargs):
+ url = args[0]
+ method = kwargs['method']
+ future_response = response_dict.get(url, None)
+ return get_response(future_response, method, get_id_user_count)
+
+ return _mocked_requests
+
+
+def create_wrapper(text_as_string):
+ """Allow to mock many times a call to one address.
+ Without this function, the StringIO is empty for the second call.
+ """
+
+ def _create_wrapper():
+ return StringIO(text_as_string)
+
+ return _create_wrapper
+
+
+def mock_good_connection():
+ token_response = {
+ 'http://keycloak.url/auth/realms/master/protocol/openid-connect/token': create_wrapper(
+ '{"access_token": "alongtoken"}'), }
+ return patch(
+ 'ansible_collections.community.general.plugins.module_utils.identity.keycloak.keycloak.open_url',
+ side_effect=build_mocked_request(count(), token_response),
+ autospec=True
+ )
+
+
+class TestKeycloakUser(ModuleTestCase):
+ def setUp(self):
+ super(TestKeycloakUser, self).setUp()
+ self.module = keycloak_user
+
+ def test_add_new_user(self):
+ """Add a new user"""
+
+ module_args = {
+ 'auth_keycloak_url': 'https: // auth.example.com / auth',
+ 'token': '{{ access_token }}',
+ 'state': 'present',
+ 'realm': 'master',
+ 'username': 'test',
+ 'groups': []
+ }
+ return_value_get_user_by_username = [None]
+ return_value_update_user_groups_membership = [False]
+ return_get_user_groups = [[]]
+ return_create_user = [{'id': '123eqwdawer24qwdqw4'}]
+ return_delete_user = None
+ return_update_user = None
+ changed = True
+
+ set_module_args(module_args)
+
+ # Run the module
+
+ with mock_good_connection():
+ with patch_keycloak_api(get_user_by_username=return_value_get_user_by_username,
+ create_user=return_create_user,
+ update_user_groups_membership=return_value_update_user_groups_membership,
+ get_user_groups=return_get_user_groups,
+ update_user=return_update_user,
+ delete_user=return_delete_user) \
+ as (mock_get_user_by_username,
+ mock_create_user,
+ mock_update_user_groups_membership,
+ mock_get_user_groups,
+ mock_delete_user,
+ mock_update_user):
+ with self.assertRaises(AnsibleExitJson) as exec_info:
+ self.module.main()
+
+ self.assertEqual(mock_get_user_by_username.call_count, 1)
+ self.assertEqual(mock_create_user.call_count, 1)
+ self.assertEqual(mock_update_user_groups_membership.call_count, 1)
+ self.assertEqual(mock_get_user_groups.call_count, 1)
+ self.assertEqual(mock_update_user.call_count, 0)
+ self.assertEqual(mock_delete_user.call_count, 0)
+
+ # Verify that the module's changed status matches what is expected
+ self.assertIs(exec_info.exception.args[0]['changed'], changed)
+
+ def test_add_exiting_user_no_change(self):
+ """Add a new user"""
+
+ module_args = {
+ 'auth_keycloak_url': 'https: // auth.example.com / auth',
+ 'token': '{{ access_token }}',
+ 'state': 'present',
+ 'realm': 'master',
+ 'username': 'test',
+ 'groups': []
+ }
+ return_value_get_user_by_username = [
+ {
+ 'id': '123eqwdawer24qwdqw4',
+ 'username': 'test',
+ 'groups': [],
+ 'enabled': True,
+ 'emailVerified': False,
+ 'disableableCredentialTypes': [],
+ 'requiredActions': [],
+ 'credentials': [],
+ 'federatedIdentities': [],
+ 'clientConsents': []
+ }
+ ]
+ return_value_update_user_groups_membership = [False]
+ return_get_user_groups = [[]]
+ return_create_user = None
+ return_delete_user = None
+ return_update_user = None
+ changed = False
+
+ set_module_args(module_args)
+
+ # Run the module
+
+ with mock_good_connection():
+ with patch_keycloak_api(get_user_by_username=return_value_get_user_by_username,
+ create_user=return_create_user,
+ update_user_groups_membership=return_value_update_user_groups_membership,
+ get_user_groups=return_get_user_groups,
+ update_user=return_update_user,
+ delete_user=return_delete_user) \
+ as (mock_get_user_by_username,
+ mock_create_user,
+ mock_update_user_groups_membership,
+ mock_get_user_groups,
+ mock_delete_user,
+ mock_update_user):
+ with self.assertRaises(AnsibleExitJson) as exec_info:
+ self.module.main()
+
+ self.assertEqual(mock_get_user_by_username.call_count, 1)
+ self.assertEqual(mock_create_user.call_count, 0)
+ self.assertEqual(mock_update_user_groups_membership.call_count, 1)
+ self.assertEqual(mock_get_user_groups.call_count, 1)
+ self.assertEqual(mock_update_user.call_count, 0)
+ self.assertEqual(mock_delete_user.call_count, 0)
+
+ # Verify that the module's changed status matches what is expected
+ self.assertIs(exec_info.exception.args[0]['changed'], changed)
+
+ def test_update_user_with_group_changes(self):
+ """Update groups for a user"""
+
+ module_args = {
+ 'auth_keycloak_url': 'https: // auth.example.com / auth',
+ 'token': '{{ access_token }}',
+ 'state': 'present',
+ 'realm': 'master',
+ 'username': 'test',
+ 'first_name': 'test',
+ 'last_name': 'user',
+ 'groups': [{
+ 'name': 'group1',
+ 'state': 'present'
+ }]
+ }
+ return_value_get_user_by_username = [
+ {
+ 'id': '123eqwdawer24qwdqw4',
+ 'username': 'test',
+ 'groups': [],
+ 'enabled': True,
+ 'emailVerified': False,
+ 'disableableCredentialTypes': [],
+ 'requiredActions': [],
+ 'credentials': [],
+ 'federatedIdentities': [],
+ 'clientConsents': []
+ }
+ ]
+ return_value_update_user_groups_membership = [True]
+ return_get_user_groups = [['group1']]
+ return_create_user = None
+ return_delete_user = None
+ return_update_user = [
+ {
+ 'id': '123eqwdawer24qwdqw4',
+ 'username': 'test',
+ 'first_name': 'test',
+ 'last_name': 'user',
+ 'enabled': True,
+ 'emailVerified': False,
+ 'disableableCredentialTypes': [],
+ 'requiredActions': [],
+ 'credentials': [],
+ 'federatedIdentities': [],
+ 'clientConsents': []
+ }
+ ]
+ changed = True
+
+ set_module_args(module_args)
+
+ # Run the module
+
+ with mock_good_connection():
+ with patch_keycloak_api(get_user_by_username=return_value_get_user_by_username,
+ create_user=return_create_user,
+ update_user_groups_membership=return_value_update_user_groups_membership,
+ get_user_groups=return_get_user_groups,
+ update_user=return_update_user,
+ delete_user=return_delete_user) \
+ as (mock_get_user_by_username,
+ mock_create_user,
+ mock_update_user_groups_membership,
+ mock_get_user_groups,
+ mock_delete_user,
+ mock_update_user):
+ with self.assertRaises(AnsibleExitJson) as exec_info:
+ self.module.main()
+
+ self.assertEqual(mock_get_user_by_username.call_count, 1)
+ self.assertEqual(mock_create_user.call_count, 0)
+ self.assertEqual(mock_update_user_groups_membership.call_count, 1)
+ self.assertEqual(mock_get_user_groups.call_count, 1)
+ self.assertEqual(mock_update_user.call_count, 1)
+ self.assertEqual(mock_delete_user.call_count, 0)
+
+ # Verify that the module's changed status matches what is expected
+ self.assertIs(exec_info.exception.args[0]['changed'], changed)
+
+ def test_delete_user(self):
+ """Delete a user"""
+
+ module_args = {
+ 'auth_keycloak_url': 'https: // auth.example.com / auth',
+ 'token': '{{ access_token }}',
+ 'state': 'absent',
+ 'realm': 'master',
+ 'username': 'test',
+ 'groups': []
+ }
+ return_value_get_user_by_username = [
+ {
+ 'id': '123eqwdawer24qwdqw4',
+ 'username': 'test',
+ 'groups': [],
+ 'enabled': True,
+ 'emailVerified': False,
+ 'disableableCredentialTypes': [],
+ 'requiredActions': [],
+ 'credentials': [],
+ 'federatedIdentities': [],
+ 'clientConsents': []
+ }
+ ]
+ return_value_update_user_groups_membership = None
+ return_get_user_groups = None
+ return_create_user = None
+ return_delete_user = None
+ return_update_user = None
+ changed = True
+
+ set_module_args(module_args)
+
+ # Run the module
+
+ with mock_good_connection():
+ with patch_keycloak_api(get_user_by_username=return_value_get_user_by_username,
+ create_user=return_create_user,
+ update_user_groups_membership=return_value_update_user_groups_membership,
+ get_user_groups=return_get_user_groups,
+ update_user=return_update_user,
+ delete_user=return_delete_user) \
+ as (mock_get_user_by_username,
+ mock_create_user,
+ mock_update_user_groups_membership,
+ mock_get_user_groups,
+ mock_delete_user,
+ mock_update_user):
+ with self.assertRaises(AnsibleExitJson) as exec_info:
+ self.module.main()
+
+ self.assertEqual(mock_get_user_by_username.call_count, 1)
+ self.assertEqual(mock_create_user.call_count, 0)
+ self.assertEqual(mock_update_user_groups_membership.call_count, 0)
+ self.assertEqual(mock_get_user_groups.call_count, 0)
+ self.assertEqual(mock_update_user.call_count, 0)
+ self.assertEqual(mock_delete_user.call_count, 1)
+
+ # Verify that the module's changed status matches what is expected
+ self.assertIs(exec_info.exception.args[0]['changed'], changed)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_user_federation.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_user_federation.py
index 8d3dcaa23..523ef9f21 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_user_federation.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_keycloak_user_federation.py
@@ -326,6 +326,7 @@ class TestKeycloakUserFederation(ModuleTestCase):
'connectionPooling': True,
'pagination': True,
'allowKerberosAuthentication': False,
+ 'krbPrincipalAttribute': 'krbPrincipalName',
'debug': False,
'useKerberosForPasswordAuthentication': False,
},
@@ -374,6 +375,9 @@ class TestKeycloakUserFederation(ModuleTestCase):
"enabled": [
"true"
],
+ "krbPrincipalAttribute": [
+ "krb5PrincipalName"
+ ],
"usernameLDAPAttribute": [
"uid"
],
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_lvg_rename.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_lvg_rename.py
new file mode 100644
index 000000000..0f2fcb7fa
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_lvg_rename.py
@@ -0,0 +1,160 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Contributors to the Ansible project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+from ansible_collections.community.general.plugins.modules import lvg_rename
+from ansible_collections.community.general.tests.unit.compat.mock import patch
+from ansible_collections.community.general.tests.unit.plugins.modules.utils import (
+ AnsibleFailJson, AnsibleExitJson, ModuleTestCase, set_module_args)
+
+
+VGS_OUTPUT = '''\
+vg_data_testhost1;XKZ5gn-YhWY-NlrT-QCFN-qmMG-VGT9-7uOmex
+vg_sys_testhost2;xgy2SJ-YlYd-fde2-e3oG-zdXL-0xGf-ihqG2H
+'''
+
+
+class TestLvgRename(ModuleTestCase):
+ """Tests for lvg_rename internals"""
+ module = lvg_rename
+ module_path = 'ansible_collections.community.general.plugins.modules.lvg_rename'
+
+ def setUp(self):
+ """Prepare mocks for module testing"""
+ super(TestLvgRename, self).setUp()
+
+ self.mock_run_responses = {}
+
+ patched_module_get_bin_path = patch('%s.AnsibleModule.get_bin_path' % (self.module_path))
+ self.mock_module_get_bin_path = patched_module_get_bin_path.start()
+ self.mock_module_get_bin_path.return_value = '/mocpath'
+ self.addCleanup(patched_module_get_bin_path.stop)
+
+ patched_module_run_command = patch('%s.AnsibleModule.run_command' % (self.module_path))
+ self.mock_module_run_command = patched_module_run_command.start()
+ self.addCleanup(patched_module_run_command.stop)
+
+ def test_vg_not_found_by_name(self):
+ """When the VG by the specified by vg name not found, the module should exit with error"""
+ failed = True
+ self.mock_module_run_command.side_effect = [(0, VGS_OUTPUT, '')]
+ expected_msg = 'Both current (vg_missing) and new (vg_data_testhost2) VG are missing.'
+
+ module_args = {
+ 'vg': 'vg_missing',
+ 'vg_new': 'vg_data_testhost2',
+ }
+ set_module_args(args=module_args)
+
+ with self.assertRaises(AnsibleFailJson) as result:
+ self.module.main()
+
+ self.assertEqual(len(self.mock_module_run_command.mock_calls), 1)
+ self.assertIs(result.exception.args[0]['failed'], failed)
+ self.assertEqual(result.exception.args[0]['msg'], expected_msg)
+
+ def test_vg_not_found_by_uuid(self):
+ """When the VG by the specified vg UUID not found, the module should exit with error"""
+ failed = True
+ self.mock_module_run_command.side_effect = [(0, VGS_OUTPUT, '')]
+ expected_msg = 'Both current (Yfj4YG-c8nI-z7w5-B7Fw-i2eM-HqlF-ApFVp0) and new (vg_data_testhost2) VG are missing.'
+
+ module_args = {
+ 'vg': 'Yfj4YG-c8nI-z7w5-B7Fw-i2eM-HqlF-ApFVp0',
+ 'vg_new': 'vg_data_testhost2',
+ }
+ set_module_args(args=module_args)
+
+ with self.assertRaises(AnsibleFailJson) as result:
+ self.module.main()
+
+ self.assertEqual(len(self.mock_module_run_command.mock_calls), 1)
+ self.assertIs(result.exception.args[0]['failed'], failed)
+ self.assertEqual(result.exception.args[0]['msg'], expected_msg)
+
+ def test_vg_and_vg_new_both_exists(self):
+ """When a VG found for both vg and vg_new options, the module should exit with error"""
+ failed = True
+ self.mock_module_run_command.side_effect = [(0, VGS_OUTPUT, '')]
+ expected_msg = 'The new VG name (vg_sys_testhost2) is already in use.'
+
+ module_args = {
+ 'vg': 'vg_data_testhost1',
+ 'vg_new': 'vg_sys_testhost2',
+ }
+ set_module_args(args=module_args)
+
+ with self.assertRaises(AnsibleFailJson) as result:
+ self.module.main()
+
+ self.assertEqual(len(self.mock_module_run_command.mock_calls), 1)
+ self.assertIs(result.exception.args[0]['failed'], failed)
+ self.assertEqual(result.exception.args[0]['msg'], expected_msg)
+
+ def test_vg_needs_renaming(self):
+ """When the VG found for vg option and there is no VG for vg_new option,
+ the module should call vgrename"""
+ changed = True
+ self.mock_module_run_command.side_effect = [
+ (0, VGS_OUTPUT, ''),
+ (0, ' Volume group "vg_data_testhost1" successfully renamed to "vg_data_testhost2"', '')
+ ]
+ expected_msg = ' Volume group "vg_data_testhost1" successfully renamed to "vg_data_testhost2"'
+
+ module_args = {
+ 'vg': '/dev/vg_data_testhost1',
+ 'vg_new': 'vg_data_testhost2',
+ }
+ set_module_args(args=module_args)
+
+ with self.assertRaises(AnsibleExitJson) as result:
+ self.module.main()
+
+ self.assertEqual(len(self.mock_module_run_command.mock_calls), 2)
+ self.assertIs(result.exception.args[0]['changed'], changed)
+ self.assertEqual(result.exception.args[0]['msg'], expected_msg)
+
+ def test_vg_needs_renaming_in_check_mode(self):
+ """When running in check mode and the VG found for vg option and there is no VG for vg_new option,
+ the module should not call vgrename"""
+ changed = True
+ self.mock_module_run_command.side_effect = [(0, VGS_OUTPUT, '')]
+ expected_msg = 'Running in check mode. The module would rename VG /dev/vg_data_testhost1 to vg_data_testhost2.'
+
+ module_args = {
+ 'vg': '/dev/vg_data_testhost1',
+ 'vg_new': 'vg_data_testhost2',
+ '_ansible_check_mode': True,
+ }
+ set_module_args(args=module_args)
+
+ with self.assertRaises(AnsibleExitJson) as result:
+ self.module.main()
+
+ self.assertEqual(len(self.mock_module_run_command.mock_calls), 1)
+ self.assertIs(result.exception.args[0]['changed'], changed)
+ self.assertEqual(result.exception.args[0]['msg'], expected_msg)
+
+ def test_vg_needs_no_renaming(self):
+ """When the VG not found for vg option and the VG found for vg_new option,
+ the module should not call vgrename"""
+ changed = False
+ self.mock_module_run_command.side_effect = [(0, VGS_OUTPUT, '')]
+ expected_msg = 'The new VG (vg_data_testhost1) already exists, nothing to do.'
+
+ module_args = {
+ 'vg': 'vg_data_testhostX',
+ 'vg_new': 'vg_data_testhost1',
+ }
+ set_module_args(args=module_args)
+
+ with self.assertRaises(AnsibleExitJson) as result:
+ self.module.main()
+
+ self.assertEqual(len(self.mock_module_run_command.mock_calls), 1)
+ self.assertIs(result.exception.args[0]['changed'], changed)
+ self.assertEqual(result.exception.args[0]['msg'], expected_msg)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_modprobe.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_modprobe.py
index 18695695a..2ad083151 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_modprobe.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_modprobe.py
@@ -152,7 +152,7 @@ class TestUnloadModule(ModuleTestCase):
class TestModuleIsLoadedPersistently(ModuleTestCase):
def setUp(self):
if (sys.version_info[0] == 3 and sys.version_info[1] < 7) or (sys.version_info[0] == 2 and sys.version_info[1] < 7):
- self.skipTest('open_mock doesnt support readline in earlier python versions')
+ self.skipTest("open_mock doesn't support readline in earlier python versions")
super(TestModuleIsLoadedPersistently, self).setUp()
@@ -230,7 +230,7 @@ class TestModuleIsLoadedPersistently(ModuleTestCase):
class TestPermanentParams(ModuleTestCase):
def setUp(self):
if (sys.version_info[0] == 3 and sys.version_info[1] < 7) or (sys.version_info[0] == 2 and sys.version_info[1] < 7):
- self.skipTest('open_mock doesnt support readline in earlier python versions')
+ self.skipTest("open_mock doesn't support readline in earlier python versions")
super(TestPermanentParams, self).setUp()
self.mock_get_bin_path = patch('ansible.module_utils.basic.AnsibleModule.get_bin_path')
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_nmcli.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_nmcli.py
index efd8284a3..8c9c007ac 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_nmcli.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_nmcli.py
@@ -118,6 +118,12 @@ TESTCASE_CONNECTION = [
'state': 'absent',
'_ansible_check_mode': True,
},
+ {
+ 'type': 'loopback',
+ 'conn_name': 'non_existent_nw_device',
+ 'state': 'absent',
+ '_ansible_check_mode': True,
+ },
]
TESTCASE_GENERIC = [
@@ -262,6 +268,25 @@ ipv4.routes: { ip = 192.168.200.0/24, nh = 192.168.1.
ipv4.route-metric: 10
"""
+TESTCASE_ETHERNET_MOD_IPV4_INT_WITH_ROUTE_AND_METRIC_CLEAR = [
+ {
+ 'type': 'ethernet',
+ 'conn_name': 'non_existent_nw_device',
+ 'routes4': [],
+ 'state': 'present',
+ '_ansible_check_mode': False,
+ '_ansible_diff': True,
+ },
+ {
+ 'type': 'ethernet',
+ 'conn_name': 'non_existent_nw_device',
+ 'routes4_extended': [],
+ 'state': 'present',
+ '_ansible_check_mode': False,
+ '_ansible_diff': True,
+ },
+]
+
TESTCASE_ETHERNET_MOD_IPV6_INT_WITH_ROUTE_AND_METRIC = [
{
'type': 'ethernet',
@@ -453,6 +478,38 @@ ipv6.ignore-auto-dns: no
ipv6.ignore-auto-routes: no
"""
+TESTCASE_GENERIC_DNS4_OPTIONS = [
+ {
+ 'type': 'generic',
+ 'conn_name': 'non_existent_nw_device',
+ 'ifname': 'generic_non_existant',
+ 'ip4': '10.10.10.10/24',
+ 'gw4': '10.10.10.1',
+ 'state': 'present',
+ 'dns4_options': [],
+ 'dns6_options': [],
+ '_ansible_check_mode': False,
+ }
+]
+
+TESTCASE_GENERIC_DNS4_OPTIONS_SHOW_OUTPUT = """\
+connection.id: non_existent_nw_device
+connection.interface-name: generic_non_existant
+connection.autoconnect: yes
+ipv4.method: manual
+ipv4.addresses: 10.10.10.10/24
+ipv4.gateway: 10.10.10.1
+ipv4.ignore-auto-dns: no
+ipv4.ignore-auto-routes: no
+ipv4.never-default: no
+ipv4.dns-options: --
+ipv4.may-fail: yes
+ipv6.dns-options: --
+ipv6.method: auto
+ipv6.ignore-auto-dns: no
+ipv6.ignore-auto-routes: no
+"""
+
TESTCASE_GENERIC_ZONE = [
{
'type': 'generic',
@@ -569,6 +626,7 @@ TESTCASE_BRIDGE_SLAVE = [
'type': 'bridge-slave',
'conn_name': 'non_existent_nw_device',
'ifname': 'br0_non_existant',
+ 'hairpin': True,
'path_cost': 100,
'state': 'present',
'_ansible_check_mode': False,
@@ -892,6 +950,28 @@ TESTCASE_ETHERNET_STATIC = [
}
]
+TESTCASE_LOOPBACK = [
+ {
+ 'type': 'loopback',
+ 'conn_name': 'lo',
+ 'ifname': 'lo',
+ 'ip4': '127.0.0.1/8',
+ 'state': 'present',
+ '_ansible_check_mode': False,
+ }
+]
+
+TESTCASE_LOOPBACK_MODIFY = [
+ {
+ 'type': 'loopback',
+ 'conn_name': 'lo',
+ 'ifname': 'lo',
+ 'ip4': ['127.0.0.1/8', '127.0.0.2/8'],
+ 'state': 'present',
+ '_ansible_check_mode': False,
+ }
+]
+
TESTCASE_ETHERNET_STATIC_SHOW_OUTPUT = """\
connection.id: non_existent_nw_device
connection.interface-name: ethernet_non_existant
@@ -910,6 +990,21 @@ ipv6.ignore-auto-dns: no
ipv6.ignore-auto-routes: no
"""
+TESTCASE_LOOPBACK_SHOW_OUTPUT = """\
+connection.id: lo
+connection.interface-name: lo
+connection.autoconnect: yes
+ipv4.method: manual
+ipv4.addresses: 127.0.0.1/8
+ipv4.ignore-auto-dns: no
+ipv4.ignore-auto-routes: no
+ipv4.never-default: no
+ipv4.may-fail: yes
+ipv6.method: manual
+ipv6.ignore-auto-dns: no
+ipv6.ignore-auto-routes: no
+"""
+
TESTCASE_ETHERNET_STATIC_MULTIPLE_IP4_ADDRESSES = [
{
'type': 'ethernet',
@@ -1393,7 +1488,8 @@ ipv4.may-fail: yes
ipv6.method: auto
ipv6.ignore-auto-dns: no
ipv6.ignore-auto-routes: no
-infiniband.transport-mode datagram
+infiniband.mtu: auto
+infiniband.transport-mode: datagram
"""
TESTCASE_INFINIBAND_STATIC_MODIFY_TRANSPORT_MODE = [
@@ -1514,6 +1610,13 @@ def mocked_generic_connection_dns_search_unchanged(mocker):
@pytest.fixture
+def mocked_generic_connection_dns_options_unchanged(mocker):
+ mocker_set(mocker,
+ connection_exists=True,
+ execute_return=(0, TESTCASE_GENERIC_DNS4_OPTIONS_SHOW_OUTPUT, ""))
+
+
+@pytest.fixture
def mocked_generic_connection_zone_unchanged(mocker):
mocker_set(mocker,
connection_exists=True,
@@ -1672,6 +1775,17 @@ def mocked_ethernet_connection_with_ipv4_static_address_static_route_metric_modi
@pytest.fixture
+def mocked_ethernet_connection_with_ipv4_static_address_static_route_metric_clear(mocker):
+ mocker_set(mocker,
+ connection_exists=True,
+ execute_return=None,
+ execute_side_effect=(
+ (0, TESTCASE_ETHERNET_MOD_IPV4_INT_WITH_ROUTE_AND_METRIC_SHOW_OUTPUT, ""),
+ (0, "", ""),
+ ))
+
+
+@pytest.fixture
def mocked_ethernet_connection_with_ipv6_static_address_static_route_metric_modify(mocker):
mocker_set(mocker,
connection_exists=True,
@@ -1875,6 +1989,24 @@ def mocked_generic_connection_diff_check(mocker):
execute_return=(0, TESTCASE_GENERIC_SHOW_OUTPUT, ""))
+@pytest.fixture
+def mocked_loopback_connection_unchanged(mocker):
+ mocker_set(mocker,
+ connection_exists=True,
+ execute_return=(0, TESTCASE_LOOPBACK_SHOW_OUTPUT, ""))
+
+
+@pytest.fixture
+def mocked_loopback_connection_modify(mocker):
+ mocker_set(mocker,
+ connection_exists=True,
+ execute_return=None,
+ execute_side_effect=(
+ (0, TESTCASE_LOOPBACK_SHOW_OUTPUT, ""),
+ (0, "", ""),
+ ))
+
+
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_BOND, indirect=['patch_ansible_module'])
def test_bond_connection_create(mocked_generic_connection_create, capfd):
"""
@@ -2066,6 +2198,62 @@ def test_generic_connection_dns_search_unchanged(mocked_generic_connection_dns_s
assert not results['changed']
+@pytest.mark.parametrize('patch_ansible_module', TESTCASE_GENERIC_DNS4_OPTIONS, indirect=['patch_ansible_module'])
+def test_generic_connection_create_dns_options(mocked_generic_connection_create, capfd):
+ """
+ Test : Generic connection created with dns options
+ """
+ with pytest.raises(SystemExit):
+ nmcli.main()
+
+ assert nmcli.Nmcli.execute_command.call_count == 1
+ arg_list = nmcli.Nmcli.execute_command.call_args_list
+ args, kwargs = arg_list[0]
+
+ assert 'ipv4.dns-options' in args[0]
+ assert 'ipv6.dns-options' in args[0]
+
+ out, err = capfd.readouterr()
+ results = json.loads(out)
+ assert not results.get('failed')
+ assert results['changed']
+
+
+@pytest.mark.parametrize('patch_ansible_module', TESTCASE_GENERIC_DNS4_OPTIONS, indirect=['patch_ansible_module'])
+def test_generic_connection_modify_dns_options(mocked_generic_connection_create, capfd):
+ """
+ Test : Generic connection modified with dns options
+ """
+ with pytest.raises(SystemExit):
+ nmcli.main()
+
+ assert nmcli.Nmcli.execute_command.call_count == 1
+ arg_list = nmcli.Nmcli.execute_command.call_args_list
+ args, kwargs = arg_list[0]
+
+ assert 'ipv4.dns-options' in args[0]
+ assert 'ipv6.dns-options' in args[0]
+
+ out, err = capfd.readouterr()
+ results = json.loads(out)
+ assert not results.get('failed')
+ assert results['changed']
+
+
+@pytest.mark.parametrize('patch_ansible_module', TESTCASE_GENERIC_DNS4_OPTIONS, indirect=['patch_ansible_module'])
+def test_generic_connection_dns_options_unchanged(mocked_generic_connection_dns_options_unchanged, capfd):
+ """
+ Test : Generic connection with dns options unchanged
+ """
+ with pytest.raises(SystemExit):
+ nmcli.main()
+
+ out, err = capfd.readouterr()
+ results = json.loads(out)
+ assert not results.get('failed')
+ assert not results['changed']
+
+
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_CONNECTION, indirect=['patch_ansible_module'])
def test_dns4_none(mocked_connection_exists, capfd):
"""
@@ -2991,6 +3179,38 @@ def test_ethernet_connection_static_ipv4_address_static_route_with_metric_modify
assert not results.get('failed')
+@pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_MOD_IPV4_INT_WITH_ROUTE_AND_METRIC_CLEAR, indirect=['patch_ansible_module'])
+def test_ethernet_connection_static_ipv4_address_static_route_with_metric_clear(
+ mocked_ethernet_connection_with_ipv4_static_address_static_route_metric_clear, capfd):
+ """
+ Test : Modify ethernet connection with static IPv4 address and static route
+ """
+ with pytest.raises(SystemExit):
+ nmcli.main()
+
+ arg_list = nmcli.Nmcli.execute_command.call_args_list
+ add_args, add_kw = arg_list[1]
+
+ assert add_args[0][0] == '/usr/bin/nmcli'
+ assert add_args[0][1] == 'con'
+ assert add_args[0][2] == 'modify'
+ assert add_args[0][3] == 'non_existent_nw_device'
+
+ add_args_text = list(map(to_text, add_args[0]))
+
+ for param in ['ipv4.routes', '']:
+ assert param in add_args_text
+
+ out, err = capfd.readouterr()
+ results = json.loads(out)
+
+ assert 'ipv4.routes' in results['diff']['before']
+ assert 'ipv4.routes' in results['diff']['after']
+
+ assert results.get('changed') is True
+ assert not results.get('failed')
+
+
@pytest.mark.parametrize('patch_ansible_module', TESTCASE_ETHERNET_ADD_IPV6_INT_WITH_ROUTE, indirect=['patch_ansible_module'])
def test_ethernet_connection_static_ipv6_address_static_route_create(mocked_ethernet_connection_with_ipv6_static_address_static_route_create, capfd):
"""
@@ -4032,6 +4252,7 @@ def test_bond_connection_unchanged(mocked_generic_connection_diff_check, capfd):
state=dict(type='str', required=True, choices=['absent', 'present']),
conn_name=dict(type='str', required=True),
master=dict(type='str'),
+ slave_type=dict(type=str, choices=['bond', 'bridge', 'team']),
ifname=dict(type='str'),
type=dict(type='str',
choices=[
@@ -4077,6 +4298,7 @@ def test_bond_connection_unchanged(mocked_generic_connection_diff_check, capfd):
never_default4=dict(type='bool', default=False),
dns4=dict(type='list', elements='str'),
dns4_search=dict(type='list', elements='str'),
+ dns4_options=dict(type='list', elements='str'),
dns4_ignore_auto=dict(type='bool', default=False),
method4=dict(type='str', choices=['auto', 'link-local', 'manual', 'shared', 'disabled']),
may_fail4=dict(type='bool', default=True),
@@ -4086,6 +4308,7 @@ def test_bond_connection_unchanged(mocked_generic_connection_diff_check, capfd):
gw6_ignore_auto=dict(type='bool', default=False),
dns6=dict(type='list', elements='str'),
dns6_search=dict(type='list', elements='str'),
+ dns6_options=dict(type='list', elements='str'),
dns6_ignore_auto=dict(type='bool', default=False),
routes6=dict(type='list', elements='str'),
routes6_extended=dict(type='list',
@@ -4259,3 +4482,366 @@ def test_macvlan_mod(mocked_generic_connection_modify, capfd):
results = json.loads(out)
assert not results.get('failed')
assert results['changed']
+
+
+TESTCASE_SLAVE_TYPE_BRIDGE_CONNECTION = [
+ {
+ 'type': 'ethernet',
+ 'conn_name': 'fake_conn',
+ 'ifname': 'fake_eth0',
+ 'state': 'present',
+ 'slave_type': 'bridge',
+ 'master': 'fake_br0',
+ '_ansible_check_mode': False,
+ }
+]
+
+
+TESTCASE_SLAVE_TYPE_BRIDGE_CONNECTION_SHOW_OUTPUT = """\
+connection.id: fake_conn
+connection.type: 802-3-ethernet
+connection.interface-name: fake_eth0
+connection.autoconnect: yes
+connection.master: --
+connection.slave-type: --
+802-3-ethernet.mtu: auto
+"""
+
+
+TESTCASE_SLAVE_TYPE_BRIDGE_CONNECTION_UNCHANGED_SHOW_OUTPUT = """\
+connection.id: fake_conn
+connection.type: 802-3-ethernet
+connection.interface-name: fake_eth0
+connection.autoconnect: yes
+connection.master: fake_br0
+connection.slave-type: bridge
+802-3-ethernet.mtu: auto
+"""
+
+
+@pytest.fixture
+def mocked_slave_type_bridge_create(mocker):
+ mocker_set(mocker,
+ execute_return=None,
+ execute_side_effect=(
+ (0, TESTCASE_SLAVE_TYPE_BRIDGE_CONNECTION_SHOW_OUTPUT, ""),
+ (0, "", ""),
+ ))
+
+
+@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SLAVE_TYPE_BRIDGE_CONNECTION, indirect=['patch_ansible_module'])
+def test_create_slave_type_bridge(mocked_slave_type_bridge_create, capfd):
+ """
+ Test : slave for bridge created
+ """
+
+ with pytest.raises(SystemExit):
+ nmcli.main()
+
+ assert nmcli.Nmcli.execute_command.call_count == 1
+ arg_list = nmcli.Nmcli.execute_command.call_args_list
+ args, kwargs = arg_list[0]
+
+ assert args[0][0] == '/usr/bin/nmcli'
+ assert args[0][1] == 'con'
+ assert args[0][2] == 'add'
+ assert args[0][3] == 'type'
+ assert args[0][4] == 'ethernet'
+ assert args[0][5] == 'con-name'
+ assert args[0][6] == 'fake_conn'
+ con_master_index = args[0].index('connection.master')
+ slave_type_index = args[0].index('connection.slave-type')
+ assert args[0][con_master_index + 1] == 'fake_br0'
+ assert args[0][slave_type_index + 1] == 'bridge'
+
+ out, err = capfd.readouterr()
+ results = json.loads(out)
+ assert not results.get('failed')
+ assert results['changed']
+
+
+@pytest.fixture
+def mocked_create_slave_type_bridge_unchanged(mocker):
+ mocker_set(mocker,
+ connection_exists=True,
+ execute_return=(0, TESTCASE_SLAVE_TYPE_BRIDGE_CONNECTION_UNCHANGED_SHOW_OUTPUT, ""))
+
+
+@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SLAVE_TYPE_BRIDGE_CONNECTION, indirect=['patch_ansible_module'])
+def test_slave_type_bridge_unchanged(mocked_create_slave_type_bridge_unchanged, capfd):
+ """
+ Test : Existent slave for bridge unchanged
+ """
+ with pytest.raises(SystemExit):
+ nmcli.main()
+
+ out, err = capfd.readouterr()
+ results = json.loads(out)
+ assert not results.get('failed')
+ assert not results['changed']
+
+
+TESTCASE_SLAVE_TYPE_BOND_CONNECTION = [
+ {
+ 'type': 'ethernet',
+ 'conn_name': 'fake_conn',
+ 'ifname': 'fake_eth0',
+ 'state': 'present',
+ 'slave_type': 'bond',
+ 'master': 'fake_bond0',
+ '_ansible_check_mode': False,
+ }
+]
+
+
+TESTCASE_SLAVE_TYPE_BOND_CONNECTION_SHOW_OUTPUT = """\
+connection.id: fake_conn
+connection.type: 802-3-ethernet
+connection.interface-name: fake_eth0
+connection.autoconnect: yes
+connection.master: --
+connection.slave-type: --
+802-3-ethernet.mtu: auto
+"""
+
+
+TESTCASE_SLAVE_TYPE_BOND_CONNECTION_UNCHANGED_SHOW_OUTPUT = """\
+connection.id: fake_conn
+connection.type: 802-3-ethernet
+connection.interface-name: fake_eth0
+connection.autoconnect: yes
+connection.master: fake_bond0
+connection.slave-type: bond
+802-3-ethernet.mtu: auto
+"""
+
+
+@pytest.fixture
+def mocked_slave_type_bond_create(mocker):
+ mocker_set(mocker,
+ execute_return=None,
+ execute_side_effect=(
+ (0, TESTCASE_SLAVE_TYPE_BOND_CONNECTION_SHOW_OUTPUT, ""),
+ (0, "", ""),
+ ))
+
+
+@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SLAVE_TYPE_BOND_CONNECTION, indirect=['patch_ansible_module'])
+def test_create_slave_type_bond(mocked_slave_type_bond_create, capfd):
+ """
+ Test : slave for bond created
+ """
+
+ with pytest.raises(SystemExit):
+ nmcli.main()
+
+ assert nmcli.Nmcli.execute_command.call_count == 1
+ arg_list = nmcli.Nmcli.execute_command.call_args_list
+ args, kwargs = arg_list[0]
+
+ assert args[0][0] == '/usr/bin/nmcli'
+ assert args[0][1] == 'con'
+ assert args[0][2] == 'add'
+ assert args[0][3] == 'type'
+ assert args[0][4] == 'ethernet'
+ assert args[0][5] == 'con-name'
+ assert args[0][6] == 'fake_conn'
+ con_master_index = args[0].index('connection.master')
+ slave_type_index = args[0].index('connection.slave-type')
+ assert args[0][con_master_index + 1] == 'fake_bond0'
+ assert args[0][slave_type_index + 1] == 'bond'
+
+ out, err = capfd.readouterr()
+ results = json.loads(out)
+ assert not results.get('failed')
+ assert results['changed']
+
+
+@pytest.fixture
+def mocked_create_slave_type_bond_unchanged(mocker):
+ mocker_set(mocker,
+ connection_exists=True,
+ execute_return=(0, TESTCASE_SLAVE_TYPE_BOND_CONNECTION_UNCHANGED_SHOW_OUTPUT, ""))
+
+
+@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SLAVE_TYPE_BOND_CONNECTION, indirect=['patch_ansible_module'])
+def test_slave_type_bond_unchanged(mocked_create_slave_type_bond_unchanged, capfd):
+ """
+ Test : Existent slave for bridge unchanged
+ """
+ with pytest.raises(SystemExit):
+ nmcli.main()
+
+ out, err = capfd.readouterr()
+ results = json.loads(out)
+ assert not results.get('failed')
+ assert not results['changed']
+
+
+TESTCASE_SLAVE_TYPE_TEAM_CONNECTION = [
+ {
+ 'type': 'ethernet',
+ 'conn_name': 'fake_conn',
+ 'ifname': 'fake_eth0',
+ 'state': 'present',
+ 'slave_type': 'team',
+ 'master': 'fake_team0',
+ '_ansible_check_mode': False,
+ }
+]
+
+
+TESTCASE_SLAVE_TYPE_TEAM_CONNECTION_SHOW_OUTPUT = """\
+connection.id: fake_conn
+connection.type: 802-3-ethernet
+connection.interface-name: fake_eth0
+connection.autoconnect: yes
+connection.master: --
+connection.slave-type: --
+802-3-ethernet.mtu: auto
+"""
+
+
+TESTCASE_SLAVE_TYPE_TEAM_CONNECTION_UNCHANGED_SHOW_OUTPUT = """\
+connection.id: fake_conn
+connection.type: 802-3-ethernet
+connection.interface-name: fake_eth0
+connection.autoconnect: yes
+connection.master: fake_team0
+connection.slave-type: team
+802-3-ethernet.mtu: auto
+"""
+
+
+@pytest.fixture
+def mocked_slave_type_team_create(mocker):
+ mocker_set(mocker,
+ execute_return=None,
+ execute_side_effect=(
+ (0, TESTCASE_SLAVE_TYPE_TEAM_CONNECTION_SHOW_OUTPUT, ""),
+ (0, "", ""),
+ ))
+
+
+@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SLAVE_TYPE_TEAM_CONNECTION, indirect=['patch_ansible_module'])
+def test_create_slave_type_team(mocked_slave_type_team_create, capfd):
+ """
+ Test : slave for bond created
+ """
+
+ with pytest.raises(SystemExit):
+ nmcli.main()
+
+ assert nmcli.Nmcli.execute_command.call_count == 1
+ arg_list = nmcli.Nmcli.execute_command.call_args_list
+ args, kwargs = arg_list[0]
+
+ assert args[0][0] == '/usr/bin/nmcli'
+ assert args[0][1] == 'con'
+ assert args[0][2] == 'add'
+ assert args[0][3] == 'type'
+ assert args[0][4] == 'ethernet'
+ assert args[0][5] == 'con-name'
+ assert args[0][6] == 'fake_conn'
+ con_master_index = args[0].index('connection.master')
+ slave_type_index = args[0].index('connection.slave-type')
+ assert args[0][con_master_index + 1] == 'fake_team0'
+ assert args[0][slave_type_index + 1] == 'team'
+
+ out, err = capfd.readouterr()
+ results = json.loads(out)
+ assert not results.get('failed')
+ assert results['changed']
+
+
+@pytest.fixture
+def mocked_create_slave_type_team_unchanged(mocker):
+ mocker_set(mocker,
+ connection_exists=True,
+ execute_return=(0, TESTCASE_SLAVE_TYPE_TEAM_CONNECTION_UNCHANGED_SHOW_OUTPUT, ""))
+
+
+@pytest.mark.parametrize('patch_ansible_module', TESTCASE_SLAVE_TYPE_TEAM_CONNECTION, indirect=['patch_ansible_module'])
+def test_slave_type_team_unchanged(mocked_create_slave_type_team_unchanged, capfd):
+ """
+ Test : Existent slave for bridge unchanged
+ """
+ with pytest.raises(SystemExit):
+ nmcli.main()
+
+ out, err = capfd.readouterr()
+ results = json.loads(out)
+ assert not results.get('failed')
+ assert not results['changed']
+
+
+@pytest.mark.parametrize('patch_ansible_module', TESTCASE_LOOPBACK, indirect=['patch_ansible_module'])
+def test_create_loopback(mocked_generic_connection_create, capfd):
+ """
+ Test : Create loopback connection
+ """
+
+ with pytest.raises(SystemExit):
+ nmcli.main()
+
+ assert nmcli.Nmcli.execute_command.call_count == 1
+ arg_list = nmcli.Nmcli.execute_command.call_args_list
+ add_args, add_kw = arg_list[0]
+
+ assert add_args[0][0] == '/usr/bin/nmcli'
+ assert add_args[0][1] == 'con'
+ assert add_args[0][2] == 'add'
+ assert add_args[0][3] == 'type'
+ assert add_args[0][4] == 'loopback'
+ assert add_args[0][5] == 'con-name'
+ assert add_args[0][6] == 'lo'
+
+ add_args_text = list(map(to_text, add_args[0]))
+ for param in ['connection.interface-name', 'lo',
+ 'ipv4.addresses', '127.0.0.1/8']:
+ assert param in add_args_text
+
+ out, err = capfd.readouterr()
+ results = json.loads(out)
+ assert not results.get('failed')
+ assert results['changed']
+
+
+@pytest.mark.parametrize('patch_ansible_module', TESTCASE_LOOPBACK, indirect=['patch_ansible_module'])
+def test_unchanged_loopback(mocked_loopback_connection_unchanged, capfd):
+ """
+ Test : loopback connection unchanged
+ """
+ with pytest.raises(SystemExit):
+ nmcli.main()
+
+ out, err = capfd.readouterr()
+ results = json.loads(out)
+ assert not results.get('failed')
+ assert not results['changed']
+
+
+@pytest.mark.parametrize('patch_ansible_module', TESTCASE_LOOPBACK_MODIFY, indirect=['patch_ansible_module'])
+def test_add_second_ip4_address_to_loopback_connection(mocked_loopback_connection_modify, capfd):
+ """
+ Test : Modify loopback connection
+ """
+ with pytest.raises(SystemExit):
+ nmcli.main()
+
+ assert nmcli.Nmcli.execute_command.call_count == 2
+ arg_list = nmcli.Nmcli.execute_command.call_args_list
+ args, kwargs = arg_list[1]
+
+ assert args[0][0] == '/usr/bin/nmcli'
+ assert args[0][1] == 'con'
+ assert args[0][2] == 'modify'
+ assert args[0][3] == 'lo'
+
+ for param in ['ipv4.addresses', '127.0.0.1/8,127.0.0.2/8']:
+ assert param in args[0]
+
+ out, err = capfd.readouterr()
+ results = json.loads(out)
+ assert not results.get('failed')
+ assert results['changed']
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_nomad_token.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_nomad_token.py
new file mode 100644
index 000000000..48f060f8b
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_nomad_token.py
@@ -0,0 +1,222 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021, Ansible Project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import nomad
+from ansible_collections.community.general.plugins.modules import nomad_token
+from ansible_collections.community.general.tests.unit.compat.mock import patch
+from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, \
+ ModuleTestCase, \
+ set_module_args
+
+
+def mock_acl_get_tokens(empty_list=False):
+ response_object = []
+
+ if not empty_list:
+ response_object = [
+ {
+ 'AccessorID': 'bac2b162-2a63-efa2-4e68-55d79dcb7721',
+ 'Name': 'Bootstrap Token', 'Type': 'management',
+ 'Policies': None, 'Roles': None, 'Global': True,
+ 'Hash': 'BUJ3BerTfrqFVm1P+vZr1gz9ubOkd+JAvYjNAJyaU9Y=',
+ 'CreateTime': '2023-11-12T18:44:39.740562185Z',
+ 'ExpirationTime': None,
+ 'CreateIndex': 9,
+ 'ModifyIndex': 9
+ },
+ {
+ 'AccessorID': '0d01c55f-8d63-f832-04ff-1866d4eb594e',
+ 'Name': 'devs',
+ 'Type': 'client', 'Policies': ['readonly'],
+ 'Roles': None,
+ 'Global': True,
+ 'Hash': 'eSn8H8RVqh8As8WQNnC2vlBRqXy6DECogc5umzX0P30=',
+ 'CreateTime': '2023-11-12T18:48:34.248857001Z',
+ 'ExpirationTime': None,
+ 'CreateIndex': 14,
+ 'ModifyIndex': 836
+ }
+ ]
+
+ return response_object
+
+
+def mock_acl_generate_bootstrap():
+ response_object = {
+ 'AccessorID': '0d01c55f-8d63-f832-04ff-1866d4eb594e',
+ 'Name': 'Bootstrap Token',
+ 'Type': 'management',
+ 'Policies': None,
+ 'Roles': None,
+ 'Global': True,
+ 'Hash': 'BUJ3BerTfrqFVm1P+vZr1gz9ubOkd+JAvYjNAJyaU9Y=',
+ 'CreateTime': '2023-11-12T18:48:34.248857001Z',
+ 'ExpirationTime': None,
+ 'ExpirationTTL': '',
+ 'CreateIndex': 14,
+ 'ModifyIndex': 836,
+ 'SecretID': 'd539a03d-337a-8504-6d12-000f861337bc'
+ }
+ return response_object
+
+
+def mock_acl_create_update_token():
+ response_object = {
+ 'AccessorID': '0d01c55f-8d63-f832-04ff-1866d4eb594e',
+ 'Name': 'dev',
+ 'Type': 'client',
+ 'Policies': ['readonly'],
+ 'Roles': None,
+ 'Global': True,
+ 'Hash': 'eSn8H8RVqh8As8WQNnC2vlBRqXy6DECogc5umzX0P30=',
+ 'CreateTime': '2023-11-12T18:48:34.248857001Z',
+ 'ExpirationTime': None,
+ 'ExpirationTTL': '',
+ 'CreateIndex': 14,
+ 'ModifyIndex': 836,
+ 'SecretID': 'd539a03d-337a-8504-6d12-000f861337bc'
+ }
+
+ return response_object
+
+
+def mock_acl_delete_token():
+ return {}
+
+
+class TestNomadTokenModule(ModuleTestCase):
+
+ def setUp(self):
+ super(TestNomadTokenModule, self).setUp()
+ self.module = nomad_token
+
+ def tearDown(self):
+ super(TestNomadTokenModule, self).tearDown()
+
+ def test_should_fail_without_parameters(self):
+ with self.assertRaises(AnsibleFailJson):
+ set_module_args({})
+ self.module.main()
+
+ def test_should_create_token_type_client(self):
+ module_args = {
+ 'host': 'localhost',
+ 'name': 'Dev token',
+ 'token_type': 'client',
+ 'state': 'present'
+ }
+
+ set_module_args(module_args)
+ with patch.object(nomad.api.acl.Acl, 'get_tokens', return_value=mock_acl_get_tokens()) as mock_get_tokens:
+ with patch.object(nomad.api.acl.Acl, 'create_token', return_value=mock_acl_create_update_token()) as \
+ mock_create_update_token:
+ with self.assertRaises(AnsibleExitJson):
+ self.module.main()
+
+ self.assertIs(mock_get_tokens.call_count, 1)
+ self.assertIs(mock_create_update_token.call_count, 1)
+
+ def test_should_create_token_type_bootstrap(self):
+ module_args = {
+ 'host': 'localhost',
+ 'token_type': 'bootstrap',
+ 'state': 'present'
+ }
+
+ set_module_args(module_args)
+
+ with patch.object(nomad.api.acl.Acl, 'get_tokens') as mock_get_tokens:
+ with patch.object(nomad.api.Acl, 'generate_bootstrap') as mock_generate_bootstrap:
+ mock_get_tokens.return_value = mock_acl_get_tokens(empty_list=True)
+ mock_generate_bootstrap.return_value = mock_acl_generate_bootstrap()
+
+ with self.assertRaises(AnsibleExitJson):
+ self.module.main()
+
+ self.assertIs(mock_get_tokens.call_count, 1)
+ self.assertIs(mock_generate_bootstrap.call_count, 1)
+
+ def test_should_fail_delete_without_name_parameter(self):
+ module_args = {
+ 'host': 'localhost',
+ 'state': 'absent'
+ }
+
+ set_module_args(module_args)
+ with patch.object(nomad.api.acl.Acl, 'get_tokens') as mock_get_tokens:
+ with patch.object(nomad.api.acl.Acl, 'delete_token') as mock_delete_token:
+ mock_get_tokens.return_value = mock_acl_get_tokens()
+ mock_delete_token.return_value = mock_acl_delete_token()
+
+ with self.assertRaises(AnsibleFailJson):
+ self.module.main()
+
+ def test_should_fail_delete_bootstrap_token(self):
+ module_args = {
+ 'host': 'localhost',
+ 'token_type': 'boostrap',
+ 'state': 'absent'
+ }
+
+ set_module_args(module_args)
+
+ with self.assertRaises(AnsibleFailJson):
+ self.module.main()
+
+ def test_should_fail_delete_boostrap_token_by_name(self):
+ module_args = {
+ 'host': 'localhost',
+ 'name': 'Bootstrap Token',
+ 'state': 'absent'
+ }
+
+ set_module_args(module_args)
+
+ with self.assertRaises(AnsibleFailJson):
+ self.module.main()
+
+ def test_should_delete_client_token(self):
+ module_args = {
+ 'host': 'localhost',
+ 'name': 'devs',
+ 'state': 'absent'
+ }
+
+ set_module_args(module_args)
+
+ with patch.object(nomad.api.acl.Acl, 'get_tokens') as mock_get_tokens:
+ with patch.object(nomad.api.acl.Acl, 'delete_token') as mock_delete_token:
+ mock_get_tokens.return_value = mock_acl_get_tokens()
+ mock_delete_token.return_value = mock_acl_delete_token()
+
+ with self.assertRaises(AnsibleExitJson):
+ self.module.main()
+
+ self.assertIs(mock_delete_token.call_count, 1)
+
+ def test_should_update_client_token(self):
+ module_args = {
+ 'host': 'localhost',
+ 'name': 'devs',
+ 'token_type': 'client',
+ 'state': 'present'
+ }
+
+ set_module_args(module_args)
+
+ with patch.object(nomad.api.acl.Acl, 'get_tokens') as mock_get_tokens:
+ with patch.object(nomad.api.acl.Acl, 'update_token') as mock_create_update_token:
+ mock_get_tokens.return_value = mock_acl_get_tokens()
+ mock_create_update_token.return_value = mock_acl_create_update_token()
+
+ with self.assertRaises(AnsibleExitJson):
+ self.module.main()
+ self.assertIs(mock_get_tokens.call_count, 1)
+ self.assertIs(mock_create_update_token.call_count, 1)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_npm.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_npm.py
index f5d312775..cc4d65172 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_npm.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_npm.py
@@ -48,8 +48,8 @@ class NPMModuleTestCase(ModuleTestCase):
self.assertTrue(result['changed'])
self.module_main_command.assert_has_calls([
- call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None),
- call(['/testbin/npm', 'install', '--global', 'coffee-script'], check_rc=True, cwd=None),
+ call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
+ call(['/testbin/npm', 'install', '--global', 'coffee-script'], check_rc=True, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
])
def test_present_missing(self):
@@ -67,8 +67,8 @@ class NPMModuleTestCase(ModuleTestCase):
self.assertTrue(result['changed'])
self.module_main_command.assert_has_calls([
- call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None),
- call(['/testbin/npm', 'install', '--global', 'coffee-script'], check_rc=True, cwd=None),
+ call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
+ call(['/testbin/npm', 'install', '--global', 'coffee-script'], check_rc=True, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
])
def test_present_version(self):
@@ -87,8 +87,8 @@ class NPMModuleTestCase(ModuleTestCase):
self.assertTrue(result['changed'])
self.module_main_command.assert_has_calls([
- call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None),
- call(['/testbin/npm', 'install', '--global', 'coffee-script@2.5.1'], check_rc=True, cwd=None),
+ call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
+ call(['/testbin/npm', 'install', '--global', 'coffee-script@2.5.1'], check_rc=True, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
])
def test_present_version_update(self):
@@ -107,8 +107,8 @@ class NPMModuleTestCase(ModuleTestCase):
self.assertTrue(result['changed'])
self.module_main_command.assert_has_calls([
- call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None),
- call(['/testbin/npm', 'install', '--global', 'coffee-script@2.5.1'], check_rc=True, cwd=None),
+ call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
+ call(['/testbin/npm', 'install', '--global', 'coffee-script@2.5.1'], check_rc=True, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
])
def test_present_version_exists(self):
@@ -127,7 +127,7 @@ class NPMModuleTestCase(ModuleTestCase):
self.assertFalse(result['changed'])
self.module_main_command.assert_has_calls([
- call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None),
+ call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
])
def test_absent(self):
@@ -145,8 +145,8 @@ class NPMModuleTestCase(ModuleTestCase):
self.assertTrue(result['changed'])
self.module_main_command.assert_has_calls([
- call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None),
- call(['/testbin/npm', 'uninstall', '--global', 'coffee-script'], check_rc=True, cwd=None),
+ call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
+ call(['/testbin/npm', 'uninstall', '--global', 'coffee-script'], check_rc=True, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
])
def test_absent_version(self):
@@ -165,8 +165,8 @@ class NPMModuleTestCase(ModuleTestCase):
self.assertTrue(result['changed'])
self.module_main_command.assert_has_calls([
- call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None),
- call(['/testbin/npm', 'uninstall', '--global', 'coffee-script'], check_rc=True, cwd=None),
+ call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
+ call(['/testbin/npm', 'uninstall', '--global', 'coffee-script'], check_rc=True, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
])
def test_absent_version_different(self):
@@ -185,8 +185,8 @@ class NPMModuleTestCase(ModuleTestCase):
self.assertTrue(result['changed'])
self.module_main_command.assert_has_calls([
- call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None),
- call(['/testbin/npm', 'uninstall', '--global', 'coffee-script'], check_rc=True, cwd=None),
+ call(['/testbin/npm', 'list', '--json', '--long', '--global'], check_rc=False, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
+ call(['/testbin/npm', 'uninstall', '--global', 'coffee-script'], check_rc=True, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
])
def test_present_package_json(self):
@@ -203,7 +203,7 @@ class NPMModuleTestCase(ModuleTestCase):
self.assertTrue(result['changed'])
self.module_main_command.assert_has_calls([
- call(['/testbin/npm', 'install', '--global'], check_rc=True, cwd=None),
+ call(['/testbin/npm', 'install', '--global'], check_rc=True, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
])
def test_present_package_json_production(self):
@@ -221,7 +221,7 @@ class NPMModuleTestCase(ModuleTestCase):
self.assertTrue(result['changed'])
self.module_main_command.assert_has_calls([
- call(['/testbin/npm', 'install', '--global', '--production'], check_rc=True, cwd=None),
+ call(['/testbin/npm', 'install', '--global', '--production'], check_rc=True, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
])
def test_present_package_json_ci(self):
@@ -239,7 +239,7 @@ class NPMModuleTestCase(ModuleTestCase):
self.assertTrue(result['changed'])
self.module_main_command.assert_has_calls([
- call(['/testbin/npm', 'ci', '--global'], check_rc=True, cwd=None),
+ call(['/testbin/npm', 'ci', '--global'], check_rc=True, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
])
def test_present_package_json_ci_production(self):
@@ -258,5 +258,5 @@ class NPMModuleTestCase(ModuleTestCase):
self.assertTrue(result['changed'])
self.module_main_command.assert_has_calls([
- call(['/testbin/npm', 'ci', '--global', '--production'], check_rc=True, cwd=None),
+ call(['/testbin/npm', 'ci', '--global', '--production'], check_rc=True, cwd=None, environ_update={'LANGUAGE': 'C', 'LC_ALL': 'C'}),
])
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_opkg.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_opkg.py
index 8e52368ff..c42025959 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_opkg.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_opkg.py
@@ -6,236 +6,9 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-import json
-from collections import namedtuple
from ansible_collections.community.general.plugins.modules import opkg
+from .helper import Helper
-import pytest
-TESTED_MODULE = opkg.__name__
-
-
-ModuleTestCase = namedtuple("ModuleTestCase", ["id", "input", "output", "run_command_calls"])
-RunCmdCall = namedtuple("RunCmdCall", ["command", "environ", "rc", "out", "err"])
-
-
-@pytest.fixture
-def patch_opkg(mocker):
- mocker.patch('ansible.module_utils.basic.AnsibleModule.get_bin_path', return_value='/testbin/opkg')
-
-
-TEST_CASES = [
- ModuleTestCase(
- id="install_zlibdev",
- input={"name": "zlib-dev", "state": "present"},
- output={
- "msg": "installed 1 package(s)"
- },
- run_command_calls=[
- RunCmdCall(
- command=["/testbin/opkg", "list-installed", "zlib-dev"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="",
- err="",
- ),
- RunCmdCall(
- command=["/testbin/opkg", "install", "zlib-dev"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out=(
- "Installing zlib-dev (1.2.11-6) to root..."
- "Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib-dev_1.2.11-6_mips_24kc.ipk"
- "Installing zlib (1.2.11-6) to root..."
- "Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib_1.2.11-6_mips_24kc.ipk"
- "Configuring zlib."
- "Configuring zlib-dev."
- ),
- err="",
- ),
- RunCmdCall(
- command=["/testbin/opkg", "list-installed", "zlib-dev"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="zlib-dev - 1.2.11-6\n",
- err="",
- ),
- ],
- ),
- ModuleTestCase(
- id="install_zlibdev_present",
- input={"name": "zlib-dev", "state": "present"},
- output={
- "msg": "package(s) already present"
- },
- run_command_calls=[
- RunCmdCall(
- command=["/testbin/opkg", "list-installed", "zlib-dev"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="zlib-dev - 1.2.11-6\n",
- err="",
- ),
- ],
- ),
- ModuleTestCase(
- id="install_zlibdev_force_reinstall",
- input={"name": "zlib-dev", "state": "present", "force": "reinstall"},
- output={
- "msg": "installed 1 package(s)"
- },
- run_command_calls=[
- RunCmdCall(
- command=["/testbin/opkg", "list-installed", "zlib-dev"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="zlib-dev - 1.2.11-6\n",
- err="",
- ),
- RunCmdCall(
- command=["/testbin/opkg", "install", "--force-reinstall", "zlib-dev"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out=(
- "Installing zlib-dev (1.2.11-6) to root...\n"
- "Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib-dev_1.2.11-6_mips_24kc.ipk\n"
- "Configuring zlib-dev.\n"
- ),
- err="",
- ),
- RunCmdCall(
- command=["/testbin/opkg", "list-installed", "zlib-dev"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="zlib-dev - 1.2.11-6\n",
- err="",
- ),
- ],
- ),
- ModuleTestCase(
- id="install_zlibdev_with_version",
- input={"name": "zlib-dev=1.2.11-6", "state": "present"},
- output={
- "msg": "installed 1 package(s)"
- },
- run_command_calls=[
- RunCmdCall(
- command=["/testbin/opkg", "list-installed", "zlib-dev"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="",
- err="",
- ),
- RunCmdCall(
- command=["/testbin/opkg", "install", "zlib-dev=1.2.11-6"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out=(
- "Installing zlib-dev (1.2.11-6) to root..."
- "Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib-dev_1.2.11-6_mips_24kc.ipk"
- "Installing zlib (1.2.11-6) to root..."
- "Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib_1.2.11-6_mips_24kc.ipk"
- "Configuring zlib."
- "Configuring zlib-dev."
- ),
- err="",
- ),
- RunCmdCall(
- command=["/testbin/opkg", "list-installed", "zlib-dev"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="zlib-dev - 1.2.11-6 \n", # This output has the extra space at the end, to satisfy the behaviour of Yocto/OpenEmbedded's opkg
- err="",
- ),
- ],
- ),
- ModuleTestCase(
- id="install_vim_updatecache",
- input={"name": "vim-fuller", "state": "present", "update_cache": True},
- output={
- "msg": "installed 1 package(s)"
- },
- run_command_calls=[
- RunCmdCall(
- command=["/testbin/opkg", "update"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="",
- err="",
- ),
- RunCmdCall(
- command=["/testbin/opkg", "list-installed", "vim-fuller"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="",
- err="",
- ),
- RunCmdCall(
- command=["/testbin/opkg", "install", "vim-fuller"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out=(
- "Multiple packages (libgcc1 and libgcc1) providing same name marked HOLD or PREFER. Using latest.\n"
- "Installing vim-fuller (9.0-1) to root...\n"
- "Downloading https://downloads.openwrt.org/snapshots/packages/x86_64/packages/vim-fuller_9.0-1_x86_64.ipk\n"
- "Installing terminfo (6.4-2) to root...\n"
- "Downloading https://downloads.openwrt.org/snapshots/packages/x86_64/base/terminfo_6.4-2_x86_64.ipk\n"
- "Installing libncurses6 (6.4-2) to root...\n"
- "Downloading https://downloads.openwrt.org/snapshots/packages/x86_64/base/libncurses6_6.4-2_x86_64.ipk\n"
- "Configuring terminfo.\n"
- "Configuring libncurses6.\n"
- "Configuring vim-fuller.\n"
- ),
- err="",
- ),
- RunCmdCall(
- command=["/testbin/opkg", "list-installed", "vim-fuller"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="vim-fuller - 9.0-1 \n", # This output has the extra space at the end, to satisfy the behaviour of Yocto/OpenEmbedded's opkg
- err="",
- ),
- ],
- ),
-]
-TEST_CASES_IDS = [item.id for item in TEST_CASES]
-
-
-@pytest.mark.parametrize('patch_ansible_module, testcase',
- [[x.input, x] for x in TEST_CASES],
- ids=TEST_CASES_IDS,
- indirect=['patch_ansible_module'])
-@pytest.mark.usefixtures('patch_ansible_module')
-def test_opkg(mocker, capfd, patch_opkg, testcase):
- """
- Run unit tests for test cases listen in TEST_CASES
- """
-
- run_cmd_calls = testcase.run_command_calls
-
- # Mock function used for running commands first
- call_results = [(x.rc, x.out, x.err) for x in run_cmd_calls]
- mock_run_command = mocker.patch('ansible.module_utils.basic.AnsibleModule.run_command', side_effect=call_results)
-
- # Try to run test case
- with pytest.raises(SystemExit):
- opkg.main()
-
- out, err = capfd.readouterr()
- results = json.loads(out)
- print("testcase =\n%s" % str(testcase))
- print("results =\n%s" % results)
-
- for test_result in testcase.output:
- assert results[test_result] == testcase.output[test_result], \
- "'{0}': '{1}' != '{2}'".format(test_result, results[test_result], testcase.output[test_result])
-
- call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list]
- expected_call_args_list = [(item.command, item.environ) for item in run_cmd_calls]
- print("call args list =\n%s" % call_args_list)
- print("expected args list =\n%s" % expected_call_args_list)
-
- assert mock_run_command.call_count == len(run_cmd_calls)
- if mock_run_command.call_count:
- assert call_args_list == expected_call_args_list
+Helper.from_module(opkg, __name__)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_opkg.yaml b/ansible_collections/community/general/tests/unit/plugins/modules/test_opkg.yaml
new file mode 100644
index 000000000..6e227dea2
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_opkg.yaml
@@ -0,0 +1,142 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Alexei Znamensky (russoz@gmail.com)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+---
+- id: install_zlibdev
+ input:
+ name: zlib-dev
+ state: present
+ output:
+ msg: installed 1 package(s)
+ run_command_calls:
+ - command: [/testbin/opkg, list-installed, zlib-dev]
+ environ: &env-def {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
+ rc: 0
+ out: ""
+ err: ""
+ - command: [/testbin/opkg, install, zlib-dev]
+ environ: *env-def
+ rc: 0
+ out: |
+ Installing zlib-dev (1.2.11-6) to root...
+ Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib-dev_1.2.11-6_mips_24kc.ipk
+ Installing zlib (1.2.11-6) to root...
+ Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib_1.2.11-6_mips_24kc.ipk
+ Configuring zlib.
+ Configuring zlib-dev.
+ err: ""
+ - command: [/testbin/opkg, list-installed, zlib-dev]
+ environ: *env-def
+ rc: 0
+ out: |
+ zlib-dev - 1.2.11-6
+ err: ""
+- id: install_zlibdev_present
+ input:
+ name: zlib-dev
+ state: present
+ output:
+ msg: package(s) already present
+ run_command_calls:
+ - command: [/testbin/opkg, list-installed, zlib-dev]
+ environ: *env-def
+ rc: 0
+ out: |
+ zlib-dev - 1.2.11-6
+ err: ""
+- id: install_zlibdev_force_reinstall
+ input:
+ name: zlib-dev
+ state: present
+ force: reinstall
+ output:
+ msg: installed 1 package(s)
+ run_command_calls:
+ - command: [/testbin/opkg, list-installed, zlib-dev]
+ environ: *env-def
+ rc: 0
+ out: |
+ zlib-dev - 1.2.11-6
+ err: ""
+ - command: [/testbin/opkg, install, --force-reinstall, zlib-dev]
+ environ: *env-def
+ rc: 0
+ out: |
+ Installing zlib-dev (1.2.11-6) to root...
+ Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib-dev_1.2.11-6_mips_24kc.ipk
+ Configuring zlib-dev.
+ err: ""
+ - command: [/testbin/opkg, list-installed, zlib-dev]
+ environ: *env-def
+ rc: 0
+ out: |
+ zlib-dev - 1.2.11-6
+ err: ""
+- id: install_zlibdev_with_version
+ input:
+ name: zlib-dev=1.2.11-6
+ state: present
+ output:
+ msg: installed 1 package(s)
+ run_command_calls:
+ - command: [/testbin/opkg, list-installed, zlib-dev]
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+ - command: [/testbin/opkg, install, zlib-dev=1.2.11-6]
+ environ: *env-def
+ rc: 0
+ out: |
+ Installing zlib-dev (1.2.11-6) to root...
+ Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib-dev_1.2.11-6_mips_24kc.ipk
+ Installing zlib (1.2.11-6) to root...
+ Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib_1.2.11-6_mips_24kc.ipk
+ Configuring zlib.
+ Configuring zlib-dev.
+ err: ""
+ - command: [/testbin/opkg, list-installed, zlib-dev]
+ environ: *env-def
+ rc: 0
+ out: "zlib-dev - 1.2.11-6 \n" # This output has the extra space at the end, to satisfy the behaviour of Yocto/OpenEmbedded's opkg
+ err: ""
+- id: install_vim_updatecache
+ input:
+ name: vim-fuller
+ state: present
+ update_cache: true
+ output:
+ msg: installed 1 package(s)
+ run_command_calls:
+ - command: [/testbin/opkg, update]
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+ - command: [/testbin/opkg, list-installed, vim-fuller]
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+ - command: [/testbin/opkg, install, vim-fuller]
+ environ: *env-def
+ rc: 0
+ out: |
+ Multiple packages (libgcc1 and libgcc1) providing same name marked HOLD or PREFER. Using latest.
+ Installing vim-fuller (9.0-1) to root...
+ Downloading https://downloads.openwrt.org/snapshots/packages/x86_64/packages/vim-fuller_9.0-1_x86_64.ipk
+ Installing terminfo (6.4-2) to root...
+ Downloading https://downloads.openwrt.org/snapshots/packages/x86_64/base/terminfo_6.4-2_x86_64.ipk
+ Installing libncurses6 (6.4-2) to root...
+ Downloading https://downloads.openwrt.org/snapshots/packages/x86_64/base/libncurses6_6.4-2_x86_64.ipk
+ Configuring terminfo.
+ Configuring libncurses6.
+ Configuring vim-fuller.
+ err: ""
+ - command: [/testbin/opkg, list-installed, vim-fuller]
+ environ: *env-def
+ rc: 0
+ out: "vim-fuller - 9.0-1 \n" # This output has the extra space at the end, to satisfy the behaviour of Yocto/OpenEmbedded's opkg
+ err: ""
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty.py
index d363804bc..75987d3df 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty.py
@@ -20,9 +20,9 @@ class PagerDutyTest(unittest.TestCase):
return object(), {'status': 200}
def _assert_ongoing_window_with_v1_compatible_header(self, module, url, headers, data=None, method=None):
- self.assertDictContainsSubset(
- {'Accept': 'application/vnd.pagerduty+json;version=2'},
- headers,
+ self.assertEqual(
+ 'application/vnd.pagerduty+json;version=2',
+ headers.get('Accept'),
'Accept:application/vnd.pagerduty+json;version=2 HTTP header not found'
)
return object(), {'status': 200}
@@ -36,17 +36,17 @@ class PagerDutyTest(unittest.TestCase):
return object(), {'status': 201}
def _assert_create_a_maintenance_window_from_header(self, module, url, headers, data=None, method=None):
- self.assertDictContainsSubset(
- {'From': 'requester_id'},
- headers,
+ self.assertEqual(
+ 'requester_id',
+ headers.get('From'),
'From:requester_id HTTP header not found'
)
return object(), {'status': 201}
def _assert_create_window_with_v1_compatible_header(self, module, url, headers, data=None, method=None):
- self.assertDictContainsSubset(
- {'Accept': 'application/vnd.pagerduty+json;version=2'},
- headers,
+ self.assertEqual(
+ 'application/vnd.pagerduty+json;version=2',
+ headers.get('Accept'),
'Accept:application/vnd.pagerduty+json;version=2 HTTP header not found'
)
return object(), {'status': 201}
@@ -54,9 +54,9 @@ class PagerDutyTest(unittest.TestCase):
def _assert_create_window_payload(self, module, url, headers, data=None, method=None):
payload = json.loads(data)
window_data = payload['maintenance_window']
- self.assertTrue('start_time' in window_data, '"start_time" is requiered attribute')
- self.assertTrue('end_time' in window_data, '"end_time" is requiered attribute')
- self.assertTrue('services' in window_data, '"services" is requiered attribute')
+ self.assertTrue('start_time' in window_data, '"start_time" is required attribute')
+ self.assertTrue('end_time' in window_data, '"end_time" is required attribute')
+ self.assertTrue('services' in window_data, '"services" is required attribute')
return object(), {'status': 201}
def _assert_create_window_single_service(self, module, url, headers, data=None, method=None):
@@ -89,9 +89,9 @@ class PagerDutyTest(unittest.TestCase):
return object(), {'status': 204}
def _assert_absent_window_with_v1_compatible_header(self, module, url, headers, method=None):
- self.assertDictContainsSubset(
- {'Accept': 'application/vnd.pagerduty+json;version=2'},
- headers,
+ self.assertEqual(
+ 'application/vnd.pagerduty+json;version=2',
+ headers.get('Accept'),
'Accept:application/vnd.pagerduty+json;version=2 HTTP header not found'
)
return object(), {'status': 204}
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty_alert.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty_alert.py
index 3df992b42..7a1e951a2 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty_alert.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_pagerduty_alert.py
@@ -7,6 +7,10 @@ __metaclass__ = type
from ansible_collections.community.general.tests.unit.compat import unittest
from ansible_collections.community.general.plugins.modules import pagerduty_alert
+import json
+import pytest
+from ansible_collections.community.general.tests.unit.compat.mock import patch
+from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args
class PagerDutyAlertsTest(unittest.TestCase):
@@ -18,9 +22,9 @@ class PagerDutyAlertsTest(unittest.TestCase):
return Response(), {'status': 200}
def _assert_compatibility_header(self, module, url, method, headers):
- self.assertDictContainsSubset(
- {'Accept': 'application/vnd.pagerduty+json;version=2'},
- headers,
+ self.assertEqual(
+ 'application/vnd.pagerduty+json;version=2',
+ headers.get('Accept'),
'Accept:application/vnd.pagerduty+json;version=2 HTTP header not found'
)
return Response(), {'status': 200}
@@ -44,3 +48,106 @@ class PagerDutyAlertsTest(unittest.TestCase):
class Response(object):
def read(self):
return '{"incidents":[{"id": "incident_id", "status": "triggered"}]}'
+
+
+class TestPagerDutyAlertModule(ModuleTestCase):
+ def setUp(self):
+ super(TestPagerDutyAlertModule, self).setUp()
+ self.module = pagerduty_alert
+
+ def tearDown(self):
+ super(TestPagerDutyAlertModule, self).tearDown()
+
+ @pytest.fixture
+ def fetch_url_mock(self, mocker):
+ return mocker.patch('ansible.module_utils.monitoring.pagerduty_change.fetch_url')
+
+ def test_module_fail_when_required_args_missing(self):
+ with self.assertRaises(AnsibleFailJson):
+ set_module_args({})
+ self.module.main()
+
+ def test_ensure_alert_created_with_minimal_data(self):
+ set_module_args({
+ 'state': 'triggered',
+ 'api_version': 'v2',
+ 'integration_key': 'test',
+ 'source': 'My Ansible Script',
+ 'desc': 'Description for alert'
+ })
+
+ with patch.object(pagerduty_alert, 'fetch_url') as fetch_url_mock:
+ fetch_url_mock.return_value = (Response(), {"status": 202})
+ with self.assertRaises(AnsibleExitJson):
+ self.module.main()
+
+ assert fetch_url_mock.call_count == 1
+ url = fetch_url_mock.call_args[0][1]
+ json_data = fetch_url_mock.call_args[1]['data']
+ data = json.loads(json_data)
+
+ assert url == 'https://events.pagerduty.com/v2/enqueue'
+ assert data['routing_key'] == 'test'
+ assert data['event_action'] == 'trigger'
+ assert data['payload']['summary'] == 'Description for alert'
+ assert data['payload']['source'] == 'My Ansible Script'
+ assert data['payload']['severity'] == 'critical'
+ assert data['payload']['timestamp'] is not None
+
+ def test_ensure_alert_created_with_full_data(self):
+ set_module_args({
+ 'api_version': 'v2',
+ 'component': 'mysql',
+ 'custom_details': {'environment': 'production', 'notes': 'this is a test note'},
+ 'desc': 'Description for alert',
+ 'incident_class': 'ping failure',
+ 'integration_key': 'test',
+ 'link_url': 'https://pagerduty.com',
+ 'link_text': 'PagerDuty',
+ 'state': 'triggered',
+ 'source': 'My Ansible Script',
+ })
+
+ with patch.object(pagerduty_alert, 'fetch_url') as fetch_url_mock:
+ fetch_url_mock.return_value = (Response(), {"status": 202})
+ with self.assertRaises(AnsibleExitJson):
+ self.module.main()
+
+ assert fetch_url_mock.call_count == 1
+ url = fetch_url_mock.call_args[0][1]
+ json_data = fetch_url_mock.call_args[1]['data']
+ data = json.loads(json_data)
+
+ assert url == 'https://events.pagerduty.com/v2/enqueue'
+ assert data['routing_key'] == 'test'
+ assert data['payload']['summary'] == 'Description for alert'
+ assert data['payload']['source'] == 'My Ansible Script'
+ assert data['payload']['class'] == 'ping failure'
+ assert data['payload']['component'] == 'mysql'
+ assert data['payload']['custom_details']['environment'] == 'production'
+ assert data['payload']['custom_details']['notes'] == 'this is a test note'
+ assert data['links'][0]['href'] == 'https://pagerduty.com'
+ assert data['links'][0]['text'] == 'PagerDuty'
+
+ def test_ensure_alert_acknowledged(self):
+ set_module_args({
+ 'state': 'acknowledged',
+ 'api_version': 'v2',
+ 'integration_key': 'test',
+ 'incident_key': 'incident_test_id',
+ })
+
+ with patch.object(pagerduty_alert, 'fetch_url') as fetch_url_mock:
+ fetch_url_mock.return_value = (Response(), {"status": 202})
+ with self.assertRaises(AnsibleExitJson):
+ self.module.main()
+
+ assert fetch_url_mock.call_count == 1
+ url = fetch_url_mock.call_args[0][1]
+ json_data = fetch_url_mock.call_args[1]['data']
+ data = json.loads(json_data)
+
+ assert url == 'https://events.pagerduty.com/v2/enqueue'
+ assert data['routing_key'] == 'test'
+ assert data['event_action'] == 'acknowledge'
+ assert data['dedup_key'] == 'incident_test_id'
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_pkgin.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_pkgin.py
index d73911e0c..dea5a05b5 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_pkgin.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_pkgin.py
@@ -30,7 +30,7 @@ class TestPkginQueryPackage(unittest.TestCase):
command_result = pkgin.query_package(mock_module, package)
# then
- self.assertEquals(command_result, pkgin.PackageState.PRESENT)
+ self.assertEqual(command_result, pkgin.PackageState.PRESENT)
@mock.patch('ansible_collections.community.general.plugins.modules.pkgin.AnsibleModule')
def test_package_with_version_is_present(self, mock_module):
@@ -46,7 +46,7 @@ class TestPkginQueryPackage(unittest.TestCase):
command_result = pkgin.query_package(mock_module, package)
# then
- self.assertEquals(command_result, pkgin.PackageState.PRESENT)
+ self.assertEqual(command_result, pkgin.PackageState.PRESENT)
@mock.patch('ansible_collections.community.general.plugins.modules.pkgin.AnsibleModule')
def test_package_found_but_not_installed(self, mock_module):
@@ -62,7 +62,7 @@ class TestPkginQueryPackage(unittest.TestCase):
command_result = pkgin.query_package(mock_module, package)
# then
- self.assertEquals(command_result, pkgin.PackageState.NOT_INSTALLED)
+ self.assertEqual(command_result, pkgin.PackageState.NOT_INSTALLED)
@mock.patch('ansible_collections.community.general.plugins.modules.pkgin.AnsibleModule')
def test_package_found_outdated(self, mock_module):
@@ -78,7 +78,7 @@ class TestPkginQueryPackage(unittest.TestCase):
command_result = pkgin.query_package(mock_module, package)
# then
- self.assertEquals(command_result, pkgin.PackageState.OUTDATED)
+ self.assertEqual(command_result, pkgin.PackageState.OUTDATED)
@mock.patch('ansible_collections.community.general.plugins.modules.pkgin.AnsibleModule')
def test_package_with_version_found_outdated(self, mock_module):
@@ -94,7 +94,7 @@ class TestPkginQueryPackage(unittest.TestCase):
command_result = pkgin.query_package(mock_module, package)
# then
- self.assertEquals(command_result, pkgin.PackageState.OUTDATED)
+ self.assertEqual(command_result, pkgin.PackageState.OUTDATED)
@mock.patch('ansible_collections.community.general.plugins.modules.pkgin.AnsibleModule')
def test_package_not_found(self, mock_module):
@@ -110,7 +110,7 @@ class TestPkginQueryPackage(unittest.TestCase):
command_result = pkgin.query_package(mock_module, package)
# then
- self.assertEquals(command_result, pkgin.PackageState.NOT_FOUND)
+ self.assertEqual(command_result, pkgin.PackageState.NOT_FOUND)
@mock.patch('ansible_collections.community.general.plugins.modules.pkgin.AnsibleModule')
def test_with_parseable_flag_supported_package_is_present(self, mock_module):
@@ -126,7 +126,7 @@ class TestPkginQueryPackage(unittest.TestCase):
command_result = pkgin.query_package(mock_module, package)
# then
- self.assertEquals(command_result, pkgin.PackageState.PRESENT)
+ self.assertEqual(command_result, pkgin.PackageState.PRESENT)
@mock.patch('ansible_collections.community.general.plugins.modules.pkgin.AnsibleModule')
def test_with_parseable_flag_not_supported_package_is_present(self, mock_module):
@@ -142,4 +142,4 @@ class TestPkginQueryPackage(unittest.TestCase):
command_result = pkgin.query_package(mock_module, package)
# then
- self.assertEquals(command_result, pkgin.PackageState.PRESENT)
+ self.assertEqual(command_result, pkgin.PackageState.PRESENT)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_kvm.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_kvm.py
index 531185102..4e2cf032c 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_kvm.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_kvm.py
@@ -4,17 +4,165 @@
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
-from __future__ import (absolute_import, division, print_function)
+from __future__ import absolute_import, division, print_function
+
__metaclass__ = type
-from ansible_collections.community.general.plugins.modules.proxmox_kvm import parse_dev, parse_mac
+import sys
+
+import pytest
+
+proxmoxer = pytest.importorskip("proxmoxer")
+mandatory_py_version = pytest.mark.skipif(
+ sys.version_info < (2, 7),
+ reason="The proxmoxer dependency requires python2.7 or higher",
+)
+
+from ansible_collections.community.general.plugins.modules import proxmox_kvm
+from ansible_collections.community.general.tests.unit.compat.mock import (
+ patch,
+ DEFAULT,
+)
+from ansible_collections.community.general.tests.unit.plugins.modules.utils import (
+ AnsibleExitJson,
+ AnsibleFailJson,
+ ModuleTestCase,
+ set_module_args,
+)
+import ansible_collections.community.general.plugins.module_utils.proxmox as proxmox_utils
+
+
+class TestProxmoxKvmModule(ModuleTestCase):
+ def setUp(self):
+ super(TestProxmoxKvmModule, self).setUp()
+ proxmox_utils.HAS_PROXMOXER = True
+ self.module = proxmox_kvm
+ self.connect_mock = patch(
+ "ansible_collections.community.general.plugins.module_utils.proxmox.ProxmoxAnsible._connect"
+ ).start()
+ self.get_node_mock = patch.object(
+ proxmox_utils.ProxmoxAnsible, "get_node"
+ ).start()
+ self.get_vm_mock = patch.object(proxmox_utils.ProxmoxAnsible, "get_vm").start()
+ self.create_vm_mock = patch.object(
+ proxmox_kvm.ProxmoxKvmAnsible, "create_vm"
+ ).start()
+
+ def tearDown(self):
+ self.create_vm_mock.stop()
+ self.get_vm_mock.stop()
+ self.get_node_mock.stop()
+ self.connect_mock.stop()
+ super(TestProxmoxKvmModule, self).tearDown()
+
+ def test_module_fail_when_required_args_missing(self):
+ with self.assertRaises(AnsibleFailJson):
+ set_module_args({})
+ self.module.main()
+
+ def test_module_exits_unchaged_when_provided_vmid_exists(self):
+ set_module_args(
+ {
+ "api_host": "host",
+ "api_user": "user",
+ "api_password": "password",
+ "vmid": "100",
+ "node": "pve",
+ }
+ )
+ self.get_vm_mock.return_value = [{"vmid": "100"}]
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ self.module.main()
+
+ assert self.get_vm_mock.call_count == 1
+ result = exc_info.value.args[0]
+ assert result["changed"] is False
+ assert result["msg"] == "VM with vmid <100> already exists"
+
+ def test_vm_created_when_vmid_not_exist_but_name_already_exist(self):
+ set_module_args(
+ {
+ "api_host": "host",
+ "api_user": "user",
+ "api_password": "password",
+ "vmid": "100",
+ "name": "existing.vm.local",
+ "node": "pve",
+ }
+ )
+ self.get_vm_mock.return_value = None
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ self.module.main()
+
+ assert self.get_vm_mock.call_count == 1
+ assert self.get_node_mock.call_count == 1
+ result = exc_info.value.args[0]
+ assert result["changed"] is True
+ assert result["msg"] == "VM existing.vm.local with vmid 100 deployed"
+
+ def test_vm_not_created_when_name_already_exist_and_vmid_not_set(self):
+ set_module_args(
+ {
+ "api_host": "host",
+ "api_user": "user",
+ "api_password": "password",
+ "name": "existing.vm.local",
+ "node": "pve",
+ }
+ )
+ with patch.object(proxmox_utils.ProxmoxAnsible, "get_vmid") as get_vmid_mock:
+ get_vmid_mock.return_value = {
+ "vmid": 100,
+ "name": "existing.vm.local",
+ }
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ self.module.main()
+
+ assert get_vmid_mock.call_count == 1
+ result = exc_info.value.args[0]
+ assert result["changed"] is False
+ def test_vm_created_when_name_doesnt_exist_and_vmid_not_set(self):
+ set_module_args(
+ {
+ "api_host": "host",
+ "api_user": "user",
+ "api_password": "password",
+ "name": "existing.vm.local",
+ "node": "pve",
+ }
+ )
+ self.get_vm_mock.return_value = None
+ with patch.multiple(
+ proxmox_utils.ProxmoxAnsible, get_vmid=DEFAULT, get_nextvmid=DEFAULT
+ ) as utils_mock:
+ utils_mock["get_vmid"].return_value = None
+ utils_mock["get_nextvmid"].return_value = 101
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ self.module.main()
-def test_parse_mac():
- assert parse_mac('virtio=00:11:22:AA:BB:CC,bridge=vmbr0,firewall=1') == '00:11:22:AA:BB:CC'
+ assert utils_mock["get_vmid"].call_count == 1
+ assert utils_mock["get_nextvmid"].call_count == 1
+ result = exc_info.value.args[0]
+ assert result["changed"] is True
+ assert result["msg"] == "VM existing.vm.local with vmid 101 deployed"
+ def test_parse_mac(self):
+ assert (
+ proxmox_kvm.parse_mac("virtio=00:11:22:AA:BB:CC,bridge=vmbr0,firewall=1")
+ == "00:11:22:AA:BB:CC"
+ )
-def test_parse_dev():
- assert parse_dev('local-lvm:vm-1000-disk-0,format=qcow2') == 'local-lvm:vm-1000-disk-0'
- assert parse_dev('local-lvm:vm-101-disk-1,size=8G') == 'local-lvm:vm-101-disk-1'
- assert parse_dev('local-zfs:vm-1001-disk-0') == 'local-zfs:vm-1001-disk-0'
+ def test_parse_dev(self):
+ assert (
+ proxmox_kvm.parse_dev("local-lvm:vm-1000-disk-0,format=qcow2")
+ == "local-lvm:vm-1000-disk-0"
+ )
+ assert (
+ proxmox_kvm.parse_dev("local-lvm:vm-101-disk-1,size=8G")
+ == "local-lvm:vm-101-disk-1"
+ )
+ assert (
+ proxmox_kvm.parse_dev("local-zfs:vm-1001-disk-0")
+ == "local-zfs:vm-1001-disk-0"
+ )
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_snap.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_snap.py
index 4bdcaa8b7..545fcd1f5 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_snap.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_snap.py
@@ -7,8 +7,16 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import json
+import sys
+
import pytest
+proxmoxer = pytest.importorskip('proxmoxer')
+mandatory_py_version = pytest.mark.skipif(
+ sys.version_info < (2, 7),
+ reason='The proxmoxer dependency requires python2.7 or higher'
+)
+
from ansible_collections.community.general.tests.unit.compat.mock import MagicMock, patch
from ansible_collections.community.general.plugins.modules import proxmox_snap
import ansible_collections.community.general.plugins.module_utils.proxmox as proxmox_utils
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_storage_contents_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_storage_contents_info.py
new file mode 100644
index 000000000..df2625dba
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_storage_contents_info.py
@@ -0,0 +1,90 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2023, Julian Vanden Broeck <julian.vandenbroeck at dalibo.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import pytest
+
+proxmoxer = pytest.importorskip("proxmoxer")
+
+from ansible_collections.community.general.plugins.modules import proxmox_storage_contents_info
+from ansible_collections.community.general.tests.unit.compat.mock import patch
+from ansible_collections.community.general.tests.unit.plugins.modules.utils import (
+ AnsibleExitJson,
+ AnsibleFailJson,
+ ModuleTestCase,
+ set_module_args,
+)
+import ansible_collections.community.general.plugins.module_utils.proxmox as proxmox_utils
+
+NODE1 = "pve"
+RAW_LIST_OUTPUT = [
+ {
+ "content": "backup",
+ "ctime": 1702528474,
+ "format": "pbs-vm",
+ "size": 273804166061,
+ "subtype": "qemu",
+ "vmid": 931,
+ "volid": "datastore:backup/vm/931/2023-12-14T04:34:34Z",
+ },
+ {
+ "content": "backup",
+ "ctime": 1702582560,
+ "format": "pbs-vm",
+ "size": 273804166059,
+ "subtype": "qemu",
+ "vmid": 931,
+ "volid": "datastore:backup/vm/931/2023-12-14T19:36:00Z",
+ },
+]
+
+
+def get_module_args(node, storage, content="all", vmid=None):
+ return {
+ "api_host": "host",
+ "api_user": "user",
+ "api_password": "password",
+ "node": node,
+ "storage": storage,
+ "content": content,
+ "vmid": vmid,
+ }
+
+
+class TestProxmoxStorageContentsInfo(ModuleTestCase):
+ def setUp(self):
+ super(TestProxmoxStorageContentsInfo, self).setUp()
+ proxmox_utils.HAS_PROXMOXER = True
+ self.module = proxmox_storage_contents_info
+ self.connect_mock = patch(
+ "ansible_collections.community.general.plugins.module_utils.proxmox.ProxmoxAnsible._connect",
+ ).start()
+ self.connect_mock.return_value.nodes.return_value.storage.return_value.content.return_value.get.return_value = (
+ RAW_LIST_OUTPUT
+ )
+ self.connect_mock.return_value.nodes.get.return_value = [{"node": NODE1}]
+
+ def tearDown(self):
+ self.connect_mock.stop()
+ super(TestProxmoxStorageContentsInfo, self).tearDown()
+
+ def test_module_fail_when_required_args_missing(self):
+ with pytest.raises(AnsibleFailJson) as exc_info:
+ set_module_args({})
+ self.module.main()
+
+ def test_storage_contents_info(self):
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ set_module_args(get_module_args(node=NODE1, storage="datastore"))
+ expected_output = {}
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert not result["changed"]
+ assert result["proxmox_storage_content"] == RAW_LIST_OUTPUT
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_tasks_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_tasks_info.py
index 0d1b5a7bf..5c228655b 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_tasks_info.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_tasks_info.py
@@ -10,8 +10,16 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-import pytest
import json
+import sys
+
+import pytest
+
+proxmoxer = pytest.importorskip('proxmoxer')
+mandatory_py_version = pytest.mark.skipif(
+ sys.version_info < (2, 7),
+ reason='The proxmoxer dependency requires python2.7 or higher'
+)
from ansible_collections.community.general.plugins.modules import proxmox_tasks_info
import ansible_collections.community.general.plugins.module_utils.proxmox as proxmox_utils
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_template.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_template.py
new file mode 100644
index 000000000..dc09a44b3
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_template.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2023, Sergei Antipov <greendayonfire at gmail.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import os
+import sys
+
+import pytest
+
+proxmoxer = pytest.importorskip('proxmoxer')
+mandatory_py_version = pytest.mark.skipif(
+ sys.version_info < (2, 7),
+ reason='The proxmoxer dependency requires python2.7 or higher'
+)
+
+from ansible_collections.community.general.plugins.modules import proxmox_template
+from ansible_collections.community.general.tests.unit.compat.mock import patch, Mock
+from ansible_collections.community.general.tests.unit.plugins.modules.utils import (
+ AnsibleFailJson,
+ ModuleTestCase,
+ set_module_args,
+)
+import ansible_collections.community.general.plugins.module_utils.proxmox as proxmox_utils
+
+
+class TestProxmoxTemplateModule(ModuleTestCase):
+ def setUp(self):
+ super(TestProxmoxTemplateModule, self).setUp()
+ proxmox_utils.HAS_PROXMOXER = True
+ self.module = proxmox_template
+ self.connect_mock = patch(
+ "ansible_collections.community.general.plugins.module_utils.proxmox.ProxmoxAnsible._connect"
+ )
+ self.connect_mock.start()
+
+ def tearDown(self):
+ self.connect_mock.stop()
+ super(TestProxmoxTemplateModule, self).tearDown()
+
+ @patch("os.stat")
+ @patch.multiple(os.path, exists=Mock(return_value=True), isfile=Mock(return_value=True))
+ def test_module_fail_when_toolbelt_not_installed_and_file_size_is_big(self, mock_stat):
+ self.module.HAS_REQUESTS_TOOLBELT = False
+ mock_stat.return_value.st_size = 268435460
+ set_module_args(
+ {
+ "api_host": "host",
+ "api_user": "user",
+ "api_password": "password",
+ "node": "pve",
+ "src": "/tmp/mock.iso",
+ "content_type": "iso"
+ }
+ )
+ with pytest.raises(AnsibleFailJson) as exc_info:
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["failed"] is True
+ assert result["msg"] == "'requests_toolbelt' module is required to upload files larger than 256MB"
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_vm_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_vm_info.py
new file mode 100644
index 000000000..94bbbc948
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_proxmox_vm_info.py
@@ -0,0 +1,714 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2023, Sergei Antipov <greendayonfire at gmail.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import sys
+
+import pytest
+
+proxmoxer = pytest.importorskip("proxmoxer")
+mandatory_py_version = pytest.mark.skipif(
+ sys.version_info < (2, 7),
+ reason="The proxmoxer dependency requires python2.7 or higher",
+)
+
+from ansible_collections.community.general.plugins.modules import proxmox_vm_info
+from ansible_collections.community.general.tests.unit.compat.mock import patch
+from ansible_collections.community.general.tests.unit.plugins.modules.utils import (
+ AnsibleExitJson,
+ AnsibleFailJson,
+ ModuleTestCase,
+ set_module_args,
+)
+import ansible_collections.community.general.plugins.module_utils.proxmox as proxmox_utils
+
+NODE1 = "pve"
+NODE2 = "pve2"
+RAW_CLUSTER_OUTPUT = [
+ {
+ "cpu": 0.174069059487628,
+ "disk": 0,
+ "diskread": 6656,
+ "diskwrite": 0,
+ "id": "qemu/100",
+ "maxcpu": 1,
+ "maxdisk": 34359738368,
+ "maxmem": 4294967296,
+ "mem": 35304543,
+ "name": "pxe.home.arpa",
+ "netin": 416956,
+ "netout": 17330,
+ "node": NODE1,
+ "status": "running",
+ "template": 0,
+ "type": "qemu",
+ "uptime": 669,
+ "vmid": 100,
+ },
+ {
+ "cpu": 0,
+ "disk": 0,
+ "diskread": 0,
+ "diskwrite": 0,
+ "id": "qemu/101",
+ "maxcpu": 1,
+ "maxdisk": 0,
+ "maxmem": 536870912,
+ "mem": 0,
+ "name": "test1",
+ "netin": 0,
+ "netout": 0,
+ "node": NODE2,
+ "pool": "pool1",
+ "status": "stopped",
+ "template": 0,
+ "type": "qemu",
+ "uptime": 0,
+ "vmid": 101,
+ },
+ {
+ "cpu": 0,
+ "disk": 352190464,
+ "diskread": 0,
+ "diskwrite": 0,
+ "id": "lxc/102",
+ "maxcpu": 2,
+ "maxdisk": 10737418240,
+ "maxmem": 536870912,
+ "mem": 28192768,
+ "name": "test-lxc.home.arpa",
+ "netin": 102757,
+ "netout": 446,
+ "node": NODE1,
+ "status": "running",
+ "template": 0,
+ "type": "lxc",
+ "uptime": 161,
+ "vmid": 102,
+ },
+ {
+ "cpu": 0,
+ "disk": 0,
+ "diskread": 0,
+ "diskwrite": 0,
+ "id": "lxc/103",
+ "maxcpu": 2,
+ "maxdisk": 10737418240,
+ "maxmem": 536870912,
+ "mem": 0,
+ "name": "test1-lxc.home.arpa",
+ "netin": 0,
+ "netout": 0,
+ "node": NODE2,
+ "pool": "pool1",
+ "status": "stopped",
+ "template": 0,
+ "type": "lxc",
+ "uptime": 0,
+ "vmid": 103,
+ },
+ {
+ "cpu": 0,
+ "disk": 0,
+ "diskread": 0,
+ "diskwrite": 0,
+ "id": "lxc/104",
+ "maxcpu": 2,
+ "maxdisk": 10737418240,
+ "maxmem": 536870912,
+ "mem": 0,
+ "name": "test-lxc.home.arpa",
+ "netin": 0,
+ "netout": 0,
+ "node": NODE2,
+ "pool": "pool1",
+ "status": "stopped",
+ "template": 0,
+ "type": "lxc",
+ "uptime": 0,
+ "vmid": 104,
+ },
+ {
+ "cpu": 0,
+ "disk": 0,
+ "diskread": 0,
+ "diskwrite": 0,
+ "id": "lxc/105",
+ "maxcpu": 2,
+ "maxdisk": 10737418240,
+ "maxmem": 536870912,
+ "mem": 0,
+ "name": "",
+ "netin": 0,
+ "netout": 0,
+ "node": NODE2,
+ "pool": "pool1",
+ "status": "stopped",
+ "template": 0,
+ "type": "lxc",
+ "uptime": 0,
+ "vmid": 105,
+ },
+]
+RAW_LXC_OUTPUT = [
+ {
+ "cpu": 0,
+ "cpus": 2,
+ "disk": 0,
+ "diskread": 0,
+ "diskwrite": 0,
+ "maxdisk": 10737418240,
+ "maxmem": 536870912,
+ "maxswap": 536870912,
+ "mem": 0,
+ "name": "test1-lxc.home.arpa",
+ "netin": 0,
+ "netout": 0,
+ "status": "stopped",
+ "swap": 0,
+ "type": "lxc",
+ "uptime": 0,
+ "vmid": "103",
+ },
+ {
+ "cpu": 0,
+ "cpus": 2,
+ "disk": 352190464,
+ "diskread": 0,
+ "diskwrite": 0,
+ "maxdisk": 10737418240,
+ "maxmem": 536870912,
+ "maxswap": 536870912,
+ "mem": 28192768,
+ "name": "test-lxc.home.arpa",
+ "netin": 102757,
+ "netout": 446,
+ "pid": 4076752,
+ "status": "running",
+ "swap": 0,
+ "type": "lxc",
+ "uptime": 161,
+ "vmid": "102",
+ },
+ {
+ "cpu": 0,
+ "cpus": 2,
+ "disk": 0,
+ "diskread": 0,
+ "diskwrite": 0,
+ "maxdisk": 10737418240,
+ "maxmem": 536870912,
+ "maxswap": 536870912,
+ "mem": 0,
+ "name": "test-lxc.home.arpa",
+ "netin": 0,
+ "netout": 0,
+ "status": "stopped",
+ "swap": 0,
+ "type": "lxc",
+ "uptime": 0,
+ "vmid": "104",
+ },
+ {
+ "cpu": 0,
+ "cpus": 2,
+ "disk": 0,
+ "diskread": 0,
+ "diskwrite": 0,
+ "maxdisk": 10737418240,
+ "maxmem": 536870912,
+ "maxswap": 536870912,
+ "mem": 0,
+ "name": "",
+ "netin": 0,
+ "netout": 0,
+ "status": "stopped",
+ "swap": 0,
+ "type": "lxc",
+ "uptime": 0,
+ "vmid": "105",
+ },
+]
+RAW_QEMU_OUTPUT = [
+ {
+ "cpu": 0,
+ "cpus": 1,
+ "disk": 0,
+ "diskread": 0,
+ "diskwrite": 0,
+ "maxdisk": 0,
+ "maxmem": 536870912,
+ "mem": 0,
+ "name": "test1",
+ "netin": 0,
+ "netout": 0,
+ "status": "stopped",
+ "uptime": 0,
+ "vmid": 101,
+ },
+ {
+ "cpu": 0.174069059487628,
+ "cpus": 1,
+ "disk": 0,
+ "diskread": 6656,
+ "diskwrite": 0,
+ "maxdisk": 34359738368,
+ "maxmem": 4294967296,
+ "mem": 35304543,
+ "name": "pxe.home.arpa",
+ "netin": 416956,
+ "netout": 17330,
+ "pid": 4076688,
+ "status": "running",
+ "uptime": 669,
+ "vmid": 100,
+ },
+]
+EXPECTED_VMS_OUTPUT = [
+ {
+ "cpu": 0.174069059487628,
+ "cpus": 1,
+ "disk": 0,
+ "diskread": 6656,
+ "diskwrite": 0,
+ "id": "qemu/100",
+ "maxcpu": 1,
+ "maxdisk": 34359738368,
+ "maxmem": 4294967296,
+ "mem": 35304543,
+ "name": "pxe.home.arpa",
+ "netin": 416956,
+ "netout": 17330,
+ "node": NODE1,
+ "pid": 4076688,
+ "status": "running",
+ "template": False,
+ "type": "qemu",
+ "uptime": 669,
+ "vmid": 100,
+ },
+ {
+ "cpu": 0,
+ "cpus": 1,
+ "disk": 0,
+ "diskread": 0,
+ "diskwrite": 0,
+ "id": "qemu/101",
+ "maxcpu": 1,
+ "maxdisk": 0,
+ "maxmem": 536870912,
+ "mem": 0,
+ "name": "test1",
+ "netin": 0,
+ "netout": 0,
+ "node": NODE2,
+ "pool": "pool1",
+ "status": "stopped",
+ "template": False,
+ "type": "qemu",
+ "uptime": 0,
+ "vmid": 101,
+ },
+ {
+ "cpu": 0,
+ "cpus": 2,
+ "disk": 352190464,
+ "diskread": 0,
+ "diskwrite": 0,
+ "id": "lxc/102",
+ "maxcpu": 2,
+ "maxdisk": 10737418240,
+ "maxmem": 536870912,
+ "maxswap": 536870912,
+ "mem": 28192768,
+ "name": "test-lxc.home.arpa",
+ "netin": 102757,
+ "netout": 446,
+ "node": NODE1,
+ "pid": 4076752,
+ "status": "running",
+ "swap": 0,
+ "template": False,
+ "type": "lxc",
+ "uptime": 161,
+ "vmid": 102,
+ },
+ {
+ "cpu": 0,
+ "cpus": 2,
+ "disk": 0,
+ "diskread": 0,
+ "diskwrite": 0,
+ "id": "lxc/103",
+ "maxcpu": 2,
+ "maxdisk": 10737418240,
+ "maxmem": 536870912,
+ "maxswap": 536870912,
+ "mem": 0,
+ "name": "test1-lxc.home.arpa",
+ "netin": 0,
+ "netout": 0,
+ "node": NODE2,
+ "pool": "pool1",
+ "status": "stopped",
+ "swap": 0,
+ "template": False,
+ "type": "lxc",
+ "uptime": 0,
+ "vmid": 103,
+ },
+ {
+ "cpu": 0,
+ "cpus": 2,
+ "disk": 0,
+ "diskread": 0,
+ "diskwrite": 0,
+ "id": "lxc/104",
+ "maxcpu": 2,
+ "maxdisk": 10737418240,
+ "maxmem": 536870912,
+ "maxswap": 536870912,
+ "mem": 0,
+ "name": "test-lxc.home.arpa",
+ "netin": 0,
+ "netout": 0,
+ "node": NODE2,
+ "pool": "pool1",
+ "status": "stopped",
+ "swap": 0,
+ "template": False,
+ "type": "lxc",
+ "uptime": 0,
+ "vmid": 104,
+ },
+ {
+ "cpu": 0,
+ "cpus": 2,
+ "disk": 0,
+ "diskread": 0,
+ "diskwrite": 0,
+ "id": "lxc/105",
+ "maxcpu": 2,
+ "maxdisk": 10737418240,
+ "maxmem": 536870912,
+ "maxswap": 536870912,
+ "mem": 0,
+ "name": "",
+ "netin": 0,
+ "netout": 0,
+ "node": NODE2,
+ "pool": "pool1",
+ "status": "stopped",
+ "swap": 0,
+ "template": False,
+ "type": "lxc",
+ "uptime": 0,
+ "vmid": 105,
+ },
+]
+
+
+def get_module_args(type="all", node=None, vmid=None, name=None, config="none"):
+ return {
+ "api_host": "host",
+ "api_user": "user",
+ "api_password": "password",
+ "node": node,
+ "type": type,
+ "vmid": vmid,
+ "name": name,
+ "config": config,
+ }
+
+
+class TestProxmoxVmInfoModule(ModuleTestCase):
+ def setUp(self):
+ super(TestProxmoxVmInfoModule, self).setUp()
+ proxmox_utils.HAS_PROXMOXER = True
+ self.module = proxmox_vm_info
+ self.connect_mock = patch(
+ "ansible_collections.community.general.plugins.module_utils.proxmox.ProxmoxAnsible._connect",
+ ).start()
+ self.connect_mock.return_value.nodes.return_value.lxc.return_value.get.return_value = (
+ RAW_LXC_OUTPUT
+ )
+ self.connect_mock.return_value.nodes.return_value.qemu.return_value.get.return_value = (
+ RAW_QEMU_OUTPUT
+ )
+ self.connect_mock.return_value.cluster.return_value.resources.return_value.get.return_value = (
+ RAW_CLUSTER_OUTPUT
+ )
+ self.connect_mock.return_value.nodes.get.return_value = [{"node": NODE1}]
+
+ def tearDown(self):
+ self.connect_mock.stop()
+ super(TestProxmoxVmInfoModule, self).tearDown()
+
+ def test_module_fail_when_required_args_missing(self):
+ with pytest.raises(AnsibleFailJson) as exc_info:
+ set_module_args({})
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["msg"] == "missing required arguments: api_host, api_user"
+
+ def test_get_lxc_vms_information(self):
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ set_module_args(get_module_args(type="lxc"))
+ expected_output = [vm for vm in EXPECTED_VMS_OUTPUT if vm["type"] == "lxc"]
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["changed"] is False
+ assert result["proxmox_vms"] == expected_output
+
+ def test_get_qemu_vms_information(self):
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ set_module_args(get_module_args(type="qemu"))
+ expected_output = [vm for vm in EXPECTED_VMS_OUTPUT if vm["type"] == "qemu"]
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["proxmox_vms"] == expected_output
+
+ def test_get_all_vms_information(self):
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ set_module_args(get_module_args())
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["proxmox_vms"] == EXPECTED_VMS_OUTPUT
+
+ def test_vmid_is_converted_to_int(self):
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ set_module_args(get_module_args(type="lxc"))
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert isinstance(result["proxmox_vms"][0]["vmid"], int)
+
+ def test_get_specific_lxc_vm_information(self):
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ vmid = 102
+ expected_output = [
+ vm
+ for vm in EXPECTED_VMS_OUTPUT
+ if vm["vmid"] == vmid and vm["type"] == "lxc"
+ ]
+ set_module_args(get_module_args(type="lxc", vmid=vmid))
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["proxmox_vms"] == expected_output
+ assert len(result["proxmox_vms"]) == 1
+
+ def test_get_specific_qemu_vm_information(self):
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ vmid = 100
+ expected_output = [
+ vm
+ for vm in EXPECTED_VMS_OUTPUT
+ if vm["vmid"] == vmid and vm["type"] == "qemu"
+ ]
+ set_module_args(get_module_args(type="qemu", vmid=vmid))
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["proxmox_vms"] == expected_output
+ assert len(result["proxmox_vms"]) == 1
+
+ def test_get_specific_vm_information(self):
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ vmid = 100
+ expected_output = [vm for vm in EXPECTED_VMS_OUTPUT if vm["vmid"] == vmid]
+ set_module_args(get_module_args(type="all", vmid=vmid))
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["proxmox_vms"] == expected_output
+ assert len(result["proxmox_vms"]) == 1
+
+ def test_get_specific_vm_information_by_using_name(self):
+ name = "test1-lxc.home.arpa"
+ self.connect_mock.return_value.cluster.resources.get.return_value = [
+ {"name": name, "vmid": "103"}
+ ]
+
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ expected_output = [vm for vm in EXPECTED_VMS_OUTPUT if vm["name"] == name]
+ set_module_args(get_module_args(type="all", name=name))
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["proxmox_vms"] == expected_output
+ assert len(result["proxmox_vms"]) == 1
+
+ def test_get_multiple_vms_with_the_same_name(self):
+ name = "test-lxc.home.arpa"
+ self.connect_mock.return_value.cluster.resources.get.return_value = [
+ {"name": name, "vmid": "102"},
+ {"name": name, "vmid": "104"},
+ ]
+
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ expected_output = [vm for vm in EXPECTED_VMS_OUTPUT if vm["name"] == name]
+ set_module_args(get_module_args(type="all", name=name))
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["proxmox_vms"] == expected_output
+ assert len(result["proxmox_vms"]) == 2
+
+ def test_get_vm_with_an_empty_name(self):
+ name = ""
+ self.connect_mock.return_value.cluster.resources.get.return_value = [
+ {"name": name, "vmid": "105"},
+ ]
+
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ expected_output = [vm for vm in EXPECTED_VMS_OUTPUT if vm["name"] == name]
+ set_module_args(get_module_args(type="all", name=name))
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["proxmox_vms"] == expected_output
+ assert len(result["proxmox_vms"]) == 1
+
+ def test_get_all_lxc_vms_from_specific_node(self):
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ expected_output = [
+ vm
+ for vm in EXPECTED_VMS_OUTPUT
+ if vm["node"] == NODE1 and vm["type"] == "lxc"
+ ]
+ set_module_args(get_module_args(type="lxc", node=NODE1))
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["proxmox_vms"] == expected_output
+ assert len(result["proxmox_vms"]) == 1
+
+ def test_get_all_qemu_vms_from_specific_node(self):
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ expected_output = [
+ vm
+ for vm in EXPECTED_VMS_OUTPUT
+ if vm["node"] == NODE1 and vm["type"] == "qemu"
+ ]
+ set_module_args(get_module_args(type="qemu", node=NODE1))
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["proxmox_vms"] == expected_output
+ assert len(result["proxmox_vms"]) == 1
+
+ def test_get_all_vms_from_specific_node(self):
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ expected_output = [vm for vm in EXPECTED_VMS_OUTPUT if vm["node"] == NODE1]
+ set_module_args(get_module_args(node=NODE1))
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["proxmox_vms"] == expected_output
+ assert len(result["proxmox_vms"]) == 2
+
+ def test_module_returns_empty_list_when_vm_does_not_exist(self):
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ vmid = 200
+ set_module_args(get_module_args(type="all", vmid=vmid))
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["proxmox_vms"] == []
+
+ def test_module_fail_when_qemu_request_fails(self):
+ self.connect_mock.return_value.nodes.return_value.qemu.return_value.get.side_effect = IOError(
+ "Some mocked connection error."
+ )
+ with pytest.raises(AnsibleFailJson) as exc_info:
+ set_module_args(get_module_args(type="qemu"))
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert "Failed to retrieve QEMU VMs information:" in result["msg"]
+
+ def test_module_fail_when_lxc_request_fails(self):
+ self.connect_mock.return_value.nodes.return_value.lxc.return_value.get.side_effect = IOError(
+ "Some mocked connection error."
+ )
+ with pytest.raises(AnsibleFailJson) as exc_info:
+ set_module_args(get_module_args(type="lxc"))
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert "Failed to retrieve LXC VMs information:" in result["msg"]
+
+ def test_module_fail_when_cluster_resources_request_fails(self):
+ self.connect_mock.return_value.cluster.return_value.resources.return_value.get.side_effect = IOError(
+ "Some mocked connection error."
+ )
+ with pytest.raises(AnsibleFailJson) as exc_info:
+ set_module_args(get_module_args())
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert (
+ "Failed to retrieve VMs information from cluster resources:"
+ in result["msg"]
+ )
+
+ def test_module_fail_when_node_does_not_exist(self):
+ with pytest.raises(AnsibleFailJson) as exc_info:
+ set_module_args(get_module_args(type="all", node="NODE3"))
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["msg"] == "Node NODE3 doesn't exist in PVE cluster"
+
+ def test_call_to_get_vmid_is_not_used_when_vmid_provided(self):
+ with patch(
+ "ansible_collections.community.general.plugins.module_utils.proxmox.ProxmoxAnsible.get_vmid"
+ ) as get_vmid_mock:
+ with pytest.raises(AnsibleExitJson):
+ vmid = 100
+ set_module_args(
+ get_module_args(type="all", vmid=vmid, name="something")
+ )
+ self.module.main()
+
+ assert get_vmid_mock.call_count == 0
+
+ def test_config_returned_when_specified_qemu_vm_with_config(self):
+ config_vm_value = {
+ 'scsi0': 'local-lvm:vm-101-disk-0,iothread=1,size=32G',
+ 'net0': 'virtio=4E:79:9F:A8:EE:E4,bridge=vmbr0,firewall=1',
+ 'scsihw': 'virtio-scsi-single',
+ 'cores': 1,
+ 'name': 'test1',
+ 'ostype': 'l26',
+ 'boot': 'order=scsi0;ide2;net0',
+ 'memory': 2048,
+ 'sockets': 1,
+ }
+ (self.connect_mock.return_value.nodes.return_value.qemu.return_value.
+ config.return_value.get.return_value) = config_vm_value
+
+ with pytest.raises(AnsibleExitJson) as exc_info:
+ vmid = 101
+ set_module_args(get_module_args(
+ type="qemu",
+ vmid=vmid,
+ config="current",
+ ))
+ expected_output = [vm for vm in EXPECTED_VMS_OUTPUT if vm["vmid"] == vmid]
+ expected_output[0]["config"] = config_vm_value
+ self.module.main()
+
+ result = exc_info.value.args[0]
+ assert result["proxmox_vms"] == expected_output
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_puppet.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_puppet.py
index f62523e7f..57f88ada1 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_puppet.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_puppet.py
@@ -12,216 +12,9 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-import json
-from collections import namedtuple
from ansible_collections.community.general.plugins.modules import puppet
+from .helper import Helper
-import pytest
-TESTED_MODULE = puppet.__name__
-
-
-ModuleTestCase = namedtuple("ModuleTestCase", ["id", "input", "output", "run_command_calls"])
-RunCmdCall = namedtuple("RunCmdCall", ["command", "environ", "rc", "out", "err"])
-
-
-@pytest.fixture
-def patch_get_bin_path(mocker):
- """
- Function used for mocking AnsibleModule.get_bin_path
- """
- def mockie(self, path, *args, **kwargs):
- return "/testbin/{0}".format(path)
- mocker.patch("ansible.module_utils.basic.AnsibleModule.get_bin_path", mockie)
-
-
-TEST_CASES = [
- ModuleTestCase(
- id="puppet_agent_plain",
- input={},
- output=dict(changed=False),
- run_command_calls=[
- RunCmdCall(
- command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="blah, anything",
- err="",
- ),
- RunCmdCall(
- command=[
- "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
- "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0"
- ],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="",
- err="",
- ),
- ]
- ),
- ModuleTestCase(
- id="puppet_agent_certname",
- input={"certname": "potatobox"},
- output=dict(changed=False),
- run_command_calls=[
- RunCmdCall(
- command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="blah, anything",
- err="",
- ),
- RunCmdCall(
- command=[
- "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
- "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--certname=potatobox"
- ],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="",
- err="",
- ),
- ]
- ),
- ModuleTestCase(
- id="puppet_agent_tags_abc",
- input={"tags": ["a", "b", "c"]},
- output=dict(changed=False),
- run_command_calls=[
- RunCmdCall(
- command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="blah, anything",
- err="",
- ),
- RunCmdCall(
- command=[
- "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
- "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--tags", "a,b,c"
- ],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="",
- err="",
- ),
- ]
- ),
- ModuleTestCase(
- id="puppet_agent_skip_tags_def",
- input={"skip_tags": ["d", "e", "f"]},
- output=dict(changed=False),
- run_command_calls=[
- RunCmdCall(
- command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="blah, anything",
- err="",
- ),
- RunCmdCall(
- command=[
- "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
- "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--skip_tags", "d,e,f"
- ],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="",
- err="",
- ),
- ]
- ),
- ModuleTestCase(
- id="puppet_agent_noop_false",
- input={"noop": False},
- output=dict(changed=False),
- run_command_calls=[
- RunCmdCall(
- command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="blah, anything",
- err="",
- ),
- RunCmdCall(
- command=[
- "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
- "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--no-noop"
- ],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="",
- err="",
- ),
- ]
- ),
- ModuleTestCase(
- id="puppet_agent_noop_true",
- input={"noop": True},
- output=dict(changed=False),
- run_command_calls=[
- RunCmdCall(
- command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="blah, anything",
- err="",
- ),
- RunCmdCall(
- command=[
- "/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
- "--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--noop"
- ],
- environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- rc=0,
- out="",
- err="",
- ),
- ]
- ),
-]
-TEST_CASES_IDS = [item.id for item in TEST_CASES]
-
-
-@pytest.mark.parametrize("patch_ansible_module, testcase",
- [[x.input, x] for x in TEST_CASES],
- ids=TEST_CASES_IDS,
- indirect=["patch_ansible_module"])
-@pytest.mark.usefixtures("patch_ansible_module")
-def test_puppet(mocker, capfd, patch_get_bin_path, testcase):
- """
- Run unit tests for test cases listen in TEST_CASES
- """
-
- run_cmd_calls = testcase.run_command_calls
-
- # Mock function used for running commands first
- call_results = [(x.rc, x.out, x.err) for x in run_cmd_calls]
- mock_run_command = mocker.patch(
- "ansible.module_utils.basic.AnsibleModule.run_command",
- side_effect=call_results)
-
- # Try to run test case
- with pytest.raises(SystemExit):
- puppet.main()
-
- out, err = capfd.readouterr()
- results = json.loads(out)
- print("testcase =\n%s" % str(testcase))
- print("results =\n%s" % results)
-
- assert mock_run_command.call_count == len(run_cmd_calls)
- if mock_run_command.call_count:
- call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list]
- expected_call_args_list = [(item.command, item.environ) for item in run_cmd_calls]
- print("call args list =\n%s" % call_args_list)
- print("expected args list =\n%s" % expected_call_args_list)
- assert call_args_list == expected_call_args_list
-
- assert results.get("changed", False) == testcase.output["changed"]
- if "failed" in testcase:
- assert results.get("failed", False) == testcase.output["failed"]
- if "msg" in testcase:
- assert results.get("msg", "") == testcase.output["msg"]
+Helper.from_module(puppet, __name__)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_puppet.yaml b/ansible_collections/community/general/tests/unit/plugins/modules/test_puppet.yaml
new file mode 100644
index 000000000..308be9797
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_puppet.yaml
@@ -0,0 +1,192 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Alexei Znamensky (russoz@gmail.com)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+---
+- id: puppet_agent_plain
+ input: {}
+ output:
+ changed: false
+ run_command_calls:
+ - command: [/testbin/puppet, config, print, agent_disabled_lockfile]
+ environ: &env-def {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
+ rc: 0
+ out: "blah, anything"
+ err: ""
+ - command:
+ - /testbin/timeout
+ - -s
+ - "9"
+ - 30m
+ - /testbin/puppet
+ - agent
+ - --onetime
+ - --no-daemonize
+ - --no-usecacheonfailure
+ - --no-splay
+ - --detailed-exitcodes
+ - --verbose
+ - --color
+ - "0"
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+- id: puppet_agent_certname
+ input:
+ certname: potatobox
+ output:
+ changed: false
+ run_command_calls:
+ - command: [/testbin/puppet, config, print, agent_disabled_lockfile]
+ environ: *env-def
+ rc: 0
+ out: "blah, anything"
+ err: ""
+ - command:
+ - /testbin/timeout
+ - -s
+ - "9"
+ - 30m
+ - /testbin/puppet
+ - agent
+ - --onetime
+ - --no-daemonize
+ - --no-usecacheonfailure
+ - --no-splay
+ - --detailed-exitcodes
+ - --verbose
+ - --color
+ - "0"
+ - --certname=potatobox
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+- id: puppet_agent_tags_abc
+ input:
+ tags: [a, b, c]
+ output:
+ changed: false
+ run_command_calls:
+ - command: [/testbin/puppet, config, print, agent_disabled_lockfile]
+ environ: *env-def
+ rc: 0
+ out: "blah, anything"
+ err: ""
+ - command:
+ - /testbin/timeout
+ - -s
+ - "9"
+ - 30m
+ - /testbin/puppet
+ - agent
+ - --onetime
+ - --no-daemonize
+ - --no-usecacheonfailure
+ - --no-splay
+ - --detailed-exitcodes
+ - --verbose
+ - --color
+ - "0"
+ - --tags
+ - a,b,c
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+- id: puppet_agent_skip_tags_def
+ input:
+ skip_tags: [d, e, f]
+ output:
+ changed: false
+ run_command_calls:
+ - command: [/testbin/puppet, config, print, agent_disabled_lockfile]
+ environ: *env-def
+ rc: 0
+ out: "blah, anything"
+ err: ""
+ - command:
+ - /testbin/timeout
+ - -s
+ - "9"
+ - 30m
+ - /testbin/puppet
+ - agent
+ - --onetime
+ - --no-daemonize
+ - --no-usecacheonfailure
+ - --no-splay
+ - --detailed-exitcodes
+ - --verbose
+ - --color
+ - "0"
+ - --skip_tags
+ - d,e,f
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+- id: puppet_agent_noop_false
+ input:
+ noop: false
+ output:
+ changed: false
+ run_command_calls:
+ - command: [/testbin/puppet, config, print, agent_disabled_lockfile]
+ environ: *env-def
+ rc: 0
+ out: "blah, anything"
+ err: ""
+ - command:
+ - /testbin/timeout
+ - -s
+ - "9"
+ - 30m
+ - /testbin/puppet
+ - agent
+ - --onetime
+ - --no-daemonize
+ - --no-usecacheonfailure
+ - --no-splay
+ - --detailed-exitcodes
+ - --verbose
+ - --color
+ - "0"
+ - --no-noop
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+- id: puppet_agent_noop_true
+ input:
+ noop: true
+ output:
+ changed: false
+ run_command_calls:
+ - command: [/testbin/puppet, config, print, agent_disabled_lockfile]
+ environ: *env-def
+ rc: 0
+ out: "blah, anything"
+ err: ""
+ - command:
+ - /testbin/timeout
+ - -s
+ - "9"
+ - 30m
+ - /testbin/puppet
+ - agent
+ - --onetime
+ - --no-daemonize
+ - --no-usecacheonfailure
+ - --no-splay
+ - --detailed-exitcodes
+ - --verbose
+ - --color
+ - "0"
+ - --noop
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_redhat_subscription.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_redhat_subscription.py
index 4bf272916..9473d0d46 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_redhat_subscription.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_redhat_subscription.py
@@ -22,13 +22,15 @@ def patch_redhat_subscription(mocker):
"""
Function used for mocking some parts of redhat_subscription module
"""
- mocker.patch('ansible_collections.community.general.plugins.modules.redhat_subscription.RegistrationBase.REDHAT_REPO')
+ mocker.patch('ansible_collections.community.general.plugins.modules.redhat_subscription.Rhsm.REDHAT_REPO')
mocker.patch('ansible_collections.community.general.plugins.modules.redhat_subscription.isfile', return_value=False)
mocker.patch('ansible_collections.community.general.plugins.modules.redhat_subscription.unlink', return_value=True)
mocker.patch('ansible_collections.community.general.plugins.modules.redhat_subscription.AnsibleModule.get_bin_path',
return_value='/testbin/subscription-manager')
mocker.patch('ansible_collections.community.general.plugins.modules.redhat_subscription.Rhsm._can_connect_to_dbus',
return_value=False)
+ mocker.patch('ansible_collections.community.general.plugins.modules.redhat_subscription.Rhsm._has_dbus_interface',
+ return_value=False)
mocker.patch('ansible_collections.community.general.plugins.modules.redhat_subscription.getuid',
return_value=0)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_redis_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_redis_info.py
index 8b30a2316..cdc78680e 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_redis_info.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_redis_info.py
@@ -50,7 +50,12 @@ class TestRedisInfoModule(ModuleTestCase):
set_module_args({})
self.module.main()
self.assertEqual(redis_client.call_count, 1)
- self.assertEqual(redis_client.call_args, ({'host': 'localhost', 'port': 6379, 'password': None},))
+ self.assertEqual(redis_client.call_args, ({'host': 'localhost',
+ 'port': 6379,
+ 'password': None,
+ 'ssl': False,
+ 'ssl_ca_certs': None,
+ 'ssl_cert_reqs': 'required'},))
self.assertEqual(result.exception.args[0]['info']['redis_version'], '999.999.999')
def test_with_parameters(self):
@@ -64,7 +69,34 @@ class TestRedisInfoModule(ModuleTestCase):
})
self.module.main()
self.assertEqual(redis_client.call_count, 1)
- self.assertEqual(redis_client.call_args, ({'host': 'test', 'port': 1234, 'password': 'PASS'},))
+ self.assertEqual(redis_client.call_args, ({'host': 'test',
+ 'port': 1234,
+ 'password': 'PASS',
+ 'ssl': False,
+ 'ssl_ca_certs': None,
+ 'ssl_cert_reqs': 'required'},))
+ self.assertEqual(result.exception.args[0]['info']['redis_version'], '999.999.999')
+
+ def test_with_tls_parameters(self):
+ """Test with tls parameters"""
+ with self.patch_redis_client(side_effect=FakeRedisClient) as redis_client:
+ with self.assertRaises(AnsibleExitJson) as result:
+ set_module_args({
+ 'login_host': 'test',
+ 'login_port': 1234,
+ 'login_password': 'PASS',
+ 'tls': True,
+ 'ca_certs': '/etc/ssl/ca.pem',
+ 'validate_certs': False
+ })
+ self.module.main()
+ self.assertEqual(redis_client.call_count, 1)
+ self.assertEqual(redis_client.call_args, ({'host': 'test',
+ 'port': 1234,
+ 'password': 'PASS',
+ 'ssl': True,
+ 'ssl_ca_certs': '/etc/ssl/ca.pem',
+ 'ssl_cert_reqs': None},))
self.assertEqual(result.exception.args[0]['info']['redis_version'], '999.999.999')
def test_with_fail_client(self):
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_rhsm_release.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_rhsm_release.py
index c5696962b..e8b2db6fd 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_rhsm_release.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_rhsm_release.py
@@ -14,6 +14,8 @@ from ansible_collections.community.general.tests.unit.plugins.modules.utils impo
class RhsmRepositoryReleaseModuleTestCase(ModuleTestCase):
module = rhsm_release
+ SUBMAN_KWARGS = dict(check_rc=True, expand_user_and_vars=False)
+
def setUp(self):
super(RhsmRepositoryReleaseModuleTestCase, self).setUp()
@@ -63,8 +65,8 @@ class RhsmRepositoryReleaseModuleTestCase(ModuleTestCase):
self.assertTrue(result['changed'])
self.assertEqual('7.5', result['current_release'])
self.module_main_command.assert_has_calls([
- call('/testbin/subscription-manager release --show', check_rc=True),
- call('/testbin/subscription-manager release --set 7.5', check_rc=True),
+ call(['/testbin/subscription-manager', 'release', '--show'], **self.SUBMAN_KWARGS),
+ call(['/testbin/subscription-manager', 'release', '--set', '7.5'], **self.SUBMAN_KWARGS),
])
def test_release_set_idempotent(self):
@@ -81,7 +83,7 @@ class RhsmRepositoryReleaseModuleTestCase(ModuleTestCase):
self.assertFalse(result['changed'])
self.assertEqual('7.5', result['current_release'])
self.module_main_command.assert_has_calls([
- call('/testbin/subscription-manager release --show', check_rc=True),
+ call(['/testbin/subscription-manager', 'release', '--show'], **self.SUBMAN_KWARGS),
])
def test_release_unset(self):
@@ -100,8 +102,8 @@ class RhsmRepositoryReleaseModuleTestCase(ModuleTestCase):
self.assertTrue(result['changed'])
self.assertIsNone(result['current_release'])
self.module_main_command.assert_has_calls([
- call('/testbin/subscription-manager release --show', check_rc=True),
- call('/testbin/subscription-manager release --unset', check_rc=True),
+ call(['/testbin/subscription-manager', 'release', '--show'], **self.SUBMAN_KWARGS),
+ call(['/testbin/subscription-manager', 'release', '--unset'], **self.SUBMAN_KWARGS),
])
def test_release_unset_idempotent(self):
@@ -118,7 +120,7 @@ class RhsmRepositoryReleaseModuleTestCase(ModuleTestCase):
self.assertFalse(result['changed'])
self.assertIsNone(result['current_release'])
self.module_main_command.assert_has_calls([
- call('/testbin/subscription-manager release --show', check_rc=True),
+ call(['/testbin/subscription-manager', 'release', '--show'], **self.SUBMAN_KWARGS),
])
def test_release_insane(self):
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_rhsm_repository.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_rhsm_repository.py
new file mode 100644
index 000000000..e822c7e84
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_rhsm_repository.py
@@ -0,0 +1,833 @@
+# -*- coding: utf-8 -*-
+# Author: Pino Toscano (ptoscano@redhat.com)
+# Largely adapted from test_rhsm_repository by
+# Jiri Hnidek (jhnidek@redhat.com)
+#
+# Copyright (c) Pino Toscano (ptoscano@redhat.com)
+# Copyright (c) Jiri Hnidek (jhnidek@redhat.com)
+#
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import copy
+import fnmatch
+import itertools
+import json
+
+from ansible.module_utils import basic
+from ansible_collections.community.general.plugins.modules import rhsm_repository
+
+import pytest
+
+TESTED_MODULE = rhsm_repository.__name__
+
+
+@pytest.fixture
+def patch_rhsm_repository(mocker):
+ """
+ Function used for mocking some parts of rhsm_repository module
+ """
+ mocker.patch('ansible_collections.community.general.plugins.modules.rhsm_repository.AnsibleModule.get_bin_path',
+ return_value='/testbin/subscription-manager')
+ mocker.patch('ansible_collections.community.general.plugins.modules.rhsm_repository.os.getuid',
+ return_value=0)
+
+
+class Repos(object):
+ """
+ Helper class to represent a list of repositories
+
+ Each repository is an object with few properties.
+ """
+
+ _SUBMAN_OUT_HEADER = """+----------------------------------------------------------+
+ Available Repositories in /etc/yum.repos.d/redhat.repo
++----------------------------------------------------------+
+"""
+ _SUBMAN_OUT_ENTRY = """Repo ID: %s
+Repo Name: %s
+Repo URL: %s
+Enabled: %s
+
+"""
+
+ def __init__(self, repos):
+ self.repos = repos
+
+ def to_subman_list_output(self):
+ """
+ Return a string mimicking the output of `subscription-manager repos --list`
+ """
+ out = self._SUBMAN_OUT_HEADER
+ for repo in self.repos:
+ out += self._SUBMAN_OUT_ENTRY % (
+ repo["id"],
+ repo["name"],
+ repo["url"],
+ "1" if repo["enabled"] else "0",
+ )
+
+ return out
+
+ def copy(self):
+ """
+ Clone the object; used to do changes (enable(), disable()) without
+ affecting the original object.
+ """
+ return copy.deepcopy(self)
+
+ def _set_status(self, repo_id, status):
+ for repo in self.repos:
+ if fnmatch.fnmatch(repo['id'], repo_id):
+ repo['enabled'] = status
+
+ def enable(self, repo_ids):
+ """
+ Enable the specified IDs.
+
+ 'repo_ids' can be either a string or a list of strings representing
+ an ID (wildcard included).
+
+ Returns the same object, so calls to this can be chained.
+ """
+ if not isinstance(repo_ids, list):
+ repo_ids = [repo_ids]
+ for repo_id in repo_ids:
+ self._set_status(repo_id, True)
+ return self
+
+ def disable(self, repo_ids):
+ """
+ Disable the specified IDs.
+
+ 'repo_ids' can be either a string or a list of strings representing
+ an ID (wildcard included).
+
+ Returns the same object, so calls to this can be chained.
+ """
+ if not isinstance(repo_ids, list):
+ repo_ids = [repo_ids]
+ for repo_id in repo_ids:
+ self._set_status(repo_id, False)
+ return self
+
+ def _filter_by_status(self, filter, status):
+ return [
+ repo['id']
+ for repo in self.repos
+ if repo['enabled'] == status and fnmatch.fnmatch(repo['id'], filter)
+ ]
+
+ def ids_enabled(self, filter='*'):
+ """
+ Get a list with the enabled repositories.
+
+ 'filter' is a wildcard expression.
+ """
+ return self._filter_by_status(filter, True)
+
+ def ids_disabled(self, filter='*'):
+ """
+ Get a list with the disabled repositories.
+
+ 'filter' is a wildcard expression.
+ """
+ return self._filter_by_status(filter, False)
+
+ def to_list(self):
+ """
+ Get the list of repositories.
+ """
+ return self.repos
+
+
+def flatten(iter_of_iters):
+ return list(itertools.chain.from_iterable(iter_of_iters))
+
+
+# List with test repositories, directly from the Candlepin test data.
+REPOS_LIST = [
+ {
+ "id": "never-enabled-content-801",
+ "name": "never-enabled-content-801",
+ "url": "https://candlepin.local/foo/path/never_enabled/801-100",
+ "enabled": False,
+ },
+ {
+ "id": "never-enabled-content-100000000000060",
+ "name": "never-enabled-content-100000000000060",
+ "url": "https://candlepin.local/foo/path/never_enabled/100000000000060-100",
+ "enabled": False,
+ },
+ {
+ "id": "awesomeos-x86_64-1000000000000023",
+ "name": "awesomeos-x86_64-1000000000000023",
+ "url": "https://candlepin.local/path/to/awesomeos/x86_64/1000000000000023-11124",
+ "enabled": False,
+ },
+ {
+ "id": "awesomeos-ppc64-100000000000011",
+ "name": "awesomeos-ppc64-100000000000011",
+ "url": "https://candlepin.local/path/to/awesomeos/ppc64/100000000000011-11126",
+ "enabled": False,
+ },
+ {
+ "id": "awesomeos-99000",
+ "name": "awesomeos-99000",
+ "url": "https://candlepin.local/path/to/generic/awesomeos/99000-11113",
+ "enabled": True,
+ },
+ {
+ "id": "content-label-27060",
+ "name": "content-27060",
+ "url": "https://candlepin.local/foo/path/common/27060-1111",
+ "enabled": True,
+ },
+ {
+ "id": "content-label-no-gpg-32060",
+ "name": "content-nogpg-32060",
+ "url": "https://candlepin.local/foo/path/no_gpg/32060-234",
+ "enabled": False,
+ },
+ {
+ "id": "awesomeos-1000000000000023",
+ "name": "awesomeos-1000000000000023",
+ "url": "https://candlepin.local/path/to/generic/awesomeos/1000000000000023-11113",
+ "enabled": False,
+ },
+ {
+ "id": "awesomeos-x86-100000000000020",
+ "name": "awesomeos-x86-100000000000020",
+ "url": "https://candlepin.local/path/to/awesomeos/x86/100000000000020-11120",
+ "enabled": False,
+ },
+ {
+ "id": "awesomeos-x86_64-99000",
+ "name": "awesomeos-x86_64-99000",
+ "url": "https://candlepin.local/path/to/awesomeos/x86_64/99000-11124",
+ "enabled": True,
+ },
+ {
+ "id": "awesomeos-s390x-99000",
+ "name": "awesomeos-s390x-99000",
+ "url": "https://candlepin.local/path/to/awesomeos/s390x/99000-11121",
+ "enabled": False,
+ },
+ {
+ "id": "awesomeos-modifier-37080",
+ "name": "awesomeos-modifier-37080",
+ "url": "https://candlepin.local/example.com/awesomeos-modifier/37080-1112",
+ "enabled": False,
+ },
+ {
+ "id": "awesomeos-i686-99000",
+ "name": "awesomeos-i686-99000",
+ "url": "https://candlepin.local/path/to/awesomeos/i686/99000-11123",
+ "enabled": False,
+ },
+ {
+ "id": "fake-content-38072",
+ "name": "fake-content-38072",
+ "url": "https://candlepin.local/path/to/fake-content/38072-3902",
+ "enabled": True,
+ },
+]
+
+
+# A static object with the list of repositories, used as reference to query
+# the repositories, and create (by copy()) new Repos objects.
+REPOS = Repos(REPOS_LIST)
+
+# The mock string for the output of `subscription-manager repos --list`.
+REPOS_LIST_OUTPUT = REPOS.to_subman_list_output()
+
+# MUST match what's in the Rhsm class in the module.
+SUBMAN_KWARGS = {
+ 'environ_update': dict(LANG='C', LC_ALL='C', LC_MESSAGES='C'),
+ 'expand_user_and_vars': False,
+ 'use_unsafe_shell': False,
+}
+
+
+TEST_CASES = [
+ # enable a disabled repository
+ [
+ {
+ 'name': 'awesomeos-1000000000000023',
+ },
+ {
+ 'id': 'test_enable_single',
+ 'run_command.calls': [
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--list',
+ ],
+ SUBMAN_KWARGS,
+ (0, REPOS_LIST_OUTPUT, '')
+ ),
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--enable',
+ 'awesomeos-1000000000000023',
+ ],
+ SUBMAN_KWARGS,
+ (0, '', '')
+ ),
+ ],
+ 'changed': True,
+ 'repositories': REPOS.copy().enable('awesomeos-1000000000000023'),
+ }
+ ],
+ # enable an already enabled repository
+ [
+ {
+ 'name': 'fake-content-38072',
+ },
+ {
+ 'id': 'test_enable_already_enabled',
+ 'run_command.calls': [
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--list',
+ ],
+ SUBMAN_KWARGS,
+ (0, REPOS_LIST_OUTPUT, '')
+ ),
+ ],
+ 'changed': False,
+ 'repositories': REPOS.copy(),
+ }
+ ],
+ # enable two disabled repositories
+ [
+ {
+ 'name': ['awesomeos-1000000000000023', 'content-label-no-gpg-32060'],
+ },
+ {
+ 'id': 'test_enable_multiple',
+ 'run_command.calls': [
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--list',
+ ],
+ SUBMAN_KWARGS,
+ (0, REPOS_LIST_OUTPUT, '')
+ ),
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--enable',
+ 'awesomeos-1000000000000023',
+ '--enable',
+ 'content-label-no-gpg-32060',
+ ],
+ SUBMAN_KWARGS,
+ (0, '', '')
+ ),
+ ],
+ 'changed': True,
+ 'repositories': REPOS.copy().enable('awesomeos-1000000000000023').enable('content-label-no-gpg-32060'),
+ }
+ ],
+ # enable two repositories, one disabled and one already enabled
+ [
+ {
+ 'name': ['awesomeos-1000000000000023', 'fake-content-38072'],
+ },
+ {
+ 'id': 'test_enable_multiple_mixed',
+ 'run_command.calls': [
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--list',
+ ],
+ SUBMAN_KWARGS,
+ (0, REPOS_LIST_OUTPUT, '')
+ ),
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--enable',
+ 'awesomeos-1000000000000023',
+ '--enable',
+ 'fake-content-38072',
+ ],
+ SUBMAN_KWARGS,
+ (0, '', '')
+ ),
+ ],
+ 'changed': True,
+ 'repositories': REPOS.copy().enable('awesomeos-1000000000000023'),
+ }
+ ],
+ # purge everything but never-enabled-content-801 (disabled)
+ [
+ {
+ 'name': 'never-enabled-content-801',
+ 'purge': True,
+ },
+ {
+ 'id': 'test_purge_everything_but_one_disabled',
+ 'run_command.calls': [
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--list',
+ ],
+ SUBMAN_KWARGS,
+ (0, REPOS_LIST_OUTPUT, '')
+ ),
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--enable',
+ 'never-enabled-content-801',
+ ] + flatten([['--disable', i] for i in REPOS.ids_enabled() if i != 'never-enabled-content-801']),
+ SUBMAN_KWARGS,
+ (0, '', '')
+ ),
+ ],
+ 'changed': True,
+ 'repositories': REPOS.copy().disable('*').enable('never-enabled-content-801'),
+ }
+ ],
+ # purge everything but awesomeos-99000 (already enabled)
+ [
+ {
+ 'name': 'awesomeos-99000',
+ 'purge': True,
+ },
+ {
+ 'id': 'test_purge_everything_but_one_enabled',
+ 'run_command.calls': [
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--list',
+ ],
+ SUBMAN_KWARGS,
+ (0, REPOS_LIST_OUTPUT, '')
+ ),
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--enable',
+ 'awesomeos-99000',
+ '--disable',
+ 'content-label-27060',
+ '--disable',
+ 'awesomeos-x86_64-99000',
+ '--disable',
+ 'fake-content-38072',
+ ],
+ SUBMAN_KWARGS,
+ (0, '', '')
+ ),
+ ],
+ 'changed': True,
+ 'repositories': REPOS.copy().disable('*').enable('awesomeos-99000'),
+ }
+ ],
+ # enable everything, then purge everything but content-label-27060
+ [
+ {
+ 'name': 'content-label-27060',
+ 'purge': True,
+ },
+ {
+ 'id': 'test_enable_everything_purge_everything_but_one_enabled',
+ 'run_command.calls': [
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--list',
+ ],
+ SUBMAN_KWARGS,
+ (0, REPOS.copy().enable('*').to_subman_list_output(), '')
+ ),
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--enable',
+ 'content-label-27060',
+ '--disable',
+ 'never-enabled-content-801',
+ '--disable',
+ 'never-enabled-content-100000000000060',
+ '--disable',
+ 'awesomeos-x86_64-1000000000000023',
+ '--disable',
+ 'awesomeos-ppc64-100000000000011',
+ '--disable',
+ 'awesomeos-99000',
+ '--disable',
+ 'content-label-no-gpg-32060',
+ '--disable',
+ 'awesomeos-1000000000000023',
+ '--disable',
+ 'awesomeos-x86-100000000000020',
+ '--disable',
+ 'awesomeos-x86_64-99000',
+ '--disable',
+ 'awesomeos-s390x-99000',
+ '--disable',
+ 'awesomeos-modifier-37080',
+ '--disable',
+ 'awesomeos-i686-99000',
+ '--disable',
+ 'fake-content-38072',
+ ],
+ SUBMAN_KWARGS,
+ (0, '', '')
+ ),
+ ],
+ 'changed': True,
+ 'repositories': REPOS.copy().disable('*').enable('content-label-27060'),
+ }
+ ],
+ # enable all awesomeos-*
+ [
+ {
+ 'name': 'awesomeos-*',
+ },
+ {
+ 'id': 'test_enable_all_awesomeos_star',
+ 'run_command.calls': [
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--list',
+ ],
+ SUBMAN_KWARGS,
+ (0, REPOS_LIST_OUTPUT, '')
+ ),
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--enable',
+ 'awesomeos-x86_64-1000000000000023',
+ '--enable',
+ 'awesomeos-ppc64-100000000000011',
+ '--enable',
+ 'awesomeos-99000',
+ '--enable',
+ 'awesomeos-1000000000000023',
+ '--enable',
+ 'awesomeos-x86-100000000000020',
+ '--enable',
+ 'awesomeos-x86_64-99000',
+ '--enable',
+ 'awesomeos-s390x-99000',
+ '--enable',
+ 'awesomeos-modifier-37080',
+ '--enable',
+ 'awesomeos-i686-99000',
+ ],
+ SUBMAN_KWARGS,
+ (0, '', '')
+ ),
+ ],
+ 'changed': True,
+ 'repositories': REPOS.copy().enable('awesomeos-*'),
+ }
+ ],
+ # purge everything but awesomeos-*
+ [
+ {
+ 'name': REPOS.ids_enabled('awesomeos-*'),
+ 'purge': True,
+ },
+ {
+ 'id': 'test_purge_everything_but_awesomeos_list',
+ 'run_command.calls': [
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--list',
+ ],
+ SUBMAN_KWARGS,
+ (0, REPOS_LIST_OUTPUT, '')
+ ),
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--enable',
+ 'awesomeos-99000',
+ '--enable',
+ 'awesomeos-x86_64-99000',
+ '--disable',
+ 'content-label-27060',
+ '--disable',
+ 'fake-content-38072',
+ ],
+ SUBMAN_KWARGS,
+ (0, '', '')
+ ),
+ ],
+ 'changed': True,
+ 'repositories': REPOS.copy().disable('*').enable(REPOS.ids_enabled('awesomeos-*')),
+ }
+ ],
+ # enable a repository that does not exist
+ [
+ {
+ 'name': 'repo-that-does-not-exist',
+ },
+ {
+ 'id': 'test_enable_nonexisting',
+ 'run_command.calls': [
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--list',
+ ],
+ SUBMAN_KWARGS,
+ (0, REPOS_LIST_OUTPUT, '')
+ ),
+ ],
+ 'failed': True,
+ 'msg': 'repo-that-does-not-exist is not a valid repository ID',
+ }
+ ],
+ # disable an enabled repository
+ [
+ {
+ 'name': 'awesomeos-99000',
+ 'state': 'disabled',
+ },
+ {
+ 'id': 'test_disable_single',
+ 'run_command.calls': [
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--list',
+ ],
+ SUBMAN_KWARGS,
+ (0, REPOS_LIST_OUTPUT, '')
+ ),
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--disable',
+ 'awesomeos-99000',
+ ],
+ SUBMAN_KWARGS,
+ (0, '', '')
+ ),
+ ],
+ 'changed': True,
+ 'repositories': REPOS.copy().disable('awesomeos-99000'),
+ }
+ ],
+ # disable an enabled repository (using state=absent)
+ [
+ {
+ 'name': 'awesomeos-99000',
+ 'state': 'absent',
+ },
+ {
+ 'id': 'test_disable_single_using_absent',
+ 'run_command.calls': [
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--list',
+ ],
+ SUBMAN_KWARGS,
+ (0, REPOS_LIST_OUTPUT, '')
+ ),
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--disable',
+ 'awesomeos-99000',
+ ],
+ SUBMAN_KWARGS,
+ (0, '', '')
+ ),
+ ],
+ 'changed': True,
+ 'repositories': REPOS.copy().disable('awesomeos-99000'),
+ }
+ ],
+ # disable an already disabled repository
+ [
+ {
+ 'name': 'never-enabled-content-801',
+ 'state': 'disabled',
+ },
+ {
+ 'id': 'test_disable_already_disabled',
+ 'run_command.calls': [
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--list',
+ ],
+ SUBMAN_KWARGS,
+ (0, REPOS_LIST_OUTPUT, '')
+ ),
+ ],
+ 'changed': False,
+ 'repositories': REPOS.copy(),
+ }
+ ],
+ # disable an already disabled repository, and purge
+ [
+ {
+ 'name': 'never-enabled-content-801',
+ 'state': 'disabled',
+ 'purge': True,
+ },
+ {
+ 'id': 'test_disable_already_disabled_and_purge',
+ 'run_command.calls': [
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--list',
+ ],
+ SUBMAN_KWARGS,
+ (0, REPOS_LIST_OUTPUT, '')
+ ),
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ ] + flatten([['--disable', i] for i in REPOS.ids_enabled()]),
+ SUBMAN_KWARGS,
+ (0, '', '')
+ ),
+ ],
+ 'changed': True,
+ 'repositories': REPOS.copy().disable('*'),
+ }
+ ],
+ # disable an enabled repository, and purge
+ [
+ {
+ 'name': 'awesomeos-99000',
+ 'state': 'disabled',
+ 'purge': True,
+ },
+ {
+ 'id': 'test_disable_single_and_purge',
+ 'run_command.calls': [
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--list',
+ ],
+ SUBMAN_KWARGS,
+ (0, REPOS_LIST_OUTPUT, '')
+ ),
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ ] + flatten([['--disable', i] for i in REPOS.ids_enabled()]),
+ SUBMAN_KWARGS,
+ (0, '', '')
+ ),
+ ],
+ 'changed': True,
+ 'repositories': REPOS.copy().disable('*'),
+ }
+ ],
+ # disable a repository that does not exist
+ [
+ {
+ 'name': 'repo-that-does-not-exist',
+ 'state': 'disabled',
+ },
+ {
+ 'id': 'test_disable_nonexisting',
+ 'run_command.calls': [
+ (
+ [
+ '/testbin/subscription-manager',
+ 'repos',
+ '--list',
+ ],
+ SUBMAN_KWARGS,
+ (0, REPOS_LIST_OUTPUT, '')
+ ),
+ ],
+ 'failed': True,
+ 'msg': 'repo-that-does-not-exist is not a valid repository ID',
+ }
+ ],
+]
+
+
+TEST_CASES_IDS = [item[1]['id'] for item in TEST_CASES]
+
+
+@pytest.mark.parametrize('patch_ansible_module, testcase', TEST_CASES, ids=TEST_CASES_IDS, indirect=['patch_ansible_module'])
+@pytest.mark.usefixtures('patch_ansible_module')
+def test_rhsm_repository(mocker, capfd, patch_rhsm_repository, testcase):
+ """
+ Run unit tests for test cases listen in TEST_CASES
+ """
+
+ # Mock function used for running commands first
+ call_results = [item[2] for item in testcase['run_command.calls']]
+ mock_run_command = mocker.patch.object(
+ basic.AnsibleModule,
+ 'run_command',
+ side_effect=call_results)
+
+ # Try to run test case
+ with pytest.raises(SystemExit):
+ rhsm_repository.main()
+
+ out, err = capfd.readouterr()
+ results = json.loads(out)
+
+ if 'failed' in testcase:
+ assert results['failed'] == testcase['failed']
+ assert results['msg'] == testcase['msg']
+ else:
+ assert 'changed' in results
+ assert results['changed'] == testcase['changed']
+ assert results['repositories'] == testcase['repositories'].to_list()
+
+ assert basic.AnsibleModule.run_command.call_count == len(testcase['run_command.calls'])
+ # FIXME ideally we need also to compare the actual calls with the expected
+ # ones; the problem is that the module uses a dict to collect the repositories
+ # to enable and disable, so the order of the --enable/--disable parameters to
+ # `subscription-manager repos` is not stable
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_sap_task_list_execute.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_sap_task_list_execute.py
deleted file mode 100644
index 34c97c4a8..000000000
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_sap_task_list_execute.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# Copyright (c) Ansible project
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-from __future__ import (absolute_import, division, print_function)
-__metaclass__ = type
-
-import sys
-from ansible_collections.community.general.tests.unit.compat.mock import patch, MagicMock
-from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args
-
-sys.modules['pyrfc'] = MagicMock()
-sys.modules['pyrfc.Connection'] = MagicMock()
-sys.modules['xmltodict'] = MagicMock()
-sys.modules['xmltodict.parse'] = MagicMock()
-
-from ansible_collections.community.general.plugins.modules import sap_task_list_execute
-
-
-class TestSAPRfcModule(ModuleTestCase):
-
- def setUp(self):
- super(TestSAPRfcModule, self).setUp()
- self.module = sap_task_list_execute
-
- def tearDown(self):
- super(TestSAPRfcModule, self).tearDown()
-
- def define_rfc_connect(self, mocker):
- return mocker.patch(self.module.call_rfc_method)
-
- def test_without_required_parameters(self):
- """Failure must occurs when all parameters are missing"""
- with self.assertRaises(AnsibleFailJson):
- set_module_args({})
- self.module.main()
-
- def test_error_no_task_list(self):
- """tests fail to exec task list"""
-
- set_module_args({
- "conn_username": "DDIC",
- "conn_password": "Test1234",
- "host": "10.1.8.9",
- "task_to_execute": "SAP_BASIS_SSL_CHECK"
- })
-
- with patch.object(self.module, 'Connection') as conn:
- conn.return_value = ''
- with self.assertRaises(AnsibleFailJson) as result:
- self.module.main()
- self.assertEqual(result.exception.args[0]['msg'], 'The task list does not exsist.')
-
- def test_success(self):
- """test execute task list success"""
-
- set_module_args({
- "conn_username": "DDIC",
- "conn_password": "Test1234",
- "host": "10.1.8.9",
- "task_to_execute": "SAP_BASIS_SSL_CHECK"
- })
- with patch.object(self.module, 'xml_to_dict') as XML:
- XML.return_value = {'item': [{'TASK': {'CHECK_STATUS_DESCR': 'Check successfully',
- 'STATUS_DESCR': 'Executed successfully', 'TASKNAME': 'CL_STCT_CHECK_SEC_CRYPTO',
- 'LNR': '1', 'DESCRIPTION': 'Check SAP Cryptographic Library', 'DOCU_EXIST': 'X',
- 'LOG_EXIST': 'X', 'ACTION_SKIP': None, 'ACTION_UNSKIP': None, 'ACTION_CONFIRM': None,
- 'ACTION_MAINTAIN': None}}]}
-
- with self.assertRaises(AnsibleExitJson) as result:
- sap_task_list_execute.main()
- self.assertEqual(result.exception.args[0]['out'], {'item': [{'TASK': {'CHECK_STATUS_DESCR': 'Check successfully',
- 'STATUS_DESCR': 'Executed successfully', 'TASKNAME': 'CL_STCT_CHECK_SEC_CRYPTO',
- 'LNR': '1', 'DESCRIPTION': 'Check SAP Cryptographic Library', 'DOCU_EXIST': 'X',
- 'LOG_EXIST': 'X', 'ACTION_SKIP': None, 'ACTION_UNSKIP': None,
- 'ACTION_CONFIRM': None, 'ACTION_MAINTAIN': None}}]})
-
- def test_success_no_log(self):
- """test execute task list success without logs"""
-
- set_module_args({
- "conn_username": "DDIC",
- "conn_password": "Test1234",
- "host": "10.1.8.9",
- "task_to_execute": "SAP_BASIS_SSL_CHECK"
- })
- with patch.object(self.module, 'xml_to_dict') as XML:
- XML.return_value = "No logs available."
- with self.assertRaises(AnsibleExitJson) as result:
- sap_task_list_execute.main()
- self.assertEqual(result.exception.args[0]['out'], 'No logs available.')
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_sapcar_extract.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_sapcar_extract.py
deleted file mode 100644
index bec9cf886..000000000
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_sapcar_extract.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (c) 2021, Rainer Leber (@rainerleber) <rainerleber@gmail.com>
-# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
-# SPDX-License-Identifier: GPL-3.0-or-later
-
-from __future__ import absolute_import, division, print_function
-__metaclass__ = type
-
-from ansible_collections.community.general.plugins.modules import sapcar_extract
-from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args
-from ansible_collections.community.general.tests.unit.compat.mock import patch
-from ansible.module_utils import basic
-
-
-def get_bin_path(*args, **kwargs):
- """Function to return path of SAPCAR"""
- return "/tmp/sapcar"
-
-
-class Testsapcar_extract(ModuleTestCase):
- """Main class for testing sapcar_extract module."""
-
- def setUp(self):
- """Setup."""
- super(Testsapcar_extract, self).setUp()
- self.module = sapcar_extract
- self.mock_get_bin_path = patch.object(basic.AnsibleModule, 'get_bin_path', get_bin_path)
- self.mock_get_bin_path.start()
- self.addCleanup(self.mock_get_bin_path.stop) # ensure that the patching is 'undone'
-
- def tearDown(self):
- """Teardown."""
- super(Testsapcar_extract, self).tearDown()
-
- def test_without_required_parameters(self):
- """Failure must occurs when all parameters are missing."""
- with self.assertRaises(AnsibleFailJson):
- set_module_args({})
- self.module.main()
-
- def test_sapcar_extract(self):
- """Check that result is changed."""
- set_module_args({
- 'path': "/tmp/HANA_CLIENT_REV2_00_053_00_LINUX_X86_64.SAR",
- 'dest': "/tmp/test2",
- 'binary_path': "/tmp/sapcar"
- })
- with patch.object(basic.AnsibleModule, 'run_command') as run_command:
- run_command.return_value = 0, '', '' # successful execution, no output
- with self.assertRaises(AnsibleExitJson) as result:
- sapcar_extract.main()
- self.assertTrue(result.exception.args[0]['changed'])
- self.assertEqual(run_command.call_count, 1)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_simpleinit_msb.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_simpleinit_msb.py
new file mode 100644
index 000000000..d97e9b5f2
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_simpleinit_msb.py
@@ -0,0 +1,200 @@
+# Copyright (c) 2023 Vlad Glagolev <scm@vaygr.net>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+from ansible_collections.community.general.tests.unit.compat.mock import patch
+from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleFailJson, ModuleTestCase, set_module_args
+
+from ansible_collections.community.general.plugins.modules.simpleinit_msb import SimpleinitMSB, build_module
+
+
+_TELINIT_LIST = """
+RUNLEVEL SCRIPT
+2 smgl-suspend-single
+3 crond
+3 fuse
+3 network
+3 nscd
+3 smgl-default-remote-fs
+3 smgl-misc
+3 sshd
+DEV coldplug
+DEV devices
+DEV udevd
+S hostname.sh
+S hwclock.sh
+S keymap.sh
+S modutils
+S mountall.sh
+S mountroot.sh
+S single
+S smgl-default-crypt-fs
+S smgl-metalog
+S smgl-sysctl
+S sysstat
+"""
+
+_TELINIT_LIST_ENABLED = """
+smgl-suspend-single
+crond
+fuse
+network
+nscd
+smgl-default-remote-fs
+smgl-misc
+sshd
+coldplug
+devices
+udevd
+hostname.sh
+hwclock.sh
+keymap.sh
+modutils
+mountall.sh
+mountroot.sh
+single
+smgl-default-crypt-fs
+smgl-metalog
+smgl-sysctl
+"""
+
+_TELINIT_LIST_DISABLED = """
+sysstat
+"""
+
+_TELINIT_ALREADY_ENABLED = """
+Service smgl-suspend-single already enabled.
+"""
+
+_TELINIT_ALREADY_DISABLED = """
+Service smgl-suspend-single already disabled.
+"""
+
+_TELINIT_STATUS_RUNNING = """
+sshd is running with Process ID(s) 8510 8508 2195
+"""
+
+_TELINIT_STATUS_RUNNING_NOT = """
+/sbin/metalog is not running
+"""
+
+
+class TestSimpleinitMSB(ModuleTestCase):
+
+ def setUp(self):
+ super(TestSimpleinitMSB, self).setUp()
+
+ def tearDown(self):
+ super(TestSimpleinitMSB, self).tearDown()
+
+ def init_module(self, args):
+ set_module_args(args)
+
+ return SimpleinitMSB(build_module())
+
+ @patch('os.path.exists', return_value=True)
+ @patch('ansible.module_utils.basic.AnsibleModule.get_bin_path', return_value="/sbin/telinit")
+ def test_get_service_tools(self, *args, **kwargs):
+ simpleinit_msb = self.init_module({
+ 'name': 'smgl-suspend-single',
+ 'state': 'running',
+ })
+
+ simpleinit_msb.get_service_tools()
+
+ self.assertEqual(simpleinit_msb.telinit_cmd, "/sbin/telinit")
+
+ @patch('ansible_collections.community.general.plugins.modules.simpleinit_msb.SimpleinitMSB.execute_command')
+ def test_service_exists(self, execute_command):
+ simpleinit_msb = self.init_module({
+ 'name': 'smgl-suspend-single',
+ 'state': 'running',
+ })
+
+ execute_command.return_value = (0, _TELINIT_LIST, "")
+
+ simpleinit_msb.service_exists()
+
+ @patch('ansible_collections.community.general.plugins.modules.simpleinit_msb.SimpleinitMSB.execute_command')
+ def test_service_exists_not(self, execute_command):
+ simpleinit_msb = self.init_module({
+ 'name': 'ntp',
+ 'state': 'running',
+ })
+
+ execute_command.return_value = (0, _TELINIT_LIST, "")
+
+ with self.assertRaises(AnsibleFailJson) as context:
+ simpleinit_msb.service_exists()
+
+ self.assertEqual("telinit could not find the requested service: ntp", context.exception.args[0]["msg"])
+
+ @patch('ansible_collections.community.general.plugins.modules.simpleinit_msb.SimpleinitMSB.service_exists')
+ @patch('ansible_collections.community.general.plugins.modules.simpleinit_msb.SimpleinitMSB.execute_command')
+ def test_check_service_enabled(self, execute_command, service_exists):
+ simpleinit_msb = self.init_module({
+ 'name': 'nscd',
+ 'state': 'running',
+ 'enabled': 'true',
+ })
+
+ service_exists.return_value = True
+ execute_command.return_value = (0, _TELINIT_LIST_ENABLED, "")
+
+ self.assertTrue(simpleinit_msb.service_enabled())
+
+ # Race condition check
+ with patch('ansible_collections.community.general.plugins.modules.simpleinit_msb.SimpleinitMSB.service_enabled', return_value=False):
+ execute_command.return_value = (0, "", _TELINIT_ALREADY_ENABLED)
+
+ simpleinit_msb.service_enable()
+
+ self.assertFalse(simpleinit_msb.changed)
+
+ @patch('ansible_collections.community.general.plugins.modules.simpleinit_msb.SimpleinitMSB.service_exists')
+ @patch('ansible_collections.community.general.plugins.modules.simpleinit_msb.SimpleinitMSB.execute_command')
+ def test_check_service_disabled(self, execute_command, service_exists):
+ simpleinit_msb = self.init_module({
+ 'name': 'sysstat',
+ 'state': 'stopped',
+ 'enabled': 'false',
+ })
+
+ service_exists.return_value = True
+ execute_command.return_value = (0, _TELINIT_LIST_DISABLED, "")
+
+ self.assertFalse(simpleinit_msb.service_enabled())
+
+ # Race condition check
+ with patch('ansible_collections.community.general.plugins.modules.simpleinit_msb.SimpleinitMSB.service_enabled', return_value=True):
+ execute_command.return_value = (0, "", _TELINIT_ALREADY_DISABLED)
+
+ simpleinit_msb.service_enable()
+
+ self.assertFalse(simpleinit_msb.changed)
+
+ @patch('ansible_collections.community.general.plugins.modules.simpleinit_msb.SimpleinitMSB.service_control')
+ def test_check_service_running(self, service_control):
+ simpleinit_msb = self.init_module({
+ 'name': 'sshd',
+ 'state': 'running',
+ })
+
+ service_control.return_value = (0, _TELINIT_STATUS_RUNNING, "")
+
+ self.assertFalse(simpleinit_msb.get_service_status())
+
+ @patch('ansible_collections.community.general.plugins.modules.simpleinit_msb.SimpleinitMSB.service_control')
+ def test_check_service_running_not(self, service_control):
+ simpleinit_msb = self.init_module({
+ 'name': 'smgl-metalog',
+ 'state': 'running',
+ })
+
+ service_control.return_value = (0, _TELINIT_STATUS_RUNNING_NOT, "")
+
+ self.assertFalse(simpleinit_msb.get_service_status())
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_slack.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_slack.py
index ab4405baa..52ac9b7f3 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_slack.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_slack.py
@@ -105,7 +105,7 @@ class TestSlackModule(ModuleTestCase):
self.module.main()
self.assertTrue(fetch_url_mock.call_count, 1)
- self.assertEquals(fetch_url_mock.call_args[1]['url'], "https://slack.com/api/chat.postMessage")
+ self.assertEqual(fetch_url_mock.call_args[1]['url'], "https://slack.com/api/chat.postMessage")
def test_edit_message(self):
set_module_args({
@@ -125,9 +125,9 @@ class TestSlackModule(ModuleTestCase):
self.module.main()
self.assertTrue(fetch_url_mock.call_count, 2)
- self.assertEquals(fetch_url_mock.call_args[1]['url'], "https://slack.com/api/chat.update")
+ self.assertEqual(fetch_url_mock.call_args[1]['url'], "https://slack.com/api/chat.update")
call_data = json.loads(fetch_url_mock.call_args[1]['data'])
- self.assertEquals(call_data['ts'], "12345")
+ self.assertEqual(call_data['ts'], "12345")
def test_message_with_blocks(self):
"""tests sending a message with blocks"""
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_snap.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_snap.py
new file mode 100644
index 000000000..480f637b6
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_snap.py
@@ -0,0 +1,474 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Alexei Znamensky (russoz@gmail.com)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+from .helper import Helper, ModuleTestCase, RunCmdCall
+from ansible_collections.community.general.plugins.modules import snap
+
+
+issue_6803_status_out = """Name Version Rev Tracking Publisher Notes
+core20 20220826 1623 latest/stable canonical** base
+lxd 5.6-794016a 23680 latest/stable/… canonical** -
+snapd 2.57.4 17336 latest/stable canonical** snapd
+"""
+
+issue_6803_microk8s_out = (
+ "\rEnsure prerequisites for \"microk8s\" are available /"
+ "\rDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" "
+ "\rDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" \\"
+ "\rDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" "
+ "\rDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" /\u001b[?25"
+ "\r\u001b[7m\u001b[0mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 0% 0B/s ages"
+ "\r\u001b[7m\u001b[0mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 0% 0B/s ages"
+ "\r\u001b[7m\u001b[0mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 0% 0B/s ages"
+ "\r\u001b[7m\u001b[0mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 0% 880kB/s 3m21"
+ "\r\u001b[7m\u001b[0mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 1% 2.82MB/s 1m02"
+ "\r\u001b[7mD\u001b[0mownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 2% 4.71MB/s 37.0"
+ "\r\u001b[7mDo\u001b[0mwnload snap \"microk8s\" (5372) from channel \"1.27/stable\" 4% 9.09MB/s 18.8"
+ "\r\u001b[7mDown\u001b[0mload snap \"microk8s\" (5372) from channel \"1.27/stable\" 6% 12.4MB/s 13.5"
+ "\r\u001b[7mDownl\u001b[0moad snap \"microk8s\" (5372) from channel \"1.27/stable\" 7% 14.5MB/s 11.3"
+ "\r\u001b[7mDownloa\u001b[0md snap \"microk8s\" (5372) from channel \"1.27/stable\" 9% 15.9MB/s 10.1"
+ "\r\u001b[7mDownload \u001b[0msnap \"microk8s\" (5372) from channel \"1.27/stable\" 11% 18.0MB/s 8.75"
+ "\r\u001b[7mDownload s\u001b[0mnap \"microk8s\" (5372) from channel \"1.27/stable\" 13% 19.4MB/s 7.91"
+ "\r\u001b[7mDownload sn\u001b[0map \"microk8s\" (5372) from channel \"1.27/stable\" 15% 20.1MB/s 7.50"
+ "\r\u001b[7mDownload snap\u001b[0m \"microk8s\" (5372) from channel \"1.27/stable\" 17% 20.9MB/s 7.05"
+ "\r\u001b[7mDownload snap \"\u001b[0mmicrok8s\" (5372) from channel \"1.27/stable\" 19% 22.1MB/s 6.50"
+ "\r\u001b[7mDownload snap \"m\u001b[0microk8s\" (5372) from channel \"1.27/stable\" 21% 22.9MB/s 6.11"
+ "\r\u001b[7mDownload snap \"mic\u001b[0mrok8s\" (5372) from channel \"1.27/stable\" 23% 23.2MB/s 5.90"
+ "\r\u001b[7mDownload snap \"micr\u001b[0mok8s\" (5372) from channel \"1.27/stable\" 25% 23.9MB/s 5.58"
+ "\r\u001b[7mDownload snap \"microk\u001b[0m8s\" (5372) from channel \"1.27/stable\" 27% 24.5MB/s 5.30"
+ "\r\u001b[7mDownload snap \"microk8\u001b[0ms\" (5372) from channel \"1.27/stable\" 29% 24.9MB/s 5.09"
+ "\r\u001b[7mDownload snap \"microk8s\"\u001b[0m (5372) from channel \"1.27/stable\" 31% 25.4MB/s 4.85"
+ "\r\u001b[7mDownload snap \"microk8s\" (\u001b[0m5372) from channel \"1.27/stable\" 33% 25.8MB/s 4.63"
+ "\r\u001b[7mDownload snap \"microk8s\" (5\u001b[0m372) from channel \"1.27/stable\" 35% 26.2MB/s 4.42"
+ "\r\u001b[7mDownload snap \"microk8s\" (53\u001b[0m72) from channel \"1.27/stable\" 36% 26.3MB/s 4.30"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372\u001b[0m) from channel \"1.27/stable\" 38% 26.7MB/s 4.10"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) \u001b[0mfrom channel \"1.27/stable\" 40% 26.9MB/s 3.95"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) f\u001b[0mrom channel \"1.27/stable\" 42% 27.2MB/s 3.77"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) fro\u001b[0mm channel \"1.27/stable\" 44% 27.4MB/s 3.63"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from\u001b[0m channel \"1.27/stable\" 46% 27.8MB/s 3.44"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from c\u001b[0mhannel \"1.27/stable\" 48% 27.9MB/s 3.31"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from cha\u001b[0mnnel \"1.27/stable\" 50% 28.1MB/s 3.15"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from chan\u001b[0mnel \"1.27/stable\" 52% 28.3MB/s 3.02"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channe\u001b[0ml \"1.27/stable\" 54% 28.5MB/s 2.87"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel\u001b[0m \"1.27/stable\" 56% 28.6MB/s 2.75"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \u001b[0m\"1.27/stable\" 57% 28.7MB/s 2.63"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1\u001b[0m.27/stable\" 60% 28.9MB/s 2.47"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.2\u001b[0m7/stable\" 62% 29.0MB/s 2.35"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27\u001b[0m/stable\" 63% 29.1MB/s 2.23"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/s\u001b[0mtable\" 65% 29.2MB/s 2.10"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/st\u001b[0mable\" 67% 29.4MB/s 1.97"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stab\u001b[0mle\" 69% 29.5MB/s 1.85"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stabl\u001b[0me\" 71% 29.5MB/s 1.74"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\"\u001b[0m 73% 29.7MB/s 1.59"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" \u001b[0m 75% 29.8MB/s 1.48"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" \u001b[0m 77% 29.8MB/s 1.37"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 7\u001b[0m9% 29.9MB/s 1.26"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 81\u001b[0m% 30.0MB/s 1.14"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 83% \u001b[0m30.1MB/s 1.01"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 84% 3\u001b[0m0.1MB/s 919m"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 86% 30.\u001b[0m1MB/s 810m"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 88% 30.2\u001b[0mMB/s 676m"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 91% 30.3MB\u001b[0m/s 555m"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 93% 30.4MB/s\u001b[0m 436m"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 95% 30.5MB/s \u001b[0m317m"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 96% 30.5MB/s 21\u001b[0m1m"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 98% 30.5MB/s 117\u001b[0mm"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 100% 30.5MB/s 11m\u001b[0m"
+ "\r\u001b[7mDownload snap \"microk8s\" (5372) from channel \"1.27/stable\" 100% 30.0MB/s 0.0ns\u001b[0"
+ "\rFetch and check assertions for snap \"microk8s\" (5372) "
+ "\rMount snap \"microk8s\" (5372) \\"
+ "\rMount snap \"microk8s\" (5372) "
+ "\rMount snap \"microk8s\" (5372) "
+ "\rMount snap \"microk8s\" (5372) "
+ "\rSetup snap \"microk8s\" (5372) security profiles \\"
+ "\rSetup snap \"microk8s\" (5372) security profiles "
+ "\rSetup snap \"microk8s\" (5372) security profiles "
+ "\rSetup snap \"microk8s\" (5372) security profiles "
+ "\rSetup snap \"microk8s\" (5372) security profiles \\"
+ "\rSetup snap \"microk8s\" (5372) security profiles "
+ "\rSetup snap \"microk8s\" (5372) security profiles "
+ "\rSetup snap \"microk8s\" (5372) security profiles "
+ "\rSetup snap \"microk8s\" (5372) security profiles \\"
+ "\rSetup snap \"microk8s\" (5372) security profiles "
+ "\rSetup snap \"microk8s\" (5372) security profiles "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present \\"
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present \\"
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present \\"
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present \\"
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present \\"
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present \\"
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present \\"
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present \\"
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present \\"
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present \\"
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present \\"
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present \\"
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present \\"
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present \\"
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rRun install hook of \"microk8s\" snap if present "
+ "\rStart snap \"microk8s\" (5372) services \\"
+ "\rStart snap \"microk8s\" (5372) services "
+ "\rStart snap \"microk8s\" (5372) services "
+ "\rStart snap \"microk8s\" (5372) services "
+ "\rStart snap \"microk8s\" (5372) services \\"
+ "\rStart snap \"microk8s\" (5372) services "
+ "\rStart snap \"microk8s\" (5372) services "
+ "\rStart snap \"microk8s\" (5372) services "
+ "\rStart snap \"microk8s\" (5372) services \\"
+ "\rStart snap \"microk8s\" (5372) services "
+ "\rStart snap \"microk8s\" (5372) services "
+ "\rStart snap \"microk8s\" (5372) services "
+ "\rStart snap \"microk8s\" (5372) services \\"
+ "\rStart snap \"microk8s\" (5372) services "
+ "\rStart snap \"microk8s\" (5372) services "
+ "\rStart snap \"microk8s\" (5372) services "
+ "\rStart snap \"microk8s\" (5372) services \\"
+ "\rRun configure hook of \"microk8s\" snap if present "
+ "\rRun configure hook of \"microk8s\" snap if present "
+ "\rRun configure hook of \"microk8s\" snap if present "
+ "\rRun configure hook of \"microk8s\" snap if present \\"
+ "\rRun configure hook of \"microk8s\" snap if present "
+ "\rRun configure hook of \"microk8s\" snap if present "
+ "\rRun configure hook of \"microk8s\" snap if present "
+ "\rRun configure hook of \"microk8s\" snap if present \\"
+ "\rRun configure hook of \"microk8s\" snap if present "
+ "\rRun configure hook of \"microk8s\" snap if present "
+ "\rRun configure hook of \"microk8s\" snap if present "
+ "\rRun configure hook of \"microk8s\" snap if present \\"
+ "\rRun configure hook of \"microk8s\" snap if present "
+ "\rRun configure hook of \"microk8s\" snap if present "
+ "\rRun configure hook of \"microk8s\" snap if present "
+ "\rRun configure hook of \"microk8s\" snap if present \\"
+ "\rRun configure hook of \"microk8s\" snap if present "
+ "\rRun configure hook of \"microk8s\" snap if present "
+ "\rRun configure hook of \"microk8s\" snap if present "
+ "\rRun configure hook of \"microk8s\" snap if present \\"
+ "\rRun configure hook of \"microk8s\" snap if present "
+ "\rRun service command \"restart\" for services [\"daemon-apiserver-proxy\"] of snap \""
+ "\r\u001b[0m\u001b[?25h\u001b[Kmicrok8s (1.27/stable) v1.27.2 from Canonical** installed\n"
+)
+
+issue_6803_kubectl_out = (
+ "\rEnsure prerequisites for \"kubectl\" are available /"
+ "\rDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" "
+ "\rDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" \\"
+ "\rDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" "
+ "\rDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" /\u001b[?25"
+ "\r\u001b[7m\u001b[0mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 0% 0B/s ages"
+ "\r\u001b[7m\u001b[0mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 0% 0B/s ages"
+ "\r\u001b[7m\u001b[0mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 0% 0B/s ages"
+ "\r\u001b[7m\u001b[0mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 0% 880kB/s 3m21"
+ "\r\u001b[7m\u001b[0mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 1% 2.82MB/s 1m02"
+ "\r\u001b[7mD\u001b[0mownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 2% 4.71MB/s 37.0"
+ "\r\u001b[7mDo\u001b[0mwnload snap \"kubectl\" (5372) from channel \"1.27/stable\" 4% 9.09MB/s 18.8"
+ "\r\u001b[7mDown\u001b[0mload snap \"kubectl\" (5372) from channel \"1.27/stable\" 6% 12.4MB/s 13.5"
+ "\r\u001b[7mDownl\u001b[0moad snap \"kubectl\" (5372) from channel \"1.27/stable\" 7% 14.5MB/s 11.3"
+ "\r\u001b[7mDownloa\u001b[0md snap \"kubectl\" (5372) from channel \"1.27/stable\" 9% 15.9MB/s 10.1"
+ "\r\u001b[7mDownload \u001b[0msnap \"kubectl\" (5372) from channel \"1.27/stable\" 11% 18.0MB/s 8.75"
+ "\r\u001b[7mDownload s\u001b[0mnap \"kubectl\" (5372) from channel \"1.27/stable\" 13% 19.4MB/s 7.91"
+ "\r\u001b[7mDownload sn\u001b[0map \"kubectl\" (5372) from channel \"1.27/stable\" 15% 20.1MB/s 7.50"
+ "\r\u001b[7mDownload snap\u001b[0m \"kubectl\" (5372) from channel \"1.27/stable\" 17% 20.9MB/s 7.05"
+ "\r\u001b[7mDownload snap \"\u001b[0mkubectl\" (5372) from channel \"1.27/stable\" 19% 22.1MB/s 6.50"
+ "\r\u001b[7mDownload snap \"m\u001b[0kubectl\" (5372) from channel \"1.27/stable\" 21% 22.9MB/s 6.11"
+ "\r\u001b[7mDownload snap \"mic\u001b[0mrok8s\" (5372) from channel \"1.27/stable\" 23% 23.2MB/s 5.90"
+ "\r\u001b[7mDownload snap \"micr\u001b[0mok8s\" (5372) from channel \"1.27/stable\" 25% 23.9MB/s 5.58"
+ "\r\u001b[7mDownload snap \"microk\u001b[0m8s\" (5372) from channel \"1.27/stable\" 27% 24.5MB/s 5.30"
+ "\r\u001b[7mDownload snap \"microk8\u001b[0ms\" (5372) from channel \"1.27/stable\" 29% 24.9MB/s 5.09"
+ "\r\u001b[7mDownload snap \"kubectl\"\u001b[0m (5372) from channel \"1.27/stable\" 31% 25.4MB/s 4.85"
+ "\r\u001b[7mDownload snap \"kubectl\" (\u001b[0m5372) from channel \"1.27/stable\" 33% 25.8MB/s 4.63"
+ "\r\u001b[7mDownload snap \"kubectl\" (5\u001b[0m372) from channel \"1.27/stable\" 35% 26.2MB/s 4.42"
+ "\r\u001b[7mDownload snap \"kubectl\" (53\u001b[0m72) from channel \"1.27/stable\" 36% 26.3MB/s 4.30"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372\u001b[0m) from channel \"1.27/stable\" 38% 26.7MB/s 4.10"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) \u001b[0mfrom channel \"1.27/stable\" 40% 26.9MB/s 3.95"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) f\u001b[0mrom channel \"1.27/stable\" 42% 27.2MB/s 3.77"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) fro\u001b[0mm channel \"1.27/stable\" 44% 27.4MB/s 3.63"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from\u001b[0m channel \"1.27/stable\" 46% 27.8MB/s 3.44"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from c\u001b[0mhannel \"1.27/stable\" 48% 27.9MB/s 3.31"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from cha\u001b[0mnnel \"1.27/stable\" 50% 28.1MB/s 3.15"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from chan\u001b[0mnel \"1.27/stable\" 52% 28.3MB/s 3.02"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channe\u001b[0ml \"1.27/stable\" 54% 28.5MB/s 2.87"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel\u001b[0m \"1.27/stable\" 56% 28.6MB/s 2.75"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \u001b[0m\"1.27/stable\" 57% 28.7MB/s 2.63"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1\u001b[0m.27/stable\" 60% 28.9MB/s 2.47"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.2\u001b[0m7/stable\" 62% 29.0MB/s 2.35"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27\u001b[0m/stable\" 63% 29.1MB/s 2.23"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/s\u001b[0mtable\" 65% 29.2MB/s 2.10"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/st\u001b[0mable\" 67% 29.4MB/s 1.97"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stab\u001b[0mle\" 69% 29.5MB/s 1.85"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stabl\u001b[0me\" 71% 29.5MB/s 1.74"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\"\u001b[0m 73% 29.7MB/s 1.59"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" \u001b[0m 75% 29.8MB/s 1.48"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" \u001b[0m 77% 29.8MB/s 1.37"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 7\u001b[0m9% 29.9MB/s 1.26"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 81\u001b[0m% 30.0MB/s 1.14"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 83% \u001b[0m30.1MB/s 1.01"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 84% 3\u001b[0m0.1MB/s 919m"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 86% 30.\u001b[0m1MB/s 810m"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 88% 30.2\u001b[0mMB/s 676m"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 91% 30.3MB\u001b[0m/s 555m"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 93% 30.4MB/s\u001b[0m 436m"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 95% 30.5MB/s \u001b[0m317m"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 96% 30.5MB/s 21\u001b[0m1m"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 98% 30.5MB/s 117\u001b[0mm"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 100% 30.5MB/s 11m\u001b[0m"
+ "\r\u001b[7mDownload snap \"kubectl\" (5372) from channel \"1.27/stable\" 100% 30.0MB/s 0.0ns\u001b[0"
+ "\rFetch and check assertions for snap \"kubectl\" (5372) "
+ "\rMount snap \"kubectl\" (5372) \\"
+ "\rMount snap \"kubectl\" (5372) "
+ "\rMount snap \"kubectl\" (5372) "
+ "\rMount snap \"kubectl\" (5372) "
+ "\rSetup snap \"kubectl\" (5372) security profiles \\"
+ "\rSetup snap \"kubectl\" (5372) security profiles "
+ "\rSetup snap \"kubectl\" (5372) security profiles "
+ "\rSetup snap \"kubectl\" (5372) security profiles "
+ "\rSetup snap \"kubectl\" (5372) security profiles \\"
+ "\rSetup snap \"kubectl\" (5372) security profiles "
+ "\rSetup snap \"kubectl\" (5372) security profiles "
+ "\rSetup snap \"kubectl\" (5372) security profiles "
+ "\rSetup snap \"kubectl\" (5372) security profiles \\"
+ "\rSetup snap \"kubectl\" (5372) security profiles "
+ "\rSetup snap \"kubectl\" (5372) security profiles "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present \\"
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present \\"
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present \\"
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present \\"
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present \\"
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present \\"
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present \\"
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present \\"
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present \\"
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present \\"
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present \\"
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present \\"
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present \\"
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present \\"
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rRun install hook of \"kubectl\" snap if present "
+ "\rStart snap \"kubectl\" (5372) services \\"
+ "\rStart snap \"kubectl\" (5372) services "
+ "\rStart snap \"kubectl\" (5372) services "
+ "\rStart snap \"kubectl\" (5372) services "
+ "\rStart snap \"kubectl\" (5372) services \\"
+ "\rStart snap \"kubectl\" (5372) services "
+ "\rStart snap \"kubectl\" (5372) services "
+ "\rStart snap \"kubectl\" (5372) services "
+ "\rStart snap \"kubectl\" (5372) services \\"
+ "\rStart snap \"kubectl\" (5372) services "
+ "\rStart snap \"kubectl\" (5372) services "
+ "\rStart snap \"kubectl\" (5372) services "
+ "\rStart snap \"kubectl\" (5372) services \\"
+ "\rStart snap \"kubectl\" (5372) services "
+ "\rStart snap \"kubectl\" (5372) services "
+ "\rStart snap \"kubectl\" (5372) services "
+ "\rStart snap \"kubectl\" (5372) services \\"
+ "\rRun configure hook of \"kubectl\" snap if present "
+ "\rRun configure hook of \"kubectl\" snap if present "
+ "\rRun configure hook of \"kubectl\" snap if present "
+ "\rRun configure hook of \"kubectl\" snap if present \\"
+ "\rRun configure hook of \"kubectl\" snap if present "
+ "\rRun configure hook of \"kubectl\" snap if present "
+ "\rRun configure hook of \"kubectl\" snap if present "
+ "\rRun configure hook of \"kubectl\" snap if present \\"
+ "\rRun configure hook of \"kubectl\" snap if present "
+ "\rRun configure hook of \"kubectl\" snap if present "
+ "\rRun configure hook of \"kubectl\" snap if present "
+ "\rRun configure hook of \"kubectl\" snap if present \\"
+ "\rRun configure hook of \"kubectl\" snap if present "
+ "\rRun configure hook of \"kubectl\" snap if present "
+ "\rRun configure hook of \"kubectl\" snap if present "
+ "\rRun configure hook of \"kubectl\" snap if present \\"
+ "\rRun configure hook of \"kubectl\" snap if present "
+ "\rRun configure hook of \"kubectl\" snap if present "
+ "\rRun configure hook of \"kubectl\" snap if present "
+ "\rRun configure hook of \"kubectl\" snap if present \\"
+ "\rRun configure hook of \"kubectl\" snap if present "
+ "\rRun service command \"restart\" for services [\"daemon-apiserver-proxy\"] of snap \""
+ "\r\u001b[0m\u001b[?25h\u001b[Kkubectl (1.27/stable) v1.27.2 from Canonical** installed\n"
+)
+
+TEST_CASES = [
+ ModuleTestCase(
+ id="simple case",
+ input={"name": ["hello-world"]},
+ output=dict(changed=True, snaps_installed=["hello-world"]),
+ flags={},
+ run_command_calls=[
+ RunCmdCall(
+ command=['/testbin/snap', 'info', 'hello-world'],
+ environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+ rc=0,
+ out='name: hello-world\n',
+ err="",
+ ),
+ RunCmdCall(
+ command=['/testbin/snap', 'list'],
+ environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+ rc=0,
+ out="",
+ err="",
+ ),
+ RunCmdCall(
+ command=['/testbin/snap', 'install', 'hello-world'],
+ environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+ rc=0,
+ out="hello-world (12345/stable) v12345 from Canonical** installed\n",
+ err="",
+ ),
+ RunCmdCall(
+ command=['/testbin/snap', 'list'],
+ environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+ rc=0,
+ out=(
+ "Name Version Rev Tracking Publisher Notes"
+ "core20 20220826 1623 latest/stable canonical** base"
+ "lxd 5.6-794016a 23680 latest/stable/… canonical** -"
+ "hello-world 5.6-794016a 23680 latest/stable/… canonical** -"
+ "snapd 2.57.4 17336 latest/stable canonical** snapd"
+ ""),
+ err="",
+ ),
+ ]
+ ),
+ ModuleTestCase(
+ id="issue_6803",
+ input={"name": ["microk8s", "kubectl"], "classic": True},
+ output=dict(changed=True, snaps_installed=["microk8s", "kubectl"]),
+ flags={},
+ run_command_calls=[
+ RunCmdCall(
+ command=['/testbin/snap', 'info', 'microk8s', 'kubectl'],
+ environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+ rc=0,
+ out='name: microk8s\n---\nname: kubectl\n',
+ err="",
+ ),
+ RunCmdCall(
+ command=['/testbin/snap', 'list'],
+ environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+ rc=0,
+ out=issue_6803_status_out,
+ err="",
+ ),
+ RunCmdCall(
+ command=['/testbin/snap', 'install', '--classic', 'microk8s'],
+ environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+ rc=0,
+ out=issue_6803_microk8s_out,
+ err="",
+ ),
+ RunCmdCall(
+ command=['/testbin/snap', 'install', '--classic', 'kubectl'],
+ environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+ rc=0,
+ out=issue_6803_kubectl_out,
+ err="",
+ ),
+ RunCmdCall(
+ command=['/testbin/snap', 'list'],
+ environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
+ rc=0,
+ out=(
+ "Name Version Rev Tracking Publisher Notes"
+ "core20 20220826 1623 latest/stable canonical** base"
+ "lxd 5.6-794016a 23680 latest/stable/… canonical** -"
+ "microk8s 5.6-794016a 23680 latest/stable/… canonical** -"
+ "kubectl 5.6-794016a 23680 latest/stable/… canonical** -"
+ "snapd 2.57.4 17336 latest/stable canonical** snapd"
+ ""),
+ err="",
+ ),
+ ]
+ ),
+]
+
+helper = Helper.from_list(snap.main, TEST_CASES)
+patch_bin = helper.cmd_fixture
+test_module = helper.test_module
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_usb_facts.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_usb_facts.py
new file mode 100644
index 000000000..084433492
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_usb_facts.py
@@ -0,0 +1,105 @@
+# Copyright (c) Ansible project
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import json
+
+from ansible_collections.community.general.tests.unit.compat import mock
+from ansible_collections.community.general.tests.unit.compat import unittest
+from ansible.module_utils import basic
+from ansible.module_utils.common.text.converters import to_bytes
+from ansible_collections.community.general.plugins.modules import usb_facts
+
+
+def set_module_args(args):
+ """prepare arguments so that they will be picked up during module creation"""
+ args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
+ basic._ANSIBLE_ARGS = to_bytes(args)
+
+
+class AnsibleExitJson(Exception):
+ """Exception class to be raised by module.exit_json and caught by the test case"""
+ pass
+
+
+class AnsibleFailJson(Exception):
+ """Exception class to be raised by module.fail_json and caught by the test case"""
+ pass
+
+
+def exit_json(*args, **kwargs):
+ """function to patch over exit_json; package return data into an exception"""
+ if 'changed' not in kwargs:
+ kwargs['changed'] = False
+ raise AnsibleExitJson(kwargs)
+
+
+def fail_json(*args, **kwargs):
+ """function to patch over fail_json; package return data into an exception"""
+ kwargs['failed'] = True
+ raise AnsibleFailJson(kwargs)
+
+
+def get_bin_path(self, arg, required=False):
+ """Mock AnsibleModule.get_bin_path"""
+ if arg == 'lsusb':
+ return '/usr/bin/lsusb'
+ else:
+ if required:
+ fail_json(msg='%r not found !' % arg)
+
+
+class TestUsbFacts(unittest.TestCase):
+
+ def setUp(self):
+ self.mock_module_helper = mock.patch.multiple(basic.AnsibleModule,
+ exit_json=exit_json,
+ fail_json=fail_json,
+ get_bin_path=get_bin_path)
+ self.mock_module_helper.start()
+ self.addCleanup(self.mock_module_helper.stop)
+ self.testing_data = [
+ {
+ "input": "Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub",
+ "bus": "001",
+ "device": "001",
+ "id": "1d6b:0002",
+ "name": "Linux Foundation 2.0 root hub"
+ },
+ {
+ "input": "Bus 003 Device 002: ID 8087:8008 Intel Corp. Integrated Rate Matching Hub",
+ "bus": "003",
+ "device": "002",
+ "id": "8087:8008",
+ "name": "Intel Corp. Integrated Rate Matching Hub"
+ }
+ ]
+ self.output_fields = ["bus", "device", "id", "name"]
+
+ def test_parsing_single_line(self):
+ for data in self.testing_data:
+ with mock.patch.object(basic.AnsibleModule, 'run_command') as mock_run_command:
+ command_output = data["input"]
+ mock_run_command.return_value = 0, command_output, None
+ with self.assertRaises(AnsibleExitJson) as result:
+ set_module_args({})
+ usb_facts.main()
+ for output_field in self.output_fields:
+ self.assertEqual(result.exception.args[0]["ansible_facts"]["usb_devices"][0][output_field], data[output_field])
+
+ def test_parsing_multiple_lines(self):
+ input = ""
+ for data in self.testing_data:
+ input += ("%s\n" % data["input"])
+ with mock.patch.object(basic.AnsibleModule, 'run_command') as mock_run_command:
+ mock_run_command.return_value = 0, input, None
+ with self.assertRaises(AnsibleExitJson) as result:
+ set_module_args({})
+ usb_facts.main()
+ for index in range(0, len(self.testing_data)):
+ for output_field in self.output_fields:
+ self.assertEqual(result.exception.args[0]["ansible_facts"]["usb_devices"][index][output_field],
+ self.testing_data[index][output_field])
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf.py
index c979fd8d2..fbc2dae5f 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf.py
@@ -12,301 +12,9 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-import json
from ansible_collections.community.general.plugins.modules import xfconf
+from .helper import Helper
-import pytest
-TESTED_MODULE = xfconf.__name__
-
-
-@pytest.fixture
-def patch_xfconf(mocker):
- """
- Function used for mocking some parts of redhat_subscription module
- """
- mocker.patch('ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.get_bin_path',
- return_value='/testbin/xfconf-query')
-
-
-@pytest.mark.parametrize('patch_ansible_module', [{}], indirect=['patch_ansible_module'])
-@pytest.mark.usefixtures('patch_ansible_module')
-def test_without_required_parameters(capfd, patch_xfconf):
- """
- Failure must occurs when all parameters are missing
- """
- with pytest.raises(SystemExit):
- xfconf.main()
- out, err = capfd.readouterr()
- results = json.loads(out)
- assert results['failed']
- assert 'missing required arguments' in results['msg']
-
-
-TEST_CASES = [
- [
- {
- 'channel': 'xfwm4',
- 'property': '/general/inactive_opacity',
- 'state': 'present',
- 'value_type': 'int',
- 'value': 90,
- },
- {
- 'id': 'test_property_set_property',
- 'run_command.calls': [
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/inactive_opacity'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- # Mock of returned code, stdout and stderr
- (0, '100\n', '',),
- ),
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/inactive_opacity',
- '--create', '--type', 'int', '--set', '90'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- # Mock of returned code, stdout and stderr
- (0, '', '',),
- ),
- ],
- 'changed': True,
- 'previous_value': '100',
- 'value_type': 'int',
- 'value': '90',
- },
- ],
- [
- {
- 'channel': 'xfwm4',
- 'property': '/general/inactive_opacity',
- 'state': 'present',
- 'value_type': 'int',
- 'value': 90,
- },
- {
- 'id': 'test_property_set_property_same_value',
- 'run_command.calls': [
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/inactive_opacity'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- # Mock of returned code, stdout and stderr
- (0, '90\n', '',),
- ),
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/inactive_opacity',
- '--create', '--type', 'int', '--set', '90'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- # Mock of returned code, stdout and stderr
- (0, '', '',),
- ),
- ],
- 'changed': False,
- 'previous_value': '90',
- 'value_type': 'int',
- 'value': '90',
- },
- ],
- [
- {
- 'channel': 'xfce4-session',
- 'property': '/general/SaveOnExit',
- 'state': 'present',
- 'value_type': 'bool',
- 'value': False,
- },
- {
- 'id': 'test_property_set_property_bool_false',
- 'run_command.calls': [
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--channel', 'xfce4-session', '--property', '/general/SaveOnExit'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- # Mock of returned code, stdout and stderr
- (0, 'true\n', '',),
- ),
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--channel', 'xfce4-session', '--property', '/general/SaveOnExit',
- '--create', '--type', 'bool', '--set', 'false'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- # Mock of returned code, stdout and stderr
- (0, 'false\n', '',),
- ),
- ],
- 'changed': True,
- 'previous_value': 'true',
- 'value_type': 'bool',
- 'value': 'False',
- },
- ],
- [
- {
- 'channel': 'xfwm4',
- 'property': '/general/workspace_names',
- 'state': 'present',
- 'value_type': 'string',
- 'value': ['A', 'B', 'C'],
- },
- {
- 'id': 'test_property_set_array',
- 'run_command.calls': [
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- # Mock of returned code, stdout and stderr
- (0, 'Value is an array with 3 items:\n\nMain\nWork\nTmp\n', '',),
- ),
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names',
- '--create', '--force-array', '--type', 'string', '--set', 'A', '--type', 'string', '--set', 'B',
- '--type', 'string', '--set', 'C'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- # Mock of returned code, stdout and stderr
- (0, '', '',),
- ),
- ],
- 'changed': True,
- 'previous_value': ['Main', 'Work', 'Tmp'],
- 'value_type': ['str', 'str', 'str'],
- 'value': ['A', 'B', 'C'],
- },
- ],
- [
- {
- 'channel': 'xfwm4',
- 'property': '/general/workspace_names',
- 'state': 'present',
- 'value_type': 'string',
- 'value': ['A', 'B', 'C'],
- },
- {
- 'id': 'test_property_set_array_to_same_value',
- 'run_command.calls': [
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- # Mock of returned code, stdout and stderr
- (0, 'Value is an array with 3 items:\n\nA\nB\nC\n', '',),
- ),
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names',
- '--create', '--force-array', '--type', 'string', '--set', 'A', '--type', 'string', '--set', 'B',
- '--type', 'string', '--set', 'C'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- # Mock of returned code, stdout and stderr
- (0, '', '',),
- ),
- ],
- 'changed': False,
- 'previous_value': ['A', 'B', 'C'],
- 'value_type': ['str', 'str', 'str'],
- 'value': ['A', 'B', 'C'],
- },
- ],
- [
- {
- 'channel': 'xfwm4',
- 'property': '/general/workspace_names',
- 'state': 'absent',
- },
- {
- 'id': 'test_property_reset_value',
- 'run_command.calls': [
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- # Mock of returned code, stdout and stderr
- (0, 'Value is an array with 3 items:\n\nA\nB\nC\n', '',),
- ),
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names',
- '--reset'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
- # Mock of returned code, stdout and stderr
- (0, '', '',),
- ),
- ],
- 'changed': True,
- 'previous_value': ['A', 'B', 'C'],
- 'value_type': None,
- 'value': None,
- },
- ],
-]
-TEST_CASES_IDS = [item[1]['id'] for item in TEST_CASES]
-
-
-@pytest.mark.parametrize('patch_ansible_module, testcase',
- TEST_CASES,
- ids=TEST_CASES_IDS,
- indirect=['patch_ansible_module'])
-@pytest.mark.usefixtures('patch_ansible_module')
-def test_xfconf(mocker, capfd, patch_xfconf, testcase):
- """
- Run unit tests for test cases listen in TEST_CASES
- """
-
- # Mock function used for running commands first
- call_results = [item[2] for item in testcase['run_command.calls']]
- mock_run_command = mocker.patch(
- 'ansible.module_utils.basic.AnsibleModule.run_command',
- side_effect=call_results)
-
- # Try to run test case
- with pytest.raises(SystemExit):
- xfconf.main()
-
- out, err = capfd.readouterr()
- results = json.loads(out)
- print("testcase =\n%s" % testcase)
- print("results =\n%s" % results)
-
- assert 'changed' in results
- assert results['changed'] == testcase['changed']
-
- for test_result in ('channel', 'property'):
- assert test_result in results, "'{0}' not found in {1}".format(test_result, results)
- assert results[test_result] == results['invocation']['module_args'][test_result], \
- "'{0}': '{1}' != '{2}'".format(test_result, results[test_result], results['invocation']['module_args'][test_result])
-
- assert mock_run_command.call_count == len(testcase['run_command.calls'])
- if mock_run_command.call_count:
- call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list]
- expected_call_args_list = [(item[0], item[1]) for item in testcase['run_command.calls']]
- print("call args list =\n%s" % call_args_list)
- print("expected args list =\n%s" % expected_call_args_list)
- assert call_args_list == expected_call_args_list
-
- expected_cmd, dummy, expected_res = testcase['run_command.calls'][-1]
- assert results['cmd'] == expected_cmd
- assert results['stdout'] == expected_res[1]
- assert results['stderr'] == expected_res[2]
-
- for conditional_test_result in ('msg', 'value', 'previous_value'):
- if conditional_test_result in testcase:
- assert conditional_test_result in results, "'{0}' not found in {1}".format(conditional_test_result, results)
- assert results[conditional_test_result] == testcase[conditional_test_result], \
- "'{0}': '{1}' != '{2}'".format(conditional_test_result, results[conditional_test_result], testcase[conditional_test_result])
+Helper.from_module(xfconf, __name__)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf.yaml b/ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf.yaml
new file mode 100644
index 000000000..908154df2
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf.yaml
@@ -0,0 +1,185 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Alexei Znamensky (russoz@gmail.com)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+---
+- id: test_missing_input
+ input: {}
+ output:
+ failed: true
+ msg: "missing required arguments: channel, property"
+- id: test_property_set_property
+ input:
+ channel: xfwm4
+ property: /general/inactive_opacity
+ state: present
+ value_type: int
+ value: 90
+ output:
+ changed: true
+ previous_value: '100'
+ type: int
+ value: '90'
+ run_command_calls:
+ - command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/inactive_opacity]
+ environ: &env-def {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
+ rc: 0
+ out: "100\n"
+ err: ""
+ - command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/inactive_opacity, --create, --type, int, --set, '90']
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+- id: test_property_set_property_same_value
+ input:
+ channel: xfwm4
+ property: /general/inactive_opacity
+ state: present
+ value_type: int
+ value: 90
+ output:
+ changed: false
+ previous_value: '90'
+ type: int
+ value: '90'
+ run_command_calls:
+ - command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/inactive_opacity]
+ environ: *env-def
+ rc: 0
+ out: "90\n"
+ err: ""
+ - command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/inactive_opacity, --create, --type, int, --set, '90']
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+- id: test_property_set_property_bool_false
+ input:
+ channel: xfce4-session
+ property: /general/SaveOnExit
+ state: present
+ value_type: bool
+ value: False
+ output:
+ changed: true
+ previous_value: 'true'
+ type: bool
+ value: 'False'
+ run_command_calls:
+ - command: [/testbin/xfconf-query, --channel, xfce4-session, --property, /general/SaveOnExit]
+ environ: *env-def
+ rc: 0
+ out: "true\n"
+ err: ""
+ - command: [/testbin/xfconf-query, --channel, xfce4-session, --property, /general/SaveOnExit, --create, --type, bool, --set, 'false']
+ environ: *env-def
+ rc: 0
+ out: "false\n"
+ err: ""
+- id: test_property_set_array
+ input:
+ channel: xfwm4
+ property: /general/workspace_names
+ state: present
+ value_type: string
+ value: [A, B, C]
+ output:
+ changed: true
+ previous_value: [Main, Work, Tmp]
+ type: [string, string, string]
+ value: [A, B, C]
+ run_command_calls:
+ - command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/workspace_names]
+ environ: *env-def
+ rc: 0
+ out: "Value is an array with 3 items:\n\nMain\nWork\nTmp\n"
+ err: ""
+ - command:
+ - /testbin/xfconf-query
+ - --channel
+ - xfwm4
+ - --property
+ - /general/workspace_names
+ - --create
+ - --force-array
+ - --type
+ - string
+ - --set
+ - A
+ - --type
+ - string
+ - --set
+ - B
+ - --type
+ - string
+ - --set
+ - C
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+- id: test_property_set_array_to_same_value
+ input:
+ channel: xfwm4
+ property: /general/workspace_names
+ state: present
+ value_type: string
+ value: [A, B, C]
+ output:
+ changed: false
+ previous_value: [A, B, C]
+ type: [string, string, string]
+ value: [A, B, C]
+ run_command_calls:
+ - command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/workspace_names]
+ environ: *env-def
+ rc: 0
+ out: "Value is an array with 3 items:\n\nA\nB\nC\n"
+ err: ""
+ - command:
+ - /testbin/xfconf-query
+ - --channel
+ - xfwm4
+ - --property
+ - /general/workspace_names
+ - --create
+ - --force-array
+ - --type
+ - string
+ - --set
+ - A
+ - --type
+ - string
+ - --set
+ - B
+ - --type
+ - string
+ - --set
+ - C
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
+- id: test_property_reset_value
+ input:
+ channel: xfwm4
+ property: /general/workspace_names
+ state: absent
+ output:
+ changed: true
+ previous_value: [A, B, C]
+ type: null
+ value: null
+ run_command_calls:
+ - command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/workspace_names]
+ environ: *env-def
+ rc: 0
+ out: "Value is an array with 3 items:\n\nA\nB\nC\n"
+ err: ""
+ - command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/workspace_names, --reset]
+ environ: *env-def
+ rc: 0
+ out: ""
+ err: ""
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf_info.py b/ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf_info.py
index dfcd4f33a..67c63dda0 100644
--- a/ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf_info.py
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf_info.py
@@ -5,168 +5,9 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
-import json
from ansible_collections.community.general.plugins.modules import xfconf_info
+from .helper import Helper
-import pytest
-TESTED_MODULE = xfconf_info.__name__
-
-
-@pytest.fixture
-def patch_xfconf_info(mocker):
- """
- Function used for mocking some parts of redhat_subscription module
- """
- mocker.patch('ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.get_bin_path',
- return_value='/testbin/xfconf-query')
-
-
-TEST_CASES = [
- [
- {'channel': 'xfwm4', 'property': '/general/inactive_opacity'},
- {
- 'id': 'test_simple_property_get',
- 'run_command.calls': [
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/inactive_opacity'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- # Mock of returned code, stdout and stderr
- (0, '100\n', '',),
- ),
- ],
- 'is_array': False,
- 'value': '100',
- }
- ],
- [
- {'channel': 'xfwm4', 'property': '/general/i_dont_exist'},
- {
- 'id': 'test_simple_property_get_nonexistent',
- 'run_command.calls': [
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/i_dont_exist'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- # Mock of returned code, stdout and stderr
- (1, '', 'Property "/general/i_dont_exist" does not exist on channel "xfwm4".\n',),
- ),
- ],
- 'is_array': False,
- }
- ],
- [
- {'property': '/general/i_dont_exist'},
- {
- 'id': 'test_property_no_channel',
- 'run_command.calls': [],
- }
- ],
- [
- {'channel': 'xfwm4', 'property': '/general/workspace_names'},
- {
- 'id': 'test_property_get_array',
- 'run_command.calls': [
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- # Mock of returned code, stdout and stderr
- (0, 'Value is an array with 3 items:\n\nMain\nWork\nTmp\n', '',),
- ),
- ],
- 'is_array': True,
- 'value_array': ['Main', 'Work', 'Tmp'],
- },
- ],
- [
- {},
- {
- 'id': 'get_channels',
- 'run_command.calls': [
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--list'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- # Mock of returned code, stdout and stderr
- (0, 'Channels:\n a\n b\n c\n', '',),
- ),
- ],
- 'is_array': False,
- 'channels': ['a', 'b', 'c'],
- },
- ],
- [
- {'channel': 'xfwm4'},
- {
- 'id': 'get_properties',
- 'run_command.calls': [
- (
- # Calling of following command will be asserted
- ['/testbin/xfconf-query', '--list', '--channel', 'xfwm4'],
- # Was return code checked?
- {'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
- # Mock of returned code, stdout and stderr
- (0, '/general/wrap_cycle\n/general/wrap_layout\n/general/wrap_resistance\n/general/wrap_windows\n'
- '/general/wrap_workspaces\n/general/zoom_desktop\n', '',),
- ),
- ],
- 'is_array': False,
- 'properties': [
- '/general/wrap_cycle',
- '/general/wrap_layout',
- '/general/wrap_resistance',
- '/general/wrap_windows',
- '/general/wrap_workspaces',
- '/general/zoom_desktop',
- ],
- },
- ],
-]
-TEST_CASES_IDS = [item[1]['id'] for item in TEST_CASES]
-
-
-@pytest.mark.parametrize('patch_ansible_module, testcase',
- TEST_CASES,
- ids=TEST_CASES_IDS,
- indirect=['patch_ansible_module'])
-@pytest.mark.usefixtures('patch_ansible_module')
-def test_xfconf_info(mocker, capfd, patch_xfconf_info, testcase):
- """
- Run unit tests for test cases listen in TEST_CASES
- """
-
- # Mock function used for running commands first
- call_results = [item[2] for item in testcase['run_command.calls']]
- mock_run_command = mocker.patch(
- 'ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.run_command',
- side_effect=call_results)
-
- # Try to run test case
- with pytest.raises(SystemExit):
- xfconf_info.main()
-
- out, err = capfd.readouterr()
- results = json.loads(out)
- print("testcase =\n%s" % testcase)
- print("results =\n%s" % results)
-
- for conditional_test_result in ('value_array', 'value', 'is_array', 'properties', 'channels'):
- if conditional_test_result in testcase:
- assert conditional_test_result in results, "'{0}' not found in {1}".format(conditional_test_result, results)
- assert results[conditional_test_result] == testcase[conditional_test_result], \
- "'{0}': '{1}' != '{2}'".format(conditional_test_result, results[conditional_test_result], testcase[conditional_test_result])
-
- assert mock_run_command.call_count == len(testcase['run_command.calls'])
- if mock_run_command.call_count:
- call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list]
- expected_call_args_list = [(item[0], item[1]) for item in testcase['run_command.calls']]
- print("call args list =\n%s" % call_args_list)
- print("expected args list =\n%s" % expected_call_args_list)
- assert call_args_list == expected_call_args_list
+Helper.from_module(xfconf_info, __name__)
diff --git a/ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf_info.yaml b/ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf_info.yaml
new file mode 100644
index 000000000..519a87fdb
--- /dev/null
+++ b/ansible_collections/community/general/tests/unit/plugins/modules/test_xfconf_info.yaml
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Alexei Znamensky (russoz@gmail.com)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+---
+- id: test_simple_property_get
+ input:
+ channel: xfwm4
+ property: /general/inactive_opacity
+ output:
+ value: '100'
+ is_array: false
+ run_command_calls:
+ - command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/inactive_opacity]
+ environ: &env-def {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
+ rc: 0
+ out: "100\n"
+ err: ""
+- id: test_simple_property_get_nonexistent
+ input:
+ channel: xfwm4
+ property: /general/i_dont_exist
+ output: {}
+ run_command_calls:
+ - command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/i_dont_exist]
+ environ: *env-def
+ rc: 1
+ out: ""
+ err: 'Property "/general/i_dont_exist" does not exist on channel "xfwm4".\n'
+- id: test_property_no_channel
+ input:
+ property: /general/i_dont_exist
+ output:
+ failed: true
+ msg: "missing parameter(s) required by 'property': channel"
+ run_command_calls: []
+- id: test_property_get_array
+ input:
+ channel: xfwm4
+ property: /general/workspace_names
+ output:
+ is_array: true
+ value_array: [Main, Work, Tmp]
+ run_command_calls:
+ - command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/workspace_names]
+ environ: *env-def
+ rc: 0
+ out: "Value is an array with 3 items:\n\nMain\nWork\nTmp\n"
+ err: ""
+- id: get_channels
+ input: {}
+ output:
+ channels: [a, b, c]
+ run_command_calls:
+ - command: [/testbin/xfconf-query, --list]
+ environ: *env-def
+ rc: 0
+ out: "Channels:\n a\n b\n c\n"
+ err: ""
+- id: get_properties
+ input:
+ channel: xfwm4
+ output:
+ properties:
+ - /general/wrap_cycle
+ - /general/wrap_layout
+ - /general/wrap_resistance
+ - /general/wrap_windows
+ - /general/wrap_workspaces
+ - /general/zoom_desktop
+ run_command_calls:
+ - command: [/testbin/xfconf-query, --list, --channel, xfwm4]
+ environ: *env-def
+ rc: 0
+ out: |
+ /general/wrap_cycle
+ /general/wrap_layout
+ /general/wrap_resistance
+ /general/wrap_windows
+ /general/wrap_workspaces
+ /general/zoom_desktop
+ err: ""
diff --git a/ansible_collections/community/general/tests/unit/requirements.txt b/ansible_collections/community/general/tests/unit/requirements.txt
index 0aa7c1fc9..218fe4567 100644
--- a/ansible_collections/community/general/tests/unit/requirements.txt
+++ b/ansible_collections/community/general/tests/unit/requirements.txt
@@ -6,10 +6,12 @@ unittest2 ; python_version < '2.7'
importlib ; python_version < '2.7'
# requirement for the memcached cache plugin
-python-memcached
+python-memcached < 1.60 ; python_version < '3.6'
+python-memcached ; python_version >= '3.6'
# requirement for the redis cache plugin
redis
+async-timeout ; python_version == '3.11'
# requirement for the linode module
linode-python # APIv3
@@ -43,4 +45,12 @@ dataclasses ; python_version == '3.6'
elastic-apm ; python_version >= '3.6'
# requirements for scaleway modules
-passlib[argon2] \ No newline at end of file
+passlib[argon2]
+
+# requirements for the proxmox modules
+proxmoxer < 2.0.0 ; python_version >= '2.7' and python_version <= '3.6'
+proxmoxer ; python_version > '3.6'
+
+#requirements for nomad_token modules
+python-nomad < 2.0.0 ; python_version <= '3.6'
+python-nomad >= 2.0.0 ; python_version >= '3.7'
diff --git a/ansible_collections/community/general/tests/utils/constraints.txt b/ansible_collections/community/general/tests/utils/constraints.txt
index 4fb5276e2..c4d0312d8 100644
--- a/ansible_collections/community/general/tests/utils/constraints.txt
+++ b/ansible_collections/community/general/tests/utils/constraints.txt
@@ -19,7 +19,7 @@ wheel < 0.30.0 ; python_version < '2.7' # wheel 0.30.0 and later require python
yamllint != 1.8.0, < 1.14.0 ; python_version < '2.7' # yamllint 1.8.0 and 1.14.0+ require python 2.7+
pycrypto >= 2.6 # Need features found in 2.6 and greater
ncclient >= 0.5.2 # Need features added in 0.5.2 and greater
-idna < 2.6, >= 2.5 # linode requires idna < 2.9, >= 2.5, requests requires idna < 2.6, but cryptography will cause the latest version to be installed instead
+# idna < 2.6, >= 2.5 # linode requires idna < 2.9, >= 2.5, requests requires idna < 2.6, but cryptography will cause the latest version to be installed instead
paramiko < 2.4.0 ; python_version < '2.7' # paramiko 2.4.0 drops support for python 2.6
python-nomad < 2.0.0 ; python_version <= '3.7' # python-nomad 2.0.0 needs Python 3.7+
pytest < 3.3.0 ; python_version < '2.7' # pytest 3.3.0 drops support for python 2.6
diff --git a/ansible_collections/community/general/tests/utils/shippable/shippable.sh b/ansible_collections/community/general/tests/utils/shippable/shippable.sh
index e98680438..e288cca1c 100755
--- a/ansible_collections/community/general/tests/utils/shippable/shippable.sh
+++ b/ansible_collections/community/general/tests/utils/shippable/shippable.sh
@@ -65,16 +65,7 @@ else
retry pip install "https://github.com/ansible/ansible/archive/stable-${ansible_version}.tar.gz" --disable-pip-version-check
fi
-if [ "${SHIPPABLE_BUILD_ID:-}" ]; then
- export ANSIBLE_COLLECTIONS_PATHS="${HOME}/.ansible"
- SHIPPABLE_RESULT_DIR="$(pwd)/shippable"
- TEST_DIR="${ANSIBLE_COLLECTIONS_PATHS}/ansible_collections/community/general"
- mkdir -p "${TEST_DIR}"
- cp -aT "${SHIPPABLE_BUILD_DIR}" "${TEST_DIR}"
- cd "${TEST_DIR}"
-else
- export ANSIBLE_COLLECTIONS_PATHS="${PWD}/../../../"
-fi
+export ANSIBLE_COLLECTIONS_PATHS="${PWD}/../../../"
if [ "${test}" == "sanity/extra" ]; then
retry pip install junit-xml --disable-pip-version-check